Scripts
Let's turn our attention back to the script. All unlocking scripts are written in a language called Lurk and share the same signature of a lambda function with a specific set of parameters.
(lambda (locking-params unlocking-params input-index private-params public-params)
;; Must return t (true) or nil (false)
)
Notice the locking-params
parameter. This is the same lockingParams
that was committed to as part of the scriptHash
.
scriptHash = hash(scriptCommitment || lockingParams)
The lockingParams
that are committed to will ultimately get passed in to the unlocking function when the utxo is being
spent and the script will have an opportunity to evaluate the arguments.
Consider the following script:
(lambda (locking-params unlocking-params input-index private-params public-params)
!(import std/crypto/checksig)
(checksig unlocking-params locking-params (car public-params))
)
This script handles a basic transfer. The lockingParams
that is committed to is a public key and the script verifies
a digital signature covering the transaction's sighash
against the committed public key.
The signature gets passed into the script via the unlocking-params
and the sighash
gets passed in by the circuit via
the public-params
.
If we wanted to we could have just hard-coded the public key inside the script instead of putting it in the params.
(lambda (locking-params unlocking-params input-index private-params public-params)
!(import std/crypto/checksig)
(checksig unlocking-params !(list 0x06efea8759a776da6aba3eae8cb8546259dcbf8b972336218eb60ebec93d5136 1c9db35580e3c8d4b5bb2dc33075835d85c4e340845337b6e5f6624aaf1f086a) (car public-params))
)
This works just fine, but it's generally preferred to put variables like this in the locking-params
field instead of hard-coding
them so scripts can be shared and reused easily without having to edit them.
Parameters
The parameters to the unlocking function are as follows:
- locking-params (type
list
): The list of parameters committed to in the script-hash. - unlocking-params (type
list
): A list of unlocking parameters provided when creating the transaction's proof and spending the utxo. If the unlocking parameters make the function returnTrue
, the funds can move. - input-index (type
num
): The index of the input in the transaction currently being evaluated. - private-params (type
cons
): A cons cell containing two elements ― a list ofprivate-input
s and a list ofprivate-output
s.- private-input (type
list
):- amount (type
num
): The amount of coins or tokens of this utxo. - asset-id (type
num
): The asset ID of the utxo. - salt (type
num
): The utxo's salt field. - state (type
list
): The utxo's state field. - commitment-index (type
num
): The index of the utxo in the transaction output commitment set. - inclusion-proof (type
list
): A list of (num
,bool
) cons cells representing the inclusion proof linking the output commitment to the transaction's merkle root. - script (type
lambda
): The script being executed. - locking-params (type
list
): The locking-params for the input. - unlocking-params (type
list
): The unlocking-params for the input.
- amount (type
- private-output (type
list
):- script-hash (type
num
): The output's script-hash. - amount (type
num
): The output's amount. - asset-id (type
num
): The asset ID of the output. - salt (type
num
): The output's salt field. - state (type
list
): The output's state field.
- script-hash (type
- private-input (type
- public-params (type
list
): A list of public parameters. These are all pulled from the body of the transaction as seen by the network.- sighash (type
num
): The transaction's sighash. - nullifiers (type
list
): A list ofnum
nullifiers from the transaction. - txo-root (type
num
): The transaction's txo-root. - fee (type
num
): The fee paid by the transaction. - mint-id (type
num
): If this is a token mint transaction, this is the ID of the token being minted. It will benil
otherwise. - mint-amount (type
num
): This is the amount of tokens being minted if this is a token mint transaction. - public-outputs (type
list
): A list of cons cells of format (num
,list
) representing the output commitment and ciphertext respectively. Note that each 32-byte chunk of ciphertext has the two most significant bits set to zero to fit within the max field element size. - locktime (type
num
): The transaction's locktime field. - locktime-precision (type
num
): The transaction's locktime precision field.
- sighash (type
Examples
Password Script
A user can spend the utxo if they know the password.
locking-params = (hash(<large_random_secret_number>))
unlocking-params = (<large_random_secret_number>)
(lambda (locking-params unlocking-params input-index private-params public-params)
(= (num (commit (car unlocking-params))) (car locking-params))
)
Multisig Script
This is an example of a 2 of 3 multisig. If 2 out of 3 keyholders sign the transaction, the funds will unlock.
The committed locking params contains the threshold and a list of public keys.
The unlocking params contains a list of booleans which controls which public keys are used in the validation plus enough signatures to meet the threshold.
locking-params = (<threshold> <pubkey0-x> <pubkey0-y> <pubkey1-x> <pubkey1-y> <pubkey2-x> <pubkey2-y>)
unlocking-params ((1 0 1) <sig0> <sig1>)
(lambda (locking-params unlocking-params input-index private-params public-params)
!(import std/crypto/checksig)
!(def threshold (car locking-params))
!(def key-selector (car unlocking-params))
!(def pubkeys (cdr locking-params))
!(def signatures (cdr unlocking-params))
!(def sighash !(param sighash))
!(defun validate-sigs (selector sigs keys valid-sigs) (
(if (car selector)
(if (= (car selector) 1)
(if (checksig (car sigs) !(list (car keys) (car (cdr keys))) sighash)
(validate-sigs (cdr selector) (cdr sigs) (cdr (cdr keys)) (+ valid-sigs 1))
nil
)
(validate-sigs (cdr selector) sigs (cdr (cdr keys)) valid-sigs)
)
(>= valid-sigs threshold)
)
))
(validate-sigs key-selector signatures pubkeys 0)
)
Timelock script
Coins cannot be spent until after a certain time has past.
The locking-params contain the locktime and a public key. The unlocking-params a signature.
script-params = (<lock-until-timestamp> <pubkey>)
unlocking-params = (<signature>)
(lambda (locking-params unlocking-params input-index private-params public-params)
!(import std/crypto/checksig)
!(assert (<= !(param locktime-precision) 120))
!(assert (> !(param locktime) (car locking-params))
!(assert (checksig unlocking-params (cdr locking-params) !(param sighash)))
t
)
Hash Timelock Contract
Alice can spend the coins at any time by revealing a hash preimage. Bob can spend the coins after a timelock expires with his public key.
The locking-params contain the locktime, hash, and public key. The unlocking-params a signature or hash preimage and a control bit.
locking-params = (<lock-until-timestamp> <hash> <bob-pubkey>)
unlocking-params = (t <hash-preimage>)
or
unlocking-params = (nil <signature>)
(lambda (locking-params unlocking-params input-index private-params public-params)
!(import std/crypto/checksig)
!(defun validate-hash () (
(= (num (commit (car (cdr unlocking-params)))) (car (cdr locking-params)))
))
!(defun validate-sig () (
!(assert (<= !(param locktime-precision) 120))
!(assert (> !(param locktime) (car locking-params))
!(assert (checksig (cdr unlocking-params) (cdr (cdr locking-params)) !(param sighash)))
t
))
(if (car unlocking-params)
(validate-hash)
(validate-sig)
)
)
HODL Vault
Funds can only be spent after a third party price oracle signs a message attesting to the illium dollar exchange rate reaching a certain amount.
locking-params = (<exchange-rate-target> <oracle-pubkey> <spend-pubkey>)
unlocking-params = (<exchange-rate> <oracle-signature> <spend-signature>)
(lambda (locking-params unlocking-params input-index private-params public-params)
!(import std/crypto/checksig)
!(import std/collections/nth)
!(assert (>= (car unlocking-params) (car locking-params)))
!(assert (checksig (car (cdr unlocking-params)) !(list (nth 1 locking-params) (nth 2 locking-params)) (car unlocking-params)))
!(assert (checksig (car (cdr (cdr unlocking-params))) !(list (nth 3 locking-params) (nth 4 locking-params)) !(param sighash)))
t
)