db_test.go 6.9 KB
Newer Older
1 2 3
package l2

import (
4
	"fmt"
5 6 7
	"math/big"
	"testing"

8
	"github.com/ethereum-optimism/optimism/op-program/client/l2/test"
9 10 11 12
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core"
	"github.com/ethereum/go-ethereum/core/rawdb"
	"github.com/ethereum/go-ethereum/core/state"
13
	"github.com/ethereum/go-ethereum/core/tracing"
14 15 16 17 18
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/ethdb"
	"github.com/ethereum/go-ethereum/ethdb/memorydb"
	"github.com/ethereum/go-ethereum/params"
19 20
	"github.com/ethereum/go-ethereum/triedb"
	"github.com/ethereum/go-ethereum/triedb/hashdb"
21
	"github.com/holiman/uint256"
22 23 24 25 26 27 28 29 30 31 32 33 34 35
	"github.com/stretchr/testify/require"
)

var (
	userAccount    = common.HexToAddress("0x1111")
	codeAccount    = common.HexToAddress("0x2222")
	unknownAccount = common.HexToAddress("0x3333")
)

// Should implement the KeyValueStore API
var _ ethdb.KeyValueStore = (*OracleKeyValueStore)(nil)

func TestGet(t *testing.T) {
	t.Run("IncorrectLengthKey", func(t *testing.T) {
36
		oracle := test.NewStubStateOracle(t)
37 38 39 40 41 42 43
		db := NewOracleBackedDB(oracle)
		val, err := db.Get([]byte{1, 2, 3})
		require.ErrorIs(t, err, ErrInvalidKeyLength)
		require.Nil(t, val)
	})

	t.Run("KeyWithCodePrefix", func(t *testing.T) {
44
		oracle := test.NewStubStateOracle(t)
45 46 47 48 49
		db := NewOracleBackedDB(oracle)
		key := common.HexToHash("0x12345678")
		prefixedKey := append(rawdb.CodePrefix, key.Bytes()...)

		expected := []byte{1, 2, 3}
50
		oracle.Code[key] = expected
51 52 53 54 55 56 57
		val, err := db.Get(prefixedKey)

		require.NoError(t, err)
		require.Equal(t, expected, val)
	})

	t.Run("NormalKeyThatHappensToStartWithCodePrefix", func(t *testing.T) {
58
		oracle := test.NewStubStateOracle(t)
59 60 61
		db := NewOracleBackedDB(oracle)
		key := make([]byte, common.HashLength)
		copy(rawdb.CodePrefix, key)
62
		fmt.Println(key[0])
63
		expected := []byte{1, 2, 3}
64
		oracle.Data[common.BytesToHash(key)] = expected
65 66 67 68 69 70 71 72 73
		val, err := db.Get(key)

		require.NoError(t, err)
		require.Equal(t, expected, val)
	})

	t.Run("KnownKey", func(t *testing.T) {
		key := common.HexToHash("0xAA4488")
		expected := []byte{2, 6, 3, 8}
74 75
		oracle := test.NewStubStateOracle(t)
		oracle.Data[key] = expected
76 77 78 79 80 81 82 83 84
		db := NewOracleBackedDB(oracle)
		val, err := db.Get(key.Bytes())
		require.NoError(t, err)
		require.Equal(t, expected, val)
	})
}

func TestPut(t *testing.T) {
	t.Run("NewKey", func(t *testing.T) {
85
		oracle := test.NewStubStateOracle(t)
86 87 88 89 90 91 92 93 94 95 96
		db := NewOracleBackedDB(oracle)
		key := common.HexToHash("0xAA4488")
		value := []byte{2, 6, 3, 8}
		err := db.Put(key.Bytes(), value)
		require.NoError(t, err)

		actual, err := db.Get(key.Bytes())
		require.NoError(t, err)
		require.Equal(t, value, actual)
	})
	t.Run("ReplaceKey", func(t *testing.T) {
97
		oracle := test.NewStubStateOracle(t)
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
		db := NewOracleBackedDB(oracle)
		key := common.HexToHash("0xAA4488")
		value1 := []byte{2, 6, 3, 8}
		value2 := []byte{1, 2, 3}
		err := db.Put(key.Bytes(), value1)
		require.NoError(t, err)
		err = db.Put(key.Bytes(), value2)
		require.NoError(t, err)

		actual, err := db.Get(key.Bytes())
		require.NoError(t, err)
		require.Equal(t, value2, actual)
	})
}

func TestSupportsStateDBOperations(t *testing.T) {
	l2Genesis := createGenesis()
	realDb := rawdb.NewDatabase(memorydb.New())
116
	trieDB := triedb.NewDatabase(realDb, &triedb.Config{HashDB: hashdb.Defaults})
117
	genesisBlock := l2Genesis.MustCommit(realDb, trieDB)
118

119
	loader := test.NewKvStateOracle(t, realDb)
120 121 122 123 124
	assertStateDataAvailable(t, NewOracleBackedDB(loader), l2Genesis, genesisBlock)
}

func TestUpdateState(t *testing.T) {
	l2Genesis := createGenesis()
125
	oracle := test.NewStubStateOracle(t)
126 127
	db := rawdb.NewDatabase(NewOracleBackedDB(oracle))

128
	trieDB := triedb.NewDatabase(db, &triedb.Config{HashDB: hashdb.Defaults})
129
	genesisBlock := l2Genesis.MustCommit(db, trieDB)
130 131 132 133
	assertStateDataAvailable(t, db, l2Genesis, genesisBlock)

	statedb, err := state.New(genesisBlock.Root(), state.NewDatabase(rawdb.NewDatabase(db)), nil)
	require.NoError(t, err)
134
	statedb.MakeSinglethreaded()
135
	statedb.SetBalance(userAccount, uint256.NewInt(50), tracing.BalanceChangeUnspecified)
136
	require.Equal(t, uint256.NewInt(50), statedb.GetBalance(userAccount))
137 138 139
	statedb.SetNonce(userAccount, uint64(5))
	require.Equal(t, uint64(5), statedb.GetNonce(userAccount))

140
	statedb.SetBalance(unknownAccount, uint256.NewInt(60), tracing.BalanceChangeUnspecified)
141
	require.Equal(t, uint256.NewInt(60), statedb.GetBalance(unknownAccount))
142 143 144 145
	statedb.SetCode(codeAccount, []byte{1})
	require.Equal(t, []byte{1}, statedb.GetCode(codeAccount))

	// Changes should be available under the new state root after committing
146
	newRoot, err := statedb.Commit(genesisBlock.NumberU64()+1, false)
147 148 149 150 151 152
	require.NoError(t, err)
	err = statedb.Database().TrieDB().Commit(newRoot, true)
	require.NoError(t, err)

	statedb, err = state.New(newRoot, state.NewDatabase(rawdb.NewDatabase(db)), nil)
	require.NoError(t, err)
153
	statedb.MakeSinglethreaded()
154
	require.Equal(t, uint256.NewInt(50), statedb.GetBalance(userAccount))
155
	require.Equal(t, uint64(5), statedb.GetNonce(userAccount))
156
	require.Equal(t, uint256.NewInt(60), statedb.GetBalance(unknownAccount))
157 158 159 160 161 162 163 164 165
	require.Equal(t, []byte{1}, statedb.GetCode(codeAccount))
}

func createGenesis() *core.Genesis {
	l2Genesis := &core.Genesis{
		Config:     &params.ChainConfig{},
		Difficulty: common.Big0,
		ParentHash: common.Hash{},
		BaseFee:    big.NewInt(7),
166
		Alloc: map[common.Address]types.Account{
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
			userAccount: {
				Balance: big.NewInt(1_000_000_000_000_000_000),
				Nonce:   10,
			},
			codeAccount: {
				Balance: big.NewInt(100),
				Code:    []byte{5, 7, 8, 3, 4},
				Storage: map[common.Hash]common.Hash{
					common.HexToHash("0x01"): common.HexToHash("0x11"),
					common.HexToHash("0x02"): common.HexToHash("0x12"),
					common.HexToHash("0x03"): common.HexToHash("0x13"),
				},
			},
		},
	}
	return l2Genesis
}

func assertStateDataAvailable(t *testing.T, db ethdb.KeyValueStore, l2Genesis *core.Genesis, genesisBlock *types.Block) {
	statedb, err := state.New(genesisBlock.Root(), state.NewDatabase(rawdb.NewDatabase(db)), nil)
	require.NoError(t, err)

	for address, account := range l2Genesis.Alloc {
190
		require.Equal(t, uint256.MustFromBig(account.Balance), statedb.GetBalance(address))
191 192 193 194 195 196 197 198
		require.Equal(t, account.Nonce, statedb.GetNonce(address))
		require.Equal(t, common.BytesToHash(crypto.Keccak256(account.Code)), statedb.GetCodeHash(address))
		require.Equal(t, account.Code, statedb.GetCode(address))
		for key, value := range account.Storage {
			require.Equal(t, value, statedb.GetState(address, key))
		}
	}
	require.Equal(t, common.Hash{}, statedb.GetState(codeAccount, common.HexToHash("0x99")), "retrieve unset storage key")
199
	require.Equal(t, common.U2560, statedb.GetBalance(unknownAccount), "unset account balance")
200 201 202 203
	require.Equal(t, uint64(0), statedb.GetNonce(unknownAccount), "unset account balance")
	require.Nil(t, statedb.GetCode(unknownAccount), "unset account code")
	require.Equal(t, common.Hash{}, statedb.GetCodeHash(unknownAccount), "unset account code hash")
}