Commit 7bf3cf04 authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into jg/ci

parents 5150cff2 0c0cf54b
......@@ -100,7 +100,7 @@ func NewMetrics(procName string) *Metrics {
Help: "1 if the op-batcher has finished starting up",
}),
ChannelEvs: opmetrics.NewEventVec(factory, ns, "channel", "Channel", []string{"stage"}),
ChannelEvs: opmetrics.NewEventVec(factory, ns, "", "channel", "Channel", []string{"stage"}),
PendingBlocksCount: *factory.NewGaugeVec(prometheus.GaugeOpts{
Namespace: ns,
......@@ -145,7 +145,7 @@ func NewMetrics(procName string) *Metrics {
Buckets: append([]float64{0.1, 0.2}, prometheus.LinearBuckets(0.3, 0.05, 14)...),
}),
BatcherTxEvs: opmetrics.NewEventVec(factory, ns, "batcher_tx", "BatcherTx", []string{"stage"}),
BatcherTxEvs: opmetrics.NewEventVec(factory, ns, "", "batcher_tx", "BatcherTx", []string{"stage"}),
}
}
......
......@@ -4,7 +4,7 @@ import (
"errors"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-program/l2/engineapi"
"github.com/ethereum-optimism/optimism/op-program/client/l2/engineapi"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/common"
......
......@@ -4,8 +4,8 @@ import (
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-program/l2/engineapi"
"github.com/ethereum-optimism/optimism/op-program/l2/engineapi/test"
"github.com/ethereum-optimism/optimism/op-program/client/l2/engineapi"
"github.com/ethereum-optimism/optimism/op-program/client/l2/engineapi/test"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/consensus/ethash"
......
......@@ -8,8 +8,8 @@ import (
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/metrics"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-program/config"
"github.com/ethereum/go-ethereum/log"
)
......@@ -23,8 +23,8 @@ type Driver struct {
pipeline Derivation
}
func NewDriver(logger log.Logger, cfg *config.Config, l1Source derive.L1Fetcher, l2Source derive.Engine) *Driver {
pipeline := derive.NewDerivationPipeline(logger, cfg.Rollup, l1Source, l2Source, metrics.NoopMetrics)
func NewDriver(logger log.Logger, cfg *rollup.Config, l1Source derive.L1Fetcher, l2Source derive.Engine) *Driver {
pipeline := derive.NewDerivationPipeline(logger, cfg, l1Source, l2Source, metrics.NoopMetrics)
pipeline.Reset()
return &Driver{
logger: logger,
......
package l1
import (
"context"
"errors"
"fmt"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
var (
ErrNotFound = ethereum.NotFound
ErrUnknownLabel = errors.New("unknown label")
)
type OracleL1Client struct {
oracle Oracle
head eth.L1BlockRef
}
func NewOracleL1Client(logger log.Logger, oracle Oracle, l1Head common.Hash) *OracleL1Client {
head := eth.InfoToL1BlockRef(oracle.HeaderByBlockHash(l1Head))
logger.Info("L1 head loaded", "hash", head.Hash, "number", head.Number)
return &OracleL1Client{
oracle: oracle,
head: head,
}
}
func (o OracleL1Client) L1BlockRefByLabel(ctx context.Context, label eth.BlockLabel) (eth.L1BlockRef, error) {
if label != eth.Unsafe && label != eth.Safe && label != eth.Finalized {
return eth.L1BlockRef{}, fmt.Errorf("%w: %s", ErrUnknownLabel, label)
}
// The L1 head is pre-agreed and unchanging so it can be used for all of unsafe, safe and finalized
return o.head, nil
}
func (o OracleL1Client) L1BlockRefByNumber(ctx context.Context, number uint64) (eth.L1BlockRef, error) {
if number > o.head.Number {
return eth.L1BlockRef{}, fmt.Errorf("%w: block number %d", ErrNotFound, number)
}
block := o.head
for block.Number > number {
block = eth.InfoToL1BlockRef(o.oracle.HeaderByBlockHash(block.ParentHash))
}
return block, nil
}
func (o OracleL1Client) L1BlockRefByHash(ctx context.Context, hash common.Hash) (eth.L1BlockRef, error) {
return eth.InfoToL1BlockRef(o.oracle.HeaderByBlockHash(hash)), nil
}
func (o OracleL1Client) InfoByHash(ctx context.Context, hash common.Hash) (eth.BlockInfo, error) {
return o.oracle.HeaderByBlockHash(hash), nil
}
func (o OracleL1Client) FetchReceipts(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Receipts, error) {
info, rcpts := o.oracle.ReceiptsByBlockHash(blockHash)
return info, rcpts, nil
}
func (o OracleL1Client) InfoAndTxsByHash(ctx context.Context, hash common.Hash) (eth.BlockInfo, types.Transactions, error) {
info, txs := o.oracle.TransactionsByBlockHash(hash)
return info, txs, nil
}
package l1
import (
"context"
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-node/testutils"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
)
var _ derive.L1Fetcher = (*OracleL1Client)(nil)
var head = blockNum(1000)
func TestInfoByHash(t *testing.T) {
client, oracle := newClient(t)
hash := common.HexToHash("0xAABBCC")
expected := &sources.HeaderInfo{}
oracle.blocks[hash] = expected
info, err := client.InfoByHash(context.Background(), hash)
require.NoError(t, err)
require.Equal(t, expected, info)
}
func TestL1BlockRefByHash(t *testing.T) {
client, oracle := newClient(t)
hash := common.HexToHash("0xAABBCC")
header := &sources.HeaderInfo{}
oracle.blocks[hash] = header
expected := eth.InfoToL1BlockRef(header)
ref, err := client.L1BlockRefByHash(context.Background(), hash)
require.NoError(t, err)
require.Equal(t, expected, ref)
}
func TestFetchReceipts(t *testing.T) {
client, oracle := newClient(t)
hash := common.HexToHash("0xAABBCC")
expectedInfo := &sources.HeaderInfo{}
expectedReceipts := types.Receipts{
&types.Receipt{},
}
oracle.blocks[hash] = expectedInfo
oracle.rcpts[hash] = expectedReceipts
info, rcpts, err := client.FetchReceipts(context.Background(), hash)
require.NoError(t, err)
require.Equal(t, expectedInfo, info)
require.Equal(t, expectedReceipts, rcpts)
}
func TestInfoAndTxsByHash(t *testing.T) {
client, oracle := newClient(t)
hash := common.HexToHash("0xAABBCC")
expectedInfo := &sources.HeaderInfo{}
expectedTxs := types.Transactions{
&types.Transaction{},
}
oracle.blocks[hash] = expectedInfo
oracle.txs[hash] = expectedTxs
info, txs, err := client.InfoAndTxsByHash(context.Background(), hash)
require.NoError(t, err)
require.Equal(t, expectedInfo, info)
require.Equal(t, expectedTxs, txs)
}
func TestL1BlockRefByLabel(t *testing.T) {
t.Run("Unsafe", func(t *testing.T) {
client, _ := newClient(t)
ref, err := client.L1BlockRefByLabel(context.Background(), eth.Unsafe)
require.NoError(t, err)
require.Equal(t, eth.InfoToL1BlockRef(head), ref)
})
t.Run("Safe", func(t *testing.T) {
client, _ := newClient(t)
ref, err := client.L1BlockRefByLabel(context.Background(), eth.Safe)
require.NoError(t, err)
require.Equal(t, eth.InfoToL1BlockRef(head), ref)
})
t.Run("Finalized", func(t *testing.T) {
client, _ := newClient(t)
ref, err := client.L1BlockRefByLabel(context.Background(), eth.Finalized)
require.NoError(t, err)
require.Equal(t, eth.InfoToL1BlockRef(head), ref)
})
t.Run("UnknownLabel", func(t *testing.T) {
client, _ := newClient(t)
ref, err := client.L1BlockRefByLabel(context.Background(), eth.BlockLabel("unknown"))
require.ErrorIs(t, err, ErrUnknownLabel)
require.Equal(t, eth.L1BlockRef{}, ref)
})
}
func TestL1BlockRefByNumber(t *testing.T) {
t.Run("Head", func(t *testing.T) {
client, _ := newClient(t)
ref, err := client.L1BlockRefByNumber(context.Background(), head.NumberU64())
require.NoError(t, err)
require.Equal(t, eth.InfoToL1BlockRef(head), ref)
})
t.Run("AfterHead", func(t *testing.T) {
client, _ := newClient(t)
ref, err := client.L1BlockRefByNumber(context.Background(), head.NumberU64()+1)
// Must be ethereum.NotFound error so the derivation pipeline knows it has gone past the chain head
require.ErrorIs(t, err, ethereum.NotFound)
require.Equal(t, eth.L1BlockRef{}, ref)
})
t.Run("ParentOfHead", func(t *testing.T) {
client, oracle := newClient(t)
parent := blockNum(head.NumberU64() - 1)
oracle.blocks[parent.Hash()] = parent
ref, err := client.L1BlockRefByNumber(context.Background(), parent.NumberU64())
require.NoError(t, err)
require.Equal(t, eth.InfoToL1BlockRef(parent), ref)
})
t.Run("AncestorOfHead", func(t *testing.T) {
client, oracle := newClient(t)
block := head
blocks := []eth.BlockInfo{block}
for i := 0; i < 10; i++ {
block = blockNum(block.NumberU64() - 1)
oracle.blocks[block.Hash()] = block
blocks = append(blocks, block)
}
for _, block := range blocks {
ref, err := client.L1BlockRefByNumber(context.Background(), block.NumberU64())
require.NoError(t, err)
require.Equal(t, eth.InfoToL1BlockRef(block), ref)
}
})
}
func newClient(t *testing.T) (*OracleL1Client, *stubOracle) {
stub := &stubOracle{
t: t,
blocks: make(map[common.Hash]eth.BlockInfo),
txs: make(map[common.Hash]types.Transactions),
rcpts: make(map[common.Hash]types.Receipts),
}
stub.blocks[head.Hash()] = head
client := NewOracleL1Client(testlog.Logger(t, log.LvlDebug), stub, head.Hash())
return client, stub
}
type stubOracle struct {
t *testing.T
// blocks maps block hash to eth.BlockInfo
blocks map[common.Hash]eth.BlockInfo
// txs maps block hash to transactions
txs map[common.Hash]types.Transactions
// rcpts maps Block hash to receipts
rcpts map[common.Hash]types.Receipts
}
func (o stubOracle) HeaderByBlockHash(blockHash common.Hash) eth.BlockInfo {
info, ok := o.blocks[blockHash]
if !ok {
o.t.Fatalf("unknown block %s", blockHash)
}
return info
}
func (o stubOracle) TransactionsByBlockHash(blockHash common.Hash) (eth.BlockInfo, types.Transactions) {
txs, ok := o.txs[blockHash]
if !ok {
o.t.Fatalf("unknown txs %s", blockHash)
}
return o.HeaderByBlockHash(blockHash), txs
}
func (o stubOracle) ReceiptsByBlockHash(blockHash common.Hash) (eth.BlockInfo, types.Receipts) {
rcpts, ok := o.rcpts[blockHash]
if !ok {
o.t.Fatalf("unknown rcpts %s", blockHash)
}
return o.HeaderByBlockHash(blockHash), rcpts
}
func blockNum(num uint64) eth.BlockInfo {
parentNum := num - 1
return &testutils.MockBlockInfo{
InfoHash: common.BytesToHash(big.NewInt(int64(num)).Bytes()),
InfoParentHash: common.BytesToHash(big.NewInt(int64(parentNum)).Bytes()),
InfoCoinbase: common.Address{},
InfoRoot: common.Hash{},
InfoNum: num,
InfoTime: num * 2,
InfoMixDigest: [32]byte{},
InfoBaseFee: nil,
InfoReceiptRoot: common.Hash{},
InfoGasUsed: 0,
}
}
package l1
import (
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
type Oracle interface {
// HeaderByBlockHash retrieves the block header with the given hash.
HeaderByBlockHash(blockHash common.Hash) eth.BlockInfo
// TransactionsByBlockHash retrieves the transactions from the block with the given hash.
TransactionsByBlockHash(blockHash common.Hash) (eth.BlockInfo, types.Transactions)
// ReceiptsByBlockHash retrieves the receipts from the block with the given hash.
ReceiptsByBlockHash(blockHash common.Hash) (eth.BlockInfo, types.Receipts)
}
......@@ -8,7 +8,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-program/l2/engineapi"
"github.com/ethereum-optimism/optimism/op-program/client/l2/engineapi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
......
......@@ -4,7 +4,7 @@ import (
"fmt"
"math/big"
"github.com/ethereum-optimism/optimism/op-program/l2/engineapi"
"github.com/ethereum-optimism/optimism/op-program/client/l2/engineapi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/beacon"
......@@ -39,6 +39,7 @@ func NewOracleBackedL2Chain(logger log.Logger, oracle Oracle, chainCfg *params.C
if err != nil {
return nil, fmt.Errorf("loading l2 head: %w", err)
}
logger.Info("Loaded L2 head", "hash", head.Hash(), "number", head.Number())
return &OracleBackedL2Chain{
log: logger,
oracle: oracle,
......
......@@ -6,8 +6,8 @@ import (
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-program/l2/engineapi"
"github.com/ethereum-optimism/optimism/op-program/l2/engineapi/test"
"github.com/ethereum-optimism/optimism/op-program/client/l2/engineapi"
"github.com/ethereum-optimism/optimism/op-program/client/l2/engineapi/test"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/core"
......
......@@ -7,7 +7,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-program/l2/engineapi"
"github.com/ethereum-optimism/optimism/op-program/client/l2/engineapi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
......
......@@ -10,12 +10,12 @@ import (
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-program/config"
"github.com/ethereum-optimism/optimism/op-program/driver"
"github.com/ethereum-optimism/optimism/op-program/flags"
"github.com/ethereum-optimism/optimism/op-program/l1"
"github.com/ethereum-optimism/optimism/op-program/l2"
"github.com/ethereum-optimism/optimism/op-program/version"
cldr "github.com/ethereum-optimism/optimism/op-program/client/driver"
"github.com/ethereum-optimism/optimism/op-program/host/config"
"github.com/ethereum-optimism/optimism/op-program/host/flags"
"github.com/ethereum-optimism/optimism/op-program/host/l1"
"github.com/ethereum-optimism/optimism/op-program/host/l2"
"github.com/ethereum-optimism/optimism/op-program/host/version"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli"
......@@ -111,7 +111,7 @@ func FaultProofProgram(logger log.Logger, cfg *config.Config) error {
return fmt.Errorf("connect l2 oracle: %w", err)
}
d := driver.NewDriver(logger, cfg, l1Source, l2Source)
d := cldr.NewDriver(logger, cfg.Rollup, l1Source, l2Source)
for {
if err = d.Step(ctx); errors.Is(err, io.EOF) {
break
......
......@@ -7,13 +7,15 @@ import (
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-program/config"
"github.com/ethereum-optimism/optimism/op-program/host/config"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
)
var l2HeadValue = "0x6303578b1fa9480389c51bbcef6fe045bb877da39740819e9eb5f36f94949bd0"
// Use HexToHash(...).Hex() to ensure the strings are the correct length for a hash
var l1HeadValue = common.HexToHash("0x111111").Hex()
var l2HeadValue = common.HexToHash("0x222222").Hex()
func TestLogLevel(t *testing.T) {
t.Run("RejectInvalid", func(t *testing.T) {
......@@ -32,7 +34,8 @@ func TestLogLevel(t *testing.T) {
func TestDefaultCLIOptionsMatchDefaultConfig(t *testing.T) {
cfg := configForArgs(t, addRequiredArgs())
require.Equal(t, config.NewConfig(&chaincfg.Goerli, "genesis.json", common.HexToHash(l2HeadValue)), cfg)
defaultCfg := config.NewConfig(&chaincfg.Goerli, "genesis.json", common.HexToHash(l1HeadValue), common.HexToHash(l2HeadValue))
require.Equal(t, defaultCfg, cfg)
}
func TestNetwork(t *testing.T) {
......@@ -102,6 +105,21 @@ func TestL2Head(t *testing.T) {
})
}
func TestL1Head(t *testing.T) {
t.Run("Required", func(t *testing.T) {
verifyArgsInvalid(t, "flag l1.head is required", addRequiredArgsExcept("--l1.head"))
})
t.Run("Valid", func(t *testing.T) {
cfg := configForArgs(t, replaceRequiredArg("--l1.head", l1HeadValue))
require.Equal(t, common.HexToHash(l1HeadValue), cfg.L1Head)
})
t.Run("Invalid", func(t *testing.T) {
verifyArgsInvalid(t, config.ErrInvalidL1Head.Error(), replaceRequiredArg("--l1.head", "something"))
})
}
func TestL1(t *testing.T) {
expected := "https://example.com:8545"
cfg := configForArgs(t, addRequiredArgs("--l1", expected))
......@@ -149,7 +167,8 @@ func TestL1RPCKind(t *testing.T) {
// Offline support will be added later, but for now it just bails out with an error
func TestOfflineModeNotSupported(t *testing.T) {
logger := log.New()
err := FaultProofProgram(logger, config.NewConfig(&chaincfg.Goerli, "genesis.json", common.HexToHash(l2HeadValue)))
cfg := config.NewConfig(&chaincfg.Goerli, "genesis.json", common.HexToHash(l1HeadValue), common.HexToHash(l2HeadValue))
err := FaultProofProgram(logger, cfg)
require.ErrorContains(t, err, "offline mode not supported")
}
......@@ -199,8 +218,9 @@ func replaceRequiredArg(name string, value string) []string {
func requiredArgs() map[string]string {
return map[string]string{
"--network": "goerli",
"--l2.genesis": "genesis.json",
"--l1.head": l1HeadValue,
"--l2.head": l2HeadValue,
"--l2.genesis": "genesis.json",
}
}
......
......@@ -6,7 +6,7 @@ import (
opnode "github.com/ethereum-optimism/optimism/op-node"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-program/flags"
"github.com/ethereum-optimism/optimism/op-program/host/flags"
"github.com/ethereum/go-ethereum/common"
"github.com/urfave/cli"
)
......@@ -14,6 +14,7 @@ import (
var (
ErrMissingRollupConfig = errors.New("missing rollup config")
ErrMissingL2Genesis = errors.New("missing l2 genesis")
ErrInvalidL1Head = errors.New("invalid l1 head")
ErrInvalidL2Head = errors.New("invalid l2 head")
ErrL1AndL2Inconsistent = errors.New("l1 and l2 options must be specified together or both omitted")
)
......@@ -22,6 +23,7 @@ type Config struct {
Rollup *rollup.Config
L2URL string
L2GenesisPath string
L1Head common.Hash
L2Head common.Hash
L1URL string
L1TrustRPC bool
......@@ -35,12 +37,15 @@ func (c *Config) Check() error {
if err := c.Rollup.Check(); err != nil {
return err
}
if c.L2GenesisPath == "" {
return ErrMissingL2Genesis
if c.L1Head == (common.Hash{}) {
return ErrInvalidL1Head
}
if c.L2Head == (common.Hash{}) {
return ErrInvalidL2Head
}
if c.L2GenesisPath == "" {
return ErrMissingL2Genesis
}
if (c.L1URL != "") != (c.L2URL != "") {
return ErrL1AndL2Inconsistent
}
......@@ -52,10 +57,11 @@ func (c *Config) FetchingEnabled() bool {
}
// NewConfig creates a Config with all optional values set to the CLI default value
func NewConfig(rollupCfg *rollup.Config, l2GenesisPath string, l2Head common.Hash) *Config {
func NewConfig(rollupCfg *rollup.Config, l2GenesisPath string, l1Head common.Hash, l2Head common.Hash) *Config {
return &Config{
Rollup: rollupCfg,
L2GenesisPath: l2GenesisPath,
L1Head: l1Head,
L2Head: l2Head,
L1RPCKind: sources.RPCKindBasic,
}
......@@ -73,11 +79,16 @@ func NewConfigFromCLI(ctx *cli.Context) (*Config, error) {
if l2Head == (common.Hash{}) {
return nil, ErrInvalidL2Head
}
l1Head := common.HexToHash(ctx.GlobalString(flags.L1Head.Name))
if l1Head == (common.Hash{}) {
return nil, ErrInvalidL1Head
}
return &Config{
Rollup: rollupCfg,
L2URL: ctx.GlobalString(flags.L2NodeAddr.Name),
L2GenesisPath: ctx.GlobalString(flags.L2GenesisPath.Name),
L2Head: l2Head,
L1Head: l1Head,
L1URL: ctx.GlobalString(flags.L1NodeAddr.Name),
L1TrustRPC: ctx.GlobalBool(flags.L1TrustRPC.Name),
L1RPCKind: sources.RPCProviderKind(ctx.GlobalString(flags.L1RPCProviderKind.Name)),
......
......@@ -11,66 +11,58 @@ import (
var validRollupConfig = &chaincfg.Goerli
var validL2GenesisPath = "genesis.json"
var validL1Head = common.HexToHash("0x112233889988FF")
var validL2Head = common.HexToHash("0x6303578b1fa9480389c51bbcef6fe045bb877da39740819e9eb5f36f94949bd0")
func TestDefaultConfigIsValid(t *testing.T) {
err := NewConfig(validRollupConfig, validL2GenesisPath, validL2Head).Check()
err := NewConfig(validRollupConfig, validL2GenesisPath, validL1Head, validL2Head).Check()
require.NoError(t, err)
}
func TestRollupConfig(t *testing.T) {
t.Run("Required", func(t *testing.T) {
err := NewConfig(nil, validL2GenesisPath, validL2Head).Check()
err := NewConfig(nil, validL2GenesisPath, validL1Head, validL2Head).Check()
require.ErrorIs(t, err, ErrMissingRollupConfig)
})
t.Run("Invalid", func(t *testing.T) {
err := NewConfig(&rollup.Config{}, validL2GenesisPath, validL2Head).Check()
err := NewConfig(&rollup.Config{}, validL2GenesisPath, validL1Head, validL2Head).Check()
require.ErrorIs(t, err, rollup.ErrBlockTimeZero)
})
}
func TestL2Genesis(t *testing.T) {
t.Run("Required", func(t *testing.T) {
err := NewConfig(validRollupConfig, "", validL2Head).Check()
require.ErrorIs(t, err, ErrMissingL2Genesis)
})
t.Run("Valid", func(t *testing.T) {
err := NewConfig(validRollupConfig, validL2GenesisPath, validL2Head).Check()
require.NoError(t, err)
})
func TestL1HeadRequired(t *testing.T) {
err := NewConfig(validRollupConfig, validL2GenesisPath, common.Hash{}, validL2Head).Check()
require.ErrorIs(t, err, ErrInvalidL1Head)
}
func TestL2Head(t *testing.T) {
t.Run("Required", func(t *testing.T) {
err := NewConfig(validRollupConfig, validL2GenesisPath, common.Hash{}).Check()
require.ErrorIs(t, err, ErrInvalidL2Head)
})
func TestL2HeadRequired(t *testing.T) {
err := NewConfig(validRollupConfig, validL2GenesisPath, validL1Head, common.Hash{}).Check()
require.ErrorIs(t, err, ErrInvalidL2Head)
}
t.Run("Valid", func(t *testing.T) {
err := NewConfig(validRollupConfig, validL2GenesisPath, validL2Head).Check()
require.NoError(t, err)
})
func TestL2GenesisRequired(t *testing.T) {
err := NewConfig(validRollupConfig, "", validL1Head, validL2Head).Check()
require.ErrorIs(t, err, ErrMissingL2Genesis)
}
func TestFetchingArgConsistency(t *testing.T) {
t.Run("RequireL2WhenL1Set", func(t *testing.T) {
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL2Head)
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL1Head, validL2Head)
cfg.L1URL = "https://example.com:1234"
require.ErrorIs(t, cfg.Check(), ErrL1AndL2Inconsistent)
})
t.Run("RequireL1WhenL2Set", func(t *testing.T) {
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL2Head)
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL1Head, validL2Head)
cfg.L2URL = "https://example.com:1234"
require.ErrorIs(t, cfg.Check(), ErrL1AndL2Inconsistent)
})
t.Run("AllowNeitherSet", func(t *testing.T) {
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL2Head)
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL1Head, validL2Head)
require.NoError(t, cfg.Check())
})
t.Run("AllowBothSet", func(t *testing.T) {
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL2Head)
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL1Head, validL2Head)
cfg.L1URL = "https://example.com:1234"
cfg.L2URL = "https://example.com:4678"
require.NoError(t, cfg.Check())
......@@ -79,30 +71,30 @@ func TestFetchingArgConsistency(t *testing.T) {
func TestFetchingEnabled(t *testing.T) {
t.Run("FetchingNotEnabledWhenNoFetcherUrlsSpecified", func(t *testing.T) {
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL2Head)
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL1Head, validL2Head)
require.False(t, cfg.FetchingEnabled(), "Should not enable fetching when node URL not supplied")
})
t.Run("FetchingEnabledWhenFetcherUrlsSpecified", func(t *testing.T) {
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL2Head)
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL1Head, validL2Head)
cfg.L2URL = "https://example.com:1234"
require.False(t, cfg.FetchingEnabled(), "Should not enable fetching when node URL not supplied")
})
t.Run("FetchingNotEnabledWhenNoL1UrlSpecified", func(t *testing.T) {
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL2Head)
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL1Head, validL2Head)
cfg.L2URL = "https://example.com:1234"
require.False(t, cfg.FetchingEnabled(), "Should not enable L1 fetching when L1 node URL not supplied")
})
t.Run("FetchingNotEnabledWhenNoL2UrlSpecified", func(t *testing.T) {
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL2Head)
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL1Head, validL2Head)
cfg.L1URL = "https://example.com:1234"
require.False(t, cfg.FetchingEnabled(), "Should not enable L2 fetching when L2 node URL not supplied")
})
t.Run("FetchingEnabledWhenBothFetcherUrlsSpecified", func(t *testing.T) {
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL2Head)
cfg := NewConfig(&chaincfg.Beta1, validL2GenesisPath, validL1Head, validL2Head)
cfg.L1URL = "https://example.com:1234"
cfg.L2URL = "https://example.com:5678"
require.True(t, cfg.FetchingEnabled(), "Should enable fetching when node URL supplied")
......
......@@ -31,16 +31,21 @@ var (
Usage: "Address of L2 JSON-RPC endpoint to use (eth and debug namespace required)",
EnvVar: service.PrefixEnvVar(envVarPrefix, "L2_RPC"),
}
L2GenesisPath = cli.StringFlag{
Name: "l2.genesis",
Usage: "Path to the op-geth genesis file",
EnvVar: service.PrefixEnvVar(envVarPrefix, "L2_GENESIS"),
L1Head = cli.StringFlag{
Name: "l1.head",
Usage: "Hash of the L1 head block. Derivation stops after this block is processed.",
EnvVar: service.PrefixEnvVar(envVarPrefix, "L1_HEAD"),
}
L2Head = cli.StringFlag{
Name: "l2.head",
Usage: "Hash of the agreed L2 block to start derivation from",
EnvVar: service.PrefixEnvVar(envVarPrefix, "L2_HEAD"),
}
L2GenesisPath = cli.StringFlag{
Name: "l2.genesis",
Usage: "Path to the op-geth genesis file",
EnvVar: service.PrefixEnvVar(envVarPrefix, "L2_GENESIS"),
}
L1NodeAddr = cli.StringFlag{
Name: "l1",
Usage: "Address of L1 JSON-RPC endpoint to use (eth namespace required)",
......@@ -70,8 +75,9 @@ var programFlags = []cli.Flag{
RollupConfig,
Network,
L2NodeAddr,
L2GenesisPath,
L1Head,
L2Head,
L2GenesisPath,
L1NodeAddr,
L1TrustRPC,
L1RPCProviderKind,
......@@ -97,5 +103,8 @@ func CheckRequired(ctx *cli.Context) error {
if ctx.GlobalString(L2Head.Name) == "" {
return fmt.Errorf("flag %s is required", L2Head.Name)
}
if ctx.GlobalString(L1Head.Name) == "" {
return fmt.Errorf("flag %s is required", L1Head.Name)
}
return nil
}
package l1
import (
"context"
"fmt"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
type Source interface {
InfoByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, error)
InfoAndTxsByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Transactions, error)
FetchReceipts(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Receipts, error)
}
type FetchingL1Oracle struct {
ctx context.Context
logger log.Logger
source Source
}
func NewFetchingL1Oracle(ctx context.Context, logger log.Logger, source Source) *FetchingL1Oracle {
return &FetchingL1Oracle{
ctx: ctx,
logger: logger,
source: source,
}
}
func (o FetchingL1Oracle) HeaderByBlockHash(blockHash common.Hash) eth.BlockInfo {
o.logger.Trace("HeaderByBlockHash", "hash", blockHash)
info, err := o.source.InfoByHash(o.ctx, blockHash)
if err != nil {
panic(fmt.Errorf("retrieve block %s: %w", blockHash, err))
}
if info == nil {
panic(fmt.Errorf("unknown block: %s", blockHash))
}
return info
}
func (o FetchingL1Oracle) TransactionsByBlockHash(blockHash common.Hash) (eth.BlockInfo, types.Transactions) {
o.logger.Trace("TransactionsByBlockHash", "hash", blockHash)
info, txs, err := o.source.InfoAndTxsByHash(o.ctx, blockHash)
if err != nil {
panic(fmt.Errorf("retrieve transactions for block %s: %w", blockHash, err))
}
if info == nil || txs == nil {
panic(fmt.Errorf("unknown block: %s", blockHash))
}
return info, txs
}
func (o FetchingL1Oracle) ReceiptsByBlockHash(blockHash common.Hash) (eth.BlockInfo, types.Receipts) {
o.logger.Trace("ReceiptsByBlockHash", "hash", blockHash)
info, rcpts, err := o.source.FetchReceipts(o.ctx, blockHash)
if err != nil {
panic(fmt.Errorf("retrieve receipts for block %s: %w", blockHash, err))
}
if info == nil || rcpts == nil {
panic(fmt.Errorf("unknown block: %s", blockHash))
}
return info, rcpts
}
package l1
import (
"context"
"errors"
"fmt"
"testing"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-node/testlog"
cll1 "github.com/ethereum-optimism/optimism/op-program/client/l1"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
)
// Needs to implement the Oracle interface
var _ cll1.Oracle = (*FetchingL1Oracle)(nil)
// Want to be able to use an L1Client as the data source
var _ Source = (*sources.L1Client)(nil)
func TestHeaderByHash(t *testing.T) {
t.Run("Success", func(t *testing.T) {
expected := &sources.HeaderInfo{}
source := &stubSource{nextInfo: expected}
oracle := newFetchingOracle(t, source)
actual := oracle.HeaderByBlockHash(expected.Hash())
require.Equal(t, expected, actual)
})
t.Run("UnknownBlock", func(t *testing.T) {
oracle := newFetchingOracle(t, &stubSource{})
hash := common.HexToHash("0x4455")
require.PanicsWithError(t, fmt.Errorf("unknown block: %s", hash).Error(), func() {
oracle.HeaderByBlockHash(hash)
})
})
t.Run("Error", func(t *testing.T) {
err := errors.New("kaboom")
source := &stubSource{nextErr: err}
oracle := newFetchingOracle(t, source)
hash := common.HexToHash("0x8888")
require.PanicsWithError(t, fmt.Errorf("retrieve block %s: %w", hash, err).Error(), func() {
oracle.HeaderByBlockHash(hash)
})
})
}
func TestTransactionsByHash(t *testing.T) {
t.Run("Success", func(t *testing.T) {
expectedInfo := &sources.HeaderInfo{}
expectedTxs := types.Transactions{
&types.Transaction{},
}
source := &stubSource{nextInfo: expectedInfo, nextTxs: expectedTxs}
oracle := newFetchingOracle(t, source)
info, txs := oracle.TransactionsByBlockHash(expectedInfo.Hash())
require.Equal(t, expectedInfo, info)
require.Equal(t, expectedTxs, txs)
})
t.Run("UnknownBlock_NoInfo", func(t *testing.T) {
oracle := newFetchingOracle(t, &stubSource{})
hash := common.HexToHash("0x4455")
require.PanicsWithError(t, fmt.Errorf("unknown block: %s", hash).Error(), func() {
oracle.TransactionsByBlockHash(hash)
})
})
t.Run("UnknownBlock_NoTxs", func(t *testing.T) {
oracle := newFetchingOracle(t, &stubSource{nextInfo: &sources.HeaderInfo{}})
hash := common.HexToHash("0x4455")
require.PanicsWithError(t, fmt.Errorf("unknown block: %s", hash).Error(), func() {
oracle.TransactionsByBlockHash(hash)
})
})
t.Run("Error", func(t *testing.T) {
err := errors.New("kaboom")
source := &stubSource{nextErr: err}
oracle := newFetchingOracle(t, source)
hash := common.HexToHash("0x8888")
require.PanicsWithError(t, fmt.Errorf("retrieve transactions for block %s: %w", hash, err).Error(), func() {
oracle.TransactionsByBlockHash(hash)
})
})
}
func TestReceiptsByHash(t *testing.T) {
t.Run("Success", func(t *testing.T) {
expectedInfo := &sources.HeaderInfo{}
expectedRcpts := types.Receipts{
&types.Receipt{},
}
source := &stubSource{nextInfo: expectedInfo, nextRcpts: expectedRcpts}
oracle := newFetchingOracle(t, source)
info, rcpts := oracle.ReceiptsByBlockHash(expectedInfo.Hash())
require.Equal(t, expectedInfo, info)
require.Equal(t, expectedRcpts, rcpts)
})
t.Run("UnknownBlock_NoInfo", func(t *testing.T) {
oracle := newFetchingOracle(t, &stubSource{})
hash := common.HexToHash("0x4455")
require.PanicsWithError(t, fmt.Errorf("unknown block: %s", hash).Error(), func() {
oracle.ReceiptsByBlockHash(hash)
})
})
t.Run("UnknownBlock_NoTxs", func(t *testing.T) {
oracle := newFetchingOracle(t, &stubSource{nextInfo: &sources.HeaderInfo{}})
hash := common.HexToHash("0x4455")
require.PanicsWithError(t, fmt.Errorf("unknown block: %s", hash).Error(), func() {
oracle.ReceiptsByBlockHash(hash)
})
})
t.Run("Error", func(t *testing.T) {
err := errors.New("kaboom")
source := &stubSource{nextErr: err}
oracle := newFetchingOracle(t, source)
hash := common.HexToHash("0x8888")
require.PanicsWithError(t, fmt.Errorf("retrieve receipts for block %s: %w", hash, err).Error(), func() {
oracle.ReceiptsByBlockHash(hash)
})
})
}
func newFetchingOracle(t *testing.T, source Source) *FetchingL1Oracle {
return NewFetchingL1Oracle(context.Background(), testlog.Logger(t, log.LvlDebug), source)
}
type stubSource struct {
nextInfo eth.BlockInfo
nextTxs types.Transactions
nextRcpts types.Receipts
nextErr error
}
func (s stubSource) InfoByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, error) {
return s.nextInfo, s.nextErr
}
func (s stubSource) InfoAndTxsByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Transactions, error) {
return s.nextInfo, s.nextTxs, s.nextErr
}
func (s stubSource) FetchReceipts(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Receipts, error) {
return s.nextInfo, s.nextRcpts, s.nextErr
}
......@@ -6,7 +6,8 @@ import (
"github.com/ethereum-optimism/optimism/op-node/client"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-program/config"
cll1 "github.com/ethereum-optimism/optimism/op-program/client/l1"
"github.com/ethereum-optimism/optimism/op-program/host/config"
"github.com/ethereum/go-ethereum/log"
)
......@@ -16,5 +17,10 @@ func NewFetchingL1(ctx context.Context, logger log.Logger, cfg *config.Config) (
return nil, err
}
return sources.NewL1Client(rpc, logger, nil, sources.L1ClientDefaultConfig(cfg.Rollup, cfg.L1TrustRPC, cfg.L1RPCKind))
source, err := sources.NewL1Client(rpc, logger, nil, sources.L1ClientDefaultConfig(cfg.Rollup, cfg.L1TrustRPC, cfg.L1RPCKind))
if err != nil {
return nil, err
}
oracle := NewFetchingL1Oracle(ctx, logger, source)
return cll1.NewOracleL1Client(logger, oracle, cfg.L1Head), err
}
......@@ -10,6 +10,8 @@ import (
"testing"
"github.com/ethereum-optimism/optimism/op-node/testutils"
cll2 "github.com/ethereum-optimism/optimism/op-program/client/l2"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/rawdb"
......@@ -19,7 +21,7 @@ import (
)
// Require the fetching oracle to implement StateOracle
var _ StateOracle = (*FetchingL2Oracle)(nil)
var _ cll2.StateOracle = (*FetchingL2Oracle)(nil)
type callContextRequest struct {
ctx context.Context
......
......@@ -7,7 +7,8 @@ import (
"os"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-program/config"
cll2 "github.com/ethereum-optimism/optimism/op-program/client/l2"
"github.com/ethereum-optimism/optimism/op-program/host/config"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
......@@ -23,11 +24,11 @@ func NewFetchingEngine(ctx context.Context, logger log.Logger, cfg *config.Confi
return nil, fmt.Errorf("connect l2 oracle: %w", err)
}
engineBackend, err := NewOracleBackedL2Chain(logger, oracle, genesis, cfg.L2Head)
engineBackend, err := cll2.NewOracleBackedL2Chain(logger, oracle, genesis, cfg.L2Head)
if err != nil {
return nil, fmt.Errorf("create l2 chain: %w", err)
}
return NewOracleEngine(cfg.Rollup, logger, engineBackend), nil
return cll2.NewOracleEngine(cfg.Rollup, logger, engineBackend), nil
}
func loadL2Genesis(cfg *config.Config) (*params.ChainConfig, error) {
......
......@@ -15,6 +15,7 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli"
......@@ -334,7 +335,11 @@ func (l *L2OutputSubmitter) sendTransaction(ctx context.Context, output *eth.Out
if err != nil {
return err
}
l.log.Info("proposer tx successfully published", "tx_hash", receipt.TxHash)
if receipt.Status == types.ReceiptStatusFailed {
l.log.Error("proposer tx successfully published but reverted", "tx_hash", receipt.TxHash)
} else {
l.log.Info("proposer tx successfully published", "tx_hash", receipt.TxHash)
}
return nil
}
......
......@@ -16,17 +16,19 @@ func (e *Event) Record() {
e.LastTime.SetToCurrentTime()
}
func NewEvent(factory Factory, ns string, name string, displayName string) Event {
func NewEvent(factory Factory, ns string, subsystem string, name string, displayName string) Event {
return Event{
Total: factory.NewCounter(prometheus.CounterOpts{
Namespace: ns,
Name: fmt.Sprintf("%s_total", name),
Help: fmt.Sprintf("Count of %s events", displayName),
Subsystem: subsystem,
}),
LastTime: factory.NewGauge(prometheus.GaugeOpts{
Namespace: ns,
Name: fmt.Sprintf("last_%s_unix", name),
Help: fmt.Sprintf("Timestamp of last %s event", displayName),
Subsystem: subsystem,
}),
}
}
......@@ -41,17 +43,19 @@ func (e *EventVec) Record(lvs ...string) {
e.LastTime.WithLabelValues(lvs...).SetToCurrentTime()
}
func NewEventVec(factory Factory, ns string, name string, displayName string, labelNames []string) EventVec {
func NewEventVec(factory Factory, ns string, subsystem string, name string, displayName string, labelNames []string) EventVec {
return EventVec{
Total: *factory.NewCounterVec(prometheus.CounterOpts{
Namespace: ns,
Name: fmt.Sprintf("%s_total", name),
Help: fmt.Sprintf("Count of %s events", displayName),
Subsystem: subsystem,
}, labelNames),
LastTime: *factory.NewGaugeVec(prometheus.GaugeOpts{
Namespace: ns,
Name: fmt.Sprintf("last_%s_unix", name),
Help: fmt.Sprintf("Timestamp of last %s event", displayName),
Subsystem: subsystem,
}, labelNames),
}
}
......@@ -19,7 +19,9 @@ type TxMetricer interface {
type TxMetrics struct {
TxL1GasFee prometheus.Gauge
txFees prometheus.Counter
TxGasBump prometheus.Gauge
txFeeHistogram prometheus.Histogram
LatencyConfirmedTx prometheus.Gauge
currentNonce prometheus.Gauge
txPublishError *prometheus.CounterVec
......@@ -49,6 +51,19 @@ func MakeTxMetrics(ns string, factory metrics.Factory) TxMetrics {
Help: "L1 gas fee for transactions in GWEI",
Subsystem: "txmgr",
}),
txFees: factory.NewCounter(prometheus.CounterOpts{
Namespace: ns,
Name: "tx_fee_gwei_total",
Help: "Sum of fees spent for all transactions in GWEI",
Subsystem: "txmgr",
}),
txFeeHistogram: factory.NewHistogram(prometheus.HistogramOpts{
Namespace: ns,
Name: "tx_fee_histogram_gwei",
Help: "Tx Fee in GWEI",
Subsystem: "txmgr",
Buckets: []float64{0.5, 1, 2, 5, 10, 20, 40, 60, 80, 100, 200, 400, 800, 1600},
}),
TxGasBump: factory.NewGauge(prometheus.GaugeOpts{
Namespace: ns,
Name: "tx_gas_bump",
......@@ -73,8 +88,8 @@ func MakeTxMetrics(ns string, factory metrics.Factory) TxMetrics {
Help: "Count of publish errors. Labells are sanitized error strings",
Subsystem: "txmgr",
}, []string{"error"}),
confirmEvent: metrics.NewEventVec(factory, ns, "confirm", "tx confirm", []string{"status"}),
publishEvent: metrics.NewEvent(factory, ns, "publish", "tx publish"),
confirmEvent: metrics.NewEventVec(factory, ns, "txmgr", "confirm", "tx confirm", []string{"status"}),
publishEvent: metrics.NewEvent(factory, ns, "txmgr", "publish", "tx publish"),
rpcError: factory.NewCounter(prometheus.CounterOpts{
Namespace: ns,
Name: "rpc_error_count",
......@@ -90,8 +105,12 @@ func (t *TxMetrics) RecordNonce(nonce uint64) {
// TxConfirmed records lots of information about the confirmed transaction
func (t *TxMetrics) TxConfirmed(receipt *types.Receipt) {
fee := float64(receipt.EffectiveGasPrice.Uint64() * receipt.GasUsed / params.GWei)
t.confirmEvent.Record(receiptStatusString(receipt))
t.TxL1GasFee.Set(float64(receipt.EffectiveGasPrice.Uint64() * receipt.GasUsed / params.GWei))
t.TxL1GasFee.Set(fee)
t.txFees.Add(fee)
t.txFeeHistogram.Observe(fee)
}
func (t *TxMetrics) RecordGasBumpCount(times int) {
......
import { DeployConfig } from '../src/deploy-config'
const config: DeployConfig = {
finalSystemOwner: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
controller: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
portalGuardian: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
proxyAdminOwner: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
l1StartingBlockTag:
'0x126e52a0cc0ae18948f567ee9443f4a8f0db67c437706e35baee424eb314a0d0',
l1ChainID: 1,
l2ChainID: 10,
l2BlockTime: 2,
maxSequencerDrift: 600,
sequencerWindowSize: 3600,
channelTimeout: 300,
p2pSequencerAddress: '0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65',
batchInboxAddress: '0xff00000000000000000000000000000000000010',
batchSenderAddress: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8',
l2OutputOracleSubmissionInterval: 20,
l2OutputOracleStartingTimestamp: 1679069195,
l2OutputOracleStartingBlockNumber: 79149704,
l2OutputOracleProposer: '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC',
l2OutputOracleChallenger: '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC',
finalizationPeriodSeconds: 2,
baseFeeVaultRecipient: '0x90F79bf6EB2c4f870365E785982E1f101E93b906',
l1FeeVaultRecipient: '0x90F79bf6EB2c4f870365E785982E1f101E93b906',
sequencerFeeVaultRecipient: '0x90F79bf6EB2c4f870365E785982E1f101E93b906',
governanceTokenName: 'Optimism',
governanceTokenSymbol: 'OP',
governanceTokenOwner: '0x90F79bf6EB2c4f870365E785982E1f101E93b906',
l2GenesisBlockGasLimit: '0x17D7840',
l2GenesisBlockCoinbase: '0x4200000000000000000000000000000000000011',
l2GenesisBlockBaseFeePerGas: '0x3b9aca00',
gasPriceOracleOverhead: 2100,
gasPriceOracleScalar: 1000000,
eip1559Denominator: 50,
eip1559Elasticity: 10,
l2GenesisRegolithTimeOffset: '0x0',
}
export default config
......@@ -7,6 +7,7 @@ const config: DeployConfig = {
finalSystemOwner: '0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A',
controller: '0x78339d822c23d943e4a2d4c3dd5408f66e6d662d',
portalGuardian: '0x78339d822c23d943e4a2d4c3dd5408f66e6d662d',
proxyAdminOwner: '0x90F79bf6EB2c4f870365E785982E1f101E93b906',
l1StartingBlockTag:
'0x126e52a0cc0ae18948f567ee9443f4a8f0db67c437706e35baee424eb314a0d0',
......@@ -28,7 +29,6 @@ const config: DeployConfig = {
l2OutputOracleChallenger: '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC',
finalizationPeriodSeconds: 2,
proxyAdminOwner: '0x90F79bf6EB2c4f870365E785982E1f101E93b906',
baseFeeVaultRecipient: '0x90F79bf6EB2c4f870365E785982E1f101E93b906',
l1FeeVaultRecipient: '0x90F79bf6EB2c4f870365E785982E1f101E93b906',
sequencerFeeVaultRecipient: '0x90F79bf6EB2c4f870365E785982E1f101E93b906',
......
......@@ -13,6 +13,7 @@ import {
getDeploymentAddress,
doOwnershipTransfer,
doPhase,
liveDeployer,
} from '../src/deploy-utils'
const uint128Max = ethers.BigNumber.from('0xffffffffffffffffffffffffffffffff')
......@@ -66,8 +67,12 @@ const deployFn: DeployFunction = async (hre) => {
])
// If we have the key for the controller then we don't need to wait for external txns.
const isLiveDeployer =
deployer.toLowerCase() === hre.deployConfig.controller.toLowerCase()
// Set the DISABLE_LIVE_DEPLOYER=true in the env to ensure the script will pause to simulate scenarios
// where the controller is not the deployer.
const isLiveDeployer = await liveDeployer({
hre,
disabled: process.env.DISABLE_LIVE_DEPLOYER,
})
// Transfer ownership of the ProxyAdmin to the SystemDictator.
if ((await ProxyAdmin.owner()) !== SystemDictator.address) {
......
......@@ -15,6 +15,7 @@ import {
doStep,
printTenderlySimulationLink,
printCastCommand,
liveDeployer,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
......@@ -82,8 +83,12 @@ const deployFn: DeployFunction = async (hre) => {
])
// If we have the key for the controller then we don't need to wait for external txns.
const isLiveDeployer =
deployer.toLowerCase() === hre.deployConfig.controller.toLowerCase()
// Set the DISABLE_LIVE_DEPLOYER=true in the env to ensure the script will pause to simulate scenarios
// where the controller is not the deployer.
const isLiveDeployer = await liveDeployer({
hre,
disabled: process.env.DISABLE_LIVE_DEPLOYER,
})
// Step 3 clears out some state from the AddressManager.
await doStep({
......
......@@ -22,6 +22,14 @@ const config: HardhatUserConfig = {
hardhat: {
live: false,
},
local: {
live: false,
url: 'http://localhost:8545',
saveDeployments: false,
accounts: [
'ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
],
},
// NOTE: The 'mainnet' network is currently being used for mainnet rehearsals.
mainnet: {
url: process.env.L1_RPC || 'https://mainnet-l1-rehearsal.optimism.io',
......
......@@ -355,6 +355,28 @@ export const doOwnershipTransfer = async (opts: {
}
}
/**
* Check if the script should submit the transaction or wait for the deployer to do it manually.
*
* @param hre HardhatRuntimeEnvironment.
* @param ovveride Allow m
* @returns True if the current step is the target step.
*/
export const liveDeployer = async (opts: {
hre: HardhatRuntimeEnvironment
disabled: string | undefined
}): Promise<boolean> => {
let ret: boolean
if (!!opts.disabled) {
ret = false
}
const { deployer } = await opts.hre.getNamedAccounts()
ret =
deployer.toLowerCase() === opts.hre.deployConfig.controller.toLowerCase()
console.log('Setting live deployer to', ret)
return ret
}
/**
* Mini helper for checking if the current step is a target step.
*
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -15,8 +15,8 @@
"lint:check": "eslint . --max-warnings=0",
"lint:fix": "yarn lint:check --fix",
"pre-commit": "lint-staged",
"test": "ts-mocha test/*.spec.ts",
"test:coverage": "nyc ts-mocha test/*.spec.ts && nyc merge .nyc_output coverage.json"
"test": "ts-mocha test/**/*.spec.ts",
"test:coverage": "nyc ts-mocha test/**/*.spec.ts && nyc merge .nyc_output coverage.json"
},
"keywords": [
"optimism",
......
import { BigNumber } from '@ethersproject/bignumber'
/* Imports: Internal */
import { expect } from './setup'
import { expect } from '../setup'
import {
toRpcHexString,
remove0x,
......@@ -12,7 +12,7 @@ import {
encodeHex,
hexStringEquals,
bytes32ify,
} from '../src'
} from '../../src'
describe('remove0x', () => {
it('should return undefined', () => {
......@@ -62,6 +62,7 @@ describe('toHexString', () => {
'The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received null'
)
})
it('should return with a hex string', () => {
const cases = [
{ input: 0, output: '0x00' },
......@@ -104,6 +105,7 @@ describe('padHexString', () => {
expect(padHexString('abcd', 1)).to.deep.equal('abcd')
expect(padHexString('abcdefgh', 3).length).to.deep.equal(8)
})
it('should return a string padded with 0x and zeros', () => {
expect(padHexString('0xabcd', 3)).to.deep.equal('0x00abcd')
})
......
/* Imports: Internal */
import { expect } from './setup'
import { sleep, clone, reqenv, getenv } from '../src'
import { expect } from '../setup'
import { sleep, clone, reqenv, getenv } from '../../src'
describe('sleep', async () => {
it('should return wait input amount of ms', async () => {
......@@ -21,7 +21,7 @@ describe('clone', async () => {
})
describe('reqenv', async () => {
let cachedEnvironment
let cachedEnvironment: NodeJS.ProcessEnv
const temporaryEnvironmentKey = 'testVariable'
const temporaryEnvironment = {
[temporaryEnvironmentKey]: 'This is an environment variable',
......@@ -51,7 +51,7 @@ describe('reqenv', async () => {
})
describe('getenv', async () => {
let cachedEnvironment
let cachedEnvironment: NodeJS.ProcessEnv
const temporaryEnvironmentKey = 'testVariable'
const temporaryEnvironment = {
[temporaryEnvironmentKey]: 'This is an environment variable',
......
import { assert } from 'chai'
/* Imports: Internal */
import { expect } from './setup'
import { expectApprox, awaitCondition } from '../src'
import { expect } from '../setup'
import { expectApprox, awaitCondition } from '../../src'
describe('awaitCondition', () => {
it('should try the condition fn until it returns true', async () => {
......@@ -42,6 +42,7 @@ describe('expectApprox', () => {
absoluteLowerDeviation: 20,
})
})
it('should pass when the actual number is lower, but within the expected range of the target', async () => {
expectApprox(81, 100, {
percentUpperDeviation: 20,
......@@ -50,6 +51,7 @@ describe('expectApprox', () => {
absoluteLowerDeviation: 20,
})
})
it('should throw an error when no deviation values are given', async () => {
try {
expectApprox(101, 100, {})
......@@ -75,6 +77,7 @@ describe('expectApprox', () => {
)
}
})
it('... and absoluteUpperDeviation sets the upper bound', async () => {
try {
expectApprox(121, 100, {
......@@ -88,6 +91,7 @@ describe('expectApprox', () => {
}
})
})
describe('... when both values are defined', () => {
it('... and percentUpperDeviation sets the upper bound', async () => {
try {
......@@ -102,6 +106,7 @@ describe('expectApprox', () => {
)
}
})
it('... and absoluteUpperDeviation sets the upper bound', async () => {
try {
expectApprox(121, 100, {
......
import { expect } from './setup'
import { applyL1ToL2Alias, undoL1ToL2Alias } from '../src'
import { expect } from '../setup'
import { applyL1ToL2Alias, undoL1ToL2Alias } from '../../src'
describe('address aliasing utils', () => {
describe('applyL1ToL2Alias', () => {
......
import './setup'
import '../setup'
/* Internal Imports */
import { expect } from 'chai'
......@@ -9,13 +9,13 @@ import {
sequencerBatch,
BatchType,
SequencerBatch,
} from '../src'
} from '../../src'
describe('BatchEncoder', function () {
this.timeout(10_000)
// eslint-disable-next-line @typescript-eslint/no-var-requires
const data = require('./fixtures/calldata.json')
const data = require('../fixtures/calldata.json')
describe('appendSequencerBatch', () => {
it('legacy: should work with the simple case', () => {
......@@ -112,6 +112,7 @@ describe('BatchEncoder', function () {
],
transactions: ['0x454234000000112', '0x45423400000012'],
}
expect(() => encodeAppendSequencerBatch(batch)).to.throw(
'Unexpected uneven hex string value!'
)
......
import './setup'
import '../setup'
import { BigNumber } from '@ethersproject/bignumber'
import { zeroesAndOnes, calldataCost } from '../src'
import { zeroesAndOnes, calldataCost } from '../../src'
describe('Fees', () => {
it('should count zeros and ones', () => {
......
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