go-ethereum/ethclient/gethclient/gethclient_test.go
Péter Garamvölgyi 983d630244
feat: Sync and relay L1 messages (#350)
* add l1 config in genesis config (#249)

* add l1 config in genesis config

* fix lint

* Update params/config.go

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>

---------

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>

* extend node configuration (#251)

* extend node configuration

* use block number instead of hash

* accept safe, finalized and numbers for L1Confirmations

* fix typos

---------

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>
Co-authored-by: Péter Garamvölgyi <peter@scroll.io>

* Fix/improve node config parsing (#260)

* raise error on failed parsing

* default value

* add l1-message-type, transaction methods (#252)

* add l1-message-type, transaction methods

* goimports

* Update core/types/transaction.go

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>

* txpool l1 check, pointer change, marhsal test

* draft: start implementing l1message gas behavior

* draft: start implementing l1message gas behavior

* change to gas usage

* error comment typo

Co-authored-by: Haichen Shen <shenhaichen@gmail.com>

* goimports

* update nonce, add hash test (fails), marshal test

* goimports

* target addr cant be nil

* change call msg

* comment out test

* lint

---------

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>
Co-authored-by: Haichen Shen <shenhaichen@gmail.com>

* Add L1 message database (#255)

* add l1-message-type, transaction methods

* goimports

* Update core/types/transaction.go

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>

* add L1 message store to rawdb

* remove comments

* rename to l1_message

* rename variables and add comments

* write l1 msgs in a batch

* add more comments

* update tests

* allow batched and non-batched writes

* rename to accessors_l1_message

* handle error

* add range check

* fix tests

* update comments

* nit

* support blocks with 0 l1 messages

---------

Co-authored-by: Max Wolff <maxcwolff@gmail.com>
Co-authored-by: Max Wolff <max@scroll.io>

* Fix L1Message Deep Copy, Complete Bridge Tx Hash test (#269)

* deep copy value field, add tx hash test

comment

* typo

* Rename nonce to queueindex, increment sender nonce on L1 message execution (#271)

* change nonce to queueindex, increment nonce on L1 message

* fix db acccessors

* Update core/types/transaction_marshalling.go

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>

---------

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>

* Fix db inspect command (#276)

fix db inspect command

* Add l1 sync service (#256)

* extend node configuration

* add l1-message-type, transaction methods

* goimports

* Update core/types/transaction.go

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>

* use block number instead of hash

* accept safe, finalized and numbers for L1Confirmations

* add L1 message store to rawdb

* remove comments

* fix typos

* add L1 message sync service

* use l1 contract address and chain ID

* use L1DeploymentBlock

* add confirmation config

* move bridge client to separate file

* use uint64 block number

* fix bigint comparison

* rename constants

* add more logs

* rename to l1_message

* rename variables and add comments

* write l1 msgs in a batch

* add more comments

* update tests

* allow batched and non-batched writes

* rename to accessors_l1_message

* handle error

* check if config is provided

* improve sync service DB batched writes

* add range check

* fix tests

* update comments

* nit

* fix flush range and improve comments

* solve circular dependency

* update stress tests

* initialize l1 client for geth

* start sync service

* add more comments

* check nil correctly

* address comments

* fix merge

* fix genesis l1config deserialization

* add sync progress logs

* initial sync

* handle leveldb not found error

* use errors.Is

* address comments

* update DefaultPollInterval

---------

Co-authored-by: Nazarii Denha <dengaaa2002@gmail.com>
Co-authored-by: Max Wolff <maxcwolff@gmail.com>
Co-authored-by: Max Wolff <max@scroll.io>

* Add L1 message validation (#272)

* add L1 message validation

* add comments and better error handling

* handle leveldb not found error

* update incorrect condition for genesis block

* typo

* change inclusion index logic

* disable L1 message check for legacy tests

* set NumL1MessagesPerBlock to 0 in tests

* update default genesis config

* Add L1 msg validation tests (#303)

add L1 msg validation tests

* Update miner include l1 messages (#265)

* add l1-message-type, transaction methods

* goimports

* Update core/types/transaction.go

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>

* add L1 message store to rawdb

* add L1 message sync service

* remove comments

* use l1 contract address and chain ID

* extend node configuration

* use block number instead of hash

* accept safe, finalized and numbers for L1Confirmations

* fix typos

* use L1DeploymentBlock

* add confirmation config

* move bridge client to separate file

* use uint64 block number

* fix bigint comparison

* rename constants

* add more logs

* Fix/improve node config parsing (#260)

* raise error on failed parsing

* default value

* rename to l1_message

* rename variables and add comments

* write l1 msgs in a batch

* add more comments

* update tests

* allow batched and non-batched writes

* rename to accessors_l1_message

* handle error

* check if config is provided

* improve sync service DB batched writes

* include l1 messages in blocks: part 1

* add l1-message-type, transaction methods (#252)

* add l1-message-type, transaction methods

* goimports

* Update core/types/transaction.go

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>

* txpool l1 check, pointer change, marhsal test

* draft: start implementing l1message gas behavior

* draft: start implementing l1message gas behavior

* change to gas usage

* error comment typo

Co-authored-by: Haichen Shen <shenhaichen@gmail.com>

* goimports

* update nonce, add hash test (fails), marshal test

* goimports

* target addr cant be nil

* change call msg

* comment out test

* lint

---------

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>
Co-authored-by: Haichen Shen <shenhaichen@gmail.com>

* Add L1 message database (#255)

* add l1-message-type, transaction methods

* goimports

* Update core/types/transaction.go

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>

* add L1 message store to rawdb

* remove comments

* rename to l1_message

* rename variables and add comments

* write l1 msgs in a batch

* add more comments

* update tests

* allow batched and non-batched writes

* rename to accessors_l1_message

* handle error

* add range check

* fix tests

* update comments

* nit

* support blocks with 0 l1 messages

---------

Co-authored-by: Max Wolff <maxcwolff@gmail.com>
Co-authored-by: Max Wolff <max@scroll.io>

* build(docker): auto docker push when pushing git tags (#258)

* build(docker): update docker trigger tag prefix (#259)

* Fix L1Message Deep Copy, Complete Bridge Tx Hash test (#269)

* deep copy value field, add tx hash test

comment

* typo

* commitl1messages

* lint

* Revert "add L1 message sync service"

This reverts commit 5305e8a5de14766ed249e1a7d64042c7a72cf5c2.

* Revert "move bridge client to separate file"

This reverts commit 0b220bee37de93c3250545e23430db2c401a2f90.

* update branch

* use commitMessages for l1Txs

* little fix

* fix config

* fix test

* comment fixes

* fix

* fix config check

---------

Co-authored-by: Max Wolff <maxcwolff@gmail.com>
Co-authored-by: Max Wolff <max@scroll.io>
Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>
Co-authored-by: Péter Garamvölgyi <peter@scroll.io>
Co-authored-by: Haichen Shen <shenhaichen@gmail.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>

* Add ErrUnknownAncestor tests (#305)

add ErrUnknownAncestor tests

* worker test include l1 msgs (#306)

* worker test include l1 msgs

* move L1 message index update next to block insertion

---------

Co-authored-by: Péter Garamvölgyi <peter@scroll.io>

* exclude l1 messages from transaction count limit in block (#307)

* exclude l1 messages from transaction count limit in block

* fix comments

* trigger ci

* nit

---------

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>
Co-authored-by: Péter Garamvölgyi <peter@scroll.io>

* Expose queueIndex on Transaction (#316)

expose queueIndex on Transaction

* test that l1msg doesn't count in maxTxPerBlock limit (#312)

* test that l1msg doesn't count in maxTxPerBlock limit

* fix, comment

* retrigger ci

* change order inside test

---------

Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>

* reuse trace nonce field for queueIndex

* expose scroll APIs on the geth console

* add L1 message query APIs

* Trigger new block on new l1 messages (#343)

* trigger new block on new l1 messages

* typo

* initialize l1MsgCh

* fix worker l1msg tests (#345)

---------

Co-authored-by: Nazarii Denha <dengaaa2002@gmail.com>

* test(worker): ensure that l1 messages are included in the correct order (#346)

test that l1msgs added in correct order

* rename enqueueIndex --> queueIndex

* move QueueIndex into transaction

* improve l1 db interface

* formatting

* bump version

* print l1config

* add API to query latest included message queue index

* clean up tx limit logic

* add clarifying comments and todos to ValidateL1Messages

* improve db comments and logs

* clean up L1MessageTx type handling

* format

* format

* improve L1 message block check

* fix missing L1 event handling

* fix TestL1MessageValidationFailure

* simplify sync height resume logic

* make l1Config.l1MessageQueueAddress non-pointer

* improve command line flags

* remove todo

* use abigen tools for log filtering

* cache block L1 message count

* nit: fix variable name case

* improve logs

* flush pending writes to DB before shutdown

---------

Co-authored-by: Nazarii Denha <dengaaa2002@gmail.com>
Co-authored-by: Max Wolff <max@scroll.io>
Co-authored-by: Haichen Shen <shenhaichen@gmail.com>
Co-authored-by: Max Wolff <maxcwolff@gmail.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <haoyu@protonmail.com>
2023-06-07 07:24:19 -07:00

341 lines
9.8 KiB
Go

// Copyright 2021 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package gethclient
import (
"bytes"
"context"
"math/big"
"testing"
"github.com/scroll-tech/go-ethereum"
"github.com/scroll-tech/go-ethereum/common"
"github.com/scroll-tech/go-ethereum/consensus/ethash"
"github.com/scroll-tech/go-ethereum/core"
"github.com/scroll-tech/go-ethereum/core/rawdb"
"github.com/scroll-tech/go-ethereum/core/types"
"github.com/scroll-tech/go-ethereum/crypto"
"github.com/scroll-tech/go-ethereum/eth"
"github.com/scroll-tech/go-ethereum/eth/ethconfig"
"github.com/scroll-tech/go-ethereum/ethclient"
"github.com/scroll-tech/go-ethereum/node"
"github.com/scroll-tech/go-ethereum/params"
"github.com/scroll-tech/go-ethereum/rollup/rcfg"
"github.com/scroll-tech/go-ethereum/rpc"
)
var (
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
emptyAccountKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f292")
emptyAddr = crypto.PubkeyToAddress(emptyAccountKey.PublicKey)
testBalance = big.NewInt(2e15)
)
func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
// Generate test chain.
genesis, blocks := generateTestChain()
// Create node
n, err := node.New(&node.Config{})
if err != nil {
t.Fatalf("can't create new node: %v", err)
}
// Create Ethereum Service
config := &ethconfig.Config{Genesis: genesis}
config.Ethash.PowMode = ethash.ModeFake
ethservice, err := eth.New(n, config, nil)
if err != nil {
t.Fatalf("can't create new ethereum service: %v", err)
}
// Import the test chain.
if err := n.Start(); err != nil {
t.Fatalf("can't start test node: %v", err)
}
if _, err := ethservice.BlockChain().InsertChain(blocks[1:]); err != nil {
t.Fatalf("can't import test blocks: %v", err)
}
return n, blocks
}
func generateTestChain() (*core.Genesis, []*types.Block) {
db := rawdb.NewMemoryDatabase()
config := params.AllEthashProtocolChanges
genesis := &core.Genesis{
Config: config,
Alloc: core.GenesisAlloc{
testAddr: {Balance: testBalance},
rcfg.L1GasPriceOracleAddress: {
Balance: big.NewInt(0),
Storage: map[common.Hash]common.Hash{
rcfg.L1BaseFeeSlot: common.BigToHash(big.NewInt(10000)),
rcfg.OverheadSlot: common.BigToHash(big.NewInt(10000)),
rcfg.ScalarSlot: common.BigToHash(big.NewInt(10000)),
},
},
},
ExtraData: []byte("test genesis"),
Timestamp: 9000,
}
generate := func(i int, g *core.BlockGen) {
g.OffsetTime(5)
g.SetExtra([]byte("test"))
}
gblock := genesis.ToBlock(db)
engine := ethash.NewFaker()
blocks, _ := core.GenerateChain(config, gblock, engine, db, 1, generate)
blocks = append([]*types.Block{gblock}, blocks...)
return genesis, blocks
}
func TestGethClient(t *testing.T) {
backend, _ := newTestBackend(t)
client, err := backend.Attach()
if err != nil {
t.Fatal(err)
}
defer backend.Close()
defer client.Close()
tests := []struct {
name string
test func(t *testing.T)
}{
{
"TestAccessList",
func(t *testing.T) { testAccessList(t, client) },
},
{
"TestGetProof",
func(t *testing.T) { testGetProof(t, client) },
}, {
"TestGCStats",
func(t *testing.T) { testGCStats(t, client) },
}, {
"TestMemStats",
func(t *testing.T) { testMemStats(t, client) },
}, {
"TestGetNodeInfo",
func(t *testing.T) { testGetNodeInfo(t, client) },
}, {
"TestSetHead",
func(t *testing.T) { testSetHead(t, client) },
}, {
"TestSubscribePendingTxs",
func(t *testing.T) { testSubscribePendingTransactions(t, client) },
}, {
"TestCallContract",
func(t *testing.T) { testCallContract(t, client) },
}, {
"TestCallContractNoGas",
func(t *testing.T) { testCallContractNoGas(t, client) },
},
}
t.Parallel()
for _, tt := range tests {
t.Run(tt.name, tt.test)
}
}
func testAccessList(t *testing.T, client *rpc.Client) {
ec := New(client)
// Test transfer
msg := ethereum.CallMsg{
From: testAddr,
To: &common.Address{},
Gas: 21000,
GasPrice: big.NewInt(765625000),
Value: big.NewInt(1),
}
al, gas, vmErr, err := ec.CreateAccessList(context.Background(), msg)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if vmErr != "" {
t.Fatalf("unexpected vm error: %v", vmErr)
}
if gas != 21000 {
t.Fatalf("unexpected gas used: %v", gas)
}
if len(*al) != 0 {
t.Fatalf("unexpected length of accesslist: %v", len(*al))
}
// Test reverting transaction
msg = ethereum.CallMsg{
From: testAddr,
To: nil,
Gas: 100000,
GasPrice: big.NewInt(1000000000),
Value: big.NewInt(1),
Data: common.FromHex("0x608060806080608155fd"),
}
al, gas, vmErr, err = ec.CreateAccessList(context.Background(), msg)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if vmErr == "" {
t.Fatalf("wanted vmErr, got none")
}
if gas == 21000 {
t.Fatalf("unexpected gas used: %v", gas)
}
if len(*al) != 1 || al.StorageKeys() != 1 {
t.Fatalf("unexpected length of accesslist: %v", len(*al))
}
// address changes between calls, so we can't test for it.
if (*al)[0].Address == common.HexToAddress("0x0") {
t.Fatalf("unexpected address: %v", (*al)[0].Address)
}
if (*al)[0].StorageKeys[0] != common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000081") {
t.Fatalf("unexpected storage key: %v", (*al)[0].StorageKeys[0])
}
}
func testGetProof(t *testing.T, client *rpc.Client) {
ec := New(client)
ethcl := ethclient.NewClient(client)
result, err := ec.GetProof(context.Background(), testAddr, []string{}, nil)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(result.Address[:], testAddr[:]) {
t.Fatalf("unexpected address, want: %v got: %v", testAddr, result.Address)
}
// test nonce
nonce, _ := ethcl.NonceAt(context.Background(), result.Address, nil)
if result.Nonce != nonce {
t.Fatalf("invalid nonce, want: %v got: %v", nonce, result.Nonce)
}
// test balance
balance, _ := ethcl.BalanceAt(context.Background(), result.Address, nil)
if result.Balance.Cmp(balance) != 0 {
t.Fatalf("invalid balance, want: %v got: %v", balance, result.Balance)
}
}
func testGCStats(t *testing.T, client *rpc.Client) {
ec := New(client)
_, err := ec.GCStats(context.Background())
if err != nil {
t.Fatal(err)
}
}
func testMemStats(t *testing.T, client *rpc.Client) {
ec := New(client)
stats, err := ec.MemStats(context.Background())
if err != nil {
t.Fatal(err)
}
if stats.Alloc == 0 {
t.Fatal("Invalid mem stats retrieved")
}
}
func testGetNodeInfo(t *testing.T, client *rpc.Client) {
ec := New(client)
info, err := ec.GetNodeInfo(context.Background())
if err != nil {
t.Fatal(err)
}
if info.Name == "" {
t.Fatal("Invalid node info retrieved")
}
}
func testSetHead(t *testing.T, client *rpc.Client) {
ec := New(client)
err := ec.SetHead(context.Background(), big.NewInt(0))
if err != nil {
t.Fatal(err)
}
}
func testSubscribePendingTransactions(t *testing.T, client *rpc.Client) {
ec := New(client)
ethcl := ethclient.NewClient(client)
// Subscribe to Transactions
ch := make(chan common.Hash)
ec.SubscribePendingTransactions(context.Background(), ch)
// Send a transaction
chainID, err := ethcl.ChainID(context.Background())
if err != nil {
t.Fatal(err)
}
// Create transaction
tx := types.NewTransaction(0, common.Address{1}, big.NewInt(1), 22000, big.NewInt(1), nil)
signer := types.LatestSignerForChainID(chainID)
signature, err := crypto.Sign(signer.Hash(tx).Bytes(), testKey)
if err != nil {
t.Fatal(err)
}
signedTx, err := tx.WithSignature(signer, signature)
if err != nil {
t.Fatal(err)
}
// Send transaction
err = ethcl.SendTransaction(context.Background(), signedTx)
if err != nil {
t.Fatal(err)
}
// Check that the transaction was send over the channel
hash := <-ch
if hash != signedTx.Hash() {
t.Fatalf("Invalid tx hash received, got %v, want %v", hash, signedTx.Hash())
}
}
func testCallContract(t *testing.T, client *rpc.Client) {
ec := New(client)
msg := ethereum.CallMsg{
From: testAddr,
To: &common.Address{},
Gas: 21000,
GasPrice: big.NewInt(1000000000),
Value: big.NewInt(1),
}
// CallContract without override
if _, err := ec.CallContract(context.Background(), msg, big.NewInt(0), nil); err != nil {
t.Fatalf("unexpected error: %v", err)
}
// CallContract with override
override := OverrideAccount{
Nonce: 1,
}
mapAcc := make(map[common.Address]OverrideAccount)
mapAcc[testAddr] = override
if _, err := ec.CallContract(context.Background(), msg, big.NewInt(0), &mapAcc); err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
func testCallContractNoGas(t *testing.T, client *rpc.Client) {
ec := New(client)
msg := ethereum.CallMsg{
From: emptyAddr, // 0 balance
To: &common.Address{},
Gas: 21000,
GasPrice: big.NewInt(0), // gas price 0
Value: big.NewInt(0),
}
// this would fail with `insufficient funds for gas * price + value`
// before we started considering l1fee for 0 gas calls.
if _, err := ec.CallContract(context.Background(), msg, big.NewInt(0), nil); err != nil {
t.Fatalf("unexpected error: %v", err)
}
}