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.