Segregated-Witness

交易的 SegWit 部分如何被序列化和解析?

  • July 20, 2022

我正在閱讀程式比特幣。在第 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 之外的整數被編碼),但不會通過嚴格的測試。

引用自:https://bitcoin.stackexchange.com/questions/114537