Skip to content

Multisignature

Multisignature (often called "multisig") refers to requiring multiple keys to spend funds in a Nexa transaction, rather than a single signature from one key. It has a number of applications and allows users to divide up responsibility for possession of coins.

Multisignature scripts set a condition where N public keys are recorded in the script and at least M of those must provide signatures to unlock the funds. This is also known as M-of-N multisignature scheme, where N is the total number of keys and M is the threshold of signatures required for validation. The signatures used must be Schnorr signatures.

Public Multisignature: OP_CHECKMULTISIG(VERIFY)

Multisig schemes can be built with the opcodes OP_CHECKMULTISIG and OP_CHECKMULTISIGVERIFY, two opcodes of the Nexa scripting language. OP_CHECKMULTISIGVERIFY has the same implementation as OP_CHECKMULTISIG, except OP_VERIFY is executed afterward. Since it makes no sense for these operations to fail (in valid transactions), caveats have been added to failing OP_CHECKMULTISIG. It is therefore recommended that OP_CHECKMULTISIGVERIFY be used.

The minimal locking script using OP_CHECKMULTISIGVERIFY is:

M <pubkey1> ... <pubkeyN> N CHECKMULTISIGVERIFY

This script should be placed inside a script template "constraint" (or locking) script. M is a number denoting the minimum number of valid signatures required for success. Pubkey1 through N are the constraining public keys, and N is a number denoting how many public keys are pushd onto the stack.

The unlocking script corresponding to the previous locking script is:

<dummy> <sig1> ... <sigM>

Upon script execution, this will act like:

<checkbits> <sig1> ... <sigM> M <pubkey1> ... <pubkeyN> N CHECKMULTISIG

Signatures are checked according to the checkbits field:

  • If the least significant bit of checkbits is set, then the first signature is checked against the first public key.
  • If it is not but if the second least significant bit is set, then the first signature is checked against the second public key.
  • Once the first signature has been checked against a matching public key, checkbits is bit-shifted to the right (checkbits := checkbits >> 1).
  • The process is repeated until all signatures have been checked or not enough public keys remain to produce a successful result.
  • All signatures need to match a public key, otherwise false is returned.
  • If the final checkbits value is non-zero, then false is returned.

Because public keys are not checked again if they fail any signature comparison (in both cases), signatures must be placed in the unlocking script using the same order as their corresponding public keys were placed in the locking script (P2MS) or redeem script (P2SH).

Note that the checkbits element is encoded as a byte array of length floor((N + 7)/8) (the shortest byte array that can hold N bits) and must have exactly M bits set to ensure that all signatures are checked against public keys.

To know more about the Schnorr mode, see the specification.

Example

The most commonly used scheme is the 2-of-3 multisig scheme:

2 <pubkeyA> <pubkeyB> <pubkeyC> 3 CHECKMULTISIGVERIFY

where 2 out of 3 participants (Alice, Bob and Carol) can sign a transaction from the shared account.

Let's say Alice and Carol want to spend funds. They have to sign the transaction with the Schnorr signature algorithm and build the following script:

5 <sigA> <sigC>

The value of the dummy element is 5, whose binary representation is 0b101. This ensures that Alice's signature (sigA) is checked against her public key (pubkeyA), that Carol's signature (sigC) is not checked against Bob's public key (pubkeyB) but against her public key (pubkeyC).

Interactive Example

The following example pays to a 2 of 3 multisig script template address, and then spend it. The source for this example is available (libnexakotlin:multisig.kt)[https://gitlab.com/nexa/libnexakotlin/-/blob/main/libnexakotlin/src/commonTest/kotlin/multisig.kt].

First create some 3 wallets:

Wallet 0 secret: wash parent cinnamon fame judge tool arrange thing permit gorilla include glow (master private key: e54597b4928da8ea2e81b0aef566b9fbc56713f23d82766f26ae61eadc17cef8707227108bd9ae3b9f14c096cb4d29e4a4872eba5dc6e0195e621b631ca9c851)
Wallet 1 secret: piano beauty sphere nation expire music human verb lamp clarify abandon apology (master private key: cfc7e1e8cd74b6e674b4078b03a784235beb428576f92228a30017fdb8e224a62c69efd0510c0d2bdb19cb16cd5e6f40772aaa7f3aa34bb71418c3a8704191e0)
Wallet 2 secret: feed winner shoe author grunt bunker sibling swear raccoon bone hub other (master private key: 7895a0d2b859d13b042f9f0d7295e00c585a46c8832a51f4d7eaa002a04bb280bc843f239c3768379dbee5080886f40451c275fa8a4cf740975455d511f55b51)
Wallet 0 destination 0 secret: 86a3a83a0dbae7542818c0db08d707950d5abd3f96b828cb270d85aa6e4088a9
Wallet 1 destination 0 secret: 60b684fd412b8e2d0fca01c35f02c06fe732ef1df3842898b9f5d851a7d3f063
Wallet 2 destination 0 secret: fbeaaa00ec7584fcce2140cd46a7473288095f30e02ca88605ec0871e8852427

Next produce the multisig destination (address) from these secrets:

Multisig Destination:
  template is 5221032e76fb3a30bf8cadfce0ce90a6b63827dbd7971f4e8ffa726a6606054d6894852103c72d1b8e6a35f20d3922fff7fc49c16ef388181215f6a505feb1abfeb00caef12103fa38bb2737ce3ca6eafe034c24eb2034df5cfd0992a056a25aacd49cef6da7e453af 
    NexaASM: 
    2 
    032e76fb3a30bf8cadfce0ce90a6b63827dbd7971f4e8ffa726a6606054d689485h 
    03c72d1b8e6a35f20d3922fff7fc49c16ef388181215f6a505feb1abfeb00caef1h 
    03fa38bb2737ce3ca6eafe034c24eb2034df5cfd0992a056a25aacd49cef6da7e4h 
    3 
    CHECKMULTISIGVERIFY
  locking script is: 00146f8c7fe1ea9c2d425125b099307b402e6d7e2acc00 
    NexaASM: 
    0 
    6f8c7fe1ea9c2d425125b099307b402e6d7e2acch
    0
In Nexa a locking script can be translated directly to an address:
  address is: nexareg:nqtsq9r033l7r65u94p9zfdsnyc8kspwd4lz4nqqckntf3j9

In a Regtest sandbox, this address was funded with this transaction (view it in (the Nexa explorer)[https://explorer.nexa.org/decoder] . It is the first output -- the address does not match because the explorer is assuming mainnet):

000100076b2ed8079c240e04bbb844b62aca9c459ae44b3c8534c14e62468fde41ae47642221027731f0e8910d76ba2d06759bbf2433b955a2e7a857f19319a6c6426b633d429340c981fd9bc8e0f4a269feb92a0840e470381d1eebdefefdb456d3ee166403637851621af972511ee69677fe659b4bcdbea4b51c8cbb3dfd89bc15bdab769d179efeffffffca9a3b0000000000020140420f00000000001700146f8c7fe1ea9c2d425125b099307b402e6d7e2acc0001af572c00000000001700511447e1ac5ab24c2c71285d88db2e4998cb88919620f4050000

To spend this transaction, the following transaction can be used:

00010078922a15766284b86f19078b04713d6da3bf04c357354a0f812f0f2f646e88c4ee4c695221032e76fb3a30bf8cadfce0ce90a6b63827dbd7971f4e8ffa726a6606054d6894852103c72d1b8e6a35f20d3922fff7fc49c16ef388181215f6a505feb1abfeb00caef12103fa38bb2737ce3ca6eafe034c24eb2034df5cfd0992a056a25aacd49cef6da7e453af53400cae79c20e83eeeed63ca1b8628f484f8a9bbcc4bf976c6e1cf9ec3fcfd6564da23ad87f6c04687206f97805a12f99e96edd6f6911ac4a5cf6daf8497aeed1ce4079999cd8ce81341734308069cfe506ee6a98e463bdb7925f40c8eafbad220dbf13f84ea9cb8086111962ff29364ca76470ad4867c050da7ad8cad04e0a2aa8c1ffffffff40420f000000000001014c400f0000000000170051140bf717e9414132307797633612ace59779db184f00000000

You can step through this spend via the (Nexa online script debugger)[http://debug.nexa.org/tx/00010078922a15766284b86f19078b04713d6da3bf04c357354a0f812f0f2f646e88c4ee4c695221032e76fb3a30bf8cadfce0ce90a6b63827dbd7971f4e8ffa726a6606054d6894852103c72d1b8e6a35f20d3922fff7fc49c16ef388181215f6a505feb1abfeb00caef12103fa38bb2737ce3ca6eafe034c24eb2034df5cfd0992a056a25aacd49cef6da7e453af53400cae79c20e83eeeed63ca1b8628f484f8a9bbcc4bf976c6e1cf9ec3fcfd6564da23ad87f6c04687206f97805a12f99e96edd6f6911ac4a5cf6daf8497aeed1ce4079999cd8ce81341734308069cfe506ee6a98e463bdb7925f40c8eafbad220dbf13f84ea9cb8086111962ff29364ca76470ad4867c050da7ad8cad04e0a2aa8c1ffffffff40420f000000000001014c400f0000000000170051140bf717e9414132307797633612ace59779db184f00000000?idx=0&utxo=0140420f00000000001700146f8c7fe1ea9c2d425125b099307b402e6d7e2acc00]

Private Multisignature

N-of-N multisig schemes can also be implemented in P2PKT outputs, using the Schnorr aggregation property. By combining the public keys of the cooperating parties, a combined public key can be created and used in a locking script. When spending the output, the parties can jointly create a signature that will validate as a normal Schnorr signature for the combined public key in the locking script. For more details, see MuSig.