From b16e56052933439c89e61e312fece1c55cdeb7c2 Mon Sep 17 00:00:00 2001 From: Andrew Ashikhmin <34320705+yperbasis@users.noreply.github.com> Date: Mon, 25 May 2020 13:12:25 +0200 Subject: [PATCH] Use uint256.Int rather than common.Hash for storage values to reduce memory allocation in opSload & opSstore (#575) * Produce less garbage in GetState * Still playing with mem allocation in GetCommittedState * Pass key by pointer in GetState as well * linter * Avoid a memory allocation in opSload * Use uint256.Int rather than common.Hash for storage values to reduce memory allocation in opSload & opSstore * linter * linters * small clean up --- accounts/abi/bind/backends/simulated.go | 6 ++- core/blockchain_test.go | 45 +++++++++--------- core/genesis.go | 9 ++-- core/state/change_set_writer.go | 10 ++-- core/state/database.go | 14 +++--- core/state/database_test.go | 40 +++++++++------- core/state/db_state_writer.go | 7 +-- core/state/history_test.go | 62 +++++++++++++------------ core/state/intra_block_state.go | 12 +++-- core/state/intra_block_state_test.go | 30 ++++++++---- core/state/journal.go | 9 ++-- core/state/plain_state_writer.go | 7 +-- core/state/readonly.go | 15 +++--- core/state/state_object.go | 22 +++++---- core/state/state_test.go | 27 +++++------ core/state/stateless.go | 7 +-- core/state/util.go | 7 --- core/vm/evmc.go | 35 +++++++------- core/vm/gas_table.go | 52 ++++++++++----------- core/vm/gas_table_test.go | 4 +- core/vm/instructions.go | 7 ++- core/vm/interface.go | 8 ++-- eth/api.go | 6 ++- eth/api_test.go | 8 ++-- eth/tracers/tracer.go | 10 ++-- graphql/graphql.go | 6 ++- internal/ethapi/api.go | 12 +++-- tests/state_test_util.go | 8 +++- tests/vm_test_util.go | 6 ++- 29 files changed, 269 insertions(+), 222 deletions(-) delete mode 100644 core/state/util.go diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index f16bc774cb..83b65ac67e 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -24,6 +24,8 @@ import ( "sync" "time" + "github.com/holiman/uint256" + ethereum "github.com/ledgerwatch/turbo-geth" "github.com/ledgerwatch/turbo-geth/accounts/abi" "github.com/ledgerwatch/turbo-geth/accounts/abi/bind" @@ -235,9 +237,9 @@ func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Addres if err != nil { return nil, err } - var val common.Hash + var val uint256.Int statedb.GetState(contract, &key, &val) - return val[:], nil + return val.Bytes(), nil } // TransactionReceipt returns the receipt of a transaction. diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 2b6599f317..c6ca4af874 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -27,6 +27,7 @@ import ( "testing" "time" + "github.com/holiman/uint256" "github.com/stretchr/testify/assert" "github.com/ledgerwatch/turbo-geth/common" @@ -2763,26 +2764,26 @@ func TestDeleteRecreateSlots(t *testing.T) { // If all is correct, then slot 1 and 2 are zero key1 := common.HexToHash("01") - var got common.Hash + var got uint256.Int statedb.GetState(aa, &key1, &got) - if exp := (common.Hash{}); got != exp { - t.Errorf("got %x exp %x", got, exp) + if !got.IsZero() { + t.Errorf("got %x exp %x", got, 0) } key2 := common.HexToHash("02") statedb.GetState(aa, &key2, &got) - if exp := (common.Hash{}); got != exp { - t.Errorf("got %x exp %x", got, exp) + if !got.IsZero() { + t.Errorf("got %x exp %x", got, 0) } // Also, 3 and 4 should be set key3 := common.HexToHash("03") statedb.GetState(aa, &key3, &got) - if exp := common.HexToHash("03"); got != exp { - t.Fatalf("got %x exp %x", got, exp) + if got.Uint64() != 3 { + t.Fatalf("got %x exp %x", got, 3) } key4 := common.HexToHash("04") statedb.GetState(aa, &key4, &got) - if exp := common.HexToHash("04"); got != exp { - t.Fatalf("got %x exp %x", got, exp) + if got.Uint64() != 4 { + t.Fatalf("got %x exp %x", got, 4) } } @@ -2860,15 +2861,15 @@ func TestDeleteRecreateAccount(t *testing.T) { // If all is correct, then both slots are zero key1 := common.HexToHash("01") - var got common.Hash + var got uint256.Int statedb.GetState(aa, &key1, &got) - if exp := (common.Hash{}); got != exp { - t.Errorf("got %x exp %x", got, exp) + if !got.IsZero() { + t.Errorf("got %x exp %x", got, 0) } key2 := common.HexToHash("02") statedb.GetState(aa, &key2, &got) - if exp := (common.Hash{}); got != exp { - t.Errorf("got %x exp %x", got, exp) + if !got.IsZero() { + t.Errorf("got %x exp %x", got, 0) } } @@ -3053,15 +3054,15 @@ func TestDeleteRecreateSlotsAcrossManyBlocks(t *testing.T) { statedb, _, _ := chain.State() // If all is correct, then slot 1 and 2 are zero key1 := common.HexToHash("01") - var got common.Hash + var got uint256.Int statedb.GetState(aa, &key1, &got) - if exp := (common.Hash{}); got != exp { - t.Errorf("block %d, got %x exp %x", blockNum, got, exp) + if !got.IsZero() { + t.Errorf("block %d, got %x exp %x", blockNum, got, 0) } key2 := common.HexToHash("02") statedb.GetState(aa, &key2, &got) - if exp := (common.Hash{}); got != exp { - t.Errorf("block %d, got %x exp %x", blockNum, got, exp) + if !got.IsZero() { + t.Errorf("block %d, got %x exp %x", blockNum, got, 0) } exp := expectations[i] if exp.exist { @@ -3070,10 +3071,10 @@ func TestDeleteRecreateSlotsAcrossManyBlocks(t *testing.T) { } for slot, val := range exp.values { key := asHash(slot) - var gotValue common.Hash + var gotValue uint256.Int statedb.GetState(aa, &key, &gotValue) - if expValue := asHash(val); gotValue != expValue { - t.Fatalf("block %d, slot %d, got %x exp %x", blockNum, slot, gotValue, expValue) + if gotValue.Uint64() != uint64(val) { + t.Fatalf("block %d, slot %d, got %x exp %x", blockNum, slot, gotValue, val) } } } else { diff --git a/core/genesis.go b/core/genesis.go index 7d98bd2bf7..488ac78391 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -18,6 +18,7 @@ package core import ( "bytes" + "context" "encoding/hex" "encoding/json" "errors" @@ -25,7 +26,7 @@ import ( "math/big" "strings" - "context" + "github.com/holiman/uint256" "github.com/ledgerwatch/turbo-geth/common" "github.com/ledgerwatch/turbo-geth/common/hexutil" @@ -41,7 +42,7 @@ import ( ) var UsePlainStateExecution = false // FIXME: when we can move the hashed state forward. -// ^--- will be overriden e when parsing flags anyway +// ^--- will be overridden when parsing flags anyway //go:generate gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go //go:generate gencodec -type GenesisAccount -field-override genesisAccountMarshaling -out gen_genesis_account.go @@ -253,7 +254,9 @@ func (g *Genesis) ToBlock(db ethdb.Database, history bool) (*types.Block, *state statedb.SetCode(addr, account.Code) statedb.SetNonce(addr, account.Nonce) for key, value := range account.Storage { - statedb.SetState(addr, key, value) + key := key + val := uint256.NewInt().SetBytes(value.Bytes()) + statedb.SetState(addr, &key, *val) } if len(account.Code) > 0 || len(account.Storage) > 0 { diff --git a/core/state/change_set_writer.go b/core/state/change_set_writer.go index 7135bc2ce2..fc457ea1bc 100644 --- a/core/state/change_set_writer.go +++ b/core/state/change_set_writer.go @@ -4,6 +4,8 @@ import ( "context" "fmt" + "github.com/holiman/uint256" + "github.com/ledgerwatch/turbo-geth/common" "github.com/ledgerwatch/turbo-geth/common/changeset" "github.com/ledgerwatch/turbo-geth/common/dbutils" @@ -139,7 +141,7 @@ func (w *ChangeSetWriter) DeleteAccount(ctx context.Context, address common.Addr return nil } -func (w *ChangeSetWriter) WriteAccountStorage(ctx context.Context, address common.Address, incarnation uint64, key, original, value *common.Hash) error { +func (w *ChangeSetWriter) WriteAccountStorage(ctx context.Context, address common.Address, incarnation uint64, key *common.Hash, original, value *uint256.Int) error { if *original == *value { return nil } @@ -149,11 +151,7 @@ func (w *ChangeSetWriter) WriteAccountStorage(ctx context.Context, address commo return err } - o := cleanUpTrailingZeroes(original[:]) - originalValue := make([]byte, len(o)) - copy(originalValue, o) - - w.storageChanges[string(compositeKey)] = originalValue + w.storageChanges[string(compositeKey)] = original.Bytes() w.storageChanged[address] = true return nil diff --git a/core/state/database.go b/core/state/database.go index 65bb58a4be..4f64d8baa6 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -28,6 +28,8 @@ import ( "sync" "sync/atomic" + "github.com/holiman/uint256" + "github.com/ledgerwatch/turbo-geth/common" "github.com/ledgerwatch/turbo-geth/common/dbutils" "github.com/ledgerwatch/turbo-geth/core/rawdb" @@ -61,7 +63,7 @@ type StateWriter interface { UpdateAccountData(ctx context.Context, address common.Address, original, account *accounts.Account) error UpdateAccountCode(address common.Address, incarnation uint64, codeHash common.Hash, code []byte) error DeleteAccount(ctx context.Context, address common.Address, original *accounts.Account) error - WriteAccountStorage(ctx context.Context, address common.Address, incarnation uint64, key, original, value *common.Hash) error + WriteAccountStorage(ctx context.Context, address common.Address, incarnation uint64, key *common.Hash, original, value *uint256.Int) error CreateContract(address common.Address) error } @@ -89,7 +91,7 @@ func (nw *NoopWriter) UpdateAccountCode(address common.Address, incarnation uint return nil } -func (nw *NoopWriter) WriteAccountStorage(_ context.Context, address common.Address, incarnation uint64, key, original, value *common.Hash) error { +func (nw *NoopWriter) WriteAccountStorage(_ context.Context, address common.Address, incarnation uint64, key *common.Hash, original, value *uint256.Int) error { return nil } @@ -97,7 +99,7 @@ func (nw *NoopWriter) CreateContract(address common.Address) error { return nil } -// Structure holding updates, deletes, and reads registered within one change period +// Buffer is a structure holding updates, deletes, and reads registered within one change period // A change period can be transaction within a block, or a block within group of blocks type Buffer struct { codeReads map[common.Hash]common.Hash @@ -1350,13 +1352,13 @@ func (tsw *TrieStateWriter) UpdateAccountCode(address common.Address, incarnatio return nil } -func (tsw *TrieStateWriter) WriteAccountStorage(_ context.Context, address common.Address, incarnation uint64, key, original, value *common.Hash) error { +func (tsw *TrieStateWriter) WriteAccountStorage(_ context.Context, address common.Address, incarnation uint64, key *common.Hash, original, value *uint256.Int) error { addrHash, err := tsw.tds.pw.HashAddress(address, false /*save*/) if err != nil { return err } - v := bytes.TrimLeft(value[:], "\x00") + v := value.Bytes() m, ok := tsw.tds.currentBuffer.storageUpdates[addrHash] if !ok { m = make(map[common.Hash][]byte) @@ -1373,7 +1375,7 @@ func (tsw *TrieStateWriter) WriteAccountStorage(_ context.Context, address commo } m1[seckey] = struct{}{} if len(v) > 0 { - m[seckey] = common.CopyBytes(v) + m[seckey] = v } else { m[seckey] = nil } diff --git a/core/state/database_test.go b/core/state/database_test.go index 6cea32da2f..8b78d5a7e6 100644 --- a/core/state/database_test.go +++ b/core/state/database_test.go @@ -24,6 +24,7 @@ import ( "testing" "github.com/davecgh/go-spew/spew" + "github.com/holiman/uint256" "github.com/stretchr/testify/assert" "github.com/ledgerwatch/turbo-geth/accounts/abi/bind" @@ -170,9 +171,9 @@ func TestCreate2Revive(t *testing.T) { } // We expect number 0x42 in the position [2], because it is the block number 2 key2 := common.BigToHash(big.NewInt(2)) - var check2 common.Hash + var check2 uint256.Int st.GetState(create2address, &key2, &check2) - if check2 != common.HexToHash("0x42") { + if check2.Uint64() != 0x42 { t.Errorf("expected 0x42 in position 2, got: %x", check2) } @@ -205,14 +206,14 @@ func TestCreate2Revive(t *testing.T) { } // We expect number 0x42 in the position [4], because it is the block number 4 key4 := common.BigToHash(big.NewInt(4)) - var check4 common.Hash + var check4 uint256.Int st.GetState(create2address, &key4, &check4) - if check4 != common.HexToHash("0x42") { + if check4.Uint64() != 0x42 { t.Errorf("expected 0x42 in position 4, got: %x", check4) } // We expect number 0x0 in the position [2], because it is the block number 4 st.GetState(create2address, &key2, &check2) - if check2 != common.HexToHash("0x0") { + if !check2.IsZero() { t.Errorf("expected 0x0 in position 2, got: %x", check2) } } @@ -322,7 +323,8 @@ func TestReorgOverSelfDestruct(t *testing.T) { } // Remember value of field "x" (storage item 0) after the first block, to check after rewinding - var key0, correctValueX common.Hash + var key0 common.Hash + var correctValueX uint256.Int st.GetState(contractAddress, &key0, &correctValueX) // BLOCKS 2 + 3 @@ -351,7 +353,7 @@ func TestReorgOverSelfDestruct(t *testing.T) { t.Fatal(err) } st, _, _ = blockchain.State() - var valueX common.Hash + var valueX uint256.Int st.GetState(contractAddress, &key0, &valueX) if valueX != correctValueX { t.Fatalf("storage value has changed after reorg: %x, expected %x", valueX, correctValueX) @@ -457,7 +459,8 @@ func TestReorgOverStateChange(t *testing.T) { } // Remember value of field "x" (storage item 0) after the first block, to check after rewinding - var key0, correctValueX common.Hash + var key0 common.Hash + var correctValueX uint256.Int st.GetState(contractAddress, &key0, &correctValueX) fmt.Println("Insert block 2") @@ -482,7 +485,7 @@ func TestReorgOverStateChange(t *testing.T) { t.Fatal(err) } st, _, _ = blockchain.State() - var valueX common.Hash + var valueX uint256.Int st.GetState(contractAddress, &key0, &valueX) if valueX != correctValueX { t.Fatalf("storage value has changed after reorg: %x, expected %x", valueX, correctValueX) @@ -746,9 +749,10 @@ func TestCreateOnExistingStorage(t *testing.T) { t.Error("expected contractAddress to exist at the block 1", contractAddress.String()) } - var key0, check0 common.Hash + var key0 common.Hash + var check0 uint256.Int st.GetState(contractAddress, &key0, &check0) - if check0 != common.HexToHash("0x0") { + if !check0.IsZero() { t.Errorf("expected 0x00 in position 0, got: %x", check0) } } @@ -760,12 +764,12 @@ func TestReproduceCrash(t *testing.T) { // 1. Setting storageKey 1 to a non-zero value // 2. Setting storageKey 2 to a non-zero value // 3. Setting both storageKey1 and storageKey2 to zero values - value0 := common.Hash{} + value0 := uint256.NewInt() contract := common.HexToAddress("0x71dd1027069078091B3ca48093B00E4735B20624") storageKey1 := common.HexToHash("0x0e4c0e7175f9d22279a4f63ff74f7fa28b7a954a6454debaa62ce43dd9132541") - value1 := common.HexToHash("0x016345785d8a0000") + value1 := uint256.NewInt().SetUint64(0x016345785d8a0000) storageKey2 := common.HexToHash("0x0e4c0e7175f9d22279a4f63ff74f7fa28b7a954a6454debaa62ce43dd9132542") - value2 := common.HexToHash("0x58c00a51") + value2 := uint256.NewInt().SetUint64(0x58c00a51) db := ethdb.NewMemDatabase() tds := state.NewTrieDbState(common.Hash{}, db, 0) @@ -780,22 +784,22 @@ func TestReproduceCrash(t *testing.T) { } // Start the 2nd transaction tds.StartNewBuffer() - intraBlockState.SetState(contract, storageKey1, value1) + intraBlockState.SetState(contract, &storageKey1, *value1) if err := intraBlockState.FinalizeTx(ctx, tsw); err != nil { t.Errorf("error finalising 1st tx: %v", err) } // Start the 3rd transaction tds.StartNewBuffer() intraBlockState.AddBalance(contract, big.NewInt(1000000000)) - intraBlockState.SetState(contract, storageKey2, value2) + intraBlockState.SetState(contract, &storageKey2, *value2) if err := intraBlockState.FinalizeTx(ctx, tsw); err != nil { t.Errorf("error finalising 1st tx: %v", err) } // Start the 4th transaction - clearing both storage cells tds.StartNewBuffer() intraBlockState.SubBalance(contract, big.NewInt(1000000000)) - intraBlockState.SetState(contract, storageKey1, value0) - intraBlockState.SetState(contract, storageKey2, value0) + intraBlockState.SetState(contract, &storageKey1, *value0) + intraBlockState.SetState(contract, &storageKey2, *value0) if err := intraBlockState.FinalizeTx(ctx, tsw); err != nil { t.Errorf("error finalising 1st tx: %v", err) } diff --git a/core/state/db_state_writer.go b/core/state/db_state_writer.go index 1387936b13..3ef9e5e4d5 100644 --- a/core/state/db_state_writer.go +++ b/core/state/db_state_writer.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/VictoriaMetrics/fastcache" + "github.com/holiman/uint256" "github.com/ledgerwatch/turbo-geth/common" "github.com/ledgerwatch/turbo-geth/common/changeset" @@ -155,7 +156,7 @@ func (dsw *DbStateWriter) UpdateAccountCode(address common.Address, incarnation return nil } -func (dsw *DbStateWriter) WriteAccountStorage(ctx context.Context, address common.Address, incarnation uint64, key, original, value *common.Hash) error { +func (dsw *DbStateWriter) WriteAccountStorage(ctx context.Context, address common.Address, incarnation uint64, key *common.Hash, original, value *uint256.Int) error { // We delegate here first to let the changeSetWrite make its own decision on whether to proceed in case *original == *value if err := dsw.csw.WriteAccountStorage(ctx, address, incarnation, key, original, value); err != nil { return err @@ -173,14 +174,14 @@ func (dsw *DbStateWriter) WriteAccountStorage(ctx context.Context, address commo } compositeKey := dbutils.GenerateCompositeStorageKey(addrHash, incarnation, seckey) - v := cleanUpTrailingZeroes(value[:]) + v := value.Bytes() if dsw.storageCache != nil { dsw.storageCache.Set(compositeKey, v) } if len(v) == 0 { return dsw.stateDb.Delete(dbutils.CurrentStateBucket, compositeKey) } - return dsw.stateDb.Put(dbutils.CurrentStateBucket, compositeKey, common.CopyBytes(v)) + return dsw.stateDb.Put(dbutils.CurrentStateBucket, compositeKey, v) } func (dsw *DbStateWriter) CreateContract(address common.Address) error { diff --git a/core/state/history_test.go b/core/state/history_test.go index a51e6e3702..c15f273ad0 100644 --- a/core/state/history_test.go +++ b/core/state/history_test.go @@ -11,6 +11,9 @@ import ( "testing" "github.com/davecgh/go-spew/spew" + "github.com/holiman/uint256" + "github.com/stretchr/testify/assert" + "github.com/ledgerwatch/turbo-geth/common" "github.com/ledgerwatch/turbo-geth/common/changeset" "github.com/ledgerwatch/turbo-geth/common/dbutils" @@ -19,7 +22,6 @@ import ( "github.com/ledgerwatch/turbo-geth/crypto" "github.com/ledgerwatch/turbo-geth/ethdb" "github.com/ledgerwatch/turbo-geth/trie" - "github.com/stretchr/testify/assert" ) func TestMutation_DeleteTimestamp(t *testing.T) { @@ -137,9 +139,9 @@ func TestMutationCommitThinHistory(t *testing.T) { t.Fatal("incorrect history index") } - resAccStorage := make(map[common.Hash]common.Hash) + resAccStorage := make(map[common.Hash]uint256.Int) err = db.Walk(dbutils.CurrentStateBucket, dbutils.GenerateStoragePrefix(addrHash[:], acc.Incarnation), 8*(common.HashLength+8), func(k, v []byte) (b bool, e error) { - resAccStorage[common.BytesToHash(k[common.HashLength+8:])] = common.BytesToHash(v) + resAccStorage[common.BytesToHash(k[common.HashLength+8:])] = *uint256.NewInt().SetBytes(v) return true, nil }) if err != nil { @@ -158,9 +160,9 @@ func TestMutationCommitThinHistory(t *testing.T) { t.Fatal(err) } - resultHash := common.BytesToHash(res) - if resultHash != v { - t.Fatalf("incorrect storage history for %x %x %x", addrHash.String(), v, resultHash) + result := uint256.NewInt().SetBytes(res) + if !v.Eq(result) { + t.Fatalf("incorrect storage history for %x %x %x", addrHash.String(), v, result) } } } @@ -211,7 +213,7 @@ func TestMutationCommitThinHistory(t *testing.T) { if err1 != nil { t.Fatal(err1) } - value := common.Hash{uint8(10 + j)} + value := uint256.NewInt().SetUint64(uint64(10 + j)) if err2 := expectedChangeSet.Add(dbutils.GenerateCompositeStorageKey(addrHash, accHistory[i].Incarnation, keyHash), value.Bytes()); err2 != nil { t.Fatal(err2) } @@ -227,13 +229,13 @@ func TestMutationCommitThinHistory(t *testing.T) { } } -func generateAccountsWithStorageAndHistory(t *testing.T, db ethdb.Database, numOfAccounts, numOfStateKeys int) ([]common.Hash, []*accounts.Account, []map[common.Hash]common.Hash, []*accounts.Account, []map[common.Hash]common.Hash) { +func generateAccountsWithStorageAndHistory(t *testing.T, db ethdb.Database, numOfAccounts, numOfStateKeys int) ([]common.Hash, []*accounts.Account, []map[common.Hash]uint256.Int, []*accounts.Account, []map[common.Hash]uint256.Int) { t.Helper() accHistory := make([]*accounts.Account, numOfAccounts) accState := make([]*accounts.Account, numOfAccounts) - accStateStorage := make([]map[common.Hash]common.Hash, numOfAccounts) - accHistoryStateStorage := make([]map[common.Hash]common.Hash, numOfAccounts) + accStateStorage := make([]map[common.Hash]uint256.Int, numOfAccounts) + accHistoryStateStorage := make([]map[common.Hash]uint256.Int, numOfAccounts) addrs := make([]common.Address, numOfAccounts) addrHashes := make([]common.Hash, numOfAccounts) tds := NewTrieDbState(common.Hash{}, db, 1) @@ -251,23 +253,23 @@ func generateAccountsWithStorageAndHistory(t *testing.T, db ethdb.Database, numO accState[i].Nonce++ accState[i].Balance = *big.NewInt(200) - accStateStorage[i] = make(map[common.Hash]common.Hash) - accHistoryStateStorage[i] = make(map[common.Hash]common.Hash) + accStateStorage[i] = make(map[common.Hash]uint256.Int) + accHistoryStateStorage[i] = make(map[common.Hash]uint256.Int) for j := 0; j < numOfStateKeys; j++ { key := common.Hash{uint8(i*100 + j)} keyHash, err := common.HashData(key.Bytes()) if err != nil { t.Fatal(err) } - newValue := common.Hash{uint8(j)} - if newValue != (common.Hash{}) { + newValue := uint256.NewInt().SetUint64(uint64(j)) + if !newValue.IsZero() { // Empty value is not considered to be present - accStateStorage[i][keyHash] = newValue + accStateStorage[i][keyHash] = *newValue } - value := common.Hash{uint8(10 + j)} - accHistoryStateStorage[i][keyHash] = value - if err := blockWriter.WriteAccountStorage(ctx, addrs[i], accHistory[i].Incarnation, &key, &value, &newValue); err != nil { + value := uint256.NewInt().SetUint64(uint64(10 + j)) + accHistoryStateStorage[i][keyHash] = *value + if err := blockWriter.WriteAccountStorage(ctx, addrs[i], accHistory[i].Incarnation, &key, value, newValue); err != nil { t.Fatal(err) } } @@ -309,7 +311,7 @@ func TestBoltDB_WalkAsOf1(t *testing.T) { tds := NewTrieDbState(common.Hash{}, db, 1) blockWriter := tds.DbStateWriter() ctx := context.Background() - emptyVal := common.Hash{} + emptyVal := uint256.NewInt() block2Expected := &changeset.ChangeSet{ Changes: make([]changeset.Change, 0), @@ -330,15 +332,15 @@ func TestBoltDB_WalkAsOf1(t *testing.T) { k := common.Hash{i} keyHash, _ := common.HashData(k[:]) key := dbutils.GenerateCompositeStorageKey(addrHash, 1, keyHash) - val3 := common.BytesToHash([]byte("block 3 " + strconv.Itoa(int(i)))) - val5 := common.BytesToHash([]byte("block 5 " + strconv.Itoa(int(i)))) - val := common.BytesToHash([]byte("state " + strconv.Itoa(int(i)))) + val3 := uint256.NewInt().SetBytes([]byte("block 3 " + strconv.Itoa(int(i)))) + val5 := uint256.NewInt().SetBytes([]byte("block 5 " + strconv.Itoa(int(i)))) + val := uint256.NewInt().SetBytes([]byte("state " + strconv.Itoa(int(i)))) if i <= 2 { - if err := blockWriter.WriteAccountStorage(ctx, addr, 1, &k, &val3, &val); err != nil { + if err := blockWriter.WriteAccountStorage(ctx, addr, 1, &k, val3, val); err != nil { t.Fatal(err) } } else { - if err := blockWriter.WriteAccountStorage(ctx, addr, 1, &k, &val3, &val5); err != nil { + if err := blockWriter.WriteAccountStorage(ctx, addr, 1, &k, val3, val5); err != nil { t.Fatal(err) } } @@ -360,14 +362,14 @@ func TestBoltDB_WalkAsOf1(t *testing.T) { k := common.Hash{i} keyHash, _ := common.HashData(k[:]) key := dbutils.GenerateCompositeStorageKey(addrHash, 1, keyHash) - val5 := common.BytesToHash([]byte("block 5 " + strconv.Itoa(int(i)))) - val := common.BytesToHash([]byte("state " + strconv.Itoa(int(i)))) + val5 := uint256.NewInt().SetBytes([]byte("block 5 " + strconv.Itoa(int(i)))) + val := uint256.NewInt().SetBytes([]byte("state " + strconv.Itoa(int(i)))) if i > 4 { - if err := blockWriter.WriteAccountStorage(ctx, addr, 1, &k, &val5, &emptyVal); err != nil { + if err := blockWriter.WriteAccountStorage(ctx, addr, 1, &k, val5, emptyVal); err != nil { t.Fatal(err) } } else { - if err := blockWriter.WriteAccountStorage(ctx, addr, 1, &k, &val5, &val); err != nil { + if err := blockWriter.WriteAccountStorage(ctx, addr, 1, &k, val5, val); err != nil { t.Fatal(err) } } @@ -505,8 +507,8 @@ func TestUnwindTruncateHistory(t *testing.T) { } newAcc.Incarnation = FirstContractIncarnation } - var oldValue common.Hash - var newValue common.Hash + var oldValue uint256.Int + var newValue uint256.Int newValue[0] = 1 var location common.Hash location.SetBytes(big.NewInt(int64(blockNumber)).Bytes()) diff --git a/core/state/intra_block_state.go b/core/state/intra_block_state.go index 6d63602fd3..869f808f7a 100644 --- a/core/state/intra_block_state.go +++ b/core/state/intra_block_state.go @@ -25,6 +25,9 @@ import ( "sort" "sync" + "github.com/holiman/uint256" + "github.com/petar/GoLLRB/llrb" + "github.com/ledgerwatch/turbo-geth/common" "github.com/ledgerwatch/turbo-geth/core/types" "github.com/ledgerwatch/turbo-geth/core/types/accounts" @@ -32,7 +35,6 @@ import ( "github.com/ledgerwatch/turbo-geth/log" "github.com/ledgerwatch/turbo-geth/params" "github.com/ledgerwatch/turbo-geth/trie" - "github.com/petar/GoLLRB/llrb" ) type revision struct { @@ -373,7 +375,7 @@ func (sdb *IntraBlockState) GetCodeHash(addr common.Address) common.Hash { // GetState retrieves a value from the given account's storage trie. // DESCRIBED: docs/programmers_guide/guide.md#address---identifier-of-an-account -func (sdb *IntraBlockState) GetState(addr common.Address, key *common.Hash, value *common.Hash) { +func (sdb *IntraBlockState) GetState(addr common.Address, key *common.Hash, value *uint256.Int) { sdb.Lock() defer sdb.Unlock() @@ -411,7 +413,7 @@ func (sdb *IntraBlockState) GetStorageProof(a common.Address, key common.Hash) ( // GetCommittedState retrieves a value from the given account's committed storage trie. // DESCRIBED: docs/programmers_guide/guide.md#address---identifier-of-an-account -func (sdb *IntraBlockState) GetCommittedState(addr common.Address, key *common.Hash, value *common.Hash) { +func (sdb *IntraBlockState) GetCommittedState(addr common.Address, key *common.Hash, value *uint256.Int) { sdb.Lock() defer sdb.Unlock() @@ -535,7 +537,7 @@ func (sdb *IntraBlockState) SetCode(addr common.Address, code []byte) { } // DESCRIBED: docs/programmers_guide/guide.md#address---identifier-of-an-account -func (sdb *IntraBlockState) SetState(addr common.Address, key, value common.Hash) { +func (sdb *IntraBlockState) SetState(addr common.Address, key *common.Hash, value uint256.Int) { stateObject := sdb.GetOrNewStateObject(addr) if stateObject != nil { stateObject.SetState(key, value) @@ -544,7 +546,7 @@ func (sdb *IntraBlockState) SetState(addr common.Address, key, value common.Hash // SetStorage replaces the entire storage for the specified account with given // storage. This function should only be used for debugging. -func (sdb *IntraBlockState) SetStorage(addr common.Address, storage map[common.Hash]common.Hash) { +func (sdb *IntraBlockState) SetStorage(addr common.Address, storage Storage) { stateObject := sdb.GetOrNewStateObject(addr) if stateObject != nil { stateObject.SetStorage(storage) diff --git a/core/state/intra_block_state_test.go b/core/state/intra_block_state_test.go index 88233b4f97..1537d4c2ba 100644 --- a/core/state/intra_block_state_test.go +++ b/core/state/intra_block_state_test.go @@ -29,6 +29,7 @@ import ( "testing" "testing/quick" + "github.com/holiman/uint256" check "gopkg.in/check.v1" "github.com/ledgerwatch/turbo-geth/common" @@ -52,7 +53,8 @@ func TestUpdateLeaks(t *testing.T) { state.AddBalance(addr, big.NewInt(int64(11*i))) state.SetNonce(addr, uint64(42*i)) if i%2 == 0 { - state.SetState(addr, common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i})) + val := uint256.NewInt().SetBytes([]byte{i, i, i, i}) + state.SetState(addr, &common.Hash{i, i, i}, *val) } if i%3 == 0 { state.SetCode(addr, []byte{i, i, i, i, i}) @@ -96,8 +98,10 @@ func TestIntermediateLeaks(t *testing.T) { state.SetBalance(addr, big.NewInt(int64(11*i)+int64(tweak))) state.SetNonce(addr, uint64(42*i+tweak)) if i%2 == 0 { - state.SetState(addr, common.Hash{i, i, i, 0}, common.Hash{}) - state.SetState(addr, common.Hash{i, i, i, tweak}, common.Hash{i, i, i, i, tweak}) + val := uint256.NewInt() + state.SetState(addr, &common.Hash{i, i, i, 0}, *val) + val.SetBytes([]byte{i, i, i, i, tweak}) + state.SetState(addr, &common.Hash{i, i, i, tweak}, *val) } if i%3 == 0 { state.SetCode(addr, []byte{i, i, i, i, i, tweak}) @@ -247,10 +251,10 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction { { name: "SetState", fn: func(a testAction, s *IntraBlockState) { - var key, val common.Hash + var key common.Hash binary.BigEndian.PutUint16(key[:], uint16(a.args[0])) - binary.BigEndian.PutUint16(val[:], uint16(a.args[1])) - s.SetState(addr, key, val) + val := uint256.NewInt().SetUint64(uint64(a.args[1])) + s.SetState(addr, &key, *val) }, args: make([]int64, 2), }, @@ -418,16 +422,22 @@ func (test *snapshotTest) checkEqual(state, checkstate *IntraBlockState, ds, che checkeq("GetCodeSize", state.GetCodeSize(addr), checkstate.GetCodeSize(addr)) // Check storage. if obj := state.getStateObject(addr); obj != nil { - ds.ForEachStorage(addr, []byte{} /*startKey*/, func(key, seckey, value common.Hash) bool { - var out common.Hash + err = ds.ForEachStorage(addr, []byte{} /*startKey*/, func(key, seckey common.Hash, value uint256.Int) bool { + var out uint256.Int checkstate.GetState(addr, &key, &out) return checkeq("GetState("+key.Hex()+")", out, value) }, 1000) - checkds.ForEachStorage(addr, []byte{} /*startKey*/, func(key, seckey, value common.Hash) bool { - var out common.Hash + if err != nil { + return err + } + err = checkds.ForEachStorage(addr, []byte{} /*startKey*/, func(key, seckey common.Hash, value uint256.Int) bool { + var out uint256.Int state.GetState(addr, &key, &out) return checkeq("GetState("+key.Hex()+")", out, value) }, 1000) + if err != nil { + return err + } } if err != nil { return err diff --git a/core/state/journal.go b/core/state/journal.go index 591c7610ae..4676721692 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -20,6 +20,8 @@ import ( "math/big" "sync" + "github.com/holiman/uint256" + "github.com/ledgerwatch/turbo-geth/common" ) @@ -120,8 +122,9 @@ type ( prev uint64 } storageChange struct { - account *common.Address - key, prevalue common.Hash + account *common.Address + key common.Hash + prevalue uint256.Int } codeChange struct { account *common.Address @@ -212,7 +215,7 @@ func (ch codeChange) dirtied() *common.Address { } func (ch storageChange) revert(s *IntraBlockState) { - s.getStateObject(*ch.account).setState(ch.key, ch.prevalue) + s.getStateObject(*ch.account).setState(&ch.key, ch.prevalue) } func (ch storageChange) dirtied() *common.Address { diff --git a/core/state/plain_state_writer.go b/core/state/plain_state_writer.go index 00e48f8cbe..11332fee64 100644 --- a/core/state/plain_state_writer.go +++ b/core/state/plain_state_writer.go @@ -5,6 +5,7 @@ import ( "encoding/binary" "github.com/VictoriaMetrics/fastcache" + "github.com/holiman/uint256" "github.com/ledgerwatch/turbo-geth/common" "github.com/ledgerwatch/turbo-geth/common/changeset" @@ -108,7 +109,7 @@ func (w *PlainStateWriter) DeleteAccount(ctx context.Context, address common.Add return w.stateDb.Delete(dbutils.PlainStateBucket, address[:]) } -func (w *PlainStateWriter) WriteAccountStorage(ctx context.Context, address common.Address, incarnation uint64, key, original, value *common.Hash) error { +func (w *PlainStateWriter) WriteAccountStorage(ctx context.Context, address common.Address, incarnation uint64, key *common.Hash, original, value *uint256.Int) error { if err := w.csw.WriteAccountStorage(ctx, address, incarnation, key, original, value); err != nil { return err } @@ -117,14 +118,14 @@ func (w *PlainStateWriter) WriteAccountStorage(ctx context.Context, address comm } compositeKey := dbutils.PlainGenerateCompositeStorageKey(address, incarnation, *key) - v := cleanUpTrailingZeroes(value[:]) + v := value.Bytes() if w.storageCache != nil { w.storageCache.Set(compositeKey, v) } if len(v) == 0 { return w.stateDb.Delete(dbutils.PlainStateBucket, compositeKey) } - return w.stateDb.Put(dbutils.PlainStateBucket, compositeKey, common.CopyBytes(v)) + return w.stateDb.Put(dbutils.PlainStateBucket, compositeKey, v) } func (w *PlainStateWriter) CreateContract(address common.Address) error { diff --git a/core/state/readonly.go b/core/state/readonly.go index 8b3efc26af..05537a9880 100644 --- a/core/state/readonly.go +++ b/core/state/readonly.go @@ -23,20 +23,23 @@ import ( "fmt" "math/big" + "github.com/holiman/uint256" + "github.com/petar/GoLLRB/llrb" + "github.com/ledgerwatch/turbo-geth/common" "github.com/ledgerwatch/turbo-geth/common/dbutils" "github.com/ledgerwatch/turbo-geth/core/types/accounts" "github.com/ledgerwatch/turbo-geth/ethdb" "github.com/ledgerwatch/turbo-geth/log" "github.com/ledgerwatch/turbo-geth/trie" - "github.com/petar/GoLLRB/llrb" ) var _ StateReader = (*DbState)(nil) var _ StateWriter = (*DbState)(nil) type storageItem struct { - key, seckey, value common.Hash + key, seckey common.Hash + value uint256.Int } func (a *storageItem) Less(b llrb.Item) bool { @@ -67,7 +70,7 @@ func (dbs *DbState) GetBlockNr() uint64 { return dbs.blockNr } -func (dbs *DbState) ForEachStorage(addr common.Address, start []byte, cb func(key, seckey, value common.Hash) bool, maxResults int) error { +func (dbs *DbState) ForEachStorage(addr common.Address, start []byte, cb func(key, seckey common.Hash, value uint256.Int) bool, maxResults int) error { addrHash, err := common.HashData(addr[:]) if err != nil { log.Error("Error on hashing", "err", err) @@ -93,7 +96,7 @@ func (dbs *DbState) ForEachStorage(addr common.Address, start []byte, cb func(ke t.AscendGreaterOrEqual(min, func(i llrb.Item) bool { item := i.(*storageItem) st.ReplaceOrInsert(item) - if item.value != emptyHash { + if !item.value.IsZero() { copy(lastSecKey[:], item.seckey[:]) // Only count non-zero items overrideCounter++ @@ -133,7 +136,7 @@ func (dbs *DbState) ForEachStorage(addr common.Address, start []byte, cb func(ke var innerErr error st.AscendGreaterOrEqual(min, func(i llrb.Item) bool { item := i.(*storageItem) - if item.value != emptyHash { + if !item.value.IsZero() { // Skip if value == 0 if item.key == emptyHash { key, err := dbs.db.Get(dbutils.PreimagePrefix, item.seckey[:]) @@ -258,7 +261,7 @@ func (dbs *DbState) UpdateAccountCode(address common.Address, incarnation uint64 return nil } -func (dbs *DbState) WriteAccountStorage(_ context.Context, address common.Address, incarnation uint64, key, original, value *common.Hash) error { +func (dbs *DbState) WriteAccountStorage(_ context.Context, address common.Address, incarnation uint64, key *common.Hash, original, value *uint256.Int) error { t, ok := dbs.storage[address] if !ok { t = llrb.New() diff --git a/core/state/state_object.go b/core/state/state_object.go index 86857f8d7f..0092d90e27 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -23,6 +23,8 @@ import ( "io" "math/big" + "github.com/holiman/uint256" + "github.com/ledgerwatch/turbo-geth/common" "github.com/ledgerwatch/turbo-geth/core/types/accounts" "github.com/ledgerwatch/turbo-geth/crypto" @@ -39,7 +41,7 @@ func (c Code) String() string { return string(c) //strings.Join(Disassemble(c), " ") } -type Storage map[common.Hash]common.Hash +type Storage map[common.Hash]uint256.Int func (s Storage) String() (str string) { for key, value := range s { @@ -155,7 +157,7 @@ func (so *stateObject) touch() { } // GetState returns a value from account storage. -func (so *stateObject) GetState(key *common.Hash, out *common.Hash) { +func (so *stateObject) GetState(key *common.Hash, out *uint256.Int) { value, dirty := so.dirtyStorage[*key] if dirty { *out = value @@ -166,7 +168,7 @@ func (so *stateObject) GetState(key *common.Hash, out *common.Hash) { } // GetCommittedState retrieves a value from the committed account storage trie. -func (so *stateObject) GetCommittedState(key *common.Hash, out *common.Hash) { +func (so *stateObject) GetCommittedState(key *common.Hash, out *uint256.Int) { // If we have the original value cached, return that { value, cached := so.originStorage[*key] @@ -196,17 +198,17 @@ func (so *stateObject) GetCommittedState(key *common.Hash, out *common.Hash) { } // SetState updates a value in account storage. -func (so *stateObject) SetState(key, value common.Hash) { +func (so *stateObject) SetState(key *common.Hash, value uint256.Int) { // If the new value is the same as old, don't set - var prev common.Hash - so.GetState(&key, &prev) + var prev uint256.Int + so.GetState(key, &prev) if prev == value { return } // New value is different, update and journal the change so.db.journal.append(storageChange{ account: &so.address, - key: key, + key: *key, prevalue: prev, }) so.setState(key, value) @@ -218,7 +220,7 @@ func (so *stateObject) SetState(key, value common.Hash) { // lookup only happens in the fake state storage. // // Note this function should only be used for debugging purpose. -func (so *stateObject) SetStorage(storage map[common.Hash]common.Hash) { +func (so *stateObject) SetStorage(storage Storage) { // Allocate fake storage if it's nil. if so.fakeStorage == nil { so.fakeStorage = make(Storage) @@ -230,8 +232,8 @@ func (so *stateObject) SetStorage(storage map[common.Hash]common.Hash) { // debugging and the `fake` storage won't be committed to database. } -func (so *stateObject) setState(key, value common.Hash) { - so.dirtyStorage[key] = value +func (so *stateObject) setState(key *common.Hash, value uint256.Int) { + so.dirtyStorage[*key] = value } // updateTrie writes cached storage modifications into the object's storage trie. diff --git a/core/state/state_test.go b/core/state/state_test.go index f591b9d987..341085b46b 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -22,6 +22,7 @@ import ( "math/big" "testing" + "github.com/holiman/uint256" checker "gopkg.in/check.v1" "github.com/ledgerwatch/turbo-geth/common" @@ -109,9 +110,9 @@ func (s *StateSuite) TestNull(c *checker.C) { address := common.HexToAddress("0x823140710bf13990e4500136726d8b55") s.state.CreateAccount(address, true) //value := common.FromHex("0x823140710bf13990e4500136726d8b55") - var value common.Hash + var value uint256.Int - s.state.SetState(address, common.Hash{}, value) + s.state.SetState(address, &common.Hash{}, value) ctx := context.TODO() err := s.state.FinalizeTx(ctx, s.tds.TrieStateWriter()) @@ -123,7 +124,7 @@ func (s *StateSuite) TestNull(c *checker.C) { c.Check(err, checker.IsNil) s.state.GetCommittedState(address, &common.Hash{}, &value) - if value != (common.Hash{}) { + if !value.IsZero() { c.Errorf("expected empty hash. got %x", value) } } @@ -131,21 +132,21 @@ func (s *StateSuite) TestNull(c *checker.C) { func (s *StateSuite) TestSnapshot(c *checker.C) { stateobjaddr := toAddr([]byte("aa")) var storageaddr common.Hash - data1 := common.BytesToHash([]byte{42}) - data2 := common.BytesToHash([]byte{43}) + data1 := uint256.NewInt().SetUint64(42) + data2 := uint256.NewInt().SetUint64(43) // snapshot the genesis state genesis := s.state.Snapshot() // set initial state object value - s.state.SetState(stateobjaddr, storageaddr, data1) + s.state.SetState(stateobjaddr, &storageaddr, *data1) snapshot := s.state.Snapshot() // set a new state object value, revert it and ensure correct content - s.state.SetState(stateobjaddr, storageaddr, data2) + s.state.SetState(stateobjaddr, &storageaddr, *data2) s.state.RevertToSnapshot(snapshot) - var value common.Hash + var value uint256.Int s.state.GetState(stateobjaddr, &storageaddr, &value) c.Assert(value, checker.DeepEquals, data1) s.state.GetCommittedState(stateobjaddr, &storageaddr, &value) @@ -176,11 +177,11 @@ func TestSnapshot2(t *testing.T) { stateobjaddr1 := toAddr([]byte("so1")) var storageaddr common.Hash - data0 := common.BytesToHash([]byte{17}) - data1 := common.BytesToHash([]byte{18}) + data0 := uint256.NewInt().SetUint64(17) + data1 := uint256.NewInt().SetUint64(18) - state.SetState(stateobjaddr0, storageaddr, data0) - state.SetState(stateobjaddr1, storageaddr, data1) + state.SetState(stateobjaddr0, &storageaddr, *data0) + state.SetState(stateobjaddr1, &storageaddr, *data1) // db, trie are already non-empty values so0 := state.getStateObject(stateobjaddr0) @@ -227,7 +228,7 @@ func TestSnapshot2(t *testing.T) { so0Restored := state.getStateObject(stateobjaddr0) // Update lazily-loaded values before comparing. - var tmp common.Hash + var tmp uint256.Int so0Restored.GetState(&storageaddr, &tmp) so0Restored.Code() // non-deleted is equal (restored) diff --git a/core/state/stateless.go b/core/state/stateless.go index 8141ad0848..7684a34d9a 100644 --- a/core/state/stateless.go +++ b/core/state/stateless.go @@ -22,9 +22,10 @@ import ( "fmt" "os" - "github.com/ledgerwatch/turbo-geth/common/dbutils" + "github.com/holiman/uint256" "github.com/ledgerwatch/turbo-geth/common" + "github.com/ledgerwatch/turbo-geth/common/dbutils" "github.com/ledgerwatch/turbo-geth/core/types/accounts" "github.com/ledgerwatch/turbo-geth/trie" ) @@ -212,13 +213,13 @@ func (s *Stateless) UpdateAccountCode(address common.Address, incarnation uint64 // WriteAccountStorage is a part of the StateWriter interface // This implementation registeres the change of the account's storage in the internal double map `storageUpdates` -func (s *Stateless) WriteAccountStorage(_ context.Context, address common.Address, incarnation uint64, key, original, value *common.Hash) error { +func (s *Stateless) WriteAccountStorage(_ context.Context, address common.Address, incarnation uint64, key *common.Hash, original, value *uint256.Int) error { addrHash, err := common.HashData(address[:]) if err != nil { return err } - v := bytes.TrimLeft(value[:], "\x00") + v := value.Bytes() m, ok := s.storageUpdates[addrHash] if !ok { m = make(map[common.Hash][]byte) diff --git a/core/state/util.go b/core/state/util.go deleted file mode 100644 index be5ca31ed0..0000000000 --- a/core/state/util.go +++ /dev/null @@ -1,7 +0,0 @@ -package state - -import "bytes" - -func cleanUpTrailingZeroes(value []byte) []byte { - return bytes.TrimLeft(value[:], "\x00") -} diff --git a/core/vm/evmc.go b/core/vm/evmc.go index 4fff3c7b71..0c2cf74206 100644 --- a/core/vm/evmc.go +++ b/core/vm/evmc.go @@ -27,6 +27,7 @@ import ( "sync" "github.com/ethereum/evmc/v7/bindings/go/evmc" + "github.com/holiman/uint256" "github.com/ledgerwatch/turbo-geth/common" "github.com/ledgerwatch/turbo-geth/core/types" @@ -122,37 +123,35 @@ func (host *hostContext) AccountExists(evmcAddr evmc.Address) bool { } func (host *hostContext) GetStorage(addr evmc.Address, evmcKey evmc.Hash) evmc.Hash { - var value common.Hash + var value uint256.Int key := common.Hash(evmcKey) host.env.IntraBlockState.GetState(common.Address(addr), &key, &value) - return evmc.Hash(value) + return evmc.Hash(value.Bytes32()) } func (host *hostContext) SetStorage(evmcAddr evmc.Address, evmcKey evmc.Hash, evmcValue evmc.Hash) (status evmc.StorageStatus) { addr := common.Address(evmcAddr) key := common.Hash(evmcKey) - value := common.Hash(evmcValue) - var oldValue common.Hash + value := uint256.NewInt().SetBytes(evmcValue[:]) + var oldValue uint256.Int host.env.IntraBlockState.GetState(addr, &key, &oldValue) - if oldValue == value { + if oldValue.Eq(value) { return evmc.StorageUnchanged } - var current, original common.Hash + var current, original uint256.Int host.env.IntraBlockState.GetState(addr, &key, ¤t) host.env.IntraBlockState.GetCommittedState(addr, &key, &original) - host.env.IntraBlockState.SetState(addr, key, value) + host.env.IntraBlockState.SetState(addr, &key, *value) hasNetStorageCostEIP := host.env.ChainConfig().IsConstantinople(host.env.BlockNumber) && !host.env.ChainConfig().IsPetersburg(host.env.BlockNumber) if !hasNetStorageCostEIP { - - zero := common.Hash{} status = evmc.StorageModified - if oldValue == zero { + if oldValue.IsZero() { return evmc.StorageAdded - } else if value == zero { + } else if value.IsZero() { host.env.IntraBlockState.AddRefund(params.SstoreRefundGas) return evmc.StorageDeleted } @@ -160,24 +159,24 @@ func (host *hostContext) SetStorage(evmcAddr evmc.Address, evmcKey evmc.Hash, ev } if original == current { - if original == (common.Hash{}) { // create slot (2.1.1) + if original.IsZero() { // create slot (2.1.1) return evmc.StorageAdded } - if value == (common.Hash{}) { // delete slot (2.1.2b) + if value.IsZero() { // delete slot (2.1.2b) host.env.IntraBlockState.AddRefund(params.NetSstoreClearRefund) return evmc.StorageDeleted } return evmc.StorageModified } - if original != (common.Hash{}) { - if current == (common.Hash{}) { // recreate slot (2.2.1.1) + if !original.IsZero() { + if current.IsZero() { // recreate slot (2.2.1.1) host.env.IntraBlockState.SubRefund(params.NetSstoreClearRefund) - } else if value == (common.Hash{}) { // delete slot (2.2.1.2) + } else if value.IsZero() { // delete slot (2.2.1.2) host.env.IntraBlockState.AddRefund(params.NetSstoreClearRefund) } } - if original == value { - if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1) + if original.Eq(value) { + if original.IsZero() { // reset to original inexistent slot (2.2.2.1) host.env.IntraBlockState.AddRefund(params.NetSstoreResetClearRefund) } else { // reset to original existing slot (2.2.2.2) host.env.IntraBlockState.AddRefund(params.NetSstoreResetRefund) diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 39c523d6a9..f52029362e 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -19,6 +19,8 @@ package vm import ( "errors" + "github.com/holiman/uint256" + "github.com/ledgerwatch/turbo-geth/common" "github.com/ledgerwatch/turbo-geth/common/math" "github.com/ledgerwatch/turbo-geth/params" @@ -94,9 +96,9 @@ var ( ) func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - y, x := stack.Back(1), stack.Back(0) + value, x := stack.Back(1), stack.Back(0) key := common.Hash(x.Bytes32()) - var current common.Hash + var current uint256.Int evm.IntraBlockState.GetState(contract.Address(), &key, ¤t) // The legacy gas metering only takes into consideration the current state @@ -109,9 +111,9 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi // 2. From a non-zero value address to a zero-value address (DELETE) // 3. From a non-zero to a non-zero (CHANGE) switch { - case current == (common.Hash{}) && y.Sign() != 0: // 0 => non 0 + case current.IsZero() && !value.IsZero(): // 0 => non 0 return params.SstoreSetGas, nil - case current != (common.Hash{}) && y.Sign() == 0: // non 0 => 0 + case !current.IsZero() && value.IsZero(): // non 0 => 0 evm.IntraBlockState.AddRefund(params.SstoreRefundGas) return params.SstoreClearGas, nil default: // non 0 => non 0 (or 0 => 0) @@ -132,30 +134,29 @@ 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.1. If original value is 0, add 19800 gas to refund counter. // 2.2.2.2. Otherwise, add 4800 gas to refund counter. - value := common.Hash(y.Bytes32()) - if current == value { // noop (1) + if current.Eq(value) { // noop (1) return params.NetSstoreNoopGas, nil } - var original common.Hash + var original uint256.Int evm.IntraBlockState.GetCommittedState(contract.Address(), &key, &original) if original == current { - if original == (common.Hash{}) { // create slot (2.1.1) + if original.IsZero() { // create slot (2.1.1) return params.NetSstoreInitGas, nil } - if value == (common.Hash{}) { // delete slot (2.1.2b) + if value.IsZero() { // delete slot (2.1.2b) evm.IntraBlockState.AddRefund(params.NetSstoreClearRefund) } return params.NetSstoreCleanGas, nil // write existing slot (2.1.2) } - if original != (common.Hash{}) { - if current == (common.Hash{}) { // recreate slot (2.2.1.1) + if !original.IsZero() { + if current.IsZero() { // recreate slot (2.2.1.1) evm.IntraBlockState.SubRefund(params.NetSstoreClearRefund) - } else if value == (common.Hash{}) { // delete slot (2.2.1.2) + } else if value.IsZero() { // delete slot (2.2.1.2) evm.IntraBlockState.AddRefund(params.NetSstoreClearRefund) } } - if original == value { - if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1) + if original.Eq(value) { + if original.IsZero() { // reset to original inexistent slot (2.2.2.1) evm.IntraBlockState.AddRefund(params.NetSstoreResetClearRefund) } else { // reset to original existing slot (2.2.2.2) evm.IntraBlockState.AddRefund(params.NetSstoreResetRefund) @@ -184,35 +185,34 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m return 0, errors.New("not enough gas for reentrancy sentry") } // Gas sentry honoured, do the actual gas calculation based on the stored value - y, x := stack.Back(1), stack.Back(0) + value, x := stack.Back(1), stack.Back(0) key := common.Hash(x.Bytes32()) - var current common.Hash + var current uint256.Int evm.IntraBlockState.GetState(contract.Address(), &key, ¤t) - value := common.Hash(y.Bytes32()) - if current == value { // noop (1) + if current.Eq(value) { // noop (1) return params.SstoreNoopGasEIP2200, nil } - var original common.Hash + var original uint256.Int evm.IntraBlockState.GetCommittedState(contract.Address(), &key, &original) if original == current { - if original == (common.Hash{}) { // create slot (2.1.1) + if original.IsZero() { // create slot (2.1.1) return params.SstoreInitGasEIP2200, nil } - if value == (common.Hash{}) { // delete slot (2.1.2b) + if value.IsZero() { // delete slot (2.1.2b) evm.IntraBlockState.AddRefund(params.SstoreClearRefundEIP2200) } return params.SstoreCleanGasEIP2200, nil // write existing slot (2.1.2) } - if original != (common.Hash{}) { - if current == (common.Hash{}) { // recreate slot (2.2.1.1) + if !original.IsZero() { + if current.IsZero() { // recreate slot (2.2.1.1) evm.IntraBlockState.SubRefund(params.SstoreClearRefundEIP2200) - } else if value == (common.Hash{}) { // delete slot (2.2.1.2) + } else if value.IsZero() { // delete slot (2.2.1.2) evm.IntraBlockState.AddRefund(params.SstoreClearRefundEIP2200) } } - if original == value { - if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1) + if original.Eq(value) { + if original.IsZero() { // reset to original inexistent slot (2.2.2.1) evm.IntraBlockState.AddRefund(params.SstoreInitRefundEIP2200) } else { // reset to original existing slot (2.2.2.2) evm.IntraBlockState.AddRefund(params.SstoreCleanRefundEIP2200) diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go index 6531f32f19..4cf8464fbe 100644 --- a/core/vm/gas_table_test.go +++ b/core/vm/gas_table_test.go @@ -22,6 +22,8 @@ import ( "math/big" "testing" + "github.com/holiman/uint256" + "github.com/ledgerwatch/turbo-geth/common" "github.com/ledgerwatch/turbo-geth/common/hexutil" "github.com/ledgerwatch/turbo-geth/core/state" @@ -87,7 +89,7 @@ func TestEIP2200(t *testing.T) { s := state.New(tds) s.CreateAccount(address, true) s.SetCode(address, hexutil.MustDecode(tt.input)) - s.SetState(address, common.Hash{}, common.BytesToHash([]byte{tt.original})) + s.SetState(address, &common.Hash{}, *uint256.NewInt().SetUint64(uint64(tt.original))) s.CommitBlock(context.Background(), tds.DbStateWriter()) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 54d7187296..a3569ac99f 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -523,16 +523,15 @@ func opMstore8(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ func opSload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { loc := callContext.stack.peek() interpreter.hasherBuf = loc.Bytes32() - var val common.Hash - interpreter.evm.IntraBlockState.GetState(callContext.contract.Address(), &interpreter.hasherBuf, &val) - loc.SetBytes(val.Bytes()) + interpreter.evm.IntraBlockState.GetState(callContext.contract.Address(), &interpreter.hasherBuf, loc) return nil, nil } func opSstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { loc := callContext.stack.pop() val := callContext.stack.pop() - interpreter.evm.IntraBlockState.SetState(callContext.contract.Address(), common.Hash(loc.Bytes32()), common.Hash(val.Bytes32())) + interpreter.hasherBuf = loc.Bytes32() + interpreter.evm.IntraBlockState.SetState(callContext.contract.Address(), &interpreter.hasherBuf, val) return nil, nil } diff --git a/core/vm/interface.go b/core/vm/interface.go index 9dc28f6be7..4d9506b69b 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -19,6 +19,8 @@ package vm import ( "math/big" + "github.com/holiman/uint256" + "github.com/ledgerwatch/turbo-geth/common" "github.com/ledgerwatch/turbo-geth/core/types" ) @@ -43,9 +45,9 @@ type IntraBlockState interface { SubRefund(uint64) GetRefund() uint64 - GetCommittedState(common.Address, *common.Hash, *common.Hash) - GetState(common.Address, *common.Hash, *common.Hash) - SetState(common.Address, common.Hash, common.Hash) + GetCommittedState(common.Address, *common.Hash, *uint256.Int) + GetState(common.Address, *common.Hash, *uint256.Int) + SetState(common.Address, *common.Hash, uint256.Int) Suicide(common.Address) bool HasSuicided(common.Address) bool diff --git a/eth/api.go b/eth/api.go index 4f9b52a929..bf37c39f1c 100644 --- a/eth/api.go +++ b/eth/api.go @@ -28,6 +28,8 @@ import ( "strings" "time" + "github.com/holiman/uint256" + "github.com/ledgerwatch/turbo-geth/common" "github.com/ledgerwatch/turbo-geth/common/hexutil" "github.com/ledgerwatch/turbo-geth/core" @@ -419,9 +421,9 @@ func StorageRangeAt(dbstate *state.DbState, contractAddress common.Address, star result := StorageRangeResult{Storage: StorageMap{}} resultCount := 0 - if err := dbstate.ForEachStorage(contractAddress, start, func(key, seckey, value common.Hash) bool { + if err := dbstate.ForEachStorage(contractAddress, start, func(key, seckey common.Hash, value uint256.Int) bool { if resultCount < maxResult { - result.Storage[seckey] = StorageEntry{Key: &key, Value: value} + result.Storage[seckey] = StorageEntry{Key: &key, Value: value.Bytes32()} } else { result.NextKey = &seckey } diff --git a/eth/api_test.go b/eth/api_test.go index 98212b9f0b..5f56a3af64 100644 --- a/eth/api_test.go +++ b/eth/api_test.go @@ -18,6 +18,7 @@ package eth import ( "bytes" + "context" "fmt" "math/big" "reflect" @@ -25,9 +26,9 @@ import ( "strconv" "testing" - "context" - "github.com/davecgh/go-spew/spew" + "github.com/holiman/uint256" + "github.com/ledgerwatch/turbo-geth/common" "github.com/ledgerwatch/turbo-geth/core/state" "github.com/ledgerwatch/turbo-geth/crypto" @@ -183,7 +184,8 @@ func TestStorageRangeAt(t *testing.T) { tds.StartNewBuffer() for _, entry := range storage { - statedb.SetState(addr, *entry.Key, entry.Value) + val := uint256.NewInt().SetBytes(entry.Value.Bytes()) + statedb.SetState(addr, entry.Key, *val) } //we are working with contract, so it need codehash&incarnation statedb.SetIncarnation(addr, state.FirstContractIncarnation) diff --git a/eth/tracers/tracer.go b/eth/tracers/tracer.go index 3113d9dceb..9d18768cf2 100644 --- a/eth/tracers/tracer.go +++ b/eth/tracers/tracer.go @@ -25,12 +25,14 @@ import ( "time" "unsafe" + "github.com/holiman/uint256" + duktape "gopkg.in/olebedev/go-duktape.v3" + "github.com/ledgerwatch/turbo-geth/common" "github.com/ledgerwatch/turbo-geth/common/hexutil" "github.com/ledgerwatch/turbo-geth/core/vm" "github.com/ledgerwatch/turbo-geth/crypto" "github.com/ledgerwatch/turbo-geth/log" - duktape "gopkg.in/olebedev/go-duktape.v3" ) // bigIntegerJS is the minified version of https://github.com/peterolson/BigInteger.js. @@ -224,11 +226,11 @@ func (dw *dbWrapper) pushObject(vm *duktape.Context) { addr := popSlice(ctx) key := common.BytesToHash(hash) - var state common.Hash + var state uint256.Int dw.db.GetState(common.BytesToAddress(addr), &key, &state) - ptr := ctx.PushFixedBuffer(len(state)) - copy(makeSlice(ptr, uint(len(state))), state[:]) + ptr := ctx.PushFixedBuffer(state.ByteLen()) + copy(makeSlice(ptr, uint(state.ByteLen())), state.Bytes()) return 1 }) vm.PutPropString(obj, "getState") diff --git a/graphql/graphql.go b/graphql/graphql.go index be37f5c1f3..449d18d596 100644 --- a/graphql/graphql.go +++ b/graphql/graphql.go @@ -22,6 +22,8 @@ import ( "errors" "time" + "github.com/holiman/uint256" + ethereum "github.com/ledgerwatch/turbo-geth" "github.com/ledgerwatch/turbo-geth/common" "github.com/ledgerwatch/turbo-geth/common/hexutil" @@ -85,9 +87,9 @@ func (a *Account) Storage(ctx context.Context, args struct{ Slot common.Hash }) if err != nil { return common.Hash{}, err } - var val common.Hash + var val uint256.Int state.GetState(a.address, &args.Slot, &val) - return val, nil + return val.Bytes32(), nil } // Log represents an individual log message. All arguments are mandatory. diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 97f1d58b5d..9c25a9f053 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -25,6 +25,7 @@ import ( "time" "github.com/davecgh/go-spew/spew" + "github.com/holiman/uint256" bip39 "github.com/tyler-smith/go-bip39" "github.com/ledgerwatch/turbo-geth/accounts" @@ -661,9 +662,9 @@ func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.A return nil, err } keyHash := common.HexToHash(key) - var res common.Hash + var res uint256.Int state.GetState(address, &keyHash, &res) - return res[:], state.Error() + return res.Bytes(), state.Error() } // CallArgs represents the arguments for a call. @@ -722,8 +723,8 @@ type account struct { Nonce *hexutil.Uint64 `json:"nonce"` Code *hexutil.Bytes `json:"code"` Balance **hexutil.Big `json:"balance"` - State *map[common.Hash]common.Hash `json:"state"` - StateDiff *map[common.Hash]common.Hash `json:"stateDiff"` + State *map[common.Hash]uint256.Int `json:"state"` + StateDiff *map[common.Hash]uint256.Int `json:"stateDiff"` } func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides map[common.Address]account, vmCfg vm.Config, timeout time.Duration, globalGasCap *big.Int) (*core.ExecutionResult, error) { @@ -757,7 +758,8 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.Blo // Apply state diff into specified accounts. if account.StateDiff != nil { for key, value := range *account.StateDiff { - state.SetState(addr, key, value) + key := key + state.SetState(addr, &key, value) } } } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 50ab5ad190..0c1871057d 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -25,6 +25,9 @@ import ( "strconv" "strings" + "github.com/holiman/uint256" + "golang.org/x/crypto/sha3" + "github.com/ledgerwatch/turbo-geth/common" "github.com/ledgerwatch/turbo-geth/common/hexutil" "github.com/ledgerwatch/turbo-geth/common/math" @@ -36,7 +39,6 @@ import ( "github.com/ledgerwatch/turbo-geth/ethdb" "github.com/ledgerwatch/turbo-geth/params" "github.com/ledgerwatch/turbo-geth/rlp" - "golang.org/x/crypto/sha3" ) // StateTest checks transaction processing without block context. @@ -245,7 +247,9 @@ func MakePreState(ctx context.Context, db ethdb.Database, accounts core.GenesisA statedb.SetNonce(addr, a.Nonce) statedb.SetBalance(addr, a.Balance) for k, v := range a.Storage { - statedb.SetState(addr, k, v) + key := k + val := uint256.NewInt().SetBytes(v.Bytes()) + statedb.SetState(addr, &key, *val) } } // Commit and re-open to start with a clean state. diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go index 25ebcdf75b..b42c358c53 100644 --- a/tests/vm_test_util.go +++ b/tests/vm_test_util.go @@ -23,6 +23,8 @@ import ( "fmt" "math/big" + "github.com/holiman/uint256" + "github.com/ledgerwatch/turbo-geth/common" "github.com/ledgerwatch/turbo-geth/common/hexutil" "github.com/ledgerwatch/turbo-geth/common/math" @@ -106,12 +108,12 @@ func (t *VMTest) Run(vmconfig vm.Config, blockNr uint64) error { if gasRemaining != uint64(*t.json.GasRemaining) { return fmt.Errorf("remaining gas %v, want %v", gasRemaining, *t.json.GasRemaining) } - var haveV common.Hash + var haveV uint256.Int for addr, account := range t.json.Post { for k, wantV := range account.Storage { key := k state.GetState(addr, &key, &haveV) - if haveV != wantV { + if haveV.Bytes32() != wantV { return fmt.Errorf("wrong storage value at %x:\n got %x\n want %x", k, haveV, wantV) } } -- GitLab