def parse(cls: Type[_Tx], data: BinaryData, check_validity: bool = True) -> _Tx: "Return a Tx by parsing binary data." stream = bytesio_from_binarydata(data) # version is a signed int (int32_t, not uint32_t) version = int.from_bytes(stream.read(4), byteorder="little", signed=True) segwit = stream.read(2) == _SEGWIT_MARKER if not segwit: # Change stream position: seek to byte offset relative to position stream.seek(-2, SEEK_CUR) # current position n = var_int.parse(stream) vin = [TxIn.parse(stream) for _ in range(n)] n = var_int.parse(stream) vout = [TxOut.parse(stream) for _ in range(n)] if segwit: for tx_in in vin: tx_in.script_witness = Witness.parse(stream, check_validity) lock_time = int.from_bytes(stream.read(4), byteorder="little", signed=False) return cls(version, lock_time, vin, vout, check_validity)
def test_coinbase_block_1() -> None: coinbase_out = "00f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac" tx_out = TxOut.parse(coinbase_out) assert tx_out.serialize().hex() == coinbase_out coinbase_inp = ( # prev_out "0000000000000000000000000000000000000000000000000000000000000000ffffffff" "0704ffff001d0104" # script_sig "ffffffff" # sequence ) tx_in = TxIn.parse(coinbase_inp) assert tx_in.serialize().hex() == coinbase_inp assert tx_in.prev_out.is_coinbase coinbase = "01000000" "01" + coinbase_inp + "01" + coinbase_out + "00000000" tx = Tx.parse(coinbase) assert tx.serialize(include_witness=True).hex() == coinbase assert tx == Tx.from_dict(tx.to_dict()) assert tx.version == 1 assert tx.lock_time == 0 assert len(tx.vin) == 1 assert len(tx.vout) == 1 assert tx.vin[0].script_sig == tx_in.script_sig assert tx.vout[0].script_pub_key == tx_out.script_pub_key tx_id = "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098" assert tx.id.hex() == tx_id assert tx.id == tx.hash assert tx.size == 134 assert tx.vsize == tx.size assert tx.weight == tx.size * 4 assert not tx.is_segwit() assert not any(bool(w) for w in tx.vwitness) assert not any(bool(tx_in.script_witness) for tx_in in tx.vin) assert tx.is_coinbase()
def parse(cls: Type["Tx"], data: BinaryData, check_validity: bool = True) -> "Tx": "Return a Tx by parsing binary data." stream = bytesio_from_binarydata(data) # version is a signed int (int32_t) in bitcoin_core # However there are at least two transactions: # 35e79ee733fad376e76d16d1f10088273c2f4c2eaba1374a837378a88e530005 # c659729a7fea5071361c2c1a68551ca2bf77679b27086cc415adeeb03852e369 # where the version number is negative if it is considered as a signed # integer. As such in btclib the version is an UNSIGNED integer. # This has been discussed in: https://github.com/bitcoin/bitcoin/pull/16525 version = int.from_bytes(stream.read(4), byteorder="little", signed=False) segwit = stream.read(2) == _SEGWIT_MARKER if not segwit: # Change stream position: seek to byte offset relative to position stream.seek(-2, SEEK_CUR) # current position n = var_int.parse(stream) vin = [TxIn.parse(stream) for _ in range(n)] n = var_int.parse(stream) vout = [TxOut.parse(stream) for _ in range(n)] if segwit: for tx_in in vin: tx_in.script_witness = Witness.parse(stream, check_validity) lock_time = int.from_bytes(stream.read(4), byteorder="little", signed=False) return cls(version, lock_time, vin, vout, check_validity)
def test_tx_in() -> None: tx_in = TxIn() assert tx_in.prev_out == OutPoint() assert tx_in.script_sig == b"" assert tx_in.sequence == 0 assert tx_in.outpoint == tx_in.prev_out assert tx_in.scriptSig == tx_in.script_sig assert tx_in.nSequence == tx_in.nSequence assert tx_in.is_coinbase() assert not tx_in.is_segwit() tx_in2 = TxIn.parse(tx_in.serialize()) assert not tx_in2.is_segwit() assert tx_in == tx_in2 tx_in2 = TxIn.from_dict(tx_in.to_dict()) assert not tx_in2.is_segwit() assert tx_in == tx_in2 tx_id = "d5b5982254eebca64e4b42a3092a10bfb76ab430455b2bf0cf7c4f7f32db1c2e" vout = 0 prev_out = OutPoint(tx_id, vout) script_sig = b"" sequence = 0 tx_in = TxIn(prev_out, script_sig, sequence) assert tx_in.prev_out == prev_out assert tx_in.script_sig == script_sig assert tx_in.sequence == sequence assert tx_in.outpoint == tx_in.prev_out assert tx_in.scriptSig == tx_in.script_sig assert tx_in.nSequence == tx_in.nSequence assert not tx_in.is_coinbase() assert not tx_in.is_segwit() tx_in2 = TxIn.parse(tx_in.serialize()) assert not tx_in2.is_segwit() assert tx_in == tx_in2 tx_in2 = TxIn.from_dict(tx_in.to_dict()) assert not tx_in2.is_segwit() assert tx_in == tx_in2 prev_out = OutPoint( "9dcfdb5836ecfe146bdaa896605ba21222f83cd014dd47adde14fab2aba7de9b", 1 ) script_sig = b"" sequence = 0xFFFFFFFF tx_in = TxIn(prev_out, script_sig, sequence) stack = [ "", "30440220421fbbedf2ee096d6289b99973509809d5e09589040d5e0d453133dd11b2f78a02205686dbdb57e0c44e49421e9400dd4e931f1655332e8d078260c9295ba959e05d01", "30440220398f141917e4525d3e9e0d1c6482cb19ca3188dc5516a3a5ac29a0f4017212d902204ea405fae3a58b1fc30c5ad8ac70a76ab4f4d876e8af706a6a7b4cd6fa100f4401", "52210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae", ] tx_in.script_witness = Witness(stack) assert tx_in.prev_out == prev_out assert tx_in.script_sig == script_sig assert tx_in.sequence == sequence assert tx_in.outpoint == tx_in.prev_out assert tx_in.scriptSig == tx_in.script_sig assert tx_in.nSequence == tx_in.nSequence assert not tx_in.is_coinbase() assert tx_in.is_segwit() tx_in2 = TxIn.parse(tx_in.serialize()) assert not tx_in2.is_segwit() assert tx_in == tx_in2 or TX_IN_COMPARES_WITNESS tx_in2 = TxIn.from_dict(tx_in.to_dict()) assert tx_in2.is_segwit() assert tx_in == tx_in2 assert tx_in != OutPoint() tx_in.sequence = 0xFFFFFFFF + 1 with pytest.raises(BTClibValueError, match="invalid sequence: "): tx_in.assert_valid()