When working with CKB, we are exploring into unvisited wonderland. This means the design choice we made might be problematic at times. For example, dynamic linking in CKB turns out to be something that does more harm than good. Hence we made the decision to deprecate it in favor of exec coming in the next hardfork.
That still leaves one remaining question unsolved: sometimes you have multiple different signatures to verify within a single lock script. With dynamic linking, one can load multiple signature verification algorithm in current VM. Exec, on the other hand, would just use the callee script to replace caller script, the callee script would just exit with a return code, without returning to the caller script. How should we validate multiple different signatures using an exec based design?
In this article, we will explore the idea of chained locks.
A chained lock looks like a normal lock script, you can even use it like a normal lock script. Each chained lock script contains validation code for one particular signature algorithm, for example, we might have chained lock scripts for secp256k1, secp256r1, RSA, Schnorr, BLS, etc. The trick here, lies in the 2 different code pathes that you can execute a chained lock script:
Normal Execution
Recall that the entrypoint for a CKB script, is exactly like a Linux program:
int main(int argc, char* argv[]) {
// actual logic...
}
Normally when CKB executes a lock script, argc
will be 0, while argv
will be completely empty. In this case, a chained lock script executes exactly like a typically lock script: it runs the sighash_all algorithm to calculate signing message, then extract a signature from the first witness in current script group, then performs the actual signature verification algorithm.
Exec Execution
When a script is loaded and executed via exec
syscall, the caller script can provide arguments to the argc
/argv
interface used by the entrypoint main
function. A chained lock script will enter exec execution
mode, when argc
is not zero.
In exec execution
mode, a chained lock script will expect each individual item in argv
to take the following form:
<code hash in hex>:<hash type in hex>:<message 1>:<signature 1>:<message 2>:<signature 2>:...:<message n>:<signature n>
Since argv
contains null-terminated strings, each component of items included in argv
are encoded using hexadecimals. Chained lock script will proceed with the following logic:
- For each message/signature pair included in
argv[0]
, the chained lock script would perform its own signature verification logic. If any message/signature pair results in an error state, the lock script will also exit with an error state. - If
argc
is exactly 1, the chained lock script returns with a success return code. - The chained lock script would locate the cell using
code hash
andhash type
included inargv[1]
. It will then removeargv[0]
fromargvs
, then use the remaining arguments to invokeexec
syscall using binary provided by the located cell.
Usage Behavior
In this new design, one chained lock script for each individual signature verification algorithm, e.g., secp256k1, Schnorr, RSA, etc., will be deployed to Nervos CKB. A dapp can build an entrypoint lock script first, the entrypoint lock script is in charge of general validation of the transaction, then prepare a series of signing items in the above format to sign. Below is such an example:
โโโโโโโโโโโโโโโโโโโ
โ โ
โ Entrypoint Lock โ
โ โ
โโโโโโโโโฌโโโโโโโโโโ
โ
โ
โ
โโโโโโโโโผโโโโโโโโโโ
โ โ
โ RSA โ
โ โ
โโโโโโโโโฌโโโโโโโโโโ
โ
โ
โ
โโโโโโโโโผโโโโโโโโโโ
โ โ
โ secp256r1 โ
โ โ
โโโโโโโโโโโโโโโโโโโ
As shown in the example, then entrypoint lock first calls into the RSA chained lock using exec
, which then calls into secp256r1
chained lock for more verifications. The arguments prepared by the entrypoint lock might look like the following:
<RSA code hash>:<RSA hash type>:<message 1>:<signature 1>
<secp256r1 code hash>:<secp256r1 hash type>:<message 2>:<signature 2>:<message 3>:<signature 3>
Here message/signature pair 1 will be validated via RSA logic, while message/signature pairs 2 & 3 are validated as secp256r1 signatures.
This way, we can decouple locks for dapps from locks for signature verifications. A lock script is also empowered with the ability to support arbitrary, dynamically determined signature verification algorithms.