Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,16 @@ func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) commo
return common.Hash{}
}

// GetStateAndCommittedState retrieves the current and committed values from the
// given account's storage trie.
func (s *StateDB) GetStateAndCommittedState(addr common.Address, hash common.Hash) (common.Hash, common.Hash) {
stateObject := s.getStateObject(addr)
if stateObject != nil {
return stateObject.getState(hash)
}
return common.Hash{}, common.Hash{}
}

// Database retrieves the low level database supporting the lower level trie ops.
func (s *StateDB) Database() Database {
return s.db
Expand Down
4 changes: 2 additions & 2 deletions core/state/statedb_hooked.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ func (s *hookedStateDB) GetRefund() uint64 {
return s.inner.GetRefund()
}

func (s *hookedStateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
return s.inner.GetCommittedState(addr, hash)
func (s *hookedStateDB) GetStateAndCommittedState(addr common.Address, hash common.Hash) (common.Hash, common.Hash) {
return s.inner.GetStateAndCommittedState(addr, hash)
}

func (s *hookedStateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
Expand Down
35 changes: 35 additions & 0 deletions core/state/statedb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1056,6 +1056,41 @@ func TestCommitCopy(t *testing.T) {
}
}

func TestGetStateAndCommittedState(t *testing.T) {
db := NewDatabase(rawdb.NewMemoryDatabase())
state, _ := New(types.EmptyRootHash, db)

addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe")
key := common.HexToHash("a1")
committed := common.HexToHash("b1")
dirty := common.HexToHash("b2")

current, original := state.GetStateAndCommittedState(addr, key)
if current != (common.Hash{}) || original != (common.Hash{}) {
t.Fatalf("empty slot mismatch: have current=%x original=%x", current, original)
}

state.SetState(addr, key, committed)
current, original = state.GetStateAndCommittedState(addr, key)
if current != committed || original != (common.Hash{}) {
t.Fatalf("dirty slot mismatch: have current=%x original=%x want current=%x original=%x", current, original, committed, common.Hash{})
}

root, _ := state.Commit(0, false)
state, _ = New(root, db)

current, original = state.GetStateAndCommittedState(addr, key)
if current != committed || original != committed {
t.Fatalf("committed slot mismatch: have current=%x original=%x want current=%x original=%x", current, original, committed, committed)
}

state.SetState(addr, key, dirty)
current, original = state.GetStateAndCommittedState(addr, key)
if current != dirty || original != committed {
t.Fatalf("updated dirty slot mismatch: have current=%x original=%x want current=%x original=%x", current, original, dirty, committed)
}
}

// Tests that account and storage tries are flushed in the correct order and that
// no data loss occurs.
func TestFlushOrderDataLoss(t *testing.T) {
Expand Down
10 changes: 4 additions & 6 deletions core/vm/gas_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
return 0, ErrWriteProtection
}
var (
y, x = stack.Back(1), stack.Back(0)
current = evm.StateDB.GetState(contract.Address(), x.Bytes32())
y, x = stack.Back(1), stack.Back(0)
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), x.Bytes32())
)
// 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)
Expand Down Expand Up @@ -140,7 +140,6 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
if current == value { // noop (1)
return params.NetSstoreNoopGas, nil
}
original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return params.NetSstoreInitGas, nil
Expand Down Expand Up @@ -190,15 +189,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
var (
y, x = stack.Back(1), stack.Back(0)
current = evm.StateDB.GetState(contract.Address(), x.Bytes32())
y, x = stack.Back(1), stack.Back(0)
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), x.Bytes32())
)
value := common.Hash(y.Bytes32())

if current == value { // noop (1)
return params.SloadGasEIP2200, nil
}
original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return params.SstoreSetGasEIP2200, nil
Expand Down
52 changes: 52 additions & 0 deletions core/vm/gas_table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,58 @@ func TestEIP2200(t *testing.T) {
}
}

type countingStateDB struct {
*state.StateDB
getStateCalls int
getCommittedStateCalls int
getCombinedStateCalls int
}

func (s *countingStateDB) GetState(addr common.Address, key common.Hash) common.Hash {
s.getStateCalls++
return s.StateDB.GetState(addr, key)
}

func (s *countingStateDB) GetCommittedState(addr common.Address, key common.Hash) common.Hash {
s.getCommittedStateCalls++
return s.StateDB.GetCommittedState(addr, key)
}

func (s *countingStateDB) GetStateAndCommittedState(addr common.Address, key common.Hash) (common.Hash, common.Hash) {
s.getCombinedStateCalls++
return s.StateDB.GetStateAndCommittedState(addr, key)
}

func TestEIP2200UsesCombinedStateGetter(t *testing.T) {
address := common.BytesToAddress([]byte("contract"))
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()))
statedb.CreateAccount(address)
statedb.SetCode(address, hexutil.MustDecode("0x6002600055"))
statedb.SetState(address, common.Hash{}, common.BytesToHash([]byte{1}))
statedb.Finalise(true)

countingDB := &countingStateDB{StateDB: statedb}
vmctx := BlockContext{
CanTransfer: func(StateDB, common.Address, *uint256.Int) bool { return true },
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {},
}
evm := NewEVM(vmctx, countingDB, nil, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}})

_, _, err := evm.Call(common.Address{}, address, nil, math.MaxUint64, new(uint256.Int))
if err != nil {
t.Fatalf("call failed: %v", err)
}
if countingDB.getCombinedStateCalls == 0 {
t.Fatalf("expected GetStateAndCommittedState to be used")
}
if countingDB.getStateCalls != 0 {
t.Fatalf("expected GetState to be bypassed, got %d calls", countingDB.getStateCalls)
}
if countingDB.getCommittedStateCalls != 0 {
t.Fatalf("expected GetCommittedState to be bypassed, got %d calls", countingDB.getCommittedStateCalls)
}
}

var createGasTests = []struct {
code string
eip3860 bool
Expand Down
2 changes: 1 addition & 1 deletion core/vm/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ type StateDB interface {
SubRefund(uint64)
GetRefund() uint64

GetCommittedState(common.Address, common.Hash) common.Hash
GetStateAndCommittedState(common.Address, common.Hash) (common.Hash, common.Hash)
GetState(common.Address, common.Hash) common.Hash
SetState(common.Address, common.Hash, common.Hash) common.Hash
GetStorageRoot(addr common.Address) common.Hash
Expand Down
95 changes: 95 additions & 0 deletions core/vm/interpreter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright 2021 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"
"math/big"
"testing"
"time"

"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/holiman/uint256"
)

var loopInterruptTests = []string{
// infinite loop using JUMP: push(2) jumpdest dup1 jump
"60025b8056",
// infinite loop using JUMPI: push(1) push(4) jumpdest dup2 dup2 jumpi
"600160045b818157",
}

func TestLoopInterrupt(t *testing.T) {
address := common.BytesToAddress([]byte("contract"))
vmctx := BlockContext{
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {},
}

for i, tt := range loopInterruptTests {
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
statedb.CreateAccount(address)
statedb.SetCode(address, common.Hex2Bytes(tt))
statedb.Finalise(true)

evm := NewEVM(vmctx, statedb, nil, params.AllEthashProtocolChanges, Config{})

errChannel := make(chan error)
timeout := make(chan bool)

go func(evm *EVM) {
_, _, err := evm.Call(common.Address{}, address, nil, math.MaxUint64, new(uint256.Int))
errChannel <- err
}(evm)

go func() {
<-time.After(time.Second)
timeout <- true
}()

evm.Cancel()

select {
case <-timeout:
Comment thread
gzliudan marked this conversation as resolved.
t.Errorf("test %d timed out", i)
case err := <-errChannel:
if err != nil {
t.Errorf("test %d failure: %v", i, err)
}
}
}
}

func BenchmarkInterpreter(b *testing.B) {
var (
statedb, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
evm = NewEVM(BlockContext{BlockNumber: big.NewInt(1), Time: 1, Random: &common.Hash{}}, statedb, nil, params.MergedTestChainConfig, Config{})
startGas uint64 = 100_000_000
value = uint256.NewInt(0)
stack = newstack()
mem = NewMemory()
contract = NewContract(common.Address{}, common.Address{}, value, startGas, nil)
)
stack.push(uint256.NewInt(123))
stack.push(uint256.NewInt(123))
gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529)
for b.Loop() {
gasSStoreEIP3529(evm, contract, stack, mem, 1234)
Comment thread
gzliudan marked this conversation as resolved.
}
}
9 changes: 4 additions & 5 deletions core/vm/operations_acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
}
// Gas sentry honoured, do the actual gas calculation based on the stored value
var (
y, x = stack.Back(1), stack.peek()
slot = common.Hash(x.Bytes32())
current = evm.StateDB.GetState(contract.Address(), slot)
cost = uint64(0)
y, x = stack.Back(1), stack.peek()
slot = common.Hash(x.Bytes32())
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), slot)
cost = uint64(0)
)
// Check slot presence in the access list
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
Expand All @@ -55,7 +55,6 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
// return params.SloadGasEIP2200, nil
return cost + params.WarmStorageReadCostEIP2929, nil // SLOAD_GAS
}
original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return cost + params.SstoreSetGasEIP2200, nil
Expand Down
4 changes: 4 additions & 0 deletions eth/tracers/logger/logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ func (*dummyStatedb) SetState(_ common.Address, _ common.Hash, _ common.Hash) co
return common.Hash{}
}

func (*dummyStatedb) GetStateAndCommittedState(common.Address, common.Hash) (common.Hash, common.Hash) {
return common.Hash{}, common.Hash{}
}

type dummyOpContext struct{}

func (dummyOpContext) MemoryData() []byte { return nil }
Expand Down
Loading