When developing and debugging sCrypt contracts, checkSig and checkPreimage errors are among the most common and troublesome issues. Although the debugger can help pinpoint the exact location of the error in the source code, figuring out why it failed and how to fix it is generally difficult. Today we are going to talk about how to quickly debug and fix these two types of errors¹.
checkPreimage Failure
Let us take the tokenUtxo contract in the boilerplate project as an example. Its debugging configuration is located in .vscode/launch.json. (Some values are omitted for ease of exposition).
The above configuration specifies that the function for debugging is split, and its parameters are specified in entryMethodParams. It also specifies txContext at the same time. When we start this configuration in the debugger, we find that the following exception was thrown in the Debug Console:
Execution failed with error SCRIPT_ERR_NULLFAIL. Stacktrace: /Users/hero/work/boilerplate/contracts/tokenUtxo.scrypt:14:in 'Token.split'
The message shows that the exception occurred at line 14, and the code is require(Tx.checkPreimage (txPreimage)). We know that there is something wrong with txPreimage. But what is the underlying cause?
The failure here for calling Tx.checkPreimage indicates that the txPreimage passed in the debug configuration parameter entryMethodParams is inconsistent with the result calculated using the various parameters in txContext.
As shown in the graph above, sighash preimage is composed of multiple parts. If the two preimages are inconsistent, some of the fields must be different. Which fields are the problem? Let’s look at the following log example:
----- 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 | 6393778445f442466414464ee3be7cc7Preimage calculated with tx:
0100000028bce...----- CheckPreimage Fail Hints End -----
This log show us more details of the exception for calling checkPreimage and compare the specific differences between the two preimages mentioned earlier. It shows that the MD5 hashes of the scriptCode of the two are different, which means that the scriptCode (corresponding to the locking script of the input) of the two are inconsistent. The root problem is identified. After we update the preimage and recalculate related parts, such as txContext. hex and txContext.opReturn, checkPreimage finally passes.
checkSig Failure
Another common error is failure when calling checkSig, which is usually caused by a wrong signature.
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...
----- CheckSig Fail Hints End -----
The above error message shows the two main points to check when debugging signature errors, namely:
- Check whether the private key used to generate the signature is correct
- Check whether the preimage of the transaction to be signed (automatically calculated base on txContext) is consistent with the passed parameters.
In order to check whether the two preimages are consistent, you can use the toJSON() method of SigHashPreimage to deserialize it and see their internal details similar to the following:
The trick here is that you can insert a piece of code to where the input parameter preimage is generated, compare it with the preimage output in the above CheckSig failure hint, and find out the differences between the two. Your code will look like the following code:
The key is that all fields under the debug configuration txContext attribute will affect the result to calculate preimage, so you need to check one by one to see which ones are distinct when troubleshooting.
Acknowledgement
Original article is by Yiqiang Wang and translation is drafted by Hongfeng Zheng.
[1] The version of the sCrypt IDE used in this article is 0.4.3.