Skip to content
This repository has been archived by the owner on Dec 26, 2023. It is now read-only.

Rewards and incentives #38

lrettig opened this issue Dec 13, 2020 · 2 comments

Rewards and incentives #38

lrettig opened this issue Dec 13, 2020 · 2 comments


Copy link

lrettig commented Dec 13, 2020


The Spacemesh system needs to reward nodes that perform objectively valuable actions for the network such as mining new transactions into blocks, executing and validating the transactions in those blocks, and participating in Hare consensus.

For background see these forum threads:

Goals and motivation


High-level design

This design is based on EIP-1559, modified to work for a mesh rather than a chain. We introduce a base_fee in each layer, which all transactions in blocks in that layer must pay. Rewards collected in a layer and distributed to miners are smoothed over all blocks and transactions in a configurable number of layers. We also add reward maturation, so that a reward earned by a miner in a given layer isn't spendable until a configurable number of layers later.

Proposed implementation

  1. Config variables. We add several new config variables, including:
    • smoothing_distance: the number of layers over which rewards are smoothed (0 = no smoothing, 1 = layer-wide smoothing, etc.). Note that this must be hardcoded in genesis for a given net/mesh and cannot change later.
    • maturation_distance: the number of layers that must pass before a reward earned by a miner is spendable
    • max_adjust: the maximum ratio by which we allow the base_fee to change from layer to layer
    • min_gas: the minimum total fee that a miner accepts to mine a transaction into a block. This is not in consensus and will differ from miner to miner. It should represent something close to a miner's actual variable cost of including an additional transaction in a block.
  2. Transaction fees and gas
    • Update Transaction data structure to include GasLimit (already present), MaxFeePerGas (max base fee tx is willing to pay) and MaxTipPerGas
    • Update gas arithmetic in tx processing/STF, and in state projection
  3. Miner transaction selection algorithm and add per-layer base_fee
    • Miners should keep their txpool sorted by fee
    • They should determine the base_fee for the current layer (based on votes in the previous layer), determine which transactions in the txpool pay at least this fee, and select transactions randomly from the set that are above this threshold
    • They should calculate a base_fee (based on layer capacity, to take effect in the following layer) and include their explicit vote for base_fee in blocks they publish in this layer
    • They should consider as contextually invalid, and vote against, any block that includes transactions below the base_fee for a given layer
  4. Reward smoothing and maturation
    • Modify reward smoothing to make it work over a variable number of layers. Make sure it includes all collected tips and fees, as well as the block subsidy.
    • Add maturation so that "paid" rewards don't appear in the recipient's account, and aren't spendable, until maturation_distance has passed
  5. API changes: this will require rethinking the way the API returns info on rewards and account state (balances). We already split account state into current/projected in the API. We could include upcoming (immature) rewards in projected but not current account info. We may need to split rewards endpoint to do something similar. Rewards will now have three states: upcoming/projected (due to smoothing), paid (but not matured/spendable), final (matured/spendable).

Implementation plan

  1. Refactor existing rewards code to standardize type (Standardize rewards type go-spacemesh#2069)
  2. Update types
    • Transaction (as described above)
    • Block (add fee voting: this is just a uint)
  3. Gas arithmetic
    • Implement EIP-1559 logic in STF/tx processor
    • Update state projection logic as well
  4. Mempool management, tx selection & block building logic
    • Rewrite mempool to sort by fee. Will require changing data structures, something like a priority queue/heap but also indexed by account.
    • Add logic to calculate base_fee for new layer based on blocks in DB for previous layer: cutoff top N transactions then pick randomly among them; include vote for base_fee in new block
  5. Smoothing
    • Add smoothing_distance param
    • Rewrite rewards logic using smoothing
    • Evaluate how expensive it would be to reread rewards from previous smoothing_distance layers in each new layer, and whether we need to cache these values in memory. Build a cache if necessary.
  6. Maturation
    • Does not require major changes to rewards logic. Just apply rewards maturation_distance layers behind the latest confirmed layer, whenever a new layer is confirmed (same as now).
  7. Update API methods related to rewards, fees, and account state/balance, as described above.


  • Don’t we want to emit an event every time we, e.g., update a balance (e.g., applying a reward)? Otherwise, how does a user know why their balance is what it is? Or does this go into a receipt or something?

Ongoing work

This proposal only includes the barebones design and implementation required for a functional reward system for genesis. In particular, only mining is rewarded. Other useful behaviors such as transaction validation and participation in Hare are not covered by this design and are left for follow-on designs.

Dependencies and interactions

  • Config/CLI flags/genesis config
  • Mempool/mining: The implementation work related to mempool management, tx selection, and block construction overlaps with @noamnelke's work on Transaction Processing #37.
  • Mesh
  • Transaction processing/state updates/state projection
  • API

Stakeholders and reviewers

These changes touch several core go-spacemesh components. Reviews will be requested from the folks most familiar with the components that are changed.

  • @noamnelke will review changes related to data structures, mempool management, tx selection, and mining

Testing and performance

Tests will be added for all new and changed code.

Performance considerations:

  • calculating the rewards to be paid out in a given layer when those rewards were smoothed over many previous layers may be expensive if the smoothing_distance is great. We may need a cache for this.
  • we need to carefully design a new data structure for managing the mempool that allows a miner to sort by total fee (max_fee and tip), find the top N transactions, randomly choose among them, as well as to do the things that are already done with the mempool: pruning conflicting transactions, finding transactions by sender/recipient, etc.
@lrettig lrettig changed the title Rewards and incentives (WIP) Rewards and incentives Dec 22, 2020
Copy link
Member Author

lrettig commented Dec 23, 2020

Thank you everyone for your helpful feedback on today's R&D call. Here are some followup items:

  • @tal-m pointed out that, regarding smoothing, the question is not just how many layers to smooth but also how to do it. I've been assuming a naive, overlapping "waterfall" approach where the rewards for each layer are smoothed over the following smoothing_distance layers, but as Tal pointed out, we might choose a different strategy where we, e.g., group layers into "chunks", or align smoothing with epoch boundaries.
    • I think that smoothing_distance is a parameter and that choosing epoch-wide or layer-wide smoothing doesn't make much difference from the perspective of implementation, but I think Tal disagrees. I'm hoping to better understand why.
  • @y0sher pointed out that we used to have maturation but we dropped it because we didn't want to make reorgs easer.
    • @noamnelke said he thinks maturation is irrelevant in our system, that it would only matter in very rare edge cases anyway
    • I think maturation could be removed from the scope of this SMIP if we want more time to think about it, since it's basically orthogonal to the rest of this design/implementation. This could also be a separate SMIP.
  • @tal-m suggested that, when redesigning the API to account for reward smoothing and maturation, we also take into consideration validators and Iddo's recent proposal that we maintain two separate "accounts" or balances for each account, one that contains funds that can be immediately spent (for, e.g., fee payment to miners) and the other with funds locked into smart contracts that need to be "unlocked" over some number of layers (e.g., after a challenge period expires) before they can be spent. This sounds sensible to me and probably needs a new SMIP, but also has some serious UX considerations we should discuss.
  • @iddo333 and @tal-m pointed out that, when sorting the mempool, we need to be careful how we sort transactions.
    • I've been using the assumption that we use a single, straightforward, scalar gas value (max_fee), but in future fees may be vectorized and we need to be careful how we scalarize and sort them. Tal suggests a "fee per unit density" construction that sounds reasonable.
    • @iddo333 is concerned about "competitive pricing" and transactions that don't truly cover their full, long-term, social cost through fees, which could thus be a DoS vector. He thinks we want to add heuristics to tx selection to assess tx "quality."
    • Tal pointed out that, in any case, even if the reference implementation does this, miners are free to perform tx selection in another way, which sort of defeats the purpose of trying to design a coordinated approach.
    • While I agree with Iddo's point that mempool management and tx selection give us more tools for dealing with these issues, my philosophy is that we should instead invest time in making sure that our fee model is robust and that all transactions pay enough of a fee to cover their complete costs, that we don't do "competitive pricing," and that if we get this right there is effectively no such thing as a "spam" transaction.
    • In general, designing a new data structure for the mempool is tricky and I think we should move this topic into a separate SMIP as well. @noamnelke suggested a brainstorming session to discuss requirements as a next step.
  • Regarding the question I asked in the SMIP, about transaction receipts, @avive pointed out that the topic is already discussed in SMIP-0003: Global state data, STF, APIs #13.
    • We all agree that we want receipts but it's not entirely clear what they contain, where they're stored, how they're used, etc. This should probably be a separate SMIP, which I will work on. I'll lay out my argument for why we need them for rewards as well. It's largely orthogonal to this SMIP and should not delay implementation.

Copy link
Member Author

lrettig commented Oct 7, 2021

Moving this over here from #50, need to find a place for it in this SMIP or another:

Initially no change to incentives is necessary. Dishonest miners may submit empty content blocks, no content blocks, or duplicate content blocks but this will have little or no impact on the superblock produced for each layer. In any case, the degree of harm that dishonest miners can cause under this proposal is no greater than in Spacemesh 0.1, where they could engage in the same behavior. (In fact, there's less harm, since they cannot cause bloat in the mesh due to many duplicate transactions in a layer.)

If we're concerned about high fee paying transactions not being included in layers due to Byzantine behavior by a dishonest minority of miners in scenarios where the mempool contains many pending transactions, we could increase redundancy of these transactions by having multiple miners include such transactions in their proposed content blocks (at the cost of total throughput). See Coupon collector's problem for one probabilistic approach to solving this problem.

We should continue to research ways to better align incentives, such as by punishing miners (or withholding incentives from miners) who submit content blocks that are less than full, or contain many duplicate transactions.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
None yet
None yet

No branches or pull requests

1 participant