Skip to content

EIP-1283: Net gas metering for SSTORE without dirty maps - #1283

Merged
Arachnid merged 32 commits into
ethereum:masterfrom
sorpaas:sp-sstore-no-dirty-map
Aug 7, 2018
Merged

Arachnid merged 32 commits into
ethereum:masterfrom
sorpaas:sp-sstore-no-dirty-map

Conversation

@sorpaas

@sorpaas sorpaas commented Aug 1, 2018 •

Copy link
Copy Markdown
Contributor
eip: 1283
title: Net gas metering for SSTORE without dirty maps
author: Wei Tang (@sorpaas)
discussions-to: https://github.com/sorpaas/EIPs/issues/1
status: Draft
type: Standards Track
category: Core
created: 2018-08-01

Abstract: This EIP proposes net gas metering changes for SSTORE opcode, as an
alternative for EIP-1087. It tries to be friendlier to implementations
that uses different opetimiazation strategies for storage change
caches.

Rendered

@sorpaas sorpaas changed the title EIP-x: Net gas metering for SSTORE without dirty maps EIP-1283: Net gas metering for SSTORE without dirty maps Aug 1, 2018
Comment thread EIPS/eip-1283.md
Replace SSTORE opcode gas cost calculation (including refunds) with
the following logic:

* If *current value* equals *new value* (this is a no-op), 200 gas is

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following this process, starting with original = 1 and current = 1:

  1. new = 0; deducts 5k gas and adds 15k gas to the refund counter; current = 0.
  2. new = 1; adds 4800 gas to the refund counter
  3. Goto 1.

Thus for every 5k gas deducted from the gas meter, we can add 19800 gas to the refund counter.

@sorpaas sorpaas Aug 1, 2018 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed! Added an additional clause when storage slot is dirty. If original value is not 0, if current value is 0 (also means that new value is not 0, because of the parent clause "If current value does not equal new value"), deduct 15000 gas.

  1. new = 0, original = 1, current = 1; deducts 5k gas and adds 15k gas to refund counter.
  2. new = 1, original = 1, current = 0; deducts 200 + 15k gas, adds 4800 gas to the refund counter.
  3. original = 1, current = 1; goto 1; for every round 200 + 20k gas is deducted, and 19800 gas in the refund counter.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems reasonable. A couple of critiques:

  • Unlike the existing proposal, this can result in consuming a lot of gas and adding it to the refund counter. Since refunds are limited to 1/2 the total gas used, you could easily end up paying more than expected if you set a value to and from its original value a number of times.
  • It's hard to follow the logic here. Some diagramming, or breaking down the cases more would help.

@sorpaas sorpaas Aug 2, 2018 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I think (1) is a really good point. If there're cases where it can cost a lot most gases compared with the current scheme, then we may break backward compatibility for existing contracts. So in 23ebc94 I changed the clause from deducting 15k gas to removing 15k gas from refund counter. In this way, we will never consume more gases compared with current scheme. Indeed, this adds a new semantics for gas refunds (we never removed gas from refund counter before), so I'm definitely open to better ideas on this. The current rationale/assumption for this is:

  • Backward compatibility is important.
  • We can prove that refund counter will never go below 0.
  • This new semantic of removing gas from refund counter is trivial to implement in most clients.

Talking about other cases where it may cost more gases compared with EIP-1087 -- it happens with sub-call frames to the same contract where we cannot track the dirtiness. But I still think it may be worth the trade off because:

  • We don't suffer from the optimization limitation of EIP-1087. After EIP-658, an efficient storage cache implementation would probably use an in-memory trie (without RLP encoding/decoding) or other immutable data structures to keep track of storage changes, and only commit changes at the end of a block. For those implementations, we cannot efficiently iterate over a transaction's storage change slots without doing a full diff of the trie.
  • It never costs more gases compared with current scheme.
  • It covers commons usages like reentry locks, same-contract multi-send, etc.

Still working on (2). Let me try to see whether there are clearer ways to describe the spec.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added an explanation section to make the logic more clearer!

@Arachnid

Arachnid commented Aug 3, 2018 •

Copy link
Copy Markdown
Contributor

So, here's a state transition diagram as best I can establish. This diagram ignores no-op writes, which are trivial. The graph has two parts: one for where the original value is 0, and one for where the original value is nonzero.

I agree that there doesn't seem to be a way to "pump" the refund counter. There are a couple of situations where a series of writes can result in moving gas in 5k chunks from gas meter to refund counter still, however - such as when the value is nonzero to start, and it's repeatedly set to another value and then back to its original value.

I think a table version of this would probably be useful, too. Each of the two graphs can likely be summarised in a table fairly intuitively.

screen shot 2018-08-03 at 12 42 25

Here's the graphviz source for the diagram:

digraph G {
  00 -> 01 [label="~0; 20k gas"];
  01 -> 01 [label="~0; 200 gas"];
  01 -> 00 [label="0; 200 gas, 19.8k refund"];

  11 -> 12 [label="~orig, ~0; 5k gas"];
  12 -> 12 [label="~orig; 200 gas"];
  12 -> 11 [label="orig; 200 gas, 4.8k refund"];
  12 -> 10 [label="0; 200 gas, 15k refund"];
  11 -> 10 [label="0; 5k gas, 15k refund"];
  10 -> 11 [label="orig; 200 gas, -10.2k refund"];
  10 -> 12 [label="~orig; ~0; 200 gas, -15k refund"];

  00 [label="current=orig=0"];
  01 [label="current!=orig"];
  11 [label="current=orig!=0"];
  12 [label="current!=orig"];
  10 [label="current=0"];
}

@sorpaas

sorpaas commented Aug 3, 2018 •

Copy link
Copy Markdown
Contributor Author

Thanks so much! I added the diagram and the table version to the spec Explanation section (if that's okay for you)!

I agree that there doesn't seem to be a way to "pump" the refund counter. There are a couple of situations where a series of writes can result in moving gas in 5k chunks from gas meter to refund counter still, however - such as when the value is nonzero to start, and it's repeatedly set to another value and then back to its original value.

Yes indeed. That's the case for current scheme as well (repeatedly call SSTORE to set a slot from 0 to non-zero, and then back to zero, each cycle would cost 25k and gets 15k refund). I can't think of a way to use the methodology in this EIP to fix that, though.

Comment thread EIPS/eip-1283.md Outdated
Below are table version of the above diagram. Horizontal shows the
next state, and vertical shows the current state.

| | `current=orig=0` | `current!=orig` |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't this be clearer if one axis had (current, original) and the other axis had new?

Comment thread EIPS/eip-1283.md Outdated

![State Transition](../assets/eip-1283/state.png)

Below are table version of the above diagram. Horizontal shows the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you have 'horizontal' and 'vertical' reversed here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops! Fixed!

@Arachnid
Arachnid merged commit d087ea8 into ethereum:master Aug 7, 2018
@chfast

chfast commented Aug 8, 2018

Copy link
Copy Markdown
Member

Some test cases: ethereum/tests#483.

@k06a

k06a commented Jan 15, 2019

Copy link
Copy Markdown
Contributor

Reentrancy attack returns: https://www.coindesk.com/ethereums-constantinople-upgrade-faces-delay-due-to-security-vulnerability

Any ideas on mitigating?

@banteg

banteg commented Jan 15, 2019

Copy link
Copy Markdown
Contributor

charge 5000, refund 4800?

@k06a

k06a commented Jan 16, 2019

Copy link
Copy Markdown
Contributor

charge 5000, refund 4800?

Charge 2300, refund 2100? :)

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants