Skip to content

Commit

Permalink
feat(taiko-client): introduce TxBuilderWithFallback
Browse files Browse the repository at this point in the history
  • Loading branch information
davidtaikocha committed Jan 2, 2025
1 parent abc0554 commit 0bfb03c
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 30 deletions.
8 changes: 8 additions & 0 deletions packages/taiko-client/cmd/flags/proposer.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,13 @@ var (
Value: false,
EnvVars: []string{"L1_BLOB_ALLOWED"},
}
FallbackToCalldata = &cli.BoolFlag{
Name: "l1.fallbackToCalldata",
Usage: "If set to true, proposer will use calldata as DA when blob fee is more expensive than using calldata",
Value: false,
Category: proposerCategory,
EnvVars: []string{"L1_FALLBACK_TO_CALLDATA"},
}
RevertProtectionEnabled = &cli.BoolFlag{
Name: "revertProtection",
Usage: "Enable revert protection with the support of endpoint and contract",
Expand Down Expand Up @@ -133,5 +140,6 @@ var ProposerFlags = MergeFlags(CommonFlags, []cli.Flag{
AllowZeroInterval,
MaxProposedTxListsPerEpoch,
BlobAllowed,
FallbackToCalldata,
RevertProtectionEnabled,
}, TxmgrFlags)
2 changes: 1 addition & 1 deletion packages/taiko-client/pkg/rpc/methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,7 @@ func (c *Client) checkSyncedL1SnippetFromAnchor(
blockID *big.Int,
l1Height uint64,
) (bool, error) {
log.Info("Check synced L1 snippet from anchor", "blockID", blockID, "l1Height", l1Height)
log.Debug("Check synced L1 snippet from anchor", "blockID", blockID, "l1Height", l1Height)
block, err := c.L2.BlockByNumber(ctx, blockID)
if err != nil {
return false, err
Expand Down
2 changes: 2 additions & 0 deletions packages/taiko-client/proposer/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type Config struct {
MaxProposedTxListsPerEpoch uint64
ProposeBlockTxGasLimit uint64
BlobAllowed bool
FallbackToCalldata bool
RevertProtectionEnabled bool
TxmgrConfigs *txmgr.CLIConfig
PrivateTxmgrConfigs *txmgr.CLIConfig
Expand Down Expand Up @@ -104,6 +105,7 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) {
AllowZeroInterval: c.Uint64(flags.AllowZeroInterval.Name),
ProposeBlockTxGasLimit: c.Uint64(flags.TxGasLimit.Name),
BlobAllowed: c.Bool(flags.BlobAllowed.Name),
FallbackToCalldata: c.Bool(flags.FallbackToCalldata.Name),
RevertProtectionEnabled: c.Bool(flags.RevertProtectionEnabled.Name),
TxmgrConfigs: pkgFlags.InitTxmgrConfigsFromCli(
c.String(flags.L1WSEndpoint.Name),
Expand Down
44 changes: 15 additions & 29 deletions packages/taiko-client/proposer/proposer.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,35 +114,21 @@ func (p *Proposer) InitFromConfig(
}

p.txmgrSelector = utils.NewTxMgrSelector(txMgr, privateTxMgr, nil)

chainConfig := config.NewChainConfig(p.protocolConfigs)
p.chainConfig = chainConfig

if cfg.BlobAllowed {
p.txBuilder = builder.NewBlobTransactionBuilder(
p.rpc,
p.L1ProposerPrivKey,
cfg.TaikoL1Address,
cfg.ProverSetAddress,
cfg.L2SuggestedFeeRecipient,
cfg.ProposeBlockTxGasLimit,
cfg.ExtraData,
chainConfig,
cfg.RevertProtectionEnabled,
)
} else {
p.txBuilder = builder.NewCalldataTransactionBuilder(
p.rpc,
p.L1ProposerPrivKey,
cfg.L2SuggestedFeeRecipient,
cfg.TaikoL1Address,
cfg.ProverSetAddress,
cfg.ProposeBlockTxGasLimit,
cfg.ExtraData,
chainConfig,
cfg.RevertProtectionEnabled,
)
}
p.chainConfig = config.NewChainConfig(p.protocolConfigs)
p.txBuilder = builder.NewBuilderWithFallback(
p.rpc,
p.L1ProposerPrivKey,
cfg.L2SuggestedFeeRecipient,
cfg.TaikoL1Address,
cfg.ProverSetAddress,
cfg.ProposeBlockTxGasLimit,
cfg.ExtraData,
p.chainConfig,
p.txmgrSelector,
cfg.RevertProtectionEnabled,
cfg.BlobAllowed,
cfg.FallbackToCalldata,
)

return nil
}
Expand Down
164 changes: 164 additions & 0 deletions packages/taiko-client/proposer/transaction_builder/fallback.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package builder

import (
"context"
"crypto/ecdsa"
"fmt"
"math/big"

"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/config"
"github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/rpc"
"github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/utils"
)

// TxBuilderWithFallback builds type-2 or type-3 transactions based on the
// the realtime onchain cost, if the fallback feature is enabled.
type TxBuilderWithFallback struct {
rpc *rpc.Client
blobTransactionBuilder *BlobTransactionBuilder
calldataTransactionBuilder *CalldataTransactionBuilder
txmgrSelector *utils.TxMgrSelector
fallback bool
}

// NewBuilderWithFallback creates a new TxBuilderWithFallback instance.
func NewBuilderWithFallback(
rpc *rpc.Client,
proposerPrivateKey *ecdsa.PrivateKey,
l2SuggestedFeeRecipient common.Address,
taikoL1Address common.Address,
proverSetAddress common.Address,
gasLimit uint64,
extraData string,
chainConfig *config.ChainConfig,
txmgrSelector *utils.TxMgrSelector,
revertProtectionEnabled bool,
blobAllowed bool,
fallback bool,
) *TxBuilderWithFallback {
builder := &TxBuilderWithFallback{
rpc: rpc,
fallback: fallback,
txmgrSelector: txmgrSelector,
}

if blobAllowed {
builder.blobTransactionBuilder = NewBlobTransactionBuilder(
rpc,
proposerPrivateKey,
taikoL1Address,
proverSetAddress,
l2SuggestedFeeRecipient,
gasLimit,
extraData,
chainConfig,
revertProtectionEnabled,
)
}

builder.calldataTransactionBuilder = NewCalldataTransactionBuilder(
rpc,
proposerPrivateKey,
l2SuggestedFeeRecipient,
taikoL1Address,
proverSetAddress,
gasLimit,
extraData,
chainConfig,
revertProtectionEnabled,
)

return builder
}

// BuildOntake builds a type-2 or type-3 transaction based on the
// the realtime onchain cost, if the fallback feature is enabled.
func (b *TxBuilderWithFallback) BuildOntake(
ctx context.Context,
txListBytesArray [][]byte,
) (*txmgr.TxCandidate, error) {
// If calldata is the only option, just use it.
if b.blobTransactionBuilder == nil {
return b.calldataTransactionBuilder.BuildOntake(ctx, txListBytesArray)
}
// If blob is enabled, and fallback is not enabled, just build a blob transaction.
if !b.fallback {
return b.blobTransactionBuilder.BuildOntake(ctx, txListBytesArray)
}
// Otherwise, compare the cost, and choose the cheaper option.
txWithCalldata, err := b.calldataTransactionBuilder.BuildOntake(ctx, txListBytesArray)
if err != nil {
return nil, err
}
txWithBlob, err := b.blobTransactionBuilder.BuildOntake(ctx, txListBytesArray)
if err != nil {
return nil, err
}

costCalldata, err := b.estimateCandidateCost(ctx, txWithCalldata)
if err != nil {
return nil, err
}
costBlob, err := b.estimateCandidateCost(ctx, txWithBlob)
if err != nil {
return nil, err
}

if costCalldata.Cmp(costBlob) < 1 {
log.Info("Building a type-2 transaction", "costCalldata", costCalldata, "costBlob", costBlob)
return txWithCalldata, nil
}

log.Info("Building a type-3 transaction", "costCalldata", costCalldata, "costBlob", costBlob)
return txWithBlob, nil
}

// estimateCandidateCost estimates the realtime onchain cost of the given transaction.
func (b *TxBuilderWithFallback) estimateCandidateCost(
ctx context.Context,
candidate *txmgr.TxCandidate,
) (*big.Int, error) {
txmgr, _ := b.txmgrSelector.Select()
gasTipCap, baseFee, blobBaseFee, err := txmgr.SuggestGasPriceCaps(ctx)
if err != nil {
return nil, err
}
log.Info("Suggested gas price", "gasTipCap", gasTipCap, "baseFee", baseFee, "blobBaseFee", blobBaseFee)

gasPrice := new(big.Int).Add(baseFee, gasTipCap)
gasUsed, err := b.rpc.L1.EstimateGas(ctx, ethereum.CallMsg{
From: txmgr.From(),
To: candidate.To,
Gas: candidate.GasLimit,
GasPrice: gasPrice,
GasFeeCap: gasPrice,
GasTipCap: gasTipCap,
Value: candidate.Value,
Data: candidate.TxData,
})
if err != nil {
return nil, fmt.Errorf("failed to estimate gas used: %w", err)
}

feeWithoutBlob := new(big.Int).Mul(gasPrice, new(big.Int).SetUint64(gasUsed))

// If its a type-2 transaction, we won't calculate blob fee.
if len(candidate.Blobs) == 0 {
return feeWithoutBlob, nil
}

// Otherwise, we add blob fee to the cost.
return new(big.Int).Add(
feeWithoutBlob,
new(big.Int).Mul(new(big.Int).SetUint64(uint64(len(candidate.Blobs))), blobBaseFee),
), nil
}

// TxBuilderWithFallback returns whether the blob transactions is enabled.
func (b *TxBuilderWithFallback) BlobAllow() bool {
return b.blobTransactionBuilder != nil
}

0 comments on commit 0bfb03c

Please sign in to comment.