Skip to content

Commit

Permalink
Use uint256.Int rather than common.Hash for storage values to reduce …
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
yperbasis authored May 25, 2020
1 parent db746bb commit b16e560
Show file tree
Hide file tree
Showing 29 changed files with 269 additions and 222 deletions.
6 changes: 4 additions & 2 deletions accounts/abi/bind/backends/simulated.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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.
Expand Down
45 changes: 23 additions & 22 deletions core/blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"testing"
"time"

"github.com/holiman/uint256"
"github.com/stretchr/testify/assert"

"github.com/ledgerwatch/turbo-geth/common"
Expand Down Expand Up @@ -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)
}
}

Expand Down Expand Up @@ -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)
}
}

Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down
9 changes: 6 additions & 3 deletions core/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ package core

import (
"bytes"
"context"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"math/big"
"strings"

"context"
"github.com/holiman/uint256"

"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/common/hexutil"
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
10 changes: 4 additions & 6 deletions core/state/change_set_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}
Expand All @@ -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
Expand Down
14 changes: 8 additions & 6 deletions core/state/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -89,15 +91,15 @@ 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
}

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
Expand Down Expand Up @@ -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)
Expand All @@ -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
}
Expand Down
40 changes: 22 additions & 18 deletions core/state/database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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)
}
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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")
Expand All @@ -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)
Expand Down Expand Up @@ -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)
}
}
Expand All @@ -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)

Expand All @@ -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)
}
Expand Down
Loading

0 comments on commit b16e560

Please sign in to comment.