Skip to content

Commit cf66745

Browse files
chfastholiman
andauthored
core/vm: use uint256 in EVM implementation (#20787)
* core/vm: use fixed uint256 library instead of big * core/vm: remove intpools * core/vm: upgrade uint256, fixes uint256.NewFromBig * core/vm: use uint256.Int by value in Stack * core/vm: upgrade uint256 to v1.0.0 * core/vm: don't preallocate space for 1024 stack items (only 16) Co-authored-by: Martin Holst Swende <martin@swende.se>
1 parent 39abd92 commit cf66745

21 files changed

+358
-703
lines changed

Diff for: core/vm/common.go

+5-22
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,14 @@
1717
package vm
1818

1919
import (
20-
"math/big"
21-
2220
"github.com/ethereum/go-ethereum/common"
2321
"github.com/ethereum/go-ethereum/common/math"
22+
"github.com/holiman/uint256"
2423
)
2524

2625
// calcMemSize64 calculates the required memory size, and returns
2726
// the size and whether the result overflowed uint64
28-
func calcMemSize64(off, l *big.Int) (uint64, bool) {
27+
func calcMemSize64(off, l *uint256.Int) (uint64, bool) {
2928
if !l.IsUint64() {
3029
return 0, true
3130
}
@@ -35,16 +34,16 @@ func calcMemSize64(off, l *big.Int) (uint64, bool) {
3534
// calcMemSize64WithUint calculates the required memory size, and returns
3635
// the size and whether the result overflowed uint64
3736
// Identical to calcMemSize64, but length is a uint64
38-
func calcMemSize64WithUint(off *big.Int, length64 uint64) (uint64, bool) {
37+
func calcMemSize64WithUint(off *uint256.Int, length64 uint64) (uint64, bool) {
3938
// if length is zero, memsize is always zero, regardless of offset
4039
if length64 == 0 {
4140
return 0, false
4241
}
4342
// Check that offset doesn't overflow
44-
if !off.IsUint64() {
43+
offset64, overflow := off.Uint64WithOverflow()
44+
if overflow {
4545
return 0, true
4646
}
47-
offset64 := off.Uint64()
4847
val := offset64 + length64
4948
// if value < either of it's parts, then it overflowed
5049
return val, val < offset64
@@ -64,22 +63,6 @@ func getData(data []byte, start uint64, size uint64) []byte {
6463
return common.RightPadBytes(data[start:end], int(size))
6564
}
6665

67-
// getDataBig returns a slice from the data based on the start and size and pads
68-
// up to size with zero's. This function is overflow safe.
69-
func getDataBig(data []byte, start *big.Int, size *big.Int) []byte {
70-
dlen := big.NewInt(int64(len(data)))
71-
72-
s := math.BigMin(start, dlen)
73-
e := math.BigMin(new(big.Int).Add(s, size), dlen)
74-
return common.RightPadBytes(data[s.Uint64():e.Uint64()], int(size.Uint64()))
75-
}
76-
77-
// bigUint64 returns the integer casted to a uint64 and returns whether it
78-
// overflowed in the process.
79-
func bigUint64(v *big.Int) (uint64, bool) {
80-
return v.Uint64(), !v.IsUint64()
81-
}
82-
8366
// toWordSize returns the ceiled word size required for memory expansion.
8467
func toWordSize(size uint64) uint64 {
8568
if size > math.MaxUint64-31 {

Diff for: core/vm/contract.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"math/big"
2121

2222
"github.com/ethereum/go-ethereum/common"
23+
"github.com/holiman/uint256"
2324
)
2425

2526
// ContractRef is a reference to the contract's backing object
@@ -81,11 +82,11 @@ func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uin
8182
return c
8283
}
8384

84-
func (c *Contract) validJumpdest(dest *big.Int) bool {
85-
udest := dest.Uint64()
86-
// PC cannot go beyond len(code) and certainly can't be bigger than 63 bits.
85+
func (c *Contract) validJumpdest(dest *uint256.Int) bool {
86+
udest, overflow := dest.Uint64WithOverflow()
87+
// PC cannot go beyond len(code) and certainly can't be bigger than 63bits.
8788
// Don't bother checking for JUMPDEST in that case.
88-
if dest.BitLen() >= 63 || udest >= uint64(len(c.Code)) {
89+
if overflow || udest >= uint64(len(c.Code)) {
8990
return false
9091
}
9192
// Only JUMPDESTs allowed for destinations

Diff for: core/vm/eips.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"fmt"
2121

2222
"github.com/ethereum/go-ethereum/params"
23+
"github.com/holiman/uint256"
2324
)
2425

2526
// EnableEIP enables the given EIP on the config.
@@ -63,7 +64,7 @@ func enable1884(jt *JumpTable) {
6364
}
6465

6566
func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
66-
balance := interpreter.intPool.get().Set(interpreter.evm.StateDB.GetBalance(callContext.contract.Address()))
67+
balance, _ := uint256.FromBig(interpreter.evm.StateDB.GetBalance(callContext.contract.Address()))
6768
callContext.stack.push(balance)
6869
return nil, nil
6970
}
@@ -83,7 +84,7 @@ func enable1344(jt *JumpTable) {
8384

8485
// opChainID implements CHAINID opcode
8586
func opChainID(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
86-
chainId := interpreter.intPool.get().Set(interpreter.evm.chainConfig.ChainID)
87+
chainId, _ := uint256.FromBig(interpreter.evm.chainConfig.ChainID)
8788
callContext.stack.push(chainId)
8889
return nil, nil
8990
}

Diff for: core/vm/evm.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
352352
// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
353353
// but is the correct thing to do and matters on other networks, in tests, and potential
354354
// future scenarios
355-
evm.StateDB.AddBalance(addr, bigZero)
355+
evm.StateDB.AddBalance(addr, big.NewInt(0))
356356

357357
// When an error was returned by the EVM or when setting the creation code
358358
// above we revert to the snapshot and consume any gas remaining. Additionally

Diff for: core/vm/gas.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
package vm
1818

1919
import (
20-
"math/big"
20+
"github.com/holiman/uint256"
2121
)
2222

2323
// Gas costs
@@ -34,7 +34,7 @@ const (
3434
//
3535
// The cost of gas was changed during the homestead price change HF.
3636
// As part of EIP 150 (TangerineWhistle), the returned gas is gas - base * 63 / 64.
37-
func callGas(isEip150 bool, availableGas, base uint64, callCost *big.Int) (uint64, error) {
37+
func callGas(isEip150 bool, availableGas, base uint64, callCost *uint256.Int) (uint64, error) {
3838
if isEip150 {
3939
availableGas = availableGas - base
4040
gas := availableGas - availableGas/64

Diff for: core/vm/gas_table.go

+13-13
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ func memoryCopierGas(stackpos int) gasFunc {
7070
return 0, err
7171
}
7272
// And gas for copying data, charged per word at param.CopyGas
73-
words, overflow := bigUint64(stack.Back(stackpos))
73+
words, overflow := stack.Back(stackpos).Uint64WithOverflow()
7474
if overflow {
7575
return 0, ErrGasUintOverflow
7676
}
@@ -96,7 +96,7 @@ var (
9696
func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
9797
var (
9898
y, x = stack.Back(1), stack.Back(0)
99-
current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
99+
current = evm.StateDB.GetState(contract.Address(), common.Hash(x.Bytes32()))
100100
)
101101
// The legacy gas metering only takes into consideration the current state
102102
// Legacy rules should be applied if we are in Petersburg (removal of EIP-1283)
@@ -131,11 +131,11 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
131131
// 2.2.2. If original value equals new value (this storage slot is reset)
132132
// 2.2.2.1. If original value is 0, add 19800 gas to refund counter.
133133
// 2.2.2.2. Otherwise, add 4800 gas to refund counter.
134-
value := common.BigToHash(y)
134+
value := common.Hash(y.Bytes32())
135135
if current == value { // noop (1)
136136
return params.NetSstoreNoopGas, nil
137137
}
138-
original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x))
138+
original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32()))
139139
if original == current {
140140
if original == (common.Hash{}) { // create slot (2.1.1)
141141
return params.NetSstoreInitGas, nil
@@ -183,14 +183,14 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
183183
// Gas sentry honoured, do the actual gas calculation based on the stored value
184184
var (
185185
y, x = stack.Back(1), stack.Back(0)
186-
current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
186+
current = evm.StateDB.GetState(contract.Address(), common.Hash(x.Bytes32()))
187187
)
188-
value := common.BigToHash(y)
188+
value := common.Hash(y.Bytes32())
189189

190190
if current == value { // noop (1)
191191
return params.SstoreNoopGasEIP2200, nil
192192
}
193-
original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x))
193+
original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32()))
194194
if original == current {
195195
if original == (common.Hash{}) { // create slot (2.1.1)
196196
return params.SstoreInitGasEIP2200, nil
@@ -219,7 +219,7 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
219219

220220
func makeGasLog(n uint64) gasFunc {
221221
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
222-
requestedSize, overflow := bigUint64(stack.Back(1))
222+
requestedSize, overflow := stack.Back(1).Uint64WithOverflow()
223223
if overflow {
224224
return 0, ErrGasUintOverflow
225225
}
@@ -252,7 +252,7 @@ func gasSha3(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
252252
if err != nil {
253253
return 0, err
254254
}
255-
wordGas, overflow := bigUint64(stack.Back(1))
255+
wordGas, overflow := stack.Back(1).Uint64WithOverflow()
256256
if overflow {
257257
return 0, ErrGasUintOverflow
258258
}
@@ -286,7 +286,7 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS
286286
if err != nil {
287287
return 0, err
288288
}
289-
wordGas, overflow := bigUint64(stack.Back(2))
289+
wordGas, overflow := stack.Back(2).Uint64WithOverflow()
290290
if overflow {
291291
return 0, ErrGasUintOverflow
292292
}
@@ -328,8 +328,8 @@ func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
328328
func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
329329
var (
330330
gas uint64
331-
transfersValue = stack.Back(2).Sign() != 0
332-
address = common.BigToAddress(stack.Back(1))
331+
transfersValue = !stack.Back(2).IsZero()
332+
address = common.Address(stack.Back(1).Bytes20())
333333
)
334334
if evm.chainRules.IsEIP158 {
335335
if transfersValue && evm.StateDB.Empty(address) {
@@ -422,7 +422,7 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
422422
// EIP150 homestead gas reprice fork:
423423
if evm.chainRules.IsEIP150 {
424424
gas = params.SelfdestructGasEIP150
425-
var address = common.BigToAddress(stack.Back(0))
425+
var address = common.Address(stack.Back(0).Bytes20())
426426

427427
if evm.chainRules.IsEIP158 {
428428
// if empty and transfers value

0 commit comments

Comments
 (0)