Skip to content

Commit

Permalink
Merge pull request #216 from roberto-bayardo/baseFee
Browse files Browse the repository at this point in the history
baseFee consistency related renaming
  • Loading branch information
sebastianst authored Jan 11, 2024
2 parents a79bde2 + b52ff66 commit a72d34b
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 88 deletions.
4 changes: 2 additions & 2 deletions core/types/receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -570,15 +570,15 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu
}
}
if config.Optimism != nil && len(txs) >= 2 { // need at least an info tx and a non-info tx
l1Basefee, costFunc, feeScalar, err := extractL1GasParams(config, time, txs[0].Data())
l1BaseFee, costFunc, feeScalar, err := extractL1GasParams(config, time, txs[0].Data())
if err != nil {
return err
}
for i := 0; i < len(rs); i++ {
if txs[i].IsDepositTx() {
continue
}
rs[i].L1GasPrice = l1Basefee
rs[i].L1GasPrice = l1BaseFee
rs[i].L1Fee, rs[i].L1GasUsed = costFunc(txs[i].RollupCostData())
rs[i].FeeScalar = feeScalar
}
Expand Down
22 changes: 11 additions & 11 deletions core/types/receipt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -458,10 +458,10 @@ func TestDecodeEmptyTypedReceipt(t *testing.T) {
// Tests that receipt data can be correctly derived from the contextual infos
func TestDeriveFields(t *testing.T) {
// Re-derive receipts.
basefee := big.NewInt(1000)
baseFee := big.NewInt(1000)
blobGasPrice := big.NewInt(920)
derivedReceipts := clearComputedFieldsOnReceipts(receipts)
err := Receipts(derivedReceipts).DeriveFields(params.TestChainConfig, blockHash, blockNumber.Uint64(), blockTime, basefee, blobGasPrice, txs)
err := Receipts(derivedReceipts).DeriveFields(params.TestChainConfig, blockHash, blockNumber.Uint64(), blockTime, baseFee, blobGasPrice, txs)
if err != nil {
t.Fatalf("DeriveFields(...) = %v, want <nil>", err)
}
Expand Down Expand Up @@ -765,49 +765,49 @@ func TestDeriveOptimismBedrockTxReceipts(t *testing.T) {
// Bedrock style l1 attributes with L1Scalar=7_000_000 (becomes 7 after division), L1Overhead=50, L1BaseFee=1000*1e6
payload := common.Hex2Bytes("015d8eb900000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d2000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d2000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000006acfc0015d8eb900000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d2000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d2000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000006acfc0")
// the parameters we use below are defined in rollup_test.go
l1GasPrice := basefee
l1GasPrice := baseFee
l1GasUsed := bedrockGas
feeScalar := big.NewFloat(float64(scalar.Uint64() / 1e6))
l1Fee := bedrockFee
txs, receipts := getOptimismTxReceipts(t, payload, l1GasPrice, l1GasUsed, feeScalar, l1Fee)

// Re-derive receipts.
basefee := big.NewInt(1000)
baseFee := big.NewInt(1000)
derivedReceipts := clearComputedFieldsOnReceipts(receipts)
err := Receipts(derivedReceipts).DeriveFields(params.OptimismTestConfig, blockHash, blockNumber.Uint64(), 0, basefee, nil, txs)
err := Receipts(derivedReceipts).DeriveFields(params.OptimismTestConfig, blockHash, blockNumber.Uint64(), 0, baseFee, nil, txs)
if err != nil {
t.Fatalf("DeriveFields(...) = %v, want <nil>", err)
}
checkBedrockReceipts(t, receipts, derivedReceipts)

// Should get same result with the Ecotone config because it will assume this is "first ecotone block"
// if it sees the bedrock style L1 attributes.
err = Receipts(derivedReceipts).DeriveFields(ecotoneTestConfig, blockHash, blockNumber.Uint64(), 0, basefee, nil, txs)
err = Receipts(derivedReceipts).DeriveFields(ecotoneTestConfig, blockHash, blockNumber.Uint64(), 0, baseFee, nil, txs)
if err != nil {
t.Fatalf("DeriveFields(...) = %v, want <nil>", err)
}
checkBedrockReceipts(t, receipts, derivedReceipts)
}

func TestDeriveOptimismEcotoneTxReceipts(t *testing.T) {
// Ecotone style l1 attributes with basefeeScalar=2, blobBasfeeScalar=3, baseFee=1000*1e6, blobBasefee=10*1e6
// Ecotone style l1 attributes with baseFeeScalar=2, blobBaseFeeScalar=3, baseFee=1000*1e6, blobBaseFee=10*1e6
payload := common.Hex2Bytes("440a5e20000000020000000300000000000004d200000000000004d200000000000004d2000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000000098968000000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d2")
// the parameters we use below are defined in rollup_test.go
l1GasPrice := basefee
l1GasPrice := baseFee
l1GasUsed := ecotoneGas
l1Fee := ecotoneFee
txs, receipts := getOptimismTxReceipts(t, payload, l1GasPrice, l1GasUsed, nil /*feeScalar*/, l1Fee)

// Re-derive receipts.
basefee := big.NewInt(1000)
baseFee := big.NewInt(1000)
derivedReceipts := clearComputedFieldsOnReceipts(receipts)
// Should error out if we try to process this with a pre-Ecotone config
err := Receipts(derivedReceipts).DeriveFields(params.OptimismTestConfig, blockHash, blockNumber.Uint64(), 0, basefee, nil, txs)
err := Receipts(derivedReceipts).DeriveFields(params.OptimismTestConfig, blockHash, blockNumber.Uint64(), 0, baseFee, nil, txs)
if err == nil {
t.Fatalf("expected error from deriving ecotone receipts with pre-ecotone config, got none")
}

err = Receipts(derivedReceipts).DeriveFields(ecotoneTestConfig, blockHash, blockNumber.Uint64(), 0, basefee, nil, txs)
err = Receipts(derivedReceipts).DeriveFields(ecotoneTestConfig, blockHash, blockNumber.Uint64(), 0, baseFee, nil, txs)
if err != nil {
t.Fatalf("DeriveFields(...) = %v, want <nil>", err)
}
Expand Down
86 changes: 43 additions & 43 deletions core/types/rollup_cost.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,17 @@ const (
// offsets correspond to the last byte of the value in the slot, counting backwards from the
// end of the slot. For example, The 8-byte sequence number has offset 0, and is therefore
// stored as big-endian format in bytes [24:32] of the slot.
BasefeeScalarSlotOffset = 12 // bytes [16:20] of the slot
BlobBasefeeScalarSlotOffset = 8 // bytes [20:24] of the slot
BaseFeeScalarSlotOffset = 12 // bytes [16:20] of the slot
BlobBaseFeeScalarSlotOffset = 8 // bytes [20:24] of the slot

// scalarSectionStart is the beginning of the scalar values segment in the slot
// array. basefeeScalar is in the first four bytes of the segment, blobBasefeeScalar the next
// array. baseFeeScalar is in the first four bytes of the segment, blobBaseFeeScalar the next
// four.
scalarSectionStart = 32 - BasefeeScalarSlotOffset - 4
scalarSectionStart = 32 - BaseFeeScalarSlotOffset - 4
)

func init() {
if BlobBasefeeScalarSlotOffset != BasefeeScalarSlotOffset-4 {
if BlobBaseFeeScalarSlotOffset != BaseFeeScalarSlotOffset-4 {
panic("this code assumes the scalars are at adjacent positions in the scalars slot")
}
}
Expand All @@ -57,16 +57,16 @@ var (
// L1BlockAddr is the address of the L1Block contract which stores the L1 gas attributes.
L1BlockAddr = common.HexToAddress("0x4200000000000000000000000000000000000015")

L1BasefeeSlot = common.BigToHash(big.NewInt(1))
L1BaseFeeSlot = common.BigToHash(big.NewInt(1))
OverheadSlot = common.BigToHash(big.NewInt(5))
ScalarSlot = common.BigToHash(big.NewInt(6))

// L2BlobBasefeeSlot was added with the Ecotone upgrade and stores the blobBasefee L1 gas
// L2BlobBaseFeeSlot was added with the Ecotone upgrade and stores the blobBaseFee L1 gas
// attribute.
L1BlobBasefeeSlot = common.BigToHash(big.NewInt(7))
L1BlobBaseFeeSlot = common.BigToHash(big.NewInt(7))
// L1FeeScalarsSlot as of the Ecotone upgrade stores the 32-bit basefeeScalar and
// blobBasefeeScalar L1 gas attributes at offsets `BasefeeScalarSlotOffset` and
// `BlobBasefeeScalarSlotOffset` respectively.
// blobBaseFeeScalar L1 gas attributes at offsets `BaseFeeScalarSlotOffset` and
// `BlobBaseFeeScalarSlotOffset` respectively.
L1FeeScalarsSlot = common.BigToHash(big.NewInt(3))

oneMillion = big.NewInt(1_000_000)
Expand Down Expand Up @@ -130,23 +130,23 @@ func NewL1CostFunc(config *params.ChainConfig, statedb StateGetter) L1CostFunc {
if !config.IsOptimismEcotone(blockTime) {
cachedFunc = newL1CostFuncBedrock(config, statedb, blockTime)
} else {
l1BlobBasefee := statedb.GetState(L1BlockAddr, L1BlobBasefeeSlot).Big()
l1BlobBaseFee := statedb.GetState(L1BlockAddr, L1BlobBaseFeeSlot).Big()
l1FeeScalars := statedb.GetState(L1BlockAddr, L1FeeScalarsSlot).Bytes()

// Edge case: the very first Ecotone block requires we use the Bedrock cost
// function. We detect this scenario by checking if the Ecotone parameters are
// unset. Not here we rely on assumption that the scalar parameters are adjacent
// in the buffer and basefeeScalar comes first.
if l1BlobBasefee.BitLen() == 0 &&
if l1BlobBaseFee.BitLen() == 0 &&
bytes.Equal(emptyScalars, l1FeeScalars[scalarSectionStart:scalarSectionStart+8]) {
log.Info("using bedrock l1 cost func for first Ecotone block", "time", blockTime)
cachedFunc = newL1CostFuncBedrock(config, statedb, blockTime)
} else {
l1Basefee := statedb.GetState(L1BlockAddr, L1BasefeeSlot).Big()
l1BaseFee := statedb.GetState(L1BlockAddr, L1BaseFeeSlot).Big()
offset := scalarSectionStart
l1BasefeeScalar := new(big.Int).SetBytes(l1FeeScalars[offset : offset+4])
l1BlobBasefeeScalar := new(big.Int).SetBytes(l1FeeScalars[offset+4 : offset+8])
cachedFunc = newL1CostFuncEcotone(l1Basefee, l1BlobBasefee, l1BasefeeScalar, l1BlobBasefeeScalar)
l1BaseFeeScalar := new(big.Int).SetBytes(l1FeeScalars[offset : offset+4])
l1BlobBaseFeeScalar := new(big.Int).SetBytes(l1FeeScalars[offset+4 : offset+8])
cachedFunc = newL1CostFuncEcotone(l1BaseFee, l1BlobBaseFee, l1BaseFeeScalar, l1BlobBaseFeeScalar)
}
}
}
Expand All @@ -158,16 +158,16 @@ func NewL1CostFunc(config *params.ChainConfig, statedb StateGetter) L1CostFunc {
// newL1CostFuncBedrock returns an L1 cost function suitable for Bedrock, Regolith, and the first
// block only of the Ecotone upgrade.
func newL1CostFuncBedrock(config *params.ChainConfig, statedb StateGetter, blockTime uint64) l1CostFunc {
l1Basefee := statedb.GetState(L1BlockAddr, L1BasefeeSlot).Big()
l1BaseFee := statedb.GetState(L1BlockAddr, L1BaseFeeSlot).Big()
overhead := statedb.GetState(L1BlockAddr, OverheadSlot).Big()
scalar := statedb.GetState(L1BlockAddr, ScalarSlot).Big()
isRegolith := config.IsRegolith(blockTime)
return newL1CostFuncBedrockHelper(l1Basefee, overhead, scalar, isRegolith)
return newL1CostFuncBedrockHelper(l1BaseFee, overhead, scalar, isRegolith)
}

// newL1CostFuncBedrockHelper is lower level version of newL1CostFuncBedrock that expects already
// extracted parameters
func newL1CostFuncBedrockHelper(l1Basefee, overhead, scalar *big.Int, isRegolith bool) l1CostFunc {
func newL1CostFuncBedrockHelper(l1BaseFee, overhead, scalar *big.Int, isRegolith bool) l1CostFunc {
return func(rollupCostData RollupCostData) (fee, gasUsed *big.Int) {
if rollupCostData == (RollupCostData{}) {
return nil, nil // Do not charge if there is no rollup cost-data (e.g. RPC call or deposit)
Expand All @@ -180,35 +180,35 @@ func newL1CostFuncBedrockHelper(l1Basefee, overhead, scalar *big.Int, isRegolith
}
gasWithOverhead := new(big.Int).SetUint64(gas)
gasWithOverhead.Add(gasWithOverhead, overhead)
l1Cost := l1CostHelper(gasWithOverhead, l1Basefee, scalar)
l1Cost := l1CostHelper(gasWithOverhead, l1BaseFee, scalar)
return l1Cost, gasWithOverhead
}
}

// newL1CostFuncEcotone returns an l1 cost function suitable for the Ecotone upgrade except for the
// very first block of the upgrade.
func newL1CostFuncEcotone(l1Basefee, l1BlobBasefee, l1BasefeeScalar, l1BlobBasefeeScalar *big.Int) l1CostFunc {
func newL1CostFuncEcotone(l1BaseFee, l1BlobBaseFee, l1BaseFeeScalar, l1BlobBaseFeeScalar *big.Int) l1CostFunc {
return func(costData RollupCostData) (fee, calldataGasUsed *big.Int) {
calldataGas := (costData.zeroes * params.TxDataZeroGas) + (costData.ones * params.TxDataNonZeroGasEIP2028)
calldataGasUsed = new(big.Int).SetUint64(calldataGas)

// Ecotone L1 cost function:
//
// (calldataGas/16)*(l1Basefee*16*l1BasefeeScalar + l1BlobBasefee*l1BlobBasefeeScalar)/1e6
// (calldataGas/16)*(l1BaseFee*16*l1BaseFeeScalar + l1BlobBaseFee*l1BlobBaseFeeScalar)/1e6
//
// We divide "calldataGas" by 16 to change from units of calldata gas to "estimated # of bytes when
// compressed". Known as "compressedTxSize" in the spec.
//
// Function is actually computed as follows for better precision under integer arithmetic:
//
// calldataGas*(l1Basefee*16*l1BasefeeScalar + l1BlobBasefee*l1BlobBasefeeScalar)/16e6
// calldataGas*(l1BaseFee*16*l1BaseFeeScalar + l1BlobBaseFee*l1BlobBaseFeeScalar)/16e6

calldataCostPerByte := new(big.Int).Set(l1Basefee)
calldataCostPerByte := new(big.Int).Set(l1BaseFee)
calldataCostPerByte = calldataCostPerByte.Mul(calldataCostPerByte, sixteen)
calldataCostPerByte = calldataCostPerByte.Mul(calldataCostPerByte, l1BasefeeScalar)
calldataCostPerByte = calldataCostPerByte.Mul(calldataCostPerByte, l1BaseFeeScalar)

blobCostPerByte := new(big.Int).Set(l1BlobBasefee)
blobCostPerByte = blobCostPerByte.Mul(blobCostPerByte, l1BlobBasefeeScalar)
blobCostPerByte := new(big.Int).Set(l1BlobBaseFee)
blobCostPerByte = blobCostPerByte.Mul(blobCostPerByte, l1BlobBaseFeeScalar)

fee = new(big.Int).Add(calldataCostPerByte, blobCostPerByte)
fee = fee.Mul(fee, calldataGasUsed)
Expand All @@ -219,12 +219,12 @@ func newL1CostFuncEcotone(l1Basefee, l1BlobBasefee, l1BasefeeScalar, l1BlobBasef
}

// extractL1GasParams extracts the gas parameters necessary to compute gas costs from L1 block info
func extractL1GasParams(config *params.ChainConfig, time uint64, data []byte) (l1Basefee *big.Int, costFunc l1CostFunc, feeScalar *big.Float, err error) {
func extractL1GasParams(config *params.ChainConfig, time uint64, data []byte) (l1BaseFee *big.Int, costFunc l1CostFunc, feeScalar *big.Float, err error) {
if config.IsEcotone(time) {
// edge case: for the very first Ecotone block we still need to use the Bedrock
// function. We detect this edge case by seeing if the function selector is the old one
if len(data) >= 4 && !bytes.Equal(data[0:4], BedrockL1AttributesSelector) {
l1Basefee, costFunc, err = extractL1GasParamsEcotone(data)
l1BaseFee, costFunc, err = extractL1GasParamsEcotone(data)
return
}
}
Expand All @@ -234,52 +234,52 @@ func extractL1GasParams(config *params.ChainConfig, time uint64, data []byte) (l
return nil, nil, nil, fmt.Errorf("expected at least %d L1 info bytes, got %d", 4+32*8, len(data))
}
data = data[4:] // trim function selector
l1Basefee = new(big.Int).SetBytes(data[32*2 : 32*3]) // arg index 2
l1BaseFee = new(big.Int).SetBytes(data[32*2 : 32*3]) // arg index 2
overhead := new(big.Int).SetBytes(data[32*6 : 32*7]) // arg index 6
scalar := new(big.Int).SetBytes(data[32*7 : 32*8]) // arg index 7
fscalar := new(big.Float).SetInt(scalar) // legacy: format fee scalar as big Float
fdivisor := new(big.Float).SetUint64(1_000_000) // 10**6, i.e. 6 decimals
feeScalar = new(big.Float).Quo(fscalar, fdivisor)
costFunc = newL1CostFuncBedrockHelper(l1Basefee, overhead, scalar, config.IsRegolith(time))
costFunc = newL1CostFuncBedrockHelper(l1BaseFee, overhead, scalar, config.IsRegolith(time))
return
}

// extractEcotoneL1GasParams extracts the gas parameters necessary to compute gas from L1 attribute
// info calldata after the Ecotone upgrade, but not for the very first Ecotone block.
func extractL1GasParamsEcotone(data []byte) (l1Basefee *big.Int, costFunc l1CostFunc, err error) {
func extractL1GasParamsEcotone(data []byte) (l1BaseFee *big.Int, costFunc l1CostFunc, err error) {
if len(data) != 164 {
return nil, nil, fmt.Errorf("expected 164 L1 info bytes, got %d", len(data))
}
// data layout assumed for Ecotone:
// offset type varname
// 0 <selector>
// 4 uint32 _basefeeScalar
// 8 uint32 _blobBasefeeScalar
// 8 uint32 _blobBaseFeeScalar
// 12 uint64 _sequenceNumber,
// 20 uint64 _timestamp,
// 28 uint64 _l1BlockNumber
// 36 uint256 _basefee,
// 68 uint256 _blobBasefee,
// 68 uint256 _blobBaseFee,
// 100 bytes32 _hash,
// 132 bytes32 _batcherHash,
l1Basefee = new(big.Int).SetBytes(data[36:68])
l1BlobBasefee := new(big.Int).SetBytes(data[68:100])
l1BasefeeScalar := new(big.Int).SetBytes(data[4:8])
l1BlobBasefeeScalar := new(big.Int).SetBytes(data[8:12])
costFunc = newL1CostFuncEcotone(l1Basefee, l1BlobBasefee, l1BasefeeScalar, l1BlobBasefeeScalar)
l1BaseFee = new(big.Int).SetBytes(data[36:68])
l1BlobBaseFee := new(big.Int).SetBytes(data[68:100])
l1BaseFeeScalar := new(big.Int).SetBytes(data[4:8])
l1BlobBaseFeeScalar := new(big.Int).SetBytes(data[8:12])
costFunc = newL1CostFuncEcotone(l1BaseFee, l1BlobBaseFee, l1BaseFeeScalar, l1BlobBaseFeeScalar)
return
}

// L1Cost computes the the data availability fee for transactions in blocks prior to the Ecotone
// upgrade. It is used by e2e tests so must remain exported.
func L1Cost(rollupDataGas uint64, l1Basefee, overhead, scalar *big.Int) *big.Int {
func L1Cost(rollupDataGas uint64, l1BaseFee, overhead, scalar *big.Int) *big.Int {
l1GasUsed := new(big.Int).SetUint64(rollupDataGas)
l1GasUsed.Add(l1GasUsed, overhead)
return l1CostHelper(l1GasUsed, l1Basefee, scalar)
return l1CostHelper(l1GasUsed, l1BaseFee, scalar)
}

func l1CostHelper(gasWithOverhead, l1Basefee, scalar *big.Int) *big.Int {
func l1CostHelper(gasWithOverhead, l1BaseFee, scalar *big.Int) *big.Int {
fee := new(big.Int).Set(gasWithOverhead)
fee.Mul(fee, l1Basefee).Mul(fee, scalar).Div(fee, oneMillion)
fee.Mul(fee, l1BaseFee).Mul(fee, scalar).Div(fee, oneMillion)
return fee
}
Loading

0 comments on commit a72d34b

Please sign in to comment.