From fbd4d06ac41cfd13aba729d037f664947f984092 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 15 Mar 2024 17:42:21 +0800 Subject: [PATCH] feat(driver): improve driver implementation (#639) --- .../anchor_tx_constructor.go | 12 ++- .../beaconsync/progress_tracker.go | 12 ++- driver/chain_syncer/beaconsync/syncer.go | 15 ++-- driver/chain_syncer/calldata/syncer.go | 41 +++++----- driver/chain_syncer/chain_syncer.go | 28 +++++-- driver/driver.go | 27 +++---- driver/state/l1_current.go | 11 +-- driver/state/state.go | 74 +++++-------------- driver/state/state_test.go | 17 ----- driver/txlist_fetcher/blob.go | 5 ++ driver/txlist_fetcher/calldata.go | 2 + driver/txlist_fetcher/interface.go | 1 + pkg/rpc/methods.go | 38 ++++------ pkg/rpc/methods_test.go | 6 -- proposer/transaction_builder/common.go | 3 +- prover/event_handler/transition_contested.go | 2 +- prover/event_handler/transition_proved.go | 3 +- prover/proof_submitter/proof_submitter.go | 3 +- 18 files changed, 123 insertions(+), 177 deletions(-) diff --git a/driver/anchor_tx_constructor/anchor_tx_constructor.go b/driver/anchor_tx_constructor/anchor_tx_constructor.go index a85ac9b84..7787fe2d5 100644 --- a/driver/anchor_tx_constructor/anchor_tx_constructor.go +++ b/driver/anchor_tx_constructor/anchor_tx_constructor.go @@ -20,7 +20,7 @@ import ( const AnchorGasLimit = 250_000 // AnchorTxConstructor is responsible for assembling the anchor transaction (TaikoL2.anchor) in -// each L2 block, which is always the first transaction. +// each L2 block, which must be the first transaction, and its sender must be the golden touch account. type AnchorTxConstructor struct { rpc *rpc.Client goldenTouchAddress common.Address @@ -39,11 +39,7 @@ func New(rpc *rpc.Client) (*AnchorTxConstructor, error) { return nil, fmt.Errorf("invalid golden touch private key %s", encoding.GoldenTouchPrivKey) } - return &AnchorTxConstructor{ - rpc: rpc, - goldenTouchAddress: goldenTouchAddress, - signer: signer, - }, nil + return &AnchorTxConstructor{rpc, goldenTouchAddress, signer}, nil } // AssembleAnchorTx assembles a signed TaikoL2.anchor transaction. @@ -69,9 +65,11 @@ func (c *AnchorTxConstructor) AssembleAnchorTx( log.Info( "Anchor arguments", + "l2Height", l2Height, + "l1Height", l1Height, "l1Hash", l1Hash, "stateRoot", l1Header.Root, - "l1Height", l1Height, + "baseFee", baseFee, "gasUsed", parentGasUsed, ) diff --git a/driver/chain_syncer/beaconsync/progress_tracker.go b/driver/chain_syncer/beaconsync/progress_tracker.go index d9c23d6be..1948b8ddf 100644 --- a/driver/chain_syncer/beaconsync/progress_tracker.go +++ b/driver/chain_syncer/beaconsync/progress_tracker.go @@ -18,7 +18,7 @@ var ( ) // SyncProgressTracker is responsible for tracking the L2 execution engine's sync progress, after -// a beacon sync is triggered in it, and check whether the L2 execution is not able to sync through P2P (due to no +// a beacon sync is triggered, and check whether the L2 execution is not able to sync through P2P (due to no // connected peer or some other reasons). type SyncProgressTracker struct { // RPC client @@ -96,18 +96,16 @@ func (t *SyncProgressTracker) track(ctx context.Context) { 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", + 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, "lastSyncedVerifiedBlockHash", t.lastSyncedVerifiedBlockHash, ) return } - log.Debug( - "L2 execution engine has not started P2P syncing yet", - "timeout", t.timeout, - ) + log.Info("L2 execution engine has not started P2P syncing yet", "timeout", t.timeout) } defer func() { t.lastSyncProgress = progress }() diff --git a/driver/chain_syncer/beaconsync/syncer.go b/driver/chain_syncer/beaconsync/syncer.go index 03f0f5493..d263edc7b 100644 --- a/driver/chain_syncer/beaconsync/syncer.go +++ b/driver/chain_syncer/beaconsync/syncer.go @@ -34,7 +34,8 @@ func NewSyncer( return &Syncer{ctx, rpc, state, progressTracker} } -// TriggerBeaconSync triggers the L2 execution engine to start performing a beacon sync. +// TriggerBeaconSync triggers the L2 execution engine to start performing a beacon sync, if the +// latest verified block has changed. func (s *Syncer) TriggerBeaconSync() error { blockID, latestVerifiedHeadPayload, err := s.getVerifiedBlockPayload(s.ctx) if err != nil { @@ -56,10 +57,7 @@ func (s *Syncer) TriggerBeaconSync() error { } } - status, err := s.rpc.L2Engine.NewPayload( - s.ctx, - latestVerifiedHeadPayload, - ) + status, err := s.rpc.L2Engine.NewPayload(s.ctx, latestVerifiedHeadPayload) if err != nil { return err } @@ -81,10 +79,7 @@ func (s *Syncer) TriggerBeaconSync() error { } // Update sync status. - s.progressTracker.UpdateMeta( - blockID, - latestVerifiedHeadPayload.BlockHash, - ) + s.progressTracker.UpdateMeta(blockID, latestVerifiedHeadPayload.BlockHash) log.Info( "⛓️ Beacon sync triggered", @@ -103,7 +98,7 @@ func (s *Syncer) getVerifiedBlockPayload(ctx context.Context) (*big.Int, *engine return nil, nil, err } - blockInfo, err := s.rpc.TaikoL1.GetBlock(&bind.CallOpts{Context: ctx}, stateVars.B.LastVerifiedBlockId) + blockInfo, err := s.rpc.GetL2BlockInfo(ctx, new(big.Int).SetUint64(stateVars.B.LastVerifiedBlockId)) if err != nil { return nil, nil, err } diff --git a/driver/chain_syncer/calldata/syncer.go b/driver/chain_syncer/calldata/syncer.go index c655957d5..d772c7b20 100644 --- a/driver/chain_syncer/calldata/syncer.go +++ b/driver/chain_syncer/calldata/syncer.go @@ -191,7 +191,15 @@ func (s *Syncer) onBlockProposed( "removed", event.Raw.Removed, ) - // Fetch the L2 parent block. + // If the event's timestamp is in the future, we wait until the timestamp is reached, should + // only happen when testing. + if event.Meta.Timestamp > uint64(time.Now().Unix()) { + log.Warn("Future L2 block, waiting", "L2BlockTimestamp", event.Meta.Timestamp, "now", time.Now().Unix()) + time.Sleep(time.Until(time.Unix(int64(event.Meta.Timestamp), 0))) + } + + // Fetch the L2 parent block, if the node is just finished a P2P sync, we simply use the tracker's + // last synced verified block as the parent, otherwise, we fetch the parent block from L2 EE. var ( parent *types.Header err error @@ -206,18 +214,23 @@ func (s *Syncer) onBlockProposed( } else { parent, err = s.rpc.L2ParentByBlockID(ctx, event.BlockId) } - if err != nil { return fmt.Errorf("failed to fetch L2 parent block: %w", err) } - log.Debug("Parent block", "height", parent.Number, "hash", parent.Hash()) + log.Debug( + "Parent block", + "height", parent.Number, + "hash", parent.Hash(), + "beaconSyncTriggered", s.progressTracker.Triggered(), + ) tx, err := s.rpc.L1.TransactionInBlock(ctx, event.Raw.BlockHash, event.Raw.TxIndex) if err != nil { return fmt.Errorf("failed to fetch original TaikoL1.proposeBlock transaction: %w", err) } + // Decode transactions list. var txListDecoder txlistfetcher.TxListFetcher if event.Meta.BlobUsed { txListDecoder = txlistfetcher.NewBlobTxListFetcher(s.rpc) @@ -234,18 +247,6 @@ func (s *Syncer) onBlockProposed( } } - l1Origin := &rawdb.L1Origin{ - BlockID: event.BlockId, - L2BlockHash: common.Hash{}, // Will be set by taiko-geth. - L1BlockHeight: new(big.Int).SetUint64(event.Raw.BlockNumber), - L1BlockHash: event.Raw.BlockHash, - } - - if event.Meta.Timestamp > uint64(time.Now().Unix()) { - log.Warn("Future L2 block, waiting", "L2BlockTimestamp", event.Meta.Timestamp, "now", time.Now().Unix()) - time.Sleep(time.Until(time.Unix(int64(event.Meta.Timestamp), 0))) - } - // If the transactions list is invalid, we simply insert an empty L2 block. if !s.txListValidator.ValidateTxList(event.BlockId, txListBytes, event.Meta.BlobUsed) { log.Info("Invalid transactions list, insert an empty L2 block instead", "blockID", event.BlockId) @@ -258,7 +259,12 @@ func (s *Syncer) onBlockProposed( parent, s.state.GetHeadBlockID(), txListBytes, - l1Origin, + &rawdb.L1Origin{ + BlockID: event.BlockId, + L2BlockHash: common.Hash{}, // Will be set by taiko-geth. + L1BlockHeight: new(big.Int).SetUint64(event.Raw.BlockNumber), + L1BlockHash: event.Raw.BlockHash, + }, ) if err != nil { return fmt.Errorf("failed to insert new head to L2 execution engine: %w", err) @@ -350,6 +356,7 @@ func (s *Syncer) insertNewHead( return nil, fmt.Errorf("failed to create TaikoL2.anchor transaction: %w", err) } + // Insert the anchor transaction at the head of the transactions list txList = append([]*types.Transaction{anchorTx}, txList...) if txListBytes, err = rlp.EncodeToBytes(txList); err != nil { log.Error("Encode txList error", "blockID", event.BlockId, "error", err) @@ -487,7 +494,7 @@ func (s *Syncer) checkLastVerifiedBlockMismatch(ctx context.Context) (bool, erro return false, nil } - blockInfo, err := s.rpc.TaikoL1.GetBlock(&bind.CallOpts{Context: ctx}, stateVars.B.LastVerifiedBlockId) + blockInfo, err := s.rpc.GetL2BlockInfo(ctx, new(big.Int).SetUint64(stateVars.B.LastVerifiedBlockId)) if err != nil { return false, err } diff --git a/driver/chain_syncer/chain_syncer.go b/driver/chain_syncer/chain_syncer.go index 1a15a3947..f9d4f1ac8 100644 --- a/driver/chain_syncer/chain_syncer.go +++ b/driver/chain_syncer/chain_syncer.go @@ -30,7 +30,7 @@ type L2ChainSyncer struct { progressTracker *beaconsync.SyncProgressTracker // If this flag is activated, will try P2P beacon sync if current node is behind of the protocol's - // latest verified block head + // the latest verified block head p2pSyncVerifiedBlocks bool } @@ -98,7 +98,7 @@ func (s *L2ChainSyncer) Sync(l1End *types.Header) error { "L2 head information", "number", l2Head.Number, "hash", l2Head.Hash(), - "LastSyncedVerifiedBlockID", s.progressTracker.LastSyncedVerifiedBlockID(), + "lastSyncedVerifiedBlockID", s.progressTracker.LastSyncedVerifiedBlockID(), "lastSyncedVerifiedBlockHash", s.progressTracker.LastSyncedVerifiedBlockHash(), ) @@ -126,10 +126,12 @@ func (s *L2ChainSyncer) AheadOfProtocolVerifiedHead(verifiedHeightToCompare uint // If latest verified head height is equal to L2 execution engine's synced head height minus one, // we also mark the triggered P2P sync progress as finished to prevent a potential `InsertBlockWithoutSetHead` in // execution engine, which may cause errors since we do not pass all transactions in ExecutePayload when calling - // NewPayloadV1. + // `NewPayloadV1`. verifiedHeightToCompare-- } + // If the L2 execution engine's chain is behind of the protocol's latest verified block head, + // we should keep the beacon sync. if s.state.GetL2Head().Number.Uint64() < verifiedHeightToCompare { return false } @@ -142,16 +144,28 @@ func (s *L2ChainSyncer) AheadOfProtocolVerifiedHead(verifiedHeightToCompare uint } // needNewBeaconSyncTriggered checks whether the current L2 execution engine needs to trigger -// another new beacon sync. +// another new beacon sync, the following conditions should be met: +// 1. The `P2PSyncVerifiedBlocks` flag is set. +// 2. The protocol's latest verified block head is not zero. +// 3. The L2 execution engine's chain is behind of the protocol's latest verified block head. +// 4. The L2 execution engine's chain have met a sync timeout issue. func (s *L2ChainSyncer) needNewBeaconSyncTriggered() (bool, error) { + // If the flag is not set, we simply return false. + if !s.p2pSyncVerifiedBlocks { + return false, nil + } + stateVars, err := s.rpc.GetProtocolStateVariables(&bind.CallOpts{Context: s.ctx}) if err != nil { return false, err } - return s.p2pSyncVerifiedBlocks && - stateVars.B.LastVerifiedBlockId > 0 && - !s.AheadOfProtocolVerifiedHead(stateVars.B.LastVerifiedBlockId) && + // If the protocol's latest verified block head is zero, we simply return false. + if stateVars.B.LastVerifiedBlockId == 0 { + return false, nil + } + + return !s.AheadOfProtocolVerifiedHead(stateVars.B.LastVerifiedBlockId) && !s.progressTracker.OutOfSync(), nil } diff --git a/driver/driver.go b/driver/driver.go index 08f235066..36b1d1ca8 100644 --- a/driver/driver.go +++ b/driver/driver.go @@ -92,7 +92,6 @@ func (d *Driver) InitFromConfig(ctx context.Context, cfg *Config) (err error) { // Start starts the driver instance. func (d *Driver) Start() error { - d.wg.Add(3) go d.eventLoop() go d.reportProtocolStatus() go d.exchangeTransitionConfigLoop() @@ -109,6 +108,7 @@ func (d *Driver) Close(_ context.Context) { // eventLoop starts the main loop of a L2 execution engine's driver. func (d *Driver) eventLoop() { + d.wg.Add(1) defer d.wg.Done() // reqSync requests performing a synchronising operation, won't block @@ -152,9 +152,7 @@ func (d *Driver) doSync() error { return nil } - l1Head := d.state.GetL1Head() - - if err := d.l2ChainSyncer.Sync(l1Head); err != nil { + if err := d.l2ChainSyncer.Sync(d.state.GetL1Head()); err != nil { log.Error("Process new L1 blocks error", "error", err) return err } @@ -162,20 +160,25 @@ func (d *Driver) doSync() error { return nil } -// ChainSyncer returns the driver's chain syncer. +// ChainSyncer returns the driver's chain syncer, this method +// should only be used for testing. func (d *Driver) ChainSyncer() *chainSyncer.L2ChainSyncer { return d.l2ChainSyncer } // reportProtocolStatus reports some protocol status intervally. func (d *Driver) reportProtocolStatus() { - ticker := time.NewTicker(protocolStatusReportInterval) + var ( + ticker = time.NewTicker(protocolStatusReportInterval) + maxNumBlocks uint64 + ) + d.wg.Add(1) + defer func() { ticker.Stop() d.wg.Done() }() - var maxNumBlocks uint64 if err := backoff.Retry( func() error { if d.ctx.Err() != nil { @@ -220,6 +223,8 @@ func (d *Driver) reportProtocolStatus() { // L2 execution engine. func (d *Driver) exchangeTransitionConfigLoop() { ticker := time.NewTicker(exchangeTransitionConfigInterval) + d.wg.Add(1) + defer func() { ticker.Stop() d.wg.Done() @@ -238,13 +243,9 @@ func (d *Driver) exchangeTransitionConfigLoop() { }) if err != nil { log.Error("Failed to exchange Transition Configuration", "error", err) - return + } else { + log.Debug("Exchanged transition config", "transitionConfig", tc) } - - log.Debug( - "Exchanged transition config", - "transitionConfig", tc, - ) }() } } diff --git a/driver/state/l1_current.go b/driver/state/l1_current.go index 302738de6..01151ceff 100644 --- a/driver/state/l1_current.go +++ b/driver/state/l1_current.go @@ -5,7 +5,6 @@ import ( "fmt" "math/big" - "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" @@ -28,16 +27,14 @@ func (s *State) SetL1Current(h *types.Header) { // ResetL1Current resets the l1Current cursor to the L1 height which emitted a // BlockProposed event with given blockID / blockHash. -func (s *State) ResetL1Current( - ctx context.Context, - blockID *big.Int, -) error { +func (s *State) ResetL1Current(ctx context.Context, blockID *big.Int) error { if blockID == nil { return fmt.Errorf("empty block ID") } log.Info("Reset L1 current cursor", "blockID", blockID) + // If blockID is zero, reset to genesis L1 height. if blockID.Cmp(common.Big0) == 0 { l1Current, err := s.rpc.L1.HeaderByNumber(ctx, s.GenesisL1Height) if err != nil { @@ -47,11 +44,11 @@ func (s *State) ResetL1Current( return nil } - blockInfo, err := s.rpc.TaikoL1.GetBlock(&bind.CallOpts{Context: ctx}, blockID.Uint64()) + // Fetch the block info from TaikoL1 contract, and set the L1 height. + blockInfo, err := s.rpc.GetL2BlockInfo(ctx, blockID) if err != nil { return err } - l1Current, err := s.rpc.L1.HeaderByNumber(ctx, new(big.Int).SetUint64(blockInfo.Blk.ProposedIn)) if err != nil { return err diff --git a/driver/state/state.go b/driver/state/state.go index b8f0e3dde..6e8e632c7 100644 --- a/driver/state/state.go +++ b/driver/state/state.go @@ -1,9 +1,7 @@ package state import ( - "bytes" "context" - "fmt" "math/big" "sync" "sync/atomic" @@ -26,7 +24,7 @@ type State struct { l1Head *atomic.Value // Latest known L1 head l2Head *atomic.Value // Current L2 execution engine's local chain head - l2HeadBlockID *atomic.Value // Latest known L2 block ID + l2HeadBlockID *atomic.Value // Latest known L2 block ID in protocol l1Current *atomic.Value // Current L1 block sync cursor // Constants @@ -108,17 +106,22 @@ func (s *State) eventLoop(ctx context.Context) { s.wg.Add(1) defer s.wg.Done() - l1HeadCh := make(chan *types.Header, 10) - l2HeadCh := make(chan *types.Header, 10) - blockProposedCh := make(chan *bindings.TaikoL1ClientBlockProposed, 10) - transitionProvedCh := make(chan *bindings.TaikoL1ClientTransitionProved, 10) - blockVerifiedCh := make(chan *bindings.TaikoL1ClientBlockVerified, 10) - - l1HeadSub := rpc.SubscribeChainHead(s.rpc.L1, l1HeadCh) - l2HeadSub := rpc.SubscribeChainHead(s.rpc.L2, l2HeadCh) - l2BlockVerifiedSub := rpc.SubscribeBlockVerified(s.rpc.TaikoL1, blockVerifiedCh) - l2BlockProposedSub := rpc.SubscribeBlockProposed(s.rpc.TaikoL1, blockProposedCh) - l2TransitionProvedSub := rpc.SubscribeTransitionProved(s.rpc.TaikoL1, transitionProvedCh) + var ( + // Channels for subscriptions. + l1HeadCh = make(chan *types.Header, 10) + l2HeadCh = make(chan *types.Header, 10) + blockProposedCh = make(chan *bindings.TaikoL1ClientBlockProposed, 10) + transitionProvedCh = make(chan *bindings.TaikoL1ClientTransitionProved, 10) + blockVerifiedCh = make(chan *bindings.TaikoL1ClientBlockVerified, 10) + + // Subscriptions. + l1HeadSub = rpc.SubscribeChainHead(s.rpc.L1, l1HeadCh) + l2HeadSub = rpc.SubscribeChainHead(s.rpc.L2, l2HeadCh) + l2BlockVerifiedSub = rpc.SubscribeBlockVerified(s.rpc.TaikoL1, blockVerifiedCh) + l2BlockProposedSub = rpc.SubscribeBlockProposed(s.rpc.TaikoL1, blockProposedCh) + l2TransitionProvedSub = rpc.SubscribeTransitionProved(s.rpc.TaikoL1, transitionProvedCh) + ) + defer func() { l1HeadSub.Unsubscribe() l2HeadSub.Unsubscribe() @@ -214,46 +217,3 @@ func (s *State) GetHeadBlockID() *big.Int { func (s *State) SubL1HeadsFeed(ch chan *types.Header) event.Subscription { return s.l1HeadsFeed.Subscribe(ch) } - -// VerifyL2Block checks whether the given block is in L2 execution engine's local chain. -func (s *State) VerifyL2Block(ctx context.Context, height *big.Int, hash common.Hash) error { - header, err := s.rpc.L2.HeaderByNumber(ctx, height) - if err != nil { - return err - } - - if header.Hash() != hash { - // TODO(david): do not exit but re-sync from genesis? - log.Crit( - "Verified block hash mismatch", - "protocolBlockHash", hash, - "block number in L2 execution engine", header.Number, - "block hash in L2 execution engine", header.Hash(), - ) - } - return nil -} - -// getSyncedHeaderID fetches the block ID of the synced L2 header. -func (s *State) getSyncedHeaderID(ctx context.Context, l1Height uint64, hash common.Hash) (*big.Int, error) { - iter, err := s.rpc.TaikoL1.FilterBlockVerified(&bind.FilterOpts{ - Start: l1Height, - End: &l1Height, - Context: ctx, - }, nil, nil, nil) - if err != nil { - return nil, fmt.Errorf("failed to filter BlockVerified event: %w", err) - } - - for iter.Next() { - e := iter.Event - - if !bytes.Equal(e.BlockHash[:], hash.Bytes()) { - continue - } - - return e.BlockId, nil - } - - return nil, fmt.Errorf("verified block %s BlockVerified event not found", hash) -} diff --git a/driver/state/state_test.go b/driver/state/state_test.go index 0707d666c..3ca7d99b3 100644 --- a/driver/state/state_test.go +++ b/driver/state/state_test.go @@ -5,7 +5,6 @@ import ( "math/big" "testing" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/suite" @@ -25,13 +24,6 @@ func (s *DriverStateTestSuite) SetupTest() { s.s = state } -func (s *DriverStateTestSuite) TestVerifyL2Block() { - head, err := s.RPCClient.L2.HeaderByNumber(context.Background(), nil) - - s.Nil(err) - s.Nil(s.s.VerifyL2Block(context.Background(), head.Number, head.Hash())) -} - func (s *DriverStateTestSuite) TestGetL1Head() { l1Head := s.s.GetL1Head() s.NotNil(l1Head) @@ -58,15 +50,6 @@ func (s *DriverStateTestSuite) TestSubL1HeadsFeed() { s.NotNil(s.s.SubL1HeadsFeed(make(chan *types.Header))) } -func (s *DriverStateTestSuite) TestGetSyncedHeaderID() { - l2Genesis, err := s.RPCClient.L2.BlockByNumber(context.Background(), common.Big0) - s.Nil(err) - - id, err := s.s.getSyncedHeaderID(context.Background(), s.s.GenesisL1Height.Uint64(), l2Genesis.Hash()) - s.Nil(err) - s.Zero(id.Uint64()) -} - func (s *DriverStateTestSuite) TestNewDriverContextErr() { ctx, cancel := context.WithCancel(context.Background()) cancel() diff --git a/driver/txlist_fetcher/blob.go b/driver/txlist_fetcher/blob.go index 3bbf79bbf..feabca6c5 100644 --- a/driver/txlist_fetcher/blob.go +++ b/driver/txlist_fetcher/blob.go @@ -14,14 +14,17 @@ import ( "github.com/taikoxyz/taiko-client/pkg/rpc" ) +// BlobFetcher is responsible for fetching the txList blob from the L1 block sidecar. type BlobFetcher struct { rpc *rpc.Client } +// NewBlobTxListFetcher creates a new BlobFetcher instance based on the given rpc client. func NewBlobTxListFetcher(rpc *rpc.Client) *BlobFetcher { return &BlobFetcher{rpc} } +// Fetch implements the TxListFetcher interface. func (d *BlobFetcher) Fetch( ctx context.Context, _ *types.Transaction, @@ -31,6 +34,7 @@ func (d *BlobFetcher) Fetch( return nil, errBlobUnused } + // Fetch the L1 block sidecars. sidecars, err := d.rpc.L1Beacon.GetBlobs(ctx, new(big.Int).SetUint64(meta.L1Height+1)) if err != nil { return nil, err @@ -38,6 +42,7 @@ func (d *BlobFetcher) Fetch( log.Info("Fetch sidecars", "slot", meta.L1Height+1, "sidecars", len(sidecars)) + // Compare the blob hash with the sidecar's kzg commitment. for i, sidecar := range sidecars { log.Info( "Block sidecar", diff --git a/driver/txlist_fetcher/calldata.go b/driver/txlist_fetcher/calldata.go index c581ccc07..4abb686b0 100644 --- a/driver/txlist_fetcher/calldata.go +++ b/driver/txlist_fetcher/calldata.go @@ -8,8 +8,10 @@ import ( "github.com/taikoxyz/taiko-client/bindings/encoding" ) +// CalldataFetcher is responsible for fetching the txList bytes from the transaction's calldata. type CalldataFetcher struct{} +// NewCalldataTxListFetcher creates a new CalldataFetcher instance. func (d *CalldataFetcher) Fetch( _ context.Context, tx *types.Transaction, diff --git a/driver/txlist_fetcher/interface.go b/driver/txlist_fetcher/interface.go index 44b221bf0..6f43e69d8 100644 --- a/driver/txlist_fetcher/interface.go +++ b/driver/txlist_fetcher/interface.go @@ -14,6 +14,7 @@ var ( errSidecarNotFound = errors.New("sidecar not found") ) +// TxListFetcher is responsible for fetching the L2 txList bytes from L1 type TxListFetcher interface { Fetch(ctx context.Context, tx *types.Transaction, meta *bindings.TaikoDataBlockMetadata) ([]byte, error) } diff --git a/pkg/rpc/methods.go b/pkg/rpc/methods.go index b40085328..f7ef5cefa 100644 --- a/pkg/rpc/methods.go +++ b/pkg/rpc/methods.go @@ -111,11 +111,13 @@ func (c *Client) WaitTillL2ExecutionEngineSynced(ctx context.Context) error { ) } -// LatestL2KnownL1Header fetches the L2 execution engine's latest known L1 header. +// LatestL2KnownL1Header fetches the L2 execution engine's latest known L1 header, +// if we can't find the L1Origin data, we will use the L1 genesis header instead. func (c *Client) LatestL2KnownL1Header(ctx context.Context) (*types.Header, error) { ctxWithTimeout, cancel := ctxWithTimeoutOrDefault(ctx, defaultTimeout) defer cancel() + // Try to fetch the latest known L1 header from the L2 execution engine. headL1Origin, err := c.L2.HeadL1Origin(ctxWithTimeout) if err != nil { switch err.Error() { @@ -130,6 +132,7 @@ func (c *Client) LatestL2KnownL1Header(ctx context.Context) (*types.Header, erro return c.GetGenesisL1Header(ctxWithTimeout) } + // Fetch the L1 header from the L1 chain. header, err := c.L1.HeaderByHash(ctxWithTimeout, headL1Origin.L1BlockHash) if err != nil { switch err.Error() { @@ -376,6 +379,17 @@ func (c *Client) GetProtocolStateVariables(opts *bind.CallOpts) (*struct { return GetProtocolStateVariables(c.TaikoL1, opts) } +// GetL2BlockInfo fetches the L2 block and its corresponding transition state from the protocol. +func (c *Client) GetL2BlockInfo(ctx context.Context, blockID *big.Int) (struct { + Blk bindings.TaikoDataBlock + Ts bindings.TaikoDataTransitionState // nolint: stylecheck +}, error) { + ctxWithTimeout, cancel := ctxWithTimeoutOrDefault(ctx, defaultTimeout) + defer cancel() + + return c.TaikoL1.GetBlock(&bind.CallOpts{Context: ctxWithTimeout}, blockID.Uint64()) +} + // ReorgCheckResult represents the information about whether the L1 block has been reorged // and how to reset the L1 cursor. type ReorgCheckResult struct { @@ -629,28 +643,6 @@ func (c *Client) getSyncedL1SnippetFromAnchor( return l1BlockHash, l1StateRoot, l1Height, parentGasUsed, nil } -// IsJustSyncedByP2P checks whether the given L2 execution engine has just finished a P2P -// sync. -func (c *Client) IsJustSyncedByP2P(ctx context.Context) (bool, error) { - ctxWithTimeout, cancel := ctxWithTimeoutOrDefault(ctx, defaultTimeout) - defer cancel() - - l2Head, err := c.L2.HeaderByNumber(ctxWithTimeout, nil) - if err != nil { - return false, err - } - - if _, err = c.L2.L1OriginByID(ctxWithTimeout, l2Head.Number); err != nil { - if err.Error() == ethereum.NotFound.Error() { - return true, nil - } - - return false, err - } - - return false, nil -} - // TierProviderTierWithID wraps protocol ITierProviderTier struct with an ID. type TierProviderTierWithID struct { ID uint16 diff --git a/pkg/rpc/methods_test.go b/pkg/rpc/methods_test.go index f668222c9..4cc388b80 100644 --- a/pkg/rpc/methods_test.go +++ b/pkg/rpc/methods_test.go @@ -68,12 +68,6 @@ func TestGetProtocolStateVariables(t *testing.T) { require.Nil(t, err) } -func TestIsJustSyncedByP2P(t *testing.T) { - client := newTestClient(t) - _, err := client.IsJustSyncedByP2P(context.Background()) - require.Nil(t, err) -} - func TestWaitTillL2ExecutionEngineSyncedNewClient(t *testing.T) { client := newTestClient(t) err := client.WaitTillL2ExecutionEngineSynced(context.Background()) diff --git a/proposer/transaction_builder/common.go b/proposer/transaction_builder/common.go index 815e783c6..ebedebc66 100644 --- a/proposer/transaction_builder/common.go +++ b/proposer/transaction_builder/common.go @@ -2,6 +2,7 @@ package builder import ( "context" + "math/big" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" @@ -15,7 +16,7 @@ func getParentMetaHash(ctx context.Context, rpc *rpc.Client) (common.Hash, error return common.Hash{}, err } - parent, err := rpc.TaikoL1.GetBlock(&bind.CallOpts{Context: ctx}, state.SlotB.NumBlocks-1) + parent, err := rpc.GetL2BlockInfo(ctx, new(big.Int).SetUint64(state.SlotB.NumBlocks-1)) if err != nil { return common.Hash{}, err } diff --git a/prover/event_handler/transition_contested.go b/prover/event_handler/transition_contested.go index f831a7131..0a3837ec5 100644 --- a/prover/event_handler/transition_contested.go +++ b/prover/event_handler/transition_contested.go @@ -83,7 +83,7 @@ func (h *TransitionContestedEventHandler) Handle( } // If the proof is invalid, we contest it. - blockInfo, err := h.rpc.TaikoL1.GetBlock(&bind.CallOpts{Context: ctx}, e.BlockId.Uint64()) + blockInfo, err := h.rpc.GetL2BlockInfo(ctx, e.BlockId) if err != nil { return err } diff --git a/prover/event_handler/transition_proved.go b/prover/event_handler/transition_proved.go index b74e4ca58..42a03711a 100644 --- a/prover/event_handler/transition_proved.go +++ b/prover/event_handler/transition_proved.go @@ -4,7 +4,6 @@ import ( "context" "math/big" - "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/taikoxyz/taiko-client/bindings" @@ -59,7 +58,7 @@ func (h *TransitionProvedEventHandler) Handle( } // If the proof is invalid, we contest it. - blockInfo, err := h.rpc.TaikoL1.GetBlock(&bind.CallOpts{Context: ctx}, e.BlockId.Uint64()) + blockInfo, err := h.rpc.GetL2BlockInfo(ctx, e.BlockId) if err != nil { return err } diff --git a/prover/proof_submitter/proof_submitter.go b/prover/proof_submitter/proof_submitter.go index 03fc8054a..e39473043 100644 --- a/prover/proof_submitter/proof_submitter.go +++ b/prover/proof_submitter/proof_submitter.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" - "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" @@ -85,7 +84,7 @@ func (s *ProofSubmitter) RequestProof(ctx context.Context, event *bindings.Taiko return fmt.Errorf("failed to get the L2 parent block by hash (%s): %w", block.ParentHash(), err) } - blockInfo, err := s.rpc.TaikoL1.GetBlock(&bind.CallOpts{Context: ctx}, event.BlockId.Uint64()) + blockInfo, err := s.rpc.GetL2BlockInfo(ctx, event.BlockId) if err != nil { return err }