Script
從腳本中解析比特幣輸入和輸出地址
有人可以指導我完成組合腳本輸入和輸出的過程,以揭示 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 字節)
00e26d9ff76a07d68369e5782be3f8532d25ecc8add58ee256da6c550b52e8006b
r 座標為大端整數- 0x02 = 標頭字節
- 0x21 = s 值長度描述符(33 字節)
00b4431f5a9a4dcb51cbdcaae935218c0ae4cfc8aa903fe4e5bac4c208290b7d5d
s 座標為大端整數- 0x01 = 雜湊類型字節
第二個原始簽名也具有相同的格式:
493046022100a2ab7cdc5b67aca032899ea1b262f6e8181060f5a34ee667a82dac9c7b7db4c3022100911bc945c4b435df8227466433e56899fbb65833e4853683ecaa12ee840d16bf01
- 0x49 = OP_PUSHDATA0 - 將 73 個字節壓入堆棧(腳本命令不是簽名值的一部分)
- 0x30 = 標頭字節
- 0x46 = 長度描述符(70 字節)
- 0x02 = 標頭字節
- 0x21 = r 值長度描述符(33 字節)
00a2ab7cdc5b67aca032899ea1b262f6e8181060f5a34ee667a82dac9c7b7db4c3
r 座標為大端整數- 0x02 = 標頭字節
- 0x21 = s 值長度描述符(33 字節)
00911bc945c4b435df8227466433e56899fbb65833e4853683ecaa12ee840d16bf
s 座標為大端整數- 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)