Commit 0f8c7ce5 authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into seb/fix-frame-parsing

parents 5c49c0e6 1e780a39
---
'@eth-optimism/proxyd': minor
---
Add support for global method override rate limit
......@@ -178,4 +178,4 @@ require (
lukechampine.com/blake3 v1.1.7 // indirect
)
replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468
replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20230214215134-401b7fd3309b
......@@ -203,8 +203,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z8veEq5ZO3DfIhZ7xgRP9WTc=
github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs=
github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468 h1:7KgjBYDji5AKi42eRYI+n8Gs+ZJVilSASL3WBu82c3M=
github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468/go.mod h1:p0Yox74PhYlq1HvijrCBCD9A3cI7rXco7hT6KrQr+rY=
github.com/ethereum-optimism/op-geth v0.0.0-20230214215134-401b7fd3309b h1:qpsJ9tFppQOwO0rHrggOrSXW5XIx4G3DBEV6jrf9gU0=
github.com/ethereum-optimism/op-geth v0.0.0-20230214215134-401b7fd3309b/go.mod h1:p0Yox74PhYlq1HvijrCBCD9A3cI7rXco7hT6KrQr+rY=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fjl/memsize v0.0.1 h1:+zhkb+dhUgx0/e+M8sF0QqiouvMQUiKR+QYvdxIOKcQ=
github.com/fjl/memsize v0.0.1/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
......
......@@ -2,7 +2,7 @@ module github.com/ethereum-optimism/optimism/indexer
go 1.17
replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468
replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20230214215134-401b7fd3309b
require (
github.com/ethereum-optimism/optimism/op-bindings v0.10.14
......
......@@ -266,8 +266,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z8veEq5ZO3DfIhZ7xgRP9WTc=
github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs=
github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468 h1:7KgjBYDji5AKi42eRYI+n8Gs+ZJVilSASL3WBu82c3M=
github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468/go.mod h1:p0Yox74PhYlq1HvijrCBCD9A3cI7rXco7hT6KrQr+rY=
github.com/ethereum-optimism/op-geth v0.0.0-20230214215134-401b7fd3309b h1:qpsJ9tFppQOwO0rHrggOrSXW5XIx4G3DBEV6jrf9gU0=
github.com/ethereum-optimism/op-geth v0.0.0-20230214215134-401b7fd3309b/go.mod h1:p0Yox74PhYlq1HvijrCBCD9A3cI7rXco7hT6KrQr+rY=
github.com/ethereum-optimism/optimism/op-batcher v0.10.14 h1:4C8hR2ut4kfzY9Lk7IZ8Utyw3P5rgiSabfech57nHP8=
github.com/ethereum-optimism/optimism/op-batcher v0.10.14/go.mod h1:j+uvhHcyqifm+IjpIKMSrdGRVyjjdmtLnYazZGt+ti4=
github.com/ethereum-optimism/optimism/op-bindings v0.10.1/go.mod h1:UeTZlpZyhOL3y9Sogzvbn8Z3q1tDmZEv1VmGxMiZYCg=
......
This diff is collapsed.
......@@ -22,7 +22,10 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup"
)
var ErrInvalidDeployConfig = errors.New("invalid deploy config")
var (
ErrInvalidDeployConfig = errors.New("invalid deploy config")
ErrInvalidImmutablesConfig = errors.New("invalid immutables config")
)
// DeployConfig represents the deployment configuration for Optimism
type DeployConfig struct {
......@@ -344,12 +347,27 @@ func NewDeployConfigWithNetwork(network, path string) (*DeployConfig, error) {
}
// NewL2ImmutableConfig will create an ImmutableConfig given an instance of a
// Hardhat and a DeployConfig.
// DeployConfig and a block.
func NewL2ImmutableConfig(config *DeployConfig, block *types.Block) (immutables.ImmutableConfig, error) {
immutable := make(immutables.ImmutableConfig)
if config.L1StandardBridgeProxy == (common.Address{}) {
return immutable, fmt.Errorf("L1StandardBridgeProxy cannot be address(0): %w", ErrInvalidImmutablesConfig)
}
if config.L1CrossDomainMessengerProxy == (common.Address{}) {
return immutable, fmt.Errorf("L1CrossDomainMessengerProxy cannot be address(0): %w", ErrInvalidImmutablesConfig)
}
if config.L1ERC721BridgeProxy == (common.Address{}) {
return immutable, errors.New("L1ERC721BridgeProxy cannot be address(0)")
return immutable, fmt.Errorf("L1ERC721BridgeProxy cannot be address(0): %w", ErrInvalidImmutablesConfig)
}
if config.SequencerFeeVaultRecipient == (common.Address{}) {
return immutable, fmt.Errorf("SequencerFeeVaultRecipient cannot be address(0): %w", ErrInvalidImmutablesConfig)
}
if config.BaseFeeVaultRecipient == (common.Address{}) {
return immutable, fmt.Errorf("BaseFeeVaultRecipient cannot be address(0): %w", ErrInvalidImmutablesConfig)
}
if config.L1FeeVaultRecipient == (common.Address{}) {
return immutable, fmt.Errorf("L1FeeVaultRecipient cannot be address(0): %w", ErrInvalidImmutablesConfig)
}
immutable["L2StandardBridge"] = immutables.ImmutableValues{
......
......@@ -23,6 +23,8 @@
"l1FeeVaultRecipient": "0x71bE63f3384f5fb98995898A86B02Fb2426c5788",
"sequencerFeeVaultRecipient": "0x71bE63f3384f5fb98995898A86B02Fb2426c5788",
"l1ERC721BridgeProxy": "0xff000000000000000000000000000000000000ff",
"l1StandardBridgeProxy": "0xff000000000000000000000000000000000000fd",
"l1CrossDomainMessengerProxy": "0xff000000000000000000000000000000000000dd",
"deploymentWaitConfirmations": 1,
"fundDevAccounts": true
......
package immutables
import (
"errors"
"fmt"
"math/big"
......@@ -24,6 +25,39 @@ type ImmutableValues map[string]any
// contracts.
type ImmutableConfig map[string]ImmutableValues
// Check does a sanity check that the specific values that
// Optimism uses are set inside of the ImmutableConfig.
func (i ImmutableConfig) Check() error {
if _, ok := i["L2CrossDomainMessenger"]["otherMessenger"]; !ok {
return errors.New("L2CrossDomainMessenger otherMessenger not set")
}
if _, ok := i["L2StandardBridge"]["otherBridge"]; !ok {
return errors.New("L2StandardBridge otherBridge not set")
}
if _, ok := i["L2ERC721Bridge"]["messenger"]; !ok {
return errors.New("L2ERC721Bridge messenger not set")
}
if _, ok := i["L2ERC721Bridge"]["otherBridge"]; !ok {
return errors.New("L2ERC721Bridge otherBridge not set")
}
if _, ok := i["OptimismMintableERC721Factory"]["bridge"]; !ok {
return errors.New("OptimismMintableERC20Factory bridge not set")
}
if _, ok := i["OptimismMintableERC721Factory"]["remoteChainId"]; !ok {
return errors.New("OptimismMintableERC20Factory remoteChainId not set")
}
if _, ok := i["SequencerFeeVault"]["recipient"]; !ok {
return errors.New("SequencerFeeVault recipient not set")
}
if _, ok := i["L1FeeVault"]["recipient"]; !ok {
return errors.New("L1FeeVault recipient not set")
}
if _, ok := i["BaseFeeVault"]["recipient"]; !ok {
return errors.New("BaseFeeVault recipient not set")
}
return nil
}
// DeploymentResults represents the output of deploying each of the
// contracts so that the immutables can be set properly in the bytecode.
type DeploymentResults map[string]hexutil.Bytes
......@@ -31,6 +65,10 @@ type DeploymentResults map[string]hexutil.Bytes
// BuildOptimism will deploy the L2 predeploys so that their immutables are set
// correctly.
func BuildOptimism(immutable ImmutableConfig) (DeploymentResults, error) {
if err := immutable.Check(); err != nil {
return DeploymentResults{}, err
}
deployments := []deployer.Constructor{
{
Name: "GasPriceOracle",
......
......@@ -19,9 +19,11 @@ func TestBuildOptimism(t *testing.T) {
},
"L2ERC721Bridge": {
"otherBridge": common.HexToAddress("0x1234567890123456789012345678901234567890"),
"messenger": common.HexToAddress("0x1234567890123456789012345678901234567890"),
},
"OptimismMintableERC721Factory": {
"remoteChainId": big.NewInt(1),
"bridge": common.HexToAddress("0x1234567890123456789012345678901234567890"),
},
"SequencerFeeVault": {
"recipient": common.HexToAddress("0x1234567890123456789012345678901234567890"),
......
......@@ -102,6 +102,10 @@ func MakeDeployParams(t require.TestingT, tp *TestParams) *DeployParams {
GasPriceOracleScalar: 1000_000,
DeploymentWaitConfirmations: 1,
SequencerFeeVaultRecipient: common.Address{19: 1},
BaseFeeVaultRecipient: common.Address{19: 2},
L1FeeVaultRecipient: common.Address{19: 3},
EIP1559Elasticity: 10,
EIP1559Denominator: 50,
......
......@@ -92,6 +92,10 @@ func DefaultSystemConfig(t *testing.T) SystemConfig {
GasPriceOracleOverhead: 2100,
GasPriceOracleScalar: 1_000_000,
SequencerFeeVaultRecipient: common.Address{19: 1},
BaseFeeVaultRecipient: common.Address{19: 2},
L1FeeVaultRecipient: common.Address{19: 3},
DeploymentWaitConfirmations: 1,
EIP1559Elasticity: 2,
......
......@@ -20,6 +20,7 @@ type BlockInfo interface {
MixDigest() common.Hash
BaseFee() *big.Int
ReceiptHash() common.Hash
GasUsed() uint64
}
func InfoToL1BlockRef(info BlockInfo) L1BlockRef {
......@@ -79,6 +80,10 @@ func (h headerBlockInfo) ReceiptHash() common.Hash {
return h.Header.ReceiptHash
}
func (h headerBlockInfo) GasUsed() uint64 {
return h.Header.GasUsed
}
// HeaderBlockInfo returns h as a BlockInfo implementation.
func HeaderBlockInfo(h *types.Header) BlockInfo {
return headerBlockInfo{h}
......
......@@ -34,7 +34,8 @@ const (
globalValidateThrottle = 512
gossipHeartbeat = 500 * time.Millisecond
// seenMessagesTTL limits the duration that message IDs are remembered for gossip deduplication purposes
seenMessagesTTL = 80 * gossipHeartbeat
// 130 * gossipHeartbeat
seenMessagesTTL = 130 * gossipHeartbeat
DefaultMeshD = 8 // topic stable mesh target count
DefaultMeshDlo = 6 // topic stable mesh low watermark
DefaultMeshDhi = 12 // topic stable mesh high watermark
......@@ -234,7 +235,7 @@ func BuildBlocksValidator(log log.Logger, cfg *rollup.Config, runCfg GossipRunti
// Seen block hashes per block height
// uint64 -> *seenBlocks
blockHeightLRU, err := lru.New(100)
blockHeightLRU, err := lru.New(1000)
if err != nil {
panic(fmt.Errorf("failed to set up block height LRU cache: %w", err))
}
......@@ -328,9 +329,9 @@ func BuildBlocksValidator(log log.Logger, cfg *rollup.Config, runCfg GossipRunti
}
func verifyBlockSignature(log log.Logger, cfg *rollup.Config, runCfg GossipRuntimeConfig, id peer.ID, signatureBytes []byte, payloadBytes []byte) pubsub.ValidationResult {
result := verifyBlockSignatureWithHasher(log, cfg, runCfg, id, signatureBytes, payloadBytes, BlockSigningHash)
result := verifyBlockSignatureWithHasher(nil, cfg, runCfg, id, signatureBytes, payloadBytes, LegacyBlockSigningHash)
if result != pubsub.ValidationAccept {
return verifyBlockSignatureWithHasher(log, cfg, runCfg, id, signatureBytes, payloadBytes, LegacyBlockSigningHash)
return verifyBlockSignatureWithHasher(log, cfg, runCfg, id, signatureBytes, payloadBytes, BlockSigningHash)
}
return result
}
......@@ -338,13 +339,17 @@ func verifyBlockSignature(log log.Logger, cfg *rollup.Config, runCfg GossipRunti
func verifyBlockSignatureWithHasher(log log.Logger, cfg *rollup.Config, runCfg GossipRuntimeConfig, id peer.ID, signatureBytes []byte, payloadBytes []byte, hasher func(cfg *rollup.Config, payloadBytes []byte) (common.Hash, error)) pubsub.ValidationResult {
signingHash, err := hasher(cfg, payloadBytes)
if err != nil {
if log != nil {
log.Warn("failed to compute block signing hash", "err", err, "peer", id)
}
return pubsub.ValidationReject
}
pub, err := crypto.SigToPub(signingHash[:], signatureBytes)
if err != nil {
if log != nil {
log.Warn("invalid block signature", "err", err, "peer", id)
}
return pubsub.ValidationReject
}
addr := crypto.PubkeyToAddress(*pub)
......@@ -355,10 +360,14 @@ func verifyBlockSignatureWithHasher(log log.Logger, cfg *rollup.Config, runCfg G
// This means we may drop old payloads upon key rotation,
// but this can be recovered from like any other missed unsafe payload.
if expected := runCfg.P2PSequencerAddress(); expected == (common.Address{}) {
if log != nil {
log.Warn("no configured p2p sequencer address, ignoring gossiped block", "peer", id, "addr", addr)
}
return pubsub.ValidationIgnore
} else if addr != expected {
if log != nil {
log.Warn("unexpected block author", "err", err, "peer", id, "addr", addr, "expected", expected)
}
return pubsub.ValidationReject
}
return pubsub.ValidationAccept
......
......@@ -12,6 +12,27 @@ import (
"github.com/ethereum-optimism/optimism/op-node/eth"
)
var (
ErrBlockTimeZero = errors.New("block time cannot be 0")
ErrMissingChannelTimeout = errors.New("channel timeout must be set, this should cover at least a L1 block time")
ErrInvalidSeqWindowSize = errors.New("sequencing window size must at least be 2")
ErrMissingGenesisL1Hash = errors.New("genesis L1 hash cannot be empty")
ErrMissingGenesisL2Hash = errors.New("genesis L2 hash cannot be empty")
ErrGenesisHashesSame = errors.New("achievement get! rollup inception: L1 and L2 genesis cannot be the same")
ErrMissingGenesisL2Time = errors.New("missing L2 genesis time")
ErrMissingBatcherAddr = errors.New("missing genesis system config batcher address")
ErrMissingOverhead = errors.New("missing genesis system config overhead")
ErrMissingScalar = errors.New("missing genesis system config scalar")
ErrMissingGasLimit = errors.New("missing genesis system config gas limit")
ErrMissingBatchInboxAddress = errors.New("missing batch inbox address")
ErrMissingDepositContractAddress = errors.New("missing deposit contract address")
ErrMissingL1ChainID = errors.New("L1 chain ID must not be nil")
ErrMissingL2ChainID = errors.New("L2 chain ID must not be nil")
ErrChainIDsSame = errors.New("L1 and L2 chain IDs must be different")
ErrL1ChainIDNotPositive = errors.New("L1 chain ID must be non-zero and positive")
ErrL2ChainIDNotPositive = errors.New("L2 chain ID must be non-zero and positive")
)
type Genesis struct {
// The L1 block that the rollup starts *after* (no derived transactions)
L1 eth.BlockID `json:"l1"`
......@@ -147,52 +168,58 @@ func (cfg *Config) CheckL2GenesisBlockHash(ctx context.Context, client L2Client)
// Check verifies that the given configuration makes sense
func (cfg *Config) Check() error {
if cfg.BlockTime == 0 {
return fmt.Errorf("block time cannot be 0, got %d", cfg.BlockTime)
return ErrBlockTimeZero
}
if cfg.ChannelTimeout == 0 {
return fmt.Errorf("channel timeout must be set, this should cover at least a L1 block time")
return ErrMissingChannelTimeout
}
if cfg.SeqWindowSize < 2 {
return fmt.Errorf("sequencing window size must at least be 2, got %d", cfg.SeqWindowSize)
return ErrInvalidSeqWindowSize
}
if cfg.Genesis.L1.Hash == (common.Hash{}) {
return errors.New("genesis l1 hash cannot be empty")
return ErrMissingGenesisL1Hash
}
if cfg.Genesis.L2.Hash == (common.Hash{}) {
return errors.New("genesis l2 hash cannot be empty")
return ErrMissingGenesisL2Hash
}
if cfg.Genesis.L2.Hash == cfg.Genesis.L1.Hash {
return errors.New("achievement get! rollup inception: L1 and L2 genesis cannot be the same")
return ErrGenesisHashesSame
}
if cfg.Genesis.L2Time == 0 {
return errors.New("missing L2 genesis time")
return ErrMissingGenesisL2Time
}
if cfg.Genesis.SystemConfig.BatcherAddr == (common.Address{}) {
return errors.New("missing genesis system config batcher address")
return ErrMissingBatcherAddr
}
if cfg.Genesis.SystemConfig.Overhead == (eth.Bytes32{}) {
return errors.New("missing genesis system config overhead")
return ErrMissingOverhead
}
if cfg.Genesis.SystemConfig.Scalar == (eth.Bytes32{}) {
return errors.New("missing genesis system config scalar")
return ErrMissingScalar
}
if cfg.Genesis.SystemConfig.GasLimit == 0 {
return errors.New("missing genesis system config gas limit")
return ErrMissingGasLimit
}
if cfg.BatchInboxAddress == (common.Address{}) {
return errors.New("missing batch inbox address")
return ErrMissingBatchInboxAddress
}
if cfg.DepositContractAddress == (common.Address{}) {
return errors.New("missing deposit contract address")
return ErrMissingDepositContractAddress
}
if cfg.L1ChainID == nil {
return errors.New("l1 chain ID must not be nil")
return ErrMissingL1ChainID
}
if cfg.L2ChainID == nil {
return errors.New("l2 chain ID must not be nil")
return ErrMissingL2ChainID
}
if cfg.L1ChainID.Cmp(cfg.L2ChainID) == 0 {
return errors.New("l1 and l2 chain IDs must be different")
return ErrChainIDsSame
}
if cfg.L1ChainID.Sign() < 1 {
return ErrL1ChainIDNotPositive
}
if cfg.L2ChainID.Sign() < 1 {
return ErrL2ChainIDNotPositive
}
return nil
}
......
......@@ -212,3 +212,125 @@ func TestCheckL2BlockRefByNumber(t *testing.T) {
err = config.CheckL2GenesisBlockHash(context.TODO(), &mockClient)
assert.Error(t, err)
}
func TestConfig_Check(t *testing.T) {
tests := []struct {
name string
modifier func(cfg *Config)
expectedErr error
}{
{
name: "BlockTimeZero",
modifier: func(cfg *Config) { cfg.BlockTime = 0 },
expectedErr: ErrBlockTimeZero,
},
{
name: "ChannelTimeoutZero",
modifier: func(cfg *Config) { cfg.ChannelTimeout = 0 },
expectedErr: ErrMissingChannelTimeout,
},
{
name: "SeqWindowSizeZero",
modifier: func(cfg *Config) { cfg.SeqWindowSize = 0 },
expectedErr: ErrInvalidSeqWindowSize,
},
{
name: "SeqWindowSizeOne",
modifier: func(cfg *Config) { cfg.SeqWindowSize = 1 },
expectedErr: ErrInvalidSeqWindowSize,
},
{
name: "NoL1Genesis",
modifier: func(cfg *Config) { cfg.Genesis.L1.Hash = common.Hash{} },
expectedErr: ErrMissingGenesisL1Hash,
},
{
name: "NoL2Genesis",
modifier: func(cfg *Config) { cfg.Genesis.L2.Hash = common.Hash{} },
expectedErr: ErrMissingGenesisL2Hash,
},
{
name: "GenesisHashesEqual",
modifier: func(cfg *Config) { cfg.Genesis.L2.Hash = cfg.Genesis.L1.Hash },
expectedErr: ErrGenesisHashesSame,
},
{
name: "GenesisL2TimeZero",
modifier: func(cfg *Config) { cfg.Genesis.L2Time = 0 },
expectedErr: ErrMissingGenesisL2Time,
},
{
name: "NoBatcherAddr",
modifier: func(cfg *Config) { cfg.Genesis.SystemConfig.BatcherAddr = common.Address{} },
expectedErr: ErrMissingBatcherAddr,
},
{
name: "NoOverhead",
modifier: func(cfg *Config) { cfg.Genesis.SystemConfig.Overhead = eth.Bytes32{} },
expectedErr: ErrMissingOverhead,
},
{
name: "NoScalar",
modifier: func(cfg *Config) { cfg.Genesis.SystemConfig.Scalar = eth.Bytes32{} },
expectedErr: ErrMissingScalar,
},
{
name: "NoGasLimit",
modifier: func(cfg *Config) { cfg.Genesis.SystemConfig.GasLimit = 0 },
expectedErr: ErrMissingGasLimit,
},
{
name: "NoBatchInboxAddress",
modifier: func(cfg *Config) { cfg.BatchInboxAddress = common.Address{} },
expectedErr: ErrMissingBatchInboxAddress,
},
{
name: "NoDepositContractAddress",
modifier: func(cfg *Config) { cfg.DepositContractAddress = common.Address{} },
expectedErr: ErrMissingDepositContractAddress,
},
{
name: "NoL1ChainId",
modifier: func(cfg *Config) { cfg.L1ChainID = nil },
expectedErr: ErrMissingL1ChainID,
},
{
name: "NoL2ChainId",
modifier: func(cfg *Config) { cfg.L2ChainID = nil },
expectedErr: ErrMissingL2ChainID,
},
{
name: "ChainIDsEqual",
modifier: func(cfg *Config) { cfg.L2ChainID = cfg.L1ChainID },
expectedErr: ErrChainIDsSame,
},
{
name: "L1ChainIdNegative",
modifier: func(cfg *Config) { cfg.L1ChainID = big.NewInt(-1) },
expectedErr: ErrL1ChainIDNotPositive,
},
{
name: "L1ChainIdZero",
modifier: func(cfg *Config) { cfg.L1ChainID = big.NewInt(0) },
expectedErr: ErrL1ChainIDNotPositive,
},
{
name: "L2ChainIdNegative",
modifier: func(cfg *Config) { cfg.L2ChainID = big.NewInt(-1) },
expectedErr: ErrL2ChainIDNotPositive,
},
{
name: "L2ChainIdZero",
modifier: func(cfg *Config) { cfg.L2ChainID = big.NewInt(0) },
expectedErr: ErrL2ChainIDNotPositive,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
cfg := randConfig()
test.modifier(cfg)
err := cfg.Check()
assert.Same(t, err, test.expectedErr)
})
}
}
......@@ -46,6 +46,7 @@ type HeaderInfo struct {
baseFee *big.Int
txHash common.Hash
receiptHash common.Hash
gasUsed uint64
}
var _ eth.BlockInfo = (*HeaderInfo)(nil)
......@@ -90,6 +91,10 @@ func (info *HeaderInfo) ReceiptHash() common.Hash {
return info.receiptHash
}
func (info *HeaderInfo) GasUsed() uint64 {
return info.gasUsed
}
type rpcHeader struct {
ParentHash common.Hash `json:"parentHash"`
UncleHash common.Hash `json:"sha3Uncles"`
......@@ -182,6 +187,7 @@ func (hdr *rpcHeader) Info(trustCache bool, mustBePostMerge bool) (*HeaderInfo,
baseFee: (*big.Int)(hdr.BaseFee),
txHash: hdr.TxHash,
receiptHash: hdr.ReceiptHash,
gasUsed: uint64(hdr.GasUsed),
}
return &info, nil
}
......
......@@ -21,6 +21,7 @@ type MockBlockInfo struct {
InfoMixDigest [32]byte
InfoBaseFee *big.Int
InfoReceiptRoot common.Hash
InfoGasUsed uint64
}
func (l *MockBlockInfo) Hash() common.Hash {
......@@ -59,6 +60,10 @@ func (l *MockBlockInfo) ReceiptHash() common.Hash {
return l.InfoReceiptRoot
}
func (l *MockBlockInfo) GasUsed() uint64 {
return l.InfoGasUsed
}
func (l *MockBlockInfo) ID() eth.BlockID {
return eth.BlockID{Hash: l.InfoHash, Number: l.InfoNum}
}
......@@ -81,6 +86,7 @@ func RandomBlockInfo(rng *rand.Rand) *MockBlockInfo {
InfoBaseFee: big.NewInt(rng.Int63n(1000_000 * 1e9)), // a million GWEI
InfoReceiptRoot: types.EmptyRootHash,
InfoRoot: RandomHash(rng),
InfoGasUsed: rng.Uint64(),
}
}
......
......@@ -361,6 +361,7 @@ func (l *L2OutputSubmitter) SendTransaction(ctx context.Context, tx *types.Trans
// receipt is received it's likely our gas price was too low.
cCtx, cancel := context.WithTimeout(ctx, 100*time.Second)
defer cancel()
l.log.Info("Sending transaction", "tx_hash", tx.Hash())
receipt, err := l.txMgr.Send(cCtx, tx)
if err != nil {
l.log.Error("proposer unable to publish tx", "err", err)
......
......@@ -15,6 +15,14 @@ import (
opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto"
)
// Geth defaults the priceBump to 10
// Set it to 15% to be more aggressive about including transactions
const priceBump int64 = 15
// new = old * (100 + priceBump) / 100
var priceBumpPercent = big.NewInt(100 + priceBump)
var oneHundred = big.NewInt(100)
// UpdateGasPriceSendTxFunc defines a function signature for publishing a
// desired tx with a specific gas price. Implementations of this signature
// should also return promptly when the context is canceled.
......@@ -94,8 +102,10 @@ type SimpleTxManager struct {
}
// IncreaseGasPrice takes the previous transaction & potentially clones then signs it with a higher tip.
// If the basefee + priority fee did not increase by a minimum percent (geth's replacement percent) an
// error will be returned.
// If the tip + basefee suggested by the network are not greater than the previous values, the same transaction
// will be returned. If they are greater, this function will ensure that they are at least greater by 15% than
// the previous transaction's value to ensure that the price bump is large enough.
//
// We do not re-estimate the amount of gas used because for some stateful transactions (like output proposals) the
// act of including the transaction renders the repeat of the transaction invalid.
func (m *SimpleTxManager) IncreaseGasPrice(ctx context.Context, tx *types.Transaction) (*types.Transaction, error) {
......@@ -112,15 +122,38 @@ func (m *SimpleTxManager) IncreaseGasPrice(ctx context.Context, tx *types.Transa
gasTipCap = tip
}
// new = old * (100 + priceBump) / 100
// Enforce a min priceBump on the tip. Do this before the feeCap is calculated
thresholdTip := new(big.Int).Mul(priceBumpPercent, tx.GasTipCap())
thresholdTip = thresholdTip.Div(thresholdTip, oneHundred)
if tx.GasTipCapIntCmp(gasTipCap) >= 0 {
m.l.Debug("Reusing the previous tip", "previous", tx.GasTipCap(), "suggested", gasTipCap)
gasTipCap = tx.GasTipCap()
} else if thresholdTip.Cmp(gasTipCap) > 0 {
m.l.Debug("Overriding the tip to enforce a price bump", "previous", tx.GasTipCap(), "suggested", gasTipCap, "new", thresholdTip)
gasTipCap = thresholdTip
}
if head, err := m.backend.HeaderByNumber(ctx, nil); err != nil {
return nil, err
} else if head.BaseFee == nil {
return nil, errors.New("txmgr does not support pre-london blocks that do not have a basefee")
} else {
// CalcGasFeeCap ensure that the fee cap is large enough for the tip.
gasFeeCap = CalcGasFeeCap(head.BaseFee, gasTipCap)
}
// TODO (CLI-2630): Check for a large enough price bump
// new = old * (100 + priceBump) / 100
// Enforce a min priceBump on the feeCap
thresholdFeeCap := new(big.Int).Mul(priceBumpPercent, tx.GasFeeCap())
thresholdFeeCap = thresholdFeeCap.Div(thresholdFeeCap, oneHundred)
if tx.GasFeeCapIntCmp(gasFeeCap) >= 0 {
m.l.Debug("Reusing the previous fee cap", "previous", tx.GasFeeCap(), "suggested", gasFeeCap)
gasFeeCap = tx.GasFeeCap()
} else if thresholdFeeCap.Cmp(gasFeeCap) > 0 {
m.l.Debug("Overriding the fee cap to enforce a price bump", "previous", tx.GasFeeCap(), "suggested", gasFeeCap, "new", thresholdFeeCap)
gasFeeCap = thresholdFeeCap
}
rawTx := &types.DynamicFeeTx{
ChainID: tx.ChainId(),
......
......@@ -11,7 +11,6 @@ import (
"github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
......@@ -290,6 +289,7 @@ func TestTxMgrConfirmsAtHigherGasPrice(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
receipt, err := h.mgr.Send(ctx, tx)
require.Nil(t, err)
require.NotNil(t, receipt)
......@@ -529,6 +529,7 @@ func TestManagerPanicOnZeroConfs(t *testing.T) {
type failingBackend struct {
returnSuccessBlockNumber bool
returnSuccessReceipt bool
baseFee, gasTip *big.Int
}
// BlockNumber for the failingBackend returns errRpcFailure on the first
......@@ -559,7 +560,9 @@ func (b *failingBackend) TransactionReceipt(
}
func (b *failingBackend) HeaderByNumber(_ context.Context, _ *big.Int) (*types.Header, error) {
return nil, ethereum.NotFound
return &types.Header{
BaseFee: b.baseFee,
}, nil
}
func (b *failingBackend) SendTransaction(_ context.Context, _ *types.Transaction) error {
......@@ -567,7 +570,7 @@ func (b *failingBackend) SendTransaction(_ context.Context, _ *types.Transaction
}
func (b *failingBackend) SuggestGasTipCap(_ context.Context) (*big.Int, error) {
return nil, errors.New("unimplemented")
return b.gasTip, nil
}
// TestWaitMinedReturnsReceiptAfterFailure asserts that WaitMined is able to
......@@ -602,3 +605,125 @@ func TestWaitMinedReturnsReceiptAfterFailure(t *testing.T) {
require.NotNil(t, receipt)
require.Equal(t, receipt.TxHash, txHash)
}
// TestIncreaseGasPriceEnforcesMinBump asserts that if the suggest gas tip
// returned from L1 is less than the required price bump the price bump is
// used instead.
func TestIncreaseGasPriceEnforcesMinBump(t *testing.T) {
t.Parallel()
borkedBackend := failingBackend{
gasTip: big.NewInt(101),
baseFee: big.NewInt(460),
}
mgr := &SimpleTxManager{
Config: Config{
ResubmissionTimeout: time.Second,
ReceiptQueryInterval: 50 * time.Millisecond,
NumConfirmations: 1,
SafeAbortNonceTooLowCount: 3,
Signer: func(ctx context.Context, from common.Address, tx *types.Transaction) (*types.Transaction, error) {
return tx, nil
},
From: common.Address{},
},
name: "TEST",
backend: &borkedBackend,
l: testlog.Logger(t, log.LvlCrit),
}
tx := types.NewTx(&types.DynamicFeeTx{
GasTipCap: big.NewInt(100),
GasFeeCap: big.NewInt(1000),
})
ctx := context.Background()
newTx, err := mgr.IncreaseGasPrice(ctx, tx)
require.NoError(t, err)
require.True(t, newTx.GasFeeCap().Cmp(tx.GasFeeCap()) > 0, "new tx fee cap must be larger")
require.True(t, newTx.GasTipCap().Cmp(tx.GasTipCap()) > 0, "new tx tip must be larger")
}
// TestIncreaseGasPriceNotExponential asserts that if the L1 basefee & tip remain the
// same, repeated calls to IncreaseGasPrice do not continually increase the gas price.
func TestIncreaseGasPriceNotExponential(t *testing.T) {
t.Parallel()
borkedBackend := failingBackend{
gasTip: big.NewInt(10),
baseFee: big.NewInt(45),
}
feeCap := CalcGasFeeCap(borkedBackend.baseFee, borkedBackend.gasTip)
mgr := &SimpleTxManager{
Config: Config{
ResubmissionTimeout: time.Second,
ReceiptQueryInterval: 50 * time.Millisecond,
NumConfirmations: 1,
SafeAbortNonceTooLowCount: 3,
Signer: func(ctx context.Context, from common.Address, tx *types.Transaction) (*types.Transaction, error) {
return tx, nil
},
From: common.Address{},
},
name: "TEST",
backend: &borkedBackend,
l: testlog.Logger(t, log.LvlCrit),
}
tx := types.NewTx(&types.DynamicFeeTx{
GasTipCap: big.NewInt(10),
GasFeeCap: big.NewInt(100),
})
// Run IncreaseGasPrice a bunch of times in a row to simulate a very fast resubmit loop.
for i := 0; i < 20; i++ {
ctx := context.Background()
newTx, err := mgr.IncreaseGasPrice(ctx, tx)
require.NoError(t, err)
require.True(t, newTx.GasFeeCap().Cmp(feeCap) == 0, "new tx fee cap must be equal L1")
require.True(t, newTx.GasTipCap().Cmp(borkedBackend.gasTip) == 0, "new tx tip must be equal L1")
tx = newTx
}
}
// TestIncreaseGasPriceUseLargeIncrease asserts that if the suggest gas tip
// returned from L1 is much larger than the required price bump the L1 value
// is used instead of the price bump
func TestIncreaseGasPriceUseLargeIncrease(t *testing.T) {
t.Parallel()
borkedBackend := failingBackend{
gasTip: big.NewInt(50),
baseFee: big.NewInt(200),
}
feeCap := CalcGasFeeCap(borkedBackend.baseFee, borkedBackend.gasTip)
mgr := &SimpleTxManager{
Config: Config{
ResubmissionTimeout: time.Second,
ReceiptQueryInterval: 50 * time.Millisecond,
NumConfirmations: 1,
SafeAbortNonceTooLowCount: 3,
Signer: func(ctx context.Context, from common.Address, tx *types.Transaction) (*types.Transaction, error) {
return tx, nil
},
From: common.Address{},
},
name: "TEST",
backend: &borkedBackend,
l: testlog.Logger(t, log.LvlCrit),
}
tx := types.NewTx(&types.DynamicFeeTx{
GasTipCap: big.NewInt(10),
GasFeeCap: big.NewInt(100),
})
ctx := context.Background()
newTx, err := mgr.IncreaseGasPrice(ctx, tx)
require.NoError(t, err)
require.True(t, newTx.GasFeeCap().Cmp(feeCap) == 0, "new tx fee cap must be equal L1")
require.True(t, newTx.GasTipCap().Cmp(borkedBackend.gasTip) == 0, "new tx tip must be equal L1")
}
......@@ -85,4 +85,4 @@ require (
lukechampine.com/blake3 v1.1.7 // indirect
)
replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468
replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20230214215134-401b7fd3309b
......@@ -81,8 +81,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468 h1:7KgjBYDji5AKi42eRYI+n8Gs+ZJVilSASL3WBu82c3M=
github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468/go.mod h1:p0Yox74PhYlq1HvijrCBCD9A3cI7rXco7hT6KrQr+rY=
github.com/ethereum-optimism/op-geth v0.0.0-20230214215134-401b7fd3309b h1:qpsJ9tFppQOwO0rHrggOrSXW5XIx4G3DBEV6jrf9gU0=
github.com/ethereum-optimism/op-geth v0.0.0-20230214215134-401b7fd3309b/go.mod h1:p0Yox74PhYlq1HvijrCBCD9A3cI7rXco7hT6KrQr+rY=
github.com/ethereum-optimism/optimism/op-node v0.10.3 h1:96KbEtbfJTg5GXtNqLnrDPnXMbeynIy1G8iSc47whrA=
github.com/ethereum-optimism/optimism/op-node v0.10.3/go.mod h1:fsRLXH68xaLhjfr67MPEtjCocCzSXGhZIre536QccIw=
github.com/ethereum-optimism/optimism/op-service v0.10.3 h1:gr+eVq6CzxMFqo0/9n6EoUkpumtYZEzO84gti6ekj/s=
......
#!/usr/bin/env python3
import urllib3
import json
import subprocess
import os
......@@ -11,34 +10,10 @@ GETH_VERSION='v1.10.26'
def main():
for project in ('op-service', 'op-node', 'op-proposer', 'op-batcher', 'op-bindings', 'op-chain-ops', 'op-e2e', 'op-wheel'):
for project in ('.', 'op-wheel', 'indexer'):
print(f'Updating {project}...')
update_mod(project)
# pull the replacer from one of the modules above, since
# go work edit -replace does not resolve the branch name
# into a pseudo-version.
pv = None
with open('./op-service/go.mod') as f:
for line in f:
if line.startswith(f'replace github.com/ethereum/go-ethereum {GETH_VERSION}'):
splits = line.split(' => ')
pv = splits[1].strip()
pv = pv.split(' ')[1]
break
if pv is None:
raise Exception('Pseudo version not found.')
print('Updating go.work...')
subprocess.run([
'go',
'work',
'edit',
'-replace',
f'github.com/ethereum/go-ethereum@{GETH_VERSION}=github.com/ethereum-optimism/op-geth@{pv}'
], cwd=os.path.join(project), check=True)
def update_mod(project):
print('Replacing...')
......
......@@ -139,29 +139,30 @@ L2ERC721Bridge_Test:test_finalizeBridgeERC721_notFromRemoteMessenger_reverts() (
L2ERC721Bridge_Test:test_finalizeBridgeERC721_notViaLocalMessenger_reverts() (gas: 16148)
L2ERC721Bridge_Test:test_finalizeBridgeERC721_selfToken_reverts() (gas: 17637)
L2ERC721Bridge_Test:test_finalizeBridgeERC721_succeeds() (gas: 168905)
L2OutputOracleTest:test_computeL2Timestamp_succeeds() (gas: 37184)
L2OutputOracleTest:test_constructor_badTimestamp_reverts() (gas: 70717)
L2OutputOracleTest:test_constructor_succeeds() (gas: 33760)
L2OutputOracleTest:test_deleteL2Outputs_afterLatest_reverts() (gas: 211900)
L2OutputOracleTest:test_computeL2Timestamp_succeeds() (gas: 37206)
L2OutputOracleTest:test_constructor_badTimestamp_reverts() (gas: 70767)
L2OutputOracleTest:test_constructor_l2BlockTimeZero_reverts() (gas: 45786)
L2OutputOracleTest:test_constructor_succeeds() (gas: 33695)
L2OutputOracleTest:test_deleteL2Outputs_afterLatest_reverts() (gas: 211855)
L2OutputOracleTest:test_deleteL2Outputs_ifNotChallenger_reverts() (gas: 18883)
L2OutputOracleTest:test_deleteL2Outputs_nonExistent_reverts() (gas: 107292)
L2OutputOracleTest:test_deleteOutputs_multipleOutputs_succeeds() (gas: 302121)
L2OutputOracleTest:test_deleteOutputs_singleOutput_succeeds() (gas: 180656)
L2OutputOracleTest:test_getL2OutputIndexAfter_multipleOutputsExist_succeeds() (gas: 267182)
L2OutputOracleTest:test_getL2OutputIndexAfter_noOutputsExis_reverts() (gas: 17914)
L2OutputOracleTest:test_getL2OutputIndexAfter_previousBlock_succeeds() (gas: 96086)
L2OutputOracleTest:test_getL2OutputIndexAfter_sameBlock_succeeds() (gas: 95994)
L2OutputOracleTest:test_getL2Output_succeeds() (gas: 101699)
L2OutputOracleTest:test_latestBlockNumber_succeeds() (gas: 96983)
L2OutputOracleTest:test_nextBlockNumber_succeeds() (gas: 17468)
L2OutputOracleTest:test_proposeL2Output_emptyOutput_reverts() (gas: 26688)
L2OutputOracleTest:test_proposeL2Output_futureTimetamp_reverts() (gas: 28646)
L2OutputOracleTest:test_proposeL2Output_notProposer_reverts() (gas: 25782)
L2OutputOracleTest:test_proposeL2Output_proposeAnotherOutput_succeeds() (gas: 101027)
L2OutputOracleTest:test_deleteOutputs_multipleOutputs_succeeds() (gas: 302143)
L2OutputOracleTest:test_deleteOutputs_singleOutput_succeeds() (gas: 180700)
L2OutputOracleTest:test_getL2OutputIndexAfter_multipleOutputsExist_succeeds() (gas: 267226)
L2OutputOracleTest:test_getL2OutputIndexAfter_noOutputsExis_reverts() (gas: 17936)
L2OutputOracleTest:test_getL2OutputIndexAfter_previousBlock_succeeds() (gas: 96042)
L2OutputOracleTest:test_getL2OutputIndexAfter_sameBlock_succeeds() (gas: 96016)
L2OutputOracleTest:test_getL2Output_succeeds() (gas: 101721)
L2OutputOracleTest:test_latestBlockNumber_succeeds() (gas: 96960)
L2OutputOracleTest:test_nextBlockNumber_succeeds() (gas: 17490)
L2OutputOracleTest:test_proposeL2Output_emptyOutput_reverts() (gas: 26710)
L2OutputOracleTest:test_proposeL2Output_futureTimetamp_reverts() (gas: 28690)
L2OutputOracleTest:test_proposeL2Output_notProposer_reverts() (gas: 25826)
L2OutputOracleTest:test_proposeL2Output_proposeAnotherOutput_succeeds() (gas: 101049)
L2OutputOracleTest:test_proposeL2Output_unexpectedBlockNumber_reverts() (gas: 28402)
L2OutputOracleTest:test_proposeL2Output_unmatchedBlockhash_reverts() (gas: 29446)
L2OutputOracleTest:test_proposeL2Output_unmatchedBlockhash_reverts() (gas: 29402)
L2OutputOracleTest:test_proposeL2Output_wrongFork_reverts() (gas: 29005)
L2OutputOracleTest:test_proposeWithBlockhashAndHeight_succeeds() (gas: 95296)
L2OutputOracleTest:test_proposeWithBlockhashAndHeight_succeeds() (gas: 95318)
L2OutputOracleUpgradeable_Test:test_initValuesOnProxy_succeeds() (gas: 26093)
L2OutputOracleUpgradeable_Test:test_initializeImpl_alreadyInitialized_reverts() (gas: 15149)
L2OutputOracleUpgradeable_Test:test_initializeProxy_alreadyInitialized_reverts() (gas: 20131)
......
......@@ -73,7 +73,7 @@ contract L2OutputOracle is Initializable, Semver {
event OutputsDeleted(uint256 indexed prevNextOutputIndex, uint256 indexed newNextOutputIndex);
/**
* @custom:semver 1.0.0
* @custom:semver 1.1.0
*
* @param _submissionInterval Interval in blocks at which checkpoints must be submitted.
* @param _l2BlockTime The time per L2 block, in seconds.
......@@ -89,7 +89,13 @@ contract L2OutputOracle is Initializable, Semver {
uint256 _startingTimestamp,
address _proposer,
address _challenger
) Semver(1, 0, 0) {
) Semver(1, 1, 0) {
require(_l2BlockTime > 0, "L2OutputOracle: L2 block time must be greater than 0");
require(
_submissionInterval > _l2BlockTime,
"L2OutputOracle: submission interval must be greater than L2 block time"
);
SUBMISSION_INTERVAL = _submissionInterval;
L2_BLOCK_TIME = _l2BlockTime;
PROPOSER = _proposer;
......
......@@ -104,4 +104,9 @@ library Predeploys {
* @notice Address of the L1FeeVault predeploy.
*/
address internal constant L1_FEE_VAULT = 0x420000000000000000000000000000000000001A;
/**
* @notice Address of the GovernanceToken predeploy.
*/
address internal constant GOVERNANCE_TOKEN = 0x4200000000000000000000000000000000000042;
}
......@@ -44,6 +44,40 @@ contract L2OutputOracleTest is L2OutputOracle_Initializer {
);
}
function test_constructor_l2BlockTimeZero_reverts() external {
vm.expectRevert("L2OutputOracle: L2 block time must be greater than 0");
new L2OutputOracle(
submissionInterval,
0,
startingBlockNumber,
block.timestamp,
proposer,
owner
);
}
function testFuzz_constructor_submissionIntervalLteL2BlockTime_reverts(
uint256 _submissionInterval,
uint256 _l2BlockTime
) external {
// Bound the _l2blockTime to be in the range of [1, type(uint256).max]
_l2BlockTime = bound(_l2BlockTime, 1, type(uint256).max);
// Roll the block number to _l2blockTime (the starting L2 timestamp must be less than or equal to the current time)
vm.roll(_l2BlockTime);
// Bound _submissionInterval to be less than or equal to _l2BlockTime
_submissionInterval = bound(_submissionInterval, 0, _l2BlockTime);
vm.expectRevert("L2OutputOracle: submission interval must be greater than L2 block time");
new L2OutputOracle(
_submissionInterval,
_l2BlockTime,
startingBlockNumber,
block.timestamp,
proposer,
owner
);
}
/****************
* Getter Tests *
****************/
......
{
"numDeployConfirmations": 1,
"finalSystemOwner": "ADMIN",
"controller": "ADMIN",
"l1StartingBlockTag": "BLOCKHASH",
"l1ChainID": 5,
"l2ChainID": 42069,
"l2BlockTime": 2,
"maxSequencerDrift": 600,
"sequencerWindowSize": 3600,
"channelTimeout": 300,
"p2pSequencerAddress": "SEQUENCER",
"batchInboxAddress": "0xff00000000000000000000000000000000042069",
"batchSenderAddress": "BATCHER",
"l2OutputOracleSubmissionInterval": 120,
"l2OutputOracleStartingBlockNumber": 0,
"l2OutputOracleStartingTimestamp": "TIMESTAMP",
"l2OutputOracleProposer": "PROPOSER",
"l2OutputOracleChallenger": "ADMIN",
"finalizationPeriodSeconds": 12,
"proxyAdminOwner": "ADMIN",
"baseFeeVaultRecipient": "ADMIN",
"l1FeeVaultRecipient": "ADMIN",
"sequencerFeeVaultRecipient": "ADMIN",
"gasPriceOracleOverhead": 2100,
"gasPriceOracleScalar": 1000000,
"governanceTokenSymbol": "OP",
"governanceTokenName": "Optimism",
"governanceTokenOwner": "ADMIN",
"l2GenesisBlockGasLimit": "0x17D7840",
"l2GenesisBlockBaseFeePerGas": "0x3b9aca00",
"eip1559Denominator": 50,
"eip1559Elasticity": 10
}
import { DeployConfig } from '../src/deploy-config'
import config from './getting-started.json'
export default config satisfies DeployConfig
......@@ -5,6 +5,19 @@ import '@nomiclabs/hardhat-ethers'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
if (hre.deployConfig.l2BlockTime === 0) {
throw new Error(
'L2OutputOracle deployment: l2BlockTime must be greater than 0'
)
} else if (
hre.deployConfig.l2OutputOracleSubmissionInterval <=
hre.deployConfig.l2BlockTime
) {
throw new Error(
'L2OutputOracle deployment: submissionInterval must be greater than the l2BlockTime'
)
}
await deploy({
hre,
name: 'L2OutputOracle',
......
......@@ -84,6 +84,12 @@ const config: HardhatUserConfig = {
accounts: [process.env.PRIVATE_KEY_DEPLOYER || ethers.constants.HashZero],
live: true,
},
'getting-started': {
chainId: 5,
url: process.env.L1_RPC || '',
accounts: [process.env.PRIVATE_KEY_DEPLOYER || ethers.constants.HashZero],
live: true,
},
},
foundry: {
buildInfo: true,
......
......@@ -18,7 +18,7 @@
"build:forge": "forge build",
"build:with-metadata": "FOUNDRY_PROFILE=echidna yarn build:forge",
"build:differential": "tsc scripts/differential-testing.ts --outDir dist --moduleResolution node --esModuleInterop",
"build:fuzz": "go build -o test-case-generator test-case-generator/cmd/fuzz.go",
"build:fuzz": "(cd test-case-generator && go build ./cmd/fuzz.go)",
"prebuild": "yarn ts-node scripts/verify-foundry-install.ts",
"build": "hardhat compile && yarn autogen:artifacts && yarn build:ts && yarn typechain",
"build:ts": "tsc -p tsconfig.build.json",
......
......@@ -55,6 +55,7 @@ type RateLimitConfig struct {
type RateLimitMethodOverride struct {
Limit int `toml:"limit"`
Interval TOMLDuration `toml:"interval"`
Global bool `toml:"global"`
}
type TOMLDuration time.Duration
......
......@@ -13,7 +13,6 @@ require (
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d
github.com/prometheus/client_golang v1.11.0
github.com/rs/cors v1.8.2
github.com/sethvargo/go-limiter v0.7.2
github.com/stretchr/testify v1.7.0
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
)
......
......@@ -451,8 +451,6 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sethvargo/go-limiter v0.7.2 h1:FgC4N7RMpV5gMrUdda15FaFTkQ/L4fEqM7seXMs4oO8=
github.com/sethvargo/go-limiter v0.7.2/go.mod h1:C0kbSFbiriE5k2FFOe18M1YZbAR2Fiwf72uGu0CXCcU=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
......
......@@ -139,6 +139,19 @@ func TestFrontendMaxRPSLimit(t *testing.T) {
require.Nil(t, res[1].Error)
require.Nil(t, res[2].Error)
})
time.Sleep(time.Second)
t.Run("global RPC override", func(t *testing.T) {
h := make(http.Header)
h.Set("User-Agent", "exempt_agent")
client := NewProxydClientWithHeaders("http://127.0.0.1:8545", h)
limitedRes, codes := spamReqs(t, client, "eth_baz", 429, 2)
// use 1 and 1 here since the limit for eth_baz is 1
require.Equal(t, 1, codes[429])
require.Equal(t, 1, codes[200])
RequireEqualJSON(t, []byte(frontendOverLimitResponseWithID), limitedRes)
})
}
func spamReqs(t *testing.T, client *ProxydHTTPClient, method string, limCode int, n int) ([]byte, map[int]int) {
......
......@@ -16,6 +16,7 @@ backends = ["good"]
[rpc_method_mappings]
eth_chainId = "main"
eth_foobar = "main"
eth_baz = "main"
[rate_limit]
base_rate = 2
......@@ -27,3 +28,8 @@ error_message = "over rate limit with special message"
[rate_limit.method_overrides.eth_foobar]
limit = 1
interval = "1s"
[rate_limit.method_overrides.eth_baz]
limit = 1
interval = "1s"
global = true
\ No newline at end of file
......@@ -56,6 +56,7 @@ type Server struct {
senderLim FrontendRateLimiter
limExemptOrigins []*regexp.Regexp
limExemptUserAgents []*regexp.Regexp
globallyLimitedMethods map[string]bool
rpcServer *http.Server
wsServer *http.Server
cache RPCCache
......@@ -133,12 +134,17 @@ func NewServer(
}
overrideLims := make(map[string]FrontendRateLimiter)
globalMethodLims := make(map[string]bool)
for method, override := range rateLimitConfig.MethodOverrides {
var err error
overrideLims[method] = limiterFactory(time.Duration(override.Interval), override.Limit, method)
if err != nil {
return nil, err
}
if override.Global {
globalMethodLims[method] = true
}
}
var senderLim FrontendRateLimiter
if senderRateLimitConfig.Enabled {
......@@ -163,6 +169,7 @@ func NewServer(
},
mainLim: mainLim,
overrideLims: overrideLims,
globallyLimitedMethods: globalMethodLims,
senderLim: senderLim,
limExemptOrigins: limExemptOrigins,
limExemptUserAgents: limExemptUserAgents,
......@@ -243,7 +250,9 @@ func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) {
}
isLimited := func(method string) bool {
if isUnlimitedOrigin || isUnlimitedUserAgent {
isGloballyLimitedMethod := s.isGlobalLimit(method)
fmt.Println(method, isGloballyLimitedMethod)
if !isGloballyLimitedMethod && (isUnlimitedOrigin || isUnlimitedUserAgent) {
return false
}
......@@ -597,6 +606,10 @@ func (s *Server) isUnlimitedUserAgent(origin string) bool {
return false
}
func (s *Server) isGlobalLimit(method string) bool {
return s.globallyLimitedMethods[method]
}
func (s *Server) rateLimitSender(ctx context.Context, req *RPCReq) error {
var params []string
if err := json.Unmarshal(req.Params, &params); err != nil {
......
......@@ -211,7 +211,7 @@ GossipSub [parameters][gossip-parameters]:
- `fanout_ttl` (ttl for fanout maps for topics we are not subscribed to but have published to, in seconds): 24
- `mcache_len` (number of windows to retain full messages in cache for `IWANT` responses): 12
- `mcache_gossip` (number of windows to gossip about): 3
- `seen_ttl` (number of heartbeat intervals to retain message IDs): 80 (= 40 seconds)
- `seen_ttl` (number of heartbeat intervals to retain message IDs): 130 (= 65 seconds)
Notable differences from L1 consensus (Eth2):
......
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