diff --git a/prover/guardian.go b/prover/guardian.go index 9de54ba92..48e210a68 100644 --- a/prover/guardian.go +++ b/prover/guardian.go @@ -15,7 +15,9 @@ var ( // gurdianProverHeartbeatLoop keeps sending heartbeats to the guardian prover health check server // on an interval. func (p *Prover) gurdianProverHeartbeatLoop(ctx context.Context) { - // Only guardian provers need to send heartbeat. + p.wg.Add(1) + defer p.wg.Done() + if !p.IsGuardianProver() { return } diff --git a/prover/init.go b/prover/init.go index 01b4e83e5..ef14e8783 100644 --- a/prover/init.go +++ b/prover/init.go @@ -137,7 +137,6 @@ func (p *Prover) initProofSubmitters( producer, p.proofGenerationCh, p.cfg.TaikoL2Address, - p.cfg.L1ProverPrivKey, p.cfg.Graffiti, sender, txBuilder, diff --git a/prover/init_test.go b/prover/init_test.go index 663a9fe93..66983dc0f 100644 --- a/prover/init_test.go +++ b/prover/init_test.go @@ -10,7 +10,7 @@ import ( ) func (s *ProverTestSuite) TestSetApprovalAmount() { - opts, err := bind.NewKeyedTransactorWithChainID(s.p.proverPrivateKey, s.p.rpc.L1.ChainID) + opts, err := bind.NewKeyedTransactorWithChainID(s.p.cfg.L1ProverPrivKey, s.p.rpc.L1.ChainID) s.Nil(err) tx, err := s.p.rpc.TaikoToken.Approve(opts, s.p.cfg.AssignmentHookAddress, common.Big0) diff --git a/prover/proof_submitter/proof_contester.go b/prover/proof_submitter/proof_contester.go index 8ab7ed543..6fbc2ba1e 100644 --- a/prover/proof_submitter/proof_contester.go +++ b/prover/proof_submitter/proof_contester.go @@ -2,7 +2,6 @@ package submitter import ( "context" - "crypto/ecdsa" "math/big" "strings" @@ -32,7 +31,6 @@ type ProofContester struct { func NewProofContester( ctx context.Context, rpcClient *rpc.Client, - proverPrivKey *ecdsa.PrivateKey, txSender *sender.Sender, graffiti string, builder *transaction.ProveBlockTxBuilder, @@ -40,7 +38,6 @@ func NewProofContester( sender, err := transaction.NewSender( ctx, rpcClient, - proverPrivKey, txSender, ) if err != nil { diff --git a/prover/proof_submitter/proof_submitter.go b/prover/proof_submitter/proof_submitter.go index 6616d2fd4..0bb2181e9 100644 --- a/prover/proof_submitter/proof_submitter.go +++ b/prover/proof_submitter/proof_submitter.go @@ -2,13 +2,11 @@ package submitter import ( "context" - "crypto/ecdsa" "errors" "fmt" "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/taikoxyz/taiko-client/bindings" @@ -44,7 +42,6 @@ func New( proofProducer proofProducer.ProofProducer, resultCh chan *proofProducer.ProofWithHeader, taikoL2Address common.Address, - proverPrivKey *ecdsa.PrivateKey, graffiti string, txSender *sender.Sender, builder *transaction.ProveBlockTxBuilder, @@ -57,7 +54,6 @@ func New( proofSender, err := transaction.NewSender( ctx, rpcClient, - proverPrivKey, txSender, ) if err != nil { @@ -71,7 +67,7 @@ func New( anchorValidator: anchorValidator, txBuilder: builder, sender: proofSender, - proverAddress: crypto.PubkeyToAddress(proverPrivKey.PublicKey), + proverAddress: txSender.GetOpts().From, taikoL2Address: taikoL2Address, graffiti: rpc.StringToBytes32(graffiti), }, nil diff --git a/prover/proof_submitter/proof_submitter_test.go b/prover/proof_submitter/proof_submitter_test.go index d938ae4ef..74594dbe3 100644 --- a/prover/proof_submitter/proof_submitter_test.go +++ b/prover/proof_submitter/proof_submitter_test.go @@ -56,7 +56,6 @@ func (s *ProofSubmitterTestSuite) SetupTest() { &producer.OptimisticProofProducer{}, s.proofCh, common.HexToAddress(os.Getenv("TAIKO_L2_ADDRESS")), - l1ProverPrivKey, "test", sender, builder, @@ -65,7 +64,6 @@ func (s *ProofSubmitterTestSuite) SetupTest() { s.contester, err = NewProofContester( context.Background(), s.RPCClient, - l1ProverPrivKey, sender, "test", builder, diff --git a/prover/proof_submitter/transaction/sender.go b/prover/proof_submitter/transaction/sender.go index 01afbcdbb..208ed1001 100644 --- a/prover/proof_submitter/transaction/sender.go +++ b/prover/proof_submitter/transaction/sender.go @@ -2,7 +2,6 @@ package transaction import ( "context" - "crypto/ecdsa" "math/big" "strings" @@ -25,7 +24,6 @@ type Sender struct { func NewSender( ctx context.Context, cli *rpc.Client, - proverPrivateKey *ecdsa.PrivateKey, txSender *sender.Sender, ) (*Sender, error) { return &Sender{ diff --git a/prover/proof_submitter/transaction/sender_test.go b/prover/proof_submitter/transaction/sender_test.go index f93070629..4ce0ce0d3 100644 --- a/prover/proof_submitter/transaction/sender_test.go +++ b/prover/proof_submitter/transaction/sender_test.go @@ -38,7 +38,7 @@ func (s *TransactionTestSuite) SetupTest() { txSender, err := sender.NewSender(context.Background(), &sender.Config{}, s.RPCClient.L1, l1ProverPrivKey) s.Nil(err) - s.sender, err = NewSender(context.Background(), s.RPCClient, l1ProverPrivKey, txSender) + s.sender, err = NewSender(context.Background(), s.RPCClient, txSender) s.Nil(err) s.builder = NewProveBlockTxBuilder(s.RPCClient, l1ProverPrivKey) diff --git a/prover/prover.go b/prover/prover.go index 7edc91113..cbcb4a16f 100644 --- a/prover/prover.go +++ b/prover/prover.go @@ -2,7 +2,6 @@ package prover import ( "context" - "crypto/ecdsa" "errors" "fmt" "math/big" @@ -35,8 +34,7 @@ import ( // Prover keeps trying to prove newly proposed blocks. type Prover struct { // Configurations - cfg *Config - proverPrivateKey *ecdsa.PrivateKey + cfg *Config // Clients rpc *rpc.Client @@ -89,7 +87,9 @@ 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 - p.proverPrivateKey = cfg.L1ProverPrivKey + + // 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() @@ -152,11 +152,11 @@ 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.proverPrivateKey) + txSender, err := sender.NewSender(p.ctx, senderCfg, p.rpc.L1, p.cfg.L1ProverPrivKey) if err != nil { return err } - txBuilder := transaction.NewProveBlockTxBuilder(p.rpc, p.proverPrivateKey) + txBuilder := transaction.NewProveBlockTxBuilder(p.rpc, p.cfg.L1ProverPrivKey) // Proof submitters if err := p.initProofSubmitters(p.ctx, txSender, txBuilder); err != nil { @@ -167,7 +167,6 @@ func InitFromConfig(ctx context.Context, p *Prover, cfg *Config) (err error) { p.proofContester, err = proofSubmitter.NewProofContester( p.ctx, p.rpc, - p.cfg.L1ProverPrivKey, txSender, p.cfg.Graffiti, txBuilder, @@ -253,9 +252,7 @@ func (p *Prover) Start() error { // eventLoop starts the main loop of Taiko prover. func (p *Prover) eventLoop() { p.wg.Add(1) - defer func() { - p.wg.Done() - }() + defer p.wg.Done() // reqProving requests performing a proving operation, won't block // if we are already proving. @@ -297,11 +294,11 @@ func (p *Prover) eventLoop() { case <-p.ctx.Done(): return case proofWithHeader := <-p.proofGenerationCh: - p.submitProofOp(p.ctx, proofWithHeader) + p.withRetry(func() error { return p.submitProofOp(proofWithHeader) }) case req := <-p.proofSubmissionCh: - p.requestProofOp(p.ctx, req.Event, req.Tier) + p.withRetry(func() error { return p.requestProofOp(req.Event, req.Tier) }) case req := <-p.proofContestCh: - p.contestProofOp(p.ctx, req) + p.withRetry(func() error { return p.contestProofOp(req) }) case <-p.proveNotify: if err := p.proveOp(); err != nil { log.Error("Prove new blocks error", "error", err) @@ -309,23 +306,11 @@ func (p *Prover) eventLoop() { case e := <-blockVerifiedCh: p.blockVerifiedHandler.Handle(e) case e := <-transitionProvedCh: - go func() { - if err := p.withRetry(func() error { return p.transitionProvedHandler.Handle(p.ctx, e) }); err != nil { - log.Error("Handle TaikoL1.TransitionProved event error", "error", err) - } - }() + p.withRetry(func() error { return p.transitionProvedHandler.Handle(p.ctx, e) }) case e := <-transitionContestedCh: - go func() { - if err := p.withRetry(func() error { return p.transitionContestedHandler.Handle(p.ctx, e) }); err != nil { - log.Error("Handle TaikoL1.TransitionContested event error", "error", err) - } - }() + p.withRetry(func() error { return p.transitionContestedHandler.Handle(p.ctx, e) }) case e := <-p.assignmentExpiredCh: - go func() { - if err := p.withRetry(func() error { return p.assignmentExpiredHandler.Handle(p.ctx, e) }); err != nil { - log.Error("Handle proof window expired event error", "error", err) - } - }() + p.withRetry(func() error { return p.assignmentExpiredHandler.Handle(p.ctx, e) }) case <-blockProposedCh: reqProving() case <-forceProvingTicker.C: @@ -376,86 +361,53 @@ func (p *Prover) proveOp() error { } // contestProofOp performs a proof contest operation. -func (p *Prover) contestProofOp(ctx context.Context, req *proofSubmitter.ContestRequestBody) { - go func() { - if err := backoff.Retry(func() error { - if err := p.proofContester.SubmitContest( - p.ctx, - req.BlockID, - req.ProposedIn, - req.ParentHash, - req.Meta, - req.Tier, - ); err != nil { - log.Error("Request new proof contest error", "blockID", req.BlockID, "error", err) - return err - } +func (p *Prover) contestProofOp(req *proofSubmitter.ContestRequestBody) error { + if err := p.proofContester.SubmitContest( + p.ctx, + req.BlockID, + req.ProposedIn, + req.ParentHash, + req.Meta, + req.Tier, + ); err != nil { + log.Error("Request new proof contest error", "blockID", req.BlockID, "error", err) + return err + } - return nil - }, backoff.WithMaxRetries( - backoff.NewConstantBackOff(p.cfg.BackOffRetryInterval), - p.cfg.BackOffMaxRetrys, - )); err != nil { - log.Error("Request new proof contest error", "blockID", req.BlockID, "error", err) - } - }() + return nil } // requestProofOp requests a new proof generation operation. -func (p *Prover) requestProofOp(ctx context.Context, e *bindings.TaikoL1ClientBlockProposed, minTier uint16) { - go func() { - if p.IsGuardianProver() { - minTier = encoding.TierGuardianID +func (p *Prover) requestProofOp(e *bindings.TaikoL1ClientBlockProposed, minTier uint16) error { + if p.IsGuardianProver() { + minTier = encoding.TierGuardianID + } + if submitter := p.selectSubmitter(minTier); submitter != nil { + if err := submitter.RequestProof(p.ctx, e); err != nil { + log.Error("Request new proof error", "blockID", e.BlockId, "error", err) + return err } - if err := backoff.Retry(func() error { - if ctx.Err() != nil { - log.Error("Context is done, aborting requestProofOp", "blockID", e.BlockId, "error", ctx.Err()) - return nil - } - - if proofSubmitter := p.selectSubmitter(minTier); proofSubmitter != nil { - if err := proofSubmitter.RequestProof(ctx, e); err != nil { - log.Error("Request new proof error", "blockID", e.BlockId, "error", err) - return err - } - - return nil - } + return nil + } - log.Error("Failed to find proof submitter", "blockID", e.BlockId, "minTier", minTier) - return nil - }, backoff.WithMaxRetries( - backoff.NewConstantBackOff(p.cfg.BackOffRetryInterval), - p.cfg.BackOffMaxRetrys, - )); err != nil { - log.Error("Request new proof error", "blockID", e.BlockId, "error", err) - } - }() + log.Error("Failed to find proof submitter", "blockID", e.BlockId, "minTier", minTier) + return nil } // submitProofOp performs a proof submission operation. -func (p *Prover) submitProofOp(ctx context.Context, proofWithHeader *proofProducer.ProofWithHeader) { - go func() { - if err := backoff.Retry( - func() error { - proofSubmitter := p.getSubmitterByTier(proofWithHeader.Tier) - if proofSubmitter == nil { - return nil - } +func (p *Prover) submitProofOp(proofWithHeader *proofProducer.ProofWithHeader) error { + submitter := p.getSubmitterByTier(proofWithHeader.Tier) + if submitter == nil { + return nil + } - if err := proofSubmitter.SubmitProof(p.ctx, proofWithHeader); err != nil { - log.Error("Submit proof error", "error", err) - return err - } + if err := submitter.SubmitProof(p.ctx, proofWithHeader); err != nil { + log.Error("Submit proof error", "error", err) + return err + } - return nil - }, - backoff.WithMaxRetries(backoff.NewConstantBackOff(p.cfg.BackOffRetryInterval), p.cfg.BackOffMaxRetrys), - ); err != nil { - log.Error("Submit proof error", "error", err) - } - }() + return nil } // Name returns the application name. @@ -497,19 +449,26 @@ func (p *Prover) IsGuardianProver() bool { // ProverAddress returns the current prover account address. func (p *Prover) ProverAddress() common.Address { - return crypto.PubkeyToAddress(p.proverPrivateKey.PublicKey) + return crypto.PubkeyToAddress(p.cfg.L1ProverPrivKey.PublicKey) } // withRetry retries the given function with prover backoff policy. -func (p *Prover) withRetry(f func() error) error { - return 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), - ) +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), + ) + if err != nil { + log.Error("Operation failed", "error", err) + } + }() } diff --git a/prover/prover_test.go b/prover/prover_test.go index beae14439..e88953485 100644 --- a/prover/prover_test.go +++ b/prover/prover_test.go @@ -135,7 +135,7 @@ func (s *ProverTestSuite) TestOnBlockProposed() { e := s.ProposeAndInsertValidBlock(s.proposer, s.d.ChainSyncer().CalldataSyncer()) s.Nil(s.p.blockProposedHandler.Handle(context.Background(), e, func() {})) req := <-s.p.proofSubmissionCh - s.p.requestProofOp(s.p.ctx, req.Event, req.Tier) + s.Nil(s.p.requestProofOp(req.Event, req.Tier)) s.Nil(s.p.selectSubmitter(e.Meta.MinTier).SubmitProof(context.Background(), <-s.p.proofGenerationCh)) // Empty blocks @@ -145,7 +145,7 @@ func (s *ProverTestSuite) TestOnBlockProposed() { ) { s.Nil(s.p.blockProposedHandler.Handle(context.Background(), e, func() {})) req := <-s.p.proofSubmissionCh - s.p.requestProofOp(s.p.ctx, req.Event, req.Tier) + s.Nil(s.p.requestProofOp(req.Event, req.Tier)) s.Nil(s.p.selectSubmitter(e.Meta.MinTier).SubmitProof(context.Background(), <-s.p.proofGenerationCh)) } } @@ -161,23 +161,27 @@ func (s *ProverTestSuite) TestOnBlockVerifiedEmptyBlockHash() { func (s *ProverTestSuite) TestSubmitProofOp() { s.NotPanics(func() { - s.p.submitProofOp(context.Background(), &producer.ProofWithHeader{ - BlockID: common.Big1, - Meta: &bindings.TaikoDataBlockMetadata{}, - Header: &types.Header{}, - Proof: []byte{}, - Tier: encoding.TierOptimisticID, - Opts: &producer.ProofRequestOptions{}, + s.p.withRetry(func() error { + return s.p.submitProofOp(&producer.ProofWithHeader{ + BlockID: common.Big1, + Meta: &bindings.TaikoDataBlockMetadata{}, + Header: &types.Header{}, + Proof: []byte{}, + Tier: encoding.TierOptimisticID, + Opts: &producer.ProofRequestOptions{}, + }) }) }) s.NotPanics(func() { - s.p.submitProofOp(context.Background(), &producer.ProofWithHeader{ - BlockID: common.Big1, - Meta: &bindings.TaikoDataBlockMetadata{}, - Header: &types.Header{}, - Proof: []byte{}, - Tier: encoding.TierOptimisticID, - Opts: &producer.ProofRequestOptions{}, + s.p.withRetry(func() error { + return s.p.submitProofOp(&producer.ProofWithHeader{ + BlockID: common.Big1, + Meta: &bindings.TaikoDataBlockMetadata{}, + Header: &types.Header{}, + Proof: []byte{}, + Tier: encoding.TierOptimisticID, + Opts: &producer.ProofRequestOptions{}, + }) }) }) } @@ -220,7 +224,7 @@ func (s *ProverTestSuite) TestContestWrongBlocks() { s.Nil(s.p.proveOp()) req := <-s.p.proofSubmissionCh - s.p.requestProofOp(s.p.ctx, req.Event, req.Tier) + s.Nil(s.p.requestProofOp(req.Event, req.Tier)) proofWithHeader := <-s.p.proofGenerationCh proofWithHeader.Opts.BlockHash = testutils.RandomHash() s.Nil(s.p.selectSubmitter(e.Meta.MinTier).SubmitProof(context.Background(), proofWithHeader)) @@ -252,7 +256,7 @@ func (s *ProverTestSuite) TestContestWrongBlocks() { s.Greater(header.Number.Uint64(), uint64(0)) s.Nil(s.p.transitionProvedHandler.Handle(context.Background(), event)) contestReq := <-s.p.proofContestCh - s.p.contestProofOp(s.p.ctx, contestReq) + s.Nil(s.p.contestProofOp(contestReq)) contestedEvent := <-contestedSink s.Equal(header.Number.Uint64(), contestedEvent.BlockId.Uint64()) @@ -275,7 +279,7 @@ func (s *ProverTestSuite) TestContestWrongBlocks() { close(approvedSink) }() req = <-s.p.proofSubmissionCh - s.p.requestProofOp(s.p.ctx, req.Event, req.Tier) + s.Nil(s.p.requestProofOp(req.Event, req.Tier)) s.Nil(s.p.selectSubmitter(encoding.TierGuardianID).SubmitProof(context.Background(), <-s.p.proofGenerationCh)) approvedEvent := <-approvedSink @@ -300,7 +304,7 @@ func (s *ProverTestSuite) TestProveExpiredUnassignedBlock() { s.p.cfg.GuardianProverAddress = common.Address{} s.Nil(s.p.assignmentExpiredHandler.Handle(context.Background(), e)) req := <-s.p.proofSubmissionCh - s.p.requestProofOp(s.p.ctx, req.Event, req.Tier) + s.Nil(s.p.requestProofOp(req.Event, req.Tier)) s.Nil(s.p.selectSubmitter(e.Meta.MinTier).SubmitProof(context.Background(), <-s.p.proofGenerationCh)) event := <-sink @@ -343,7 +347,7 @@ func (s *ProverTestSuite) TestProveOp() { s.Nil(s.p.proveOp()) req := <-s.p.proofSubmissionCh - s.p.requestProofOp(s.p.ctx, req.Event, req.Tier) + s.Nil(s.p.requestProofOp(req.Event, req.Tier)) s.Nil(s.p.selectSubmitter(e.Meta.MinTier).SubmitProof(context.Background(), <-s.p.proofGenerationCh)) event := <-sink @@ -375,7 +379,7 @@ func (s *ProverTestSuite) TestGetBlockProofStatus() { s.Nil(s.p.proveOp()) req := <-s.p.proofSubmissionCh - s.p.requestProofOp(s.p.ctx, req.Event, req.Tier) + s.Nil(s.p.requestProofOp(req.Event, req.Tier)) s.Nil(s.p.selectSubmitter(e.Meta.MinTier).SubmitProof(context.Background(), <-s.p.proofGenerationCh)) status, err = rpc.GetBlockProofStatus(context.Background(), s.p.rpc, e.BlockId, s.p.ProverAddress()) @@ -398,7 +402,7 @@ func (s *ProverTestSuite) TestGetBlockProofStatus() { s.Nil(s.p.proveOp()) req = <-s.p.proofSubmissionCh - s.p.requestProofOp(s.p.ctx, req.Event, req.Tier) + s.Nil(s.p.requestProofOp(req.Event, req.Tier)) proofWithHeader := <-s.p.proofGenerationCh proofWithHeader.Opts.BlockHash = testutils.RandomHash()