Practical guide on modifying Bitcoin core to allow for multiple UTXO in the coinbase transaction
I am currently working on a crypto ecosystem called Dynamo which features a blockchain fullnode that supports hybrid POS/POW security and a consensus directed foundation. As part of the technical design, I needed to add multiple outputs to the coinbase transaction. In standard Bitcoin, there is only one coinbase output, the proof of work miner reward. In my design, I require three: the POW miner reward, the POS staker reward and the development fund allocation. Each block mined will be split on some percentage, to be controlled by the community, among those three groups. After many hours of research and development, I was able to properly implement this. You may find my implementation in the Dynamo github repo listed below.
Use Bitcoin core 21 or later
All transactions must be segwit
All transactions must be Bech32 format
Miners or stakers cannot “cheat” and submit non-compliant blocks
** The detailed changes needed to implement an additional UTXO appear in commit changesets in the Dynamo github repo dated 4/14/21. I have included some code below for illustration purposes, however I suggest you clone the repo and review the code if you plan to implement this feature for your own coin.
1 — You will first need to fork BTC core and make modifications to accommodate your own coin. There are many resources available for this task and it is beyond the scope of this discussion.
2 — Choose a fee split strategy. My implementation pays a fixed fee to the miner and the development fund (POS is up next for development).
3 — Create a CSript object for the new outputs. I chose to put mine in chainparams.h and calculate it up front during the initial chainparams creation. To do this I needed to modify GetScriptForDestination as it expects chainparams to already exist. An alternative strategy would be to create it “on the fly” as each block is submitted, however I felt this was inefficient as it would be creating the same script over and over for each block.
4 — Modify CreateGenesisBlock in chainparams.cpp to add however many new outputs you want.
Make sure to add the empty script at the end, this is the placeholder for the segwit txout that will be added later by the block creation routine. You want all your blocks to be the same format so that they can be easily verified.
- Important note: do not put a valid payto script on any transaction on the genesis block. The genesis transactions cannot be spent and you dont want your dev fee transaction showing up in a wallet accidentally because it will cause all sending transactions that include it to fail.
5 — In miner.cpp you are going to modify BlockAssembler::CreateNewBlock to add your new output transaction(s):
6 — Finally, in validation.cpp you are going to modify CheckBlock to make sure that no one is cheating and submitting blocks without the dev fee. Note that no validation is done on the genesis block (by comparing the Merkle root). Basically you want to make sure that there are 3 txout (one POW, one dev fee and the witness script) and that the second one actually pays the right amount to the dev fund. When I add proof of stake, there will be a fourth transaction output here.
Note that any time you update any parameter in the txout of the genesis block you must re-mine the block and create a new genesis hash and Merkle root hash. There are numerous guides available on how to update the genesis block parameters and the code in the Dynamo repo has a sample code to do this as well in chainparams.cpp.
It is relatively easy, and safe, to modify the Bitcoin core code to create multiple UTXO on the coinbase transaction. Most examples I found used P2PKH addresses and scripts, however using P2WSH is more compliant, uses easier to read addresses and is more secure.
Dynamo core github: https://github.com/dynamo-foundation/dynamo-core
I would like to thank the developers who worked on Qtum and Lux as their code provided inspiration and guidance in my implementation.