diff --git go-ethereum/cmd/devp2p/internal/ethtest/snap.go op-geth/cmd/devp2p/internal/ethtest/snap.go
index f947e4bc9bae791bc1a0058fcadf7fae094632b8..8fa03660cfcc9f8e979cb9d9bdec20b0af4a215b 100644
--- go-ethereum/cmd/devp2p/internal/ethtest/snap.go
+++ op-geth/cmd/devp2p/internal/ethtest/snap.go
@@ -249,11 +249,11 @@ for i, tc := range []byteCodesTest{
// A few stateroots
{
nBytes: 10000, hashes: []common.Hash{s.chain.RootAt(0), s.chain.RootAt(999)},
- expHashes: 0,
+ expHashes: 1, // 32-byte keys are detected as code, even if not code (like genesis hash), in legacy lookups.
},
{
nBytes: 10000, hashes: []common.Hash{s.chain.RootAt(0), s.chain.RootAt(0)},
- expHashes: 0,
+ expHashes: 2, // 32-byte keys are detected as code, even if not code (like genesis hash), in legacy lookups.
},
// Empties
{
diff --git go-ethereum/core/blockchain.go op-geth/core/blockchain.go
index 659b2f02ea9ef7acdfbc24ed3ef2b3ba0b1a30ce..67658ec752a681bb88b6fb1776837701c9c58434 100644
--- go-ethereum/core/blockchain.go
+++ op-geth/core/blockchain.go
@@ -254,6 +254,10 @@ }
log.Info(strings.Repeat("-", 153))
log.Info("")
+ if chainConfig.IsOptimism() && chainConfig.RegolithTime == nil {
+ log.Warn("Optimism RegolithTime has not been set")
+ }
+
bc := &BlockChain{
chainConfig: chainConfig,
cacheConfig: cacheConfig,
diff --git go-ethereum/core/blockchain_reader.go op-geth/core/blockchain_reader.go
index fd65cb2db32f8faac481355cb7e0e0761adb5370..c0ea4bef3b445cab57ee8b18a0b53f130226cbb7 100644
--- go-ethereum/core/blockchain_reader.go
+++ op-geth/core/blockchain_reader.go
@@ -311,6 +311,14 @@ }
return bc.stateCache.(codeReader).ContractCodeWithPrefix(common.Hash{}, hash)
}
+// ContractCode retrieves a blob of data associated with a contract hash
+// either from ephemeral in-memory cache, or from persistent storage.
+// This is a legacy-method, replaced by ContractCodeWithPrefix,
+// but required for old databases to serve snap-sync.
+func (bc *BlockChain) ContractCode(hash common.Hash) ([]byte, error) {
+ return bc.stateCache.ContractCode(common.Hash{}, hash)
+}
+
// State returns a new mutable state based on the current HEAD block.
func (bc *BlockChain) State() (*state.StateDB, error) {
return bc.StateAt(bc.CurrentBlock().Root)
diff --git go-ethereum/core/error.go op-geth/core/error.go
index 872ba8d365d8c152641f2dcd32b39343a285bd4d..d99b85e2490725b35c5797c93060ac7009b909d8 100644
--- go-ethereum/core/error.go
+++ op-geth/core/error.go
@@ -100,4 +100,7 @@ ErrFeeCapTooLow = errors.New("max fee per gas less than block base fee")
// ErrSenderNoEOA is returned if the sender of a transaction is a contract.
ErrSenderNoEOA = errors.New("sender not an eoa")
+
+ // ErrSystemTxNotSupported is returned for any deposit tx with IsSystemTx=true after the Regolith fork
+ ErrSystemTxNotSupported = errors.New("system tx not supported")
)
diff --git go-ethereum/core/evm.go op-geth/core/evm.go
index bd4f2b0e55f1cf039549830eed93514272962171..527a81ad29d6d1b2faedb37579fce28d2ad24efd 100644
--- go-ethereum/core/evm.go
+++ op-geth/core/evm.go
@@ -23,6 +23,7 @@ "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/params"
)
// ChainContext supports retrieving headers and consensus parameters from the
@@ -36,7 +37,7 @@ GetHeader(common.Hash, uint64) *types.Header
}
// NewEVMBlockContext creates a new context for use in the EVM.
-func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common.Address) vm.BlockContext {
+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
@@ -66,6 +67,7 @@ Difficulty: new(big.Int).Set(header.Difficulty),
BaseFee: baseFee,
GasLimit: header.GasLimit,
Random: random,
+ L1CostFunc: types.NewL1CostFunc(config, statedb),
}
}
diff --git go-ethereum/core/gen_genesis.go op-geth/core/gen_genesis.go
index 4e0844e889ab4e470790529515a0d5317b4a7a12..9a682b64ac54ed56a82757d745c40a017f4cadef 100644
--- go-ethereum/core/gen_genesis.go
+++ op-geth/core/gen_genesis.go
@@ -31,6 +31,7 @@ Number math.HexOrDecimal64 `json:"number"`
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
ParentHash common.Hash `json:"parentHash"`
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
+ StateHash *common.Hash `json:"stateHash,omitempty"`
}
var enc Genesis
enc.Config = g.Config
@@ -51,6 +52,7 @@ enc.Number = math.HexOrDecimal64(g.Number)
enc.GasUsed = math.HexOrDecimal64(g.GasUsed)
enc.ParentHash = g.ParentHash
enc.BaseFee = (*math.HexOrDecimal256)(g.BaseFee)
+ enc.StateHash = g.StateHash
return json.Marshal(&enc)
}
@@ -70,6 +72,7 @@ Number *math.HexOrDecimal64 `json:"number"`
GasUsed *math.HexOrDecimal64 `json:"gasUsed"`
ParentHash *common.Hash `json:"parentHash"`
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
+ StateHash *common.Hash `json:"stateHash,omitempty"`
}
var dec Genesis
if err := json.Unmarshal(input, &dec); err != nil {
@@ -119,6 +122,9 @@ g.ParentHash = *dec.ParentHash
}
if dec.BaseFee != nil {
g.BaseFee = (*big.Int)(dec.BaseFee)
+ }
+ if dec.StateHash != nil {
+ g.StateHash = dec.StateHash
}
return nil
}
diff --git go-ethereum/core/rawdb/accessors_chain.go op-geth/core/rawdb/accessors_chain.go
index b479655b0b7469c47db053dd8591a418e865b495..de3337dfe0de2e03ab345aa2170b4fea9a0db7be 100644
--- go-ethereum/core/rawdb/accessors_chain.go
+++ op-geth/core/rawdb/accessors_chain.go
@@ -637,13 +637,11 @@ log.Error("Missing body but have receipt", "hash", hash, "number", number)
return nil
}
header := ReadHeader(db, hash, number)
- var baseFee *big.Int
if header == nil {
- baseFee = big.NewInt(0)
- } else {
- baseFee = header.BaseFee
+ log.Error("Missing header but have receipt", "hash", hash, "number", number)
+ return nil
}
- if err := receipts.DeriveFields(config, hash, number, time, baseFee, body.Transactions); err != nil {
+ if err := receipts.DeriveFields(config, hash, number, time, header.BaseFee, body.Transactions); err != nil {
log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err)
return nil
}
@@ -681,6 +679,15 @@ type storedReceiptRLP struct {
PostStateOrStatus []byte
CumulativeGasUsed uint64
Logs []*types.Log
+
+ // Remaining fields are declared to allow the receipt RLP to be parsed without errors.
+ // However, they must not be used as they may not be populated correctly due to multiple receipt formats
+ // being combined into a single list of optional fields which can be mistaken for each other.
+ // DepositNonce (*uint64) from Regolith deposit tx receipts will be parsed into L1GasUsed
+ L1GasUsed *big.Int `rlp:"optional"` // OVM legacy
+ L1GasPrice *big.Int `rlp:"optional"` // OVM legacy
+ L1Fee *big.Int `rlp:"optional"` // OVM legacy
+ FeeScalar string `rlp:"optional"` // OVM legacy
}
// ReceiptLogs is a barebone version of ReceiptForStorage which only keeps
diff --git go-ethereum/core/rawdb/accessors_chain_test.go op-geth/core/rawdb/accessors_chain_test.go
index 32e38a81ce4da94a86de28d07361913612e60b7d..9eb4103f0ae058431a7c9625c2c80750b9a52d82 100644
--- go-ethereum/core/rawdb/accessors_chain_test.go
+++ op-geth/core/rawdb/accessors_chain_test.go
@@ -27,10 +27,12 @@ "reflect"
"testing"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
+ "github.com/stretchr/testify/require"
"golang.org/x/crypto/sha3"
)
@@ -347,6 +349,9 @@ // Create a live block since we need metadata to reconstruct the receipt
tx1 := types.NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil)
tx2 := types.NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, big.NewInt(2), nil)
+ header := &types.Header{
+ Number: big.NewInt(0),
+ }
body := &types.Body{Transactions: types.Transactions{tx1, tx2}}
// Create the two receipts to manage afterwards
@@ -378,12 +383,15 @@ receipt2.Bloom = types.CreateBloom(types.Receipts{receipt2})
receipts := []*types.Receipt{receipt1, receipt2}
// Check that no receipt entries are in a pristine database
- hash := common.BytesToHash([]byte{0x03, 0x14})
+ hash := header.Hash()
if rs := ReadReceipts(db, hash, 0, 0, params.TestChainConfig); len(rs) != 0 {
t.Fatalf("non existent receipts returned: %v", rs)
}
// Insert the body that corresponds to the receipts
WriteBody(db, hash, 0, body)
+
+ // Insert the header that corresponds to the receipts
+ WriteHeader(db, header)
// Insert the receipt slice into the database and check presence
WriteReceipts(db, hash, 0, receipts)
@@ -698,6 +706,7 @@
body := &types.Body{Transactions: types.Transactions{tx1, tx2}}
// Create the two receipts to manage afterwards
+ depositNonce := uint64(math.MaxUint64)
receipt1 := &types.Receipt{
Status: types.ReceiptStatusFailed,
CumulativeGasUsed: 1,
@@ -708,6 +717,7 @@ },
TxHash: tx1.Hash(),
ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
GasUsed: 111111,
+ DepositNonce: &depositNonce,
}
receipt1.Bloom = types.CreateBloom(types.Receipts{receipt1})
@@ -765,6 +775,36 @@ t.Fatalf("receipt #%d: receipt mismatch: have %s, want %s", i, hex.EncodeToString(rlpHave), hex.EncodeToString(rlpWant))
}
}
}
+}
+
+func TestParseLegacyReceiptRLP(t *testing.T) {
+ // Create a gasUsed value greater than a uint64 can represent
+ gasUsed := big.NewInt(0)
+ gasUsed = gasUsed.SetUint64(math.MaxUint64)
+ gasUsed = gasUsed.Add(gasUsed, big.NewInt(math.MaxInt64))
+ sanityCheck := (&big.Int{}).SetUint64(gasUsed.Uint64())
+ require.NotEqual(t, gasUsed, sanityCheck)
+ receipt := types.LegacyOptimismStoredReceiptRLP{
+ CumulativeGasUsed: 1,
+ Logs: []*types.LogForStorage{
+ {Address: common.BytesToAddress([]byte{0x11})},
+ {Address: common.BytesToAddress([]byte{0x01, 0x11})},
+ },
+ L1GasUsed: gasUsed,
+ L1GasPrice: gasUsed,
+ L1Fee: gasUsed,
+ FeeScalar: "6",
+ }
+
+ data, err := rlp.EncodeToBytes(receipt)
+ require.NoError(t, err)
+ var result storedReceiptRLP
+ err = rlp.DecodeBytes(data, &result)
+ require.NoError(t, err)
+ require.Equal(t, receipt.L1GasUsed, result.L1GasUsed)
+ require.Equal(t, receipt.L1GasPrice, result.L1GasPrice)
+ require.Equal(t, receipt.L1Fee, result.L1Fee)
+ require.Equal(t, receipt.FeeScalar, result.FeeScalar)
}
func TestDeriveLogFields(t *testing.T) {
diff --git go-ethereum/core/superchain.go op-geth/core/superchain.go
new file mode 100644
index 0000000000000000000000000000000000000000..96f68cedcf437f251d18e35764e4d49f4ddcab6b
--- /dev/null
+++ op-geth/core/superchain.go
@@ -0,0 +1,99 @@
+package core
+
+import (
+ "fmt"
+ "math/big"
+
+ "github.com/ethereum-optimism/superchain-registry/superchain"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+func LoadOPStackGenesis(chainID uint64) (*Genesis, error) {
+ chConfig, ok := superchain.OPChains[chainID]
+ if !ok {
+ return nil, fmt.Errorf("unknown chain ID: %d", chainID)
+ }
+
+ cfg, err := params.LoadOPStackChainConfig(chainID)
+ if err != nil {
+ return nil, fmt.Errorf("failed to load params.ChainConfig for chain %d: %w", chainID, err)
+ }
+
+ gen, err := superchain.LoadGenesis(chainID)
+ if err != nil {
+ return nil, fmt.Errorf("failed to load genesis definition for chain %d: %w", chainID, err)
+ }
+
+ genesis := &Genesis{
+ Config: cfg,
+ Nonce: gen.Nonce,
+ Timestamp: gen.Timestamp,
+ ExtraData: gen.ExtraData,
+ GasLimit: gen.GasLimit,
+ Difficulty: (*big.Int)(gen.Difficulty),
+ Mixhash: common.Hash(gen.Mixhash),
+ Coinbase: common.Address(gen.Coinbase),
+ Alloc: make(GenesisAlloc),
+ Number: gen.Number,
+ GasUsed: gen.GasUsed,
+ ParentHash: common.Hash(gen.ParentHash),
+ BaseFee: (*big.Int)(gen.BaseFee),
+ }
+
+ for addr, acc := range gen.Alloc {
+ var code []byte
+ if acc.CodeHash != ([32]byte{}) {
+ dat, err := superchain.LoadContractBytecode(acc.CodeHash)
+ if err != nil {
+ return nil, fmt.Errorf("failed to load bytecode %s of address %s in chain %d: %w", acc.CodeHash, addr, chainID, err)
+ }
+ code = dat
+ }
+ var storage map[common.Hash]common.Hash
+ if len(acc.Storage) > 0 {
+ storage = make(map[common.Hash]common.Hash)
+ for k, v := range acc.Storage {
+ storage[common.Hash(k)] = common.Hash(v)
+ }
+ }
+ bal := common.Big0
+ if acc.Balance != nil {
+ bal = (*big.Int)(acc.Balance)
+ }
+ genesis.Alloc[common.Address(addr)] = GenesisAccount{
+ Code: code,
+ Storage: storage,
+ Balance: bal,
+ Nonce: acc.Nonce,
+ }
+ }
+ if gen.StateHash != nil {
+ if len(gen.Alloc) > 0 {
+ return nil, fmt.Errorf("chain definition unexpectedly contains both allocation (%d) and state-hash %s", len(gen.Alloc), *gen.StateHash)
+ }
+ genesis.StateHash = (*common.Hash)(gen.StateHash)
+ }
+
+ genesisBlock := genesis.ToBlock()
+ genesisBlockHash := genesisBlock.Hash()
+ expectedHash := common.Hash([32]byte(chConfig.Genesis.L2.Hash))
+
+ // Verify we correctly produced the genesis config by recomputing the genesis-block-hash,
+ // and check the genesis matches the chain genesis definition.
+ if chConfig.Genesis.L2.Number != genesisBlock.NumberU64() {
+ switch chainID {
+ case params.OPMainnetChainID:
+ expectedHash = common.HexToHash("0x7ca38a1916c42007829c55e69d3e9a73265554b586a499015373241b8a3fa48b")
+ case params.OPGoerliChainID:
+ expectedHash = common.HexToHash("0xc1fc15cd51159b1f1e5cbc4b82e85c1447ddfa33c52cf1d98d14fba0d6354be1")
+ default:
+ return nil, fmt.Errorf("unknown stateless genesis definition for chain %d", chainID)
+ }
+ }
+ if expectedHash != genesisBlockHash {
+ return nil, fmt.Errorf("produced genesis with hash %s but expected %s", genesisBlockHash, expectedHash)
+ }
+ return genesis, nil
+}
diff --git go-ethereum/core/superchain_test.go op-geth/core/superchain_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..fa3246250ac32aebea1410295f946fd10f25252d
--- /dev/null
+++ op-geth/core/superchain_test.go
@@ -0,0 +1,17 @@
+package core
+
+import (
+ "testing"
+
+ "github.com/ethereum-optimism/superchain-registry/superchain"
+)
+
+func TestOPStackGenesis(t *testing.T) {
+ for id := range superchain.OPChains {
+ gen, err := LoadOPStackGenesis(id)
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Logf("chain: %d, genesis block hash: %s", id, gen.ToBlock().Hash())
+ }
+}
diff --git go-ethereum/core/types/receipt_test.go op-geth/core/types/receipt_test.go
index 168f36208b9eb5e7ef7fd01c947105122b51c31b..809540715e7de6acc5d9a1688aac5705df5203bf 100644
--- go-ethereum/core/types/receipt_test.go
+++ op-geth/core/types/receipt_test.go
@@ -19,6 +19,7 @@
import (
"bytes"
"encoding/json"
+ "fmt"
"math"
"math/big"
"reflect"
@@ -29,6 +30,7 @@ "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256"
"github.com/kylelemons/godebug/diff"
+ "github.com/stretchr/testify/require"
)
var (
@@ -82,6 +84,42 @@ },
},
Type: DynamicFeeTxType,
}
+ depositReceiptNoNonce = &Receipt{
+ Status: ReceiptStatusFailed,
+ CumulativeGasUsed: 1,
+ Logs: []*Log{
+ {
+ Address: common.BytesToAddress([]byte{0x11}),
+ Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
+ Data: []byte{0x01, 0x00, 0xff},
+ },
+ {
+ Address: common.BytesToAddress([]byte{0x01, 0x11}),
+ Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
+ Data: []byte{0x01, 0x00, 0xff},
+ },
+ },
+ Type: DepositTxType,
+ }
+ nonce = uint64(1234)
+ depositReceiptWithNonce = &Receipt{
+ Status: ReceiptStatusFailed,
+ CumulativeGasUsed: 1,
+ DepositNonce: &nonce,
+ Logs: []*Log{
+ {
+ Address: common.BytesToAddress([]byte{0x11}),
+ Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
+ Data: []byte{0x01, 0x00, 0xff},
+ },
+ {
+ Address: common.BytesToAddress([]byte{0x01, 0x11}),
+ Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
+ Data: []byte{0x01, 0x00, 0xff},
+ },
+ },
+ Type: DepositTxType,
+ }
// Create a few transactions to have receipts for
to2 = common.HexToAddress("0x2")
@@ -147,8 +185,13 @@ GasTipCap: uint256.NewInt(77),
GasFeeCap: uint256.NewInt(1077),
BlobFeeCap: uint256.NewInt(100077),
}),
+ NewTx(&DepositTx{
+ To: nil, // contract creation
+ Value: big.NewInt(6),
+ Gas: 50,
+ }),
}
-
+ depNonce = uint64(7)
blockNumber = big.NewInt(1)
blockTime = uint64(2)
blockHash = common.BytesToHash([]byte{0x03, 0x14})
@@ -287,6 +330,41 @@ BlockHash: blockHash,
BlockNumber: blockNumber,
TransactionIndex: 6,
},
+ &Receipt{
+ Type: DepositTxType,
+ PostState: common.Hash{5}.Bytes(),
+ CumulativeGasUsed: 50 + 28,
+ Logs: []*Log{
+ {
+ Address: common.BytesToAddress([]byte{0x33}),
+ Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
+ // derived fields:
+ BlockNumber: blockNumber.Uint64(),
+ TxHash: txs[7].Hash(),
+ TxIndex: 7,
+ BlockHash: blockHash,
+ Index: 4,
+ },
+ {
+ Address: common.BytesToAddress([]byte{0x03, 0x33}),
+ Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
+ // derived fields:
+ BlockNumber: blockNumber.Uint64(),
+ TxHash: txs[7].Hash(),
+ TxIndex: 7,
+ BlockHash: blockHash,
+ Index: 5,
+ },
+ },
+ TxHash: txs[7].Hash(),
+ ContractAddress: common.HexToAddress("0x3bb898b4bbe24f68a4e9be46cfe72d1787fd74f4"),
+ GasUsed: 50,
+ EffectiveGasPrice: big.NewInt(0),
+ BlockHash: blockHash,
+ BlockNumber: blockNumber,
+ TransactionIndex: 7,
+ DepositNonce: &depNonce,
+ },
}
)
@@ -517,3 +595,205 @@ l[i] = &cpy
}
return l
}
+
+func TestDeriveOptimismTxReceipt(t *testing.T) {
+ to4 := common.HexToAddress("0x4")
+ // Create a few transactions to have receipts for
+ txs := Transactions{
+ NewTx(&DepositTx{
+ To: nil, // contract creation
+ Value: big.NewInt(6),
+ Gas: 50,
+ // System config with L1Scalar=2_000_000 (becomes 2 after division), L1Overhead=2500, L1BaseFee=5000
+ Data: common.Hex2Bytes("015d8eb900000000000000000000000000000000000000000000000026b39534042076f70000000000000000000000000000000000000000000000007e33b7c4995967580000000000000000000000000000000000000000000000000000000000001388547dea8ff339566349ed0ef6384876655d1b9b955e36ac165c6b8ab69b9af5cd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123400000000000000000000000000000000000000000000000000000000000009c400000000000000000000000000000000000000000000000000000000001e8480"),
+ }),
+ NewTx(&DynamicFeeTx{
+ To: &to4,
+ Nonce: 4,
+ Value: big.NewInt(4),
+ Gas: 4,
+ GasTipCap: big.NewInt(44),
+ GasFeeCap: big.NewInt(1045),
+ Data: []byte{0, 1, 255, 0},
+ }),
+ }
+ depNonce := uint64(7)
+ blockNumber := big.NewInt(1)
+ blockHash := common.BytesToHash([]byte{0x03, 0x14})
+
+ // 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: &depNonce,
+ },
+ &Receipt{
+ Type: DynamicFeeTxType,
+ PostState: common.Hash{4}.Bytes(),
+ CumulativeGasUsed: 10,
+ Logs: []*Log{},
+ // derived fields:
+ TxHash: txs[1].Hash(),
+ GasUsed: 18446744073709551561,
+ EffectiveGasPrice: big.NewInt(1044),
+ BlockHash: blockHash,
+ BlockNumber: blockNumber,
+ TransactionIndex: 1,
+ L1GasPrice: big.NewInt(5000),
+ L1GasUsed: big.NewInt(3976),
+ L1Fee: big.NewInt(39760000),
+ FeeScalar: big.NewFloat(2),
+ },
+ }
+
+ // Re-derive receipts.
+ basefee := big.NewInt(1000)
+ derivedReceipts := clearComputedFieldsOnReceipts(receipts)
+ err := Receipts(derivedReceipts).DeriveFields(params.OptimismTestConfig, blockHash, blockNumber.Uint64(), 0, basefee, txs)
+ if err != nil {
+ t.Fatalf("DeriveFields(...) = %v, want <nil>", err)
+ }
+
+ // Check diff of receipts against derivedReceipts.
+ r1, err := json.MarshalIndent(receipts, "", " ")
+ if err != nil {
+ t.Fatal("error marshaling input receipts:", err)
+ }
+ r2, err := json.MarshalIndent(derivedReceipts, "", " ")
+ if err != nil {
+ t.Fatal("error marshaling derived receipts:", err)
+ }
+ d := diff.Diff(string(r1), string(r2))
+ if d != "" {
+ t.Fatal("receipts differ:", d)
+ }
+
+ // Check that we preserved the invariant: l1Fee = l1GasPrice * l1GasUsed * l1FeeScalar
+ // but with more difficult int math...
+ l2Rcpt := derivedReceipts[1]
+ l1GasCost := new(big.Int).Mul(l2Rcpt.L1GasPrice, l2Rcpt.L1GasUsed)
+ l1Fee := new(big.Float).Mul(new(big.Float).SetInt(l1GasCost), l2Rcpt.FeeScalar)
+ require.Equal(t, new(big.Float).SetInt(l2Rcpt.L1Fee), l1Fee)
+}
+
+func TestBedrockDepositReceiptUnchanged(t *testing.T) {
+ expectedRlp := common.FromHex("7EF90156A003000000000000000000000000000000000000000000000000000000000000000AB9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F0D7940000000000000000000000000000000000000033C001D7940000000000000000000000000000000000000333C002")
+ // Deposit receipt with no nonce
+ receipt := &Receipt{
+ Type: DepositTxType,
+ PostState: common.Hash{3}.Bytes(),
+ CumulativeGasUsed: 10,
+ Logs: []*Log{
+ {Address: common.BytesToAddress([]byte{0x33}), Data: []byte{1}, Topics: []common.Hash{}},
+ {Address: common.BytesToAddress([]byte{0x03, 0x33}), Data: []byte{2}, Topics: []common.Hash{}},
+ },
+ TxHash: common.Hash{},
+ ContractAddress: common.BytesToAddress([]byte{0x03, 0x33, 0x33}),
+ GasUsed: 4,
+ }
+
+ rlp, err := receipt.MarshalBinary()
+ require.NoError(t, err)
+ require.Equal(t, expectedRlp, rlp)
+
+ // Consensus values should be unchanged after reparsing
+ parsed := new(Receipt)
+ err = parsed.UnmarshalBinary(rlp)
+ require.NoError(t, err)
+ require.Equal(t, receipt.Status, parsed.Status)
+ require.Equal(t, receipt.CumulativeGasUsed, parsed.CumulativeGasUsed)
+ require.Equal(t, receipt.Bloom, parsed.Bloom)
+ require.EqualValues(t, receipt.Logs, parsed.Logs)
+ // And still shouldn't have a nonce
+ require.Nil(t, parsed.DepositNonce)
+}
+
+func TestRoundTripReceipt(t *testing.T) {
+ tests := []struct {
+ name string
+ rcpt *Receipt
+ }{
+ {name: "Legacy", rcpt: legacyReceipt},
+ {name: "AccessList", rcpt: accessListReceipt},
+ {name: "EIP1559", rcpt: eip1559Receipt},
+ {name: "DepositNoNonce", rcpt: depositReceiptNoNonce},
+ {name: "DepositWithNonce", rcpt: depositReceiptWithNonce},
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ data, err := test.rcpt.MarshalBinary()
+ require.NoError(t, err)
+
+ d := &Receipt{}
+ err = d.UnmarshalBinary(data)
+ require.NoError(t, err)
+ require.Equal(t, test.rcpt, d)
+ })
+
+ t.Run(fmt.Sprintf("%sRejectExtraData", test.name), func(t *testing.T) {
+ data, err := test.rcpt.MarshalBinary()
+ require.NoError(t, err)
+ data = append(data, 1, 2, 3, 4)
+ d := &Receipt{}
+ err = d.UnmarshalBinary(data)
+ require.Error(t, err)
+ })
+ }
+}
+
+func TestRoundTripReceiptForStorage(t *testing.T) {
+ tests := []struct {
+ name string
+ rcpt *Receipt
+ }{
+ {name: "Legacy", rcpt: legacyReceipt},
+ {name: "AccessList", rcpt: accessListReceipt},
+ {name: "EIP1559", rcpt: eip1559Receipt},
+ {name: "DepositNoNonce", rcpt: depositReceiptNoNonce},
+ {name: "DepositWithNonce", rcpt: depositReceiptWithNonce},
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ data, err := rlp.EncodeToBytes((*ReceiptForStorage)(test.rcpt))
+ require.NoError(t, err)
+
+ d := &ReceiptForStorage{}
+ err = rlp.DecodeBytes(data, d)
+ require.NoError(t, err)
+ // Only check the stored fields - the others are derived later
+ require.Equal(t, test.rcpt.Status, d.Status)
+ require.Equal(t, test.rcpt.CumulativeGasUsed, d.CumulativeGasUsed)
+ require.Equal(t, test.rcpt.Logs, d.Logs)
+ require.Equal(t, test.rcpt.DepositNonce, d.DepositNonce)
+ })
+ }
+}
diff --git go-ethereum/core/types/rollup_l1_cost_test.go op-geth/core/types/rollup_l1_cost_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..e43ea967ee87a257cfc2afa58396c2eb6685ed76
--- /dev/null
+++ op-geth/core/types/rollup_l1_cost_test.go
@@ -0,0 +1,30 @@
+package types
+
+import (
+ "math/rand"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/stretchr/testify/require"
+)
+
+func TestRollupGasData(t *testing.T) {
+ for i := 0; i < 100; i++ {
+ zeroes := rand.Uint64()
+ ones := rand.Uint64()
+
+ r := RollupGasData{
+ Zeroes: zeroes,
+ Ones: ones,
+ }
+ time := uint64(1)
+ cfg := ¶ms.ChainConfig{
+ RegolithTime: &time,
+ }
+ gasPreRegolith := r.DataGas(0, cfg)
+ gasPostRegolith := r.DataGas(1, cfg)
+
+ require.Equal(t, r.Zeroes*params.TxDataZeroGas+(r.Ones+68)*params.TxDataNonZeroGasEIP2028, gasPreRegolith)
+ require.Equal(t, r.Zeroes*params.TxDataZeroGas+r.Ones*params.TxDataNonZeroGasEIP2028, gasPostRegolith)
+ }
+}
diff --git go-ethereum/core/types/tx_blob.go op-geth/core/types/tx_blob.go
index 58065d0017d314c13e0047d19203f37592aff33a..4b6caa5a996046344acccdce0be107c5749d1db4 100644
--- go-ethereum/core/types/tx_blob.go
+++ op-geth/core/types/tx_blob.go
@@ -108,6 +108,7 @@ func (tx *BlobTx) to() *common.Address { return tx.To }
func (tx *BlobTx) blobGas() uint64 { return params.BlobTxDataGasPerBlob * uint64(len(tx.BlobHashes)) }
func (tx *BlobTx) blobGasFeeCap() *big.Int { return tx.BlobFeeCap.ToBig() }
func (tx *BlobTx) blobHashes() []common.Hash { return tx.BlobHashes }
+func (tx *BlobTx) isSystemTx() bool { return false }
func (tx *BlobTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
if baseFee == nil {
diff --git go-ethereum/eth/api.go op-geth/eth/api.go
index 00ad48fbfc8f03597db7a3802c887390cb8dc4f6..96c20d60206b586510202fac10152cfa5f6e0439 100644
--- go-ethereum/eth/api.go
+++ op-geth/eth/api.go
@@ -261,6 +261,9 @@ // If we're dumping the pending state, we need to request
// both the pending block as well as the pending state from
// the miner and operate on those
_, stateDb := api.eth.miner.Pending()
+ if stateDb == nil {
+ return state.Dump{}, errors.New("no pending state")
+ }
return stateDb.RawDump(opts), nil
}
var header *types.Header
@@ -320,7 +323,7 @@ blockRlp = err.Error() // Hacky, but hey, it works
} else {
blockRlp = fmt.Sprintf("%#x", rlpBytes)
}
- if blockJSON, err = ethapi.RPCMarshalBlock(block, true, true, api.eth.APIBackend.ChainConfig()); err != nil {
+ if blockJSON, err = ethapi.RPCMarshalBlock(ctx, block, true, true, api.eth.APIBackend.ChainConfig(), api.eth.APIBackend); err != nil {
blockJSON = map[string]interface{}{"error": err.Error()}
}
results = append(results, &BadBlockArgs{
@@ -346,6 +349,9 @@ // If we're dumping the pending state, we need to request
// both the pending block as well as the pending state from
// the miner and operate on those
_, stateDb = api.eth.miner.Pending()
+ if stateDb == nil {
+ return state.IteratorDump{}, errors.New("no pending state")
+ }
} else {
var header *types.Header
if number == rpc.LatestBlockNumber {
diff --git go-ethereum/eth/gasprice/gasprice.go op-geth/eth/gasprice/gasprice.go
index a3dd83d79fb20f25ad005189c2350472353999f1..41d6bf38b1a91bbd335897cde47847b3526cce44 100644
--- go-ethereum/eth/gasprice/gasprice.go
+++ op-geth/eth/gasprice/gasprice.go
@@ -37,6 +37,8 @@
var (
DefaultMaxPrice = big.NewInt(500 * params.GWei)
DefaultIgnorePrice = big.NewInt(2 * params.Wei)
+
+ DefaultMinSuggestedPriorityFee = big.NewInt(1e6 * params.Wei) // 0.001 gwei, for Optimism fee suggestion
)
type Config struct {
@@ -47,6 +49,8 @@ MaxBlockHistory uint64
Default *big.Int `toml:",omitempty"`
MaxPrice *big.Int `toml:",omitempty"`
IgnorePrice *big.Int `toml:",omitempty"`
+
+ MinSuggestedPriorityFee *big.Int `toml:",omitempty"` // for Optimism fee suggestion
}
// OracleBackend includes all necessary background APIs for oracle.
@@ -74,6 +78,8 @@ checkBlocks, percentile int
maxHeaderHistory, maxBlockHistory uint64
historyCache *lru.Cache[cacheKey, processedFees]
+
+ minSuggestedPriorityFee *big.Int // for Optimism fee suggestion
}
// NewOracle returns a new gasprice oracle which can recommend suitable
@@ -128,7 +134,7 @@ lastHead = ev.Block.Hash()
}
}()
- return &Oracle{
+ r := &Oracle{
backend: backend,
lastPrice: params.Default,
maxPrice: maxPrice,
@@ -139,6 +145,17 @@ maxHeaderHistory: maxHeaderHistory,
maxBlockHistory: maxBlockHistory,
historyCache: cache,
}
+
+ if backend.ChainConfig().IsOptimism() {
+ r.minSuggestedPriorityFee = params.MinSuggestedPriorityFee
+ if r.minSuggestedPriorityFee == nil || r.minSuggestedPriorityFee.Int64() <= 0 {
+ r.minSuggestedPriorityFee = DefaultMinSuggestedPriorityFee
+ log.Warn("Sanitizing invalid optimism gasprice oracle min priority fee suggestion",
+ "provided", params.MinSuggestedPriorityFee,
+ "updated", r.minSuggestedPriorityFee)
+ }
+ }
+ return r
}
// SuggestTipCap returns a tip cap so that newly created transaction can have a
@@ -168,6 +185,11 @@ oracle.cacheLock.RUnlock()
if headHash == lastHead {
return new(big.Int).Set(lastPrice), nil
}
+
+ if oracle.backend.ChainConfig().IsOptimism() {
+ return oracle.SuggestOptimismPriorityFee(ctx, head, headHash), nil
+ }
+
var (
sent, exp int
number = head.Number.Uint64()
diff --git go-ethereum/eth/gasprice/optimism-gasprice.go op-geth/eth/gasprice/optimism-gasprice.go
new file mode 100644
index 0000000000000000000000000000000000000000..71cd021f637f92e4b6d99ec5fc337d27edf547e5
--- /dev/null
+++ op-geth/eth/gasprice/optimism-gasprice.go
@@ -0,0 +1,110 @@
+package gasprice
+
+import (
+ "context"
+ "math/big"
+ "sort"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/rpc"
+)
+
+// SuggestOptimismPriorityFee returns a max priority fee value that can be used such that newly
+// created transactions have a very high chance to be included in the following blocks, using a
+// simplified and more predictable algorithm appropriate for chains like Optimism with a single
+// known block builder.
+//
+// In the typical case, which results whenever the last block had room for more transactions, this
+// function returns a minimum suggested priority fee value. Otherwise it returns the higher of this
+// minimum suggestion or 10% over the median effective priority fee from the last block.
+//
+// Rationale: For a chain such as Optimism where there is a single block builder whose behavior is
+// known, we know priority fee (as long as it is non-zero) has no impact on the probability for tx
+// inclusion as long as there is capacity for it in the block. In this case then, there's no reason
+// to return any value higher than some fixed minimum. Blocks typically reach capacity only under
+// extreme events such as airdrops, meaning predicting whether the next block is going to be at
+// capacity is difficult *except* in the case where we're already experiencing the increased demand
+// from such an event. We therefore expect whether the last known block is at capacity to be one of
+// the best predictors of whether the next block is likely to be at capacity. (An even better
+// predictor is to look at the state of the transaction pool, but we want an algorithm that works
+// even if the txpool is private or unavailable.)
+//
+// In the event the next block may be at capacity, the algorithm should allow for average fees to
+// rise in order to reach a market price that appropriately reflects demand. We accomplish this by
+// returning a suggestion that is a significant amount (10%) higher than the median effective
+// priority fee from the previous block.
+func (oracle *Oracle) SuggestOptimismPriorityFee(ctx context.Context, h *types.Header, headHash common.Hash) *big.Int {
+ suggestion := new(big.Int).Set(oracle.minSuggestedPriorityFee)
+
+ // find the maximum gas used by any of the transactions in the block to use as the capacity
+ // margin
+ receipts, err := oracle.backend.GetReceipts(ctx, headHash)
+ if receipts == nil || err != nil {
+ log.Error("failed to get block receipts", "err", err)
+ return suggestion
+ }
+ var maxTxGasUsed uint64
+ for i := range receipts {
+ gu := receipts[i].GasUsed
+ if gu > maxTxGasUsed {
+ maxTxGasUsed = gu
+ }
+ }
+
+ // sanity check the max gas used value
+ if maxTxGasUsed > h.GasLimit {
+ log.Error("found tx consuming more gas than the block limit", "gas", maxTxGasUsed)
+ return suggestion
+ }
+
+ if h.GasUsed+maxTxGasUsed > h.GasLimit {
+ // A block is "at capacity" if, when it is built, there is a pending tx in the txpool that
+ // could not be included because the block's gas limit would be exceeded. Since we don't
+ // have access to the txpool, we instead adopt the following heuristic: consider a block as
+ // at capacity if the total gas consumed by its transactions is within max-tx-gas-used of
+ // the block limit, where max-tx-gas-used is the most gas used by any one transaction
+ // within the block. This heuristic is almost perfectly accurate when transactions always
+ // consume the same amount of gas, but becomes less accurate as tx gas consumption begins
+ // to vary. The typical error is we assume a block is at capacity when it was not because
+ // max-tx-gas-used will in most cases over-estimate the "capacity margin". But it's better
+ // to err on the side of returning a higher-than-needed suggestion than a lower-than-needed
+ // one in order to satisfy our desire for high chance of inclusion and rising fees under
+ // high demand.
+ block, err := oracle.backend.BlockByNumber(ctx, rpc.BlockNumber(h.Number.Int64()))
+ if block == nil || err != nil {
+ log.Error("failed to get last block", "err", err)
+ return suggestion
+ }
+ baseFee := block.BaseFee()
+ txs := block.Transactions()
+ if len(txs) == 0 {
+ log.Error("block was at capacity but doesn't have transactions")
+ return suggestion
+ }
+ tips := bigIntArray(make([]*big.Int, len(txs)))
+ for i := range txs {
+ tips[i] = txs[i].EffectiveGasTipValue(baseFee)
+ }
+ sort.Sort(tips)
+ median := tips[len(tips)/2]
+ newSuggestion := new(big.Int).Add(median, new(big.Int).Div(median, big.NewInt(10)))
+ // use the new suggestion only if it's bigger than the minimum
+ if newSuggestion.Cmp(suggestion) > 0 {
+ suggestion = newSuggestion
+ }
+ }
+
+ // the suggestion should be capped by oracle.maxPrice
+ if suggestion.Cmp(oracle.maxPrice) > 0 {
+ suggestion.Set(oracle.maxPrice)
+ }
+
+ oracle.cacheLock.Lock()
+ oracle.lastHead = headHash
+ oracle.lastPrice = suggestion
+ oracle.cacheLock.Unlock()
+
+ return new(big.Int).Set(suggestion)
+}
diff --git go-ethereum/eth/gasprice/optimism-gasprice_test.go op-geth/eth/gasprice/optimism-gasprice_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..dd0140839d91e45be16d06faae9b714ad53265fa
--- /dev/null
+++ op-geth/eth/gasprice/optimism-gasprice_test.go
@@ -0,0 +1,142 @@
+// Copyright 2020 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package gasprice
+
+import (
+ "context"
+ "math/big"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/rpc"
+ "github.com/ethereum/go-ethereum/trie"
+)
+
+const (
+ blockGasLimit = params.TxGas * 3
+)
+
+type testTxData struct {
+ priorityFee int64
+ gasLimit uint64
+}
+
+type opTestBackend struct {
+ block *types.Block
+ receipts []*types.Receipt
+}
+
+func (b *opTestBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
+ panic("not implemented")
+}
+
+func (b *opTestBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) {
+ return b.block, nil
+}
+
+func (b *opTestBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
+ return b.receipts, nil
+}
+
+func (b *opTestBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
+ panic("not implemented")
+}
+
+func (b *opTestBackend) ChainConfig() *params.ChainConfig {
+ return params.OptimismTestConfig
+}
+
+func (b *opTestBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
+ return nil
+}
+
+func newOpTestBackend(t *testing.T, txs []testTxData) *opTestBackend {
+ var (
+ key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ signer = types.LatestSigner(params.TestChainConfig)
+ )
+ // only the most recent block is considered for optimism priority fee suggestions, so this is
+ // where we add the test transactions
+ ts := []*types.Transaction{}
+ rs := []*types.Receipt{}
+ header := types.Header{}
+ header.GasLimit = blockGasLimit
+ var nonce uint64
+ for _, tx := range txs {
+ txdata := &types.DynamicFeeTx{
+ ChainID: params.TestChainConfig.ChainID,
+ Nonce: nonce,
+ To: &common.Address{},
+ Gas: params.TxGas,
+ GasFeeCap: big.NewInt(100 * params.GWei),
+ GasTipCap: big.NewInt(tx.priorityFee),
+ Data: []byte{},
+ }
+ t := types.MustSignNewTx(key, signer, txdata)
+ ts = append(ts, t)
+ r := types.Receipt{}
+ r.GasUsed = tx.gasLimit
+ header.GasUsed += r.GasUsed
+ rs = append(rs, &r)
+ nonce++
+ }
+ hasher := trie.NewStackTrie(nil)
+ b := types.NewBlock(&header, ts, nil, nil, hasher)
+ return &opTestBackend{block: b, receipts: rs}
+}
+
+func TestSuggestOptimismPriorityFee(t *testing.T) {
+ minSuggestion := new(big.Int).SetUint64(1e8 * params.Wei)
+ var cases = []struct {
+ txdata []testTxData
+ want *big.Int
+ }{
+ {
+ // block well under capacity, expect min priority fee suggestion
+ txdata: []testTxData{testTxData{params.GWei, 21000}},
+ want: minSuggestion,
+ },
+ {
+ // 2 txs, still under capacity, expect min priority fee suggestion
+ txdata: []testTxData{testTxData{params.GWei, 21000}, testTxData{params.GWei, 21000}},
+ want: minSuggestion,
+ },
+ {
+ // 2 txs w same priority fee (1 gwei), but second tx puts it right over capacity
+ txdata: []testTxData{testTxData{params.GWei, 21000}, testTxData{params.GWei, 21001}},
+ want: big.NewInt(1100000000), // 10 percent over 1 gwei, the median
+ },
+ {
+ // 3 txs, full block. return 10% over the median tx (10 gwei * 10% == 11 gwei)
+ txdata: []testTxData{testTxData{10 * params.GWei, 21000}, testTxData{1 * params.GWei, 21000}, testTxData{100 * params.GWei, 21000}},
+ want: big.NewInt(11 * params.GWei),
+ },
+ }
+ for i, c := range cases {
+ backend := newOpTestBackend(t, c.txdata)
+ oracle := NewOracle(backend, Config{MinSuggestedPriorityFee: minSuggestion})
+ got := oracle.SuggestOptimismPriorityFee(context.Background(), backend.block.Header(), backend.block.Hash())
+ if got.Cmp(c.want) != 0 {
+ t.Errorf("Gas price mismatch for test case %d: want %d, got %d", i, c.want, got)
+ }
+ }
+}
diff --git go-ethereum/eth/protocols/snap/handler.go op-geth/eth/protocols/snap/handler.go
index 55781ac54b74c1b9d111548a83feac79d385e37b..c57ac5e2e386c56c6775265c54cd5facbf18a215 100644
--- go-ethereum/eth/protocols/snap/handler.go
+++ op-geth/eth/protocols/snap/handler.go
@@ -469,7 +469,7 @@ if hash == types.EmptyCodeHash {
// Peers should not request the empty code, but if they do, at
// least sent them back a correct response without db lookups
codes = append(codes, []byte{})
- } else if blob, err := chain.ContractCodeWithPrefix(hash); err == nil {
+ } else if blob, err := chain.ContractCode(hash); err == nil {
codes = append(codes, blob)
bytes += uint64(len(blob))
}
diff --git go-ethereum/light/odr_test.go op-geth/light/odr_test.go
index 0df1fbf2f5b29c12c5b80e0fca4372b6dec17d07..6d58f9aab8bf319379786dba02b58e71ce1c0545 100644
--- go-ethereum/light/odr_test.go
+++ op-geth/light/odr_test.go
@@ -212,7 +212,7 @@ Data: data,
SkipAccountChecks: true,
}
txContext := core.NewEVMTxContext(msg)
- context := core.NewEVMBlockContext(header, chain, nil)
+ context := core.NewEVMBlockContext(header, chain, nil, config, st)
vmenv := vm.NewEVM(context, txContext, st, config, vm.Config{NoBaseFee: true})
gp := new(core.GasPool).AddGas(math.MaxUint64)
result, _ := core.ApplyMessage(vmenv, msg, gp)
diff --git go-ethereum/p2p/server.go op-geth/p2p/server.go
index f7bf948b6901249e49e06c04e08597e16b6ea260..6aa5d29d8ee301b7a77b8b6812a38d0d4a8ef7dd 100644
--- go-ethereum/p2p/server.go
+++ op-geth/p2p/server.go
@@ -621,6 +621,7 @@ }
if err != nil {
return err
}
+ srv.discmix.AddSource(srv.DiscV5.RandomNodes())
}
return nil
}
diff --git go-ethereum/params/config_test.go op-geth/params/config_test.go
index bf8ce2fc5e247242615ff478a455785f9f07f88b..14d7f833bb978c2bb7cefc70cff2015079c9a04a 100644
--- go-ethereum/params/config_test.go
+++ op-geth/params/config_test.go
@@ -137,3 +137,22 @@ if r := c.Rules(big.NewInt(0), true, stamp); !r.IsShanghai {
t.Errorf("expected %v to be shanghai", stamp)
}
}
+
+func TestConfigRulesRegolith(t *testing.T) {
+ c := &ChainConfig{
+ RegolithTime: newUint64(500),
+ Optimism: &OptimismConfig{},
+ }
+ var stamp uint64
+ if r := c.Rules(big.NewInt(0), true, stamp); r.IsOptimismRegolith {
+ t.Errorf("expected %v to not be regolith", stamp)
+ }
+ stamp = 500
+ if r := c.Rules(big.NewInt(0), true, stamp); !r.IsOptimismRegolith {
+ t.Errorf("expected %v to be regolith", stamp)
+ }
+ stamp = math.MaxInt64
+ if r := c.Rules(big.NewInt(0), true, stamp); !r.IsOptimismRegolith {
+ t.Errorf("expected %v to be regolith", stamp)
+ }
+}
diff --git go-ethereum/params/superchain.go op-geth/params/superchain.go
new file mode 100644
index 0000000000000000000000000000000000000000..f46c4eb0451d8660eaf4ed56875dd789b5c400aa
--- /dev/null
+++ op-geth/params/superchain.go
@@ -0,0 +1,96 @@
+package params
+
+import (
+ "fmt"
+ "math/big"
+
+ "github.com/ethereum-optimism/superchain-registry/superchain"
+ "github.com/ethereum/go-ethereum/common"
+)
+
+func init() {
+ for id, ch := range superchain.OPChains {
+ NetworkNames[fmt.Sprintf("%d", id)] = ch.Name
+ }
+}
+
+func OPStackChainIDByName(name string) (uint64, error) {
+ for id, ch := range superchain.OPChains {
+ if ch.Chain+"-"+ch.Superchain == name {
+ return id, nil
+ }
+ }
+ return 0, fmt.Errorf("unknown chain %q", name)
+}
+
+func LoadOPStackChainConfig(chainID uint64) (*ChainConfig, error) {
+ chConfig, ok := superchain.OPChains[chainID]
+ if !ok {
+ return nil, fmt.Errorf("unknown chain ID: %d", chainID)
+ }
+ superchainConfig, ok := superchain.Superchains[chConfig.Superchain]
+ if !ok {
+ return nil, fmt.Errorf("unknown superchain %q", chConfig.Superchain)
+ }
+
+ genesisActivation := uint64(0)
+ out := &ChainConfig{
+ ChainID: new(big.Int).SetUint64(chainID),
+ HomesteadBlock: common.Big0,
+ DAOForkBlock: nil,
+ DAOForkSupport: false,
+ EIP150Block: common.Big0,
+ EIP155Block: common.Big0,
+ EIP158Block: common.Big0,
+ ByzantiumBlock: common.Big0,
+ ConstantinopleBlock: common.Big0,
+ PetersburgBlock: common.Big0,
+ IstanbulBlock: common.Big0,
+ MuirGlacierBlock: common.Big0,
+ BerlinBlock: common.Big0,
+ LondonBlock: common.Big0,
+ ArrowGlacierBlock: common.Big0,
+ GrayGlacierBlock: common.Big0,
+ MergeNetsplitBlock: common.Big0,
+ ShanghaiTime: nil,
+ CancunTime: nil,
+ PragueTime: nil,
+ BedrockBlock: common.Big0,
+ RegolithTime: &genesisActivation,
+ TerminalTotalDifficulty: common.Big0,
+ TerminalTotalDifficultyPassed: true,
+ Ethash: nil,
+ Clique: nil,
+ Optimism: &OptimismConfig{
+ EIP1559Elasticity: 6,
+ EIP1559Denominator: 50,
+ },
+ }
+
+ // note: no actual parameters are being loaded, yet.
+ // Future superchain upgrades are loaded from the superchain chConfig and applied to the geth ChainConfig here.
+ _ = superchainConfig.Config
+
+ // special overrides for OP-Stack chains with pre-Regolith upgrade history
+ switch chainID {
+ case OPGoerliChainID:
+ out.LondonBlock = big.NewInt(4061224)
+ out.ArrowGlacierBlock = big.NewInt(4061224)
+ out.GrayGlacierBlock = big.NewInt(4061224)
+ out.MergeNetsplitBlock = big.NewInt(4061224)
+ out.BedrockBlock = big.NewInt(4061224)
+ out.RegolithTime = &OptimismGoerliRegolithTime
+ out.Optimism.EIP1559Elasticity = 10
+ case OPMainnetChainID:
+ out.BerlinBlock = big.NewInt(3950000)
+ out.LondonBlock = big.NewInt(105235063)
+ out.ArrowGlacierBlock = big.NewInt(105235063)
+ out.GrayGlacierBlock = big.NewInt(105235063)
+ out.MergeNetsplitBlock = big.NewInt(105235063)
+ out.BedrockBlock = big.NewInt(105235063)
+ case BaseGoerliChainID:
+ out.RegolithTime = &BaseGoerliRegolithTime
+ }
+
+ return out, nil
+}
diff --git go-ethereum/tests/state_test.go op-geth/tests/state_test.go
index 9d3862e1dc7c2c243982dd08808d487f803e3779..2f54ccdbc74f6d7210ae1a32d777ad469d3604cf 100644
--- go-ethereum/tests/state_test.go
+++ op-geth/tests/state_test.go
@@ -221,7 +221,7 @@ }
// Prepare the EVM.
txContext := core.NewEVMTxContext(msg)
- context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase)
+ context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase, config, statedb)
context.GetHash = vmTestBlockHash
context.BaseFee = baseFee
evm := vm.NewEVM(context, txContext, statedb, config, vmconfig)
diff --git go-ethereum/tests/state_test_util.go op-geth/tests/state_test_util.go
index eec91b67a7d461b0264ab4347c6407993d71379e..83b68da5769ca3b14ea2bf1675281a97ca9a8b62 100644
--- go-ethereum/tests/state_test_util.go
+++ op-geth/tests/state_test_util.go
@@ -247,7 +247,7 @@ }
// Prepare the EVM.
txContext := core.NewEVMTxContext(msg)
- context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase)
+ context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase, config, statedb)
context.GetHash = vmTestBlockHash
context.BaseFee = baseFee
context.Random = nil