diff --git a/db/migrations/0002.sql b/db/migrations/0002.sql index 29e318e428..f7df5d5b89 100644 --- a/db/migrations/0002.sql +++ b/db/migrations/0002.sql @@ -46,6 +46,14 @@ CREATE TABLE statev2.forced_batch ( block_num BIGINT NOT NULL REFERENCES statev2.block (block_num) ON DELETE CASCADE ); +CREATE TABLE statev2.l2block ( + block_num BIGINT PRIMARY KEY, + block_hash VARCHAR NOT NULL, + parent_hash VARCHAR, + state_root VARCHAR, + received_at TIMESTAMP WITH TIME ZONE NOT NULL +); + CREATE TABLE statev2.transaction ( --transaction abstraction. transaction == L2 block hash VARCHAR PRIMARY KEY, from_address VARCHAR, @@ -55,7 +63,7 @@ CREATE TABLE statev2.transaction ( --transaction abstraction. transaction == L2 uncles jsonb, received_at TIMESTAMP WITH TIME ZONE NOT NULL, batch_num BIGINT NOT NULL REFERENCES statev2.batch (batch_num) ON DELETE CASCADE, - l2_block_num BIGINT + l2_block_num BIGINT NOT NULL REFERENCES statev2.l2block (block_num) ON DELETE CASCADE ); CREATE TABLE statev2.exit_root diff --git a/merkletree/tree.go b/merkletree/tree.go index d3524ee1fb..eb9c2bf3b5 100644 --- a/merkletree/tree.go +++ b/merkletree/tree.go @@ -206,7 +206,7 @@ func (tree *StateTree) SetCode(ctx context.Context, address common.Address, code } // SetStorageAt sets storage value at specified position. -func (tree *StateTree) SetStorageAt(ctx context.Context, address common.Address, position *big.Int, value *big.Int, root []byte, txBundleID string) (newRoot []byte, proof *UpdateProof, err error) { +func (tree *StateTree) SetStorageAt(ctx context.Context, address common.Address, position *big.Int, value *big.Int, root []byte) (newRoot []byte, proof *UpdateProof, err error) { r := new(big.Int).SetBytes(root) key, err := KeyContractStorage(address, position.Bytes()) if err != nil { diff --git a/statev2/genesis.go b/statev2/genesis.go new file mode 100644 index 0000000000..25dca473fc --- /dev/null +++ b/statev2/genesis.go @@ -0,0 +1,15 @@ +package statev2 + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +// Genesis contains the information to populate Statev2 on creation +type Genesis struct { + Balances map[common.Address]*big.Int `json:"balances"` + SmartContracts map[common.Address][]byte `json:"smartContracts"` + Storage map[common.Address]map[*big.Int]*big.Int `json:"storage"` + Nonces map[common.Address]*big.Int `json:"nonces"` +} diff --git a/statev2/pgstatestorage.go b/statev2/pgstatestorage.go index 45b7459a2f..036d945148 100644 --- a/statev2/pgstatestorage.go +++ b/statev2/pgstatestorage.go @@ -43,6 +43,7 @@ const ( getNextForcedBatchesSQL = "SELECT forced_batch_num, global_exit_root, timestamp, raw_txs_data, sequencer, batch_num, block_num FROM statev2.forced_batch WHERE batch_num IS NULL LIMIT $1" addBatchNumberInForcedBatchSQL = "UPDATE statev2.forced_batch SET batch_num = $2 WHERE forced_batch_num = $1" getL2BlockByNumberSQL = "SELECT l2_block_num, encoded, header, uncles, received_at from statev2.transaction WHERE batch_num = $1" + addL2BlockSQL = "INSERT INTO statev2.l2block (block_num, block_hash, parent_hash, state_root, received_at) VALUES ($1, $2, $3, $4, $5)" ) // PostgresStorage implements the Storage interface @@ -486,3 +487,10 @@ func (p *PostgresStorage) GetL2BlockByNumber(ctx context.Context, blockNumber ui return &block, nil } + +// AddL2Block adds a new L2 block to the State Store +func (p *PostgresStorage) AddL2Block(ctx context.Context, l2block *L2Block, dbTx pgx.Tx) error { + e := p.getExecQuerier(dbTx) + _, err := e.Exec(ctx, addL2BlockSQL, l2block.BlockNumber, l2block.Hash().String(), l2block.Header.ParentHash.String(), l2block.Header.Root.String(), l2block.ReceivedAt) + return err +} diff --git a/statev2/state.go b/statev2/state.go index 6c48992194..78b161f52b 100644 --- a/statev2/state.go +++ b/statev2/state.go @@ -519,3 +519,65 @@ func (s *State) AddBatchNumberInForcedBatch(ctx context.Context, forceBatchNumbe func (s *State) GetTree() *merkletree.StateTree { return s.tree } + +// SetGenesis populates state with genesis information +func (s *State) SetGenesis(ctx context.Context, genesis Genesis, dbTx pgx.Tx) error { + var ( + root common.Hash + newRoot []byte + err error + ) + + if genesis.Balances != nil { + for address, balance := range genesis.Balances { + newRoot, _, err = s.tree.SetBalance(ctx, address, balance, newRoot) + if err != nil { + return err + } + } + } + + if genesis.SmartContracts != nil { + for address, sc := range genesis.SmartContracts { + newRoot, _, err = s.tree.SetCode(ctx, address, sc, newRoot) + if err != nil { + return err + } + } + } + + if len(genesis.Storage) > 0 { + for address, storage := range genesis.Storage { + for key, value := range storage { + newRoot, _, err = s.tree.SetStorageAt(ctx, address, key, value, newRoot) + if err != nil { + return err + } + } + } + } + + if genesis.Nonces != nil { + for address, nonce := range genesis.Nonces { + newRoot, _, err = s.tree.SetNonce(ctx, address, nonce, newRoot) + if err != nil { + return err + } + } + } + + root.SetBytes(newRoot) + + // Store L2 Genesis Block + header := &types.Header{ + ParentHash: ZeroHash, + Root: root, + } + + l2Block := &L2Block{ + Header: header, + BlockNumber: 0, + } + + return s.PostgresStorage.AddL2Block(ctx, l2Block, dbTx) +} diff --git a/statev2/state_test.go b/statev2/state_test.go index ea537ec3be..56454376c5 100644 --- a/statev2/state_test.go +++ b/statev2/state_test.go @@ -43,6 +43,10 @@ var ( ) func TestMain(m *testing.M) { + if err := dbutils.InitOrReset(cfg); err != nil { + panic(err) + } + stateDb, err = db.NewSQLDB(cfg) if err != nil { panic(err) @@ -317,3 +321,34 @@ func TestExecuteTransaction(t *testing.T) { // require.NoError(t, err) } */ + +/* +func TestGenesis(t *testing.T) { + balances := map[common.Address]*big.Int{ + common.HexToAddress("0xb1D0Dc8E2Ce3a93EB2b32f4C7c3fD9dDAf1211FA"): big.NewInt(1000), + common.HexToAddress("0xb1D0Dc8E2Ce3a93EB2b32f4C7c3fD9dDAf1211FB"): big.NewInt(2000), + } + + nonces := map[common.Address]*big.Int{ + common.HexToAddress("0xb1D0Dc8E2Ce3a93EB2b32f4C7c3fD9dDAf1211FA"): big.NewInt(1), + common.HexToAddress("0xb1D0Dc8E2Ce3a93EB2b32f4C7c3fD9dDAf1211FB"): big.NewInt(1), + } + + smartContracts := map[common.Address][]byte{ + common.HexToAddress("0xae4bb80be56b819606589de61d5ec3b522eeb032"): common.Hex2Bytes("608060405234801561001057600080fd5b50600436106100675760003560e01c806333d6247d1161005057806333d6247d146100a85780633ed691ef146100bd578063a3c573eb146100d257600080fd5b806301fd90441461006c5780633381fe9014610088575b600080fd5b61007560015481565b6040519081526020015b60405180910390f35b6100756100963660046101c7565b60006020819052908152604090205481565b6100bb6100b63660046101c7565b610117565b005b43600090815260208190526040902054610075565b6002546100f29073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161007f565b60025473ffffffffffffffffffffffffffffffffffffffff1633146101c2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603460248201527f476c6f62616c45786974526f6f744d616e616765724c323a3a7570646174654560448201527f786974526f6f743a204f4e4c595f425249444745000000000000000000000000606482015260840160405180910390fd5b600155565b6000602082840312156101d957600080fd5b503591905056fea2646970667358221220d6ed73b81f538d38669b0b750b93be08ca365978fae900eedc9ca93131c97ca664736f6c63430008090033"), + } + + storage := map[common.Address]map[*big.Int]*big.Int{ + common.HexToAddress("0xae4bb80be56b819606589de61d5ec3b522eeb032"): {new(big.Int).SetBytes(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")): new(big.Int).SetBytes(common.Hex2Bytes("9d98deabc42dd696deb9e40b4f1cab7ddbf55988"))}, + } + + genesis := state.Genesis{ + Balances: balances, + Nonces: nonces, + SmartContracts: smartContracts, + Storage: storage, + } + err := testState.SetGenesis(ctx, genesis, nil) + require.NoError(t, err) +} +*/ diff --git a/test/e2e/broadcast_test.go b/test/e2e/broadcast_test.go index ecfbd260d8..4aabde5021 100644 --- a/test/e2e/broadcast_test.go +++ b/test/e2e/broadcast_test.go @@ -9,11 +9,13 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "github.com/hermeznetwork/hermez-core/db" "github.com/hermeznetwork/hermez-core/merkletree" statedbclientpb "github.com/hermeznetwork/hermez-core/merkletree/pb" "github.com/hermeznetwork/hermez-core/sequencerv2/broadcast/pb" "github.com/hermeznetwork/hermez-core/statev2" + state "github.com/hermeznetwork/hermez-core/statev2" executorclientpb "github.com/hermeznetwork/hermez-core/statev2/runtime/executor/pb" "github.com/hermeznetwork/hermez-core/test/dbutils" "github.com/hermeznetwork/hermez-core/test/operations" @@ -137,14 +139,36 @@ func runCmd(c *exec.Cmd) error { func populateDB(ctx context.Context, st *statev2.State) error { const addBatch = "INSERT INTO statev2.batch (batch_num, global_exit_root, timestamp) VALUES ($1, $2, $3)" - const addTransaction = "INSERT INTO statev2.transaction (batch_num, encoded, hash, received_at) VALUES ($1, $2, $3, $4)" + const addTransaction = "INSERT INTO statev2.transaction (batch_num, encoded, hash, received_at, l2_block_num) VALUES ($1, $2, $3, $4, $5)" + var parentHash common.Hash + var l2Block state.L2Block + for i := 1; i <= totalBatches; i++ { if _, err := st.PostgresStorage.Exec(ctx, addBatch, i, common.Hash{}.String(), time.Now()); err != nil { return err } } + for i := 1; i <= totalTxsLastBatch; i++ { - if _, err := st.PostgresStorage.Exec(ctx, addTransaction, totalBatches, fmt.Sprintf(encodedFmt, i), fmt.Sprintf("hash-%d", i), time.Now()); err != nil { + if i == 1 { + parentHash = state.ZeroHash + } else { + parentHash = l2Block.Hash() + } + + // Store L2 Genesis Block + header := new(types.Header) + header.ParentHash = parentHash + + l2Block := new(state.L2Block) + l2Block.Header = header + l2Block.BlockNumber = uint64(i - 1) + + if err := st.PostgresStorage.AddL2Block(ctx, l2Block, nil); err != nil { + return err + } + + if _, err := st.PostgresStorage.Exec(ctx, addTransaction, totalBatches, fmt.Sprintf(encodedFmt, i), fmt.Sprintf("hash-%d", i), time.Now(), l2Block.BlockNumber); err != nil { return err } }