在 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 的計算,所以在排查問題時需要逐一對比確認是否一致,
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/158499.html
標籤:其他
下一篇:Raft 協議原理詳解
