Skip to content

Commit dc3e6fe

Browse files
committed
Add support for the new Isthmus block info tx
1 parent 928070c commit dc3e6fe

File tree

2 files changed

+159
-32
lines changed

2 files changed

+159
-32
lines changed

core/types/rollup_cost.go

Lines changed: 77 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,18 @@ func init() {
4949
}
5050

5151
var (
52-
// BedrockL1AttributesSelector is the function selector indicating Bedrock style L1 gas
53-
// attributes.
54-
BedrockL1AttributesSelector = []byte{0x01, 0x5d, 0x8e, 0xb9}
52+
// BedrockL1AttributesSelector is the function selector indicating Bedrock style L1 gas attributes.
53+
// keccak256("setL1BlockValues(uint64,uint64,uint256,bytes32,uint64,bytes32,uint256,uint256)")[:4]
54+
BedrockL1AttributesSelector = [4]byte{0x01, 0x5d, 0x8e, 0xb9}
5555
// EcotoneL1AttributesSelector is the selector indicating Ecotone style L1 gas attributes.
56-
EcotoneL1AttributesSelector = []byte{0x44, 0x0a, 0x5e, 0x20}
57-
56+
// keccak256("setL1BlockValuesEcotone()")[:4]
57+
EcotoneL1AttributesSelector = [4]byte{0x44, 0x0a, 0x5e, 0x20}
58+
// IsthmusL1AttributesSelector is the selector indicating Isthmus style L1 gas attributes.
59+
// keccak256("setL1BlockValuesIsthmus()")[:4]
60+
IsthmusL1AttributesSelector = [4]byte{0x09, 0x89, 0x99, 0xbe}
61+
// InteropL1AttributesSelector is the selector indicating Interop style L1 gas attributes.
62+
// keccak256("setL1BlockValuesInterop()")[:4]
63+
InteropL1AttributesSelector = [4]byte{0x76, 0x0e, 0xe0, 0x4d}
5864
// L1BlockAddr is the address of the L1Block contract which stores the L1 gas attributes.
5965
L1BlockAddr = common.HexToAddress("0x4200000000000000000000000000000000000015")
6066

@@ -259,35 +265,60 @@ func intToScaledFloat(scalar *big.Int) *big.Float {
259265

260266
// extractL1GasParams extracts the gas parameters necessary to compute gas costs from L1 block info
261267
func extractL1GasParams(config *params.ChainConfig, time uint64, data []byte) (gasParams, error) {
262-
// edge case: for the very first Ecotone block we still need to use the Bedrock
263-
// function. We detect this edge case by seeing if the function selector is the old one
264-
// If so, fall through to the pre-ecotone format
265-
// Both Ecotone and Fjord use the same function selector
266-
if config.IsEcotone(time) && len(data) >= 4 && !bytes.Equal(data[0:4], BedrockL1AttributesSelector) {
267-
p, err := extractL1GasParamsPostEcotone(data)
268-
if err != nil {
269-
return gasParams{}, err
270-
}
268+
if len(data) < 4 {
269+
return gasParams{}, fmt.Errorf("unexpected L1 info data length, got %d", len(data))
270+
}
271271

272-
if config.IsFjord(time) {
273-
p.costFunc = NewL1CostFuncFjord(
274-
p.l1BaseFee,
275-
p.l1BlobBaseFee,
276-
big.NewInt(int64(*p.l1BaseFeeScalar)),
277-
big.NewInt(int64(*p.l1BlobBaseFeeScalar)),
278-
)
279-
} else {
280-
p.costFunc = newL1CostFuncEcotone(
281-
p.l1BaseFee,
282-
p.l1BlobBaseFee,
283-
big.NewInt(int64(*p.l1BaseFeeScalar)),
284-
big.NewInt(int64(*p.l1BlobBaseFeeScalar)),
285-
)
272+
var p gasParams
273+
var err error
274+
var signature [4]byte
275+
copy(signature[:], data)
276+
// Note: for Ecotone + Isthmus, the new L1Block method selector is used in the block after
277+
// activation, so we use the selector for the switch block rather than the fork time.
278+
switch signature {
279+
case BedrockL1AttributesSelector:
280+
return extractL1GasParamsPreEcotone(config, time, data)
281+
case EcotoneL1AttributesSelector:
282+
if !config.IsEcotone(time) {
283+
return gasParams{}, fmt.Errorf("setL1BlockValuesEcotone called before Ecotone active")
284+
}
285+
p, err = extractL1GasParamsPostEcotone(data)
286+
case IsthmusL1AttributesSelector:
287+
if !config.IsIsthmus(time) {
288+
return gasParams{}, fmt.Errorf("setL1BlockValuesIsthmus called before Isthmus active")
286289
}
290+
p, err = extractL1GasParamsPostIsthmus(data)
291+
case InteropL1AttributesSelector:
292+
if !config.IsInterop(time) {
293+
return gasParams{}, fmt.Errorf("setL1BlockValuesInterop called before Interop active")
294+
}
295+
// Interop uses the same tx calldata size/format as Isthmus
296+
p, err = extractL1GasParamsPostIsthmus(data)
297+
default:
298+
return gasParams{}, fmt.Errorf("unknown L1Block function signature: 0x%s", common.Bytes2Hex(signature[:]))
299+
}
287300

288-
return p, nil
301+
if err != nil {
302+
return gasParams{}, err
289303
}
290-
return extractL1GasParamsPreEcotone(config, time, data)
304+
305+
if config.IsFjord(time) {
306+
p.costFunc = NewL1CostFuncFjord(
307+
p.l1BaseFee,
308+
p.l1BlobBaseFee,
309+
big.NewInt(int64(*p.l1BaseFeeScalar)),
310+
big.NewInt(int64(*p.l1BlobBaseFeeScalar)),
311+
)
312+
} else {
313+
p.costFunc = newL1CostFuncEcotone(
314+
p.l1BaseFee,
315+
p.l1BlobBaseFee,
316+
big.NewInt(int64(*p.l1BaseFeeScalar)),
317+
big.NewInt(int64(*p.l1BlobBaseFeeScalar)),
318+
)
319+
}
320+
321+
return p, nil
291322
}
292323

293324
func extractL1GasParamsPreEcotone(config *params.ChainConfig, time uint64, data []byte) (gasParams, error) {
@@ -314,6 +345,19 @@ func extractL1GasParamsPostEcotone(data []byte) (gasParams, error) {
314345
if len(data) != 164 {
315346
return gasParams{}, fmt.Errorf("expected 164 L1 info bytes, got %d", len(data))
316347
}
348+
return extractL1GasParamsPostEcotoneIsthmus(data)
349+
}
350+
351+
// extractL1GasParamsPostIsthmus extracts the gas parameters necessary to compute gas from L1 attribute
352+
// info calldata after the Isthmus upgrade, but not for the very first Isthmus block.
353+
func extractL1GasParamsPostIsthmus(data []byte) (gasParams, error) {
354+
if len(data) != 180 {
355+
return gasParams{}, fmt.Errorf("expected 180 L1 info bytes, got %d", len(data))
356+
}
357+
return extractL1GasParamsPostEcotoneIsthmus(data)
358+
}
359+
360+
func extractL1GasParamsPostEcotoneIsthmus(data []byte) (gasParams, error) {
317361
// data layout assumed for Ecotone:
318362
// offset type varname
319363
// 0 <selector>
@@ -326,6 +370,9 @@ func extractL1GasParamsPostEcotone(data []byte) (gasParams, error) {
326370
// 68 uint256 _blobBaseFee,
327371
// 100 bytes32 _hash,
328372
// 132 bytes32 _batcherHash,
373+
// Isthmus adds two more uint64s, which are ignored by this function:
374+
// 164 uint64 _depositNonce
375+
// 172 uint64 _configUpdateNonce
329376
l1BaseFee := new(big.Int).SetBytes(data[36:68])
330377
l1BlobBaseFee := new(big.Int).SetBytes(data[68:100])
331378
l1BaseFeeScalar := binary.BigEndian.Uint32(data[4:8])

core/types/rollup_cost_test.go

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,35 @@ func TestExtractFjordGasParams(t *testing.T) {
206206
require.Equal(t, fjordFee, c)
207207
}
208208

209+
func TestExtractIsthmusGasParams(t *testing.T) {
210+
zeroTime := uint64(0)
211+
// create a config where isthmus is active
212+
config := &params.ChainConfig{
213+
Optimism: params.OptimismTestConfig.Optimism,
214+
RegolithTime: &zeroTime,
215+
EcotoneTime: &zeroTime,
216+
FjordTime: &zeroTime,
217+
IsthmusTime: &zeroTime,
218+
}
219+
require.True(t, config.IsOptimismIsthmus(zeroTime))
220+
221+
data := getIsthmusL1Attributes(
222+
baseFee,
223+
blobBaseFee,
224+
baseFeeScalar,
225+
blobBaseFeeScalar,
226+
)
227+
228+
gasparams, err := extractL1GasParams(config, zeroTime, data)
229+
require.NoError(t, err)
230+
costFunc := gasparams.costFunc
231+
232+
c, g := costFunc(emptyTx.RollupCostData())
233+
234+
require.Equal(t, minimumFjordGas, g)
235+
require.Equal(t, fjordFee, c)
236+
}
237+
209238
// make sure the first block of the ecotone upgrade is properly detected, and invokes the bedrock
210239
// cost function appropriately
211240
func TestFirstBlockEcotoneGasParams(t *testing.T) {
@@ -228,11 +257,41 @@ func TestFirstBlockEcotoneGasParams(t *testing.T) {
228257
require.Equal(t, regolithFee, c)
229258
}
230259

260+
// make sure the first block of the isthmus upgrade is properly detected, and invokes the ecotone
261+
// cost function appropriately
262+
func TestFirstBlockIsthmusGasParams(t *testing.T) {
263+
zeroTime := uint64(0)
264+
// create a config where isthmus upgrade is active
265+
config := &params.ChainConfig{
266+
Optimism: params.OptimismTestConfig.Optimism,
267+
RegolithTime: &zeroTime,
268+
EcotoneTime: &zeroTime,
269+
FjordTime: &zeroTime,
270+
IsthmusTime: &zeroTime,
271+
}
272+
require.True(t, config.IsOptimismEcotone(0))
273+
require.True(t, config.IsOptimismIsthmus(0))
274+
275+
data := getEcotoneL1Attributes(
276+
baseFee,
277+
blobBaseFee,
278+
baseFeeScalar,
279+
blobBaseFeeScalar,
280+
)
281+
282+
gasparams, err := extractL1GasParams(config, zeroTime, data)
283+
require.NoError(t, err)
284+
oldCostFunc := gasparams.costFunc
285+
c, g := oldCostFunc(emptyTx.RollupCostData())
286+
require.Equal(t, minimumFjordGas, g)
287+
require.Equal(t, fjordFee, c)
288+
}
289+
231290
func getBedrockL1Attributes(baseFee, overhead, scalar *big.Int) []byte {
232291
uint256 := make([]byte, 32)
233292
ignored := big.NewInt(1234)
234293
data := []byte{}
235-
data = append(data, BedrockL1AttributesSelector...)
294+
data = append(data, BedrockL1AttributesSelector[:]...)
236295
data = append(data, ignored.FillBytes(uint256)...) // arg 0
237296
data = append(data, ignored.FillBytes(uint256)...) // arg 1
238297
data = append(data, baseFee.FillBytes(uint256)...) // arg 2
@@ -250,7 +309,26 @@ func getEcotoneL1Attributes(baseFee, blobBaseFee, baseFeeScalar, blobBaseFeeScal
250309
uint256Slice := make([]byte, 32)
251310
uint64Slice := make([]byte, 8)
252311
uint32Slice := make([]byte, 4)
253-
data = append(data, EcotoneL1AttributesSelector...)
312+
data = append(data, EcotoneL1AttributesSelector[:]...)
313+
data = append(data, baseFeeScalar.FillBytes(uint32Slice)...)
314+
data = append(data, blobBaseFeeScalar.FillBytes(uint32Slice)...)
315+
data = append(data, ignored.FillBytes(uint64Slice)...)
316+
data = append(data, ignored.FillBytes(uint64Slice)...)
317+
data = append(data, ignored.FillBytes(uint64Slice)...)
318+
data = append(data, baseFee.FillBytes(uint256Slice)...)
319+
data = append(data, blobBaseFee.FillBytes(uint256Slice)...)
320+
data = append(data, ignored.FillBytes(uint256Slice)...)
321+
data = append(data, ignored.FillBytes(uint256Slice)...)
322+
return data
323+
}
324+
325+
func getIsthmusL1Attributes(baseFee, blobBaseFee, baseFeeScalar, blobBaseFeeScalar *big.Int) []byte {
326+
ignored := big.NewInt(1234)
327+
data := []byte{}
328+
uint256Slice := make([]byte, 32)
329+
uint64Slice := make([]byte, 8)
330+
uint32Slice := make([]byte, 4)
331+
data = append(data, IsthmusL1AttributesSelector[:]...)
254332
data = append(data, baseFeeScalar.FillBytes(uint32Slice)...)
255333
data = append(data, blobBaseFeeScalar.FillBytes(uint32Slice)...)
256334
data = append(data, ignored.FillBytes(uint64Slice)...)
@@ -260,6 +338,8 @@ func getEcotoneL1Attributes(baseFee, blobBaseFee, baseFeeScalar, blobBaseFeeScal
260338
data = append(data, blobBaseFee.FillBytes(uint256Slice)...)
261339
data = append(data, ignored.FillBytes(uint256Slice)...)
262340
data = append(data, ignored.FillBytes(uint256Slice)...)
341+
data = append(data, ignored.FillBytes(uint64Slice)...)
342+
data = append(data, ignored.FillBytes(uint64Slice)...)
263343
return data
264344
}
265345

0 commit comments

Comments
 (0)