Skip to content

Commit 9590b44

Browse files
mergify[bot]fedekunzefacs95
authored
imp(vm): define default JumpTable (backport ethereum#3) (ethereum#13)
Co-authored-by: Federico Kunze Küllmer <[email protected]> Co-authored-by: Freddy Caceres <[email protected]>
1 parent 754e68b commit 9590b44

File tree

4 files changed

+126
-50
lines changed

4 files changed

+126
-50
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
<!--
32
Guiding Principles:
43
@@ -40,4 +39,5 @@ Ref: https://keepachangelog.com/en/1.0.0/
4039

4140
### Improvements
4241

42+
* [#3](https://github.com/evmos/go-ethereum/pull/3) Move the `JumpTable` defaults to a separate function.
4343
* [#2](https://github.com/evmos/go-ethereum/pull/2) Define `Interpreter` interface for the EVM.

core/vm/interpreter.go

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -70,36 +70,16 @@ type EVMInterpreter struct {
7070
func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
7171
// If jump table was not initialised we set the default one.
7272
if cfg.JumpTable == nil {
73-
switch {
74-
case evm.chainRules.IsMerge:
75-
cfg.JumpTable = &mergeInstructionSet
76-
case evm.chainRules.IsLondon:
77-
cfg.JumpTable = &londonInstructionSet
78-
case evm.chainRules.IsBerlin:
79-
cfg.JumpTable = &berlinInstructionSet
80-
case evm.chainRules.IsIstanbul:
81-
cfg.JumpTable = &istanbulInstructionSet
82-
case evm.chainRules.IsConstantinople:
83-
cfg.JumpTable = &constantinopleInstructionSet
84-
case evm.chainRules.IsByzantium:
85-
cfg.JumpTable = &byzantiumInstructionSet
86-
case evm.chainRules.IsEIP158:
87-
cfg.JumpTable = &spuriousDragonInstructionSet
88-
case evm.chainRules.IsEIP150:
89-
cfg.JumpTable = &tangerineWhistleInstructionSet
90-
case evm.chainRules.IsHomestead:
91-
cfg.JumpTable = &homesteadInstructionSet
92-
default:
93-
cfg.JumpTable = &frontierInstructionSet
94-
}
73+
cfg.JumpTable = DefaultJumpTable(evm.chainRules)
9574
for i, eip := range cfg.ExtraEips {
96-
copy := *cfg.JumpTable
97-
if err := EnableEIP(eip, &copy); err != nil {
75+
// Deep-copy jumptable to prevent modification of opcodes in other tables
76+
copy := CopyJumpTable(cfg.JumpTable)
77+
if err := EnableEIP(eip, copy); err != nil {
9878
// Disable it, so caller can check if it's activated or not
9979
cfg.ExtraEips = append(cfg.ExtraEips[:i], cfg.ExtraEips[i+1:]...)
10080
log.Error("EIP activation failed", "eip", eip, "error", err)
10181
}
102-
cfg.JumpTable = &copy
82+
cfg.JumpTable = copy
10383
}
10484
}
10585

core/vm/jump_table.go

Lines changed: 85 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -45,37 +45,76 @@ type operation struct {
4545
}
4646

4747
var (
48-
frontierInstructionSet = newFrontierInstructionSet()
49-
homesteadInstructionSet = newHomesteadInstructionSet()
50-
tangerineWhistleInstructionSet = newTangerineWhistleInstructionSet()
51-
spuriousDragonInstructionSet = newSpuriousDragonInstructionSet()
52-
byzantiumInstructionSet = newByzantiumInstructionSet()
53-
constantinopleInstructionSet = newConstantinopleInstructionSet()
54-
istanbulInstructionSet = newIstanbulInstructionSet()
55-
berlinInstructionSet = newBerlinInstructionSet()
56-
londonInstructionSet = newLondonInstructionSet()
57-
mergeInstructionSet = newMergeInstructionSet()
48+
FrontierInstructionSet = newFrontierInstructionSet()
49+
HomesteadInstructionSet = newHomesteadInstructionSet()
50+
TangerineWhistleInstructionSet = newTangerineWhistleInstructionSet()
51+
SpuriousDragonInstructionSet = newSpuriousDragonInstructionSet()
52+
ByzantiumInstructionSet = newByzantiumInstructionSet()
53+
ConstantinopleInstructionSet = newConstantinopleInstructionSet()
54+
IstanbulInstructionSet = newIstanbulInstructionSet()
55+
BerlinInstructionSet = newBerlinInstructionSet()
56+
LondonInstructionSet = newLondonInstructionSet()
57+
MergeInstructionSet = newMergeInstructionSet()
5858
)
5959

6060
// JumpTable contains the EVM opcodes supported at a given fork.
6161
type JumpTable [256]*operation
6262

63-
func validate(jt JumpTable) JumpTable {
63+
// DefaultJumpTable defines the default jump table used by the EVM interpreter.
64+
func DefaultJumpTable(rules params.Rules) (jumpTable *JumpTable) {
65+
switch {
66+
case rules.IsMerge:
67+
jumpTable = &MergeInstructionSet
68+
case rules.IsLondon:
69+
jumpTable = &LondonInstructionSet
70+
case rules.IsBerlin:
71+
jumpTable = &BerlinInstructionSet
72+
case rules.IsIstanbul:
73+
jumpTable = &IstanbulInstructionSet
74+
case rules.IsConstantinople:
75+
jumpTable = &ConstantinopleInstructionSet
76+
case rules.IsByzantium:
77+
jumpTable = &ByzantiumInstructionSet
78+
case rules.IsEIP158:
79+
jumpTable = &SpuriousDragonInstructionSet
80+
case rules.IsEIP150:
81+
jumpTable = &TangerineWhistleInstructionSet
82+
case rules.IsHomestead:
83+
jumpTable = &HomesteadInstructionSet
84+
default:
85+
jumpTable = &FrontierInstructionSet
86+
}
87+
88+
return jumpTable
89+
}
90+
91+
// Validate checks if all the operations are set and if they are valid according to the
92+
// interpreter assumptions.
93+
func (jt JumpTable) Validate() error {
6494
for i, op := range jt {
6595
if op == nil {
66-
panic(fmt.Sprintf("op %#x is not set", i))
96+
return fmt.Errorf("op %#x is not set", i)
6797
}
98+
6899
// The interpreter has an assumption that if the memorySize function is
69100
// set, then the dynamicGas function is also set. This is a somewhat
70101
// arbitrary assumption, and can be removed if we need to -- but it
71102
// allows us to avoid a condition check. As long as we have that assumption
72103
// in there, this little sanity check prevents us from merging in a
73104
// change which violates it.
74105
if op.memorySize != nil && op.dynamicGas == nil {
75-
panic(fmt.Sprintf("op %v has dynamic memory but not dynamic gas", OpCode(i).String()))
106+
return fmt.Errorf("op %v has dynamic memory but not dynamic gas", OpCode(i).String())
76107
}
77108
}
78-
return jt
109+
110+
return nil
111+
}
112+
113+
// MustValidate panics if the operations are not valid.
114+
func (jt JumpTable) MustValidate() {
115+
if err := jt.Validate(); err != nil {
116+
panic(err)
117+
}
79118
}
80119

81120
func newMergeInstructionSet() JumpTable {
@@ -86,7 +125,8 @@ func newMergeInstructionSet() JumpTable {
86125
minStack: minStack(0, 1),
87126
maxStack: maxStack(0, 1),
88127
}
89-
return validate(instructionSet)
128+
instructionSet.MustValidate()
129+
return instructionSet
90130
}
91131

92132
// newLondonInstructionSet returns the frontier, homestead, byzantium,
@@ -95,15 +135,17 @@ func newLondonInstructionSet() JumpTable {
95135
instructionSet := newBerlinInstructionSet()
96136
enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529
97137
enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198
98-
return validate(instructionSet)
138+
instructionSet.MustValidate()
139+
return instructionSet
99140
}
100141

101142
// newBerlinInstructionSet returns the frontier, homestead, byzantium,
102143
// contantinople, istanbul, petersburg and berlin instructions.
103144
func newBerlinInstructionSet() JumpTable {
104145
instructionSet := newIstanbulInstructionSet()
105146
enable2929(&instructionSet) // Access lists for trie accesses https://eips.ethereum.org/EIPS/eip-2929
106-
return validate(instructionSet)
147+
instructionSet.MustValidate()
148+
return instructionSet
107149
}
108150

109151
// newIstanbulInstructionSet returns the frontier, homestead, byzantium,
@@ -115,7 +157,8 @@ func newIstanbulInstructionSet() JumpTable {
115157
enable1884(&instructionSet) // Reprice reader opcodes - https://eips.ethereum.org/EIPS/eip-1884
116158
enable2200(&instructionSet) // Net metered SSTORE - https://eips.ethereum.org/EIPS/eip-2200
117159

118-
return validate(instructionSet)
160+
instructionSet.MustValidate()
161+
return instructionSet
119162
}
120163

121164
// newConstantinopleInstructionSet returns the frontier, homestead,
@@ -154,7 +197,8 @@ func newConstantinopleInstructionSet() JumpTable {
154197
maxStack: maxStack(4, 1),
155198
memorySize: memoryCreate2,
156199
}
157-
return validate(instructionSet)
200+
instructionSet.MustValidate()
201+
return instructionSet
158202
}
159203

160204
// newByzantiumInstructionSet returns the frontier, homestead and
@@ -190,14 +234,16 @@ func newByzantiumInstructionSet() JumpTable {
190234
maxStack: maxStack(2, 0),
191235
memorySize: memoryRevert,
192236
}
193-
return validate(instructionSet)
237+
instructionSet.MustValidate()
238+
return instructionSet
194239
}
195240

196241
// EIP 158 a.k.a Spurious Dragon
197242
func newSpuriousDragonInstructionSet() JumpTable {
198243
instructionSet := newTangerineWhistleInstructionSet()
199244
instructionSet[EXP].dynamicGas = gasExpEIP158
200-
return validate(instructionSet)
245+
instructionSet.MustValidate()
246+
return instructionSet
201247
}
202248

203249
// EIP 150 a.k.a Tangerine Whistle
@@ -210,7 +256,8 @@ func newTangerineWhistleInstructionSet() JumpTable {
210256
instructionSet[CALL].constantGas = params.CallGasEIP150
211257
instructionSet[CALLCODE].constantGas = params.CallGasEIP150
212258
instructionSet[DELEGATECALL].constantGas = params.CallGasEIP150
213-
return validate(instructionSet)
259+
instructionSet.MustValidate()
260+
return instructionSet
214261
}
215262

216263
// newHomesteadInstructionSet returns the frontier and homestead
@@ -225,7 +272,8 @@ func newHomesteadInstructionSet() JumpTable {
225272
maxStack: maxStack(6, 1),
226273
memorySize: memoryDelegateCall,
227274
}
228-
return validate(instructionSet)
275+
instructionSet.MustValidate()
276+
return instructionSet
229277
}
230278

231279
// newFrontierInstructionSet returns the frontier instructions
@@ -1041,5 +1089,18 @@ func newFrontierInstructionSet() JumpTable {
10411089
}
10421090
}
10431091

1044-
return validate(tbl)
1092+
tbl.MustValidate()
1093+
return tbl
1094+
}
1095+
1096+
// CopyJumpTable creates copy of the operations from the provided source JumpTable.
1097+
func CopyJumpTable(source *JumpTable) *JumpTable {
1098+
dest := *source
1099+
for i, op := range source {
1100+
if op != nil {
1101+
opCopy := *op
1102+
dest[i] = &opCopy
1103+
}
1104+
}
1105+
return &dest
10451106
}

core/vm/jump_table_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2022 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package vm
18+
19+
import (
20+
"testing"
21+
22+
"github.com/stretchr/testify/require"
23+
)
24+
25+
// TestJumpTableCopy tests that deep copy is necessery to prevent modify shared jump table
26+
func TestJumpTableCopy(t *testing.T) {
27+
tbl := newMergeInstructionSet()
28+
require.Equal(t, uint64(0), tbl[SLOAD].constantGas)
29+
30+
// a deep copy won't modify the shared jump table
31+
deepCopy := CopyJumpTable(&tbl)
32+
deepCopy[SLOAD].constantGas = 100
33+
require.Equal(t, uint64(100), deepCopy[SLOAD].constantGas)
34+
require.Equal(t, uint64(0), tbl[SLOAD].constantGas)
35+
}

0 commit comments

Comments
 (0)