Two new opcodes that atomically mutate smart contract storage are proposed:
SCREDIT, which increments a storage slot by a specified value, and SDEBIT, which
decrements a storage slot by a specified value. Overflow and underflow errors
are enforced, reverting when an unsigned 256-bit integer would overflow or
underflow.
Motivation
There has been a large amount of energy around parallel EVMs across multiple
chains, however there is a lack of parallel primitives within the EVM to support
any model other than optimistic concurrency control (OCC). By adding concurrent
increment and decrement operations more advanced parallel environments can be
introduced in Layer 2 networks.
This also provides the opportunity to serve the principal use case of increment
and decrement: token balances. We can introduce failures on overflow and
underflow conditions and provide an operation that is also useful outside of
parallel use cases.
Specification
Two operations to atomically increment and decrement a storage will be
introduced
at 0xTBD. Each operation takes two stack arguments and has no immediate
arguments. Gas schedule will be the same as SSTORE.
Mnemonic
Op
Input
Output
SCREDIT
0xTBD
2
0
SDEBIT
0xTBD+1
2
0
SCREDIT
SCREDIT: slot, value
Description
Adds value to the value stored in contract storage slot. If an overflow
would occur the operation halts exceptionally.
Gas Charging
Gas charging is identical to SSTORE. including interactions with the warm
storage slot list. Any future modifications to the SSTORE gas charges will also
apply to SCREDIT.
Execution
Not valid python, not suitable for EELS yet
slot = evm_stack.pop()
value = evm_stack.pop()
storage_value = read_contract_storage(slot)
storage_value = storage_value + value
if storage_value >= 2**256 :
raise Exception("scredit overflow")
write_contract_storage(storage_value)
SDEBIT
SDEBIT: slot, value
Description
Subtracts value to the value stored in contract storage slot. If an
underflow would occur the operation halts exceptionally.
Gas Charging
Gas charging is identical to SSTORE, including interactions with the warm
storage slot list. Any future modifications to the SSTORE gas charges will also
apply to SDEBIT.
Execution
Not valid python, not suitable for EELS yet
slot = evm_stack.pop()
value = evm_stack.pop()
storage_value = read_contract_storage(slot)
storage_value = storage_value - value
if storage_value < 0 :
raise Exception("sdebit underflow")
write_contract_storage(storage_value)
Rationale
The primary consideration when choosing between alternatives is that the primary
intended audiences is token contracts and other asset-tracking contracts
combined with a desire to ship the minimum necessary changes to enable that use
case. General concurrency controls is not a goal of this EIP.
Enforcing Overflow Semantics
When allowing for out-of-order execution there needs to be mechanism to handle
any possible order of execution. OCC handles this by validating pre- and
post-conditions, and re-evaluating the transactions if those invariants did not
hold. This technique breaks down around writing to balances and counters.
Increment/decrement with rollover checking allows for simple handling of
balances and counters while allowing for functional read support ensuring that
sufficient balance or count exists without depending on the exact values. This
allows for evaluation models where the only post-condition checked is to
validate that the storage slots could handle all possible re-ordering of
transactions.
Gas Schedule
The decision to cost the operations at the exact same value as SSTORE is partly
for ease of implementation and partly as an incentive to compilers and
developers.
These semantics could be implemented in the EVM today, but it would also include
a SLOAD, DUP, LT, JUMPI and REVERT instructions. The EVM, however, can do these
operations much more efficiently than via opcodes. First, each SSTORE always
incurs a slot load in order to apply EIP-2200 gas calculation
rules. This load is essential if there is no paired SLOAD. Math libraries for
256-bit numbers can all easily be made sensitive to overflow and underflow, if
they are not already present. Conditional logic handling is also much faster in
the operation logic as most of the overhead would be operation parsing and stack
management when interpreted.
The net impact of the most relevant operations to the most expensive
evaluation (an ADD and LT operation, above the cost of a plain SSTORE) would be
4 gas, or 0.2% of the current cost of a SSTORE. Finally, database access costs
dominate the real cost of the operation. A 0.2% overhead may disappear in I/O
stalls.
Keeping the cost the same makes implementations of gas charging vert simple.
Storage Slots Only
This most important use case for this EIP asset balances and not general
concurrency controls. Hence, only enabling credit and debit operations on
storage slots (which persist across transactions). Parallel execution within a
transaction and more generic tools like locks and semaphores have very limited
utility within this scope. The lack of in-transaction parallel execution also
precludes the use of such primitives against transient storage (as defined in
EIP-1153).
Opcode Instead of System Contract
One alternative, particularly viable for Layer 2 chains, would be to implement
SCREDIT and SDEBIT as system contracts. The primary objection to system
contracts for other operations is the gas cost overhead of constructing a call.
Because a SSTORE is always greater than the cost of a call it would be possible
to build in a discount. However, there is no such accommodation that can be made
for the code size needed to invoke such a call.
Backwards Compatibility
These opcodes are not simple replacements for SLOAD-(ADD|SUB)-SSTORE sequence
because there is an overflow/underflow check
There is no EVM functionality removed by this proposal.
Test Cases
Test for overflow and non-overflow for the following values and values before
and after:
1, 2^8, 2^16, 2^32, 2^64, 2^128 2^255, 2^256-1.
Reference Implementation
/# TBD
Security Considerations
The use of revert to handle over/underflow represents a new halt condition that
auditors will need to consider when examining reentrancy concerns.