創建見證簽名:非強制腳本驗證標誌(對於失敗的 CHECK(MULTI)SIG 操作,簽名必須為零)
因此,我正在嘗試按照 bip143 中的說明進行我自己的見證簽名。我有正確驗證範例簽名的程式碼,所以我認為我掌握了基本的簽名和驗證。我有可以正確驗證 bip143 中的簽名的程式碼,並且使用相同的程式碼我可以正確驗證此輸出,所以我不確定如何確定出了什麼問題。我得到的錯誤是
error code: -26 error message: non-mandatory-script-verify-flag (Non-canonical DER signature)
我懷疑我沒有顛倒字節順序,但我真的不確定如何進一步診斷。
----未簽名交易-------- 020000000102303e285bb228344346acb5292397a761103efcd0182c4cf95b4d01b2bdda400000000000ffffffff02400d03000000000016001453f0b90d7be69d4f5f1356908a9e12fef93561df74e0022a010000001600140f54e16bd59da94840741b69ab9b9db99467a85800000000 --------結束未簽名的交易------ dhash (prevouts) = 02303e285bb228344346acb5292397a761103efcd0182c4cf95b4d01b2bdda4000000000 dhash(序列)=ffffffff dhash(輸出)= 400d03000000000000001453f0b90d7be69d4f5f1356908a9e12fef93561df74e0022a0100000000140f54e16bd59da94840741b69ab9b9db99467a858 版本號:02000000 hashPrevouts: 9ebc8589966e2dd13cd64af1835262c2d8e931b4388a8ebe76620814b4f407bd 雜湊序列:3bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e70665044 輸出點:02303e285bb228344346acb5292397a761103efcd0182c4cf95b4d01b2bdda4000000000 腳本程式碼:1976a914992fda25fdf8447092aa223a34bedd3572173d8688ac 金額:00f2052a01000000 nSequence: ffffffff 雜湊輸出 a54e6a8744fa773a5f1c64d7f60d53efd69f8d76e85e7817970a87990874e4c1 nLockTime 00000000 nHashType 01000000 預雜湊圖片:020000009ebc8589966e2dd13cd64af1835262c2d8e931b4388a8ebe76620814b4f407bd3bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e7066504402303e285bb228344346acb5292397a761103efcd0182c4cf95b4d01b2bdda40000000001976a914992fda25fdf8447092aa223a34bedd3572173d8688ac00f2052a01000000ffffffffa54e6a8744fa773a5f1c64d7f60d53efd69f8d76e85e7817970a87990874e4c10000000001000000 **虛線圖像:4f8fce636bb4a44aa6a241e9597bbe54fce3bfe3426a49a94c5b006d42eac007 rawsig:2cbb748d06a1b87c933a3118d8c2a5a0ab02111bae777a9e52ba8571eee2d549e80a6e4cef24a172e64d11aa7b83c8005fe6fc078307b9e663ef9eb14fce05a3 簽名: 304402202cbb748d06a1b87c933a3118d8c2a5a0ab02111bae777a9e52ba8571eee2d5490220e80a6e4cef24a172e64d11aa7b83c8005fe6fc078307b9f66** 腳本公鑰:992fda25fdf8447092aa223a34bedd3572173d86 公開密鑰:0392ff36d0ae9f3a74c0483fd309ff9144972b1dce6d6dfe4d9de474c721b36521 ----簽名交易-------- 0200000000010102303e285bb228344346acb5292397a761103efcd0182c4cf95b4d01b2bdda400000000000ffffffff02400d03000000000016001453f0b90d7be69d4f5f1356908a9e12fef93561df74e0022a010000001600140f54e16bd59da94840741b69ab9b9db99467a8580247304402202cbb748d06a1b87c933a3118d8c2a5a0ab02111bae777a9e52ba8571eee2d5490220e80a6e4cef24a172e64d11aa7b83c8005fe6fc078307b9e663ef9eb14fce05a301210392ff36d0ae9f3a74c0483fd309ff9144972b1dce6d6dfe4d9de474c721b3652100000000 --------結束簽名交易-----
這是在我的 regtest 上執行的。我可以提供人們可能認為會有所幫助的任何其他資訊。在在這裡發布之前,我花了很多時間將所有可能的範例與所有範例進行比較。我知道它說的是非 connical der sig,但我的研究,其他人說當許多不同的事情出錯時就會發生錯誤。此外,當我將簽名與核心簽名的簽名進行比較時,它們的格式與“30440220”完全匹配,然後是 32 字節,然後是 0220,然後是其他 32 字節。唯一不同的是實際的 sig 數據。另外,在使用本機 connical DER 輸出時,我也會遇到同樣的錯誤。一如既往地感謝任何幫助
—–更新—–
所以上面腳本的問題主要是它沒有正確編碼,這意味著如果 s 或 r 大多數 MSB > 0x80 它會將其視為負數,因為它是有符號整數。在本例中,S 的 MSB 為 0xE8,大於 0x80。比特幣有一個政策,它實際上必須小於 ecdsa 曲線值“n”。下面首先是糾正問題的程式碼,我可以驗證簽名(但比特幣核心中仍然存在錯誤)。在python中反轉S的程式碼:
n=115792089237316195423570985008687907852837564279074904382605163141518161494337 s = bytearray(s_bytes) sint = 0 cnt = 31 for bb in s: sint += bb << cnt*8 cnt -= 1 if sint >= n/2: print("Negated") sint = n-sint s = bytearray(sint.to_bytes(32, byteorder='big'))
還不錯。至於 r,比特幣核心只是重新簽名,直到它低於 0x80。如果 der 編碼器正確填充 MSB,則它更高是合法的)。所以也是這樣做的。下面是一個已糾正問題的新事務,儘管我仍然得到相同的錯誤:-/。
----未簽名交易-------- 020000000102303e285bb228344346acb5292397a761103efcd0182c4cf95b4d01b2bdda400000000000ffffffff02400d03000000000016001453f0b90d7be69d4f5f1356908a9e12fef93561df74e0022a010000001600140f54e16bd59da94840741b69ab9b9db99467a85800000000 --------結束未簽名的交易------ dhash (prevouts) = 02303e285bb228344346acb5292397a761103efcd0182c4cf95b4d01b2bdda4000000000 dhash(序列)=ffffffff dhash(輸出)= 400d03000000000000001453f0b90d7be69d4f5f1356908a9e12fef93561df74e0022a0100000000140f54e16bd59da94840741b69ab9b9db99467a858 版本號:02000000 hashPrevouts: 9ebc8589966e2dd13cd64af1835262c2d8e931b4388a8ebe76620814b4f407bd 雜湊序列:3bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e70665044 輸出點:02303e285bb228344346acb5292397a761103efcd0182c4cf95b4d01b2bdda4000000000 腳本程式碼:1976a914992fda25fdf8447092aa223a34bedd3572173d8688ac 金額:00f2052a01000000 nSequence: ffffffff 雜湊輸出 a54e6a8744fa773a5f1c64d7f60d53efd69f8d76e85e7817970a87990874e4c1 nLockTime 00000000 nHashType 01000000 預雜湊圖片:020000009ebc8589966e2dd13cd64af1835262c2d8e931b4388a8ebe76620814b4f407bd3bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e7066504402303e285bb228344346acb5292397a761103efcd0182c4cf95b4d01b2bdda40000000001976a914992fda25fdf8447092aa223a34bedd3572173d8688ac00f2052a01000000ffffffffa54e6a8744fa773a5f1c64d7f60d53efd69f8d76e85e7817970a87990874e4c10000000001000000 散列圖像:4f8fce636bb4a44aa6a241e9597bbe54fce3bfe3426a49a94c5b006d42eac007 rawsig:38d610fa975440d09b501864185e656546015ee98dd8b5b58eb41be547b6a686be784798307a123bdfefce7463b6e7203b69d18950ea95ae684dddb5a9bcaeaf **之前:be784798307a123bdfefce7463b6e7203b69d18950ea95ae684dddb5a9bcaeaf 否定 之後:4187b867cf85edc42010318b9c4918de7f450b5d5e5e0a8d578480d726799292 簽名:3044022038d610fa975440d09b501864185e656546015ee98dd8b5b58eb41be547b6a68602204187b867cf85edc42010318b9c4918de7f450b5d5e5e0a8d5726 腳本公鑰:992fda25fdf8447092aa223a34bedd3572173d86 公開密鑰:0392ff36d0ae9f3a74c0483fd309ff9144972b1dce6d6dfe4d9de474c721b36521** ----簽名交易-------- 0200000000010102303e285bb228344346acb5292397a761103efcd0182c4cf95b4d01b2bdda400000000000ffffffff02400d03000000000016001453f0b90d7be69d4f5f1356908a9e12fef93561df74e0022a010000001600140f54e16bd59da94840741b69ab9b9db99467a85802473044022038d610fa975440d09b501864185e656546015ee98dd8b5b58eb41be547b6a68602204187b867cf85edc42010318b9c4918de7f450b5d5e5e0a8d578480d72679929201210392ff36d0ae9f3a74c0483fd309ff9144972b1dce6d6dfe4d9de474c721b3652100000000 --------結束簽名交易-----
再次感謝您的幫助
好的,想通了!大約一周後:-D。我想發布這個,因為我上週花了學習比特幣核心在 sighash 期間輸出間歇值進行比較。其他任何需要真正了解核心是如何工作的人都希望這真的很有幫助。所以首先是我的具體問題:我的輸出腳本完全不正確,我想我把鎖定和解鎖腳本混淆了,這仍然不應該使簽名無效,但你也注意到範例中的輸出如下:
202cb20600000000 1976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac
好吧,第一個 0x19 不是腳本公鑰的一部分(解碼原始交易的方式顯示它)。所以在它說的描述中
如果 sighash 類型既不是 SINGLE 也不是 NONE,hashOutputs 是所有輸出量(8 字節 little endian)用 scriptPubKey 序列化的雙 SHA256(序列化為 CTxOuts 內的腳本);
你用長度前綴 0x19 散列。我在整個交易中包含了長度,所以它仍然可以很好地解碼,但不是我散列的方式。簡單的誤解。
下面是我如何建構和修改核心(0.21.1)以輸出值來解決這個問題
- 我主要遵循這個範例<https://jonatack.github.io/articles/how-to-compile-bitcoin-core-and-run-the-tests>,基本上安裝了該列表中的大多數依賴項
- 導出 BDB_PREFIX=’/home/ubuntu/btc/bitcoinsource/bitcoin-0.21.1/db4'
- ./configure BDB_LIBS="-L${BDB_PREFIX}/lib -ldb_cxx-4.8" BDB_CFLAGS="-I${BDB_PREFIX}/include" –enable-debug
- 製作
- 注意 –enable-debug 的使用。我認為這是讓您使用 gdb 所必需的嗎?我確實偶爾會在日誌文件中遇到執行緒鎖定錯誤,這會導致它在使用錢包 api 時在調試模式下崩潰。
- 我創建了一個單獨的 bin 文件夾,其中包含一些指向 4 個主要二進制執行檔的符號連結。然後編輯 /etc/environment 以將路徑附加到該文件夾。然後執行“源 /etc/environment”。這使您可以從任何地方執行二進製文件。
- 現在你可以執行 bitcoind -regtest -daemon。從這裡您可以進入建構二進製文件並執行 gdb 的原始碼,並附加到該程序。
- 修改 <bitcoin_dir>/src/script.interpreter.cpp 在頂部,我編寫了一些基本函式來獲取一些不同的變數類型並列印為十六進制。為糟糕的程式碼道歉,我是 C 人,而不是 C++。我創建了一個單獨的日誌文件,所以我沒有弄亂 bitcoind 的主日誌
std::string hexhalftostr(unsigned char val) { 如果 (val <= 9) 返回 std::to_string(val); 否則如果(val ==10) 返回“A”; 否則如果(val ==11) 返回“B”; 否則如果(val ==12) 返回“C”; 否則如果(val ==13) 返回“D”; 否則如果(val ==14) 返回“E”; 否則如果(val ==15) 返回“F”; 否則返回“”; } 無效日誌字節(無符號字元*數據,int len) { std::ofstream 我的文件; myfile.open ("/home/ubuntu/btc/bitcoinsource/bitcoin-0.21.1/special.log", std::ios_base::app); std::string ss = ""; for (int i = 0;i < len;i++) { 字元 lsb,msb; lsb = 0x0f & 數據[i]; msb = 0x0f & (data[i] << 4); ss += hexhalftostr(msb) + hexhalftostr(lsb); } 我的文件 << ss; 我的文件.close(); } 無效 logbigint(uint256 數據) { 日誌字節(data.data(),32); } 無效登錄(無符號整數數據) { 無符號字元數組字節[4]; for (int i = 0; i < 4; i++) arrayOfByte[i] = (數據 << (i * 8)); logbytes(arrayOfByte, 4); } void loglong(無符號長數據) { 無符號字元數組字節[8]; for (int i = 0; i < 8; i++) arrayOfByte[i] = (數據 << (i * 8)); logbytes(arrayOfByte, 8); } 無效日誌文本(標準::字元串 ss) { std::ofstream 我的文件; myfile.open ("/home/ubuntu/btc/bitcoinsource/bitcoin-0.21.1/special.log", std::ios_base::app); 我的文件 << ss; 我的文件.close(); }
- SignatureHash 是我們可以在計算它們時實際輸出它們的函式
logtext("\nn版本:"); 登錄(txTo.nVersion); logtext("\nhashPrevouts:"); logbigint(hashPrevouts); logtext("\nhashSquence:"); logbigint(hashSequence); logtext("\noutpoint:"); logbigint(txTo.vin[nIn].prevout.hash); logint(txTo.vin[nIn].prevout.n); //不知道如何獲取字節 ////logtext("\nscriptCode:"); ////logbigint(scriptCode); logtext("\namount: "); loglong(金額); logtext("\nn序列:"); logint(txTo.vin[nIn].nSequence); logtext("\nhashOutputs:"); logbigint(hashOutputs); logtext("\nnLockTime: "); logint(txTo.nLockTime); logtext("\nhashType:"); 登錄(nHashType);
並且必須替換函式的返回,因為 sha256 雜湊會消耗數據,所以
return ss.GetHash();
變成
uint256 sighash = ss.GetHash(); logtext("\nhashed image: "); logbigint(sighash); logtext("\n"); //return ss.GetHash(); return sighash;
- 我建議設置一個斷點並單步執行“EvalScript”函式。這就是所有魔法發生的地方,並且非常適合教育自己了解實際比特幣腳本的執行方式,因為每個錯誤程式碼都包含幾乎無限的錯誤,我不確定是否有任何其他方法可以在不這樣做的情況下調試程式碼。