diff --git a/cmd/flags/prover.go b/cmd/flags/prover.go index 42e5a1ab5..9c5c818ba 100644 --- a/cmd/flags/prover.go +++ b/cmd/flags/prover.go @@ -69,6 +69,12 @@ var ( Value: 0, Category: proverCategory, } + ProofSubmissionMaxRetry = &cli.Uint64Flag{ + Name: "proofSubmissionMaxRetry", + Usage: "Max retry counts for proof submission", + Value: 0, + Category: proverCategory, + } Graffiti = &cli.StringFlag{ Name: "graffiti", Usage: "When string is passed, adds additional graffiti info to proof evidence", @@ -110,6 +116,7 @@ var ProverFlags = MergeFlags(CommonFlags, []cli.Flag{ OracleProver, OracleProverPrivateKey, OracleProofSubmissionDelay, + ProofSubmissionMaxRetry, Graffiti, TaikoProverPoolL1Address, CheckProofWindowExpiredInterval, diff --git a/prover/config.go b/prover/config.go index 5a0ec9482..7abccf338 100644 --- a/prover/config.go +++ b/prover/config.go @@ -31,6 +31,7 @@ type Config struct { OracleProver bool OracleProverPrivateKey *ecdsa.PrivateKey OracleProofSubmissionDelay time.Duration + ProofSubmissionMaxRetry uint64 Graffiti string RandomDummyProofDelayLowerBound *time.Duration RandomDummyProofDelayUpperBound *time.Duration @@ -116,6 +117,7 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) { OracleProver: c.Bool(flags.OracleProver.Name), OracleProverPrivateKey: oracleProverPrivKey, OracleProofSubmissionDelay: time.Duration(c.Uint64(flags.OracleProofSubmissionDelay.Name)) * time.Second, + ProofSubmissionMaxRetry: c.Uint64(flags.ProofSubmissionMaxRetry.Name), Graffiti: c.String(flags.Graffiti.Name), RandomDummyProofDelayLowerBound: randomDummyProofDelayLowerBound, RandomDummyProofDelayUpperBound: randomDummyProofDelayUpperBound, diff --git a/prover/proof_submitter/util.go b/prover/proof_submitter/util.go index f2b86c3b3..fa6e54fd4 100644 --- a/prover/proof_submitter/util.go +++ b/prover/proof_submitter/util.go @@ -71,11 +71,17 @@ func sendTxWithBackoff( meta *bindings.TaikoDataBlockMetadata, sendTxFunc func() (*types.Transaction, error), retryInterval time.Duration, + maxRetry *uint64, ) error { var ( isUnretryableError bool + backOffPolicy backoff.BackOff = backoff.NewConstantBackOff(retryInterval) ) + if maxRetry != nil { + backOffPolicy = backoff.WithMaxRetries(backOffPolicy, *maxRetry) + } + if err := backoff.Retry(func() error { if ctx.Err() != nil { return nil @@ -127,7 +133,7 @@ func sendTxWithBackoff( ) return nil - }, backoff.NewConstantBackOff(retryInterval)); err != nil { + }, backOffPolicy); err != nil { return fmt.Errorf("failed to send TaikoL1.proveBlock transaction: %w", err) } diff --git a/prover/proof_submitter/util_test.go b/prover/proof_submitter/util_test.go index c4545d501..1fac8bfc4 100644 --- a/prover/proof_submitter/util_test.go +++ b/prover/proof_submitter/util_test.go @@ -11,6 +11,10 @@ import ( "github.com/taikoxyz/taiko-client/bindings" ) +var ( + testMaxRetry uint64 = 1 +) + func (s *ProofSubmitterTestSuite) TestIsSubmitProofTxErrorRetryable() { s.True(isSubmitProofTxErrorRetryable(errors.New(testAddr.String()), common.Big0)) s.False(isSubmitProofTxErrorRetryable(errors.New("L1_NOT_SPECIAL_PROVER"), common.Big0)) @@ -43,6 +47,7 @@ func (s *ProofSubmitterTestSuite) TestSendTxWithBackoff() { meta, func() (*types.Transaction, error) { return nil, errors.New("L1_TEST") }, 12*time.Second, + &testMaxRetry, )) s.Nil(sendTxWithBackoff( @@ -69,5 +74,6 @@ func (s *ProofSubmitterTestSuite) TestSendTxWithBackoff() { return block.Transactions()[0], nil }, 12*time.Second, + &testMaxRetry, )) } diff --git a/prover/proof_submitter/valid_proof_submitter.go b/prover/proof_submitter/valid_proof_submitter.go index d6d0375f9..fbd3d7008 100644 --- a/prover/proof_submitter/valid_proof_submitter.go +++ b/prover/proof_submitter/valid_proof_submitter.go @@ -27,19 +27,20 @@ var _ ProofSubmitter = (*ValidProofSubmitter)(nil) // ValidProofSubmitter is responsible requesting zk proofs for the given valid L2 // blocks, and submitting the generated proofs to the TaikoL1 smart contract. type ValidProofSubmitter struct { - rpc *rpc.Client - proofProducer proofProducer.ProofProducer - resultCh chan *proofProducer.ProofWithHeader - anchorTxValidator *anchorTxValidator.AnchorTxValidator - proverPrivKey *ecdsa.PrivateKey - proverAddress common.Address - taikoL2Address common.Address - l1SignalService common.Address - l2SignalService common.Address - mutex *sync.Mutex - isOracleProver bool - graffiti [32]byte - retryInterval time.Duration + rpc *rpc.Client + proofProducer proofProducer.ProofProducer + resultCh chan *proofProducer.ProofWithHeader + anchorTxValidator *anchorTxValidator.AnchorTxValidator + proverPrivKey *ecdsa.PrivateKey + proverAddress common.Address + taikoL2Address common.Address + l1SignalService common.Address + l2SignalService common.Address + mutex *sync.Mutex + isOracleProver bool + graffiti [32]byte + submissionMaxRetry uint64 + retryInterval time.Duration } // NewValidProofSubmitter creates a new ValidProofSubmitter instance. @@ -52,6 +53,7 @@ func NewValidProofSubmitter( mutex *sync.Mutex, isOracleProver bool, graffiti string, + submissionMaxRetry uint64, retryInterval time.Duration, ) (*ValidProofSubmitter, error) { anchorValidator, err := anchorTxValidator.New(taikoL2Address, rpcClient.L2ChainID, rpcClient) @@ -70,19 +72,20 @@ func NewValidProofSubmitter( } return &ValidProofSubmitter{ - rpc: rpcClient, - proofProducer: proofProducer, - resultCh: resultCh, - anchorTxValidator: anchorValidator, - proverPrivKey: proverPrivKey, - proverAddress: crypto.PubkeyToAddress(proverPrivKey.PublicKey), - l1SignalService: l1SignalService, - l2SignalService: l2SignalService, - taikoL2Address: taikoL2Address, - mutex: mutex, - isOracleProver: isOracleProver, - graffiti: rpc.StringToBytes32(graffiti), - retryInterval: retryInterval, + rpc: rpcClient, + proofProducer: proofProducer, + resultCh: resultCh, + anchorTxValidator: anchorValidator, + proverPrivKey: proverPrivKey, + proverAddress: crypto.PubkeyToAddress(proverPrivKey.PublicKey), + l1SignalService: l1SignalService, + l2SignalService: l2SignalService, + taikoL2Address: taikoL2Address, + mutex: mutex, + isOracleProver: isOracleProver, + graffiti: rpc.StringToBytes32(graffiti), + submissionMaxRetry: submissionMaxRetry, + retryInterval: retryInterval, }, nil } @@ -253,6 +256,11 @@ func (s *ValidProofSubmitter) SubmitProof( return s.rpc.TaikoL1.ProveBlock(txOpts, blockID, input) } + var maxRetry = &s.submissionMaxRetry + if s.isOracleProver { + maxRetry = nil + } + if err := sendTxWithBackoff( ctx, s.rpc, @@ -262,6 +270,7 @@ func (s *ValidProofSubmitter) SubmitProof( proofWithHeader.Meta, sendTx, s.retryInterval, + maxRetry, ); err != nil { if errors.Is(err, errUnretryable) { return nil diff --git a/prover/proof_submitter/valid_proof_submitter_test.go b/prover/proof_submitter/valid_proof_submitter_test.go index 9edec5d73..7974e4558 100644 --- a/prover/proof_submitter/valid_proof_submitter_test.go +++ b/prover/proof_submitter/valid_proof_submitter_test.go @@ -48,6 +48,7 @@ func (s *ProofSubmitterTestSuite) SetupTest() { &sync.Mutex{}, false, "test", + 1, 12*time.Second, ) s.Nil(err) diff --git a/prover/prover.go b/prover/prover.go index 22ac9e541..9fb4fcbff 100644 --- a/prover/prover.go +++ b/prover/prover.go @@ -217,6 +217,7 @@ func InitFromConfig(ctx context.Context, p *Prover, cfg *Config) (err error) { p.submitProofTxMutex, p.cfg.OracleProver, p.cfg.Graffiti, + p.cfg.ProofSubmissionMaxRetry, p.cfg.BackOffRetryInterval, ); err != nil { return err