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 thenew-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.