Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/optimism' into gk/geth-v1.15.3
Browse files Browse the repository at this point in the history
  • Loading branch information
geoknee committed Feb 28, 2025
2 parents e22956b + e136827 commit 678e649
Show file tree
Hide file tree
Showing 10 changed files with 446 additions and 20 deletions.
17 changes: 12 additions & 5 deletions core/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,11 @@ type ChainContext interface {
// NewEVMBlockContext creates a new context for use in the EVM.
func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common.Address, config *params.ChainConfig, statedb types.StateGetter) vm.BlockContext {
var (
beneficiary common.Address
baseFee *big.Int
blobBaseFee *big.Int
random *common.Hash
beneficiary common.Address
baseFee *big.Int
blobBaseFee *big.Int
random *common.Hash
operatorCostFn types.OperatorCostFunc
)

// If we don't have an explicit author (i.e. not mining), extract from the header
Expand All @@ -66,6 +67,9 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
if header.Difficulty.Sign() == 0 {
random = &header.MixDigest
}
if config.IsOptimismIsthmus(header.Time) {
operatorCostFn = types.NewOperatorCostFunc(config, statedb)
}
return vm.BlockContext{
CanTransfer: CanTransfer,
Transfer: Transfer,
Expand All @@ -78,7 +82,10 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
BlobBaseFee: blobBaseFee,
GasLimit: header.GasLimit,
Random: random,
L1CostFunc: types.NewL1CostFunc(config, statedb),

// OP-Stack additions
L1CostFunc: types.NewL1CostFunc(config, statedb),
OperatorCostFunc: operatorCostFn,
}
}

Expand Down
39 changes: 35 additions & 4 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,10 +275,17 @@ func (st *stateTransition) buyGas() error {
mgval := new(big.Int).SetUint64(st.msg.GasLimit)
mgval.Mul(mgval, st.msg.GasPrice)
var l1Cost *big.Int
if st.evm.Context.L1CostFunc != nil && !st.msg.SkipNonceChecks && !st.msg.SkipFromEOACheck {
l1Cost = st.evm.Context.L1CostFunc(st.msg.RollupCostData, st.evm.Context.Time)
if l1Cost != nil {
mgval = mgval.Add(mgval, l1Cost)
var operatorCost *uint256.Int
if !st.msg.SkipNonceChecks && !st.msg.SkipFromEOACheck {
if st.evm.Context.L1CostFunc != nil {
l1Cost = st.evm.Context.L1CostFunc(st.msg.RollupCostData, st.evm.Context.Time)
if l1Cost != nil {
mgval = mgval.Add(mgval, l1Cost)
}
}
if st.evm.Context.OperatorCostFunc != nil {
operatorCost = st.evm.Context.OperatorCostFunc(st.msg.GasLimit, st.evm.Context.Time)
mgval = mgval.Add(mgval, operatorCost.ToBig())
}
}
balanceCheck := new(big.Int).Set(mgval)
Expand All @@ -288,6 +295,9 @@ func (st *stateTransition) buyGas() error {
if l1Cost != nil {
balanceCheck.Add(balanceCheck, l1Cost)
}
if operatorCost != nil {
balanceCheck.Add(balanceCheck, operatorCost.ToBig())
}
}
balanceCheck.Add(balanceCheck, st.msg.Value)

Expand Down Expand Up @@ -627,6 +637,11 @@ func (st *stateTransition) innerExecute() (*ExecutionResult, error) {
}
}
}
if rules.IsOptimismIsthmus {
// Calling st.refundOperatorCost() after st.gasRemaining is updated above,
// so that state refunds are taken into account when calculating operator fees.
st.refundIsthmusOperatorCost()
}
st.returnGas()

// OP-Stack: Note for deposit tx there is no ETH refunded for unused gas, but that's taken care of by the fact that gasPrice
Expand Down Expand Up @@ -681,6 +696,10 @@ func (st *stateTransition) innerExecute() (*ExecutionResult, error) {
}
st.state.AddBalance(params.OptimismL1FeeRecipient, amtU256, tracing.BalanceIncreaseRewardTransactionFee)
}
if rules.IsOptimismIsthmus {
operatorFeeCost := st.evm.Context.OperatorCostFunc(st.gasUsed(), st.evm.Context.Time)
st.state.AddBalance(params.OptimismOperatorFeeRecipient, operatorFeeCost, tracing.BalanceIncreaseRewardTransactionFee)
}
}
}

Expand Down Expand Up @@ -785,6 +804,18 @@ func (st *stateTransition) returnGas() {
st.gp.AddGas(st.gasRemaining)
}

func (st *stateTransition) refundIsthmusOperatorCost() {
// Return ETH to transaction sender for operator cost overcharge.
operatorCostGasLimit := st.evm.Context.OperatorCostFunc(st.msg.GasLimit, st.evm.Context.Time)
operatorCostGasUsed := st.evm.Context.OperatorCostFunc(st.gasUsed(), st.evm.Context.Time)

if operatorCostGasUsed.Cmp(operatorCostGasLimit) > 0 { // Sanity check.
panic(fmt.Sprintf("operator cost gas used (%d) > operator cost gas limit (%d)", operatorCostGasUsed, operatorCostGasLimit))
}

st.state.AddBalance(st.msg.From, new(uint256.Int).Sub(operatorCostGasLimit, operatorCostGasUsed), tracing.BalanceIncreaseGasReturn)
}

// gasUsed returns the amount of gas used up by the state transition.
func (st *stateTransition) gasUsed() uint64 {
return st.initialGas - st.gasRemaining
Expand Down
16 changes: 16 additions & 0 deletions core/types/gen_receipt_json.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion core/types/receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,16 @@ type Receipt struct {
BlockNumber *big.Int `json:"blockNumber,omitempty"`
TransactionIndex uint `json:"transactionIndex"`

// Optimism: extend receipts with L1 fee info
// Optimism: extend receipts with L1 and operator fee info
L1GasPrice *big.Int `json:"l1GasPrice,omitempty"` // Present from pre-bedrock. L1 Basefee after Bedrock
L1BlobBaseFee *big.Int `json:"l1BlobBaseFee,omitempty"` // Always nil prior to the Ecotone hardfork
L1GasUsed *big.Int `json:"l1GasUsed,omitempty"` // Present from pre-bedrock, deprecated as of Fjord
L1Fee *big.Int `json:"l1Fee,omitempty"` // Present from pre-bedrock
FeeScalar *big.Float `json:"l1FeeScalar,omitempty"` // Present from pre-bedrock to Ecotone. Nil after Ecotone
L1BaseFeeScalar *uint64 `json:"l1BaseFeeScalar,omitempty"` // Always nil prior to the Ecotone hardfork
L1BlobBaseFeeScalar *uint64 `json:"l1BlobBaseFeeScalar,omitempty"` // Always nil prior to the Ecotone hardfork
OperatorFeeScalar *uint64 `json:"operatorFeeScalar,omitempty"` // Always nil prior to the Isthmus hardfork
OperatorFeeConstant *uint64 `json:"operatorFeeConstant,omitempty"` // Always nil prior to the Isthmus hardfork
}

type receiptMarshaling struct {
Expand All @@ -116,6 +118,8 @@ type receiptMarshaling struct {
L1BlobBaseFeeScalar *hexutil.Uint64
DepositNonce *hexutil.Uint64
DepositReceiptVersion *hexutil.Uint64
OperatorFeeScalar *hexutil.Uint64
OperatorFeeConstant *hexutil.Uint64
}

// receiptRLP is the consensus encoding of a receipt.
Expand Down Expand Up @@ -590,6 +594,10 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu
rs[i].FeeScalar = gasParams.feeScalar
rs[i].L1BaseFeeScalar = u32ptrTou64ptr(gasParams.l1BaseFeeScalar)
rs[i].L1BlobBaseFeeScalar = u32ptrTou64ptr(gasParams.l1BlobBaseFeeScalar)
if gasParams.operatorFeeScalar != nil && gasParams.operatorFeeConstant != nil && (*gasParams.operatorFeeScalar != 0 || *gasParams.operatorFeeConstant != 0) {
rs[i].OperatorFeeScalar = u32ptrTou64ptr(gasParams.operatorFeeScalar)
rs[i].OperatorFeeConstant = gasParams.operatorFeeConstant
}
}
}
return nil
Expand Down
128 changes: 128 additions & 0 deletions core/types/receipt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ var (
conf.EcotoneTime = &time
return &conf
}()
isthmusTestConfig = func() *params.ChainConfig {
conf := *bedrockGenesisTestConfig // copy the config
time := uint64(0)
conf.IsthmusTime = &time
return &conf
}()

legacyReceipt = &Receipt{
Status: ReceiptStatusFailed,
Expand Down Expand Up @@ -767,6 +773,78 @@ func getOptimismEcotoneTxReceipts(l1AttributesPayload []byte, l1GasPrice, l1Blob
return txs, receipts
}

func getOptimismIsthmusTxReceipts(l1AttributesPayload []byte, l1GasPrice, l1BlobGasPrice, l1GasUsed, l1Fee *big.Int, baseFeeScalar, blobBaseFeeScalar, operatorFeeScalar, operatorFeeConstant *uint64) ([]*Transaction, []*Receipt) {
// Create a few transactions to have receipts for
txs := Transactions{
NewTx(&DepositTx{
To: nil, // contract creation
Value: big.NewInt(6),
Gas: 50,
Data: l1AttributesPayload,
}),
emptyTx,
}

// Create the corresponding receipts
receipts := Receipts{
&Receipt{
Type: DepositTxType,
PostState: common.Hash{5}.Bytes(),
CumulativeGasUsed: 50 + 15,
Logs: []*Log{
{
Address: common.BytesToAddress([]byte{0x33}),
// derived fields:
BlockNumber: blockNumber.Uint64(),
TxHash: txs[0].Hash(),
TxIndex: 0,
BlockHash: blockHash,
Index: 0,
},
{
Address: common.BytesToAddress([]byte{0x03, 0x33}),
// derived fields:
BlockNumber: blockNumber.Uint64(),
TxHash: txs[0].Hash(),
TxIndex: 0,
BlockHash: blockHash,
Index: 1,
},
},
TxHash: txs[0].Hash(),
ContractAddress: common.HexToAddress("0x3bb898b4bbe24f68a4e9be46cfe72d1787fd74f4"),
GasUsed: 65,
EffectiveGasPrice: big.NewInt(0),
BlockHash: blockHash,
BlockNumber: blockNumber,
TransactionIndex: 0,
DepositNonce: &depNonce1,
},
&Receipt{
Type: LegacyTxType,
EffectiveGasPrice: big.NewInt(0),
PostState: common.Hash{4}.Bytes(),
CumulativeGasUsed: 10,
Logs: []*Log{},
// derived fields:
TxHash: txs[1].Hash(),
GasUsed: 18446744073709551561,
BlockHash: blockHash,
BlockNumber: blockNumber,
TransactionIndex: 1,
L1GasPrice: l1GasPrice,
L1BlobBaseFee: l1BlobGasPrice,
L1GasUsed: l1GasUsed,
L1Fee: l1Fee,
L1BaseFeeScalar: baseFeeScalar,
L1BlobBaseFeeScalar: blobBaseFeeScalar,
OperatorFeeScalar: operatorFeeScalar,
OperatorFeeConstant: operatorFeeConstant,
},
}
return txs, receipts
}

func getOptimismTxReceipts(l1AttributesPayload []byte, l1GasPrice, l1GasUsed, l1Fee *big.Int, feeScalar *big.Float) ([]*Transaction, []*Receipt) {
// Create a few transactions to have receipts for
txs := Transactions{
Expand Down Expand Up @@ -887,6 +965,56 @@ func TestDeriveOptimismEcotoneTxReceipts(t *testing.T) {
diffReceipts(t, receipts, derivedReceipts)
}

func TestDeriveOptimismIsthmusTxReceipts(t *testing.T) {
// Isthmus style l1 attributes with baseFeeScalar=2, blobBaseFeeScalar=3, baseFee=1000*1e6, blobBaseFee=10*1e6, operatorFeeScalar=7, operatorFeeConstant=9
payload := common.Hex2Bytes("098999be000000020000000300000000000004d200000000000004d200000000000004d2000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000000098968000000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d255c6fb7c116fb15b44847d04")
// the parameters we use below are defined in rollup_test.go
baseFeeScalarUint64 := baseFeeScalar.Uint64()
blobBaseFeeScalarUint64 := blobBaseFeeScalar.Uint64()
operatorFeeScalarUint64 := operatorFeeScalar.Uint64()
operatorFeeConstantUint64 := operatorFeeConstant.Uint64()
txs, receipts := getOptimismIsthmusTxReceipts(payload, baseFee, blobBaseFee, minimumFjordGas, fjordFee, &baseFeeScalarUint64, &blobBaseFeeScalarUint64, &operatorFeeScalarUint64, &operatorFeeConstantUint64)

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

err = Receipts(derivedReceipts).DeriveFields(isthmusTestConfig, blockHash, blockNumber.Uint64(), 0, baseFee, nil, txs)
if err != nil {
t.Fatalf("DeriveFields(...) = %v, want <nil>", err)
}
diffReceipts(t, receipts, derivedReceipts)
}

func TestDeriveOptimismIsthmusTxReceiptsNoOperatorFee(t *testing.T) {
// Isthmus style l1 attributes with baseFeeScalar=2, blobBaseFeeScalar=3, baseFee=1000*1e6, blobBaseFee=10*1e6, operatorFeeScalar=7, operatorFeeConstant=9
payload := common.Hex2Bytes("098999be000000020000000300000000000004d200000000000004d200000000000004d2000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000000098968000000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d2000000000000000000000000")
// the parameters we use below are defined in rollup_test.go
baseFeeScalarUint64 := baseFeeScalar.Uint64()
blobBaseFeeScalarUint64 := blobBaseFeeScalar.Uint64()
txs, receipts := getOptimismIsthmusTxReceipts(payload, baseFee, blobBaseFee, minimumFjordGas, fjordFee, &baseFeeScalarUint64, &blobBaseFeeScalarUint64, nil, nil)

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

err = Receipts(derivedReceipts).DeriveFields(isthmusTestConfig, blockHash, blockNumber.Uint64(), 0, baseFee, nil, txs)
if err != nil {
t.Fatalf("DeriveFields(...) = %v, want <nil>", err)
}
diffReceipts(t, receipts, derivedReceipts)
}

func diffReceipts(t *testing.T, receipts, derivedReceipts []*Receipt) {
// Check diff of receipts against derivedReceipts.
r1, err := json.MarshalIndent(receipts, "", " ")
Expand Down
Loading

0 comments on commit 678e649

Please sign in to comment.