From 9e9c7f3811ebd42acd476d89246051ceb724323e Mon Sep 17 00:00:00 2001 From: David Date: Tue, 31 Oct 2023 03:22:48 +0800 Subject: [PATCH] test(all): add more tests (#439) --- bindings/encoding/input_test.go | 54 ++++++ .../beaconsync/progress_tracker.go | 29 +-- .../beaconsync/progress_tracker_test.go | 8 +- driver/chain_syncer/beaconsync/syncer.go | 4 +- driver/chain_syncer/chain_syncer.go | 16 +- driver/driver_test.go | 4 +- driver/state/l1_current.go | 80 ++------ driver/state/l1_current_test.go | 14 +- driver/state/state.go | 11 -- driver/state/state_test.go | 6 - pkg/rpc/ethclient_test.go | 179 ++++++++++++++++++ pkg/rpc/utils.go | 8 +- proposer/proposer_test.go | 17 ++ .../prover_selector/eth_fee_eoa_selector.go | 8 +- .../eth_fee_eoa_selector_test.go | 17 ++ prover/proof_producer/dummty_producer_test.go | 53 ++++++ .../proof_producer/guardian_producer_test.go | 51 +++++ .../optimistic_producer_test.go | 10 +- prover/proof_producer/sgx_producer_test.go | 51 +++++ .../zkevm_rpcd_producer_test.go | 29 +++ .../proof_submitter/proof_contester_test.go | 22 +++ .../proof_submitter/proof_submitter_test.go | 31 ++- .../transaction/builder_test.go | 18 ++ .../transaction/sender_test.go | 9 +- prover/prover.go | 3 +- prover/prover_test.go | 5 + 26 files changed, 576 insertions(+), 161 deletions(-) create mode 100644 pkg/rpc/ethclient_test.go create mode 100644 prover/proof_producer/dummty_producer_test.go create mode 100644 prover/proof_producer/guardian_producer_test.go create mode 100644 prover/proof_producer/sgx_producer_test.go create mode 100644 prover/proof_submitter/proof_contester_test.go diff --git a/bindings/encoding/input_test.go b/bindings/encoding/input_test.go index f58d05fef..89939267f 100644 --- a/bindings/encoding/input_test.go +++ b/bindings/encoding/input_test.go @@ -1,13 +1,19 @@ package encoding import ( + "context" "math/big" "math/rand" + "os" "testing" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" "github.com/stretchr/testify/require" + "github.com/taikoxyz/taiko-client/bindings" ) func TestEncodeEvidence(t *testing.T) { @@ -42,6 +48,18 @@ func TestEncodeProverAssignment(t *testing.T) { require.NotNil(t, encoded) } +func TestEncodeProverAssignmentPayload(t *testing.T) { + encoded, err := EncodeProverAssignmentPayload( + common.BytesToHash(randomBytes(32)), + common.BytesToAddress(randomBytes(20)), + 120, + []TierFee{{Tier: 0, Fee: common.Big1}}, + ) + + require.Nil(t, err) + require.NotNil(t, encoded) +} + func TestUnpackTxListBytes(t *testing.T) { _, err := UnpackTxListBytes(randomBytes(1024)) require.NotNil(t, err) @@ -53,4 +71,40 @@ func TestUnpackTxListBytes(t *testing.T) { ), ) require.ErrorContains(t, err, "no method with id") + + cli, err := ethclient.Dial(os.Getenv("L1_NODE_WS_ENDPOINT")) + require.Nil(t, err) + + chainID, err := cli.ChainID(context.Background()) + require.Nil(t, err) + + taikoL1, err := bindings.NewTaikoL1Client( + common.HexToAddress(os.Getenv("TAIKO_L1_ADDRESS")), + cli, + ) + require.Nil(t, err) + + l1ProposerPrivKey, err := crypto.ToECDSA(common.Hex2Bytes(os.Getenv("L1_PROPOSER_PRIVATE_KEY"))) + require.Nil(t, err) + + opts, err := bind.NewKeyedTransactorWithChainID(l1ProposerPrivKey, chainID) + require.Nil(t, err) + + opts.NoSend = true + opts.GasLimit = randomHash().Big().Uint64() + + txListBytes := randomBytes(1024) + + tx, err := taikoL1.ProposeBlock( + opts, + randomHash(), + [32]byte(randomHash().Bytes()), + randomBytes(32), + txListBytes, + ) + require.Nil(t, err) + + b, err := UnpackTxListBytes(tx.Data()) + require.Nil(t, err) + require.Equal(t, txListBytes, b) } diff --git a/driver/chain_syncer/beaconsync/progress_tracker.go b/driver/chain_syncer/beaconsync/progress_tracker.go index 11dd9dad0..85dcf24c3 100644 --- a/driver/chain_syncer/beaconsync/progress_tracker.go +++ b/driver/chain_syncer/beaconsync/progress_tracker.go @@ -13,7 +13,7 @@ import ( ) var ( - syncProgressCheckInterval = 10 * time.Second + syncProgressCheckInterval = 12 * time.Second ) // SyncProgressTracker is responsible for tracking the L2 execution engine's sync progress, after @@ -24,10 +24,9 @@ type SyncProgressTracker struct { client *rpc.EthClient // Meta data - triggered bool - lastSyncedVerifiedBlockID *big.Int - lastSyncedVerifiedBlockHeight *big.Int - lastSyncedVerifiedBlockHash common.Hash + triggered bool + lastSyncedVerifiedBlockID *big.Int + lastSyncedVerifiedBlockHash common.Hash // Out-of-sync check related lastSyncProgress *ethereum.SyncProgress @@ -94,12 +93,11 @@ func (t *SyncProgressTracker) track(ctx context.Context) { return } - if new(big.Int).SetUint64(headHeight).Cmp(t.lastSyncedVerifiedBlockHeight) >= 0 { + if new(big.Int).SetUint64(headHeight).Cmp(t.lastSyncedVerifiedBlockID) >= 0 { t.lastProgressedTime = time.Now() log.Info("L2 execution engine has finished the P2P sync work, all verified blocks synced, "+ "will switch to insert pending blocks one by one", "lastSyncedVerifiedBlockID", t.lastSyncedVerifiedBlockID, - "lastSyncedVerifiedBlockHeight", t.lastSyncedVerifiedBlockHeight, "lastSyncedVerifiedBlockHash", t.lastSyncedVerifiedBlockHash, ) return @@ -134,11 +132,11 @@ func (t *SyncProgressTracker) track(ctx context.Context) { } // UpdateMeta updates the inner beacon sync meta data. -func (t *SyncProgressTracker) UpdateMeta(id, height *big.Int, blockHash common.Hash) { +func (t *SyncProgressTracker) UpdateMeta(id *big.Int, blockHash common.Hash) { t.mutex.Lock() defer t.mutex.Unlock() - log.Debug("Update sync progress tracker meta", "id", id, "height", height, "hash", blockHash) + log.Debug("Update sync progress tracker meta", "id", id, "hash", blockHash) if !t.triggered { t.lastProgressedTime = time.Now() @@ -146,7 +144,6 @@ func (t *SyncProgressTracker) UpdateMeta(id, height *big.Int, blockHash common.H t.triggered = true t.lastSyncedVerifiedBlockID = id - t.lastSyncedVerifiedBlockHeight = height t.lastSyncedVerifiedBlockHash = blockHash } @@ -203,18 +200,6 @@ func (t *SyncProgressTracker) LastSyncedVerifiedBlockID() *big.Int { return new(big.Int).Set(t.lastSyncedVerifiedBlockID) } -// LastSyncedVerifiedBlockHeight returns tracker.lastSyncedVerifiedBlockHeight. -func (t *SyncProgressTracker) LastSyncedVerifiedBlockHeight() *big.Int { - t.mutex.RLock() - defer t.mutex.RUnlock() - - if t.lastSyncedVerifiedBlockHeight == nil { - return nil - } - - return new(big.Int).Set(t.lastSyncedVerifiedBlockHeight) -} - // LastSyncedVerifiedBlockHash returns tracker.lastSyncedVerifiedBlockHash. func (t *SyncProgressTracker) LastSyncedVerifiedBlockHash() common.Hash { t.mutex.RLock() diff --git a/driver/chain_syncer/beaconsync/progress_tracker_test.go b/driver/chain_syncer/beaconsync/progress_tracker_test.go index f4fcfd0ba..470ef26d7 100644 --- a/driver/chain_syncer/beaconsync/progress_tracker_test.go +++ b/driver/chain_syncer/beaconsync/progress_tracker_test.go @@ -58,7 +58,7 @@ func (s *BeaconSyncProgressTrackerTestSuite) TestTrack() { // Triggered ctx, cancel = context.WithCancel(context.Background()) - s.t.UpdateMeta(common.Big256, common.Big256, testutils.RandomHash()) + s.t.UpdateMeta(common.Big256, testutils.RandomHash()) go s.t.Track(ctx) time.Sleep(syncProgressCheckInterval + 5*time.Second) cancel() @@ -90,12 +90,6 @@ func (s *BeaconSyncProgressTrackerTestSuite) TestLastSyncedVerifiedBlockID() { s.Equal(common.Big1.Uint64(), s.t.LastSyncedVerifiedBlockID().Uint64()) } -func (s *BeaconSyncProgressTrackerTestSuite) TestLastSyncedVerifiedBlockHeight() { - s.Nil(s.t.LastSyncedVerifiedBlockHeight()) - s.t.lastSyncedVerifiedBlockHeight = common.Big1 - s.Equal(common.Big1.Uint64(), s.t.LastSyncedVerifiedBlockHeight().Uint64()) -} - func (s *BeaconSyncProgressTrackerTestSuite) TestLastSyncedVerifiedBlockHash() { s.Equal( common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), diff --git a/driver/chain_syncer/beaconsync/syncer.go b/driver/chain_syncer/beaconsync/syncer.go index cba804857..3c70ae00a 100644 --- a/driver/chain_syncer/beaconsync/syncer.go +++ b/driver/chain_syncer/beaconsync/syncer.go @@ -47,7 +47,7 @@ func (s *Syncer) TriggerBeaconSync() error { if s.progressTracker.lastSyncProgress == nil { log.Info( "Syncing beacon headers, please check L2 execution engine logs for progress", - "currentSyncHead", s.progressTracker.LastSyncedVerifiedBlockHeight(), + "currentSyncHead", s.progressTracker.LastSyncedVerifiedBlockID(), "newBlockID", blockID, ) } @@ -80,14 +80,12 @@ func (s *Syncer) TriggerBeaconSync() error { // Update sync status. s.progressTracker.UpdateMeta( blockID, - new(big.Int).SetUint64(latestVerifiedHeadPayload.Number), latestVerifiedHeadPayload.BlockHash, ) log.Info( "⛓️ Beacon sync triggered", "newHeadID", blockID, - "newHeadHeight", s.progressTracker.LastSyncedVerifiedBlockHeight(), "newHeadHash", s.progressTracker.LastSyncedVerifiedBlockHash(), ) diff --git a/driver/chain_syncer/chain_syncer.go b/driver/chain_syncer/chain_syncer.go index 6b0cded7f..6fedf1e6b 100644 --- a/driver/chain_syncer/chain_syncer.go +++ b/driver/chain_syncer/chain_syncer.go @@ -98,21 +98,13 @@ func (s *L2ChainSyncer) Sync(l1End *types.Header) error { "lastSyncedVerifiedBlockHash", s.progressTracker.LastSyncedVerifiedBlockHash(), ) - heightOrID := &state.HeightOrID{Height: l2Head.Number} - - // If the L2 execution engine has synced to latest verified block. - if l2Head.Hash() == s.progressTracker.LastSyncedVerifiedBlockHash() { - heightOrID.ID = s.progressTracker.LastSyncedVerifiedBlockID() - } - // Reset the L1Current cursor. - _, blockID, err := s.state.ResetL1Current(s.ctx, heightOrID) - if err != nil { + if _, err := s.state.ResetL1Current(s.ctx, l2Head.Number); err != nil { return err } // Reset to the latest L2 execution engine's chain status. - s.progressTracker.UpdateMeta(blockID, heightOrID.Height, l2Head.Hash()) + s.progressTracker.UpdateMeta(l2Head.Number, l2Head.Hash()) } // Insert the proposed block one by one. @@ -139,8 +131,8 @@ func (s *L2ChainSyncer) AheadOfProtocolVerifiedHead() bool { return false } - if s.progressTracker.LastSyncedVerifiedBlockHeight() != nil { - return s.state.GetL2Head().Number.Uint64() >= s.progressTracker.LastSyncedVerifiedBlockHeight().Uint64() + if s.progressTracker.LastSyncedVerifiedBlockID() != nil { + return s.state.GetL2Head().Number.Uint64() >= s.progressTracker.LastSyncedVerifiedBlockID().Uint64() } return true diff --git a/driver/driver_test.go b/driver/driver_test.go index e39137e42..4ae933e72 100644 --- a/driver/driver_test.go +++ b/driver/driver_test.go @@ -11,7 +11,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/suite" "github.com/taikoxyz/taiko-client/bindings/encoding" - "github.com/taikoxyz/taiko-client/driver/state" "github.com/taikoxyz/taiko-client/pkg/jwt" "github.com/taikoxyz/taiko-client/proposer" "github.com/taikoxyz/taiko-client/testutils" @@ -308,8 +307,7 @@ func (s *DriverTestSuite) TestL1Current() { // propose and insert a block testutils.ProposeAndInsertEmptyBlocks(&s.ClientTestSuite, s.p, s.d.ChainSyncer().CalldataSyncer()) // reset L1 current with increased height - _, id, err := s.d.state.ResetL1Current(s.d.ctx, &state.HeightOrID{ID: common.Big1}) - s.Equal(common.Big1, id) + _, err := s.d.state.ResetL1Current(s.d.ctx, common.Big1) s.Nil(err) } diff --git a/driver/state/l1_current.go b/driver/state/l1_current.go index e40587d84..bbda4e8cc 100644 --- a/driver/state/l1_current.go +++ b/driver/state/l1_current.go @@ -31,72 +31,21 @@ func (s *State) SetL1Current(h *types.Header) { // BlockProposed event with given blockID / blockHash. func (s *State) ResetL1Current( ctx context.Context, - heightOrID *HeightOrID, -) (*bindings.TaikoL1ClientBlockProposed, *big.Int, error) { - if !heightOrID.NotEmpty() { - return nil, nil, fmt.Errorf("empty input %v", heightOrID) + blockID *big.Int, +) (*bindings.TaikoL1ClientBlockProposed, error) { + if blockID == nil { + return nil, fmt.Errorf("empty block ID") } - log.Info("Reset L1 current cursor", "heightOrID", heightOrID) + log.Info("Reset L1 current cursor", "blockID", blockID) - var ( - err error - ) - - if (heightOrID.ID != nil && heightOrID.ID.Cmp(common.Big0) == 0) || - (heightOrID.Height != nil && heightOrID.Height.Cmp(common.Big0) == 0) { + if blockID.Cmp(common.Big0) == 0 { l1Current, err := s.rpc.L1.HeaderByNumber(ctx, s.GenesisL1Height) if err != nil { - return nil, nil, err + return nil, err } s.SetL1Current(l1Current) - return nil, common.Big0, nil - } - - // Need to find the block ID at first, before filtering the BlockProposed events. - if heightOrID.ID == nil { - header, err := s.rpc.L2.HeaderByNumber(context.Background(), heightOrID.Height) - if err != nil { - return nil, nil, err - } - targetHash := header.Hash() - - iter, err := eventIterator.NewTransitionProvedIterator( - ctx, - &eventIterator.TransitionProvenIteratorConfig{ - Client: s.rpc.L1, - TaikoL1: s.rpc.TaikoL1, - StartHeight: s.GenesisL1Height, - EndHeight: s.GetL1Head().Number, - FilterQuery: []*big.Int{}, - Reverse: true, - OnTransitionProved: func( - ctx context.Context, - e *bindings.TaikoL1ClientTransitionProved, - end eventIterator.EndTransitionProvedEventIterFunc, - ) error { - log.Debug("Filtered TransitionProved event", "ID", e.BlockId, "hash", common.Hash(e.BlockHash)) - if e.BlockHash == targetHash { - heightOrID.ID = e.BlockId - end() - } - - return nil - }, - }, - ) - - if err != nil { - return nil, nil, err - } - - if err := iter.Iter(); err != nil { - return nil, nil, err - } - - if heightOrID.ID == nil { - return nil, nil, fmt.Errorf("TransitionProved event not found, hash: %s", targetHash) - } + return nil, nil } var event *bindings.TaikoL1ClientBlockProposed @@ -107,7 +56,7 @@ func (s *State) ResetL1Current( TaikoL1: s.rpc.TaikoL1, StartHeight: s.GenesisL1Height, EndHeight: s.GetL1Head().Number, - FilterQuery: []*big.Int{heightOrID.ID}, + FilterQuery: []*big.Int{blockID}, Reverse: true, OnBlockProposedEvent: func( ctx context.Context, @@ -120,26 +69,25 @@ func (s *State) ResetL1Current( }, }, ) - if err != nil { - return nil, nil, err + return nil, err } if err := iter.Iter(); err != nil { - return nil, nil, err + return nil, err } if event == nil { - return nil, nil, fmt.Errorf("BlockProposed event not found, blockID: %s", heightOrID.ID) + return nil, fmt.Errorf("BlockProposed event not found, blockID: %s", blockID) } l1Current, err := s.rpc.L1.HeaderByNumber(ctx, new(big.Int).SetUint64(event.Raw.BlockNumber)) if err != nil { - return nil, nil, err + return nil, err } s.SetL1Current(l1Current) log.Info("Reset L1 current cursor", "height", s.GetL1Current().Number) - return event, heightOrID.ID, nil + return event, nil } diff --git a/driver/state/l1_current_test.go b/driver/state/l1_current_test.go index b55f52e4a..d7fcdda02 100644 --- a/driver/state/l1_current_test.go +++ b/driver/state/l1_current_test.go @@ -24,27 +24,21 @@ func (s *DriverStateTestSuite) TestSetL1Current() { } func (s *DriverStateTestSuite) TestResetL1CurrentEmptyHeight() { - _, l1Current, err := s.s.ResetL1Current(context.Background(), &HeightOrID{ID: common.Big0}) + _, err := s.s.ResetL1Current(context.Background(), common.Big0) s.Nil(err) - s.Zero(l1Current.Uint64()) - _, _, err = s.s.ResetL1Current(context.Background(), &HeightOrID{Height: common.Big0}) + _, err = s.s.ResetL1Current(context.Background(), common.Big0) s.Nil(err) } func (s *DriverStateTestSuite) TestResetL1CurrentEmptyID() { - _, _, err := s.s.ResetL1Current(context.Background(), &HeightOrID{Height: common.Big1}) + _, err := s.s.ResetL1Current(context.Background(), common.Big1) s.ErrorContains(err, "not found") } -func (s *DriverStateTestSuite) TestResetL1CurrentEmptyHeightAndID() { - _, _, err := s.s.ResetL1Current(context.Background(), &HeightOrID{}) - s.ErrorContains(err, "empty input") -} - func (s *DriverStateTestSuite) TestResetL1CurrentCtxErr() { ctx, cancel := context.WithCancel(context.Background()) cancel() - _, _, err := s.s.ResetL1Current(ctx, &HeightOrID{Height: common.Big0}) + _, err := s.s.ResetL1Current(ctx, common.Big0) s.ErrorContains(err, "context canceled") } diff --git a/driver/state/state.go b/driver/state/state.go index c6aec80eb..d563f6e1f 100644 --- a/driver/state/state.go +++ b/driver/state/state.go @@ -17,17 +17,6 @@ import ( "github.com/taikoxyz/taiko-client/pkg/rpc" ) -// HeightOrID contains a block height or a block ID. -type HeightOrID struct { - Height *big.Int - ID *big.Int -} - -// NotEmpty checks whether this is an empty struct. -func (h *HeightOrID) NotEmpty() bool { - return h.Height != nil || h.ID != nil -} - // State contains all states which will be used by driver. type State struct { // Subscriptions, will automatically resubscribe on errors diff --git a/driver/state/state_test.go b/driver/state/state_test.go index de7987e98..a9d969c67 100644 --- a/driver/state/state_test.go +++ b/driver/state/state_test.go @@ -45,12 +45,6 @@ func (s *DriverStateTestSuite) TestGetHeadBlockID() { s.Equal(uint64(0), s.s.GetHeadBlockID().Uint64()) } -func (s *DriverStateTestSuite) TestHeightOrIDNotEmpty() { - s.False((&HeightOrID{}).NotEmpty()) - s.True((&HeightOrID{Height: common.Big0}).NotEmpty()) - s.True((&HeightOrID{ID: common.Big0}).NotEmpty()) -} - func (s *DriverStateTestSuite) TestClose() { s.NotPanics(s.s.Close) } diff --git a/pkg/rpc/ethclient_test.go b/pkg/rpc/ethclient_test.go new file mode 100644 index 000000000..6d8697211 --- /dev/null +++ b/pkg/rpc/ethclient_test.go @@ -0,0 +1,179 @@ +package rpc + +import ( + "context" + "testing" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func TestBlockByHash(t *testing.T) { + client := newTestClientWithTimeout(t) + + head, err := client.L1.HeaderByNumber(context.Background(), nil) + require.Nil(t, err) + + block, err := client.L1.BlockByHash(context.Background(), head.Hash()) + + require.Nil(t, err) + require.Equal(t, head.Hash(), block.Hash()) +} + +func TestBlockNumber(t *testing.T) { + client := newTestClientWithTimeout(t) + + head, err := client.L1.BlockNumber(context.Background()) + require.Nil(t, err) + require.Greater(t, head, uint64(0)) +} + +func TestPeerCount(t *testing.T) { + client := newTestClientWithTimeout(t) + + _, err := client.L1.PeerCount(context.Background()) + require.NotNil(t, err) +} + +func TestTransactionByHash(t *testing.T) { + client := newTestClientWithTimeout(t) + + _, _, err := client.L1.TransactionByHash(context.Background(), common.Hash{}) + require.NotNil(t, err) +} + +func TestTransactionSender(t *testing.T) { + client := newTestClientWithTimeout(t) + + block, err := client.L1.BlockByNumber(context.Background(), nil) + require.Nil(t, err) + require.NotZero(t, block.Transactions().Len()) + + sender, err := client.L1.TransactionSender(context.Background(), block.Transactions()[0], block.Hash(), 0) + require.Nil(t, err) + require.NotEqual(t, common.Address{}, sender) +} + +func TestTransactionCount(t *testing.T) { + client := newTestClientWithTimeout(t) + + block, err := client.L1.BlockByNumber(context.Background(), nil) + require.Nil(t, err) + require.NotZero(t, block.Transactions().Len()) + + c, err := client.L1.TransactionCount(context.Background(), block.Hash()) + require.Nil(t, err) + require.NotZero(t, c) +} + +func TestTransactionInBlock(t *testing.T) { + client := newTestClientWithTimeout(t) + + block, err := client.L1.BlockByNumber(context.Background(), nil) + require.Nil(t, err) + require.NotZero(t, block.Transactions().Len()) + + tx, err := client.L1.TransactionInBlock(context.Background(), block.Hash(), 0) + require.Nil(t, err) + require.NotEqual(t, common.Hash{}, tx.Hash()) +} + +func TestNetworkID(t *testing.T) { + client := newTestClientWithTimeout(t) + + networkID, err := client.L1.NetworkID(context.Background()) + require.Nil(t, err) + require.NotEqual(t, common.Big0.Uint64(), networkID.Uint64()) +} + +func TestStorageAt(t *testing.T) { + client := newTestClientWithTimeout(t) + + _, err := client.L1.StorageAt(context.Background(), common.Address{}, common.Hash{}, nil) + require.Nil(t, err) +} + +func TestCodeAt(t *testing.T) { + client := newTestClientWithTimeout(t) + + _, err := client.L1.CodeAt(context.Background(), common.Address{}, nil) + require.Nil(t, err) +} + +func TestNonceAt(t *testing.T) { + client := newTestClientWithTimeout(t) + + _, err := client.L1.NonceAt(context.Background(), common.Address{}, nil) + require.Nil(t, err) +} + +func TestPendingBalanceAt(t *testing.T) { + client := newTestClientWithTimeout(t) + + _, err := client.L1.PendingBalanceAt(context.Background(), common.Address{}) + require.Nil(t, err) +} + +func TestPendingStorageAt(t *testing.T) { + client := newTestClientWithTimeout(t) + + _, err := client.L1.PendingStorageAt(context.Background(), common.Address{}, common.Hash{}) + require.Nil(t, err) +} + +func TestPendingCodeAt(t *testing.T) { + client := newTestClientWithTimeout(t) + + _, err := client.L1.PendingCodeAt(context.Background(), common.Address{}) + require.Nil(t, err) +} + +func TestPendingTransactionCount(t *testing.T) { + client := newTestClientWithTimeout(t) + + _, err := client.L1.PendingTransactionCount(context.Background()) + require.Nil(t, err) +} + +func TestCallContractAtHash(t *testing.T) { + client := newTestClientWithTimeout(t) + + _, err := client.L1.CallContractAtHash(context.Background(), ethereum.CallMsg{}, common.Hash{}) + require.NotNil(t, err) +} + +func TestPendingCallContract(t *testing.T) { + client := newTestClientWithTimeout(t) + + _, err := client.L1.PendingCallContract(context.Background(), ethereum.CallMsg{}) + require.Nil(t, err) +} + +func TestSuggestGasPrice(t *testing.T) { + client := newTestClientWithTimeout(t) + + _, err := client.L1.SuggestGasPrice(context.Background()) + require.Nil(t, err) +} + +func TestSuggestGasTipCap(t *testing.T) { + client := newTestClientWithTimeout(t) + + _, err := client.L1.SuggestGasTipCap(context.Background()) + require.Nil(t, err) +} + +func TestFeeHistory(t *testing.T) { + client := newTestClientWithTimeout(t) + + _, err := client.L1.FeeHistory(context.Background(), 1, nil, []float64{}) + require.Nil(t, err) +} + +func TestEstimateGas(t *testing.T) { + client := newTestClientWithTimeout(t) + + _, err := client.L1.EstimateGas(context.Background(), ethereum.CallMsg{}) + require.Nil(t, err) +} diff --git a/pkg/rpc/utils.go b/pkg/rpc/utils.go index dacef4ba6..6a8a0ba6d 100644 --- a/pkg/rpc/utils.go +++ b/pkg/rpc/utils.go @@ -45,7 +45,7 @@ func CheckProverBalance( rpc *Client, prover common.Address, taikoL1Address common.Address, - livenessBond *big.Int, + bond *big.Int, ) (bool, error) { ctxWithTimeout, cancel := ctxWithTimeoutOrDefault(ctx, defaultTimeout) defer cancel() @@ -57,7 +57,7 @@ func CheckProverBalance( log.Info("Prover's deposited taikoTokenBalance", "balance", depositedBalance.String(), "address", prover.Hex()) - if livenessBond.Cmp(depositedBalance) > 0 { + if bond.Cmp(depositedBalance) > 0 { // Check allowance on taiko token contract allowance, err := rpc.TaikoToken.Allowance(&bind.CallOpts{Context: ctxWithTimeout}, prover, taikoL1Address) if err != nil { @@ -74,14 +74,14 @@ func CheckProverBalance( log.Info("Prover's wallet taiko token balance", "balance", balance.String(), "address", prover.Hex()) - if livenessBond.Cmp(allowance) > 0 || livenessBond.Cmp(balance) > 0 { + if bond.Cmp(allowance) > 0 || bond.Cmp(balance) > 0 { log.Info( "Assigned prover does not have required on-chain token balance or allowance", "providedProver", prover.Hex(), "depositedBalance", depositedBalance.String(), "taikoTokenBalance", balance, "allowance", allowance.String(), - "proofBond", livenessBond, + "bond", bond, ) return false, nil } diff --git a/proposer/proposer_test.go b/proposer/proposer_test.go index 5da2737a6..a38a34fb7 100644 --- a/proposer/proposer_test.go +++ b/proposer/proposer_test.go @@ -113,6 +113,23 @@ func (s *ProposerTestSuite) TestProposeOp() { s.Equal(types.ReceiptStatusSuccessful, receipt.Status) } +func (s *ProposerTestSuite) TestProposeOpLocalsOnly() { + s.p.locals = []common.Address{common.BytesToAddress(testutils.RandomBytes(20))} + s.p.localsOnly = true + + // Propose txs in L2 execution engine's mempool + sink := make(chan *bindings.TaikoL1ClientBlockProposed) + + sub, err := s.p.rpc.TaikoL1.WatchBlockProposed(nil, sink, nil, nil) + s.Nil(err) + defer func() { + sub.Unsubscribe() + close(sink) + }() + + s.Error(errNoNewTxs, s.p.ProposeOp(context.Background())) +} + func (s *ProposerTestSuite) TestProposeEmptyBlockOp() { s.Nil(s.p.ProposeEmptyBlockOp(context.Background())) } diff --git a/proposer/prover_selector/eth_fee_eoa_selector.go b/proposer/prover_selector/eth_fee_eoa_selector.go index 4106e087d..aa42982f8 100644 --- a/proposer/prover_selector/eth_fee_eoa_selector.go +++ b/proposer/prover_selector/eth_fee_eoa_selector.go @@ -133,7 +133,13 @@ func (s *ETHFeeEOASelector) AssignProver( continue } - ok, err := rpc.CheckProverBalance(ctx, s.rpc, proverAddress, s.taikoL1Address, s.protocolConfigs.LivenessBond) + ok, err := rpc.CheckProverBalance( + ctx, + s.rpc, + proverAddress, + s.taikoL1Address, + s.protocolConfigs.LivenessBond, + ) if err != nil { log.Warn("Failed to check prover balance", "endpoint", endpoint, "error", err) continue diff --git a/proposer/prover_selector/eth_fee_eoa_selector_test.go b/proposer/prover_selector/eth_fee_eoa_selector_test.go index df3d415c5..34f5059f7 100644 --- a/proposer/prover_selector/eth_fee_eoa_selector_test.go +++ b/proposer/prover_selector/eth_fee_eoa_selector_test.go @@ -1,6 +1,7 @@ package selector import ( + "context" "net/url" "os" "testing" @@ -43,6 +44,22 @@ func (s *ProverSelectorTestSuite) SetupTest() { s.Nil(err) } +func (s *ProverSelectorTestSuite) TestProverEndpoints() { + s.Equal(1, len(s.s.ProverEndpoints())) +} + +func (s *ProverSelectorTestSuite) TestProverAssignProver() { + sig, fee, err := s.s.AssignProver(context.Background(), []encoding.TierFee{ + {Tier: encoding.TierOptimisticID, Fee: common.Big256}, + {Tier: encoding.TierSgxID, Fee: common.Big256}, + {Tier: encoding.TierPseZkevmID, Fee: common.Big256}, + {Tier: encoding.TierSgxAndPseZkevmID, Fee: common.Big256}, + }, testutils.RandomHash()) + s.NotEmpty(sig) + s.True(fee.Cmp(common.Big0) > 0) + s.Nil(err) +} + func TestProverSelectorTestSuite(t *testing.T) { suite.Run(t, new(ProverSelectorTestSuite)) } diff --git a/prover/proof_producer/dummty_producer_test.go b/prover/proof_producer/dummty_producer_test.go new file mode 100644 index 000000000..10069f89a --- /dev/null +++ b/prover/proof_producer/dummty_producer_test.go @@ -0,0 +1,53 @@ +package producer + +import ( + "context" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/require" + "github.com/taikoxyz/taiko-client/bindings" +) + +func TestDummyProducerRequestProof(t *testing.T) { + producer := &DummyProofProducer{} + + resCh := make(chan *ProofWithHeader, 1) + + var tier uint16 = 1024 + + blockID := common.Big32 + header := &types.Header{ + ParentHash: randHash(), + UncleHash: randHash(), + Coinbase: common.BytesToAddress(randHash().Bytes()), + Root: randHash(), + TxHash: randHash(), + ReceiptHash: randHash(), + Difficulty: common.Big0, + Number: common.Big256, + GasLimit: 1024, + GasUsed: 1024, + Time: uint64(time.Now().Unix()), + Extra: randHash().Bytes(), + MixDigest: randHash(), + Nonce: types.BlockNonce{}, + } + require.Nil(t, producer.RequestProof( + context.Background(), + &ProofRequestOptions{}, + blockID, + &bindings.TaikoDataBlockMetadata{}, + header, + tier, + resCh, + )) + + res := <-resCh + require.Equal(t, res.BlockID, blockID) + require.Equal(t, res.Header, header) + require.Equal(t, tier, res.Tier) + require.NotEmpty(t, res.Proof) +} diff --git a/prover/proof_producer/guardian_producer_test.go b/prover/proof_producer/guardian_producer_test.go new file mode 100644 index 000000000..e7d541fcb --- /dev/null +++ b/prover/proof_producer/guardian_producer_test.go @@ -0,0 +1,51 @@ +package producer + +import ( + "context" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/require" + "github.com/taikoxyz/taiko-client/bindings" + "github.com/taikoxyz/taiko-client/bindings/encoding" +) + +func TestGuardianProducerRequestProof(t *testing.T) { + producer := &GuardianProofProducer{} + + resCh := make(chan *ProofWithHeader, 1) + + blockID := common.Big32 + header := &types.Header{ + ParentHash: randHash(), + UncleHash: randHash(), + Coinbase: common.BytesToAddress(randHash().Bytes()), + Root: randHash(), + TxHash: randHash(), + ReceiptHash: randHash(), + Difficulty: common.Big0, + Number: common.Big256, + GasLimit: 1024, + GasUsed: 1024, + Time: uint64(time.Now().Unix()), + Extra: randHash().Bytes(), + MixDigest: randHash(), + Nonce: types.BlockNonce{}, + } + require.Nil(t, producer.RequestProof( + context.Background(), + &ProofRequestOptions{}, + blockID, + &bindings.TaikoDataBlockMetadata{}, + header, + resCh, + )) + + res := <-resCh + require.Equal(t, res.BlockID, blockID) + require.Equal(t, res.Header, header) + require.Equal(t, res.Tier, encoding.TierGuardianID) + require.NotEmpty(t, res.Proof) +} diff --git a/prover/proof_producer/optimistic_producer_test.go b/prover/proof_producer/optimistic_producer_test.go index 8a4663c90..024358fd0 100644 --- a/prover/proof_producer/optimistic_producer_test.go +++ b/prover/proof_producer/optimistic_producer_test.go @@ -11,10 +11,11 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/stretchr/testify/require" "github.com/taikoxyz/taiko-client/bindings" + "github.com/taikoxyz/taiko-client/bindings/encoding" ) -func TestRequestProof(t *testing.T) { - optimisticProofProducer := &OptimisticProofProducer{} +func TestOptimisticRequestProof(t *testing.T) { + producer := &OptimisticProofProducer{} resCh := make(chan *ProofWithHeader, 1) @@ -22,7 +23,7 @@ func TestRequestProof(t *testing.T) { header := &types.Header{ ParentHash: randHash(), UncleHash: randHash(), - Coinbase: common.HexToAddress("0x0000777735367b36bC9B61C50022d9D0700dB4Ec"), + Coinbase: common.BytesToAddress(randHash().Bytes()), Root: randHash(), TxHash: randHash(), ReceiptHash: randHash(), @@ -35,7 +36,7 @@ func TestRequestProof(t *testing.T) { MixDigest: randHash(), Nonce: types.BlockNonce{}, } - require.Nil(t, optimisticProofProducer.RequestProof( + require.Nil(t, producer.RequestProof( context.Background(), &ProofRequestOptions{}, blockID, @@ -47,6 +48,7 @@ func TestRequestProof(t *testing.T) { res := <-resCh require.Equal(t, res.BlockID, blockID) require.Equal(t, res.Header, header) + require.Equal(t, res.Tier, encoding.TierOptimisticID) require.NotEmpty(t, res.Proof) } diff --git a/prover/proof_producer/sgx_producer_test.go b/prover/proof_producer/sgx_producer_test.go new file mode 100644 index 000000000..14adcf36e --- /dev/null +++ b/prover/proof_producer/sgx_producer_test.go @@ -0,0 +1,51 @@ +package producer + +import ( + "context" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/require" + "github.com/taikoxyz/taiko-client/bindings" + "github.com/taikoxyz/taiko-client/bindings/encoding" +) + +func TestSGXProducerRequestProof(t *testing.T) { + producer := &SGXProofProducer{} + + resCh := make(chan *ProofWithHeader, 1) + + blockID := common.Big32 + header := &types.Header{ + ParentHash: randHash(), + UncleHash: randHash(), + Coinbase: common.BytesToAddress(randHash().Bytes()), + Root: randHash(), + TxHash: randHash(), + ReceiptHash: randHash(), + Difficulty: common.Big0, + Number: common.Big256, + GasLimit: 1024, + GasUsed: 1024, + Time: uint64(time.Now().Unix()), + Extra: randHash().Bytes(), + MixDigest: randHash(), + Nonce: types.BlockNonce{}, + } + require.Nil(t, producer.RequestProof( + context.Background(), + &ProofRequestOptions{}, + blockID, + &bindings.TaikoDataBlockMetadata{}, + header, + resCh, + )) + + res := <-resCh + require.Equal(t, res.BlockID, blockID) + require.Equal(t, res.Header, header) + require.Equal(t, res.Tier, encoding.TierSgxID) + require.NotEmpty(t, res.Proof) +} diff --git a/prover/proof_producer/zkevm_rpcd_producer_test.go b/prover/proof_producer/zkevm_rpcd_producer_test.go index 87ae7af1d..fda9aa780 100644 --- a/prover/proof_producer/zkevm_rpcd_producer_test.go +++ b/prover/proof_producer/zkevm_rpcd_producer_test.go @@ -21,6 +21,7 @@ func TestNewZkevmRpcdProducer(t *testing.T) { &bindings.TaikoDataConfig{}, ) require.Nil(t, err) + require.False(t, dummyZkevmRpcdProducer.Cancellable()) dummyZkevmRpcdProducer.CustomProofHook = func() ([]byte, uint64, error) { return []byte{0}, CircuitsIdx, nil @@ -59,4 +60,32 @@ func TestNewZkevmRpcdProducer(t *testing.T) { require.Equal(t, res.BlockID, blockID) require.Equal(t, res.Header, header) require.NotEmpty(t, res.Proof) + + require.Nil(t, dummyZkevmRpcdProducer.Cancel(context.Background(), common.Big1)) +} + +func TestZkevmRpcdProducerCalls(t *testing.T) { + dummyZkevmRpcdProducer, err := NewZkevmRpcdProducer( + "", + "", + "", + "", + false, + &bindings.TaikoDataConfig{ + BlockMaxGasLimit: uint32(randHash().Big().Uint64()), + BlockMaxTxListBytes: randHash().Big(), + }, + ) + require.Nil(t, err) + require.False(t, dummyZkevmRpcdProducer.Cancellable()) + + dummyZkevmRpcdProducer.CustomProofHook = func() ([]byte, uint64, error) { + return []byte{0}, CircuitsIdx, nil + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + _, _, err = dummyZkevmRpcdProducer.callProverDaemon(ctx, &ProofRequestOptions{BlockID: common.Big32}) + + require.Nil(t, err) } diff --git a/prover/proof_submitter/proof_contester_test.go b/prover/proof_submitter/proof_contester_test.go new file mode 100644 index 000000000..d4a042e2b --- /dev/null +++ b/prover/proof_submitter/proof_contester_test.go @@ -0,0 +1,22 @@ +package submitter + +import ( + "context" + + "github.com/ethereum/go-ethereum/common" + "github.com/taikoxyz/taiko-client/bindings" + "github.com/taikoxyz/taiko-client/testutils" +) + +func (s *ProofSubmitterTestSuite) TestSubmitContestNoTransition() { + s.NotNil( + s.contester.SubmitContest( + context.Background(), + &bindings.TaikoL1ClientBlockProposed{}, + &bindings.TaikoL1ClientTransitionProved{ + BlockId: common.Big256, + ParentHash: testutils.RandomHash(), + }, + ), + ) +} diff --git a/prover/proof_submitter/proof_submitter_test.go b/prover/proof_submitter/proof_submitter_test.go index 37bd1374f..0a5f729b5 100644 --- a/prover/proof_submitter/proof_submitter_test.go +++ b/prover/proof_submitter/proof_submitter_test.go @@ -23,7 +23,8 @@ import ( type ProofSubmitterTestSuite struct { testutils.ClientTestSuite - proofSubmitter *ProofSubmitter + submitter *ProofSubmitter + contester *ProofContester calldataSyncer *calldata.Syncer proposer *proposer.Proposer proofCh chan *proofProducer.ProofWithHeader @@ -37,7 +38,7 @@ func (s *ProofSubmitterTestSuite) SetupTest() { s.proofCh = make(chan *proofProducer.ProofWithHeader, 1024) - s.proofSubmitter, err = New( + s.submitter, err = New( s.RpcClient, &proofProducer.OptimisticProofProducer{}, s.proofCh, @@ -52,6 +53,18 @@ func (s *ProofSubmitterTestSuite) SetupTest() { nil, ) s.Nil(err) + s.contester, err = NewProofContester( + s.RpcClient, + l1ProverPrivKey, + nil, + 2, + common.Big256, + 1, + 3*time.Second, + 36*time.Second, + "test", + ) + s.Nil(err) // Init calldata syncer testState, err := state.New(context.Background(), s.RpcClient) @@ -102,14 +115,14 @@ func (s *ProofSubmitterTestSuite) TestProofSubmitterRequestProofDeadlineExceeded defer cancel() s.ErrorContains( - s.proofSubmitter.RequestProof( + s.submitter.RequestProof( ctx, &bindings.TaikoL1ClientBlockProposed{BlockId: common.Big256}), "context deadline exceeded", ) } func (s *ProofSubmitterTestSuite) TestProofSubmitterSubmitProofMetadataNotFound() { s.Error( - s.proofSubmitter.SubmitProof( + s.submitter.SubmitProof( context.Background(), &proofProducer.ProofWithHeader{ BlockID: common.Big256, Meta: &bindings.TaikoDataBlockMetadata{}, @@ -125,9 +138,9 @@ func (s *ProofSubmitterTestSuite) TestSubmitProofs() { events := testutils.ProposeAndInsertEmptyBlocks(&s.ClientTestSuite, s.proposer, s.calldataSyncer) for _, e := range events { - s.Nil(s.proofSubmitter.RequestProof(context.Background(), e)) + s.Nil(s.submitter.RequestProof(context.Background(), e)) proofWithHeader := <-s.proofCh - s.Nil(s.proofSubmitter.SubmitProof(context.Background(), proofWithHeader)) + s.Nil(s.submitter.SubmitProof(context.Background(), proofWithHeader)) } } @@ -135,10 +148,10 @@ func (s *ProofSubmitterTestSuite) TestGuardianSubmitProofs() { events := testutils.ProposeAndInsertEmptyBlocks(&s.ClientTestSuite, s.proposer, s.calldataSyncer) for _, e := range events { - s.Nil(s.proofSubmitter.RequestProof(context.Background(), e)) + s.Nil(s.submitter.RequestProof(context.Background(), e)) proofWithHeader := <-s.proofCh proofWithHeader.Tier = encoding.TierGuardianID - s.Nil(s.proofSubmitter.SubmitProof(context.Background(), proofWithHeader)) + s.Nil(s.submitter.SubmitProof(context.Background(), proofWithHeader)) } } @@ -147,7 +160,7 @@ func (s *ProofSubmitterTestSuite) TestProofSubmitterRequestProofCancelled() { go func() { time.AfterFunc(2*time.Second, func() { cancel() }) }() s.ErrorContains( - s.proofSubmitter.RequestProof( + s.submitter.RequestProof( ctx, &bindings.TaikoL1ClientBlockProposed{BlockId: common.Big256}), "context canceled", ) } diff --git a/prover/proof_submitter/transaction/builder_test.go b/prover/proof_submitter/transaction/builder_test.go index ff35e43fa..1070648fb 100644 --- a/prover/proof_submitter/transaction/builder_test.go +++ b/prover/proof_submitter/transaction/builder_test.go @@ -2,6 +2,10 @@ package transaction import ( "context" + + "github.com/ethereum/go-ethereum/common" + "github.com/taikoxyz/taiko-client/bindings" + "github.com/taikoxyz/taiko-client/testutils" ) func (s *TransactionTestSuite) TestGetProveBlocksTxOpts() { @@ -13,3 +17,17 @@ func (s *TransactionTestSuite) TestGetProveBlocksTxOpts() { s.Nil(err) s.Greater(optsL2.GasTipCap.Uint64(), uint64(0)) } + +func (s *TransactionTestSuite) TestBuildTxs() { + _, err := s.builder.BuildForNormalProofSubmission( + context.Background(), common.Big256, testutils.RandomBytes(1024), + )(common.Big256) + s.NotNil(err) + + _, err = s.builder.BuildForGuardianProofSubmission( + context.Background(), + common.Big256, + &bindings.TaikoDataBlockEvidence{}, + )(common.Big256) + s.NotNil(err) +} diff --git a/prover/proof_submitter/transaction/sender_test.go b/prover/proof_submitter/transaction/sender_test.go index 80a07e2aa..f45c4548a 100644 --- a/prover/proof_submitter/transaction/sender_test.go +++ b/prover/proof_submitter/transaction/sender_test.go @@ -4,6 +4,7 @@ import ( "context" "errors" "math/big" + "os" "testing" "time" @@ -23,12 +24,18 @@ var ( type TransactionTestSuite struct { testutils.ClientTestSuite - sender *Sender + sender *Sender + builder *ProveBlockTxBuilder } func (s *TransactionTestSuite) SetupTest() { s.ClientTestSuite.SetupTest() + + l1ProverPrivKey, err := crypto.ToECDSA(common.Hex2Bytes(os.Getenv("L1_PROVER_PRIVATE_KEY"))) + s.Nil(err) + s.sender = NewSender(s.RpcClient, 5*time.Second, nil, 1*time.Minute) + s.builder = NewProveBlockTxBuilder(s.RpcClient, l1ProverPrivKey, nil, common.Big256, common.Big2) } func (s *TransactionTestSuite) TestIsSubmitProofTxErrorRetryable() { diff --git a/prover/prover.go b/prover/prover.go index dbde8dc6f..eb0feeda0 100644 --- a/prover/prover.go +++ b/prover/prover.go @@ -1057,8 +1057,7 @@ func (p *Prover) takeOneCapacity(blockID *big.Int) error { // releaseOneCapacity releases one capacity to the capacity manager. func (p *Prover) releaseOneCapacity(blockID *big.Int) { if !p.IsGuardianProver() { - _, released := p.capacityManager.ReleaseOneCapacity(blockID.Uint64()) - if !released { + if _, released := p.capacityManager.ReleaseOneCapacity(blockID.Uint64()); !released { log.Error("Failed to release capacity", "id", blockID) } } diff --git a/prover/prover_test.go b/prover/prover_test.go index a7ae360af..d6c144604 100644 --- a/prover/prover_test.go +++ b/prover/prover_test.go @@ -309,6 +309,7 @@ func (s *ProverTestSuite) TestGetSubmitterByTier() { submitter := s.p.getSubmitterByTier(encoding.TierGuardianID) s.NotNil(submitter) s.Equal(encoding.TierGuardianID, submitter.Tier()) + s.Nil(s.p.getSubmitterByTier(encoding.TierGuardianID + 1)) } func (s *ProverTestSuite) TestGetProvingWindowNotFound() { @@ -352,6 +353,10 @@ func (s *ProverTestSuite) TestProveOp() { s.Equal(header.ParentHash, common.BytesToHash(event.ParentHash[:])) } +func (s *ProverTestSuite) TestReleaseOneCapacity() { + s.NotPanics(func() { s.p.releaseOneCapacity(common.Big1) }) +} + func (s *ProverTestSuite) TestStartSubscription() { s.NotPanics(s.p.initSubscription) s.NotPanics(s.p.closeSubscription)