Elements code tutorial
Block creation in a Strong Federation - Block Signing
So far, in order to create blocks we have been calling the âgenerateâ command from either node. Thatâs worked so far because by default block signing is OP_TRUE.
Creating a new block has been as simple as executing:
e1-cli generatetoaddress 1 $ADDRGEN1
Elements supports a federated signing model which allows you to specify the number of signatures required in order to produce a valid block. Letâs set up something more interesting next and tell our nodes to require a valid 2-of-2 multi-signature block creation condition. This is done using the signblockscript parameter, which can be added to the config file or passed into the node on startup.
First, we need to get keys from both clients so that we can then make our block sign script. Thatâs what weâll then have to fulfill in order to produce a valid block.
Generate a new address from each of our nodes:
ADDR1=$(e1-cli getnewaddress)
ADDR2=$(e2-cli getnewaddress)
Validate the addresses and then extract the public key for each:
VALID1=$(e1-cli getaddressinfo $ADDR1)
PUBKEY1=$(echo $VALID1 | jq -r '.pubkey')
VALID2=$(e2-cli getaddressinfo $ADDR2)
PUBKEY2=$(echo $VALID2 | jq -r '.pubkey')
Now extract the private keys which weâll import later:
KEY1=$(e1-cli dumpprivkey $ADDR1)
KEY2=$(e2-cli dumpprivkey $ADDR2)
Now we need to generate a redeem script for a 2 of 2 multi-sig. We do this by using the âcreatemultisigâ command and passing the first parameter as 2 and then providing two pubkeys. If we wanted to do a â2 of 3â multi-sig weâd pass 2 and then three pubkeys etc:
MULTISIG=$(e1-cli createmultisig 2 '''["'''$PUBKEY1'''", "'''$PUBKEY2'''"]''')
REDEEMSCRIPT=$(echo $MULTISIG | jq -r '.redeemScript')
echo $REDEEMSCRIPT
Stop the nodes so we can then configure them to use our new block signing method:
e1-cli stop
e2-cli stop
Define the requirements of block creation (must be valid against our redeemscript) and store in a variable. Note that the value â214â comes from (required_signers * 74) + (number_of_nodes * 33). Also note that the âevbparams=dynafed:0:::â parameter is to prevent dynamic federations from activating which complicates block signing:
SIGNBLOCKARGS=("-signblockscript=$(echo $REDEEMSCRIPT)" "-con_max_block_sig_size=214" "-evbparams=dynafed:0:::")
Note: For Elements versions older than 0.21.0.1, â-con_dyna_deploy_start=0â should be used to disable dynamic federations (instead of â-evbparams=dynafed:0:::â).
Weâll have to wipe out the chain weâve been using so far and also the wallets and start again with a new genesis block. Note that once created you canât swap blocksigners in and out on a chain for security reasons. This may change in a later Elements release.
rm -rf ~/elementsdir1/elementsregtest
rm -rf ~/elementsdir2/elementsregtest
Start the daemons with the âsignblockscriptâ we specified for the 2 of 2 block signing:
e1-dae ${SIGNBLOCKARGS[@]}
e2-dae ${SIGNBLOCKARGS[@]}
Now we need to create default wallets and rescan the blockchain:
e1-cli createwallet ""
e2-cli createwallet ""
e1-cli rescanblockchain
e2-cli rescanblockchain
Now import the signing keys that we stored earlier before wiping the wallets:
e1-cli importprivkey $KEY1
e2-cli importprivkey $KEY2
The âgenerateâ command no longer works, even if the keys required for signing are imported into a wallet because we have started the daemons with the signblockscript argument. To make sure the error is caused by our introduction of signblockscript and not because we removed the wallets containing the generate receiving addresses, weâll create new ones. Because we have introduced the signblockscript, the generatetoaddress commands will error:
ADDRGEN1=$(e1-cli getnewaddress)
ADDRGEN2=$(e2-cli getnewaddress)
e1-cli generatetoaddress 1 $ADDRGEN1
e2-cli generatetoaddress 1 $ADDRGEN2
Both error with the message:
Because we started with the âsignblockscriptâ argument we have to follow a new process for making, signing and releasing blocks.
The process will be:
-
Someone (it doesnât matter who) calls âgetnewblockhexâ command to propose a new block.
-
The required number of block signers sign the proposed block in turn.
-
The signed block is combined by someone using the âcombineblocksigsâ command.
-
The result from âcombineblocksigsâ is submitted using the âsubmitblockâ command. Again, it doesnât matter who does this as long as the block is signed and valid.
So letâs do this. Start by proposing a new block:
HEX=$(e1-cli getnewblockhex)
So just to double check, the block count should still be zero as the proposed block has yet to be signed:
e1-cli getblockcount
That returns 0. And if we try and submit the block as it is:
e1-cli submitblock $HEX
We get an error:
And the block count is still zero:
e1-cli getblockcount
So letâs sort that out and sign the block using each daemon to satisfy the 2 of 2 requirement:
SIGN1=$(e1-cli signblock $HEX "$REDEEMSCRIPT")
SIGN2=$(e2-cli signblock $HEX "$REDEEMSCRIPT")
NOTE: Signblock tests validity before signing. This signing step can be outsourced to a Hardware Security Module (HSM) to enforce a greater level of security.
NOTE: We are using the redeemscript for the âwitnessScriptâ parameter here.
We can have each node sign the proposed block hex easily as we are running our code on a single machine with access to the output from each nodeâs call to âsignblockâ. In a production environment you will likely require remote nodes to exchange signed blocks between each other so that the requirements of the block signing script can be met. An example of how to do this can be found here: https://github.com/nkostoulas/block-signing-federation. The demo code uses Kafka as a communication protocol between 3 signing nodes running locally, implementing a 2-of-3 federation and can be extended for nodes running in remote locations by changing the demoâs configuration.
As we are running our nodes on the same machine with easy access to the output from each nodeâs execution of âsignblockâ, we can simply gather signature and pubkey data and combine them into a fully signed block like this:
SIGN1DATA=$(echo $SIGN1 | jq '.[0]')
SIGN2DATA=$(echo $SIGN2 | jq '.[0]')
COMBINED=$(e1-cli combineblocksigs $HEX "[$SIGN1DATA,$SIGN2DATA]" "$REDEEMSCRIPT")
echo $COMBINED
NOTE: Again weâre using redeemscript for the âwitnessScriptâ parameter to âcombineblocksigsâ
We see a result of âTrueâ for the âcompleteâ property as we have signatures from enough keys to satisfy the 2 of 2 requirement. So âcompleteâ in this context means âhas enough signatures for the ân of mâ multi-sig to be validâ.
Now we will extract the signed block hex from the results of the combineblocksig command and submit the block. It doesnât matter who does this as long as they have a signed and valid block hex:
SIGNEDBLOCK=$(echo $COMBINED | jq -r '.hex')
e2-cli submitblock $SIGNEDBLOCK
Check that worked:
e1-cli getblockcount
e2-cli getblockcount
Yes it did, we now have moved forward one block!
We can now shut the daemons down in preparation for the next section which will explain how to âpegâ your blockchain to another so it runs as a Sidechain:
e1-cli stop
e2-cli stop
If you do not require your blockchain to operate as a sidechain you can skip the next section and should now also execute the âb-cli stopâ command to shut the bitcoin daemon down as well.