交易的 SegWit 部分如何被序列化和解析?
我正在閱讀程式比特幣。在第 13 章中,我對見證部分如何序列化和解析感到困惑。作者提供了以下方法來解析它:
def parse_segwit(cls, s, testnet=False): version = little_endian_to_int(s.read(4)) marker = s.read(2) if marker != b'\x00\x01': raise RuntimeError('Not a segwit transaction {}'.format(marker)) num_inputs = read_varint(s) inputs = [] for _ in range(num_inputs): inputs.append(TxIn.parse(s)) num_outputs = read_varint(s) outputs = [] for _ in range(num_outputs): outputs.append(TxOut.parse(s)) for tx_in in inputs: num_items = read_varint(s) items = [] for _ in range(num_items): item_len = read_varint(s) if item_len == 0: # where I can't understand items.append(0) # else: items.append(s.read(item_len)) tx_in.witness = items locktime = little_endian_to_int(s.read(4)) return cls(version, inputs, outputs, locktime, testnet=testnet, segwit=True)
以及下面的程式碼來序列化它:
def serialize_segwit(self): result = int_to_little_endian(self.version, 4) result += b'\x00\x01' result += encode_varint(len(self.tx_ins)) for tx_in in self.tx_ins: result += tx_in.serialize() result += encode_varint(len(self.tx_outs)) for tx_out in self.tx_outs: result += tx_out.serialize() for tx_in in self.tx_ins: 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 result += int_to_little_endian(self.locktime, 4) return result
我無法理解的是這兩種方法中的最後一個 for 循環。據我了解,這些是 segwit 部分被序列化和反序列化的地方,這些程式碼應該是一一對應的。就像他們在前半部分所做的那樣。但它們在後半部分的 for 循環中不匹配。我是說:
- 對於“num_items = read_varint(s)”,應該有一個“result += encode_varint(len(tx_in.witness))”。
- 對於“item_len = read_varint(s)”,應該有一個“result += encode_varint(len(item))”。
- 而對於“if item_len == 0:”,我不知道這個 if 做什麼以及對應的序列化邏輯在哪裡。
那麼我對隔離見證部分邏輯的理解有什麼問題嗎?
對於“num_items = read_varint(s)”,應該有一個“result += encode_varint(len(tx_in.witness))”。
相應的表達式似乎是
result += int_to_little_endian(len(tx_in.witness), 1)
,儘管這是錯誤的**,**因為它最多只能工作 252 個見證堆棧元素的長度(對於超過 252 個元素,varint 編碼不僅僅是單字節編碼)。當然,如此長的見證堆棧非常罕見,因此這在實踐中可能幾乎總是有效。對於“item_len = read_varint(s)”,應該有一個“result += encode_varint(len(item))”。
對應的表達式似乎是 的第一部分
result += encode_varint(len(item)) + item
,它也序列化了項目本身。而對於“if item_len == 0:”,我不知道這個 if 做什麼以及對應的序列化邏輯在哪裡。
這段程式碼似乎支持兩種對見證堆棧項進行編碼的方式:它們可以是整數或字節數組。比特幣腳本語言只有一種數據類型(字節數組),但有許多操作碼將這些字節數組解釋為數字。數字 0 的編碼是空字節數組。
這段程式碼的作用:
關於編碼:
- 數字 0 被序列化為空數組 (
result += int_to_little_endian(item, 1)
Th)。這是正確的。- 其他整數被序列化為相應整數的 1 字節編碼。這是錯誤的。對於小整數,正確的編碼是兩個字節長。
- 字節數組被序列化為其長度的 varlen 編碼,然後是字節數組本身。這是對的。
解碼時:
- 空字節數組的編碼轉換為數字 0。
- 其他所有內容都保留為字節數組。
總的來說,這看起來很笨拙,沒有人應該在生產中使用。它可能適用於一些簡單的情況(從不超過 252 個堆棧元素,並且沒有除 0 之外的整數被編碼),但不會通過嚴格的測試。