Multi-sig#

Concordium natively supports multi-sig accounts. Each account address on Concordium is controlled by one or several credentials (real-world identities e.g. a private person with a passport or a company with a registration) and each credential has one or several public-private key pairs.

Note

A public-private key pair are related keys used for asymmetric cryptography. The private key is used to sign/authorize an action related to the account. NEVER share your private key with anyone. In contrast, the public key is shared with other entities to verify that a signature is generated by the associated private key.

Each account has an AccountThreshold (number of credentials needed to sign the transaction initiated by that account) and one or several SignatureThresholds (number of signatures needed for a specific credential so that this credential is considered to have signed the transaction initiated by that account).

The concordium-client tool (version 6.3.0 or above) facilitates creating, signing, and sending of multi-sig transactions for multi-sig accounts. This page will go through the main steps as follows:

  • Create a multi-sig account by adding keys via an on-chain transaction to an account.

  • Configure the concordium-client tool to use a multi-sig account.

  • Create a multi-sig transaction with the concordium-client tool.

  • Add an additional signature to a multi-sig transaction with the concordium-client tool.

  • Send a multi-sig transaction with the concordium-client tool on-chain.

Create a multi-sig account#

When you create an account in one of the Concordium wallets, it is a basic account by default that has exactly one credential and exactly one public-private key pair for that credential. A default account can be converted into a multi-sig account.

Note

The desktop wallet is the only wallet currently that has its independent flow of creating a multi-sig account via its frontend and generating multi-sig transactions. The two guides multi-sig accounts and multi-sig transactions will explain the flows for the desktop wallet. The multi-sig transaction file, as generated by the desktop wallet, is currently NOT compatible with the file generated by the concordium-client tool. You need to add additional signatures and send the transaction on-chain with the wallet/tool the file was initially generated with. The intention is to use the file format of the concordium-client tool in all Concordium tooling related to multi-sig signing in the future and to replace the desktop wallet’s file format.

To add additional keys to an existing account on Concordium using the concordium-client tool, you can either:

Add an additional public-private key pair to your current credential
  • Step 1: Create an additional Ed25519 public-private key pair.

For testing purposes, we recommend the frontend to generate an un-safe additional key pair.

For production purposes, we recommend using the following command to generate a safe additional key pair:

$openssl genpkey -algorithm ed25519 -out private.pem

The keys can be output in a hex format in the console as follows:

$openssl pkey -inform pem -in private.pem -noout -text

This will output your public and private key pairs in a hex format in the console as follows:

ED25519 Private-Key:
priv:
   0f:6e:.................................4e:a3:
   98:f6:.................................61:39:
   0b:2a
pub:
   aa:bb:79:18:3d:af:e2:6e:fc:5b:81:74:f1:16:37:
   51:f2:25:d4:64:f7:72:8a:de:48:c0:28:d3:e6:aa:
   1b:09
  • Step 2: Find the credential registration ID of your account that we are updating keys for.

Run the following command where you replace <account-name-or-account-address> with your account.

$concordium-client account show <account-name-or-account-address> \
   --grpc-port <grpc-port-of-server> \
   --grpc-ip <grpc-ip-url-to-node>

Using the node connection to the hosted testnet node as an example:

$concordium-client account show <account-name-or-account-address> \
   --grpc-port 20000 \
   --grpc-ip node.testnet.concordium.com

This will output something like:

Address:                3G5srtaeRvy2uzyaiheY3ZZ6nqTkDg1kRm59xWVnFo8CoEiZ6Y
Balance:                1001736.135182 CCD
Nonce:                  16
Encryption public key:  b14cbfe44a02c6b1f78711176d5f437295367aa4f2a8c2551ee10d25a03adc69d61a332a058971919dad7312e1fc94c5a411f21ab3b0962c6fb664212e41769e83c82288515f16ca72c86f8a03e87531cae46394bffc63ab6168432903d79fb0

Baking or delegating stake: no

Credentials:
* <credential-registration-id>:
   - Index: 0
   - Expiration: Jun 2023
   - Type: normal
   - Revealed attributes: none

We require the <credential-registration-id> output for the next step.

  • Step 3: Send a transaction to update the keys of your account on-chain.

$concordium-client account update-keys ./update-keys.json \
   --credId <credential-registration-id> \
   --sender <account-name-or-address> \
   --grpc-port <grpc-port-of-server> \
   --grpc-ip <grpc-ip-url-to-node>

Using the node connection to the hosted testnet node as an example:

$concordium-client account update-keys ./update-keys.json \
   --credId <credential-registration-id> \
   --sender <account-name-or-address> \
   --grpc-port 20000 \
   --grpc-ip node.testnet.concordium.com

where update-keys.json is a file of the following format:

{
   "keys": {
      "0": {
         "verifyKey": "<Hex encoding without '0x' of the public key already on-chain>"
      },
      "1": {
         "verifyKey": "<Hex encoding without '0x' of the public key from your newly generated key pair>"
      }
   },
   "threshold": 2
}

This command sends a transaction on-chain to replace the current key tied to the account with the keys in the update-keys.json file. The above example file adds two keys to your credential 0 of your account and sets the threshold (the minimum number of keys you specify to sign a transaction) to 2 for this credential. The above example generates a 2-out-of-2 multi-sig account where the key 0:0 (credentialIndex : keyIndex) is your old public key and the key 0:1 is your newly generated key.

The above file specifies all keys that are associated with your account for a given credential after the update and the threshold can be set accordingly to a number between 1 and the number of keys.

Add an additional credential with at least one public-private key pair
  • Step 1: Create an additional credential.

We recommend using the flow in the desktop wallet to create a credential file. Alternatively, you can clone the project and run its command locally to generate a credential file.

  • Step 2: Send a transaction to add a credential to your account on-chain.

$concordium-client account update-credentials \
   --new-credentials new-credential.json \
   --new-threshold <number-of-credential-to-sign> \
   --sender <account-name-or-address> \
   --grpc-port <grpc-port-of-server> \
   --grpc-ip <grpc-ip-url-to-node>

Using the node connection to the hosted testnet node as an example:

$concordium-client account update-credentials \
   --new-credentials new-credential.json \
   --new-threshold <number-of-credential-to-sign> \
   --sender <account-name-or-address> \
   --grpc-port 20000 \
   --grpc-ip node.testnet.concordium.com

where new-credential.json is the file from the previous step.

This command sends a transaction on-chain to add an additional credential to your account. The <number-of-credential-to-sign> specifies the minimum number of credentials that are needed to sign the transaction after the update.

Configure the tool to use a multi-sig account#

To configure the concordium-client tool to use a multi-sig account, you can either:

  • pass in the signing keys via a file every time you sign a transaction (Option 1).

  • configure the concordium-client tool once to include the additional keys in its local key directory (Option 2).

To view the key directory path that the concordium-client tool is using, run the following command:

$concordium-client config show

The goal is to add additional keys for your account to the local key directory of the concordium-client tool in this section (Option 2). The guide for the first option is in the following section.

Import the keys by adapting the key export file format from the browser wallet

We recommend that you export a key file from a random account on the browser wallet and use this JSON file as a template to generate a corresponding JSON file for the keys of the account you want to update.

Note

You can read up on how to export a key file from the browser wallet here.

Your adapted browser_wallet.export file should look similar to the following browser wallet export template which has two keys for its credential 0 and a threshold of 2 for this credential:

{
   "type": "concordium-browser-wallet-account",
   "v": 0,
   "environment": "testnet",
   "value": {
      "accountKeys": {
         "keys": {
            "0": {
               "keys": {

                  // "E.g. Add your key already on-chain at keyIndex 0."

                  "0": {
                     "signKey": "<Key_0_Private_Key_Without_0x_Prefix>",
                     "verifyKey": "<Key_0_Public_Key_Without_0x_Prefix>"
                  },

                  // "E.g. Add your newly generated key at keyIndex 1."

                  "1": {
                     "signKey": "<Key_1_Private_Key_Without_0x_Prefix>",
                     "verifyKey": "<Key_1_Public_Key_Without_0x_Prefix>"
                  }
               },

               // "E.g. Update the threshold to 2."

               "threshold": 2
            }
         },
         "threshold": 1
      },
      "credentials": {
         "0": "97f325c9f86066ab0c80ff879c21629eb67818841940869308d6a72886d18f8668e62e43ad228fdcbda245d0722454df"
      },
      "address": "4jxvYasaPncfmCFCLZCvuL5cZuvR5HAQezCHZH7ZA7AGsRYpix"
   }
}

You can import the keys of this browser_wallet.export file and associate it to your account as follows:

$concordium-client config account import browser_wallet.export \
   --name <choose-a-name-for-your-account>

A summary of the achievements from this section:

  • We exported a key file from the browser wallet.

  • We added additional keys to the file format.

  • We imported the key file to the local key directory of the concordium-client tool.

Assign an already imported key to your account

If you already imported the new key to concordium-client but it is associated with a different account (e.g. because the public-private key pair was generated by creating a new account on Concordium which was imported to concordium-client and this key pair was re-used in your multi-sig account), you can look up the key in the local key directory of the concordium-client tool and associate the file’s content to your multi-sig account.

Note

You can read up on how to view the key directory of the concordium-client tool here.

Note

We do not recommend to re-use keys on different accounts in production. Instead, use a newly generated key pair when adding additional keys to an account.

You can update the keys of your account by running the following command:

$concordium-client config account update-keys \
   --keys new-keys.json \
   --account <account-name-or-address>

where new-keys.json contains the content of one of the key files in the stored local key directory of the concordium-client tool that you want to reuse. The new-keys.json file has the following format:

{
   "cidx": {
      "kidx": {
         "encryptedSignKey": {
            "metadata": {
               "encryptionMethod": "AES-256",
               "iterations": ...,
               "salt": ...,
               "initializationVector": ...,
               "keyDerivationMethod": "PBKDF2WithHmacSHA256"
            },
            "cipherText": ...
         },
         "verifyKey": ...,
         "schemeId": "Ed25519"
      },
      ...
   },
   ...
}

For example, when you want to add a key for your credentialIndex 0 and the keyIndex 1 of your account, use the following new-keys.json file format:

{
   "0": {
      "1": {
         "encryptedSignKey": {
            "metadata": {
               "encryptionMethod": "AES-256",
               "iterations": ...,
               "salt": ...,
               "initializationVector": ...,
               "keyDerivationMethod": "PBKDF2WithHmacSHA256"
            },
            "cipherText": ...
         },
         "verifyKey": ...,
         "schemeId": "Ed25519"
      },
      ...
   }
}

A summary of the achievements from this section:

  • We looked up a key file from the local key directory of the concordium-client tool.

  • We created a new-keys.json file containing the looked-up key.

  • We updated the keys in the local key directory of the concordium-client tool with the new-keys.json file.

Note

These commands update the keys in the key directory of your local concordium-client tool. No transaction is sent on-chain.

Create a multi-sig transaction#

Multi-sig transactions can be created for all active transaction types which are currently: DeployModule, InitContract, UpdateContract, Transfer(CCD), TransferWithMemo, TransferWithSchedule, TransferWithScheduleAndMemo, ConfigureDelegation, ConfigureBaker, RegisterData, UpdateCredential, and UpdateCredentialsKeys.

To create a multi-sig transaction, execute a transaction-creating command and add an --out flag. This flag will cause concordium-client to output the multi-sig transaction into the specified file instead of sending it on-chain.

If you omit the --signers flag, concordium-client will output a transaction signed with all keys associated with the account as present in the local key directory. If you want to sign with specific keys from the local key directory, you can specify some of them with the --signers flag (e.g. --signers "0:0,0:1").

For example, to create a multi-sig transaction to send 1 CCD to an account on testnet using the local key “0:0” to sign, run the following command:

$concordium-client transaction send \
   --receiver 4bbdAUCDK2D6cUvUeprGr4FaSaHXKuYmYVjyCa4bXSCu3NUXzA \
   --amount 1 \
   --out ./transaction.json \
   --energy 5000 \
   --sender 4jxvYasaPncfmCFCLZCvuL5cZuvR5HAQezCHZH7ZA7AGsRYpix \
   --signers "0:0" \
   --expiry "24h" \
   --grpc-port 20000 \
   --grpc-ip node.testnet.concordium.com

Note

Choose an expiryTime with the --expiry flag for the transaction that takes into account the time it takes to gather all signatures by the different entities. The expiration time of a transaction is specified as a relative duration (e.g. “30s”, “5m”) or a UNIX timestamp. If the expiryTime has passed, the transaction is no longer valid.

The multi-sig transaction is outputted into the transaction.json file and has the following format:

{
   "energy": 5000,
   "expiryTime": 1716995242,
   "nonce": 46,
   "payload": {
      "amount": "1000000",
      "toAddress": "4bbdAUCDK2D6cUvUeprGr4FaSaHXKuYmYVjyCa4bXSCu3NUXzA",
      "transactionType": "transfer"
   },
   "signature": {
      "0": {
         "0": "3099534c5f32daf64dc40b7a0013979b9b74b167d259fc787a363ed2db7f1bcdafdcf06e166b2d915c7f29043186b3015a6064755bf3c3733bca2151b2b19c04"
      }
   },
   "signer": "4jxvYasaPncfmCFCLZCvuL5cZuvR5HAQezCHZH7ZA7AGsRYpix",
   "version": 1
}

If you want to sign the transaction with keys that are not in the local key directory, you need to provide the keys as a separate file with the --keys flag. Keys provided with a flag take precedence and no lookup of local keys in the key directory is performed.

For example, to create a multi-sig transaction to send 1 CCD to an account on testnet using keys for signing from a file, run the following command:

$concordium-client transaction send \
   --keys ./keypair.json \
   --receiver 4bbdAUCDK2D6cUvUeprGr4FaSaHXKuYmYVjyCa4bXSCu3NUXzA \
   --amount 1 \
   --out ./transaction.json \
   --energy 5000 \
   --sender 4jxvYasaPncfmCFCLZCvuL5cZuvR5HAQezCHZH7ZA7AGsRYpix \
   --signers "0:0" \
   --grpc-ip node.testnet.concordium.com

where keypair.json is a file of the following format:

{
   "cidx": {
      "kidx": {
         "encryptedSignKey": {
            "metadata": {
               "encryptionMethod": "AES-256",
               "iterations": ...,
               "salt": ...,
               "initializationVector": ...,
               "keyDerivationMethod": "PBKDF2WithHmacSHA256"
            },
            "cipherText": ...
         },
         "verifyKey": ...,
         "schemeId": "Ed25519"
      },
      ...
   },
   ...
}

Note

You can read up on how to view the key directory of the concordium-client tool here. The format of the keypair.json file is the same as the format of the key files in the local key directory of the concordium-client tool.

If you want to create a multi-sig transaction with no signatures at all, you can use an empty keypair.json file of the following format:

{}

Add an additional signature to a multi-sig transaction#

For example, to sign the multi-sig testnet transaction in the transaction.json file (created in the previous section) on a potentially different device by a different entity, run the following command:

$concordium-client transaction add-signature ./transaction.json \
   --grpc-port 20000 \
   --grpc-ip node.testnet.concordium.com

Note

All keys (as present in the local key directory) associated with the signer account are used to sign the transaction since the --signers flag is omitted in the above command. Check that the signer account in the transaction.json file, is the account you want to look up its keys and sign with.

Note

If you want to sign with specific keys from the local key directory, you can specify some of them with the --signers flag (e.g. --signers "0:0,0:1").

Note

If you want to sign the transaction with keys that are not in the local key directory, you can use the --keys flag as described here.

The additional signatures generated are added to the multi-sig transaction into the transaction.json file and the file will look similar to the following format:

{
   "energy": 5000,
   "expiryTime": 1716995242,
   "nonce": 46,
   "payload": {
      "amount": "1000000",
      "toAddress": "4bbdAUCDK2D6cUvUeprGr4FaSaHXKuYmYVjyCa4bXSCu3NUXzA",
      "transactionType": "transfer"
   },
   "signature": {
      "0": {
         "0": "3099534c5f32daf64dc40b7a0013979b9b74b167d259fc787a363ed2db7f1bcdafdcf06e166b2d915c7f29043186b3015a6064755bf3c3733bca2151b2b19c04",
         "1": "52271842cd8bacf60bcdea0e7226539079574cef512991bb57bbd09bb27fe16008552f4ce926cac16abbf8190a97264d96ad405a35bd35c466ef86c3ba15680c"
      },
   },
   "signer": "4jxvYasaPncfmCFCLZCvuL5cZuvR5HAQezCHZH7ZA7AGsRYpix",
   "version": 1
}

Send a multi-sig transaction on-chain#

For example, to send the multi-sig testnet transaction from the transaction.json file (created in the previous section) on-chain, run the following command:

$concordium-client transaction submit ./transaction.json \
   --grpc-port 20000 \
   --grpc-ip node.testnet.concordium.com

Note

The node will reject the transaction if the threshold of signatures is not met.

Additional resources#

Was this article helpful?