Skip to content

Commit

Permalink
Deserialize segwit transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
giacomocaironi committed Jun 8, 2020
1 parent 8ac71ba commit 3dde80b
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 37 deletions.
23 changes: 23 additions & 0 deletions btclib/tests/test_transactions.py
Expand Up @@ -47,3 +47,26 @@ def test_wiki_transaction():

assert tx["input_count"] == 1
assert tx["output_count"] == 2


# 4e52f7848dab7dd89ef7ba477939574198a170bfcb2fb34355c69f5e0169f63c
def test_single_witness():
tx_bytes = "010000000001019bdea7abb2fa14dead47dd14d03cf82212a25b6096a8da6b14feec3658dbcf9d0100000000ffffffff02a02526000000000017a914f987c321394968be164053d352fc49763b2be55c874361610000000000220020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d04004730440220421fbbedf2ee096d6289b99973509809d5e09589040d5e0d453133dd11b2f78a02205686dbdb57e0c44e49421e9400dd4e931f1655332e8d078260c9295ba959e05d014730440220398f141917e4525d3e9e0d1c6482cb19ca3188dc5516a3a5ac29a0f4017212d902204ea405fae3a58b1fc30c5ad8ac70a76ab4f4d876e8af706a6a7b4cd6fa100f44016952210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae00000000"
tx_bytes = bytes.fromhex(tx_bytes)

tx = transaction_deserialize(tx_bytes)

assert tx["output_count"] == 2
assert tx["lock_time"] == 0
assert transaction_serialize(tx) == tx_bytes


# a4b76807519aba5740f7865396bc4c5ca0eb8aa7c3744ca2db88fcc9e345424c
def test_double_witness():
tx_bytes = "01000000000102322d4f05c3a4f78e97deda01bd8fc5ff96777b62c8f2daa72b02b70fa1e3e1051600000017160014e123a5263695be634abf3ad3456b4bf15f09cc6afffffffffdfee6e881f12d80cbcd6dc54c3fe390670678ebd26c3ae2dd129f41882e3efc25000000171600145946c8c3def6c79859f01b34ad537e7053cf8e73ffffffff02c763ac050000000017a9145ffd6df9bd06dedb43e7b72675388cbfc883d2098727eb180a000000001976a9145f9e96f739198f65d249ea2a0336e9aa5aa0c7ed88ac024830450221009b364c1074c602b2c5a411f4034573a486847da9c9c2467596efba8db338d33402204ccf4ac0eb7793f93a1b96b599e011fe83b3e91afdc4c7ab82d765ce1da25ace01210334d50996c36638265ad8e3cd127506994100dd7f24a5828155d531ebaf736e160247304402200c6dd55e636a2e4d7e684bf429b7800a091986479d834a8d462fbda28cf6f8010220669d1f6d963079516172f5061f923ef90099136647b38cc4b3be2a80b820bdf90121030aa2a1c2344bc8f38b7a726134501a2a45db28df8b4bee2df4428544c62d731400000000"
tx_bytes = bytes.fromhex(tx_bytes)

tx = transaction_deserialize(tx_bytes)

assert tx["lock_time"] == 0
assert transaction_serialize(tx) == tx_bytes
60 changes: 23 additions & 37 deletions btclib/transactions.py
Expand Up @@ -18,6 +18,7 @@ class TxIn(TypedDict):
script_length: int = 0
signature_script: bytes = 0
sequence: int = 4294967295
witnesses: List[bytes] = []


def tx_in_deserialize(data: bytes):
Expand All @@ -30,6 +31,7 @@ def tx_in_deserialize(data: bytes):
tx_in["sequence"] = int.from_bytes(
data[tx_in["script_length"] : tx_in["script_length"] + 4], "little"
)
tx_in["witnesses"] = []
return tx_in


Expand Down Expand Up @@ -71,22 +73,21 @@ class Transaction(TypedDict):
tx_inputs: List[TxIn] = []
output_count: int = 0
tx_outputs: List[TxOut] = []
witnesses = [] # TODO
lock_time: int = 0


def transaction_deserialize(data: bytes):
# if len(data) < 60:
# raise Exception
tx = Transaction()
tx["witness_flag"] = False
tx["version"] = int.from_bytes(data[0:4], "little")
if False: # data[4:6] == b"\x01\x00":
if data[4:6] == b"\x00\x01":
tx["witness_flag"] = True
data = data[6:]
else:
data = data[4:]

# not sure why these lines are mandatory
tx["tx_inputs"] = []
tx["tx_outputs"] = []

Expand All @@ -104,53 +105,38 @@ def transaction_deserialize(data: bytes):
tx["tx_outputs"].append(tx_output)
data = data[len(tx_out_serialize(tx_output)) :]

# if trans.witness_flag:
# trans.witnesses = TxWitness.from_bytes(data)
# data = data[len(trans.witnesses.to_bytes()) :]
if tx["witness_flag"]:
for x in range(tx["input_count"]):
witness_count = varint.decode(data)
data = data[len(varint.encode(witness_count)) :]
for i in range(witness_count):
witness_len = varint.decode(data)
data = data[len(varint.encode(witness_len)) :]
tx["tx_inputs"][x]["witnesses"].append(data[:witness_len])
data = data[witness_len:]

tx["lock_time"] = int.from_bytes(data[:4], "little")
return tx


def transaction_serialize(tx: Transaction):
out = tx["version"].to_bytes(4, "little")
# if self.witness_flag:
# out += b"\x00\x01"
if tx["witness_flag"]:
out += b"\x00\x01"
out += varint.encode(tx["input_count"])
for tx_in in tx["tx_inputs"]:
out += tx_in_serialize(tx_in)

out += varint.encode(tx["output_count"])
for tx_out in tx["tx_outputs"]:
out += tx_out_serialize(tx_out)
# if self.witness_flag:
# out += self.witnesses.to_bytes()
if tx["witness_flag"]:
for x in range(tx["input_count"]):
witness_count = len(tx["tx_inputs"][x]["witnesses"])
out += varint.encode(witness_count)
for i in range(witness_count):
witness_len = len(tx["tx_inputs"][x]["witnesses"][i])
out += varint.encode(witness_len)
out += tx["tx_inputs"][x]["witnesses"][i]
out += tx["lock_time"].to_bytes(4, "little")
return out


# class TxWitness:
# def __init__(self):
# self.count = 0
# self.witnesses = []
#
# @classmethod
# def from_bytes(cls, data):
# if len(data) < 41:
# raise Exception
# tx_witness = cls()
# tx_witness.count = varint.decode(data[:4])
# data = data[len(varint.encode(tx_witness.count)) :]
#
# for x in range(tx_witness.count):
# witness_len = varint.decode(data)
# data = data[len(varint.encode(witness_len)) :]
# tx_witness.witnesses.append(data[:witness_len])
#
# return tx_witness
#
# def to_bytes(self):
# out = varint.encode(self.count)
# for witness in self.witnesses:
# out += varint.encode(len(witness)) + witness
# return out

0 comments on commit 3dde80b

Please sign in to comment.