Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 26 additions & 19 deletions op-batcher/batcher/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -843,27 +843,32 @@ func (l *BatchSubmitter) sendTransaction(txdata txData, queue *txmgr.Queue[txRef
// sendTx uses the txmgr queue to send the given transaction candidate after setting its
// gaslimit. It will block if the txmgr queue has reached its MaxPendingTransactions limit.
func (l *BatchSubmitter) sendTx(txdata txData, isCancel bool, candidate *txmgr.TxCandidate, queue *txmgr.Queue[txRef], receiptsCh chan txmgr.TxReceipt[txRef]) {
isEOAPointer := l.inboxIsEOA.Load()
if isEOAPointer == nil {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
defer cancel()
var code []byte
code, err := l.L1Client.CodeAt(ctx, *candidate.To, nil)
if err != nil {
l.Log.Error("CodeAt failed, assuming code exists", "err", err)
// assume code exist, but don't persist the result
isEOA := false
isEOAPointer = &isEOA
} else {
isEOA := len(code) == 0
isEOAPointer = &isEOA
l.inboxIsEOA.Store(isEOAPointer)
var isEOAPointer *bool
if l.RollupConfig.UseInboxContract() {
// RollupConfig.UseInboxContract() being true just means the batcher's transaction status matters,
// but the actual inbox may still be an EOA.
isEOAPointer = l.inboxIsEOA.Load()
if isEOAPointer == nil {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
defer cancel()
var code []byte
code, err := l.L1Client.CodeAt(ctx, *candidate.To, nil)
if err != nil {
l.Log.Error("CodeAt failed, assuming code exists", "err", err)
// assume code exist, but don't persist the result
isEOA := false
isEOAPointer = &isEOA
} else {
isEOA := len(code) == 0
isEOAPointer = &isEOA
l.inboxIsEOA.Store(isEOAPointer)
}
}

}

// Set GasLimit as intrinstic gas if the inbox is EOA, otherwise
// Leave GasLimit unset when inbox is contract so that later on `EstimateGas` will be called
if *isEOAPointer {
if !l.RollupConfig.UseInboxContract() || *isEOAPointer {
intrinsicGas, err := core.IntrinsicGas(candidate.TxData, nil, false, true, true, false)
if err != nil {
// we log instead of return an error here because txmgr can do its own gas estimation
Expand Down Expand Up @@ -906,7 +911,7 @@ func (l *BatchSubmitter) handleReceipt(r txmgr.TxReceipt[txRef]) {
l.recordFailedTx(r.ID.id, r.Err)
} else {
// check tx status
if r.Receipt.Status == types.ReceiptStatusFailed {
if l.RollupConfig.UseInboxContract() && r.Receipt.Status == types.ReceiptStatusFailed {
l.recordFailedTx(r.ID.id, ErrInboxTransactionFailed)
return
}
Expand All @@ -931,7 +936,9 @@ func (l *BatchSubmitter) recordFailedDARequest(id txID, err error) {
}

func (l *BatchSubmitter) recordFailedTx(id txID, err error) {
l.inboxIsEOA.Store(nil)
if l.RollupConfig.UseInboxContract() {
l.inboxIsEOA.Store(nil)
}
l.Log.Warn("Transaction failed to send", logFields(id, err)...)
l.state.TxFailed(id)
}
Expand Down
12 changes: 12 additions & 0 deletions op-chain-ops/genesis/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,7 @@ type L2InitializationConfig struct {
L2CoreDeployConfig
AltDADeployConfig
SoulGasTokenConfig
InboxContractConfig
}

func (d *L2InitializationConfig) Check(log log.Logger) error {
Expand Down Expand Up @@ -843,6 +844,12 @@ type SoulGasTokenConfig struct {
IsSoulBackedByNative bool `json:"isSoulBackedByNative,omitempty"`
}

// InboxContractConfig configures whether inbox contract is enabled.
// If enabled, the batcher tx will be further filtered by tx status.
type InboxContractConfig struct {
UseInboxContract bool `json:"useInboxContract,omitempty"`
}

// DependencyContext is the contextual configuration needed to verify the L1 dependencies,
// used by DeployConfig.CheckAddresses.
type DependencyContext struct {
Expand Down Expand Up @@ -1011,6 +1018,10 @@ func (d *DeployConfig) RollupConfig(l1StartBlock *types.Header, l2GenesisBlockHa
L2BlobTime: d.L2BlobTime(l1StartTime),
}
}
var inboxContractConfig *rollup.InboxContractConfig
if d.UseInboxContract {
inboxContractConfig = &rollup.InboxContractConfig{UseInboxContract: true}
}

return &rollup.Config{
Genesis: rollup.Genesis{
Expand Down Expand Up @@ -1050,6 +1061,7 @@ func (d *DeployConfig) RollupConfig(l1StartBlock *types.Header, l2GenesisBlockHa
ProtocolVersionsAddress: d.ProtocolVersionsProxy,
AltDAConfig: altDA,
L2BlobConfig: l2BlobConfig,
InboxContractConfig: inboxContractConfig,
}, nil
}

Expand Down
33 changes: 6 additions & 27 deletions op-node/rollup/derive/altda_data_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,6 @@ func TestAltDADataSource(t *testing.T) {
nc := 0
firstChallengeExpirationBlock := uint64(95)

// for reusing after pipeline is reset
successfulReceipts := make(map[common.Hash]types.Receipts)
for i := uint64(0); i <= pcfg.ChallengeWindow+pcfg.ResolveWindow; i++ {
parent := l1Refs[len(l1Refs)-1]
// create a new mock l1 ref
Expand All @@ -119,6 +117,8 @@ func TestAltDADataSource(t *testing.T) {
}
l1Refs = append(l1Refs, ref)
logger.Info("new l1 block", "ref", ref)
// called for each l1 block to sync challenges
l1F.ExpectFetchReceipts(ref.Hash, nil, types.Receipts{}, nil)

// pick a random number of commitments to include in the l1 block
c := rng.Intn(4)
Expand Down Expand Up @@ -149,12 +149,6 @@ func TestAltDADataSource(t *testing.T) {
txs = append(txs, tx)

}

successfulReceipts[ref.Hash] = successfulReceiptsForTxs(txs)
// called by `getTxSucceed` to fetch tx status
l1F.ExpectFetchReceipts(ref.Hash, nil, successfulReceipts[ref.Hash], nil)
// called for each l1 block to sync challenges
l1F.ExpectFetchReceipts(ref.Hash, nil, types.Receipts{}, nil)
logger.Info("included commitments", "count", c)
l1F.ExpectInfoAndTxsByHash(ref.Hash, testutils.RandomBlockInfo(rng), txs, nil)
// called once per derivation
Expand Down Expand Up @@ -211,8 +205,6 @@ func TestAltDADataSource(t *testing.T) {
ref = l1Refs[i]
logger.Info("re deriving block", "ref", ref, "i", i)

// called by `getTxSucceed` to fetch tx status
l1F.ExpectFetchReceipts(ref.Hash, nil, successfulReceipts[ref.Hash], nil)
if i == len(l1Refs)-1 {
l1F.ExpectFetchReceipts(ref.Hash, nil, types.Receipts{}, nil)
}
Expand All @@ -228,6 +220,8 @@ func TestAltDADataSource(t *testing.T) {
}
l1Refs = append(l1Refs, ref)
logger.Info("new l1 block", "ref", ref)
// called for each l1 block to sync challenges
l1F.ExpectFetchReceipts(ref.Hash, nil, types.Receipts{}, nil)

// pick a random number of commitments to include in the l1 block
c := rng.Intn(4)
Expand Down Expand Up @@ -257,10 +251,6 @@ func TestAltDADataSource(t *testing.T) {
txs = append(txs, tx)

}
// called by `getTxSucceed` to fetch tx status
l1F.ExpectFetchReceipts(ref.Hash, nil, successfulReceiptsForTxs(txs), nil)
// called for each l1 block to sync challenges
l1F.ExpectFetchReceipts(ref.Hash, nil, types.Receipts{}, nil)
logger.Info("included commitments", "count", c)
l1F.ExpectInfoAndTxsByHash(ref.Hash, testutils.RandomBlockInfo(rng), txs, nil)
}
Expand Down Expand Up @@ -292,13 +282,6 @@ func TestAltDADataSource(t *testing.T) {
finalitySignal.AssertExpectations(t)
}

func successfulReceiptsForTxs(txs []*types.Transaction) (receipts types.Receipts) {
for _, tx := range txs {
receipts = append(receipts, &types.Receipt{TxHash: tx.Hash(), Status: types.ReceiptStatusSuccessful})
}
return
}

// This tests makes sure the pipeline returns a temporary error if data is not found.
func TestAltDADataSourceStall(t *testing.T) {
logger := testlog.Logger(t, log.LevelDebug)
Expand Down Expand Up @@ -367,6 +350,7 @@ func TestAltDADataSourceStall(t *testing.T) {
ParentHash: parent.Hash,
Time: parent.Time + l1Time,
}
l1F.ExpectFetchReceipts(ref.Hash, nil, types.Receipts{}, nil)
// mock input commitments in l1 transactions
input := testutils.RandomData(rng, 2000)
comm, _ := storage.SetInput(ctx, input)
Expand All @@ -385,9 +369,6 @@ func TestAltDADataSourceStall(t *testing.T) {

txs := []*types.Transaction{tx}

// called by `getTxSucceed` to fetch tx status
l1F.ExpectFetchReceipts(ref.Hash, nil, successfulReceiptsForTxs(txs), nil)
l1F.ExpectFetchReceipts(ref.Hash, nil, types.Receipts{}, nil)
l1F.ExpectInfoAndTxsByHash(ref.Hash, testutils.RandomBlockInfo(rng), txs, nil)

// delete the input from the DA provider so it returns not found
Expand Down Expand Up @@ -491,6 +472,7 @@ func TestAltDADataSourceInvalidData(t *testing.T) {
ParentHash: parent.Hash,
Time: parent.Time + l1Time,
}
l1F.ExpectFetchReceipts(ref.Hash, nil, types.Receipts{}, nil)
// mock input commitments in l1 transactions with an oversized input
input := testutils.RandomData(rng, altda.MaxInputSize+1)
comm, _ := storage.SetInput(ctx, input)
Expand Down Expand Up @@ -538,9 +520,6 @@ func TestAltDADataSourceInvalidData(t *testing.T) {

txs := []*types.Transaction{tx1, tx2, tx3}

// called by `getTxSucceed` to fetch tx status
l1F.ExpectFetchReceipts(ref.Hash, nil, successfulReceiptsForTxs(txs), nil)
l1F.ExpectFetchReceipts(ref.Hash, nil, types.Receipts{}, nil)
l1F.ExpectInfoAndTxsByHash(ref.Hash, testutils.RandomBlockInfo(rng), txs, nil)

src, err := factory.OpenData(ctx, ref, batcherAddr)
Expand Down
10 changes: 7 additions & 3 deletions op-node/rollup/derive/blob_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,12 @@ func (ds *BlobDataSource) Next(ctx context.Context) (eth.Data, error) {
return data, nil
}

// getTxSucceed returns a non-nil map which contains all successful tx hashes to batch inbox
func getTxSucceed(ctx context.Context, fetcher L1Fetcher, hash common.Hash, txs types.Transactions) (successTxs types.Transactions, err error) {
// getTxSucceed returns all successful txs
func getTxSucceed(ctx context.Context, useInboxContract bool, fetcher L1Fetcher, hash common.Hash, txs types.Transactions) (successTxs types.Transactions, err error) {
if !useInboxContract {
// if !useInboxContract, all txs are considered successful
return txs, nil
}
_, receipts, err := fetcher.FetchReceipts(ctx, hash)
if err != nil {
return nil, NewTemporaryError(fmt.Errorf("failed to fetch L1 block info and receipts: %w", err))
Expand Down Expand Up @@ -107,7 +111,7 @@ func (ds *BlobDataSource) open(ctx context.Context) ([]blobOrCalldata, error) {
}
return nil, NewTemporaryError(fmt.Errorf("failed to open blob data source: %w", err))
}
txs, err = getTxSucceed(ctx, ds.fetcher, ds.ref.Hash, txs)
txs, err = getTxSucceed(ctx, ds.dsCfg.useInboxContract, ds.fetcher, ds.ref.Hash, txs)
if err != nil {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions op-node/rollup/derive/calldata_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func NewCalldataSource(ctx context.Context, log log.Logger, dsCfg DataSourceConf
batcherAddr: batcherAddr,
}
}
txs, err = getTxSucceed(ctx, fetcher, ref.Hash, txs)
txs, err = getTxSucceed(ctx, dsCfg.useInboxContract, fetcher, ref.Hash, txs)
if err != nil {
return &CalldataSource{
open: false,
Expand All @@ -67,7 +67,7 @@ func NewCalldataSource(ctx context.Context, log log.Logger, dsCfg DataSourceConf
func (ds *CalldataSource) Next(ctx context.Context) (eth.Data, error) {
if !ds.open {
if _, txs, err := ds.fetcher.InfoAndTxsByHash(ctx, ds.ref.Hash); err == nil {
txs, err := getTxSucceed(ctx, ds.fetcher, ds.ref.Hash, txs)
txs, err := getTxSucceed(ctx, ds.dsCfg.useInboxContract, ds.fetcher, ds.ref.Hash, txs)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion op-node/rollup/derive/calldata_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func TestDataFromEVMTransactions(t *testing.T) {
}
}

out := DataFromEVMTransactions(DataSourceConfig{cfg.L1Signer(), cfg.BatchInboxAddress, false}, batcherAddr, txs, testlog.Logger(t, log.LevelCrit))
out := DataFromEVMTransactions(DataSourceConfig{cfg.L1Signer(), cfg.BatchInboxAddress, false, false}, batcherAddr, txs, testlog.Logger(t, log.LevelCrit))
require.ElementsMatch(t, expectedData, out)
}

Expand Down
2 changes: 2 additions & 0 deletions op-node/rollup/derive/data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ func NewDataSourceFactory(log log.Logger, cfg *rollup.Config, fetcher L1Fetcher,
l1Signer: cfg.L1Signer(),
batchInboxAddress: cfg.BatchInboxAddress,
altDAEnabled: cfg.AltDAEnabled(),
useInboxContract: cfg.UseInboxContract(),
}
return &DataSourceFactory{
log: log,
Expand Down Expand Up @@ -88,6 +89,7 @@ type DataSourceConfig struct {
l1Signer types.Signer
batchInboxAddress common.Address
altDAEnabled bool
useInboxContract bool
}

// isValidBatchTx returns true if:
Expand Down
12 changes: 11 additions & 1 deletion op-node/rollup/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,18 +140,28 @@ type Config struct {
// AltDAConfig. We are in the process of migrating to the AltDAConfig from these legacy top level values
AltDAConfig *AltDAConfig `json:"alt_da,omitempty"`

L2BlobConfig *L2BlobConfig `json:"l2_blob_config,omitempty"`
L2BlobConfig *L2BlobConfig `json:"l2_blob_config,omitempty"`
InboxContractConfig *InboxContractConfig `json:"inbox_contract_config,omitempty"`
}

type L2BlobConfig struct {
L2BlobTime *uint64 `json:"l2BlobTime,omitempty"`
}

type InboxContractConfig struct {
UseInboxContract bool `json:"use_inbox_contract,omitempty"`
}

// IsL2Blob returns whether l2 blob is enabled
func (cfg *Config) IsL2Blob(parentTime uint64) bool {
return cfg.IsL2BlobTimeSet() && *cfg.L2BlobConfig.L2BlobTime <= parentTime
}

// UseInboxContract returns whether inbox contract is enabled
func (cfg *Config) UseInboxContract() bool {
return cfg.InboxContractConfig != nil && cfg.InboxContractConfig.UseInboxContract
}

// IsL2BlobTimeSet returns whether l2 blob activation time is set
func (cfg *Config) IsL2BlobTimeSet() bool {
return cfg.L2BlobConfig != nil && cfg.L2BlobConfig.L2BlobTime != nil
Expand Down