Script

從腳本中解析比特幣輸入和輸出地址

  • February 1, 2020

有人可以指導我完成組合腳本輸入和輸出的過程,以揭示 base58 編碼的輸入和輸出地址。以塊 728為例。這有兩個原始腳本輸入:

493046022100e26d9ff76a07d68369e5782be3f8532d25ecc8add58ee256da6c550b52e8006b022100b4431f5a9a4dcb51cbdcaae935218c0ae4cfc8aa903fe4e5bac4c208290b7d5d01

and

493046022100a2ab7cdc5b67aca032899ea1b262f6e8181060f5a34ee667a82dac9c7b7db4c3022100911bc945c4b435df8227466433e56899fbb65833e4853683ecaa12ee840d16bf01

每個字節的第一個字節(49 hex = 73 dec)是將 73 個字節壓入堆棧的指令。根據維基,這些輸入腳本中的每一個都包含一個簽名和一個公鑰。

原始輸出腳本是

76a91412ab8dc588ca9d5787dde7eb29569da63c3a238c88ac

轉化為說明

OP_DUP OP_HASH160 OP_PUSHDATA0(20 bytes) 12ab8dc588ca9d5787dde7eb29569da63c3a238c OP_EQUALVERIFY OP_CHECKSIG

所以給定這些資訊,我如何提取輸入地址(如 blockexplorer.com 所示)?這甚至可能嗎?

1Miuw7ifaTYY5qrzKYFcTDiojSFxRfAqwP and 18KrJNtPVu6LWRNPQReqF29iFm7vDhirMk

和輸出地址

12higDjoCCNXSA95xZMWUdPvXNmkAduhWv

輸出地址僅來自wiki 中從步驟 4開始的輸出腳本,如下所示:

首先添加前導零:

0012ab8dc588ca9d5787dde7eb29569da63c3a238c

然後用 sha256 散列(如果你查看 wiki,這實際上是 OP_HASH160 操作的一部分)給出:

e158c4be10913422dadcf1c36843020ebb3ffe9d0cb13fb9e8c0a564a53c7832

然後再次使用 sha256 進行雜湊處理(這裡的 wiki 是錯誤的 - 它說要使用ripemd160,但實際上再次需要 sha256)給出:

96bf1d277213bbcd91145138e4c7ad8dcd6e1de1c39884fcbc1f5a6d4d7aee93

然後抓取該結果的前 4 個字節 ( 96bf1d27) 並將它們粘貼在原始 hash160 公鑰的末尾並帶有前導零:

0012ab8dc588ca9d5787dde7eb29569da63c3a238c96bf1d27

轉換為十進制:

457790304922245030616719694560989441716273193824169172263

base58 編碼給出:

2higDjoCCNXSA95xZMWUdPvXNmkAduhWv

並在前面加上一個 1 以獲得比特幣輸出地址:

12higDjoCCNXSA95xZMWUdPvXNmkAduhWv

現在要弄清楚如何獲取輸入地址…

多虧了這個答案,我們可以看到兩個簽名都以以下格式進行了 der 編碼:

  • 0x30:表示複合結構的頭字節
  • 後面所有內容的 1 字節長度描述符
  • 0x02:表示整數的頭字節
  • r 值的 1 字節長度描述符
  • r 座標,作為大端整數
  • 0x02:表示整數的頭字節
  • s 值的 1 字節長度描述符
  • s 座標,作為大端整數
  • 一個雜湊類型字節

因此,解碼原始腳本簽名:

493046022100e26d9ff76a07d68369e5782be3f8532d25ecc8add58ee256da6c550b52e8006b022100b4431f5a9a4dcb51cbdcaae935218c0ae4cfc8aa903fe4e5bac4c208290b7d5d01
  • 0x49 = OP_PUSHDATA0 - 將 73 個字節壓入堆棧(腳本命令不是簽名值的一部分)
  • 0x30 = 標頭字節
  • 0x46 = 長度描述符(70 字節)
  • 0x02 = 標頭字節
  • 0x21 = r 值長度描述符(33 字節)
  • 00e26d9ff76a07d68369e5782be3f8532d25ecc8add58ee256da6c550b52e8006br 座標為大端整數
  • 0x02 = 標頭字節
  • 0x21 = s 值長度描述符(33 字節)
  • 00b4431f5a9a4dcb51cbdcaae935218c0ae4cfc8aa903fe4e5bac4c208290b7d5ds 座標為大端整數
  • 0x01 = 雜湊類型字節

第二個原始簽名也具有相同的格式:

493046022100a2ab7cdc5b67aca032899ea1b262f6e8181060f5a34ee667a82dac9c7b7db4c3022100911bc945c4b435df8227466433e56899fbb65833e4853683ecaa12ee840d16bf01
  • 0x49 = OP_PUSHDATA0 - 將 73 個字節壓入堆棧(腳本命令不是簽名值的一部分)
  • 0x30 = 標頭字節
  • 0x46 = 長度描述符(70 字節)
  • 0x02 = 標頭字節
  • 0x21 = r 值長度描述符(33 字節)
  • 00a2ab7cdc5b67aca032899ea1b262f6e8181060f5a34ee667a82dac9c7b7db4c3r 座標為大端整數
  • 0x02 = 標頭字節
  • 0x21 = s 值長度描述符(33 字節)
  • 00911bc945c4b435df8227466433e56899fbb65833e4853683ecaa12ee840d16bfs 座標為大端整數
  • 0x01 = 雜湊類型字節

所以這證實了交易輸入中的簽名值實際上根本不包含公鑰。輸入地址實際上來自之前的交易輸出,可以通過目前交易中的輸入雜湊和輸入索引來辨識。

我的解決方案是,我將十六進製字元串傳遞給:

   def publicKeyDecode(pub):
           hash1 = hashlib.sha256(binascii.unhexlify(pub))
           hash2 = hashlib.new('ripemd160', hash1.digest())
           padded = (b'\x00') + hash2.digest()
           hash3 = hashlib.sha256(padded)
           hash4 = hashlib.sha256(hash3.digest())
           padded += hash4.digest()[:4]
           return base58.b58encode(padded)

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