Skip to content
This repository has been archived by the owner on May 11, 2024. It is now read-only.

Commit

Permalink
feat(prover): increase gasTipCap when resending `TaikoL1.proveBlock…
Browse files Browse the repository at this point in the history
…` transactions (#411)

Co-authored-by: jeff <[email protected]>
Co-authored-by: Roger <[email protected]>
Co-authored-by: RogerLamTd <[email protected]>
  • Loading branch information
4 people committed Sep 27, 2023
1 parent c096505 commit d5cf89d
Show file tree
Hide file tree
Showing 11 changed files with 263 additions and 158 deletions.
13 changes: 13 additions & 0 deletions cmd/flags/prover.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,17 @@ var (
Usage: "Gas limit will be used for TaikoL1.proveBlock transactions",
Category: proverCategory,
}
ProveBlockTxReplacementMultiplier = &cli.Uint64Flag{
Name: "proveBlockTxReplacementMultiplier",
Value: 2,
Usage: "Gas tip multiplier when replacing a TaikoL1.proveBlock transaction with same nonce",
Category: proverCategory,
}
ProveBlockMaxTxGasTipCap = &cli.Uint64Flag{
Name: "proveBlockMaxTxGasTipCap",
Usage: "Gas tip cap (in wei) for a TaikoL1.proveBlock transaction when doing the transaction replacement",
Category: proverCategory,
}
ProverHTTPServerPort = &cli.Uint64Flag{
Name: "prover.httpServerPort",
Usage: "Port to expose for http server",
Expand Down Expand Up @@ -142,6 +153,8 @@ var ProverFlags = MergeFlags(CommonFlags, []cli.Flag{
OracleProverPrivateKey,
OracleProofSubmissionDelay,
ProofSubmissionMaxRetry,
ProveBlockTxReplacementMultiplier,
ProveBlockMaxTxGasTipCap,
Graffiti,
CheckProofWindowExpiredInterval,
ProveUnassignedBlocks,
Expand Down
50 changes: 50 additions & 0 deletions pkg/rpc/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient/gethclient"
"github.com/ethereum/go-ethereum/log"
Expand Down Expand Up @@ -200,6 +201,55 @@ func ContentFrom(
)
}

// IncreaseGasTipCap tries to increase the given transaction's gasTipCap.
func IncreaseGasTipCap(
ctx context.Context,
cli *Client,
opts *bind.TransactOpts,
address common.Address,
txReplacementTipMultiplier *big.Int,
maxGasTipCap *big.Int,
) (*bind.TransactOpts, error) {
ctxWithTimeout, cancel := ctxWithTimeoutOrDefault(ctx, defaultTimeout)
defer cancel()

log.Info("Try replacing a transaction with same nonce", "sender", address, "nonce", opts.Nonce)

originalTx, err := GetPendingTxByNonce(ctxWithTimeout, cli, address, opts.Nonce.Uint64())
if err != nil || originalTx == nil {
log.Warn(
"Original transaction not found",
"sender", address,
"nonce", opts.Nonce,
"error", err,
)

opts.GasTipCap = new(big.Int).Mul(opts.GasTipCap, txReplacementTipMultiplier)
} else {
log.Info(
"Original transaction to replace",
"sender", address,
"nonce", opts.Nonce,
"gasTipCap", originalTx.GasTipCap(),
"gasFeeCap", originalTx.GasFeeCap(),
)

opts.GasTipCap = new(big.Int).Mul(originalTx.GasTipCap(), txReplacementTipMultiplier)
}

if maxGasTipCap != nil && opts.GasTipCap.Cmp(maxGasTipCap) > 0 {
log.Info(
"New gasTipCap exceeds limit, keep waiting",
"multiplier", txReplacementTipMultiplier,
"newGasTipCap", opts.GasTipCap,
"maxTipCap", maxGasTipCap,
)
return nil, txpool.ErrReplaceUnderpriced
}

return opts, nil
}

// GetPendingTxByNonce tries to retrieve a pending transaction with a given nonce in a node's mempool.
func GetPendingTxByNonce(
ctx context.Context,
Expand Down
49 changes: 14 additions & 35 deletions proposer/proposer.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ 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/core"
"github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
Expand Down Expand Up @@ -358,40 +359,15 @@ func (p *Proposer) sendProposeBlockTx(
opts.GasLimit = *p.proposeBlockTxGasLimit
}
if isReplacement {
log.Info("Try replacing a transaction with same nonce", "sender", p.l1ProposerAddress, "nonce", nonce)
originalTx, err := rpc.GetPendingTxByNonce(ctx, p.rpc, p.l1ProposerAddress, *nonce)
if err != nil || originalTx == nil {
log.Warn(
"Original transaction not found",
"sender", p.l1ProposerAddress,
"nonce", nonce,
"error", err,
)

opts.GasTipCap = new(big.Int).Mul(opts.GasTipCap, new(big.Int).SetUint64(p.txReplacementTipMultiplier))
} else {
log.Info(
"Original transaction to replace",
"sender", p.l1ProposerAddress,
"nonce", nonce,
"gasTipCap", originalTx.GasTipCap(),
"gasFeeCap", originalTx.GasFeeCap(),
)

opts.GasTipCap = new(big.Int).Mul(
originalTx.GasTipCap(),
new(big.Int).SetUint64(p.txReplacementTipMultiplier),
)
}

if p.proposeBlockTxGasTipCap != nil && opts.GasTipCap.Cmp(p.proposeBlockTxGasTipCap) > 0 {
log.Info(
"New gasTipCap exceeds limit, keep waiting",
"multiplier", p.txReplacementTipMultiplier,
"newGasTipCap", opts.GasTipCap,
"maxTipCap", p.proposeBlockTxGasTipCap,
)
return nil, txpool.ErrReplaceUnderpriced
if opts, err = rpc.IncreaseGasTipCap(
ctx,
p.rpc,
opts,
p.l1ProposerAddress,
new(big.Int).SetUint64(p.txReplacementTipMultiplier),
p.proposeBlockTxGasTipCap,
); err != nil {
return nil, err
}
}

Expand Down Expand Up @@ -426,7 +402,10 @@ func (p *Proposer) ProposeTxList(
return nil
}
if tx, err = p.sendProposeBlockTx(ctx, meta, txListBytes, nonce, assignment, fee, isReplacement); err != nil {
log.Warn("Failed to send propose block transaction, retrying", "error", encoding.TryParsingCustomError(err))
log.Warn("Failed to send propose block transaction", "error", encoding.TryParsingCustomError(err))
if strings.Contains(err.Error(), core.ErrNonceTooLow.Error()) {
return nil
}
if strings.Contains(err.Error(), txpool.ErrReplaceUnderpriced.Error()) {
isReplacement = true
} else {
Expand Down
99 changes: 57 additions & 42 deletions prover/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,37 +15,39 @@ import (

// Config contains the configurations to initialize a Taiko prover.
type Config struct {
L1WsEndpoint string
L1HttpEndpoint string
L2WsEndpoint string
L2HttpEndpoint string
TaikoL1Address common.Address
TaikoL2Address common.Address
TaikoTokenAddress common.Address
L1ProverPrivKey *ecdsa.PrivateKey
ZKEvmRpcdEndpoint string
ZkEvmRpcdParamsPath string
StartingBlockID *big.Int
MaxConcurrentProvingJobs uint
Dummy bool
OracleProver bool
OracleProverPrivateKey *ecdsa.PrivateKey
OracleProofSubmissionDelay time.Duration
ProofSubmissionMaxRetry uint64
Graffiti string
RandomDummyProofDelayLowerBound *time.Duration
RandomDummyProofDelayUpperBound *time.Duration
BackOffMaxRetrys uint64
BackOffRetryInterval time.Duration
CheckProofWindowExpiredInterval time.Duration
ProveUnassignedBlocks bool
RPCTimeout *time.Duration
WaitReceiptTimeout time.Duration
ProveBlockGasLimit *uint64
HTTPServerPort uint64
Capacity uint64
MinProofFee *big.Int
MaxExpiry time.Duration
L1WsEndpoint string
L1HttpEndpoint string
L2WsEndpoint string
L2HttpEndpoint string
TaikoL1Address common.Address
TaikoL2Address common.Address
TaikoTokenAddress common.Address
L1ProverPrivKey *ecdsa.PrivateKey
ZKEvmRpcdEndpoint string
ZkEvmRpcdParamsPath string
StartingBlockID *big.Int
MaxConcurrentProvingJobs uint
Dummy bool
OracleProver bool
OracleProverPrivateKey *ecdsa.PrivateKey
OracleProofSubmissionDelay time.Duration
ProofSubmissionMaxRetry uint64
Graffiti string
RandomDummyProofDelayLowerBound *time.Duration
RandomDummyProofDelayUpperBound *time.Duration
BackOffMaxRetrys uint64
BackOffRetryInterval time.Duration
CheckProofWindowExpiredInterval time.Duration
ProveUnassignedBlocks bool
RPCTimeout *time.Duration
WaitReceiptTimeout time.Duration
ProveBlockGasLimit *uint64
ProveBlockTxReplacementMultiplier uint64
ProveBlockMaxTxGasTipCap *big.Int
HTTPServerPort uint64
Capacity uint64
MinProofFee *big.Int
MaxExpiry time.Duration
}

// NewConfigFromCliContext creates a new config instance from command line flags.
Expand All @@ -65,9 +67,7 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) {
return nil, fmt.Errorf("oracleProver flag set without oracleProverPrivateKey set")
}

oracleProverPrivKeyStr := c.String(flags.OracleProverPrivateKey.Name)

oracleProverPrivKey, err = crypto.ToECDSA(common.Hex2Bytes(oracleProverPrivKeyStr))
oracleProverPrivKey, err = crypto.ToECDSA(common.Hex2Bytes(c.String(flags.OracleProverPrivateKey.Name)))
if err != nil {
return nil, fmt.Errorf("invalid oracle private key: %w", err)
}
Expand Down Expand Up @@ -128,6 +128,19 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) {
return nil, fmt.Errorf("invalid minProofFee: %v", minProofFee)
}

proveBlockTxReplacementMultiplier := c.Uint64(flags.ProveBlockTxReplacementMultiplier.Name)
if proveBlockTxReplacementMultiplier == 0 {
return nil, fmt.Errorf(
"invalid --proveBlockTxReplacementMultiplier value: %d",
proveBlockTxReplacementMultiplier,
)
}

var proveBlockMaxTxGasTipCap *big.Int
if c.IsSet(flags.ProveBlockMaxTxGasTipCap.Name) {
proveBlockMaxTxGasTipCap = new(big.Int).SetUint64(c.Uint64(flags.ProveBlockMaxTxGasTipCap.Name))
}

return &Config{
L1WsEndpoint: c.String(flags.L1WSEndpoint.Name),
L1HttpEndpoint: c.String(flags.L1HTTPEndpoint.Name),
Expand All @@ -154,13 +167,15 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) {
CheckProofWindowExpiredInterval: time.Duration(
c.Uint64(flags.CheckProofWindowExpiredInterval.Name),
) * time.Second,
ProveUnassignedBlocks: c.Bool(flags.ProveUnassignedBlocks.Name),
RPCTimeout: timeout,
WaitReceiptTimeout: time.Duration(c.Uint64(flags.WaitReceiptTimeout.Name)) * time.Second,
ProveBlockGasLimit: proveBlockTxGasLimit,
Capacity: c.Uint64(flags.ProverCapacity.Name),
HTTPServerPort: c.Uint64(flags.ProverHTTPServerPort.Name),
MinProofFee: minProofFee,
MaxExpiry: time.Duration(c.Uint64(flags.MaxExpiry.Name)) * time.Second,
ProveUnassignedBlocks: c.Bool(flags.ProveUnassignedBlocks.Name),
RPCTimeout: timeout,
WaitReceiptTimeout: time.Duration(c.Uint64(flags.WaitReceiptTimeout.Name)) * time.Second,
ProveBlockGasLimit: proveBlockTxGasLimit,
Capacity: c.Uint64(flags.ProverCapacity.Name),
ProveBlockTxReplacementMultiplier: proveBlockTxReplacementMultiplier,
ProveBlockMaxTxGasTipCap: proveBlockMaxTxGasTipCap,
HTTPServerPort: c.Uint64(flags.ProverHTTPServerPort.Name),
MinProofFee: minProofFee,
MaxExpiry: time.Duration(c.Uint64(flags.MaxExpiry.Name)) * time.Second,
}, nil
}
44 changes: 25 additions & 19 deletions prover/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,32 +50,36 @@ func (s *ProverTestSuite) TestNewConfigFromCliContext_OracleProver() {
s.Equal(rpcTimeout, *c.RPCTimeout)
s.Equal(uint64(8), c.Capacity)
s.Equal(minProofFee, c.MinProofFee.String())
s.Equal(uint64(3), c.ProveBlockTxReplacementMultiplier)
s.Equal(uint64(256), c.ProveBlockMaxTxGasTipCap.Uint64())
s.Nil(new(Prover).InitFromCli(context.Background(), ctx))

return err
}

s.Nil(app.Run([]string{
"TestNewConfigFromCliContext_OracleProver",
"-" + flags.L1WSEndpoint.Name, l1WsEndpoint,
"-" + flags.L1HTTPEndpoint.Name, l1HttpEndpoint,
"-" + flags.L2WSEndpoint.Name, l2WsEndpoint,
"-" + flags.L2HTTPEndpoint.Name, l2HttpEndpoint,
"-" + flags.TaikoL1Address.Name, taikoL1,
"-" + flags.TaikoL2Address.Name, taikoL2,
"-" + flags.L1ProverPrivKey.Name, os.Getenv("L1_PROVER_PRIVATE_KEY"),
"-" + flags.StartingBlockID.Name, "0",
"-" + flags.RPCTimeout.Name, "5",
"-" + flags.ProveBlockTxGasLimit.Name, "100000",
"-" + flags.Dummy.Name,
"-" + flags.RandomDummyProofDelay.Name, "30m-1h",
"-" + flags.MinProofFee.Name, minProofFee,
"-" + flags.ProverCapacity.Name, "8",
"-" + flags.OracleProver.Name,
"-" + flags.OracleProverPrivateKey.Name, os.Getenv("L1_PROVER_PRIVATE_KEY"),
"-" + flags.Graffiti.Name, "",
"-" + flags.CheckProofWindowExpiredInterval.Name, "30",
"-" + flags.ProveUnassignedBlocks.Name, "true",
"--" + flags.L1WSEndpoint.Name, l1WsEndpoint,
"--" + flags.L1HTTPEndpoint.Name, l1HttpEndpoint,
"--" + flags.L2WSEndpoint.Name, l2WsEndpoint,
"--" + flags.L2HTTPEndpoint.Name, l2HttpEndpoint,
"--" + flags.TaikoL1Address.Name, taikoL1,
"--" + flags.TaikoL2Address.Name, taikoL2,
"--" + flags.L1ProverPrivKey.Name, os.Getenv("L1_PROVER_PRIVATE_KEY"),
"--" + flags.StartingBlockID.Name, "0",
"--" + flags.RPCTimeout.Name, "5",
"--" + flags.ProveBlockTxGasLimit.Name, "100000",
"--" + flags.Dummy.Name,
"--" + flags.RandomDummyProofDelay.Name, "30m-1h",
"--" + flags.MinProofFee.Name, minProofFee,
"--" + flags.ProverCapacity.Name, "8",
"--" + flags.OracleProver.Name,
"--" + flags.ProveBlockTxReplacementMultiplier.Name, "3",
"--" + flags.ProveBlockMaxTxGasTipCap.Name, "256",
"--" + flags.OracleProverPrivateKey.Name, os.Getenv("L1_PROVER_PRIVATE_KEY"),
"--" + flags.Graffiti.Name, "",
"--" + flags.CheckProofWindowExpiredInterval.Name, "30",
"--" + flags.ProveUnassignedBlocks.Name,
}))
}

Expand Down Expand Up @@ -190,6 +194,8 @@ func (s *ProverTestSuite) SetupApp() *cli.App {
&cli.StringFlag{Name: flags.Graffiti.Name},
&cli.Uint64Flag{Name: flags.CheckProofWindowExpiredInterval.Name},
&cli.BoolFlag{Name: flags.ProveUnassignedBlocks.Name},
&cli.Uint64Flag{Name: flags.ProveBlockTxReplacementMultiplier.Name},
&cli.Uint64Flag{Name: flags.ProveBlockMaxTxGasTipCap.Name},
&cli.Uint64Flag{Name: flags.RPCTimeout.Name},
&cli.Uint64Flag{Name: flags.ProverCapacity.Name},
&cli.Uint64Flag{Name: flags.MinProofFee.Name},
Expand Down
Loading

0 comments on commit d5cf89d

Please sign in to comment.