Securing transactions outside of MultiChain

Each participant in a blockchain possesses one or more private keys, which they use to digitally sign transactions relating to the addresses they own. The security of these private keys is paramount – if a user’s private key is compromised, any other user on the blockchain can forge transactions from that user. This can include spending that user’s assets, writing stream items in their name, or changing other users’ permissions on their behalf.

By default, private keys are stored in MultiChain’s built-in wallet, a regular disk file on the computer where each MultiChain node is running. Although this wallet can be encrypted on disk, its contents can still be read if the computer is sufficiently compromised. MultiChain therefore allows private keys to be stored separately from the node, e.g. in another computer or hardware security module. This external private key can be randomly generated by MultiChain using the createkeypairs API command, or by an external bitcoin-compatible software library.

By using the importaddress API command, MultiChain can track the activity of any address without needing its private key. MultiChain can also be used to build an unsigned transaction for imported addresses using the createrawsendfrom API.

An unsigned transaction can be signed in one of two ways. The simplest method is to use a regular MultiChain node’s signrawtransaction API command, passing in the private key(s) as a parameter, then passing the result to sendrawtransaction for broadcast. While this method briefly exposes the private key to MultiChain, it will not be stored on the server’s disk.

A more complex but safer method is to sign the transaction outside of a regular MultiChain node, only using sendrawtransaction to broadcast the signed transaction. This can be done with the cold version of MultiChain, or an external software library or hardware device that can unpack raw transactions and add signatures to their inputs. MultiChain uses bitcoin’s transaction structure and cryptography, so any bitcoin-compatible library or device should be fine, so long as it does not choke on per-output and per-transaction metadata.

In this tutorial, we will focus on the simpler method, since it does not require any additional software or hardware. The tutorial requires one MultiChain node, which should have at least one address with admin, issue and create permissions. If you don’t yet have this, follow the instructions in section 1 of the Getting Started guide and then run multichain-cli chain1 to enter interactive mode.

Creating the private key and address

Let’s begin by asking MultiChain to generate a new private key and corresponding address, without storing either in the node’s wallet:

createkeypairs

Copy and paste the address shown:
Copy and paste the privkey shown:

Now let’s import the address into the node, without its private key:

importaddress '' false

The false at the end saves time by not scanning the blockchain for activity relating to this address, because we know it hasn’t been used before.

At this point, let’s look for another address that we can use for receiving assets later in this tutorial. Run both of these commands:

listpermissions receive
listaddresses

An address is suitable if it appears in the output of the both commands.

Enter the address here:

You should also see the imported address listed in the output from listaddresses together with "ismine" : false.

Issuing an asset to the address

Let’s go back to the imported address, grant it some permissions and issue a new asset to it:

grant receive,send
issue asset0 50000 0.001

Now we can check that the address has receive the new asset units:

getaddressbalances 0

The 50000 units of asset0 shown be shown. Note that if we had not added the address using importaddress, we would not be able to query its balance in this way.

Sending from the address

Now we’ll send some of the newly issued asset from the imported address to another one. First:

createrawsendfrom '{"":{"asset0":2000}}'

The response should contain a hexadecimal blob containing the raw unsigned transaction, which should be copied to the clipboard. We’ll now sign the transaction using the external private key:

signrawtransaction [paste-hex-blob] '[]' '[""]'

The response should contain a complete field with value true, along with a larger hexadecimal blob in the hex field. This means that the transaction has been signed 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 2000 units of asset0 have been successfully transferred:

getaddressbalances 0
getaddressbalances 0

Publishing from the address

First let’s create a new stream which we will use in the tutorial:

create stream stream0 true

Now let’s prepare to publish something to this stream from the imported address:

createrawsendfrom '{}' '[{"for":"stream0","key":"key0","data":"45787465726e616c20697320736166657374"}]'

The response should contain a hexadecimal blob containing the raw unsigned transaction, which should be copied to the clipboard. Now sign the transaction using the external private key:

signrawtransaction [paste-hex-blob] '[]' '[""]'

The response should contain a complete field containing true, along with a larger blob in the hex field, containing the signed transaction. 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:

subscribe stream0
liststreamitems stream0

You should see one stream item, with shown in the list of publishers.

Where to go from here

Well done! You have now learned how to send assets and publish to a stream using an address whose private key is held outside of the node’s wallet. As mentioned in the introduction, instead of passing the private key to signrawtransaction, a bitcoin-compatible software library or hardware could be used to sign the transaction without ever revealing the private key to MultiChain itself.

A similar technique can be used to perform any other action for an address with an external private key – 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. Remove the final parameter send from the createrawsendfrom commands in those examples, then complete the process using signrawtransaction (or external signing) and sendrawtransaction as above.