diff --git a/pkg/sender/sender.go b/pkg/sender/sender.go index 20ab2a32b..9f6917013 100644 --- a/pkg/sender/sender.go +++ b/pkg/sender/sender.go @@ -144,7 +144,7 @@ func NewSender(ctx context.Context, cfg *Config, client *rpc.EthClient, priv *ec return sender, nil } -// CLose closes the sender. +// Close closes the sender. func (s *Sender) Close() { close(s.stopCh) s.wg.Wait() @@ -166,6 +166,11 @@ func (s *Sender) GetOpts() *bind.TransactOpts { } } +// Address returns the sender's address. +func (s *Sender) Address() common.Address { + return s.opts.From +} + // TxToConfirmChannel returns a channel to wait the given transaction's confirmation. func (s *Sender) TxToConfirmChannel(txID string) <-chan *TxToConfirm { ch, ok := s.txToConfirmCh.Get(txID) diff --git a/prover/event_handler/block_proposed.go b/prover/event_handler/block_proposed.go index 1c7cc2924..9dc78162f 100644 --- a/prover/event_handler/block_proposed.go +++ b/prover/event_handler/block_proposed.go @@ -187,7 +187,6 @@ func (h *BlockProposedEventHandler) checkL1Reorg( } else { h.sharedState.SetLastHandledBlockID(reorgCheckResult.LastHandledBlockIDToReset.Uint64()) } - h.sharedState.SetReorgDetectedFlag(true) return errL1Reorged } @@ -370,7 +369,7 @@ type BlockProposedGuaridanEventHandler struct { GuardianProverHeartbeater guardianProverHeartbeater.BlockSenderHeartbeater } -// NewBlockProposedEventHandler creates a new BlockProposedEventHandler instance. +// NewBlockProposedEventGuardianHandler creates a new BlockProposedEventHandler instance. func NewBlockProposedEventGuardianHandler( opts *NewBlockProposedGuardianEventHandlerOps, ) *BlockProposedGuaridanEventHandler { diff --git a/prover/event_handler/interface.go b/prover/event_handler/interface.go index 4ff7cebfc..06f42d36a 100644 --- a/prover/event_handler/interface.go +++ b/prover/event_handler/interface.go @@ -20,7 +20,7 @@ type TransitionContestedHandler interface { Handle(ctx context.Context, event *bindings.TaikoL1ClientTransitionContested) error } -// TransitionContestedHandler is the interface for handling `TaikoL1.TransitionProved` events. +// TransitionProvedHandler is the interface for handling `TaikoL1.TransitionProved` events. type TransitionProvedHandler interface { Handle(ctx context.Context, event *bindings.TaikoL1ClientTransitionProved) error } diff --git a/prover/guardian.go b/prover/guardian.go index 48e210a68..ed0bcd6eb 100644 --- a/prover/guardian.go +++ b/prover/guardian.go @@ -18,17 +18,8 @@ func (p *Prover) gurdianProverHeartbeatLoop(ctx context.Context) { p.wg.Add(1) defer p.wg.Done() - if !p.IsGuardianProver() { - return - } - ticker := time.NewTicker(heartbeatInterval) - p.wg.Add(1) - - defer func() { - ticker.Stop() - p.wg.Done() - }() + defer ticker.Stop() for { select { diff --git a/prover/init.go b/prover/init.go index 13580f2f2..476555bf6 100644 --- a/prover/init.go +++ b/prover/init.go @@ -2,6 +2,7 @@ package prover import ( "context" + "fmt" "math/big" "github.com/ethereum/go-ethereum" @@ -10,7 +11,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/taikoxyz/taiko-client/bindings/encoding" - "github.com/taikoxyz/taiko-client/pkg/rpc" "github.com/taikoxyz/taiko-client/pkg/sender" handler "github.com/taikoxyz/taiko-client/prover/event_handler" proofProducer "github.com/taikoxyz/taiko-client/prover/proof_producer" @@ -51,14 +51,7 @@ func (p *Prover) setApprovalAmount(ctx context.Context, contract common.Address) return nil } - // Start setting the allowance amount. - opts, err := bind.NewKeyedTransactorWithChainID( - p.cfg.L1ProverPrivKey, - p.rpc.L1.ChainID, - ) - if err != nil { - return err - } + opts := p.txSender.GetOpts() opts.Context = ctx log.Info("Approving the contract for taiko token", "allowance", p.cfg.Allowance.String(), "contract", contract) @@ -72,15 +65,18 @@ func (p *Prover) setApprovalAmount(ctx context.Context, contract common.Address) return err } - // Wait for the transaction receipt. - receipt, err := rpc.WaitReceipt(ctx, p.rpc.L1, tx) + id, err := p.txSender.SendTransaction(tx) if err != nil { return err } + confirm := <-p.txSender.TxToConfirmChannel(id) + if confirm.Err != nil { + return confirm.Err + } log.Info( "Approved the contract for taiko token", - "txHash", receipt.TxHash.Hex(), + "txHash", confirm.Receipt.TxHash.Hex(), "contract", contract, ) @@ -128,6 +124,8 @@ func (p *Prover) initProofSubmitters( producer = sgxProducer case encoding.TierGuardianID: producer = proofProducer.NewGuardianProofProducer(p.cfg.EnableLivenessBondProof) + default: + return fmt.Errorf("unsupported tier: %d", tier.ID) } if submitter, err = proofSubmitter.New( diff --git a/prover/proof_producer/dummy_producer.go b/prover/proof_producer/dummy_producer.go index bfe03dd3b..c5aedc04c 100644 --- a/prover/proof_producer/dummy_producer.go +++ b/prover/proof_producer/dummy_producer.go @@ -9,7 +9,7 @@ import ( "github.com/taikoxyz/taiko-client/bindings" ) -// OptimisticProofProducer always returns a dummy proof. +// DummyProofProducer always returns a dummy proof. type DummyProofProducer struct{} // RequestProof returns a dummy proof to the result channel. diff --git a/prover/proof_submitter/proof_submitter_test.go b/prover/proof_submitter/proof_submitter_test.go index ed95f1907..f454c2318 100644 --- a/prover/proof_submitter/proof_submitter_test.go +++ b/prover/proof_submitter/proof_submitter_test.go @@ -45,10 +45,7 @@ func (s *ProofSubmitterTestSuite) SetupTest() { sender, err := sender.NewSender(context.Background(), &sender.Config{}, s.RPCClient.L1, l1ProverPrivKey) s.Nil(err) - builder := transaction.NewProveBlockTxBuilder( - s.RPCClient, - l1ProverPrivKey, - ) + builder := transaction.NewProveBlockTxBuilder(s.RPCClient) s.submitter, err = New( s.RPCClient, diff --git a/prover/proof_submitter/transaction/builder.go b/prover/proof_submitter/transaction/builder.go index 340ad6b69..bcfe7c8d3 100644 --- a/prover/proof_submitter/transaction/builder.go +++ b/prover/proof_submitter/transaction/builder.go @@ -2,15 +2,12 @@ package transaction import ( "context" - "crypto/ecdsa" "errors" "math/big" "sync" "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/crypto" "github.com/ethereum/go-ethereum/log" "github.com/taikoxyz/taiko-client/bindings" @@ -27,23 +24,15 @@ type TxBuilder func() (*types.Transaction, error) // ProveBlockTxBuilder is responsible for building ProveBlock transactions. type ProveBlockTxBuilder struct { - rpc *rpc.Client - proverPrivateKey *ecdsa.PrivateKey - proverAddress common.Address - mutex *sync.Mutex + rpc *rpc.Client + mutex sync.Mutex } // NewProveBlockTxBuilder creates a new ProveBlockTxBuilder instance. func NewProveBlockTxBuilder( rpc *rpc.Client, - proverPrivateKey *ecdsa.PrivateKey, ) *ProveBlockTxBuilder { - return &ProveBlockTxBuilder{ - rpc: rpc, - proverPrivateKey: proverPrivateKey, - proverAddress: crypto.PubkeyToAddress(proverPrivateKey.PublicKey), - mutex: new(sync.Mutex), - } + return &ProveBlockTxBuilder{rpc: rpc} } // Build creates a new TaikoL1.ProveBlock transaction with the given nonce. diff --git a/prover/proof_submitter/transaction/sender_test.go b/prover/proof_submitter/transaction/sender_test.go index 672b1d166..9f3de9bbe 100644 --- a/prover/proof_submitter/transaction/sender_test.go +++ b/prover/proof_submitter/transaction/sender_test.go @@ -41,7 +41,7 @@ func (s *TransactionTestSuite) SetupTest() { s.sender, err = NewSender(s.RPCClient, txSender) s.Nil(err) - s.builder = NewProveBlockTxBuilder(s.RPCClient, l1ProverPrivKey) + s.builder = NewProveBlockTxBuilder(s.RPCClient) } func (s *TransactionTestSuite) TestIsSubmitProofTxErrorRetryable() { diff --git a/prover/prover.go b/prover/prover.go index da60edafc..df2fdc62a 100644 --- a/prover/prover.go +++ b/prover/prover.go @@ -12,7 +12,6 @@ import ( "github.com/cenkalti/backoff/v4" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" "github.com/urfave/cli/v2" @@ -34,7 +33,10 @@ import ( // Prover keeps trying to prove newly proposed blocks. type Prover struct { // Configurations - cfg *Config + cfg *Config + backoff backoff.BackOffContext + + txSender *sender.Sender // Clients rpc *rpc.Client @@ -44,7 +46,7 @@ type Prover struct { guardianProverHeartbeater guardianProverHeartbeater.BlockSenderHeartbeater // Contract configurations - protocolConfigs *bindings.TaikoDataConfig + protocolConfig *bindings.TaikoDataConfig // States sharedState *state.SharedState @@ -87,12 +89,15 @@ func (p *Prover) InitFromCli(ctx context.Context, c *cli.Context) error { func InitFromConfig(ctx context.Context, p *Prover, cfg *Config) (err error) { p.cfg = cfg p.ctx = ctx - - // Initialize state which will be shared by event handlers. - p.sharedState = state.New() - // Initialize state which will be shared by event handlers. p.sharedState = state.New() + p.backoff = backoff.WithContext( + backoff.WithMaxRetries( + backoff.NewConstantBackOff(p.cfg.BackOffRetryInterval), + p.cfg.BackOffMaxRetrys, + ), + p.ctx, + ) // Clients if p.rpc, err = rpc.NewClient(p.ctx, &rpc.ClientConfig{ @@ -112,13 +117,11 @@ func InitFromConfig(ctx context.Context, p *Prover, cfg *Config) (err error) { if err != nil { return fmt.Errorf("failed to get protocol configs: %w", err) } - p.protocolConfigs = &protocolConfigs - - log.Info("Protocol configs", "configs", p.protocolConfigs) + p.protocolConfig = &protocolConfigs - proverAddress := crypto.PubkeyToAddress(p.cfg.L1ProverPrivKey.PublicKey) + log.Info("Protocol configs", "configs", p.protocolConfig) - chBufferSize := p.protocolConfigs.BlockMaxProposals + chBufferSize := p.protocolConfig.BlockMaxProposals p.proofGenerationCh = make(chan *proofProducer.ProofWithHeader, chBufferSize) p.assignmentExpiredCh = make(chan *bindings.TaikoL1ClientBlockProposed, chBufferSize) p.proofSubmissionCh = make(chan *proofProducer.ProofRequestBody, p.cfg.Capacity) @@ -152,21 +155,21 @@ func InitFromConfig(ctx context.Context, p *Prover, cfg *Config) (err error) { senderCfg.MaxRetrys = 0 } - txSender, err := sender.NewSender(p.ctx, senderCfg, p.rpc.L1, p.cfg.L1ProverPrivKey) + p.txSender, err = sender.NewSender(p.ctx, senderCfg, p.rpc.L1, p.cfg.L1ProverPrivKey) if err != nil { return err } - txBuilder := transaction.NewProveBlockTxBuilder(p.rpc, p.cfg.L1ProverPrivKey) + txBuilder := transaction.NewProveBlockTxBuilder(p.rpc) // Proof submitters - if err := p.initProofSubmitters(txSender, txBuilder); err != nil { + if err := p.initProofSubmitters(p.txSender, txBuilder); err != nil { return err } // Proof contester p.proofContester, err = proofSubmitter.NewProofContester( p.rpc, - txSender, + p.txSender, p.cfg.Graffiti, txBuilder, ) @@ -191,7 +194,7 @@ func InitFromConfig(ctx context.Context, p *Prover, cfg *Config) (err error) { } // Guardian prover heartbeat sender - if p.IsGuardianProver() { + if p.IsGuardianProver() && p.cfg.GuardianProverHealthCheckServerEndpoint != nil { // Check guardian prover contract address is correct. if _, err := p.rpc.GuardianProver.MinGuardians(&bind.CallOpts{Context: ctx}); err != nil { return fmt.Errorf("failed to get MinGuardians from guardian prover contract: %w", err) @@ -201,7 +204,7 @@ func InitFromConfig(ctx context.Context, p *Prover, cfg *Config) (err error) { p.cfg.L1ProverPrivKey, p.cfg.GuardianProverHealthCheckServerEndpoint, p.rpc, - proverAddress, + p.ProverAddress(), ) } @@ -271,7 +274,7 @@ func (p *Prover) eventLoop() { defer forceProvingTicker.Stop() // Channels - chBufferSize := p.protocolConfigs.BlockMaxProposals + chBufferSize := p.protocolConfig.BlockMaxProposals blockProposedCh := make(chan *bindings.TaikoL1ClientBlockProposed, chBufferSize) blockVerifiedCh := make(chan *bindings.TaikoL1ClientBlockVerified, chBufferSize) transitionProvedCh := make(chan *bindings.TaikoL1ClientTransitionProved, chBufferSize) @@ -328,30 +331,19 @@ func (p *Prover) Close(ctx context.Context) { // proveOp iterates through BlockProposed events func (p *Prover) proveOp() error { - firstTry := true - - for firstTry || p.sharedState.GetReorgDetectedFlag() { - p.sharedState.SetReorgDetectedFlag(false) - firstTry = false - - iter, err := eventIterator.NewBlockProposedIterator(p.ctx, &eventIterator.BlockProposedIteratorConfig{ - Client: p.rpc.L1, - TaikoL1: p.rpc.TaikoL1, - StartHeight: new(big.Int).SetUint64(p.sharedState.GetL1Current().Number.Uint64()), - OnBlockProposedEvent: p.blockProposedHandler.Handle, - BlockConfirmations: &p.cfg.BlockConfirmations, - }) - if err != nil { - log.Error("Failed to start event iterator", "event", "BlockProposed", "error", err) - return err - } - - if err := iter.Iter(); err != nil { - return err - } + iter, err := eventIterator.NewBlockProposedIterator(p.ctx, &eventIterator.BlockProposedIteratorConfig{ + Client: p.rpc.L1, + TaikoL1: p.rpc.TaikoL1, + StartHeight: new(big.Int).SetUint64(p.sharedState.GetL1Current().Number.Uint64()), + OnBlockProposedEvent: p.blockProposedHandler.Handle, + BlockConfirmations: &p.cfg.BlockConfirmations, + }) + if err != nil { + log.Error("Failed to start event iterator", "event", "BlockProposed", "error", err) + return err } - return nil + return iter.Iter() } // contestProofOp performs a proof contest operation. @@ -453,7 +445,7 @@ func (p *Prover) IsGuardianProver() bool { // ProverAddress returns the current prover account address. func (p *Prover) ProverAddress() common.Address { - return crypto.PubkeyToAddress(p.cfg.L1ProverPrivKey.PublicKey) + return p.txSender.Address() } // withRetry retries the given function with prover backoff policy. @@ -461,16 +453,7 @@ func (p *Prover) withRetry(f func() error) { p.wg.Add(1) go func() { defer p.wg.Done() - err := backoff.Retry( - func() error { - if p.ctx.Err() != nil { - log.Error("Context is done, aborting", "error", p.ctx.Err()) - return nil - } - return f() - }, - backoff.WithMaxRetries(backoff.NewConstantBackOff(p.cfg.BackOffRetryInterval), p.cfg.BackOffMaxRetrys), - ) + err := backoff.Retry(f, p.backoff) if err != nil { log.Error("Operation failed", "error", err) } diff --git a/prover/shared_state/state.go b/prover/shared_state/state.go index 30fea83ea..ce570f921 100644 --- a/prover/shared_state/state.go +++ b/prover/shared_state/state.go @@ -9,15 +9,14 @@ import ( // SharedState represents the internal state of a prover. type SharedState struct { - lastHandledBlockID *atomic.Uint64 - l1Current *types.Header - reorgDetectedFlag bool + lastHandledBlockID atomic.Uint64 + l1Current atomic.Value tiers []*rpc.TierProviderTierWithID } // New creates a new prover shared state instance. func New() *SharedState { - return &SharedState{lastHandledBlockID: new(atomic.Uint64)} + return &SharedState{} } // GetLastHandledBlockID returns the last handled block ID. @@ -32,22 +31,15 @@ func (s *SharedState) SetLastHandledBlockID(blockID uint64) { // GetL1Current returns the current L1 header cursor. func (s *SharedState) GetL1Current() *types.Header { - return s.l1Current + if val := s.l1Current.Load(); val != nil { + return val.(*types.Header) + } + return nil } // SetL1Current sets the current L1 header cursor. func (s *SharedState) SetL1Current(header *types.Header) { - s.l1Current = header -} - -// GetReorgDetectedFlag returns the reorg detected flag. -func (s *SharedState) GetReorgDetectedFlag() bool { - return s.reorgDetectedFlag -} - -// SetReorgDetectedFlag sets the reorg detected flag. -func (s *SharedState) SetReorgDetectedFlag(flag bool) { - s.reorgDetectedFlag = flag + s.l1Current.Store(header) } // GetTiers returns the current proof tiers. diff --git a/prover/shared_state/state_test.go b/prover/shared_state/state_test.go index fcc92501d..d6acbb492 100644 --- a/prover/shared_state/state_test.go +++ b/prover/shared_state/state_test.go @@ -32,12 +32,6 @@ func (s *ProverSharedStateTestSuite) TestL1Current() { s.Equal(newL1Current.Hash(), s.state.GetL1Current().Hash()) } -func (s *ProverSharedStateTestSuite) TestReorgDetectedFlag() { - s.NotEqual(true, s.state.GetReorgDetectedFlag()) - s.state.SetReorgDetectedFlag(true) - s.Equal(true, s.state.GetReorgDetectedFlag()) -} - func (s *ProverSharedStateTestSuite) TestTiers() { s.Empty(s.state.GetTiers()) s.state.SetTiers([]*rpc.TierProviderTierWithID{{ID: 1}})