Skip to content

Commit 93c1110

Browse files
Merge pull request #1124 from ethereumjs/fix-eip2929
Fix EIP2929
2 parents fa84b93 + d67e8bd commit 93c1110

File tree

22 files changed

+515
-137
lines changed

22 files changed

+515
-137
lines changed

.github/workflows/vm-pr.yml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@ jobs:
5454
strategy:
5555
matrix:
5656
fork: [
57-
'MuirGlacier',
58-
'Istanbul'
57+
'Berlin',
58+
'Istanbul',
59+
'MuirGlacier'
5960
]
6061
fail-fast: false
6162
steps:
@@ -137,7 +138,9 @@ jobs:
137138
# Tests were splitted with --dir and --excludeDir to balance execution times below the 9min mark.
138139
args: [
139140
'--fork=Istanbul --dir=GeneralStateTests/stTimeConsuming --expected-test-amount=15561',
140-
'--fork=Istanbul --excludeDir=stTimeConsuming --expected-test-amount=19817'
141+
'--fork=Istanbul --excludeDir=stTimeConsuming --expected-test-amount=19817',
142+
'--fork=Berlin --dir=GeneralStateTests/stTimeConsuming',
143+
'--fork=Berlin --excludeDir=stTimeConsuming'
141144
]
142145
fail-fast: false
143146
steps:

packages/common/src/eips/2929.json

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,66 @@
1717
"warmstorageread": {
1818
"v": 100,
1919
"d": "Gas cost of reading storage locations which have already loaded 'cold'"
20+
},
21+
"sstoreCleanGasEIP2200": {
22+
"v": 2900,
23+
"d": "Once per SSTORE operation from clean non-zero to something else"
24+
},
25+
"sstoreNoopGasEIP2200": {
26+
"v": 100,
27+
"d": "Once per SSTORE operation if the value doesn't change"
28+
},
29+
"sstoreDirtyGasEIP2200": {
30+
"v": 100,
31+
"d": "Once per SSTORE operation if a dirty value is changed"
32+
},
33+
"sstoreInitRefundEIP2200": {
34+
"v": 19900,
35+
"d": "Once per SSTORE operation for resetting to the original zero value"
36+
},
37+
"sstoreCleanRefundEIP2200": {
38+
"v": 4900,
39+
"d": "Once per SSTORE operation for resetting to the original non-zero value"
40+
},
41+
"call": {
42+
"v": 0,
43+
"d": "Base fee of the CALL opcode"
44+
},
45+
"callcode": {
46+
"v": 0,
47+
"d": "Base fee of the CALLCODE opcode"
48+
},
49+
"delegatecall": {
50+
"v": 0,
51+
"d": "Base fee of the DELEGATECALL opcode"
52+
},
53+
"staticcall": {
54+
"v": 0,
55+
"d": "Base fee of the STATICCALL opcode"
56+
},
57+
"balance": {
58+
"v": 0,
59+
"d": "Base fee of the BALANCE opcode"
60+
},
61+
"extcodesize": {
62+
"v": 0,
63+
"d": "Base fee of the EXTCODESIZE opcode"
64+
},
65+
"extcodecopy": {
66+
"v": 0,
67+
"d": "Base fee of the EXTCODECOPY opcode"
68+
},
69+
"extcodehash": {
70+
"v": 0,
71+
"d": "Base fee of the EXTCODEHASH opcode"
72+
},
73+
"sload": {
74+
"v": 0,
75+
"d": "Base fee of the SLOAD opcode"
76+
},
77+
"sstore": {
78+
"v": 0,
79+
"d": "Base fee of the SSTORE opcode"
2080
}
2181
},
2282
"vm": {},

packages/common/src/hardforks/berlin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
"comment": "HF targeted for July 2020 following the Muir Glacier HF",
44
"url": "https://eips.ethereum.org/EIPS/eip-2070",
55
"status": "Draft",
6-
"eips": [ 2315 ]
6+
"eips": [ 2315, 2565, 2929 ]
77
}

packages/vm/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ The following loggers are currently available:
213213
| `vm:tx:gas` | Transaction gas logger |
214214
| `vm:evm` | EVM control flow, CALL or CREATE message execution |
215215
| `vm:evm:gas` | EVM gas logger |
216+
| `vm:eei:gas` | EEI gas logger |
216217
| `vm:state`| StateManager logger |
217218
| `vm:ops` | Opcode traces |
218219
| `vm:ops:[Lower-case opcode name]` | Traces on a specific opcode |
@@ -231,6 +232,12 @@ Run all loggers currently available:
231232
DEBUG=vm:*,vm:*:* ts-node test.ts
232233
```
233234

235+
Run only the gas loggers:
236+
237+
```shell
238+
DEBUG=vm:*:gas ts-node test.ts
239+
```
240+
234241
Excluding the state logger:
235242

236243
```shell

packages/vm/lib/evm/eei.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { debug as createDebugLogger } from 'debug'
12
import { Account, Address, BN } from 'ethereumjs-util'
23
import { Block } from '@ethereumjs/block'
34
import Blockchain from '@ethereumjs/blockchain'
@@ -8,6 +9,8 @@ import Message from './message'
89
import EVM, { EVMResult } from './evm'
910
import { Log } from './types'
1011

12+
const debugGas = createDebugLogger('vm:eei:gas')
13+
1114
function trap(err: ERROR) {
1215
throw new VmError(err)
1316
}
@@ -84,10 +87,12 @@ export default class EEI {
8487
/**
8588
* Subtracts an amount from the gas counter.
8689
* @param amount - Amount of gas to consume
90+
* @param context - Usage context for debugging
8791
* @throws if out of gas
8892
*/
89-
useGas(amount: BN): void {
93+
useGas(amount: BN, context?: string): void {
9094
this._gasLeft.isub(amount)
95+
debugGas(`${context ? context + ': ' : ''}used ${amount} gas (-> ${this._gasLeft})`)
9196
if (this._gasLeft.ltn(0)) {
9297
this._gasLeft = new BN(0)
9398
trap(ERROR.OUT_OF_GAS)
@@ -97,16 +102,20 @@ export default class EEI {
97102
/**
98103
* Adds a positive amount to the gas counter.
99104
* @param amount - Amount of gas refunded
105+
* @param context - Usage context for debugging
100106
*/
101-
refundGas(amount: BN): void {
107+
refundGas(amount: BN, context?: string): void {
108+
debugGas(`${context ? context + ': ' : ''}refund ${amount} gas (-> ${this._evm._refund})`)
102109
this._evm._refund.iadd(amount)
103110
}
104111

105112
/**
106113
* Reduces amount of gas to be refunded by a positive value.
107114
* @param amount - Amount to subtract from gas refunds
115+
* @param context - Usage context for debugging
108116
*/
109-
subRefund(amount: BN): void {
117+
subRefund(amount: BN, context?: string): void {
118+
debugGas(`${context ? context + ': ' : ''}sub gas refund ${amount} (-> ${this._evm._refund})`)
110119
this._evm._refund.isub(amount)
111120
if (this._evm._refund.ltn(0)) {
112121
this._evm._refund = new BN(0)
@@ -499,7 +508,7 @@ export default class EEI {
499508
}
500509

501510
// this should always be safe
502-
this.useGas(results.gasUsed)
511+
this.useGas(results.gasUsed, 'CALL, STATICCALL, DELEGATECALL, CALLCODE')
503512

504513
// Set return value
505514
if (
@@ -556,7 +565,7 @@ export default class EEI {
556565
}
557566

558567
// this should always be safe
559-
this.useGas(results.gasUsed)
568+
this.useGas(results.gasUsed, 'CREATE')
560569

561570
// Set return buffer in case revert happened
562571
if (

packages/vm/lib/evm/evm.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ export default class EVM {
137137
async executeMessage(message: Message): Promise<EVMResult> {
138138
await this._vm._emit('beforeMessage', message)
139139

140+
if (!message.to && this._vm._common.isActivatedEIP(2929)) {
141+
message.code = message.data
142+
;(<any>this._state).addWarmedAddress((await this._generateAddress(message)).buf)
143+
}
144+
140145
await this._state.checkpoint()
141146
debug('-'.repeat(100))
142147
debug(`message checkpoint`)
@@ -258,6 +263,7 @@ export default class EVM {
258263
message.to = await this._generateAddress(message)
259264
debug(`Generated CREATE contract address ${message.to.toString()}`)
260265
let toAccount = await this._state.getAccount(message.to)
266+
261267
// Check for collision
262268
if ((toAccount.nonce && toAccount.nonce.gtn(0)) || !toAccount.codeHash.equals(KECCAK256_NULL)) {
263269
debug(`Returning on address collision`)

packages/vm/lib/evm/interpreter.ts

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { ERROR, VmError } from '../exceptions'
66
import Memory from './memory'
77
import Stack from './stack'
88
import EEI from './eei'
9-
import { precompiles } from './precompiles'
109
import { Opcode, handlers as opHandlers, OpHandler, AsyncOpHandler } from './opcodes'
1110

1211
export interface InterpreterOpts {
@@ -27,8 +26,6 @@ export interface RunState {
2726
_common: Common
2827
stateManager: StateManager
2928
eei: EEI
30-
accessedAddresses: Set<string>
31-
accessedStorage: Map<string, Set<string>>
3229
}
3330

3431
export interface InterpreterResult {
@@ -91,8 +88,6 @@ export default class Interpreter {
9188
_common: this._vm._common,
9289
stateManager: this._state,
9390
eei: this._eei,
94-
accessedAddresses: new Set(),
95-
accessedStorage: new Map(),
9691
}
9792
}
9893

@@ -103,8 +98,6 @@ export default class Interpreter {
10398
const valid = this._getValidJumpDests(code)
10499
this._runState.validJumps = valid.jumps
105100
this._runState.validJumpSubs = valid.jumpSubs
106-
this._initAccessedAddresses()
107-
this._runState.accessedStorage.clear()
108101

109102
// Check that the programCounter is in range
110103
const pc = this._runState.programCounter
@@ -149,7 +142,7 @@ export default class Interpreter {
149142
}
150143

151144
// Reduce opcode's base fee
152-
this._eei.useGas(new BN(opInfo.fee))
145+
this._eei.useGas(new BN(opInfo.fee), `${opInfo.name} (base fee)`)
153146
// Advance program counter
154147
this._runState.programCounter++
155148

@@ -205,7 +198,6 @@ export default class Interpreter {
205198
})
206199

207200
const name = eventObj.opcode.name
208-
const nameLC = name.toLowerCase()
209201
const opTrace = {
210202
pc: eventObj.pc,
211203
op: name,
@@ -215,10 +207,10 @@ export default class Interpreter {
215207
depth: eventObj.depth,
216208
}
217209

218-
if (!(nameLC in this.opDebuggers)) {
219-
this.opDebuggers[nameLC] = createDebugLogger(`vm:ops:${nameLC}`)
210+
if (!(name in this.opDebuggers)) {
211+
this.opDebuggers[name] = createDebugLogger(`vm:ops:${name}`)
220212
}
221-
this.opDebuggers[nameLC](JSON.stringify(opTrace))
213+
this.opDebuggers[name](JSON.stringify(opTrace))
222214

223215
/**
224216
* The `step` event for trace output
@@ -264,17 +256,4 @@ export default class Interpreter {
264256

265257
return { jumps, jumpSubs }
266258
}
267-
268-
// Populates accessedAddresses with 'pre-warmed' addresses. Includes
269-
// tx.origin, `this` (e.g the address of the code being executed), and
270-
// all the precompiles. (EIP 2929)
271-
_initAccessedAddresses() {
272-
this._runState.accessedAddresses.clear()
273-
this._runState.accessedAddresses.add(this._eei._env.origin.toString())
274-
this._runState.accessedAddresses.add(this._eei.getAddress().toString())
275-
276-
for (const address of Object.keys(precompiles)) {
277-
this._runState.accessedAddresses.add(`0x${address}`)
278-
}
279-
}
280259
}

packages/vm/lib/evm/opcodes/EIP1283.ts

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,45 +12,70 @@ export function updateSstoreGasEIP1283(runState: RunState, found: any, value: Bu
1212
const { original, current } = found
1313
if (current.equals(value)) {
1414
// If current value equals new value (this is a no-op), 200 gas is deducted.
15-
runState.eei.useGas(new BN(runState._common.param('gasPrices', 'netSstoreNoopGas')))
15+
runState.eei.useGas(
16+
new BN(runState._common.param('gasPrices', 'netSstoreNoopGas')),
17+
'EIP-1283 -> netSstoreNoopGas'
18+
)
1619
return
1720
}
1821
// If current value does not equal new value
1922
if (original.equals(current)) {
2023
// If original value equals current value (this storage slot has not been changed by the current execution context)
2124
if (original.length === 0) {
2225
// If original value is 0, 20000 gas is deducted.
23-
return runState.eei.useGas(new BN(runState._common.param('gasPrices', 'netSstoreInitGas')))
26+
return runState.eei.useGas(
27+
new BN(runState._common.param('gasPrices', 'netSstoreInitGas')),
28+
'EIP-1283 -> netSstoreInitGas'
29+
)
2430
}
2531
if (value.length === 0) {
2632
// If new value is 0, add 15000 gas to refund counter.
27-
runState.eei.refundGas(new BN(runState._common.param('gasPrices', 'netSstoreClearRefund')))
33+
runState.eei.refundGas(
34+
new BN(runState._common.param('gasPrices', 'netSstoreClearRefund')),
35+
'EIP-1283 -> netSstoreClearRefund'
36+
)
2837
}
2938
// Otherwise, 5000 gas is deducted.
30-
return runState.eei.useGas(new BN(runState._common.param('gasPrices', 'netSstoreCleanGas')))
39+
return runState.eei.useGas(
40+
new BN(runState._common.param('gasPrices', 'netSstoreCleanGas')),
41+
'EIP-1283 -> netSstoreCleanGas'
42+
)
3143
}
3244
// If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses.
3345
if (original.length !== 0) {
3446
// If original value is not 0
3547
if (current.length === 0) {
3648
// If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0.
37-
runState.eei.subRefund(new BN(runState._common.param('gasPrices', 'netSstoreClearRefund')))
49+
runState.eei.subRefund(
50+
new BN(runState._common.param('gasPrices', 'netSstoreClearRefund')),
51+
'EIP-1283 -> netSstoreClearRefund'
52+
)
3853
} else if (value.length === 0) {
3954
// If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter.
40-
runState.eei.refundGas(new BN(runState._common.param('gasPrices', 'netSstoreClearRefund')))
55+
runState.eei.refundGas(
56+
new BN(runState._common.param('gasPrices', 'netSstoreClearRefund')),
57+
'EIP-1283 -> netSstoreClearRefund'
58+
)
4159
}
4260
}
4361
if (original.equals(value)) {
4462
// If original value equals new value (this storage slot is reset)
4563
if (original.length === 0) {
4664
// If original value is 0, add 19800 gas to refund counter.
4765
runState.eei.refundGas(
48-
new BN(runState._common.param('gasPrices', 'netSstoreResetClearRefund'))
66+
new BN(runState._common.param('gasPrices', 'netSstoreResetClearRefund')),
67+
'EIP-1283 -> netSstoreResetClearRefund'
4968
)
5069
} else {
5170
// Otherwise, add 4800 gas to refund counter.
52-
runState.eei.refundGas(new BN(runState._common.param('gasPrices', 'netSstoreResetRefund')))
71+
runState.eei.refundGas(
72+
new BN(runState._common.param('gasPrices', 'netSstoreResetRefund')),
73+
'EIP-1283 -> netSstoreResetRefund'
74+
)
5375
}
5476
}
55-
return runState.eei.useGas(new BN(runState._common.param('gasPrices', 'netSstoreDirtyGas')))
77+
return runState.eei.useGas(
78+
new BN(runState._common.param('gasPrices', 'netSstoreDirtyGas')),
79+
'EIP-1283 -> netSstoreDirtyGas'
80+
)
5681
}

0 commit comments

Comments
 (0)