Block Rewards in the Registry

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.

Is this really necessary? Isn’t it enough to validate the reward amount?

Yes, it is necessary. When a node imports a block it needs to run the runtime code that rewards the author of that block. And that runtime code needs to get the block author from somewhere. And everybody needs to agree on the block author so it needs to be part of the block.

If I understand correctly: we can store the author either as an inherent (among block extrinsics) or as a digest item (coming from a consensus pallet, probably storing data in an inherent, so in the end as an extrinsic as well).

I doubt that we need to rely on chain consensus mechanism to find out who created a single block. I’m not even sure if every consensus mechanism needs to save the author, e.g. why would the longest chain algorithm care about the author?

For now the inherent way seems less intertwined and just simpler.

@igor: You understand correctly and I agree with you. I’ll follow up with some more details soon.

Nice write up, @geigerzaehler!

When I looked into this myself, it seemed like obtaining the block author when using PoW wasn’t that straightforward. Have you looked further into that?

The requirements good look. Regarding the two alternatives, the inherent sounds indeed simpler but also less safe. Given the whole usp of the blockchain, I would expect the block rewards to be treated like any other transaction that would be validated by every miner. Looking forward to your follow up :+1:

I don’t understand why storing block author as inherent is less safe? Could you elaborate?

I’ve started thinking about other ways of minting tokens than mining, that would require special logic.

We certainly will need initial allocation, maybe some airdrops, maybe something else.

The simplest solution that came to my mind is to use genesis configuration to create initial allocations. It would include a special account controlled by the company, that would be promised to be used only for giveaways and other special events. No additional logic required here.

Do we plan having other ways of minting?

Nope. I would avoid any “trusted third parties” at the protocol.

https://nakamotoinstitute.org/trusted-third-parties/

Though the genesis configuration is indeed the right way to do the initial allocations, we don’t need to be an intermediary – we can directly allocate tokens to all team members individually, once provided with public keys.

Good, so mining is the only way to mint tokens and that’s all the logic we need.

That’s right.

From what I understand regarding the digest, the data isn’t part of the block, but fetched out-of-band. Is that correct? If so I would strongly favor the intrinsic.

If I understand you correctly then you’re not quite right: The digest is part of the block header. It consists of digest items. Consensus (not runtime code) determines what to put into the digest when authoring a block. Both consensus and runtime code verify the digest when blocks are imported. Does that clarify your understanding @cloudhead?

This also is an argument for inherents since having the block author in the digest would increase header size as opposed to block data size.

Gotcha. Yeah indeed the block author in PoW is not useful for light client verification and therefore should probably not be in the header. This is as opposed to PoS where the light client needs to know the block author and therefore you would have it in the digest (I’m guessing).