def filerload(self, flag=1):
        """
        필터로드 메시지의 페이로드로 직렬화하여 메시지를 생성하는 함수
        - 라이트 노드가 블룸 필터를 생성 후, 이 블룸 필터를 풀 노드에 전송해야함
        - 풀 노드에게 보내는 VersionMessage의 렐레이 필드 값을 0으로 설정
            - 풀 노드는 블룸 필터를 수신 받기 전까지 트랜잭션 메세지를 전송하지 않음
            - 풀 노드로 전송하기 위한 블룸 필터 메시지 생성 필요
        :param flag: 비트 필드 업데이트 플래그
            - 0 : 풀 노드는 동작 중 비트 필드 업데이트 x
            - 1,2 : 특정 조건하에 새로운 입력을 필터에 더해 비트 필드 업데이트
        :return: 직렬화한 페이로드를 담고 있는 GenericMessage 객체
        """
        # 비트 필드의 길이 정보 가변 정수 형식
        payload = encode_varint(self.size)

        # 비트 필드 정보
        payload += self.filter_bytes()

        # 해시 함수 개수 : 4바이트 리틀엔디언
        payload += int_to_little_endian(self.function_count, 4)

        # tweak 정보 : 4바이트 리틀엔디언
        payload += int_to_little_endian(self.tweak, 4)

        # matched item flag : 1 바이트 리틀엔디언
        payload += int_to_little_endian(flag, 1)

        return GenericMessage(b"filterload", payload)
Esempio n. 2
0
    def sig_hash(self, input_index):
        """
        해제 스크립트에 서명을 포함시켜 트랜잭션을 변형
        step1 : 해제 스크립트 삭제
            - 서명 검증시 트랜잭션의 해제 스크립트 삭제
            - 서명 생성시 동일
            - 입력이 여러 개인 경우, 해당 입력의 해제 스크립트 삭제
        step2 : 이전 트랜잭션 출력의 잠금 스크립트를 입력의 해제 스크립트에 삽입
            - 트랜잭션 입력에 대해 TxIn.script_pubkey를 사용하여 해제 스크립트 입수 가능
        step3 : 해시 유형 추가
            - 서명해시를 구하기 위한 메시지에 포함시킬 필드 결정
            - SIGHASH_ALL : 현재 입력과 다른 모든 입출력을 모두 포함
        step4 : 해시 계산
            - 최종 변경된 트랜잭션의 hash256 해시값 계산
            - 32바이트 빅엔디언 정수로 변환
        :param input_index: 현재 입력에 대한 인덱스
        :return: 변형된 트랜잭션의 서명해시 i.e 서명을 포함시킨 직렬화 정보
        """
        # 버전 : 4바이트 리틀 엔디언
        result = int_to_little_endian(self.version, 4)

        # 이전 트랜잭션 정보 : 이전 트랜잭션 개수 및 이전 트랜잭션들의 정보
        # 서명을 포함시키기 위해 serialize()와 다르게 직렬화
        result += encode_varint(len(self.tx_inputs))
        for idx, tx_in in enumerate(self.tx_inputs):
            # 현재 입력인 경우
            if idx == input_index:
                # 해당 트랜잭션 해시값에 대응되는 트랜잭션의 잠금 스크립트로 스크립트 대체
                tx_in_copy = TxIn(prev_tx=tx_in.prev_tx,
                                  prev_index=tx_in.prev_index,
                                  script_sig=tx_in.script_pubkey(self.testnet),
                                  sequence=tx_in.sequence)
                result += tx_in_copy.serialize()
            # 다른 입력
            else:
                # 해제 스크립트는 포함시키지 않는다
                tx_in_copy = TxIn(prev_tx=tx_in.prev_tx,
                                  prev_index=tx_in.prev_index,
                                  sequence=tx_in.sequence)
                result += tx_in_copy.serialize()

        # 트랜잭션 출력 : 출력 개수 및 출력들의 정보
        result += encode_varint(len(self.tx_outputs))
        for tx_output in self.tx_outputs:
            result += tx_output.serialize()

        # 록타임 : 4바이트의 리틀엔디언
        result += int_to_little_endian(self.locktime, 4)

        # 해시 유형 덧붙임 : SIGHASH_ALL 사용함
        result += int_to_little_endian(SIGHASH_ALL, 4)

        # 서명 해시 생성
        result256 = hash256(result)
        sig_message_hash = int.from_bytes(result256, "big")

        return sig_message_hash
Esempio n. 3
0
    def serialize(self):
        # 이전 트랜잭션 해시값 : 32바이트 리틀엔디언
        result = self.prev_tx[::-1]
        # 이전 트랜잭션 번호 : 4바이트 리틀엔디언
        result += int_to_little_endian(self.prev_index, 4)
        # 해제 스크립트
        result += self.script_sig.serialize()
        # 시퀀스 필드 : 4바이트 리틀엔디언
        result += int_to_little_endian(self.sequence, 4)

        return result
def exercise4():
    from io import BytesIO
    from ecc.secp256k1 import S256Point, Signature
    from ecc.utils import encode_varint, hash256, int_to_little_endian
    from ecc.transaction import Tx, SIGHASH_ALL

    # 문제 : 주어진 p2sh 다중 서명 스크립트와 서명이 주어질 때, 해당 서명 검증
    hex_tx = '0100000001868278ed6ddfb6c1ed3ad5f8181eb0c7a385aa0836f01d5e4789e6bd304d87221a000000db00483045022100dc92655fe37036f47756db8102e0d7d5e28b3beb83a8fef4f5dc0559bddfb94e02205a36d4e4e6c7fcd16658c50783e00c341609977aed3ad00937bf4ee942a8993701483045022100da6bee3c93766232079a01639d07fa869598749729ae323eab8eef53577d611b02207bef15429dcadce2121ea07f233115c6f09034c0be68db99980b9a6c5e75402201475221022626e955ea6ea6d98850c994f9107b036b1334f18ca8830bfff1295d21cfdb702103b287eaf122eea69030a0e9feed096bed8045c8b98bec453e1ffac7fbdbd4bb7152aeffffffff04d3b11400000000001976a914904a49878c0adfc3aa05de7afad2cc15f483a56a88ac7f400900000000001976a914418327e3f3dda4cf5b9089325a4b95abdfa0334088ac722c0c00000000001976a914ba35042cfe9fc66fd35ac2224eebdafd1028ad2788acdc4ace020000000017a91474d691da1574e6b3c192ecfb52cc8984ee7b6c568700000000'
    hex_sec = '03b287eaf122eea69030a0e9feed096bed8045c8b98bec453e1ffac7fbdbd4bb71'
    hex_der = '3045022100da6bee3c93766232079a01639d07fa869598749729ae323eab8eef53577d611b02207bef15429dcadce2121ea07f233115c6f09034c0be68db99980b9a6c5e754022'
    hex_redeem_script = '475221022626e955ea6ea6d98850c994f9107b036b1334f18ca8830bfff1295d21cfdb702103b287eaf122eea69030a0e9feed096bed8045c8b98bec453e1ffac7fbdbd4bb7152ae'
    sec = bytes.fromhex(hex_sec)
    der = bytes.fromhex(hex_der)

    # 해답

    # sec 공개키와 der 서명을 통해 해당 객체 생성
    point = S256Point.parse(sec)
    signature = Signature.parse(der)

    redeem_script = Script.parse(BytesIO(bytes.fromhex(hex_redeem_script)))
    stream = BytesIO(bytes.fromhex(hex_tx))

    tx = Tx.parse(stream)

    # 두번째 서명을 만들기 위한 트랜잭션의 해시 계산
    sig = int_to_little_endian(tx.version, 4)

    # 이전 트랜잭션 정보 : 이전 트랜잭션 개수 및 이전 트랜잭션들의 정보
    sig += encode_varint(len(tx.tx_inputs))

    # 서명 : 트랜잭션 입력의 해제 스크립트를 리딤 스크립트로 대체
    cur_input = tx.tx_inputs[0]
    tx_in_copy = TxIn(prev_tx=cur_input.prev_tx,
                      prev_index=cur_input.prev_index,
                      script_sig=redeem_script,
                      sequence=cur_input.sequence)
    sig += tx_in_copy.serialize()

    # 트랜잭션 출력 : 출력 개수 및 출력들의 정보
    sig += encode_varint(len(tx.tx_outputs))
    for tx_output in tx.tx_outputs:
        sig += tx_output.serialize()
    # 록타임 : 4바이트의 리틀엔디언
    sig += int_to_little_endian(tx.locktime, 4)

    # 해시 유형 덧붙임 : SIGHASH_ALL 사용함
    sig += int_to_little_endian(SIGHASH_ALL, 4)

    # 서명 해시 생성
    result256 = hash256(sig)
    sig_message_hash = int.from_bytes(result256, "big")

    print(point.verify(sig_message_hash, signature))
Esempio n. 5
0
 def hash_prevouts(self):
     if self._hash_prevouts is None:
         all_prevouts = b''
         all_sequence = b''
         for tx_in in self.tx_inputs:
             all_prevouts += tx_in.prev_tx[::-1] + int_to_little_endian(
                 tx_in.prev_index, 4)
             all_sequence += int_to_little_endian(tx_in.sequence, 4)
         self._hash_prevouts = hash256(all_prevouts)
         self._hash_sequence = hash256(all_sequence)
     return self._hash_prevouts
Esempio n. 6
0
    def serialize(self):
        # 금액 : 8바이트 리틀엔디언
        result = int_to_little_endian(self.amount, 8)
        # 해제 스크립트
        result += self.script_pubkey.serialize()

        return result
Esempio n. 7
0
    def serialize(self):
        """
        :return: 직렬화된 str
        """
        # 버전 : 4바이트 리틀 엔디언
        result = int_to_little_endian(self.version, 4)
        # 이전 트랜잭션 정보 : 이전 트랜잭션 개수 및 이전 트랜잭션들의 정보
        result += encode_varint(len(self.tx_inputs))
        for tx_input in self.tx_inputs:
            result += tx_input.serialize()
        # 트랜잭션 출력 : 출력 개수 및 출력들의 정보
        result += encode_varint(len(self.tx_outputs))
        for tx_output in self.tx_outputs:
            result += tx_output.serialize()
        # 록타임 : 4바이트의 리틀엔디언
        result += int_to_little_endian(self.locktime, 4)

        return result
    def serialize(self):
        # 버전 : 4바이트 리틀엔디언
        result = int_to_little_endian(self.version, 4)

        # 이전 블록 해시값 : 32바이트 리틀엔디언
        result += self.prev_block[::-1]

        # 머클루트 : 32 바이트 리틀엔디언
        result += self.merkle_root[::-1]

        # 타임스탬프 : 4 바이트 리틀엔디언
        result += int_to_little_endian(self.timestamp, 4)

        # 비트값 : 4바이트
        result += self.bits

        # 논스값 : 4바이트
        result += self.nounce

        return result
Esempio n. 9
0
    def serialize(self):
        # 요청하는 항목의 개수 : 가변 정수 형식
        payload = encode_varint(len(self.data))

        # 각 항목은 (유형, 항목을 특정하는 해시 식별자)로 구성
        for data_type, identifier in self.data:
            # 유형 : 4바이트 리틀 엔디언
            payload += int_to_little_endian(data_type, 4)
            # 해시 식별자 : 리틀 엔디언
            payload += identifier[::-1]

        return payload
Esempio n. 10
0
 def sig_hash_bip143(self,
                     input_index,
                     redeem_script=None,
                     witness_script=None):
     '''Returns the integer representation of the hash that needs to get
     signed for index input_index'''
     tx_in = self.tx_inputs[input_index]
     # per BIP143 spec
     s = int_to_little_endian(self.version, 4)
     s += self.hash_prevouts() + self.hash_sequence()
     s += tx_in.prev_tx[::-1] + int_to_little_endian(tx_in.prev_index, 4)
     if witness_script:
         script_code = witness_script.serialize()
     elif redeem_script:
         script_code = p2pkh_script(redeem_script.cmds[1]).serialize()
     else:
         script_code = p2pkh_script(
             tx_in.script_pubkey(self.testnet).cmds[1]).serialize()
     s += script_code
     s += int_to_little_endian(tx_in.value(), 8)
     s += int_to_little_endian(tx_in.sequence, 4)
     s += self.hash_outputs()
     s += int_to_little_endian(self.locktime, 4)
     s += int_to_little_endian(SIGHASH_ALL, 4)
     return int.from_bytes(hash256(s), 'big')
 def raw_serialize(self):
     result = b""
     for command in self.commands:
         # 명령어가 정수인 경우는 오피코드
         if type(command) == int:
             result += int_to_little_endian(command, 1)
         # 명령어가 바이트인 경우, 길이 정보를 추가
         else:
             length = len(command)
             # 길이가 75 이하이면, 1바이트로 표현
             if length <= 75:
                 result += int_to_little_endian(length, 1)
             # OP_PUSHDATA1 삽입 후, 1바이트로 표현
             elif 75 < length < 0x100:
                 result += int_to_little_endian(76, 1)
                 result += int_to_little_endian(length, 1)
             # OP_PUSHDATA2 삽입 후, 2바이트로 표현
             elif 0x100 <= length <= 520:
                 result += int_to_little_endian(77, 1)
                 result += int_to_little_endian(length, 2)
             else:
                 raise ValueError("too long an command")
             # 길이 정보 뒤에 실제 명령어 추가
             result += command
     return result
Esempio n. 12
0
    def serialize(self):
        # 프로토콜 버전 : 4바이트 리틀엔디언
        ret = int_to_little_endian(self.version, 4)
        # 서비스 정보 : 8바이트 리틀엔디언
        ret += int_to_little_endian(self.services, 8)
        # 타임스탬프 : 8바이트 리틀엔디언
        ret += int_to_little_endian(self.timestamp, 8)
        # 수신자 서비스 정보 : 8바이트 리틀엔디언
        ret += int_to_little_endian(self.receiver_services, 8)
        # 수신자 IP : 16바이트 (첫 12바이트는 IPv6 형식)
        ret += b'\x00' * 10 + b'\xff\xff' + self.receiver_ip
        # 수신자 포트 정보 : 2바이트 빅엔디언
        ret += self.receiver_port.to_bytes(2, "big")
        # 송신자 서비스 정보 : 8바이트 리틀엔디언
        ret += int_to_little_endian(self.sender_services, 8)
        # 송신자 IP : 16바이트 (첫 12바이트는 IPv6 형식)
        ret += b'\x00' * 10 + b'\xff\xff' + self.sender_ip
        # 송신자 포트 정보 : 2바이트 빅엔디언
        ret += self.sender_port.to_bytes(2, "big")
        # 논스 : 8바이트
        ret += self.nonce
        # 사용자 에이전트 : 가변필드
        ret += encode_varint(len(self.user_agent))
        ret += self.user_agent
        # 높이 : 4바이트 리틀엔디언
        ret += int_to_little_endian(self.latest_block, 4)
        # 릴레이 : 2비트
        if self.relay:
            ret += b"\x01"
        else:
            ret += b"\x00"

        return ret
Esempio n. 13
0
    def serialize(self):
        # 프로토컬 버전 : 4바이트 리틀엔디언
        ret = int_to_little_endian(self.version, 4)

        # 블록 헤더 개수 : 가변 정수
        ret += encode_varint(self.num_hashes)

        # 시작 블록 : 리틀엔디언
        ret += self.start_block[::-1]

        # 마지막 블록 : 리틀엔디언
        ret += self.end_block[::-1]

        return ret
Esempio n. 14
0
    def serialize_segwit(self):
        """
        세그윗 트랜잭션 직렬화
        - 이전 트랜잭션의 해제 스크립트는 증인 필드에 들어감
        - 증인 필드 관련 정보는 마지막에 직렬화
        :return: 직렬화된 str
        """
        # 버전 : 4바이트 리틀 엔디언
        result = int_to_little_endian(self.version, 4)

        # 마커 정보
        result += b"\x00\x01"

        # 이전 트랜잭션 정보 : 이전 트랜잭션 개수 및 이전 트랜잭션들의 정보
        result += encode_varint(len(self.tx_inputs))
        for tx_input in self.tx_inputs:
            result += tx_input.serialize()

        # 트랜잭션 출력 : 출력 개수 및 출력들의 정보
        result += encode_varint(len(self.tx_outputs))
        for tx_output in self.tx_outputs:
            result += tx_output.serialize()

        # 각 입력에 있는 증인 필드 : 가변 정수 형식
        for tx_in in self.tx_inputs:
            result += int_to_little_endian(len(tx_in.witness), 1)
            for item in tx_in.witness:
                if type(item) == int:
                    result += int_to_little_endian(item, 1)
                else:
                    result += encode_varint(len(item)) + item

        # 록타임 : 4바이트의 리틀엔디언
        result += int_to_little_endian(self.locktime, 4)

        return result
Esempio n. 15
0
    def serialize(self):
        # 매직 넘버 : 4바이트
        ret = self.magic

        # 커맨드 필드 : 12바이트(빈공간 0x00포함)
        ret += self.command
        ret += b"\x00" * (12 - len(self.command))

        # 페이로드 길이 : 4바이트 리틀엔디언
        ret += int_to_little_endian(len(self.payload), 4)

        # 체크섬 필드 : 4바이트
        ret += hash256(self.payload)[:4]

        # 페이로드 필드
        ret += self.payload

        return ret
Esempio n. 16
0
    def __init__(self,
                 version=70015,
                 services=0,
                 timestamp=None,
                 receiver_services=0,
                 receiver_ip=b'\x00\x00\x00\x00',
                 receiver_port=8333,
                 sender_services=0,
                 sender_ip=b'\x00\x00\x00\x00',
                 sender_port=8333,
                 nonce=None,
                 user_agent=b'/programmingbitcoin:0.1/',
                 latest_block=0,
                 relay=False):

        self.version = version
        self.services = services
        if timestamp is None:
            self.timestamp = int(time.time())
        else:
            self.timestamp = timestamp

        self.receiver_services = receiver_services
        self.receiver_ip = receiver_ip
        self.receiver_port = receiver_port

        self.sender_services = sender_services
        self.sender_ip = sender_ip
        self.sender_port = sender_port

        if nonce is None:
            self.nonce = int_to_little_endian(randint(0, 2**64), 8)
        else:
            self.nonce = nonce

        self.user_agent = user_agent
        self.latest_block = latest_block
        self.relay = relay