sCrypt 合約開發除錯技巧: 定位及解決 checkSig / checkPreimage 異常
在 sCrypt 合約的開發除錯過程中,最常見也最頭疼的兩個問題就是碰到 checkSig 和 checkPreimage 異常。雖然我們可以在 Debug 過程中定位到原始碼中錯誤的具體位置,但對於為什麼失敗以及如何修復總是感覺一頭霧水。今天我們就來聊聊如何快速定位和修復這兩類問題的一些技巧,希望能對大家有所幫助。
sCrypt boilerplate 專案中包含了一些 sCrypt 合約的具體示例程式碼且在不斷更新中,所以有時可能會碰到配置失效導致無法正常完成 Debug 的情況。下面我們以該專案中的 tokenUtxo 合約為例來看看如何定位及解決這兩類問題。
注意:本文中使用的 sCrypt 外掛版本為 0.4.3。
checkPreimage 異常
首先看下 tokenUtxo.scrypt
的 Debug 啟動配置(位於 .vscode/launch.json 中,為方便檢視故省略了部分數值):
{
"type": "scrypt",
"request": "launch",
"name": "Debug tokenUtxo",
"program": "${workspaceFolder}/contracts/tokenUtxo.scrypt",
"constructorParams": "",
"entryMethod": "split",
"entryMethodParams": "Sig(b'304402200...'), PubKey(b'0251c866a29a93b6eb51197be1e9ccdcc5e822caa69c7593905347e3ec310bebad'), 60, 22222, PubKey(b'0291e61f25a92c94103f0f4ef1f70bf3582f44cff95d497ceb3efdb945f4ce3cbe'), 40, 22222, SigHashPreimage(b'0100000028bc...')",
"txContext": {
"hex": "01000000015884e5...",
"inputSatoshis": 100000,
"opReturn": "029a77564154c6ed13ffcc387342692480e7e15f2e3ad832cf2ac6de1c3ccf28230a5a",
"inputIndex": 0
}
}
上述配置指定了 Debug 的啟動函式為 split
,並在 entryMethodParams
中指定了若干啟動引數;同時在 txContext
中指定了 tx 相關的上下文引數。 當我們在 vscode 中啟動這個配置準備進行 Debug 時,卻發現 Debug Console 裡輸出了以下異常:
Execution failed with error SCRIPT_ERR_NULLFAIL.
Stacktrace:
/Users/hero/work/boilerplate/contracts/tokenUtxo.scrypt:14:in 'Token.split'
這裡顯示的異常位置是在 14 行,其程式碼是 require(Tx.checkPreimage(txPreimage));
,由此可以推斷是 txPreimage 出了問題,但具體是什麼原因呢?
在之前的文章中,我們介紹過 Sighash Preiamge,它被稱為交易的原像,可由交易 tx 計算出來。這裡的 Tx.checkPreimage
失敗,說明在啟動配置引數 entryMethodParams
中傳入的數值與使用 txContext
中各項引數所計算出的結果不一致。
如上圖所示,Sighash Preimage 由多個部分組合而成,如果兩個原像不一致,一定是其中某些欄位不相同。究竟是哪個欄位的問題呢?讓我們再來看看接下來的日誌:
----- CheckPreimage Fail Hints Begin -----
You should check the differences in detail listed below:
Fields with difference | From preimage in entry method params | From preimage calculated with tx
md5(scriptCode) | 148dd2b3fcc09d6baf15c9fcf5d961d3 | 6393778445f442466414464ee3be7cc7
Preimage calculated with tx:
0100000028bce...
----- CheckPreimage Fail Hints End -----
這段日誌為我們提供了關於 checkPreimage
異常的更多細節,主要是對比了前文提到的兩個原像的具體差異。這裡顯示二者的 scriptCode
的 MD5 值有區別,即說明二者本身的 scriptCode
(對應 input 的鎖定指令碼)是不一致的。
至此基本找到了問題的所在,鑑於近期的一些改動,推測是 entryMethodParams
和 txContext
的某些配置引數可能失效了。於是重新計算並且更新了 preimage
、txContext.hex
、txContext.opReturn
等引數後,Debug 終於得到了正確的結果。
checkSig 異常
還有是一類常見的錯誤是 checkSig
異常,通常是由於簽名問題導致的。這裡我們可以通過隨意修改下 senderSig
的引數值來模擬一個簽名錯誤問題,之後再啟動 Debug 就可以看到如下提示資訊:
Execution failed with error SCRIPT_ERR_NULLFAIL.
Stacktrace:
/Users/hero/work/boilerplate/contracts/tokenUtxo.scrypt:25:in 'Token.split'
----- CheckSig Fail Hints Begin -----
You should make sure the following check points all passed:
1. private key used to sign should be corresponding to the public key 028f46cb8ec957dcda049ac549fc46d451e0095a5b6f95950bc58830a7dc21167c
2. the preimage of the tx to be signed should be 0100000028bcef7e73248aa273...
上述提示資訊涵蓋了解決簽名錯誤時的主要檢查點,即:
1. 確定生成簽名所使用私鑰是否正確;
2. 確認待簽名 tx 的 preimage(根據 txContext 自動計算得到)與傳入引數是否一致。
為了對比兩個 preimage 是否一致,可以使用 SigHashPreimage
的 toJSON()
方法檢視其內部細節,得到類似下面的結果:
{
nVersion: 1,
hashPrevouts: '1029c58f269f3a1f0165149921e7a726d13bf29883f80ec3bb08c75fabaa06ad',
hashSequence: '3bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e70665044',
outpoint: {
hash: '5884e5db9de218238671572340b207ee85b628074e7e467096c267266baf77a4',
index: 0
},
scriptCode: 'fd860f5101400...',
amount: 100000,
nSequence: 4294967295,
hashOutputs: '1029c58f269f3a1f0165149921e7a726d13bf29883f80ec3bb08c75fabaa06ad',
nLocktime: 0,
sighashType: 'SigHash.ALL | SigHash.FORKID'
}
這裡的小技巧是:在生成輸入引數 preimage 的地方插入一段程式碼,與上述異常提示中輸出的 preimage 進行對比,進而找出二者可能存在的差異。如以下程式碼所示:
const { getPreimage, SigHashPreimage, signTx } = require('scryptlib');
...
const preimage = getPreimage(tx_, token.lockingScript.toASM(), inputSatoshis, inputIndex)
const sig = signTx(tx_, privKey, token.lockingScript.toASM(), inputSatoshis)
// compare two preimages for debugging purpose
const preimage2 = new SigHashPreimage('fd860f51014001760...'); // use hex from checkSig fail hints
console.log(preimage2.toString() === preimage.toString())
console.log(preimage.toJSON())
console.log(preimage2.toJSON())
這裡需要再次提醒大家的是,啟動配置 txContext
屬性下的欄位都會影響 preimage
的計算,所以在排查問題時需要逐一對比確認是否一致。
相關文章
- 開發常見錯誤及解決方案
- Ionic異常及解決
- native程式異常crash定位解決方案
- 除錯技巧 —— 如何利用windbg + dump + map分析程式異常除錯
- CentOS 常見異常及解決辦法CentOS
- Flask開發技巧之異常處理Flask
- 智慧合約開發環境搭建及Hello World合約開發環境
- 9種常見的Android開發錯誤及解決方案Android
- 開發NEO智慧合約的實用技巧
- 9個常見的Android開發錯誤及解決辦法Android
- remix合約除錯設定pinfoREM除錯
- 量化合約系統開發(詳解開發)丨合約量化系統開發(說明及案例)
- web開發技巧-網頁排版佈局常見問題及解決辦法Web網頁
- 合約量化/量化合約開發原始碼功能,量化合約/合約量化系統開發(開發策略)及案例詳情原始碼
- php錯誤及異常捕捉PHP
- 合約量化系統開發(成熟及策略)丨合約量化開發(原始碼專案)原始碼
- 還在斷點除錯?教你四種除錯技巧讓你快速定位錯誤!斷點除錯
- XCode除錯時丟擲異常,定位到某一行程式碼XCode除錯行程
- 現貨期權合約量化/量化合約/秒合約/永續合約/交易所繫統開發(開發案例及原始碼)原始碼
- 總結一篇shell除錯技巧及常見的指令碼錯誤除錯指令碼
- NEO智慧合約除錯流程說明除錯
- Redis快取的主要異常及解決方案Redis快取
- 合約量化系統丨合約量化系統開發策略及詳情丨合約量化開發原始碼邏輯原始碼
- Java常見異常及解釋Java
- Tomcat常見異常及解決方案程式碼例項Tomcat
- 動態除錯及LLDB技巧集合除錯LLDB
- 合約跟單開發說明丨合約跟單系統開發(方案及策略)丨合約跟單原始碼版原始碼
- 合約量化開發上線版,合約量化系統開發技術邏輯及詳細方案,合約量化原始碼原始碼
- 合約跟單開發需求丨合約跟單系統開發(技術方案)丨合約跟單原始碼及功能原始碼
- [Java開發之路](5)異常詳解Java
- 量化交易/量化合約/合約量化/秒合約/永續合約/合約跟單/交易所繫統開發(策略及原始碼)原始碼
- Hadoop常見錯誤及解決方案Hadoop
- 智慧合約鏈遊開發詳情丨智慧合約鏈遊系統開發(開發方案)及原始碼分析原始碼
- Java 常見異常及趣味解釋Java
- uni-app開發 常見異常和解決辦法APP
- 異常機制與遮蔽除錯程式碼除錯
- 阿里雲異常流量及異常網路連線的安全解決過程阿里
- 合約量化系統開發(開發策略及詳細)丨量化合約系統開發(開發原始碼及說明)原始碼