mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-28 15:46:43 +00:00
* 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>
696 lines
19 KiB
Go
696 lines
19 KiB
Go
// Copyright 2016 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 ethclient
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"math/big"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"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/node"
|
|
"github.com/scroll-tech/go-ethereum/params"
|
|
"github.com/scroll-tech/go-ethereum/rollup/rcfg"
|
|
"github.com/scroll-tech/go-ethereum/rpc"
|
|
)
|
|
|
|
// Verify that Client implements the ethereum interfaces.
|
|
var (
|
|
_ = ethereum.ChainReader(&Client{})
|
|
_ = ethereum.TransactionReader(&Client{})
|
|
_ = ethereum.ChainStateReader(&Client{})
|
|
_ = ethereum.ChainSyncReader(&Client{})
|
|
_ = ethereum.ContractCaller(&Client{})
|
|
_ = ethereum.GasEstimator(&Client{})
|
|
_ = ethereum.GasPricer(&Client{})
|
|
_ = ethereum.LogFilterer(&Client{})
|
|
_ = ethereum.PendingStateReader(&Client{})
|
|
// _ = ethereum.PendingStateEventer(&Client{})
|
|
_ = ethereum.PendingContractCaller(&Client{})
|
|
)
|
|
|
|
func TestToFilterArg(t *testing.T) {
|
|
blockHashErr := fmt.Errorf("cannot specify both BlockHash and FromBlock/ToBlock")
|
|
addresses := []common.Address{
|
|
common.HexToAddress("0xD36722ADeC3EdCB29c8e7b5a47f352D701393462"),
|
|
}
|
|
blockHash := common.HexToHash(
|
|
"0xeb94bb7d78b73657a9d7a99792413f50c0a45c51fc62bdcb08a53f18e9a2b4eb",
|
|
)
|
|
|
|
for _, testCase := range []struct {
|
|
name string
|
|
input ethereum.FilterQuery
|
|
output interface{}
|
|
err error
|
|
}{
|
|
{
|
|
"without BlockHash",
|
|
ethereum.FilterQuery{
|
|
Addresses: addresses,
|
|
FromBlock: big.NewInt(1),
|
|
ToBlock: big.NewInt(2),
|
|
Topics: [][]common.Hash{},
|
|
},
|
|
map[string]interface{}{
|
|
"address": addresses,
|
|
"fromBlock": "0x1",
|
|
"toBlock": "0x2",
|
|
"topics": [][]common.Hash{},
|
|
},
|
|
nil,
|
|
},
|
|
{
|
|
"with nil fromBlock and nil toBlock",
|
|
ethereum.FilterQuery{
|
|
Addresses: addresses,
|
|
Topics: [][]common.Hash{},
|
|
},
|
|
map[string]interface{}{
|
|
"address": addresses,
|
|
"fromBlock": "0x0",
|
|
"toBlock": "latest",
|
|
"topics": [][]common.Hash{},
|
|
},
|
|
nil,
|
|
},
|
|
{
|
|
"with negative fromBlock and negative toBlock",
|
|
ethereum.FilterQuery{
|
|
Addresses: addresses,
|
|
FromBlock: big.NewInt(-1),
|
|
ToBlock: big.NewInt(-1),
|
|
Topics: [][]common.Hash{},
|
|
},
|
|
map[string]interface{}{
|
|
"address": addresses,
|
|
"fromBlock": "pending",
|
|
"toBlock": "pending",
|
|
"topics": [][]common.Hash{},
|
|
},
|
|
nil,
|
|
},
|
|
{
|
|
"with blockhash",
|
|
ethereum.FilterQuery{
|
|
Addresses: addresses,
|
|
BlockHash: &blockHash,
|
|
Topics: [][]common.Hash{},
|
|
},
|
|
map[string]interface{}{
|
|
"address": addresses,
|
|
"blockHash": blockHash,
|
|
"topics": [][]common.Hash{},
|
|
},
|
|
nil,
|
|
},
|
|
{
|
|
"with blockhash and from block",
|
|
ethereum.FilterQuery{
|
|
Addresses: addresses,
|
|
BlockHash: &blockHash,
|
|
FromBlock: big.NewInt(1),
|
|
Topics: [][]common.Hash{},
|
|
},
|
|
nil,
|
|
blockHashErr,
|
|
},
|
|
{
|
|
"with blockhash and to block",
|
|
ethereum.FilterQuery{
|
|
Addresses: addresses,
|
|
BlockHash: &blockHash,
|
|
ToBlock: big.NewInt(1),
|
|
Topics: [][]common.Hash{},
|
|
},
|
|
nil,
|
|
blockHashErr,
|
|
},
|
|
{
|
|
"with blockhash and both from / to block",
|
|
ethereum.FilterQuery{
|
|
Addresses: addresses,
|
|
BlockHash: &blockHash,
|
|
FromBlock: big.NewInt(1),
|
|
ToBlock: big.NewInt(2),
|
|
Topics: [][]common.Hash{},
|
|
},
|
|
nil,
|
|
blockHashErr,
|
|
},
|
|
} {
|
|
t.Run(testCase.name, func(t *testing.T) {
|
|
output, err := toFilterArg(testCase.input)
|
|
if (testCase.err == nil) != (err == nil) {
|
|
t.Fatalf("expected error %v but got %v", testCase.err, err)
|
|
}
|
|
if testCase.err != nil {
|
|
if testCase.err.Error() != err.Error() {
|
|
t.Fatalf("expected error %v but got %v", testCase.err, err)
|
|
}
|
|
} else if !reflect.DeepEqual(testCase.output, output) {
|
|
t.Fatalf("expected filter arg %v but got %v", testCase.output, output)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
var (
|
|
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
|
|
emptyAccountKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f292")
|
|
emptyAddr = crypto.PubkeyToAddress(emptyAccountKey.PublicKey)
|
|
testBalance = big.NewInt(2e15)
|
|
)
|
|
|
|
var genesis = &core.Genesis{
|
|
Config: params.AllEthashProtocolChanges,
|
|
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,
|
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
|
}
|
|
|
|
var testTx1 = types.MustSignNewTx(testKey, types.LatestSigner(genesis.Config), &types.LegacyTx{
|
|
Nonce: 0,
|
|
Value: big.NewInt(12),
|
|
GasPrice: big.NewInt(params.InitialBaseFee),
|
|
Gas: params.TxGas,
|
|
To: &common.Address{2},
|
|
})
|
|
|
|
var testTx2 = types.MustSignNewTx(testKey, types.LatestSigner(genesis.Config), &types.LegacyTx{
|
|
Nonce: 1,
|
|
Value: big.NewInt(8),
|
|
GasPrice: big.NewInt(params.InitialBaseFee),
|
|
Gas: params.TxGas,
|
|
To: &common.Address{2},
|
|
})
|
|
|
|
func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
|
|
// Generate test chain.
|
|
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 := ðconfig.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() []*types.Block {
|
|
db := rawdb.NewMemoryDatabase()
|
|
generate := func(i int, g *core.BlockGen) {
|
|
g.OffsetTime(5)
|
|
g.SetExtra([]byte("test"))
|
|
if i == 1 {
|
|
// Test transactions are included in block #2.
|
|
g.AddTx(testTx1)
|
|
g.AddTx(testTx2)
|
|
}
|
|
}
|
|
gblock := genesis.ToBlock(db)
|
|
engine := ethash.NewFaker()
|
|
blocks, _ := core.GenerateChain(genesis.Config, gblock, engine, db, 2, generate)
|
|
blocks = append([]*types.Block{gblock}, blocks...)
|
|
return blocks
|
|
}
|
|
|
|
func TestEthClient(t *testing.T) {
|
|
backend, chain := newTestBackend(t)
|
|
client, _ := backend.Attach()
|
|
defer backend.Close()
|
|
defer client.Close()
|
|
|
|
tests := map[string]struct {
|
|
test func(t *testing.T)
|
|
}{
|
|
"Header": {
|
|
func(t *testing.T) { testHeader(t, chain, client) },
|
|
},
|
|
"BalanceAt": {
|
|
func(t *testing.T) { testBalanceAt(t, client) },
|
|
},
|
|
"TxInBlockInterrupted": {
|
|
func(t *testing.T) { testTransactionInBlockInterrupted(t, client) },
|
|
},
|
|
"ChainID": {
|
|
func(t *testing.T) { testChainID(t, client) },
|
|
},
|
|
"GetBlock": {
|
|
func(t *testing.T) { testGetBlock(t, client) },
|
|
},
|
|
"StatusFunctions": {
|
|
func(t *testing.T) { testStatusFunctions(t, client) },
|
|
},
|
|
"CallContract": {
|
|
func(t *testing.T) { testCallContract(t, client) },
|
|
},
|
|
"EstimateGas": {
|
|
func(t *testing.T) { testEstimateGas(t, client) },
|
|
},
|
|
"AtFunctions": {
|
|
func(t *testing.T) { testAtFunctions(t, client) },
|
|
},
|
|
"TransactionSender": {
|
|
func(t *testing.T) { testTransactionSender(t, client) },
|
|
},
|
|
}
|
|
|
|
t.Parallel()
|
|
for name, tt := range tests {
|
|
t.Run(name, tt.test)
|
|
}
|
|
}
|
|
|
|
func testHeader(t *testing.T, chain []*types.Block, client *rpc.Client) {
|
|
tests := map[string]struct {
|
|
block *big.Int
|
|
want *types.Header
|
|
wantErr error
|
|
}{
|
|
"genesis": {
|
|
block: big.NewInt(0),
|
|
want: chain[0].Header(),
|
|
},
|
|
"first_block": {
|
|
block: big.NewInt(1),
|
|
want: chain[1].Header(),
|
|
},
|
|
"future_block": {
|
|
block: big.NewInt(1000000000),
|
|
want: nil,
|
|
wantErr: ethereum.NotFound,
|
|
},
|
|
}
|
|
for name, tt := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
ec := NewClient(client)
|
|
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
|
defer cancel()
|
|
|
|
got, err := ec.HeaderByNumber(ctx, tt.block)
|
|
if !errors.Is(err, tt.wantErr) {
|
|
t.Fatalf("HeaderByNumber(%v) error = %q, want %q", tt.block, err, tt.wantErr)
|
|
}
|
|
if got != nil && got.Number != nil && got.Number.Sign() == 0 {
|
|
got.Number = big.NewInt(0) // hack to make DeepEqual work
|
|
}
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
t.Fatalf("HeaderByNumber(%v)\n = %v\nwant %v", tt.block, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func testBalanceAt(t *testing.T, client *rpc.Client) {
|
|
tests := map[string]struct {
|
|
account common.Address
|
|
block *big.Int
|
|
want *big.Int
|
|
wantErr error
|
|
}{
|
|
"valid_account_genesis": {
|
|
account: testAddr,
|
|
block: big.NewInt(0),
|
|
want: testBalance,
|
|
},
|
|
"valid_account": {
|
|
account: testAddr,
|
|
block: big.NewInt(1),
|
|
want: testBalance,
|
|
},
|
|
"non_existent_account": {
|
|
account: common.Address{1},
|
|
block: big.NewInt(1),
|
|
want: big.NewInt(0),
|
|
},
|
|
"future_block": {
|
|
account: testAddr,
|
|
block: big.NewInt(1000000000),
|
|
want: big.NewInt(0),
|
|
wantErr: errors.New("header not found"),
|
|
},
|
|
}
|
|
for name, tt := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
ec := NewClient(client)
|
|
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
|
defer cancel()
|
|
|
|
got, err := ec.BalanceAt(ctx, tt.account, tt.block)
|
|
if tt.wantErr != nil && (err == nil || err.Error() != tt.wantErr.Error()) {
|
|
t.Fatalf("BalanceAt(%x, %v) error = %q, want %q", tt.account, tt.block, err, tt.wantErr)
|
|
}
|
|
if got.Cmp(tt.want) != 0 {
|
|
t.Fatalf("BalanceAt(%x, %v) = %v, want %v", tt.account, tt.block, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func testTransactionInBlockInterrupted(t *testing.T, client *rpc.Client) {
|
|
ec := NewClient(client)
|
|
|
|
// Get current block by number.
|
|
block, err := ec.BlockByNumber(context.Background(), nil)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
// Test tx in block interupted.
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
tx, err := ec.TransactionInBlock(ctx, block.Hash(), 0)
|
|
if tx != nil {
|
|
t.Fatal("transaction should be nil")
|
|
}
|
|
if err == nil || err == ethereum.NotFound {
|
|
t.Fatal("error should not be nil/notfound")
|
|
}
|
|
|
|
// Test tx in block not found.
|
|
if _, err := ec.TransactionInBlock(context.Background(), block.Hash(), 20); err != ethereum.NotFound {
|
|
t.Fatal("error should be ethereum.NotFound")
|
|
}
|
|
}
|
|
|
|
func testChainID(t *testing.T, client *rpc.Client) {
|
|
ec := NewClient(client)
|
|
id, err := ec.ChainID(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if id == nil || id.Cmp(params.AllEthashProtocolChanges.ChainID) != 0 {
|
|
t.Fatalf("ChainID returned wrong number: %+v", id)
|
|
}
|
|
}
|
|
|
|
func testGetBlock(t *testing.T, client *rpc.Client) {
|
|
ec := NewClient(client)
|
|
|
|
// Get current block number
|
|
blockNumber, err := ec.BlockNumber(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if blockNumber != 2 {
|
|
t.Fatalf("BlockNumber returned wrong number: %d", blockNumber)
|
|
}
|
|
// Get current block by number
|
|
block, err := ec.BlockByNumber(context.Background(), new(big.Int).SetUint64(blockNumber))
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if block.NumberU64() != blockNumber {
|
|
t.Fatalf("BlockByNumber returned wrong block: want %d got %d", blockNumber, block.NumberU64())
|
|
}
|
|
// Get current block by hash
|
|
blockH, err := ec.BlockByHash(context.Background(), block.Hash())
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if block.Hash() != blockH.Hash() {
|
|
t.Fatalf("BlockByHash returned wrong block: want %v got %v", block.Hash().Hex(), blockH.Hash().Hex())
|
|
}
|
|
// Get header by number
|
|
header, err := ec.HeaderByNumber(context.Background(), new(big.Int).SetUint64(blockNumber))
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if block.Header().Hash() != header.Hash() {
|
|
t.Fatalf("HeaderByNumber returned wrong header: want %v got %v", block.Header().Hash().Hex(), header.Hash().Hex())
|
|
}
|
|
// Get header by hash
|
|
headerH, err := ec.HeaderByHash(context.Background(), block.Hash())
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if block.Header().Hash() != headerH.Hash() {
|
|
t.Fatalf("HeaderByHash returned wrong header: want %v got %v", block.Header().Hash().Hex(), headerH.Hash().Hex())
|
|
}
|
|
}
|
|
|
|
func testStatusFunctions(t *testing.T, client *rpc.Client) {
|
|
ec := NewClient(client)
|
|
|
|
// Sync progress
|
|
progress, err := ec.SyncProgress(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if progress != nil {
|
|
t.Fatalf("unexpected progress: %v", progress)
|
|
}
|
|
|
|
// NetworkID
|
|
networkID, err := ec.NetworkID(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if networkID.Cmp(big.NewInt(0)) != 0 {
|
|
t.Fatalf("unexpected networkID: %v", networkID)
|
|
}
|
|
|
|
// SuggestGasPrice
|
|
gasPrice, err := ec.SuggestGasPrice(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if gasPrice.Cmp(big.NewInt(1000000000)) != 0 {
|
|
t.Fatalf("unexpected gas price: %v", gasPrice)
|
|
}
|
|
|
|
// SuggestGasTipCap
|
|
gasTipCap, err := ec.SuggestGasTipCap(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if gasTipCap.Cmp(big.NewInt(234375000)) != 0 {
|
|
t.Fatalf("unexpected gas tip cap: %v", gasTipCap)
|
|
}
|
|
}
|
|
|
|
func testCallContract(t *testing.T, client *rpc.Client) {
|
|
ec := NewClient(client)
|
|
|
|
// EstimateGas
|
|
msg := ethereum.CallMsg{
|
|
From: testAddr,
|
|
To: &common.Address{},
|
|
Gas: 21000,
|
|
Value: big.NewInt(1),
|
|
}
|
|
gas, err := ec.EstimateGas(context.Background(), msg)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if gas != 21000 {
|
|
t.Fatalf("unexpected gas price: %v", gas)
|
|
}
|
|
// CallContract
|
|
if _, err := ec.CallContract(context.Background(), msg, big.NewInt(1)); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
// PendingCallCOntract
|
|
if _, err := ec.PendingCallContract(context.Background(), msg); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func testEstimateGas(t *testing.T, client *rpc.Client) {
|
|
ec := NewClient(client)
|
|
|
|
// EstimateGas
|
|
msg := ethereum.CallMsg{
|
|
From: emptyAddr,
|
|
To: &common.Address{},
|
|
GasPrice: big.NewInt(1000000000),
|
|
}
|
|
_, err := ec.EstimateGas(context.Background(), msg)
|
|
|
|
if err == nil || err.Error() != "insufficient funds for l1 fee" {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func testAtFunctions(t *testing.T, client *rpc.Client) {
|
|
ec := NewClient(client)
|
|
|
|
// send a transaction for some interesting pending status
|
|
sendTransaction(ec)
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
// Check pending transaction count
|
|
pending, err := ec.PendingTransactionCount(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if pending != 1 {
|
|
t.Fatalf("unexpected pending, wanted 1 got: %v", pending)
|
|
}
|
|
// Query balance
|
|
balance, err := ec.BalanceAt(context.Background(), testAddr, nil)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
penBalance, err := ec.PendingBalanceAt(context.Background(), testAddr)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if balance.Cmp(penBalance) == 0 {
|
|
t.Fatalf("unexpected balance: %v %v", balance, penBalance)
|
|
}
|
|
// NonceAt
|
|
nonce, err := ec.NonceAt(context.Background(), testAddr, nil)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
penNonce, err := ec.PendingNonceAt(context.Background(), testAddr)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if penNonce != nonce+1 {
|
|
t.Fatalf("unexpected nonce: %v %v", nonce, penNonce)
|
|
}
|
|
// StorageAt
|
|
storage, err := ec.StorageAt(context.Background(), testAddr, common.Hash{}, nil)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
penStorage, err := ec.PendingStorageAt(context.Background(), testAddr, common.Hash{})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if !bytes.Equal(storage, penStorage) {
|
|
t.Fatalf("unexpected storage: %v %v", storage, penStorage)
|
|
}
|
|
// CodeAt
|
|
code, err := ec.CodeAt(context.Background(), testAddr, nil)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
penCode, err := ec.PendingCodeAt(context.Background(), testAddr)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if !bytes.Equal(code, penCode) {
|
|
t.Fatalf("unexpected code: %v %v", code, penCode)
|
|
}
|
|
}
|
|
|
|
func testTransactionSender(t *testing.T, client *rpc.Client) {
|
|
ec := NewClient(client)
|
|
ctx := context.Background()
|
|
|
|
// Retrieve testTx1 via RPC.
|
|
block2, err := ec.HeaderByNumber(ctx, big.NewInt(2))
|
|
if err != nil {
|
|
t.Fatal("can't get block 1:", err)
|
|
}
|
|
tx1, err := ec.TransactionInBlock(ctx, block2.Hash(), 0)
|
|
if err != nil {
|
|
t.Fatal("can't get tx:", err)
|
|
}
|
|
if tx1.Hash() != testTx1.Hash() {
|
|
t.Fatalf("wrong tx hash %v, want %v", tx1.Hash(), testTx1.Hash())
|
|
}
|
|
|
|
// The sender address is cached in tx1, so no additional RPC should be required in
|
|
// TransactionSender. Ensure the server is not asked by canceling the context here.
|
|
canceledCtx, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
sender1, err := ec.TransactionSender(canceledCtx, tx1, block2.Hash(), 0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if sender1 != testAddr {
|
|
t.Fatal("wrong sender:", sender1)
|
|
}
|
|
|
|
// Now try to get the sender of testTx2, which was not fetched through RPC.
|
|
// TransactionSender should query the server here.
|
|
sender2, err := ec.TransactionSender(ctx, testTx2, block2.Hash(), 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if sender2 != testAddr {
|
|
t.Fatal("wrong sender:", sender2)
|
|
}
|
|
}
|
|
|
|
func sendTransaction(ec *Client) error {
|
|
chainID, err := ec.ChainID(context.Background())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
nonce, err := ec.PendingNonceAt(context.Background(), testAddr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
signer := types.LatestSignerForChainID(chainID)
|
|
tx, err := types.SignNewTx(testKey, signer, &types.LegacyTx{
|
|
Nonce: nonce,
|
|
To: &common.Address{2},
|
|
Value: big.NewInt(1),
|
|
Gas: 22000,
|
|
GasPrice: big.NewInt(params.InitialBaseFee),
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return ec.SendTransaction(context.Background(), tx)
|
|
}
|