forked from cerc-io/plugeth
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>
This commit is contained in:
parent
39abd92ca8
commit
cf6674539c
@ -17,15 +17,14 @@
|
|||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
// calcMemSize64 calculates the required memory size, and returns
|
// calcMemSize64 calculates the required memory size, and returns
|
||||||
// the size and whether the result overflowed uint64
|
// the size and whether the result overflowed uint64
|
||||||
func calcMemSize64(off, l *big.Int) (uint64, bool) {
|
func calcMemSize64(off, l *uint256.Int) (uint64, bool) {
|
||||||
if !l.IsUint64() {
|
if !l.IsUint64() {
|
||||||
return 0, true
|
return 0, true
|
||||||
}
|
}
|
||||||
@ -35,16 +34,16 @@ func calcMemSize64(off, l *big.Int) (uint64, bool) {
|
|||||||
// calcMemSize64WithUint calculates the required memory size, and returns
|
// calcMemSize64WithUint calculates the required memory size, and returns
|
||||||
// the size and whether the result overflowed uint64
|
// the size and whether the result overflowed uint64
|
||||||
// Identical to calcMemSize64, but length is a uint64
|
// Identical to calcMemSize64, but length is a uint64
|
||||||
func calcMemSize64WithUint(off *big.Int, length64 uint64) (uint64, bool) {
|
func calcMemSize64WithUint(off *uint256.Int, length64 uint64) (uint64, bool) {
|
||||||
// if length is zero, memsize is always zero, regardless of offset
|
// if length is zero, memsize is always zero, regardless of offset
|
||||||
if length64 == 0 {
|
if length64 == 0 {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
// Check that offset doesn't overflow
|
// Check that offset doesn't overflow
|
||||||
if !off.IsUint64() {
|
offset64, overflow := off.Uint64WithOverflow()
|
||||||
|
if overflow {
|
||||||
return 0, true
|
return 0, true
|
||||||
}
|
}
|
||||||
offset64 := off.Uint64()
|
|
||||||
val := offset64 + length64
|
val := offset64 + length64
|
||||||
// if value < either of it's parts, then it overflowed
|
// if value < either of it's parts, then it overflowed
|
||||||
return val, val < offset64
|
return val, val < offset64
|
||||||
@ -64,22 +63,6 @@ func getData(data []byte, start uint64, size uint64) []byte {
|
|||||||
return common.RightPadBytes(data[start:end], int(size))
|
return common.RightPadBytes(data[start:end], int(size))
|
||||||
}
|
}
|
||||||
|
|
||||||
// getDataBig returns a slice from the data based on the start and size and pads
|
|
||||||
// up to size with zero's. This function is overflow safe.
|
|
||||||
func getDataBig(data []byte, start *big.Int, size *big.Int) []byte {
|
|
||||||
dlen := big.NewInt(int64(len(data)))
|
|
||||||
|
|
||||||
s := math.BigMin(start, dlen)
|
|
||||||
e := math.BigMin(new(big.Int).Add(s, size), dlen)
|
|
||||||
return common.RightPadBytes(data[s.Uint64():e.Uint64()], int(size.Uint64()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// bigUint64 returns the integer casted to a uint64 and returns whether it
|
|
||||||
// overflowed in the process.
|
|
||||||
func bigUint64(v *big.Int) (uint64, bool) {
|
|
||||||
return v.Uint64(), !v.IsUint64()
|
|
||||||
}
|
|
||||||
|
|
||||||
// toWordSize returns the ceiled word size required for memory expansion.
|
// toWordSize returns the ceiled word size required for memory expansion.
|
||||||
func toWordSize(size uint64) uint64 {
|
func toWordSize(size uint64) uint64 {
|
||||||
if size > math.MaxUint64-31 {
|
if size > math.MaxUint64-31 {
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ContractRef is a reference to the contract's backing object
|
// 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
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Contract) validJumpdest(dest *big.Int) bool {
|
func (c *Contract) validJumpdest(dest *uint256.Int) bool {
|
||||||
udest := dest.Uint64()
|
udest, overflow := dest.Uint64WithOverflow()
|
||||||
// PC cannot go beyond len(code) and certainly can't be bigger than 63 bits.
|
// PC cannot go beyond len(code) and certainly can't be bigger than 63bits.
|
||||||
// Don't bother checking for JUMPDEST in that case.
|
// Don't bother checking for JUMPDEST in that case.
|
||||||
if dest.BitLen() >= 63 || udest >= uint64(len(c.Code)) {
|
if overflow || udest >= uint64(len(c.Code)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// Only JUMPDESTs allowed for destinations
|
// Only JUMPDESTs allowed for destinations
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EnableEIP enables the given EIP on the config.
|
// EnableEIP enables the given EIP on the config.
|
||||||
@ -63,7 +64,7 @@ func enable1884(jt *JumpTable) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||||
balance := interpreter.intPool.get().Set(interpreter.evm.StateDB.GetBalance(callContext.contract.Address()))
|
balance, _ := uint256.FromBig(interpreter.evm.StateDB.GetBalance(callContext.contract.Address()))
|
||||||
callContext.stack.push(balance)
|
callContext.stack.push(balance)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@ -83,7 +84,7 @@ func enable1344(jt *JumpTable) {
|
|||||||
|
|
||||||
// opChainID implements CHAINID opcode
|
// opChainID implements CHAINID opcode
|
||||||
func opChainID(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
func opChainID(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||||
chainId := interpreter.intPool.get().Set(interpreter.evm.chainConfig.ChainID)
|
chainId, _ := uint256.FromBig(interpreter.evm.chainConfig.ChainID)
|
||||||
callContext.stack.push(chainId)
|
callContext.stack.push(chainId)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -352,7 +352,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
|||||||
// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
|
// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
|
||||||
// but is the correct thing to do and matters on other networks, in tests, and potential
|
// but is the correct thing to do and matters on other networks, in tests, and potential
|
||||||
// future scenarios
|
// future scenarios
|
||||||
evm.StateDB.AddBalance(addr, bigZero)
|
evm.StateDB.AddBalance(addr, big.NewInt(0))
|
||||||
|
|
||||||
// When an error was returned by the EVM or when setting the creation code
|
// When an error was returned by the EVM or when setting the creation code
|
||||||
// above we revert to the snapshot and consume any gas remaining. Additionally
|
// above we revert to the snapshot and consume any gas remaining. Additionally
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/big"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Gas costs
|
// Gas costs
|
||||||
@ -34,7 +34,7 @@ const (
|
|||||||
//
|
//
|
||||||
// The cost of gas was changed during the homestead price change HF.
|
// The cost of gas was changed during the homestead price change HF.
|
||||||
// As part of EIP 150 (TangerineWhistle), the returned gas is gas - base * 63 / 64.
|
// As part of EIP 150 (TangerineWhistle), the returned gas is gas - base * 63 / 64.
|
||||||
func callGas(isEip150 bool, availableGas, base uint64, callCost *big.Int) (uint64, error) {
|
func callGas(isEip150 bool, availableGas, base uint64, callCost *uint256.Int) (uint64, error) {
|
||||||
if isEip150 {
|
if isEip150 {
|
||||||
availableGas = availableGas - base
|
availableGas = availableGas - base
|
||||||
gas := availableGas - availableGas/64
|
gas := availableGas - availableGas/64
|
||||||
|
@ -70,7 +70,7 @@ func memoryCopierGas(stackpos int) gasFunc {
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
// And gas for copying data, charged per word at param.CopyGas
|
// And gas for copying data, charged per word at param.CopyGas
|
||||||
words, overflow := bigUint64(stack.Back(stackpos))
|
words, overflow := stack.Back(stackpos).Uint64WithOverflow()
|
||||||
if overflow {
|
if overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
@ -96,7 +96,7 @@ var (
|
|||||||
func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
var (
|
var (
|
||||||
y, x = stack.Back(1), stack.Back(0)
|
y, x = stack.Back(1), stack.Back(0)
|
||||||
current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
|
current = evm.StateDB.GetState(contract.Address(), common.Hash(x.Bytes32()))
|
||||||
)
|
)
|
||||||
// The legacy gas metering only takes into consideration the current state
|
// The legacy gas metering only takes into consideration the current state
|
||||||
// Legacy rules should be applied if we are in Petersburg (removal of EIP-1283)
|
// 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
|
|||||||
// 2.2.2. If original value equals new value (this storage slot is reset)
|
// 2.2.2. If original value equals new value (this storage slot is reset)
|
||||||
// 2.2.2.1. If original value is 0, add 19800 gas to refund counter.
|
// 2.2.2.1. If original value is 0, add 19800 gas to refund counter.
|
||||||
// 2.2.2.2. Otherwise, add 4800 gas to refund counter.
|
// 2.2.2.2. Otherwise, add 4800 gas to refund counter.
|
||||||
value := common.BigToHash(y)
|
value := common.Hash(y.Bytes32())
|
||||||
if current == value { // noop (1)
|
if current == value { // noop (1)
|
||||||
return params.NetSstoreNoopGas, nil
|
return params.NetSstoreNoopGas, nil
|
||||||
}
|
}
|
||||||
original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x))
|
original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32()))
|
||||||
if original == current {
|
if original == current {
|
||||||
if original == (common.Hash{}) { // create slot (2.1.1)
|
if original == (common.Hash{}) { // create slot (2.1.1)
|
||||||
return params.NetSstoreInitGas, nil
|
return params.NetSstoreInitGas, nil
|
||||||
@ -183,14 +183,14 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
|||||||
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
||||||
var (
|
var (
|
||||||
y, x = stack.Back(1), stack.Back(0)
|
y, x = stack.Back(1), stack.Back(0)
|
||||||
current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
|
current = evm.StateDB.GetState(contract.Address(), common.Hash(x.Bytes32()))
|
||||||
)
|
)
|
||||||
value := common.BigToHash(y)
|
value := common.Hash(y.Bytes32())
|
||||||
|
|
||||||
if current == value { // noop (1)
|
if current == value { // noop (1)
|
||||||
return params.SstoreNoopGasEIP2200, nil
|
return params.SstoreNoopGasEIP2200, nil
|
||||||
}
|
}
|
||||||
original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x))
|
original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32()))
|
||||||
if original == current {
|
if original == current {
|
||||||
if original == (common.Hash{}) { // create slot (2.1.1)
|
if original == (common.Hash{}) { // create slot (2.1.1)
|
||||||
return params.SstoreInitGasEIP2200, nil
|
return params.SstoreInitGasEIP2200, nil
|
||||||
@ -219,7 +219,7 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
|||||||
|
|
||||||
func makeGasLog(n uint64) gasFunc {
|
func makeGasLog(n uint64) gasFunc {
|
||||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
requestedSize, overflow := bigUint64(stack.Back(1))
|
requestedSize, overflow := stack.Back(1).Uint64WithOverflow()
|
||||||
if overflow {
|
if overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
@ -252,7 +252,7 @@ func gasSha3(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
wordGas, overflow := bigUint64(stack.Back(1))
|
wordGas, overflow := stack.Back(1).Uint64WithOverflow()
|
||||||
if overflow {
|
if overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
@ -286,7 +286,7 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
wordGas, overflow := bigUint64(stack.Back(2))
|
wordGas, overflow := stack.Back(2).Uint64WithOverflow()
|
||||||
if overflow {
|
if overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
@ -328,8 +328,8 @@ func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
|
|||||||
func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
var (
|
var (
|
||||||
gas uint64
|
gas uint64
|
||||||
transfersValue = stack.Back(2).Sign() != 0
|
transfersValue = !stack.Back(2).IsZero()
|
||||||
address = common.BigToAddress(stack.Back(1))
|
address = common.Address(stack.Back(1).Bytes20())
|
||||||
)
|
)
|
||||||
if evm.chainRules.IsEIP158 {
|
if evm.chainRules.IsEIP158 {
|
||||||
if transfersValue && evm.StateDB.Empty(address) {
|
if transfersValue && evm.StateDB.Empty(address) {
|
||||||
@ -422,7 +422,7 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
|||||||
// EIP150 homestead gas reprice fork:
|
// EIP150 homestead gas reprice fork:
|
||||||
if evm.chainRules.IsEIP150 {
|
if evm.chainRules.IsEIP150 {
|
||||||
gas = params.SelfdestructGasEIP150
|
gas = params.SelfdestructGasEIP150
|
||||||
var address = common.BigToAddress(stack.Back(0))
|
var address = common.Address(stack.Back(0).Bytes20())
|
||||||
|
|
||||||
if evm.chainRules.IsEIP158 {
|
if evm.chainRules.IsEIP158 {
|
||||||
// if empty and transfers value
|
// if empty and transfers value
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -21,12 +21,12 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/big"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TwoOperandTestcase struct {
|
type TwoOperandTestcase struct {
|
||||||
@ -98,42 +98,23 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu
|
|||||||
pc = uint64(0)
|
pc = uint64(0)
|
||||||
evmInterpreter = env.interpreter.(*EVMInterpreter)
|
evmInterpreter = env.interpreter.(*EVMInterpreter)
|
||||||
)
|
)
|
||||||
// Stuff a couple of nonzero bigints into pool, to ensure that ops do not rely on pooled integers to be zero
|
|
||||||
evmInterpreter.intPool = poolOfIntPools.get()
|
|
||||||
evmInterpreter.intPool.put(big.NewInt(-1337))
|
|
||||||
evmInterpreter.intPool.put(big.NewInt(-1337))
|
|
||||||
evmInterpreter.intPool.put(big.NewInt(-1337))
|
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
x := new(big.Int).SetBytes(common.Hex2Bytes(test.X))
|
x := new(uint256.Int).SetBytes(common.Hex2Bytes(test.X))
|
||||||
y := new(big.Int).SetBytes(common.Hex2Bytes(test.Y))
|
y := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Y))
|
||||||
expected := new(big.Int).SetBytes(common.Hex2Bytes(test.Expected))
|
expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected))
|
||||||
stack.push(x)
|
stack.push(x)
|
||||||
stack.push(y)
|
stack.push(y)
|
||||||
opFn(&pc, evmInterpreter, &callCtx{nil, stack, rstack, nil})
|
opFn(&pc, evmInterpreter, &callCtx{nil, stack, rstack, nil})
|
||||||
|
if len(stack.data) != 1 {
|
||||||
|
t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data))
|
||||||
|
}
|
||||||
actual := stack.pop()
|
actual := stack.pop()
|
||||||
|
|
||||||
if actual.Cmp(expected) != 0 {
|
if actual.Cmp(expected) != 0 {
|
||||||
t.Errorf("Testcase %v %d, %v(%x, %x): expected %x, got %x", name, i, name, x, y, expected, actual)
|
t.Errorf("Testcase %v %d, %v(%x, %x): expected %x, got %x", name, i, name, x, y, expected, actual)
|
||||||
}
|
}
|
||||||
// Check pool usage
|
|
||||||
// 1.pool is not allowed to contain anything on the stack
|
|
||||||
// 2.pool is not allowed to contain the same pointers twice
|
|
||||||
if evmInterpreter.intPool.pool.len() > 0 {
|
|
||||||
|
|
||||||
poolvals := make(map[*big.Int]struct{})
|
|
||||||
poolvals[actual] = struct{}{}
|
|
||||||
|
|
||||||
for evmInterpreter.intPool.pool.len() > 0 {
|
|
||||||
key := evmInterpreter.intPool.get()
|
|
||||||
if _, exist := poolvals[key]; exist {
|
|
||||||
t.Errorf("Testcase %v %d, pool contains double-entry", name, i)
|
|
||||||
}
|
}
|
||||||
poolvals[key] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
poolOfIntPools.put(evmInterpreter.intPool)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestByteOp(t *testing.T) {
|
func TestByteOp(t *testing.T) {
|
||||||
@ -209,6 +190,44 @@ func TestSAR(t *testing.T) {
|
|||||||
testTwoOperandOp(t, tests, opSAR, "sar")
|
testTwoOperandOp(t, tests, opSAR, "sar")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAddMod(t *testing.T) {
|
||||||
|
var (
|
||||||
|
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
|
||||||
|
stack = newstack()
|
||||||
|
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
|
||||||
|
pc = uint64(0)
|
||||||
|
)
|
||||||
|
tests := []struct {
|
||||||
|
x string
|
||||||
|
y string
|
||||||
|
z string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// x + y = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd
|
||||||
|
// in 256 bit repr, fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
x := new(uint256.Int).SetBytes(common.Hex2Bytes(test.x))
|
||||||
|
y := new(uint256.Int).SetBytes(common.Hex2Bytes(test.y))
|
||||||
|
z := new(uint256.Int).SetBytes(common.Hex2Bytes(test.z))
|
||||||
|
expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.expected))
|
||||||
|
stack.push(z)
|
||||||
|
stack.push(y)
|
||||||
|
stack.push(x)
|
||||||
|
opAddmod(&pc, evmInterpreter, &callCtx{nil, stack, nil, nil})
|
||||||
|
actual := stack.pop()
|
||||||
|
if actual.Cmp(expected) != 0 {
|
||||||
|
t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// getResult is a convenience function to generate the expected values
|
// getResult is a convenience function to generate the expected values
|
||||||
func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase {
|
func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase {
|
||||||
var (
|
var (
|
||||||
@ -217,11 +236,10 @@ func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcas
|
|||||||
pc = uint64(0)
|
pc = uint64(0)
|
||||||
interpreter = env.interpreter.(*EVMInterpreter)
|
interpreter = env.interpreter.(*EVMInterpreter)
|
||||||
)
|
)
|
||||||
interpreter.intPool = poolOfIntPools.get()
|
|
||||||
result := make([]TwoOperandTestcase, len(args))
|
result := make([]TwoOperandTestcase, len(args))
|
||||||
for i, param := range args {
|
for i, param := range args {
|
||||||
x := new(big.Int).SetBytes(common.Hex2Bytes(param.x))
|
x := new(uint256.Int).SetBytes(common.Hex2Bytes(param.x))
|
||||||
y := new(big.Int).SetBytes(common.Hex2Bytes(param.y))
|
y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y))
|
||||||
stack.push(x)
|
stack.push(x)
|
||||||
stack.push(y)
|
stack.push(y)
|
||||||
opFn(&pc, interpreter, &callCtx{nil, stack, rstack, nil})
|
opFn(&pc, interpreter, &callCtx{nil, stack, rstack, nil})
|
||||||
@ -269,7 +287,6 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
env.interpreter = evmInterpreter
|
env.interpreter = evmInterpreter
|
||||||
evmInterpreter.intPool = poolOfIntPools.get()
|
|
||||||
// convert args
|
// convert args
|
||||||
byteArgs := make([][]byte, len(args))
|
byteArgs := make([][]byte, len(args))
|
||||||
for i, arg := range args {
|
for i, arg := range args {
|
||||||
@ -279,13 +296,13 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
|
|||||||
bench.ResetTimer()
|
bench.ResetTimer()
|
||||||
for i := 0; i < bench.N; i++ {
|
for i := 0; i < bench.N; i++ {
|
||||||
for _, arg := range byteArgs {
|
for _, arg := range byteArgs {
|
||||||
a := new(big.Int).SetBytes(arg)
|
a := new(uint256.Int)
|
||||||
|
a.SetBytes(arg)
|
||||||
stack.push(a)
|
stack.push(a)
|
||||||
}
|
}
|
||||||
op(&pc, evmInterpreter, &callCtx{nil, stack, rstack, nil})
|
op(&pc, evmInterpreter, &callCtx{nil, stack, rstack, nil})
|
||||||
stack.pop()
|
stack.pop()
|
||||||
}
|
}
|
||||||
poolOfIntPools.put(evmInterpreter.intPool)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkOpAdd64(b *testing.B) {
|
func BenchmarkOpAdd64(b *testing.B) {
|
||||||
@ -505,21 +522,19 @@ func TestOpMstore(t *testing.T) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
env.interpreter = evmInterpreter
|
env.interpreter = evmInterpreter
|
||||||
evmInterpreter.intPool = poolOfIntPools.get()
|
|
||||||
mem.Resize(64)
|
mem.Resize(64)
|
||||||
pc := uint64(0)
|
pc := uint64(0)
|
||||||
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
|
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
|
||||||
stack.pushN(new(big.Int).SetBytes(common.Hex2Bytes(v)), big.NewInt(0))
|
stack.pushN(*new(uint256.Int).SetBytes(common.Hex2Bytes(v)), *new(uint256.Int))
|
||||||
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil})
|
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil})
|
||||||
if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v {
|
if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v {
|
||||||
t.Fatalf("Mstore fail, got %v, expected %v", got, v)
|
t.Fatalf("Mstore fail, got %v, expected %v", got, v)
|
||||||
}
|
}
|
||||||
stack.pushN(big.NewInt(0x1), big.NewInt(0))
|
stack.pushN(*new(uint256.Int).SetUint64(0x1), *new(uint256.Int))
|
||||||
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil})
|
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil})
|
||||||
if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
|
if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
|
||||||
t.Fatalf("Mstore failed to overwrite previous value")
|
t.Fatalf("Mstore failed to overwrite previous value")
|
||||||
}
|
}
|
||||||
poolOfIntPools.put(evmInterpreter.intPool)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkOpMstore(bench *testing.B) {
|
func BenchmarkOpMstore(bench *testing.B) {
|
||||||
@ -531,18 +546,16 @@ func BenchmarkOpMstore(bench *testing.B) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
env.interpreter = evmInterpreter
|
env.interpreter = evmInterpreter
|
||||||
evmInterpreter.intPool = poolOfIntPools.get()
|
|
||||||
mem.Resize(64)
|
mem.Resize(64)
|
||||||
pc := uint64(0)
|
pc := uint64(0)
|
||||||
memStart := big.NewInt(0)
|
memStart := new(uint256.Int)
|
||||||
value := big.NewInt(0x1337)
|
value := new(uint256.Int).SetUint64(0x1337)
|
||||||
|
|
||||||
bench.ResetTimer()
|
bench.ResetTimer()
|
||||||
for i := 0; i < bench.N; i++ {
|
for i := 0; i < bench.N; i++ {
|
||||||
stack.pushN(value, memStart)
|
stack.pushN(*value, *memStart)
|
||||||
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil})
|
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil})
|
||||||
}
|
}
|
||||||
poolOfIntPools.put(evmInterpreter.intPool)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkOpSHA3(bench *testing.B) {
|
func BenchmarkOpSHA3(bench *testing.B) {
|
||||||
@ -553,17 +566,15 @@ func BenchmarkOpSHA3(bench *testing.B) {
|
|||||||
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
|
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
|
||||||
)
|
)
|
||||||
env.interpreter = evmInterpreter
|
env.interpreter = evmInterpreter
|
||||||
evmInterpreter.intPool = poolOfIntPools.get()
|
|
||||||
mem.Resize(32)
|
mem.Resize(32)
|
||||||
pc := uint64(0)
|
pc := uint64(0)
|
||||||
start := big.NewInt(0)
|
start := uint256.NewInt()
|
||||||
|
|
||||||
bench.ResetTimer()
|
bench.ResetTimer()
|
||||||
for i := 0; i < bench.N; i++ {
|
for i := 0; i < bench.N; i++ {
|
||||||
stack.pushN(big.NewInt(32), start)
|
stack.pushN(*uint256.NewInt().SetUint64(32), *start)
|
||||||
opSha3(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil})
|
opSha3(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil})
|
||||||
}
|
}
|
||||||
poolOfIntPools.put(evmInterpreter.intPool)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreate2Addreses(t *testing.T) {
|
func TestCreate2Addreses(t *testing.T) {
|
||||||
@ -637,6 +648,5 @@ func TestCreate2Addreses(t *testing.T) {
|
|||||||
if !bytes.Equal(expected.Bytes(), address.Bytes()) {
|
if !bytes.Equal(expected.Bytes(), address.Bytes()) {
|
||||||
t.Errorf("test %d: expected %s, got %s", i, expected.String(), address.String())
|
t.Errorf("test %d: expected %s, got %s", i, expected.String(), address.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
// Copyright 2017 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
// +build VERIFY_EVM_INTEGER_POOL
|
|
||||||
|
|
||||||
package vm
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
const verifyPool = true
|
|
||||||
|
|
||||||
func verifyIntegerPool(ip *intPool) {
|
|
||||||
for i, item := range ip.pool.data {
|
|
||||||
if item.Cmp(checkVal) != 0 {
|
|
||||||
panic(fmt.Sprintf("%d'th item failed aggressive pool check. Value was modified", i))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
// Copyright 2017 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
// +build !VERIFY_EVM_INTEGER_POOL
|
|
||||||
|
|
||||||
package vm
|
|
||||||
|
|
||||||
const verifyPool = false
|
|
||||||
|
|
||||||
func verifyIntegerPool(ip *intPool) {}
|
|
@ -84,8 +84,6 @@ type EVMInterpreter struct {
|
|||||||
evm *EVM
|
evm *EVM
|
||||||
cfg Config
|
cfg Config
|
||||||
|
|
||||||
intPool *intPool
|
|
||||||
|
|
||||||
hasher keccakState // Keccak256 hasher instance shared across opcodes
|
hasher keccakState // Keccak256 hasher instance shared across opcodes
|
||||||
hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes
|
hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes
|
||||||
|
|
||||||
@ -141,13 +139,6 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
|
|||||||
// considered a revert-and-consume-all-gas operation except for
|
// considered a revert-and-consume-all-gas operation except for
|
||||||
// ErrExecutionReverted which means revert-and-keep-gas-left.
|
// ErrExecutionReverted which means revert-and-keep-gas-left.
|
||||||
func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) {
|
func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) {
|
||||||
if in.intPool == nil {
|
|
||||||
in.intPool = poolOfIntPools.get()
|
|
||||||
defer func() {
|
|
||||||
poolOfIntPools.put(in.intPool)
|
|
||||||
in.intPool = nil
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increment the call depth which is restricted to 1024
|
// Increment the call depth which is restricted to 1024
|
||||||
in.evm.depth++
|
in.evm.depth++
|
||||||
@ -193,9 +184,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||||||
)
|
)
|
||||||
contract.Input = input
|
contract.Input = input
|
||||||
|
|
||||||
// Reclaim the stack as an int pool when the execution stops
|
|
||||||
defer func() { in.intPool.put(stack.data...) }()
|
|
||||||
|
|
||||||
if in.cfg.Debug {
|
if in.cfg.Debug {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -290,11 +278,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||||||
|
|
||||||
// execute the operation
|
// execute the operation
|
||||||
res, err = operation.execute(&pc, in, callContext)
|
res, err = operation.execute(&pc, in, callContext)
|
||||||
// verifyPool is a build flag. Pool verification makes sure the integrity
|
|
||||||
// of the integer pool by comparing values to a default value.
|
|
||||||
if verifyPool {
|
|
||||||
verifyIntegerPool(in.intPool)
|
|
||||||
}
|
|
||||||
// if the operation clears the return data (e.g. it has returning data)
|
// if the operation clears the return data (e.g. it has returning data)
|
||||||
// set the last return to the result of the operation.
|
// set the last return to the result of the operation.
|
||||||
if operation.returns {
|
if operation.returns {
|
||||||
|
@ -1,117 +0,0 @@
|
|||||||
// Copyright 2017 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package vm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/big"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
var checkVal = big.NewInt(-42)
|
|
||||||
|
|
||||||
const poolLimit = 256
|
|
||||||
|
|
||||||
// intPool is a pool of big integers that
|
|
||||||
// can be reused for all big.Int operations.
|
|
||||||
type intPool struct {
|
|
||||||
pool *Stack
|
|
||||||
}
|
|
||||||
|
|
||||||
func newIntPool() *intPool {
|
|
||||||
return &intPool{pool: newstack()}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get retrieves a big int from the pool, allocating one if the pool is empty.
|
|
||||||
// Note, the returned int's value is arbitrary and will not be zeroed!
|
|
||||||
func (p *intPool) get() *big.Int {
|
|
||||||
if p.pool.len() > 0 {
|
|
||||||
return p.pool.pop()
|
|
||||||
}
|
|
||||||
return new(big.Int)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getZero retrieves a big int from the pool, setting it to zero or allocating
|
|
||||||
// a new one if the pool is empty.
|
|
||||||
func (p *intPool) getZero() *big.Int {
|
|
||||||
if p.pool.len() > 0 {
|
|
||||||
return p.pool.pop().SetUint64(0)
|
|
||||||
}
|
|
||||||
return new(big.Int)
|
|
||||||
}
|
|
||||||
|
|
||||||
// putOne returns an allocated big int to the pool to be later reused by get calls.
|
|
||||||
// Note, the values as saved as is; neither put nor get zeroes the ints out!
|
|
||||||
// As opposed to 'put' with variadic args, this method becomes inlined by the
|
|
||||||
// go compiler
|
|
||||||
func (p *intPool) putOne(i *big.Int) {
|
|
||||||
if len(p.pool.data) > poolLimit {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.pool.push(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
// put returns an allocated big int to the pool to be later reused by get calls.
|
|
||||||
// Note, the values as saved as is; neither put nor get zeroes the ints out!
|
|
||||||
func (p *intPool) put(is ...*big.Int) {
|
|
||||||
if len(p.pool.data) > poolLimit {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, i := range is {
|
|
||||||
// verifyPool is a build flag. Pool verification makes sure the integrity
|
|
||||||
// of the integer pool by comparing values to a default value.
|
|
||||||
if verifyPool {
|
|
||||||
i.Set(checkVal)
|
|
||||||
}
|
|
||||||
p.pool.push(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The intPool pool's default capacity
|
|
||||||
const poolDefaultCap = 25
|
|
||||||
|
|
||||||
// intPoolPool manages a pool of intPools.
|
|
||||||
type intPoolPool struct {
|
|
||||||
pools []*intPool
|
|
||||||
lock sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
var poolOfIntPools = &intPoolPool{
|
|
||||||
pools: make([]*intPool, 0, poolDefaultCap),
|
|
||||||
}
|
|
||||||
|
|
||||||
// get is looking for an available pool to return.
|
|
||||||
func (ipp *intPoolPool) get() *intPool {
|
|
||||||
ipp.lock.Lock()
|
|
||||||
defer ipp.lock.Unlock()
|
|
||||||
|
|
||||||
if len(poolOfIntPools.pools) > 0 {
|
|
||||||
ip := ipp.pools[len(ipp.pools)-1]
|
|
||||||
ipp.pools = ipp.pools[:len(ipp.pools)-1]
|
|
||||||
return ip
|
|
||||||
}
|
|
||||||
return newIntPool()
|
|
||||||
}
|
|
||||||
|
|
||||||
// put a pool that has been allocated with get.
|
|
||||||
func (ipp *intPoolPool) put(ip *intPool) {
|
|
||||||
ipp.lock.Lock()
|
|
||||||
defer ipp.lock.Unlock()
|
|
||||||
|
|
||||||
if len(ipp.pools) < cap(ipp.pools) {
|
|
||||||
ipp.pools = append(ipp.pools, ip)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
// Copyright 2018 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package vm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestIntPoolPoolGet(t *testing.T) {
|
|
||||||
poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap)
|
|
||||||
|
|
||||||
nip := poolOfIntPools.get()
|
|
||||||
if nip == nil {
|
|
||||||
t.Fatalf("Invalid pool allocation")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIntPoolPoolPut(t *testing.T) {
|
|
||||||
poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap)
|
|
||||||
|
|
||||||
nip := poolOfIntPools.get()
|
|
||||||
if len(poolOfIntPools.pools) != 0 {
|
|
||||||
t.Fatalf("Pool got added to list when none should have been")
|
|
||||||
}
|
|
||||||
|
|
||||||
poolOfIntPools.put(nip)
|
|
||||||
if len(poolOfIntPools.pools) == 0 {
|
|
||||||
t.Fatalf("Pool did not get added to list when one should have been")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIntPoolPoolReUse(t *testing.T) {
|
|
||||||
poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap)
|
|
||||||
nip := poolOfIntPools.get()
|
|
||||||
poolOfIntPools.put(nip)
|
|
||||||
poolOfIntPools.get()
|
|
||||||
|
|
||||||
if len(poolOfIntPools.pools) != 0 {
|
|
||||||
t.Fatalf("Invalid number of pools. Got %d, expected %d", len(poolOfIntPools.pools), 0)
|
|
||||||
}
|
|
||||||
}
|
|
@ -159,8 +159,8 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
|
|||||||
// it in the local storage container.
|
// it in the local storage container.
|
||||||
if op == SSTORE && stack.len() >= 2 {
|
if op == SSTORE && stack.len() >= 2 {
|
||||||
var (
|
var (
|
||||||
value = common.BigToHash(stack.data[stack.len()-2])
|
value = common.Hash(stack.data[stack.len()-2].Bytes32())
|
||||||
address = common.BigToHash(stack.data[stack.len()-1])
|
address = common.Hash(stack.data[stack.len()-1].Bytes32())
|
||||||
)
|
)
|
||||||
l.changedValues[contract.Address()][address] = value
|
l.changedValues[contract.Address()][address] = value
|
||||||
}
|
}
|
||||||
@ -175,7 +175,7 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
|
|||||||
if !l.cfg.DisableStack {
|
if !l.cfg.DisableStack {
|
||||||
stck = make([]*big.Int, len(stack.Data()))
|
stck = make([]*big.Int, len(stack.Data()))
|
||||||
for i, item := range stack.Data() {
|
for i, item := range stack.Data() {
|
||||||
stck[i] = new(big.Int).Set(item)
|
stck[i] = new(big.Int).Set(item.ToBig())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Copy a snapshot of the current storage to a new container
|
// Copy a snapshot of the current storage to a new container
|
||||||
|
@ -62,7 +62,12 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
|
|||||||
log.Memory = memory.Data()
|
log.Memory = memory.Data()
|
||||||
}
|
}
|
||||||
if !l.cfg.DisableStack {
|
if !l.cfg.DisableStack {
|
||||||
log.Stack = stack.Data()
|
//TODO(@holiman) improve this
|
||||||
|
logstack := make([]*big.Int, len(stack.Data()))
|
||||||
|
for i, item := range stack.Data() {
|
||||||
|
logstack[i] = item.ToBig()
|
||||||
|
}
|
||||||
|
log.Stack = logstack
|
||||||
log.ReturnStack = rStack.data
|
log.ReturnStack = rStack.data
|
||||||
}
|
}
|
||||||
return l.encoder.Encode(log)
|
return l.encoder.Encode(log)
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
type dummyContractRef struct {
|
type dummyContractRef struct {
|
||||||
@ -57,8 +58,8 @@ func TestStoreCapture(t *testing.T) {
|
|||||||
rstack = newReturnStack()
|
rstack = newReturnStack()
|
||||||
contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0)
|
contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0)
|
||||||
)
|
)
|
||||||
stack.push(big.NewInt(1))
|
stack.push(uint256.NewInt().SetUint64(1))
|
||||||
stack.push(big.NewInt(0))
|
stack.push(uint256.NewInt())
|
||||||
var index common.Hash
|
var index common.Hash
|
||||||
logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, rstack, contract, 0, nil)
|
logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, rstack, contract, 0, nil)
|
||||||
if len(logger.changedValues[contract.Address()]) == 0 {
|
if len(logger.changedValues[contract.Address()]) == 0 {
|
||||||
|
@ -18,9 +18,8 @@ package vm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Memory implements a simple memory model for the ethereum virtual machine.
|
// Memory implements a simple memory model for the ethereum virtual machine.
|
||||||
@ -50,7 +49,7 @@ func (m *Memory) Set(offset, size uint64, value []byte) {
|
|||||||
|
|
||||||
// Set32 sets the 32 bytes starting at offset to the value of val, left-padded with zeroes to
|
// Set32 sets the 32 bytes starting at offset to the value of val, left-padded with zeroes to
|
||||||
// 32 bytes.
|
// 32 bytes.
|
||||||
func (m *Memory) Set32(offset uint64, val *big.Int) {
|
func (m *Memory) Set32(offset uint64, val *uint256.Int) {
|
||||||
// length of store may never be less than offset + size.
|
// length of store may never be less than offset + size.
|
||||||
// The store should be resized PRIOR to setting the memory
|
// The store should be resized PRIOR to setting the memory
|
||||||
if offset+32 > uint64(len(m.store)) {
|
if offset+32 > uint64(len(m.store)) {
|
||||||
@ -59,7 +58,7 @@ func (m *Memory) Set32(offset uint64, val *big.Int) {
|
|||||||
// Zero the memory area
|
// Zero the memory area
|
||||||
copy(m.store[offset:offset+32], []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
|
copy(m.store[offset:offset+32], []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
|
||||||
// Fill in relevant bits
|
// Fill in relevant bits
|
||||||
math.ReadBits(val, m.store[offset:offset+32])
|
val.WriteToSlice(m.store[offset:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resize resizes the memory to size
|
// Resize resizes the memory to size
|
||||||
|
@ -18,36 +18,36 @@ package vm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Stack is an object for basic stack operations. Items popped to the stack are
|
// Stack is an object for basic stack operations. Items popped to the stack are
|
||||||
// expected to be changed and modified. stack does not take care of adding newly
|
// expected to be changed and modified. stack does not take care of adding newly
|
||||||
// initialised objects.
|
// initialised objects.
|
||||||
type Stack struct {
|
type Stack struct {
|
||||||
data []*big.Int
|
data []uint256.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
func newstack() *Stack {
|
func newstack() *Stack {
|
||||||
return &Stack{data: make([]*big.Int, 0, 1024)}
|
return &Stack{data: make([]uint256.Int, 0, 16)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data returns the underlying big.Int array.
|
// Data returns the underlying uint256.Int array.
|
||||||
func (st *Stack) Data() []*big.Int {
|
func (st *Stack) Data() []uint256.Int {
|
||||||
return st.data
|
return st.data
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Stack) push(d *big.Int) {
|
func (st *Stack) push(d *uint256.Int) {
|
||||||
// NOTE push limit (1024) is checked in baseCheck
|
// NOTE push limit (1024) is checked in baseCheck
|
||||||
//stackItem := new(big.Int).Set(d)
|
st.data = append(st.data, *d)
|
||||||
//st.data = append(st.data, stackItem)
|
|
||||||
st.data = append(st.data, d)
|
|
||||||
}
|
}
|
||||||
func (st *Stack) pushN(ds ...*big.Int) {
|
func (st *Stack) pushN(ds ...uint256.Int) {
|
||||||
|
// FIXME: Is there a way to pass args by pointers.
|
||||||
st.data = append(st.data, ds...)
|
st.data = append(st.data, ds...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Stack) pop() (ret *big.Int) {
|
func (st *Stack) pop() (ret uint256.Int) {
|
||||||
ret = st.data[len(st.data)-1]
|
ret = st.data[len(st.data)-1]
|
||||||
st.data = st.data[:len(st.data)-1]
|
st.data = st.data[:len(st.data)-1]
|
||||||
return
|
return
|
||||||
@ -61,17 +61,17 @@ func (st *Stack) swap(n int) {
|
|||||||
st.data[st.len()-n], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-n]
|
st.data[st.len()-n], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-n]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Stack) dup(pool *intPool, n int) {
|
func (st *Stack) dup(n int) {
|
||||||
st.push(pool.get().Set(st.data[st.len()-n]))
|
st.push(&st.data[st.len()-n])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Stack) peek() *big.Int {
|
func (st *Stack) peek() *uint256.Int {
|
||||||
return st.data[st.len()-1]
|
return &st.data[st.len()-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Back returns the n'th item in stack
|
// Back returns the n'th item in stack
|
||||||
func (st *Stack) Back(n int) *big.Int {
|
func (st *Stack) Back(n int) *uint256.Int {
|
||||||
return st.data[st.len()-n-1]
|
return &st.data[st.len()-n-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print dumps the content of the stack
|
// Print dumps the content of the stack
|
||||||
|
@ -162,7 +162,7 @@ func (sw *stackWrapper) peek(idx int) *big.Int {
|
|||||||
log.Warn("Tracer accessed out of bound stack", "size", len(sw.stack.Data()), "index", idx)
|
log.Warn("Tracer accessed out of bound stack", "size", len(sw.stack.Data()), "index", idx)
|
||||||
return new(big.Int)
|
return new(big.Int)
|
||||||
}
|
}
|
||||||
return sw.stack.Data()[len(sw.stack.Data())-idx-1]
|
return sw.stack.Back(idx).ToBig()
|
||||||
}
|
}
|
||||||
|
|
||||||
// pushObject assembles a JSVM object wrapping a swappable stack and pushes it
|
// pushObject assembles a JSVM object wrapping a swappable stack and pushes it
|
||||||
|
1
go.mod
1
go.mod
@ -31,6 +31,7 @@ require (
|
|||||||
github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989
|
github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989
|
||||||
github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277
|
github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277
|
||||||
github.com/hashicorp/golang-lru v0.5.4
|
github.com/hashicorp/golang-lru v0.5.4
|
||||||
|
github.com/holiman/uint256 v1.0.0
|
||||||
github.com/huin/goupnp v1.0.0
|
github.com/huin/goupnp v1.0.0
|
||||||
github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883
|
github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883
|
||||||
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458
|
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458
|
||||||
|
4
go.sum
4
go.sum
@ -93,6 +93,10 @@ github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277 h1:E0whKx
|
|||||||
github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
|
github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
|
||||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||||
|
github.com/holiman/uint256 v0.0.0-20200319132535-a4d16d7dba8d h1:GHyibjawOjlV6CVRfc7nAD2fFiwadHU2p87viNQtG/c=
|
||||||
|
github.com/holiman/uint256 v0.0.0-20200319132535-a4d16d7dba8d/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
|
||||||
|
github.com/holiman/uint256 v1.0.0 h1:GV654hUWgO9gVQwgjMyup5bkUCWI9t87+LSjB9fAo5c=
|
||||||
|
github.com/holiman/uint256 v1.0.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
|
||||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo=
|
github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo=
|
||||||
|
Loading…
Reference in New Issue
Block a user