I’ve started work on block rewards for the Radicle Registry ledger. Since we don’t want to reinvent the wheel I looked into two existing implementations for Substrate and I’ll review them here. I’ll follow up on this post with a comparison of the two approaches and a proposal for our implementation.
Goals and requirements
In the Radicle Registry we want to reward miners with block rewards and transaction fees. This results in the following requirements.
- The runtime logic that charges transaction fees needs to know the block author to deposit the fees.
- We need some logic that rewards the block author once per block.
- Mining nodes need provide the block author to the runtime when mining a node.
- The block author must be stored in a block (possibly indirectly) so that validators can verify the reward and fee logic.
Kulupu
Kulupu is an experimental Substrate proof-of-work chain that uses inherents to provide the block author to the runtime.
Inherents are used differently depending on whether a block is authored on a node or whether it is imported. When a block is authored the authoring logic provides some inherent data (in this case the block author) to the runtime. The runtime then constructs a runtime call from this inherent data. The runtime call is then treated like an extrinsic by the rest of the runtime code. The main difference is that the origin is None
when the handler for this code is invoked. In Kulupu we just construct a SetAuthor
call for the block author inherent and the handler for this call simply sets the author in the state storage. Now any subsequent runtime logic can obtain the author from the state storage.
When a block is imported and validated the call that sets the author is an immutable part of the block. The call will be executed like extrinsics so that the authoring information is also available to the runtime.
Usually calls from inherents are also checked against inherent data when importing a block. For example this is the case for timestamp inherent data. For block authoring data there is no need for nodes to validate since the choice of the block author is indeed at the full discretion of the block author.
Kulupu rewards the block author in the on_finalize
method of the reward module. The code simply deposits the block reward to the account read from the author storage. Substrate’s runtime wiring ensures that on_finalize
is called after all transactions in a block have been processed.
pallet_authorship
Substrate provides the authorship pallet to make block authorship data available to the runtime. The pallet provides the author
module function to get the current block author but is not concerned with block rewards or transactions.
The pallet assumes that the author is added to a block’s digest by the consensus. A block’s digest is available to the runtime via the system module’s digest
function. The author
function iterates over all digest items and extracts the block author from the appropriate item.
The author
function is lazy: It only extracts the author from the digest on its first invocation. On this invocation, it stores the author in the state and subsequent invocations get the author from the state. Like Kulupu, the authorship pallet has a on_finalize
implementation that clears the author from the state storage to make it ephemeral.
When authoring a block the miner passes the block author as part of the initial digest to the Proposer::propose
method. This happens as part of the block authoring function that is specific to every consensus mechanism.