def verify_input(self, input_index):
        """
        현재 트랜잭션 입력에 대한 검증
        step 1 : 현재 입력에 대응하는 결합 스크립트 생성
            - 입력에 대응하는 이전 트랜잭션 출력의 잠금 스크립트
            - 입력의 해제 스크립트
        step 2 : 현재 입력에 대응하는 서명 생성
        step 3 : 결합스크립트를 이용하여 서명 계산
        :param input_index: 현재 입력에 대한 인덱스
        :return: 검증 boolean
        """
        # 해당 트랜잭션 해시값에 대응되는 트랜잭션의 잠금 스크립트
        current_intput_tx = self.tx_inputs[input_index]
        current_pubkey = current_intput_tx.script_pubkey(testnet=self.testnet)

        # 스크립트 형식에 따라 리딤 스크립트 생성
        if current_pubkey.is_p2sh_script_pubkey():
            command = current_intput_tx.script_sig.commands[-1]
            redeem_script_raw = encode_varint(len(command)) + command
            redeem_script = Script.parse(BytesIO(redeem_script_raw))
        else:
            redeem_script = None

        # 현재 트랜잭션 입력에 대한 결합 스크립트
        combined = current_intput_tx.script_sig + current_pubkey

        # 현재 트랜잭션 입력에 대한 서명 생성
        message_hash = self.sig_hash(input_index, redeem_script)

        # 결합 스크립트를 통한 서명 검증
        return combined.evaluate(message_hash)
Beispiel #2
0
    def parse(cls, stream):
        # 금액 : 8바이트 리틀엔디언
        amount = little_endian_to_int(stream.read(8))
        # 해제 스크립트
        script_pubkey = Script.parse(stream)

        return cls(amount, script_pubkey)
def example2():
    print("Example : Height of Transaction in the block")
    stream = BytesIO(
        bytes.fromhex(
            '5e03d71b07254d696e656420627920416e74506f6f6c20626a31312f4542312f4144362f43205914293101fabe6d6d678e2c8c34afc36896e7d9402824ed38e856676ee94bfdb0c6c4bcd8b2e5666a0400000000000000c7270000a5e00e00'
        ))
    script_sig = Script.parse(stream)
    print("Height : {}".format(little_endian_to_int(script_sig.commands[0])))
Beispiel #4
0
def p2pkh_script(h160):
    """
    encoder.decode_base58로 구한 해시값을 잠금 스크립트로 변환하는 함수
    - [OP_DUP, OP_HASH160, <hash>, OP_EQUALVERIFY, OP_CHECKSUM] 형태로 구성
    :param h160: 공개키의 hash160 해시값
    :return: 잠금 스크립트 객체
    """
    return Script([0x76, 0xa9, h160, 0x88, 0xac])
def example1():
    print("Example : Miner-defined ScriptSig")
    stream = BytesIO(
        bytes.fromhex(
            '4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73'
        ))
    script = Script.parse(stream)
    for cmd in script.commands:
        print(cmd)
Beispiel #6
0
 def __init__(self,
              prev_tx,
              prev_index,
              script_sig=None,
              sequence=0xffffffff):
     """
     :param prev_tx: 이전 트랜잭션의 해시값/ID
     :param prev_index: 이전 트랜잭션의 출력 번호
     :param script_sig: 해제 스크립트
     :param sequence: 시퀀스 필드
     """
     self.prev_tx = prev_tx
     self.prev_index = prev_index
     if script_sig is None:
         self.script_sig = Script()
     else:
         self.script_sig = script_sig
     self.sequence = sequence
Beispiel #7
0
 def parse(cls, stream):
     # 이전 트랜잭션 해시값 : 32바이트 리틀엔디언
     prev_tx = stream.read(32)[::-1]
     # 이전 트랜잭션 번호 : 4바이트 리틀엔디언
     prev_index = little_endian_to_int(stream.read(4))
     # 해제 스크립트
     script_sig = Script.parse(stream)
     # 시퀀스 필드 : 4바이트 리틀엔디언
     seqeunce = little_endian_to_int(stream.read(4))
     return cls(prev_tx, prev_index, script_sig, seqeunce)
    def sign_input(self, input_index, private_key):
        """
        현재 트랜잭션 입력에 해제 스크립트 생성
        :param input_index: 현재 입력에 대한 인덱스
        :param private_key: 해제 스크립트에 사용할 비밀키
        :return: 추가한 해제 스크립트 검증 결과 boolean
        """
        # 트랜잭션의 서명 해시
        message_hash = self.sig_hash(input_index)

        # 트랜잭션의 서명 해시에 대한 서명 der 직렬화
        der = private_key.sign(message_hash).der()

        # 스크립트에 들어가는 서명은 DER형식 서명과 1바이트의 해시 유형으로 구성
        sig = der + SIGHASH_ALL.to_bytes(1, "big")
        sec = private_key.public_key.sec()

        # 해당 트랜잭션의 입력의 해제 스크립트 추가
        script_sig = Script([sig, sec])
        self.tx_inputs[input_index].script_sig = script_sig

        # 추가한 해제 스크립트 검증
        return self.verify_input(input_index)
Beispiel #9
0
def example4():
    from ecc.secp256k1 import PrivateKey
    print("트랜잭션 해제 스크립트 생성")
    raw_tx = (
        '0100000001813f79011acb80925dfe69b3def355fe914bd1d96a3f5f71bf8303c6a989c7d1000000006b483045022100ed81ff192e75a3fd2304004dcadb746fa5e24c5031ccfcf21320b0277457c98f02207a986d955c6e0cb35d446a89d3f56100f4d7f67801c31967743a9c8e10615bed01210349fc4e631e3624a545de3f89f5d8684c7b8138bd94bdd531d2e213bf016b278afeffffff02a135ef01000000001976a914bc3b654dca7e56b04dca18f2566cdaf02e8d9ada88ac99c39800000000001976a9141c4bc762dd5423e332166702cb75f40df79fea1288ac19430600'
    )
    stream = BytesIO(bytes.fromhex(raw_tx))
    tx = Tx.parse(stream)
    z = tx.sig_hash(0)

    # 비밀키 생성 및 트랜잭션의 서명 해시에 대한 서명 der 직렬화
    private_key = PrivateKey(secret=8675309)
    der = private_key.sign(z).der()

    # 스크립트에 들어가는 서명은 DER형식 서명과 1바이트의 해시 유형으로 구성
    sig = der + SIGHASH_ALL.to_bytes(1, "big")
    sec = private_key.public_key.sec()

    # 해당 트랜잭션의 입력의 해제 스크립트 추가
    script_sig = Script([sig, sec])
    tx.tx_inputs[0].script_sig = script_sig

    print(tx.serialize().hex())
Beispiel #10
0
class TxIn:
    """
    비트코인 입력(트랜잭션 입력)
    - 트랜잭션 입력은 이전 트랜잭션의 출력 내용과 연관이 있음
    - 본인이 소유한 비트코인을 정확히 가리키기 위한 두가지 필요 사항
        1. 이전에 내가 수신한 비트코인을 가리키는 참조 정보
        2. 해당 비트코인이 나의 소유라는 증명
            : 타원곡선 서명 알고리즘(3장) 사용
    - 4가지 필드 존재
        - 이전 트랜잭션의 출력 정보
            1. 이전 트랜잭션의 해시값/ID
            2. 이전 트랜잭션의 출력 번호
        - 이전 트랜잭션의 출력을 사용하는 방법 정의
            3. 해제 스크립트
            4. 시퀀스

    1. 이전 트랜잭션의 해시값/ID
        - 이전 트랜잭션 해시값으로 hash256 사용
        - 해시 충돌이 거의 없어 이전 트랜잭션을 unique하게 특정 가능
        - 32바이트 리틀엔디언
    2. 이전 트랜잭션의 출력 번호
        - 각각의 트랜잭션은 하나 이상의 출력을 가진다
          i.e 사용하려는 이전 트랜잭션에서 출력이 여러 개일 가능성 존재
        - 몇번째 출력인지에 대한 정보 필요
        - 4바이트 리틀엔디언
    3. 해제 스크립트
        - 비트코인의 스마트 계약 언어인 Script를 구성하는 한 부분
        - 트랜잭션 출력의 소유자만이 할 수 있는 무엇인가를 나타냄
            : 금고를 열기 위한 열쇠
        - 가변 길이 필드로 시작
            - 해당 필드의 길이를 알아야 파싱 가능하기 때문
    4. 시퀀스
        - 빈번한 거래를 위해 록타임 필드와 함께 표기했던 필드
            - 록타임이 유요하는 동안 발생한 여러 거래를 하나의 트랜잭션으로 블록체인에 기록 가능
            - 최종 정산 결과만 기록하면 됨
            => 채굴자가 악용하기 쉬워 이런 방식으로 사용되지 않음
        - RBF(Replace-By-Fee)와 OP_CHECKSEQUENCEVERIFY로 사용
        - 4바이트 리틀엔디언

    """
    def __init__(self,
                 prev_tx,
                 prev_index,
                 script_sig=None,
                 sequence=0xffffffff):
        """
        :param prev_tx: 이전 트랜잭션의 해시값/ID
        :param prev_index: 이전 트랜잭션의 출력 번호
        :param script_sig: 해제 스크립트
        :param sequence: 시퀀스 필드
        """
        self.prev_tx = prev_tx
        self.prev_index = prev_index
        if script_sig is None:
            self.script_sig = Script()
        else:
            self.script_sig = script_sig
        self.sequence = sequence

    def __repr__(self):
        return "{}:{}".format(self.prev_tx.hex(), self.prev_index)

    @classmethod
    def parse(cls, stream):
        # 이전 트랜잭션 해시값 : 32바이트 리틀엔디언
        prev_tx = stream.read(32)[::-1]
        # 이전 트랜잭션 번호 : 4바이트 리틀엔디언
        prev_index = little_endian_to_int(stream.read(4))
        # 해제 스크립트
        script_sig = Script.parse(stream)
        # 시퀀스 필드 : 4바이트 리틀엔디언
        seqeunce = little_endian_to_int(stream.read(4))
        return cls(prev_tx, prev_index, script_sig, seqeunce)

    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 fetch_tx(self, testnet=False):
        """
        :param testnet: 테스트넷 여부
        :return: 이전 트랜잭션 해시값(id)에 대응되는 트랜잭션(Tx 객체)
        """
        return TxFetcher.fetch(self.prev_tx.hex(), testnet=testnet)

    def value(self, testnet=False):
        """
        :param testnet: 테스트넷 여부
        :return: 이전 트랜잭션 해시값에 대응되는 트랜잭션의 비용
        """
        tx = self.fetch_tx(testnet=testnet)
        return tx.tx_outputs[self.prev_index].amount

    def script_pubkey(self, testnet=False):
        """
        :param testnet: 테스트넷 여부
        :return: 이전 트랜잭션 해시값에 대응되는 트랜잭션의 해제 스크립트
        """
        tx = self.fetch_tx(testnet=testnet)
        return tx.tx_outputs[self.prev_index].script_pubkey
Beispiel #11
0
    def verify_input(self, input_index):
        """
        현재 트랜잭션 입력에 대한 검증
        step 1 : 현재 입력에 대응하는 결합 스크립트 생성
            - 입력에 대응하는 이전 트랜잭션 출력의 잠금 스크립트
            - 입력의 해제 스크립트
                - p2sh의 경우 리딤 스크립트
                    - 호환성을 위해 p2wpkh/p2wsh 또한 리딤 스크립트에 포함
                - p2wpkh 스크립트의 경우, 증인필드(서명, 공개키)
        step 2 : 현재 입력에 대응하는 서명 생성
        step 3 : 결합 스크립트를 이용하여 서명 계산
        :param input_index: 현재 입력에 대한 인덱스
        :return: 검증 boolean
        """
        # 해당 트랜잭션 해시값에 대응되는 트랜잭션의 잠금 스크립트
        current_intput_tx = self.tx_inputs[input_index]
        current_pubkey = current_intput_tx.script_pubkey(testnet=self.testnet)

        # 스크립트 형식에 따라 리딤 스크립트 생성
        if current_pubkey.is_p2sh_script_pubkey():
            command = current_intput_tx.script_sig.commands[-1]
            redeem_script_raw = encode_varint(len(command)) + command
            redeem_script = Script.parse(BytesIO(redeem_script_raw))

            # 리딤 스크립트 : p2sh-p2wpkh 이거나 p2sh-p2wsh 이다
            # p2wpkh인 경우 :
            if redeem_script.is_p2wpkh_script_pubkey():
                message_hash = self.sig_hash_bip143(input_index, redeem_script)
                witness = current_intput_tx.witness
            # p2wsh인 경우
            elif redeem_script.is_p2wsh_script_pubkey():
                # 증인 스크립트 추출
                cmd = current_intput_tx.witness[-1]

                # 가변 길이의 증인 스크립트 파싱
                raw_witness = encode_varint(len(cmd)) + cmd
                witness_script = Script.parse(BytesIO(raw_witness))
                message_hash = self.sig_hash_bip143(
                    input_index, witness_script=witness_script)
                witness = current_intput_tx.witness
            # p2sh인 경우
            else:
                message_hash = self.sig_hash(input_index, redeem_script)
                witness = None
        else:
            # 리딤 스크립트 : p2sh-p2wpkh 이거나 p2sh-p2wsh 이다
            # p2wpkh인 경우 :
            if current_pubkey.is_p2wpkh_script_pubkey():
                message_hash = self.sig_hash_bip143(input_index)
                witness = current_intput_tx.witness
            # p2wsh인 경우 :
            elif current_pubkey.is_p2wsh_script_pubkey():
                # 증인 스크립트 추출
                cmd = current_intput_tx.witness[-1]

                # 가변 길이의 증인 스크립트 파싱
                raw_witness = encode_varint(len(cmd)) + cmd
                witness_script = Script.parse(BytesIO(raw_witness))
                message_hash = self.sig_hash_bip143(
                    input_index, witness_script=witness_script)
                witness = current_intput_tx.witness
            # p2sh인 경우
            else:
                message_hash = self.sig_hash(input_index)
                witness = None

        # 현재 트랜잭션 입력에 대한 결합 스크립트
        combined = current_intput_tx.script_sig + current_pubkey

        # 결합 스크립트를 통한 서명 검증
        return combined.evaluate(message_hash, witness)