Commit 2006da2e authored by protolambda's avatar protolambda Committed by GitHub

Merge pull request #7813 from testinprod-io/tip/span-batch-hard-fork-condition-spec-refactor

Span Batch Hard Fork Activation Rule Update
parents fea98f4d 6646032d
...@@ -47,10 +47,6 @@ func CheckBatch(ctx context.Context, cfg *rollup.Config, log log.Logger, l1Block ...@@ -47,10 +47,6 @@ func CheckBatch(ctx context.Context, cfg *rollup.Config, log log.Logger, l1Block
log.Error("failed type assertion to SpanBatch") log.Error("failed type assertion to SpanBatch")
return BatchDrop return BatchDrop
} }
if !cfg.IsSpanBatch(batch.Batch.GetTimestamp()) {
log.Warn("received SpanBatch before SpanBatch hard fork")
return BatchDrop
}
return checkSpanBatch(ctx, cfg, log, l1Blocks, l2SafeHead, spanBatch, batch.L1InclusionBlock, l2Fetcher) return checkSpanBatch(ctx, cfg, log, l1Blocks, l2SafeHead, spanBatch, batch.L1InclusionBlock, l2Fetcher)
default: default:
log.Warn("Unrecognized batch type: %d", batch.Batch.GetBatchType()) log.Warn("Unrecognized batch type: %d", batch.Batch.GetBatchType())
...@@ -181,6 +177,20 @@ func checkSpanBatch(ctx context.Context, cfg *rollup.Config, log log.Logger, l1B ...@@ -181,6 +177,20 @@ func checkSpanBatch(ctx context.Context, cfg *rollup.Config, log log.Logger, l1B
} }
epoch := l1Blocks[0] epoch := l1Blocks[0]
startEpochNum := uint64(batch.GetStartEpochNum())
batchOrigin := epoch
if startEpochNum == batchOrigin.Number+1 {
if len(l1Blocks) < 2 {
log.Info("eager batch wants to advance epoch, but could not without more L1 blocks", "current_epoch", epoch.ID())
return BatchUndecided
}
batchOrigin = l1Blocks[1]
}
if !cfg.IsSpanBatch(batchOrigin.Time) {
log.Warn("received SpanBatch with L1 origin before SpanBatch hard fork")
return BatchDrop
}
nextTimestamp := l2SafeHead.Time + cfg.BlockTime nextTimestamp := l2SafeHead.Time + cfg.BlockTime
if batch.GetTimestamp() > nextTimestamp { if batch.GetTimestamp() > nextTimestamp {
...@@ -220,8 +230,6 @@ func checkSpanBatch(ctx context.Context, cfg *rollup.Config, log log.Logger, l1B ...@@ -220,8 +230,6 @@ func checkSpanBatch(ctx context.Context, cfg *rollup.Config, log log.Logger, l1B
return BatchDrop return BatchDrop
} }
startEpochNum := uint64(batch.GetStartEpochNum())
// Filter out batches that were included too late. // Filter out batches that were included too late.
if startEpochNum+cfg.SeqWindowSize < l1InclusionBlock.Number { if startEpochNum+cfg.SeqWindowSize < l1InclusionBlock.Number {
log.Warn("batch was included too late, sequence window expired") log.Warn("batch was included too late, sequence window expired")
......
...@@ -711,6 +711,33 @@ func TestValidBatch(t *testing.T) { ...@@ -711,6 +711,33 @@ func TestValidBatch(t *testing.T) {
}), }),
}, },
Expected: BatchUndecided, Expected: BatchUndecided,
ExpectedLog: "eager batch wants to advance epoch, but could not without more L1 blocks",
SpanBatchTime: &minTs,
},
{
Name: "insufficient L1 info for eager derivation - long span",
L1Blocks: []eth.L1BlockRef{l1A}, // don't know about l1B yet
L2SafeHead: l2A2,
Batch: BatchWithL1InclusionBlock{
L1InclusionBlock: l1C,
Batch: NewSpanBatch([]*SingularBatch{
{
ParentHash: l2A3.ParentHash,
EpochNum: rollup.Epoch(l2A3.L1Origin.Number),
EpochHash: l2A3.L1Origin.Hash,
Timestamp: l2A3.Time,
Transactions: nil,
},
{
ParentHash: l2B0.ParentHash,
EpochNum: rollup.Epoch(l2B0.L1Origin.Number),
EpochHash: l2B0.L1Origin.Hash,
Timestamp: l2B0.Time,
Transactions: nil,
},
}),
},
Expected: BatchUndecided,
ExpectedLog: "need more l1 blocks to check entire origins of span batch", ExpectedLog: "need more l1 blocks to check entire origins of span batch",
SpanBatchTime: &minTs, SpanBatchTime: &minTs,
}, },
...@@ -1413,7 +1440,7 @@ func TestValidBatch(t *testing.T) { ...@@ -1413,7 +1440,7 @@ func TestValidBatch(t *testing.T) {
Transactions: []hexutil.Bytes{randTxData}, Transactions: []hexutil.Bytes{randTxData},
}, },
}, },
SpanBatchTime: &l2A2.Time, SpanBatchTime: &l1B.Time,
Expected: BatchAccept, Expected: BatchAccept,
}, },
{ {
...@@ -1432,8 +1459,9 @@ func TestValidBatch(t *testing.T) { ...@@ -1432,8 +1459,9 @@ func TestValidBatch(t *testing.T) {
}, },
}), }),
}, },
SpanBatchTime: &l2A2.Time, SpanBatchTime: &l1B.Time,
Expected: BatchDrop, Expected: BatchDrop,
ExpectedLog: "received SpanBatch with L1 origin before SpanBatch hard fork",
}, },
{ {
Name: "singular batch after hard fork", Name: "singular batch after hard fork",
...@@ -1449,7 +1477,7 @@ func TestValidBatch(t *testing.T) { ...@@ -1449,7 +1477,7 @@ func TestValidBatch(t *testing.T) {
Transactions: []hexutil.Bytes{randTxData}, Transactions: []hexutil.Bytes{randTxData},
}, },
}, },
SpanBatchTime: &l2A0.Time, SpanBatchTime: &l1A.Time,
Expected: BatchAccept, Expected: BatchAccept,
}, },
{ {
...@@ -1468,7 +1496,7 @@ func TestValidBatch(t *testing.T) { ...@@ -1468,7 +1496,7 @@ func TestValidBatch(t *testing.T) {
}, },
}), }),
}, },
SpanBatchTime: &l2A0.Time, SpanBatchTime: &l1A.Time,
Expected: BatchAccept, Expected: BatchAccept,
}, },
} }
......
...@@ -99,6 +99,9 @@ func (cr *ChannelInReader) NextBatch(ctx context.Context) (Batch, error) { ...@@ -99,6 +99,9 @@ func (cr *ChannelInReader) NextBatch(ctx context.Context) (Batch, error) {
return singularBatch, nil return singularBatch, nil
case SpanBatchType: case SpanBatchType:
if origin := cr.Origin(); !cr.cfg.IsSpanBatch(origin.Time) { if origin := cr.Origin(); !cr.cfg.IsSpanBatch(origin.Time) {
// Check hard fork activation with the L1 inclusion block time instead of the L1 origin block time.
// Therefore, even if the batch passed this rule, it can be dropped in the batch queue.
// This is just for early dropping invalid batches as soon as possible.
return nil, NewTemporaryError(fmt.Errorf("cannot accept span batch in L1 block %s at time %d", origin, origin.Time)) return nil, NewTemporaryError(fmt.Errorf("cannot accept span batch in L1 block %s at time %d", origin, origin.Time))
} }
rawSpanBatch, ok := batchData.inner.(*RawSpanBatch) rawSpanBatch, ok := batchData.inner.(*RawSpanBatch)
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
- [Introduction](#introduction) - [Introduction](#introduction)
- [Span batch format](#span-batch-format) - [Span batch format](#span-batch-format)
- [Span batch Activation Rule](#span-batch-activation-rule)
- [Optimization Strategies](#optimization-strategies) - [Optimization Strategies](#optimization-strategies)
- [Truncating information and storing only necessary data](#truncating-information-and-storing-only-necessary-data) - [Truncating information and storing only necessary data](#truncating-information-and-storing-only-necessary-data)
- [`tx_data_headers` removal from initial specs](#tx_data_headers-removal-from-initial-specs) - [`tx_data_headers` removal from initial specs](#tx_data_headers-removal-from-initial-specs)
...@@ -153,6 +154,16 @@ decoding. For example, lets say bad batcher wrote span batch which `block_count ...@@ -153,6 +154,16 @@ decoding. For example, lets say bad batcher wrote span batch which `block_count
the explicit limit, not trying to consume data until EOF is reached. We can also safely preallocate memory for decoding the explicit limit, not trying to consume data until EOF is reached. We can also safely preallocate memory for decoding
because we know the upper limit of memory usage. because we know the upper limit of memory usage.
## Span batch Activation Rule
The span batch upgrade is activated based on timestamp.
Activation Rule: `upgradeTime != null && span_start.l1_origin.timestamp >= upgradeTime`
`span_start.l1_origin.timestamp` is the L1 origin block timestamp of the first block in the span batch.
This rule ensures that every chain activity regarding this span batch is done after the hard fork.
i.e. Every block in the span is created, submitted to the L1, and derived from the L1 after the hard fork.
## Optimization Strategies ## Optimization Strategies
### Truncating information and storing only necessary data ### Truncating information and storing only necessary data
...@@ -261,6 +272,13 @@ Rules are enforced with the [contextual definitions](./derivation.md#batch-queue ...@@ -261,6 +272,13 @@ Rules are enforced with the [contextual definitions](./derivation.md#batch-queue
Span-batch rules, in validation order: Span-batch rules, in validation order:
- `batch_origin` is determined like with singular batches:
- `batch.epoch_num == epoch.number+1`:
- If `next_epoch` is not known -> `undecided`:
i.e. a batch that changes the L1 origin cannot be processed until we have the L1 origin data.
- If known, then define `batch_origin` as `next_epoch`
- `batch_origin.timestamp < span_batch_upgrade_timestamp` -> `drop`:
i.e. enforce the [span batch upgrade activation rule](#span-batch-activation-rule).
- `batch.start_timestamp > next_timestamp` -> `future`: i.e. the batch must be ready to process. - `batch.start_timestamp > next_timestamp` -> `future`: i.e. the batch must be ready to process.
- `batch.start_timestamp < next_timestamp` -> `drop`: i.e. the batch must not be too old. - `batch.start_timestamp < next_timestamp` -> `drop`: i.e. the batch must not be too old.
- `batch.parent_check != safe_l2_head.hash[:20]` -> `drop`: i.e. the checked part of the parent hash must be equal - `batch.parent_check != safe_l2_head.hash[:20]` -> `drop`: i.e. the checked part of the parent hash must be equal
......
...@@ -199,7 +199,10 @@ and are then retrieved from the superchain target configuration. ...@@ -199,7 +199,10 @@ and are then retrieved from the superchain target configuration.
### L2 Block-number based activation (deprecated) ### L2 Block-number based activation (deprecated)
Activation rule: `x != null && x >= upgradeNumber` Activation rule: `upgradeNumber != null && block.number >= upgradeNumber`
Starting at, and including, the L2 `block` with `block.number >= upgradeNumber`, the upgrade rules apply.
If the upgrade block-number `upgradeNumber` is not specified in the configuration, the upgrade is ignored.
This block number based method has commonly been used in L1 up until the Bellatrix/Paris upgrade, a.k.a. The Merge, This block number based method has commonly been used in L1 up until the Bellatrix/Paris upgrade, a.k.a. The Merge,
which was upgraded through special rules. which was upgraded through special rules.
...@@ -207,22 +210,19 @@ which was upgraded through special rules. ...@@ -207,22 +210,19 @@ which was upgraded through special rules.
This method is not superchain-compatible, as the activation-parameter is chain-specific This method is not superchain-compatible, as the activation-parameter is chain-specific
(different chains may have different block-heights at the same moment in time). (different chains may have different block-heights at the same moment in time).
Starting at, and including, the L2 `block` with `block.number == x`, the upgrade rules apply.
If the upgrade block-number `x` is not specified in the configuration, the upgrade is ignored.
This applies to the L2 block number, not to the L1-origin block number. This applies to the L2 block number, not to the L1-origin block number.
This means that an L2 upgrade may be inactive, and then active, without changing the L1-origin. This means that an L2 upgrade may be inactive, and then active, without changing the L1-origin.
### L2 Block-timestamp based activation ### L2 Block-timestamp based activation
Activation rule: `x != null && x >= upgradeTime` Activation rule: `upgradeTime != null && block.timestamp >= upgradeTime`
Starting at, and including, the L2 `block` with `block.timestamp >= upgradeTime`, the upgrade rules apply.
If the upgrade block-timestamp `upgradeTime` is not specified in the configuration, the upgrade is ignored.
This is the preferred superchain upgrade activation-parameter type: This is the preferred superchain upgrade activation-parameter type:
it is synchronous between all L2 chains and compatible with post-Merge timestamp-based chain upgrades in L1. it is synchronous between all L2 chains and compatible with post-Merge timestamp-based chain upgrades in L1.
Starting at, and including, the L2 `block` with `block.timestamp == x`, the upgrade rules apply.
If the upgrade block-timestamp `x` is not specified in the configuration, the upgrade is ignored.
This applies to the L2 block timestamp, not to the L1-origin block timestamp. This applies to the L2 block timestamp, not to the L1-origin block timestamp.
This means that an L2 upgrade may be inactive, and then active, without changing the L1-origin. This means that an L2 upgrade may be inactive, and then active, without changing the L1-origin.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment