Commit 6ec97fd2 authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into qbzzt/230410-spec-proposals

parents f72a7d2e 16e2cc94
......@@ -600,7 +600,7 @@ jobs:
- run:
name: run lint
command: |
golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell,errorlint --timeout 2m -e "errors.As" -e "errors.Is" ./...
golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell,errorlint --timeout 5m -e "errors.As" -e "errors.Is" ./...
working_directory: <<parameters.module>>
go-test:
......
......@@ -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"
......
......@@ -640,11 +640,11 @@ func TestSystemMockP2P(t *testing.T) {
require.Nil(t, err, "Sending L2 tx to sequencer")
// Wait for tx to be mined on the L2 sequencer chain
receiptSeq, err := waitForTransaction(tx.Hash(), l2Seq, 10*time.Duration(sys.RollupConfig.BlockTime)*time.Second)
receiptSeq, err := waitForTransaction(tx.Hash(), l2Seq, 5*time.Minute)
require.Nil(t, err, "Waiting for L2 tx on sequencer")
// Wait until the block it was first included in shows up in the safe chain on the verifier
receiptVerif, err := waitForTransaction(tx.Hash(), l2Verif, 10*time.Duration(sys.RollupConfig.BlockTime)*time.Second)
receiptVerif, err := waitForTransaction(tx.Hash(), l2Verif, 5*time.Minute)
require.Nil(t, err, "Waiting for L2 tx on verifier")
require.Equal(t, receiptSeq, receiptVerif)
......
......@@ -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)
}
......@@ -40,12 +40,12 @@ func (o *OracleKeyValueStore) Get(key []byte) ([]byte, error) {
if len(key) == codePrefixedKeyLength && bytes.HasPrefix(key, rawdb.CodePrefix) {
key = key[len(rawdb.CodePrefix):]
return o.oracle.CodeByHash(*(*[common.HashLength]byte)(key))
return o.oracle.CodeByHash(*(*[common.HashLength]byte)(key)), nil
}
if len(key) != common.HashLength {
return nil, ErrInvalidKeyLength
}
return o.oracle.NodeByHash(*(*[common.HashLength]byte)(key))
return o.oracle.NodeByHash(*(*[common.HashLength]byte)(key)), nil
}
func (o *OracleKeyValueStore) NewBatch() ethdb.Batch {
......
package l2
import (
"fmt"
"math/big"
"testing"
......@@ -27,16 +26,8 @@ var (
var _ ethdb.KeyValueStore = (*OracleKeyValueStore)(nil)
func TestGet(t *testing.T) {
t.Run("UnknownKey", func(t *testing.T) {
oracle := newStubStateOracle()
db := NewOracleBackedDB(oracle)
val, err := db.Get(common.Hash{}.Bytes())
require.Error(t, err)
require.Nil(t, val)
})
t.Run("IncorrectLengthKey", func(t *testing.T) {
oracle := newStubStateOracle()
oracle := newStubStateOracle(t)
db := NewOracleBackedDB(oracle)
val, err := db.Get([]byte{1, 2, 3})
require.ErrorIs(t, err, ErrInvalidKeyLength)
......@@ -44,7 +35,7 @@ func TestGet(t *testing.T) {
})
t.Run("KeyWithCodePrefix", func(t *testing.T) {
oracle := newStubStateOracle()
oracle := newStubStateOracle(t)
db := NewOracleBackedDB(oracle)
key := common.HexToHash("0x12345678")
prefixedKey := append(rawdb.CodePrefix, key.Bytes()...)
......@@ -58,7 +49,7 @@ func TestGet(t *testing.T) {
})
t.Run("NormalKeyThatHappensToStartWithCodePrefix", func(t *testing.T) {
oracle := newStubStateOracle()
oracle := newStubStateOracle(t)
db := NewOracleBackedDB(oracle)
key := make([]byte, common.HashLength)
copy(rawdb.CodePrefix, key)
......@@ -74,7 +65,7 @@ func TestGet(t *testing.T) {
t.Run("KnownKey", func(t *testing.T) {
key := common.HexToHash("0xAA4488")
expected := []byte{2, 6, 3, 8}
oracle := newStubStateOracle()
oracle := newStubStateOracle(t)
oracle.data[key] = expected
db := NewOracleBackedDB(oracle)
val, err := db.Get(key.Bytes())
......@@ -85,7 +76,7 @@ func TestGet(t *testing.T) {
func TestPut(t *testing.T) {
t.Run("NewKey", func(t *testing.T) {
oracle := newStubStateOracle()
oracle := newStubStateOracle(t)
db := NewOracleBackedDB(oracle)
key := common.HexToHash("0xAA4488")
value := []byte{2, 6, 3, 8}
......@@ -97,7 +88,7 @@ func TestPut(t *testing.T) {
require.Equal(t, value, actual)
})
t.Run("ReplaceKey", func(t *testing.T) {
oracle := newStubStateOracle()
oracle := newStubStateOracle(t)
db := NewOracleBackedDB(oracle)
key := common.HexToHash("0xAA4488")
value1 := []byte{2, 6, 3, 8}
......@@ -119,6 +110,7 @@ func TestSupportsStateDBOperations(t *testing.T) {
genesisBlock := l2Genesis.MustCommit(realDb)
loader := &kvStateOracle{
t: t,
source: realDb,
}
assertStateDataAvailable(t, NewOracleBackedDB(loader), l2Genesis, genesisBlock)
......@@ -126,7 +118,7 @@ func TestSupportsStateDBOperations(t *testing.T) {
func TestUpdateState(t *testing.T) {
l2Genesis := createGenesis()
oracle := newStubStateOracle()
oracle := newStubStateOracle(t)
db := rawdb.NewDatabase(NewOracleBackedDB(oracle))
genesisBlock := l2Genesis.MustCommit(db)
......@@ -203,43 +195,50 @@ func assertStateDataAvailable(t *testing.T, db ethdb.KeyValueStore, l2Genesis *c
require.Equal(t, common.Hash{}, statedb.GetCodeHash(unknownAccount), "unset account code hash")
}
func newStubStateOracle() *stubStateOracle {
func newStubStateOracle(t *testing.T) *stubStateOracle {
return &stubStateOracle{
t: t,
data: make(map[common.Hash][]byte),
code: make(map[common.Hash][]byte),
}
}
type stubStateOracle struct {
t *testing.T
data map[common.Hash][]byte
code map[common.Hash][]byte
}
func (o *stubStateOracle) NodeByHash(nodeHash common.Hash) ([]byte, error) {
func (o *stubStateOracle) NodeByHash(nodeHash common.Hash) []byte {
data, ok := o.data[nodeHash]
if !ok {
return nil, fmt.Errorf("no value for node %v", nodeHash)
o.t.Fatalf("no value for node %v", nodeHash)
}
return data, nil
return data
}
func (o *stubStateOracle) CodeByHash(hash common.Hash) ([]byte, error) {
func (o *stubStateOracle) CodeByHash(hash common.Hash) []byte {
data, ok := o.code[hash]
if !ok {
return nil, fmt.Errorf("no value for code %v", hash)
o.t.Fatalf("no value for code %v", hash)
}
return data, nil
return data
}
// kvStateOracle loads data from a source ethdb.KeyValueStore
type kvStateOracle struct {
t *testing.T
source ethdb.KeyValueStore
}
func (o *kvStateOracle) NodeByHash(nodeHash common.Hash) ([]byte, error) {
return o.source.Get(nodeHash.Bytes())
func (o *kvStateOracle) NodeByHash(nodeHash common.Hash) []byte {
val, err := o.source.Get(nodeHash.Bytes())
if err != nil {
o.t.Fatalf("error retrieving node %v: %v", nodeHash, err)
}
return val
}
func (o *kvStateOracle) CodeByHash(hash common.Hash) ([]byte, error) {
return rawdb.ReadCode(o.source, hash), nil
func (o *kvStateOracle) CodeByHash(hash common.Hash) []byte {
return rawdb.ReadCode(o.source, hash)
}
......@@ -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"
......@@ -35,10 +35,8 @@ type OracleBackedL2Chain struct {
var _ engineapi.EngineBackend = (*OracleBackedL2Chain)(nil)
func NewOracleBackedL2Chain(logger log.Logger, oracle Oracle, chainCfg *params.ChainConfig, l2Head common.Hash) (*OracleBackedL2Chain, error) {
head, err := oracle.BlockByHash(l2Head)
if err != nil {
return nil, fmt.Errorf("loading l2 head: %w", err)
}
head := oracle.BlockByHash(l2Head)
logger.Info("Loaded L2 head", "hash", head.Hash(), "number", head.Number())
return &OracleBackedL2Chain{
log: logger,
oracle: oracle,
......@@ -98,10 +96,7 @@ func (o *OracleBackedL2Chain) GetBlockByHash(hash common.Hash) *types.Block {
return block
}
// Retrieve from the oracle
block, err := o.oracle.BlockByHash(hash)
if err != nil {
handleError(err)
}
block = o.oracle.BlockByHash(hash)
if block == nil {
return nil
}
......@@ -194,7 +189,3 @@ func (o *OracleBackedL2Chain) SetFinalized(header *types.Header) {
func (o *OracleBackedL2Chain) SetSafe(header *types.Header) {
o.safe = header
}
func handleError(err error) {
panic(err)
}
......@@ -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"
......@@ -113,8 +113,9 @@ func TestUpdateStateDatabaseWhenImportingBlock(t *testing.T) {
require.NotEqual(t, blocks[1].Root(), newBlock.Root(), "block should have modified world state")
_, err = chain.StateAt(newBlock.Root())
require.Error(t, err, "state from non-imported block should not be available")
require.Panics(t, func() {
_, _ = chain.StateAt(newBlock.Root())
}, "state from non-imported block should not be available")
err = chain.InsertBlockWithoutSetHead(newBlock)
require.NoError(t, err)
......@@ -223,8 +224,8 @@ func newStubBlockOracle(chain []*types.Block, db ethdb.Database) *stubBlockOracl
}
}
func (o stubBlockOracle) BlockByHash(blockHash common.Hash) (*types.Block, error) {
return o.blocks[blockHash], nil
func (o stubBlockOracle) BlockByHash(blockHash common.Hash) *types.Block {
return o.blocks[blockHash]
}
func TestEngineAPITests(t *testing.T) {
......
......@@ -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"
......
......@@ -11,13 +11,11 @@ type StateOracle interface {
// NodeByHash retrieves the merkle-patricia trie node pre-image for a given hash.
// Trie nodes may be from the world state trie or any account storage trie.
// Contract code is not stored as part of the trie and must be retrieved via CodeByHash
// Returns an error if the pre-image is unavailable.
NodeByHash(nodeHash common.Hash) ([]byte, error)
NodeByHash(nodeHash common.Hash) []byte
// CodeByHash retrieves the contract code pre-image for a given hash.
// codeHash should be retrieved from the world state account for a contract.
// Returns an error if the pre-image is unavailable.
CodeByHash(codeHash common.Hash) ([]byte, error)
CodeByHash(codeHash common.Hash) []byte
}
// Oracle defines the high-level API used to retrieve L2 data.
......@@ -26,6 +24,5 @@ type Oracle interface {
StateOracle
// BlockByHash retrieves the block with the given hash.
// Returns an error if the block is not available.
BlockByHash(blockHash common.Hash) (*types.Block, error)
BlockByHash(blockHash common.Hash) *types.Block
}
......@@ -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
}
......@@ -42,19 +42,26 @@ func NewFetchingL2Oracle(ctx context.Context, logger log.Logger, l2Url string) (
}, nil
}
func (o *FetchingL2Oracle) NodeByHash(hash common.Hash) ([]byte, error) {
func (o *FetchingL2Oracle) NodeByHash(hash common.Hash) []byte {
// MPT nodes are stored as the hash of the node (with no prefix)
return o.dbGet(hash.Bytes())
node, err := o.dbGet(hash.Bytes())
if err != nil {
panic(err)
}
return node
}
func (o *FetchingL2Oracle) CodeByHash(hash common.Hash) ([]byte, error) {
func (o *FetchingL2Oracle) CodeByHash(hash common.Hash) []byte {
// First try retrieving with the new code prefix
code, err := o.dbGet(append(rawdb.CodePrefix, hash.Bytes()...))
if err != nil {
// Fallback to the legacy un-prefixed version
return o.dbGet(hash.Bytes())
code, err = o.dbGet(hash.Bytes())
if err != nil {
panic(err)
}
}
return code, nil
return code
}
func (o *FetchingL2Oracle) dbGet(key []byte) ([]byte, error) {
......@@ -66,10 +73,10 @@ func (o *FetchingL2Oracle) dbGet(key []byte) ([]byte, error) {
return node, nil
}
func (o *FetchingL2Oracle) BlockByHash(blockHash common.Hash) (*types.Block, error) {
func (o *FetchingL2Oracle) BlockByHash(blockHash common.Hash) *types.Block {
block, err := o.blockSource.BlockByHash(o.ctx, blockHash)
if err != nil {
return nil, fmt.Errorf("fetch block %s: %w", blockHash.Hex(), err)
panic(fmt.Errorf("fetch block %s: %w", blockHash.Hex(), err))
}
return block, nil
return block
}
......@@ -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
......@@ -61,9 +63,9 @@ func TestNodeByHash(t *testing.T) {
}
fetcher := newFetcher(nil, stub)
node, err := fetcher.NodeByHash(hash)
require.ErrorIs(t, err, stub.nextErr)
require.Nil(t, node)
require.Panics(t, func() {
fetcher.NodeByHash(hash)
})
})
t.Run("Success", func(t *testing.T) {
......@@ -73,8 +75,7 @@ func TestNodeByHash(t *testing.T) {
}
fetcher := newFetcher(nil, stub)
node, err := fetcher.NodeByHash(hash)
require.NoError(t, err)
node := fetcher.NodeByHash(hash)
require.EqualValues(t, expected, node)
})
......@@ -84,7 +85,7 @@ func TestNodeByHash(t *testing.T) {
}
fetcher := newFetcher(nil, stub)
_, _ = fetcher.NodeByHash(hash)
fetcher.NodeByHash(hash)
require.Len(t, stub.requests, 1, "should make single request")
req := stub.requests[0]
require.Equal(t, "debug_dbGet", req.method)
......@@ -102,9 +103,7 @@ func TestCodeByHash(t *testing.T) {
}
fetcher := newFetcher(nil, stub)
node, err := fetcher.CodeByHash(hash)
require.ErrorIs(t, err, stub.nextErr)
require.Nil(t, node)
require.Panics(t, func() { fetcher.CodeByHash(hash) })
})
t.Run("Success", func(t *testing.T) {
......@@ -114,8 +113,7 @@ func TestCodeByHash(t *testing.T) {
}
fetcher := newFetcher(nil, stub)
node, err := fetcher.CodeByHash(hash)
require.NoError(t, err)
node := fetcher.CodeByHash(hash)
require.EqualValues(t, expected, node)
})
......@@ -125,7 +123,7 @@ func TestCodeByHash(t *testing.T) {
}
fetcher := newFetcher(nil, stub)
_, _ = fetcher.CodeByHash(hash)
fetcher.CodeByHash(hash)
require.Len(t, stub.requests, 1, "should make single request")
req := stub.requests[0]
require.Equal(t, "debug_dbGet", req.method)
......@@ -139,7 +137,8 @@ func TestCodeByHash(t *testing.T) {
}
fetcher := newFetcher(nil, stub)
_, _ = fetcher.CodeByHash(hash)
// Panics because the code can't be found with or without the prefix
require.Panics(t, func() { fetcher.CodeByHash(hash) })
require.Len(t, stub.requests, 2, "should request with and without prefix")
req := stub.requests[0]
require.Equal(t, "debug_dbGet", req.method)
......@@ -181,8 +180,7 @@ func TestBlockByHash(t *testing.T) {
stub := &stubBlockSource{nextResult: block}
fetcher := newFetcher(stub, nil)
res, err := fetcher.BlockByHash(hash)
require.NoError(t, err)
res := fetcher.BlockByHash(hash)
require.Same(t, block, res)
})
......@@ -190,16 +188,16 @@ func TestBlockByHash(t *testing.T) {
stub := &stubBlockSource{nextErr: errors.New("boom")}
fetcher := newFetcher(stub, nil)
res, err := fetcher.BlockByHash(hash)
require.ErrorIs(t, err, stub.nextErr)
require.Nil(t, res)
require.Panics(t, func() {
fetcher.BlockByHash(hash)
})
})
t.Run("RequestArgs", func(t *testing.T) {
stub := &stubBlockSource{}
fetcher := newFetcher(stub, nil)
_, _ = fetcher.BlockByHash(hash)
fetcher.BlockByHash(hash)
require.Len(t, stub.requests, 1, "should make single request")
req := stub.requests[0]
......
......@@ -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', () => {
......
......@@ -93,7 +93,7 @@ def clamp(v: i256, min: u128, max: u128) -> u128:
if prev_num == now_num:
now_basefee = prev_basefee
now_bought_gas = prev_bought_gas + requested_gas
elif prev_num != now_num :
elif prev_num != now_num:
# Width extension and conversion to signed integer math
gas_used_delta = int128(prev_bought_gas) - int128(TARGET_RESOURCE_LIMIT)
# Use truncating (round to 0) division - solidity's default.
......@@ -101,18 +101,18 @@ elif prev_num != now_num :
base_fee_per_gas_delta = prev_basefee * gas_used_delta / TARGET_RESOURCE_LIMIT / BASE_FEE_MAX_CHANGE_DENOMINATOR
now_basefee_wide = prev_basefee + base_fee_per_gas_delta
now_basefee = clamp(now_basefee_wide, min=MINIMUM_BASEFEE, max=UINT_64_MAX_VALUE)
now_basefee = clamp(now_basefee_wide, min=MINIMUM_BASEFEE, max=UINT_128_MAX_VALUE)
now_bought_gas = requested_gas
# If we skipped multiple blocks between the previous block and now update the basefee again.
# This is not exactly the same as iterating the above function, but quite close for reasonable
# gas target values. It is also constant time wrt the number of missed blocks which is important
# for keeping gas usage stable.
if prev_num + 1 < now_num:
n = now_num - prev_num - 1
# Apply 7/8 reduction to prev_basefee for the n empty blocks in a row.
now_basefee_wide = prev_basefee * pow(1-(1/BASE_FEE_MAX_CHANGE_DENOMINATOR), n)
now_basefee = clamp(now_basefee_wide, min=MINIMUM_BASEFEE, max=UINT_64_MAX_VALUE)
# If we skipped multiple blocks between the previous block and now update the basefee again.
# This is not exactly the same as iterating the above function, but quite close for reasonable
# gas target values. It is also constant time wrt the number of missed blocks which is important
# for keeping gas usage stable.
if prev_num + 1 < now_num:
n = now_num - prev_num - 1
# Apply 7/8 reduction to prev_basefee for the n empty blocks in a row.
now_basefee_wide = now_basefee * pow(1-(1/BASE_FEE_MAX_CHANGE_DENOMINATOR), n)
now_basefee = clamp(now_basefee_wide, min=MINIMUM_BASEFEE, max=type(uint128).max)
require(now_bought_gas < MAX_RESOURCE_LIMIT)
......
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