Skip to content

Instantly share code, notes, and snippets.

Last active May 15, 2020 12:51
Show Gist options
  • Save holiman/8a3c31e459ee1bff04256bc214ea7f14 to your computer and use it in GitHub Desktop.
Save holiman/8a3c31e459ee1bff04256bc214ea7f14 to your computer and use it in GitHub Desktop.

Gas And Karma


Previously, I made a proposal: EIP-2583, which would add a penalty for certain operations, namely ops that caused trie misses.

The proposal would have "kind of" worked, but with some caveats:

  1. The maximum enforceable 'penalty' would be on the order of 800,
  2. There was a low (but not impossible) chance of breaking some contract flow

I'm going to first analyze these two points a bit.

Penalty-shielding through CALL

There exists a roofing function for the penalty. Since the penalty is deducted from gas, that means that a malicious contract can always invoke a malicious relay to perform the trie lookup. Let's refer to this as the 'shielded relay' attack.

In the EVM, gas is a 'local' concept. This means that even if at some specific point in the call-frame, the gas would have gone below zero, it actually only goes to zero, the execution is reverted, and control is given back to the caller, in the previous context.

Initiatives/EIPS such as EIP-1380 "reduced gas-cost for calls to self, would further limit the "enforceability" of penalties.

Contract flows

Any time we change the cost of an opcode, we risk breaking contracts. Unfortunately, due to the 'locality' of gas, these breakages cannot be fixed from the outside. Even if the sender adds more gas to his transaction when he invokes some token, it might still be the case that the token only sends 2300 gas to the next contract in the flow. Thus: contracts acts as bottlenecks, and prevents "fixing" a broken flow by merely adding more gas.

Some examples of contract flows that can be broken can be found in the analysis for EIP-1884, by like these found by and these found by Chain Security

Introduction to Karma

I heard about this idea first from @ledgerwatch, he calls it 'oil'. As I'm coming from the perspective of penalizing behaviour that is unwanted, I'd prefer to all it Karma, instead of just continuing the analogy with mechanical engines.

Let's say that for every transaction, we set karma=gas at the start of the transaction. So a 50K gas transaction now also has 50K karma. As opposed to gas, karma is a fully global concept. It is a per-transaction value.

Now, if we want to introduce the penalty from EIP-2583, here's what we can do:

  • For every trie-opcode that causes a trie-miss (defined in EIP-2583), deduct penalty karma (e.g. 5000).
  • If karma goes below 0, abort the entire transaction.

So, where does this land us?

  1. Since karma is global, any broken flows can be rescued by the sender by supplying more karma.
  2. Since karma is global, misbehaving contracts cannot use 'call-shields' to restrict the penalty.

Second step

If we also modify the transaction format, so that karma can be set explicitly, then users wouldn't have to raise gas needlessly only to push through a broken contract-flow.

For stateless ethereum, the concept of a global counter for trie accesses (read: witness size counter) is highly useful. Instead of using it to penalize trie-misses, the exact same mechanism can be used to estimate withness size of a transaction, and make transaction cost relative to the witness usage.

For stateless ethereum, an example rule could be to deduct karma for any operation that reads or writes state.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment