Using multiple keys for extra security

Public key encryption, also known as asymmetric cryptography, is a key underlying technology of blockchains. Participants in the chain generate their own pairs of private keys and public addresses. They keep the private keys secret, but freely distribute the associated addresses. A blockchain transaction which performs an action for a particular address (e.g. spending its funds) must be signed by the corresponding private key. All participants on the chain can then verify these signatures, using public addresses only, without needing to see each others’ private keys.

Multisignature (“multisig” for short) addresses and transactions broaden this model by creating identities on the chain which are managed collectively by multiple parties. MultiChain uses “m-of-nbitcoin-style multisignatures, in which a multisig address A is defined as: Given n regular addresses, at least m of the private keys corresponding to those addresses must sign a transaction to perform an action for A.

Some common values of m and n are given below, with practical examples:

  • 1-of-n: Any one of n different parties can approve the transaction. For example, one of three employees can spend some funds, but we want the blockchain to contain a record of who did so.
  • 2-of-2: Each of two separate parties must approve the transaction. For example, two departments in a company must sign off before some data is published on a blockchain on behalf of that company.
  • 2-of-3: Any two out of three parties can approve the transaction. This is commonly used for escrow purposes, where the two counterparties to an agreement engage a third party to act as an arbitrator in the event of dispute.

In this tutorial, we will focus on 2-of-2 multisignatures, but the same techniques can be applied for any m and n. The tutorial requires two servers running Linux, both of which should have a multichaind node up and running on the same blockchain, with no native currency or other unusual parameters. Both servers should be running multichain-cli for that chain in interactive mode. If you don’t yet have this, follow the instructions in sections 1 and 2 of the Getting Started guide and then run multichain-cli chain1 on both servers. The first server’s node should also have an address with admin, issue and create permissions – this will automatically be the case if it started the chain.

Creating the multisignature address

On the first server, run the following command:

getaddresses true

Choose any address with "ismine" : true, which means that this node’s wallet contains the private key for the address.

Copy and paste the pubkey shown:

Now run the same getaddresses true command on the second server, again choosing an address with "ismine" : true.

Copy and paste the pubkey shown:

Now run the following command on either server to create the 2-of-2 multisig address and add it to the node’s wallet:

addmultisigaddress 2 '["", ""]'

The response contains the multisignature address. Copy and paste it here:

On the same server, run the following command to start tracking the balance of this address:

importaddress '' false

The response should be empty. Now run the same commands on the other server, to add the address to the wallet and start tracking its balance:

addmultisigaddress 2 '["", ""]'
importaddress '' false

Issuing an asset to the multisig address

For most blockchain actions, a multisig address requires its own permissions, independent of the permissions of the individual regular addresses that were combined to create it (more details here). Let’s grant these permissions on the first server:

grant receive,send

The txid of the grant transaction should be displayed in the response. Now let’s issue a new asset directly to the multisig address:

issue asset9 10000 0.01

And now let’s check the multisig address has received the funds successfully:

getaddressbalances 0

A balance of 10000 units of asset9 should be displayed.

Spending funds from the multisig address

Still on the first server, let’s create a new regular address to receive some funds from the multisig:

getnewaddress

Copy and paste the new address here:

Now let’s grant this address receive permissions, so it can be sent some funds:

grant receive

Now we begin the process of building the transaction which sends funds from the multisig address to this new address. Because this is a 2-of-2 multisig, the process will require a signature from both servers. Let’s begin on the first server:

createrawsendfrom '{"":{"asset9":500}}' '[]' sign

The response should contain a complete field with value false, along with a large hexadecimal blob in the hex field. This hexadecimal blob is the raw transaction, which has been partially signed, and should be copied to the clipboard.

Now switch to the second server and run the following, pasting the raw transaction from the clipboard where shown:

signrawtransaction [paste-hex-blob]

The response should contain a complete field with value true, along with an even larger hexadecimal blob in the hex field. This means that the transaction has enough signatures to be valid, and is ready for broadcasting to the blockchain. Copy the new hexadecimal blob, and run:

sendrawtransaction [paste-bigger-hex-blob]

The response should contain the 64-character hexadecimal txid of the sent transaction. Now let’s check that the 500 units of asset9 have been successfully transferred. On either server:

getaddressbalances 0

And on the first server only, check the new address’s balance (including unconfirmed transactions):

getaddressbalances 0

Creating a multisig-only stream

Now let’s create a stream to which only this multisignature address can write. On the first server:

create stream stream9 false

This automatically grants per-stream write permissions to its creator, which can be seen here:

listpermissions stream9.write

Copy and paste the address shown:

Now we’ll revoke the write permissions for this address and grant them only to the multisig address:

revoke stream9.write
grant stream9.write

Let’s verify the outcome, in which only the multisig address should be shown:

listpermissions stream9.write

Publishing from the multisig address

Now let’s prepare to publish something to the stream from the multisignature address. Still on the first server:

createrawsendfrom '{}' '[{"for":"stream9","key":"key1","data":"48656c6c6f2066726f6d206d756c746973696721"}]' sign

The response should contain a complete field containing false, and a large hexadecimal blob in the hex field, which should be copied to the clipboard.

Now switch to the second server and paste from the clipboard where shown:

signrawtransaction [paste-hex-blob]

The response should contain a complete field containing true, along with an even larger blob in the hex field. Copy the new blob, and run:

sendrawtransaction [paste-bigger-hex-blob]

The response should contain the txid of the sent transaction. Finally let’s check that the item was published successfully. On either server:

subscribe stream9
liststreamitems stream9

You should see the item listed with the multisig address shown in the publishers field, as well as the key and data entered above.

Where to go from here

Congratulations! You have now learned how to send assets and publish to a stream using a 2-of-2 multisignature address.

A similar technique can be used to perform any other action for a multisig address – issuing or reissuing assets, creating streams, and granting/revoking permissions for other addresses. In each case, examples of the appropriate createrawsendfrom parameters can be found on the raw transactions page. Pass sign instead of send as the final parameter to createrawsendfrom, then complete the process on the second node using signrawtransaction and sendrawtransaction as above.

For transactions that must be signed by more than 2 nodes, such as those using 3-of-3 multisigs, pass the raw transaction through signrawtransaction on each node in turn, taking the new hex output before transferring to the next node. Once the complete field of the response is true, the final raw transaction can be sent from any node using sendrawtransaction.