Commit 600b1f1c authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into sequencer-stop-start

parents bbebb76e 1f134ba1
---
'@eth-optimism/contracts-bedrock': patch
---
Added a test for large deposit gaps
......@@ -26,6 +26,7 @@
/op-chain-ops @ethereum-optimism/go-reviewers
/op-e2e @ethereum-optimism/go-reviewers
/op-node @ethereum-optimism/go-reviewers
/op-node/rollup @protolambda @trianglesphere
/op-proposer @ethereum-optimism/go-reviewers
/op-service @ethereum-optimism/go-reviewers
......
......@@ -8,7 +8,7 @@ There are plenty of ways to contribute, in particular we appreciate support in t
- Fixing and responding to existing issues. You can start off with those tagged ["good first issue"](https://github.com/ethereum-optimism/optimism/contribute) which are meant as introductory issues for external contributors.
- Improving the [community site](https://community.optimism.io/), [documentation](https://github.com/ethereum-optimism/community-hub) and [tutorials](https://github.com/ethereum-optimism/optimism-tutorial).
- Become an "Optimizer" and answer questions in the [Optimism Discord](https://discord.optimism.io).
- Get involved in the protocol design process by proposing changes or new features or write parts of the spec yourself in the [optimistic-specs repo](https://github.com/ethereum-optimism/optimistic-specs).
- Get involved in the protocol design process by proposing changes or new features or write parts of the spec yourself in the [specs subdirectory](./specs/).
Note that we have a [Code of Conduct](https://github.com/ethereum-optimism/.github/blob/master/CODE_OF_CONDUCT.md), please follow it in all your interactions with the project.
......
......@@ -163,7 +163,7 @@ func NewBatchSubmitterWithSigner(cfg Config, addr common.Address, signer SignerF
done: make(chan struct{}),
log: l,
state: NewChannelManager(l, cfg.ChannelTimeout),
// TODO: this context only exists because the even loop doesn't reach done
// TODO: this context only exists because the event loop doesn't reach done
// if the tx manager is blocking forever due to e.g. insufficient balance.
ctx: ctx,
cancel: cancel,
......
......@@ -14,12 +14,12 @@ import (
// version 1 messages have a value and the most significant
// byte of the nonce is a 1
type CrossDomainMessage struct {
Nonce *big.Int
Sender *common.Address
Target *common.Address
Value *big.Int
GasLimit *big.Int
Data []byte
Nonce *big.Int `json:"nonce"`
Sender *common.Address `json:"sender"`
Target *common.Address `json:"target"`
Value *big.Int `json:"value"`
GasLimit *big.Int `json:"gasLimit"`
Data []byte `json:"data"`
}
// NewCrossDomainMessage creates a CrossDomainMessage.
......
......@@ -76,7 +76,7 @@ func MigrateWithdrawal(withdrawal *LegacyWithdrawal, l1CrossDomainMessenger *com
withdrawal.Target,
value,
new(big.Int),
withdrawal.Data,
[]byte(withdrawal.Data),
)
if err != nil {
return nil, fmt.Errorf("cannot abi encode relayMessage: %w", err)
......
......@@ -2,11 +2,13 @@ package crossdomain
import (
"errors"
"fmt"
"math/big"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
)
......@@ -28,7 +30,7 @@ type Withdrawal struct {
Target *common.Address `json:"target"`
Value *big.Int `json:"value"`
GasLimit *big.Int `json:"gasLimit"`
Data []byte `json:"data"`
Data hexutil.Bytes `json:"data"`
}
// NewWithdrawal will create a Withdrawal
......@@ -44,7 +46,7 @@ func NewWithdrawal(
Target: target,
Value: value,
GasLimit: gasLimit,
Data: data,
Data: hexutil.Bytes(data),
}
}
......@@ -58,9 +60,9 @@ func (w *Withdrawal) Encode() ([]byte, error) {
{Name: "gasLimit", Type: Uint256Type},
{Name: "data", Type: BytesType},
}
enc, err := args.Pack(w.Nonce, w.Sender, w.Target, w.Value, w.GasLimit, w.Data)
enc, err := args.Pack(w.Nonce, w.Sender, w.Target, w.Value, w.GasLimit, []byte(w.Data))
if err != nil {
return nil, err
return nil, fmt.Errorf("cannot encode withdrawal: %w", err)
}
return enc, nil
}
......@@ -110,7 +112,7 @@ func (w *Withdrawal) Decode(data []byte) error {
w.Target = &target
w.Value = value
w.GasLimit = gasLimit
w.Data = msgData
w.Data = hexutil.Bytes(msgData)
return nil
}
......@@ -150,6 +152,6 @@ func (w *Withdrawal) WithdrawalTransaction() bindings.TypesWithdrawalTransaction
Target: *w.Target,
Value: w.Value,
GasLimit: w.GasLimit,
Data: w.Data,
Data: []byte(w.Data),
}
}
......@@ -26,9 +26,10 @@ type L2Sequencer struct {
func NewL2Sequencer(t Testing, log log.Logger, l1 derive.L1Fetcher, eng L2API, cfg *rollup.Config, seqConfDepth uint64) *L2Sequencer {
ver := NewL2Verifier(t, log, l1, eng, cfg)
attrBuilder := derive.NewFetchingAttributesBuilder(cfg, l1, eng)
return &L2Sequencer{
L2Verifier: *ver,
sequencer: driver.NewSequencer(log, cfg, l1, eng, ver.derivation, metrics.NoopMetrics),
sequencer: driver.NewSequencer(log, cfg, eng, ver.derivation, attrBuilder, metrics.NoopMetrics),
l1OriginSelector: driver.NewL1OriginSelector(log, cfg, l1, seqConfDepth),
seqOldOrigin: false,
failL2GossipUnsafeBlock: nil,
......
package doc
import (
"encoding/json"
"fmt"
"os"
"strings"
"github.com/ethereum-optimism/optimism/op-node/metrics"
"github.com/olekukonko/tablewriter"
"github.com/urfave/cli"
)
var Subcommands = cli.Commands{
{
Name: "metrics",
Usage: "Dumps a list of supported metrics to stdout",
Flags: []cli.Flag{
cli.StringFlag{
Name: "format",
Value: "markdown",
Usage: "Output format (json|markdown)",
},
},
Action: func(ctx *cli.Context) error {
m := metrics.NewMetrics("default")
supportedMetrics := m.Document()
format := ctx.String("format")
if format != "markdown" && format != "json" {
return fmt.Errorf("invalid format: %s", format)
}
if format == "json" {
enc := json.NewEncoder(os.Stdout)
return enc.Encode(supportedMetrics)
}
table := tablewriter.NewWriter(os.Stdout)
table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
table.SetCenterSeparator("|")
table.SetAutoWrapText(false)
table.SetHeader([]string{"Metric", "Description", "Labels", "Type"})
var data [][]string
for _, metric := range supportedMetrics {
labels := strings.Join(metric.Labels, ",")
data = append(data, []string{metric.Name, metric.Help, labels, metric.Type})
}
table.AppendBulk(data)
table.Render()
return nil
},
},
}
......@@ -9,6 +9,8 @@ import (
"syscall"
"time"
"github.com/ethereum-optimism/optimism/op-node/cmd/doc"
"github.com/urfave/cli"
opnode "github.com/ethereum-optimism/optimism/op-node"
......@@ -68,6 +70,10 @@ func main() {
Name: "genesis",
Subcommands: genesis.Subcommands,
},
{
Name: "doc",
Subcommands: doc.Subcommands,
},
}
err := app.Run(os.Args)
......
......@@ -23,6 +23,7 @@ require (
github.com/libp2p/go-libp2p-testing v0.12.0
github.com/multiformats/go-multiaddr v0.7.0
github.com/multiformats/go-multiaddr-dns v0.3.1
github.com/olekukonko/tablewriter v0.0.5
github.com/prometheus/client_golang v1.13.0
github.com/stretchr/testify v1.8.1
github.com/urfave/cli v1.22.9
......@@ -115,7 +116,6 @@ require (
github.com/multiformats/go-multistream v0.3.3 // indirect
github.com/multiformats/go-varint v0.0.6 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/opencontainers/runtime-spec v1.0.2 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
......
package metrics
import (
"github.com/ethereum-optimism/optimism/op-service/metrics"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
// CacheMetrics implements the Metrics interface in the caching package,
......@@ -34,16 +34,16 @@ func (m *CacheMetrics) CacheGet(typeLabel string, hit bool) {
}
}
func NewCacheMetrics(registry prometheus.Registerer, ns string, name string, displayName string) *CacheMetrics {
func NewCacheMetrics(factory metrics.Factory, ns string, name string, displayName string) *CacheMetrics {
return &CacheMetrics{
SizeVec: promauto.With(registry).NewGaugeVec(prometheus.GaugeOpts{
SizeVec: factory.NewGaugeVec(prometheus.GaugeOpts{
Namespace: ns,
Name: name + "_size",
Help: displayName + " cache size",
}, []string{
"type",
}),
GetVec: promauto.With(registry).NewCounterVec(prometheus.CounterOpts{
GetVec: factory.NewCounterVec(prometheus.CounterOpts{
Namespace: ns,
Name: name + "_get",
Help: displayName + " lookups, hitting or not",
......@@ -51,7 +51,7 @@ func NewCacheMetrics(registry prometheus.Registerer, ns string, name string, dis
"type",
"hit",
}),
AddVec: promauto.With(registry).NewCounterVec(prometheus.CounterOpts{
AddVec: factory.NewCounterVec(prometheus.CounterOpts{
Namespace: ns,
Name: name + "_add",
Help: displayName + " additions, evicting previous values or not",
......
......@@ -4,8 +4,9 @@ import (
"fmt"
"time"
"github.com/ethereum-optimism/optimism/op-service/metrics"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
type EventMetrics struct {
......@@ -18,14 +19,14 @@ func (e *EventMetrics) RecordEvent() {
e.LastTime.Set(float64(time.Now().Unix()))
}
func NewEventMetrics(registry prometheus.Registerer, ns string, name string, displayName string) *EventMetrics {
func NewEventMetrics(factory metrics.Factory, ns string, name string, displayName string) *EventMetrics {
return &EventMetrics{
Total: promauto.With(registry).NewCounter(prometheus.CounterOpts{
Total: factory.NewCounter(prometheus.CounterOpts{
Namespace: ns,
Name: fmt.Sprintf("%s_total", name),
Help: fmt.Sprintf("Count of %s events", displayName),
}),
LastTime: promauto.With(registry).NewGauge(prometheus.GaugeOpts{
LastTime: factory.NewGauge(prometheus.GaugeOpts{
Namespace: ns,
Name: fmt.Sprintf("last_%s_unix", name),
Help: fmt.Sprintf("Timestamp of last %s event", displayName),
......
This diff is collapsed.
......@@ -15,7 +15,7 @@ import (
var (
// UnsafeBlockSignerAddressSystemConfigStorageSlot is the storage slot identifier of the unsafeBlockSigner
// `address` storage value in the SystemConfig L1 contract.
// `address` storage value in the SystemConfig L1 contract. Computed as `keccak256("systemconfig.unsafeblocksigner")`
UnsafeBlockSignerAddressSystemConfigStorageSlot = common.HexToHash("0x65a7ed542fb37fe237fdfbdd70b31598523fe5b32879e307bae27a0bd9581c08")
)
......
......@@ -23,17 +23,32 @@ type SystemConfigL2Fetcher interface {
SystemConfigByL2Hash(ctx context.Context, hash common.Hash) (eth.SystemConfig, error)
}
// FetchingAttributesBuilder fetches inputs for the building of L2 payload attributes on the fly.
type FetchingAttributesBuilder struct {
cfg *rollup.Config
l1 L1ReceiptsFetcher
l2 SystemConfigL2Fetcher
}
func NewFetchingAttributesBuilder(cfg *rollup.Config, l1 L1ReceiptsFetcher, l2 SystemConfigL2Fetcher) *FetchingAttributesBuilder {
return &FetchingAttributesBuilder{
cfg: cfg,
l1: l1,
l2: l2,
}
}
// PreparePayloadAttributes prepares a PayloadAttributes template that is ready to build a L2 block with deposits only, on top of the given l2Parent, with the given epoch as L1 origin.
// The template defaults to NoTxPool=true, and no sequencer transactions: the caller has to modify the template to add transactions,
// by setting NoTxPool=false as sequencer, or by appending batch transactions as verifier.
// The severity of the error is returned; a crit=false error means there was a temporary issue, like a failed RPC or time-out.
// A crit=true error means the input arguments are inconsistent or invalid.
func PreparePayloadAttributes(ctx context.Context, cfg *rollup.Config, dl L1ReceiptsFetcher, l2 SystemConfigL2Fetcher, l2Parent eth.L2BlockRef, timestamp uint64, epoch eth.BlockID) (attrs *eth.PayloadAttributes, err error) {
func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Context, l2Parent eth.L2BlockRef, epoch eth.BlockID) (attrs *eth.PayloadAttributes, err error) {
var l1Info eth.BlockInfo
var depositTxs []hexutil.Bytes
var seqNumber uint64
sysConfig, err := l2.SystemConfigByL2Hash(ctx, l2Parent.Hash)
sysConfig, err := ba.l2.SystemConfigByL2Hash(ctx, l2Parent.Hash)
if err != nil {
return nil, NewTemporaryError(fmt.Errorf("failed to retrieve L2 parent block: %w", err))
}
......@@ -42,7 +57,7 @@ func PreparePayloadAttributes(ctx context.Context, cfg *rollup.Config, dl L1Rece
// case we need to fetch all transaction receipts from the L1 origin block so we can scan for
// user deposits.
if l2Parent.L1Origin.Number != epoch.Number {
info, receipts, err := dl.FetchReceipts(ctx, epoch.Hash)
info, receipts, err := ba.l1.FetchReceipts(ctx, epoch.Hash)
if err != nil {
return nil, NewTemporaryError(fmt.Errorf("failed to fetch L1 block info and receipts: %w", err))
}
......@@ -52,13 +67,13 @@ func PreparePayloadAttributes(ctx context.Context, cfg *rollup.Config, dl L1Rece
epoch, info.ParentHash(), l2Parent.L1Origin))
}
deposits, err := DeriveDeposits(receipts, cfg.DepositContractAddress)
deposits, err := DeriveDeposits(receipts, ba.cfg.DepositContractAddress)
if err != nil {
// deposits may never be ignored. Failing to process them is a critical error.
return nil, NewCriticalError(fmt.Errorf("failed to derive some deposits: %w", err))
}
// apply sysCfg changes
if err := UpdateSystemConfigWithL1Receipts(&sysConfig, receipts, cfg); err != nil {
if err := UpdateSystemConfigWithL1Receipts(&sysConfig, receipts, ba.cfg); err != nil {
return nil, NewCriticalError(fmt.Errorf("failed to apply derived L1 sysCfg updates: %w", err))
}
......@@ -69,7 +84,7 @@ func PreparePayloadAttributes(ctx context.Context, cfg *rollup.Config, dl L1Rece
if l2Parent.L1Origin.Hash != epoch.Hash {
return nil, NewResetError(fmt.Errorf("cannot create new block with L1 origin %s in conflict with L1 origin %s", epoch, l2Parent.L1Origin))
}
info, err := dl.InfoByHash(ctx, epoch.Hash)
info, err := ba.l1.InfoByHash(ctx, epoch.Hash)
if err != nil {
return nil, NewTemporaryError(fmt.Errorf("failed to fetch L1 block info: %w", err))
}
......@@ -78,6 +93,13 @@ func PreparePayloadAttributes(ctx context.Context, cfg *rollup.Config, dl L1Rece
seqNumber = l2Parent.SequenceNumber + 1
}
// Sanity check the L1 origin was correctly selected to maintain the time invariant between L1 and L2
nextL2Time := l2Parent.Time + ba.cfg.BlockTime
if nextL2Time < l1Info.Time() {
return nil, NewResetError(fmt.Errorf("cannot build L2 block on top %s for time %d before L1 origin %s at time %d",
l2Parent, nextL2Time, eth.ToBlockID(l1Info), l1Info.Time()))
}
l1InfoTx, err := L1InfoDepositBytes(seqNumber, l1Info, sysConfig)
if err != nil {
return nil, NewCriticalError(fmt.Errorf("failed to create l1InfoTx: %w", err))
......@@ -88,7 +110,7 @@ func PreparePayloadAttributes(ctx context.Context, cfg *rollup.Config, dl L1Rece
txs = append(txs, depositTxs...)
return &eth.PayloadAttributes{
Timestamp: hexutil.Uint64(timestamp),
Timestamp: hexutil.Uint64(nextL2Time),
PrevRandao: eth.Bytes32(l1Info.MixDigest()),
SuggestedFeeRecipient: predeploys.SequencerFeeVaultAddr,
Transactions: txs,
......
......@@ -23,22 +23,24 @@ import (
// This stage can be reset by clearing it's batch buffer.
// This stage does not need to retain any references to L1 blocks.
type AttributesBuilder interface {
PreparePayloadAttributes(ctx context.Context, l2Parent eth.L2BlockRef, epoch eth.BlockID) (attrs *eth.PayloadAttributes, err error)
}
type AttributesQueue struct {
log log.Logger
config *rollup.Config
dl L1ReceiptsFetcher
eng SystemConfigL2Fetcher
prev *BatchQueue
batch *BatchData
log log.Logger
config *rollup.Config
builder AttributesBuilder
prev *BatchQueue
batch *BatchData
}
func NewAttributesQueue(log log.Logger, cfg *rollup.Config, l1Fetcher L1ReceiptsFetcher, eng SystemConfigL2Fetcher, prev *BatchQueue) *AttributesQueue {
func NewAttributesQueue(log log.Logger, cfg *rollup.Config, builder AttributesBuilder, prev *BatchQueue) *AttributesQueue {
return &AttributesQueue{
log: log,
config: cfg,
dl: l1Fetcher,
eng: eng,
prev: prev,
log: log,
config: cfg,
builder: builder,
prev: prev,
}
}
......@@ -74,9 +76,13 @@ func (aq *AttributesQueue) createNextAttributes(ctx context.Context, batch *Batc
if batch.ParentHash != l2SafeHead.Hash {
return nil, NewResetError(fmt.Errorf("valid batch has bad parent hash %s, expected %s", batch.ParentHash, l2SafeHead.Hash))
}
// sanity check timestamp
if expected := l2SafeHead.Time + aq.config.BlockTime; expected != batch.Timestamp {
return nil, NewResetError(fmt.Errorf("valid batch has bad timestamp %d, expected %d", batch.Timestamp, expected))
}
fetchCtx, cancel := context.WithTimeout(ctx, 20*time.Second)
defer cancel()
attrs, err := PreparePayloadAttributes(fetchCtx, aq.config, aq.dl, aq.eng, l2SafeHead, batch.Timestamp, batch.Epoch())
attrs, err := aq.builder.PreparePayloadAttributes(fetchCtx, l2SafeHead, batch.Epoch())
if err != nil {
return nil, err
}
......
......@@ -40,6 +40,7 @@ func TestAttributesQueue(t *testing.T) {
safeHead := testutils.RandomL2BlockRef(rng)
safeHead.L1Origin = l1Info.ID()
safeHead.Time = l1Info.InfoTime
batch := &BatchData{BatchV1{
ParentHash: safeHead.Hash,
......@@ -75,11 +76,12 @@ func TestAttributesQueue(t *testing.T) {
NoTxPool: true,
GasLimit: (*eth.Uint64Quantity)(&expectedL1Cfg.GasLimit),
}
attrBuilder := NewFetchingAttributesBuilder(cfg, l1Fetcher, l2Fetcher)
aq := NewAttributesQueue(testlog.Logger(t, log.LvlError), cfg, l1Fetcher, l2Fetcher, nil)
aq := NewAttributesQueue(testlog.Logger(t, log.LvlError), cfg, attrBuilder, nil)
actual, err := aq.createNextAttributes(context.Background(), batch, safeHead)
require.Nil(t, err)
require.NoError(t, err)
require.Equal(t, attrs, *actual)
}
......@@ -43,12 +43,12 @@ func TestPreparePayloadAttributes(t *testing.T) {
l1CfgFetcher := &testutils.MockL2Client{}
l1CfgFetcher.ExpectSystemConfigByL2Hash(l2Parent.Hash, testSysCfg, nil)
defer l1CfgFetcher.AssertExpectations(t)
l2Time := l2Parent.Time + cfg.BlockTime
l1Info := testutils.RandomBlockInfo(rng)
l1Info.InfoNum = l2Parent.L1Origin.Number + 1
epoch := l1Info.ID()
l1Fetcher.ExpectFetchReceipts(epoch.Hash, l1Info, nil, nil)
_, err := PreparePayloadAttributes(context.Background(), cfg, l1Fetcher, l1CfgFetcher, l2Parent, l2Time, epoch)
attrBuilder := NewFetchingAttributesBuilder(cfg, l1Fetcher, l1CfgFetcher)
_, err := attrBuilder.PreparePayloadAttributes(context.Background(), l2Parent, epoch)
require.NotNil(t, err, "inconsistent L1 origin error expected")
require.ErrorIs(t, err, ErrReset, "inconsistent L1 origin transition must be handled like a critical error with reorg")
})
......@@ -60,11 +60,11 @@ func TestPreparePayloadAttributes(t *testing.T) {
l1CfgFetcher := &testutils.MockL2Client{}
l1CfgFetcher.ExpectSystemConfigByL2Hash(l2Parent.Hash, testSysCfg, nil)
defer l1CfgFetcher.AssertExpectations(t)
l2Time := l2Parent.Time + cfg.BlockTime
l1Info := testutils.RandomBlockInfo(rng)
l1Info.InfoNum = l2Parent.L1Origin.Number
epoch := l1Info.ID()
_, err := PreparePayloadAttributes(context.Background(), cfg, l1Fetcher, l1CfgFetcher, l2Parent, l2Time, epoch)
attrBuilder := NewFetchingAttributesBuilder(cfg, l1Fetcher, l1CfgFetcher)
_, err := attrBuilder.PreparePayloadAttributes(context.Background(), l2Parent, epoch)
require.NotNil(t, err, "inconsistent L1 origin error expected")
require.ErrorIs(t, err, ErrReset, "inconsistent L1 origin transition must be handled like a critical error with reorg")
})
......@@ -76,12 +76,12 @@ func TestPreparePayloadAttributes(t *testing.T) {
l1CfgFetcher := &testutils.MockL2Client{}
l1CfgFetcher.ExpectSystemConfigByL2Hash(l2Parent.Hash, testSysCfg, nil)
defer l1CfgFetcher.AssertExpectations(t)
l2Time := l2Parent.Time + cfg.BlockTime
epoch := l2Parent.L1Origin
epoch.Number += 1
mockRPCErr := errors.New("mock rpc error")
l1Fetcher.ExpectFetchReceipts(epoch.Hash, nil, nil, mockRPCErr)
_, err := PreparePayloadAttributes(context.Background(), cfg, l1Fetcher, l1CfgFetcher, l2Parent, l2Time, epoch)
attrBuilder := NewFetchingAttributesBuilder(cfg, l1Fetcher, l1CfgFetcher)
_, err := attrBuilder.PreparePayloadAttributes(context.Background(), l2Parent, epoch)
require.ErrorIs(t, err, mockRPCErr, "mock rpc error expected")
require.ErrorIs(t, err, ErrTemporary, "rpc errors should not be critical, it is not necessary to reorg")
})
......@@ -93,11 +93,11 @@ func TestPreparePayloadAttributes(t *testing.T) {
l1CfgFetcher := &testutils.MockL2Client{}
l1CfgFetcher.ExpectSystemConfigByL2Hash(l2Parent.Hash, testSysCfg, nil)
defer l1CfgFetcher.AssertExpectations(t)
l2Time := l2Parent.Time + cfg.BlockTime
epoch := l2Parent.L1Origin
mockRPCErr := errors.New("mock rpc error")
l1Fetcher.ExpectInfoByHash(epoch.Hash, nil, mockRPCErr)
_, err := PreparePayloadAttributes(context.Background(), cfg, l1Fetcher, l1CfgFetcher, l2Parent, l2Time, epoch)
attrBuilder := NewFetchingAttributesBuilder(cfg, l1Fetcher, l1CfgFetcher)
_, err := attrBuilder.PreparePayloadAttributes(context.Background(), l2Parent, epoch)
require.ErrorIs(t, err, mockRPCErr, "mock rpc error expected")
require.ErrorIs(t, err, ErrTemporary, "rpc errors should not be critical, it is not necessary to reorg")
})
......@@ -109,7 +109,6 @@ func TestPreparePayloadAttributes(t *testing.T) {
l1CfgFetcher := &testutils.MockL2Client{}
l1CfgFetcher.ExpectSystemConfigByL2Hash(l2Parent.Hash, testSysCfg, nil)
defer l1CfgFetcher.AssertExpectations(t)
l2Time := l2Parent.Time + cfg.BlockTime
l1Info := testutils.RandomBlockInfo(rng)
l1Info.InfoParentHash = l2Parent.L1Origin.Hash
l1Info.InfoNum = l2Parent.L1Origin.Number + 1
......@@ -117,7 +116,8 @@ func TestPreparePayloadAttributes(t *testing.T) {
l1InfoTx, err := L1InfoDepositBytes(0, l1Info, testSysCfg)
require.NoError(t, err)
l1Fetcher.ExpectFetchReceipts(epoch.Hash, l1Info, nil, nil)
attrs, err := PreparePayloadAttributes(context.Background(), cfg, l1Fetcher, l1CfgFetcher, l2Parent, l2Time, epoch)
attrBuilder := NewFetchingAttributesBuilder(cfg, l1Fetcher, l1CfgFetcher)
attrs, err := attrBuilder.PreparePayloadAttributes(context.Background(), l2Parent, epoch)
require.NoError(t, err)
require.NotNil(t, attrs)
require.Equal(t, l2Parent.Time+cfg.BlockTime, uint64(attrs.Timestamp))
......@@ -135,7 +135,6 @@ func TestPreparePayloadAttributes(t *testing.T) {
l1CfgFetcher := &testutils.MockL2Client{}
l1CfgFetcher.ExpectSystemConfigByL2Hash(l2Parent.Hash, testSysCfg, nil)
defer l1CfgFetcher.AssertExpectations(t)
l2Time := l2Parent.Time + cfg.BlockTime
l1Info := testutils.RandomBlockInfo(rng)
l1Info.InfoParentHash = l2Parent.L1Origin.Hash
l1Info.InfoNum = l2Parent.L1Origin.Number + 1
......@@ -157,7 +156,8 @@ func TestPreparePayloadAttributes(t *testing.T) {
l2Txs := append(append(make([]eth.Data, 0), l1InfoTx), usedDepositTxs...)
l1Fetcher.ExpectFetchReceipts(epoch.Hash, l1Info, receipts, nil)
attrs, err := PreparePayloadAttributes(context.Background(), cfg, l1Fetcher, l1CfgFetcher, l2Parent, l2Time, epoch)
attrBuilder := NewFetchingAttributesBuilder(cfg, l1Fetcher, l1CfgFetcher)
attrs, err := attrBuilder.PreparePayloadAttributes(context.Background(), l2Parent, epoch)
require.NoError(t, err)
require.NotNil(t, attrs)
require.Equal(t, l2Parent.Time+cfg.BlockTime, uint64(attrs.Timestamp))
......@@ -175,7 +175,6 @@ func TestPreparePayloadAttributes(t *testing.T) {
l1CfgFetcher := &testutils.MockL2Client{}
l1CfgFetcher.ExpectSystemConfigByL2Hash(l2Parent.Hash, testSysCfg, nil)
defer l1CfgFetcher.AssertExpectations(t)
l2Time := l2Parent.Time + cfg.BlockTime
l1Info := testutils.RandomBlockInfo(rng)
l1Info.InfoHash = l2Parent.L1Origin.Hash
l1Info.InfoNum = l2Parent.L1Origin.Number
......@@ -185,7 +184,8 @@ func TestPreparePayloadAttributes(t *testing.T) {
require.NoError(t, err)
l1Fetcher.ExpectInfoByHash(epoch.Hash, l1Info, nil)
attrs, err := PreparePayloadAttributes(context.Background(), cfg, l1Fetcher, l1CfgFetcher, l2Parent, l2Time, epoch)
attrBuilder := NewFetchingAttributesBuilder(cfg, l1Fetcher, l1CfgFetcher)
attrs, err := attrBuilder.PreparePayloadAttributes(context.Background(), l2Parent, epoch)
require.NoError(t, err)
require.NotNil(t, attrs)
require.Equal(t, l2Parent.Time+cfg.BlockTime, uint64(attrs.Timestamp))
......
......@@ -74,7 +74,8 @@ func NewDerivationPipeline(log log.Logger, cfg *rollup.Config, l1Fetcher L1Fetch
bank := NewChannelBank(log, cfg, frameQueue, l1Fetcher)
chInReader := NewChannelInReader(log, bank)
batchQueue := NewBatchQueue(log, cfg, chInReader)
attributesQueue := NewAttributesQueue(log, cfg, l1Fetcher, engine, batchQueue)
attrBuilder := NewFetchingAttributesBuilder(cfg, l1Fetcher, engine)
attributesQueue := NewAttributesQueue(log, cfg, attrBuilder, batchQueue)
// Step stages
eng := NewEngineQueue(log, cfg, engine, metrics, attributesQueue, l1Fetcher)
......
......@@ -27,6 +27,17 @@ var (
ConfigUpdateEventVersion0 = common.Hash{}
)
var (
// A left-padded uint256 equal to 32.
oneWordUint = common.Hash{31: 32}
// A left-padded uint256 equal to 64.
twoWordUint = common.Hash{31: 64}
// 24 zero bytes (the padding for a uint64 in a 32 byte word)
uint64Padding = make([]byte, 24)
// 12 zero bytes (the padding for an Ethereum address in a 32 byte word)
addressPadding = make([]byte, 12)
)
// UpdateSystemConfigWithL1Receipts filters all L1 receipts to find config updates and applies the config updates to the given sysCfg
func UpdateSystemConfigWithL1Receipts(sysCfg *eth.SystemConfig, receipts []*types.Receipt, cfg *rollup.Config) error {
var result error
......@@ -69,50 +80,94 @@ func ProcessSystemConfigUpdateLogEvent(destSysCfg *eth.SystemConfig, ev *types.L
}
// indexed 1
updateType := ev.Topics[2]
// unindexed data
// Create a reader of the unindexed data
reader := bytes.NewReader(ev.Data)
// Counter for the number of bytes read from `reader` via `readWord`
countReadBytes := 0
// Helper function to read a word from the log data reader
readWord := func() (b [32]byte) {
if _, err := reader.Read(b[:]); err != nil {
// If there is an error reading the next 32 bytes from the reader, return an empty
// 32 byte array. We always check that the number of bytes read (`countReadBytes`)
// is equal to the expected amount at the end of each switch case.
return b
}
countReadBytes += 32
return b
}
// Attempt to read unindexed data
switch updateType {
case SystemConfigUpdateBatcher:
if len(ev.Data) != 32*3 {
return fmt.Errorf("expected 32*3 bytes in batcher hash update, but got %d bytes", len(ev.Data))
// Read the pointer, it should always equal 32.
if word := readWord(); word != oneWordUint {
return fmt.Errorf("expected offset to point to length location, but got %s", word)
}
if x := common.BytesToHash(ev.Data[:32]); x != (common.Hash{31: 32}) {
return fmt.Errorf("expected offset to point to length location, but got %s", x)
// Read the length, it should also always equal 32.
if word := readWord(); word != oneWordUint {
return fmt.Errorf("expected length to be 32 bytes, but got %s", word)
}
if x := common.BytesToHash(ev.Data[32:64]); x != (common.Hash{31: 32}) {
return fmt.Errorf("expected length of 1 bytes32, but got %s", x)
// Indexing `word` directly is always safe here, it is guaranteed to be 32 bytes in length.
// Check that the batcher address is correctly zero-padded.
word := readWord()
if !bytes.Equal(word[:12], addressPadding) {
return fmt.Errorf("expected version 0 batcher hash with zero padding, but got %x", word)
}
if !bytes.Equal(ev.Data[64:64+12], make([]byte, 12)) {
return fmt.Errorf("expected version 0 batcher hash with zero padding, but got %x", ev.Data)
destSysCfg.BatcherAddr.SetBytes(word[12:])
if countReadBytes != 32*3 {
return NewCriticalError(fmt.Errorf("expected 32*3 bytes in batcher hash update, but got %d bytes", len(ev.Data)))
}
destSysCfg.BatcherAddr.SetBytes(ev.Data[64+12:])
return nil
case SystemConfigUpdateGasConfig: // left padded uint8
if len(ev.Data) != 32*4 {
return fmt.Errorf("expected 32*4 bytes in GPO params update data, but got %d", len(ev.Data))
case SystemConfigUpdateGasConfig:
// Read the pointer, it should always equal 32.
if word := readWord(); word != oneWordUint {
return fmt.Errorf("expected offset to point to length location, but got %s", word)
}
if x := common.BytesToHash(ev.Data[:32]); x != (common.Hash{31: 32}) {
return fmt.Errorf("expected offset to point to length location, but got %s", x)
// Read the length, it should always equal 64.
if word := readWord(); word != twoWordUint {
return fmt.Errorf("expected length to be 64 bytes, but got %s", word)
}
if x := common.BytesToHash(ev.Data[32:64]); x != (common.Hash{31: 64}) {
return fmt.Errorf("expected length of 2 bytes32, but got %s", x)
// Set the system config's overhead and scalar values to the values read from the log
destSysCfg.Overhead = readWord()
destSysCfg.Scalar = readWord()
if countReadBytes != 32*4 {
return NewCriticalError(fmt.Errorf("expected 32*4 bytes in GPO params update data, but got %d", len(ev.Data)))
}
copy(destSysCfg.Overhead[:], ev.Data[64:96])
copy(destSysCfg.Scalar[:], ev.Data[96:128])
return nil
case SystemConfigUpdateGasLimit:
if len(ev.Data) != 32*3 {
return fmt.Errorf("expected 32*3 bytes in gas limit update, but got %d bytes", len(ev.Data))
// Read the pointer, it should always equal 32.
if word := readWord(); word != oneWordUint {
return fmt.Errorf("expected offset to point to length location, but got %s", word)
}
if x := common.BytesToHash(ev.Data[:32]); x != (common.Hash{31: 32}) {
return fmt.Errorf("expected offset to point to length location, but got %s", x)
// Read the length, it should also always equal 32.
if word := readWord(); word != oneWordUint {
return fmt.Errorf("expected length to be 32 bytes, but got %s", word)
}
if x := common.BytesToHash(ev.Data[32:64]); x != (common.Hash{31: 32}) {
return fmt.Errorf("expected length of 1 bytes32, but got %s", x)
// Indexing `word` directly is always safe here, it is guaranteed to be 32 bytes in length.
// Check that the gas limit is correctly zero-padded.
word := readWord()
if !bytes.Equal(word[:24], uint64Padding) {
return fmt.Errorf("expected zero padding for gaslimit, but got %x", word)
}
if !bytes.Equal(ev.Data[64:64+24], make([]byte, 24)) {
return fmt.Errorf("expected zero padding for gaslimit, but got %x", ev.Data)
destSysCfg.GasLimit = binary.BigEndian.Uint64(word[24:])
if countReadBytes != 32*3 {
return NewCriticalError(fmt.Errorf("expected 32*3 bytes in gas limit update, but got %d bytes", len(ev.Data)))
}
destSysCfg.GasLimit = binary.BigEndian.Uint64(ev.Data[64+24:])
return nil
case SystemConfigUpdateUnsafeBlockSigner:
// Ignored in derivation. This configurable applies to runtime configuration outside of the derivation.
......
......@@ -89,8 +89,8 @@ func NewDriver(driverCfg *Config, cfg *rollup.Config, l2 L2Chain, l1 L1Chain, ne
findL1Origin := NewL1OriginSelector(log, cfg, l1, driverCfg.SequencerConfDepth)
verifConfDepth := NewConfDepth(driverCfg.VerifierConfDepth, l1State.L1Head, l1)
derivationPipeline := derive.NewDerivationPipeline(log, cfg, verifConfDepth, l2, metrics)
sequencer := NewSequencer(log, cfg, l1, l2, derivationPipeline, metrics)
attrBuilder := derive.NewFetchingAttributesBuilder(cfg, l1, l2)
sequencer := NewSequencer(log, cfg, l2, derivationPipeline, attrBuilder, metrics)
return &Driver{
l1State: l1State,
derivation: derivationPipeline,
......
......@@ -37,10 +37,10 @@ type Sequencer struct {
log log.Logger
config *rollup.Config
l1 Downloader
l2 derive.Engine
engineState EngineState
attrBuilder derive.AttributesBuilder
buildingOnto eth.L2BlockRef
buildingID eth.PayloadID
buildingStartTime time.Time
......@@ -48,14 +48,14 @@ type Sequencer struct {
metrics SequencerMetrics
}
func NewSequencer(log log.Logger, cfg *rollup.Config, l1 Downloader, l2 derive.Engine, engineState EngineState, metrics SequencerMetrics) *Sequencer {
func NewSequencer(log log.Logger, cfg *rollup.Config, l2 derive.Engine, engineState EngineState, attributesBuilder derive.AttributesBuilder, metrics SequencerMetrics) *Sequencer {
return &Sequencer{
log: log,
config: cfg,
l1: l1,
l2: l2,
metrics: metrics,
engineState: engineState,
attrBuilder: attributesBuilder,
}
}
......@@ -75,7 +75,7 @@ func (d *Sequencer) StartBuildingBlock(ctx context.Context, l1Origin eth.L1Block
fetchCtx, cancel := context.WithTimeout(ctx, time.Second*20)
defer cancel()
attrs, err := derive.PreparePayloadAttributes(fetchCtx, d.config, d.l1, d.l2, l2Head, l2Head.Time+d.config.BlockTime, l1Origin.ID())
attrs, err := d.attrBuilder.PreparePayloadAttributes(fetchCtx, l2Head, l1Origin.ID())
if err != nil {
return err
}
......
package metrics
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
type Factory interface {
NewCounter(opts prometheus.CounterOpts) prometheus.Counter
NewCounterVec(opts prometheus.CounterOpts, labelNames []string) *prometheus.CounterVec
NewGauge(opts prometheus.GaugeOpts) prometheus.Gauge
NewGaugeVec(opts prometheus.GaugeOpts, labelNames []string) *prometheus.GaugeVec
NewHistogram(opts prometheus.HistogramOpts) prometheus.Histogram
NewHistogramVec(opts prometheus.HistogramOpts, labelNames []string) *prometheus.HistogramVec
NewSummary(opts prometheus.SummaryOpts) prometheus.Summary
NewSummaryVec(opts prometheus.SummaryOpts, labelNames []string) *prometheus.SummaryVec
Document() []DocumentedMetric
}
type DocumentedMetric struct {
Type string `json:"type"`
Name string `json:"name"`
Help string `json:"help"`
Labels []string `json:"labels"`
}
type documentor struct {
metrics []DocumentedMetric
factory promauto.Factory
}
func With(registry *prometheus.Registry) Factory {
return &documentor{
factory: promauto.With(registry),
}
}
func (d *documentor) NewCounter(opts prometheus.CounterOpts) prometheus.Counter {
d.metrics = append(d.metrics, DocumentedMetric{
Type: "counter",
Name: fullName(opts.Namespace, opts.Subsystem, opts.Name),
Help: opts.Help,
})
return d.factory.NewCounter(opts)
}
func (d *documentor) NewCounterVec(opts prometheus.CounterOpts, labelNames []string) *prometheus.CounterVec {
d.metrics = append(d.metrics, DocumentedMetric{
Type: "counter",
Name: fullName(opts.Namespace, opts.Subsystem, opts.Name),
Help: opts.Help,
Labels: labelNames,
})
return d.factory.NewCounterVec(opts, labelNames)
}
func (d *documentor) NewGauge(opts prometheus.GaugeOpts) prometheus.Gauge {
d.metrics = append(d.metrics, DocumentedMetric{
Type: "gauge",
Name: fullName(opts.Namespace, opts.Subsystem, opts.Name),
Help: opts.Help,
})
return d.factory.NewGauge(opts)
}
func (d *documentor) NewGaugeVec(opts prometheus.GaugeOpts, labelNames []string) *prometheus.GaugeVec {
d.metrics = append(d.metrics, DocumentedMetric{
Type: "gauge",
Name: fullName(opts.Namespace, opts.Subsystem, opts.Name),
Help: opts.Help,
Labels: labelNames,
})
return d.factory.NewGaugeVec(opts, labelNames)
}
func (d *documentor) NewHistogram(opts prometheus.HistogramOpts) prometheus.Histogram {
d.metrics = append(d.metrics, DocumentedMetric{
Type: "histogram",
Name: fullName(opts.Namespace, opts.Subsystem, opts.Name),
Help: opts.Help,
})
return d.factory.NewHistogram(opts)
}
func (d *documentor) NewHistogramVec(opts prometheus.HistogramOpts, labelNames []string) *prometheus.HistogramVec {
d.metrics = append(d.metrics, DocumentedMetric{
Type: "histogram",
Name: fullName(opts.Namespace, opts.Subsystem, opts.Name),
Help: opts.Help,
Labels: labelNames,
})
return d.factory.NewHistogramVec(opts, labelNames)
}
func (d *documentor) NewSummary(opts prometheus.SummaryOpts) prometheus.Summary {
d.metrics = append(d.metrics, DocumentedMetric{
Type: "summary",
Name: fullName(opts.Namespace, opts.Subsystem, opts.Name),
Help: opts.Help,
})
return d.factory.NewSummary(opts)
}
func (d *documentor) NewSummaryVec(opts prometheus.SummaryOpts, labelNames []string) *prometheus.SummaryVec {
d.metrics = append(d.metrics, DocumentedMetric{
Type: "summary",
Name: fullName(opts.Namespace, opts.Subsystem, opts.Name),
Help: opts.Help,
Labels: labelNames,
})
return d.factory.NewSummaryVec(opts, labelNames)
}
func (d *documentor) Document() []DocumentedMetric {
return d.metrics
}
func fullName(ns, subsystem, name string) string {
out := ns
if subsystem != "" {
out += "_" + subsystem
}
return out + "_" + name
}
# @eth-optimism/ci-builder
## 0.4.0
### Minor Changes
- 05cc935b2: Bump foundry to 2ff99025abade470a795724c10648c800a41025e
## 0.3.8
### Patch Changes
......
......@@ -16,7 +16,7 @@ WORKDIR /opt/foundry
# Only diff from upstream docker image is this clone instead
# of COPY. We select a specific commit to use.
RUN git clone https://github.com/foundry-rs/foundry.git . \
&& git checkout c06b53287dc23c4e5b1b3e57c937a90114bbe166
&& git checkout 2ff99025abade470a795724c10648c800a41025e
RUN source $HOME/.profile && \
cargo build --release && \
......
{
"name": "@eth-optimism/ci-builder",
"version": "0.3.8",
"version": "0.4.0",
"scripts": {},
"license": "MIT",
"dependencies": {}
......
# @eth-optimism/foundry
## 0.2.0
### Minor Changes
- 05cc935b2: Bump foundry to 2ff99025abade470a795724c10648c800a41025e
## 0.1.3
### Patch Changes
......
......@@ -9,7 +9,7 @@ WORKDIR /opt/foundry
# Only diff from upstream docker image is this clone instead
# of COPY. We select a specific commit to use.
RUN git clone https://github.com/foundry-rs/foundry.git . \
&& git checkout f540aa9ebde88dce720140b332412089c2ee85b6
&& git checkout 2ff99025abade470a795724c10648c800a41025e
RUN source $HOME/.profile && cargo build --release \
&& strip /opt/foundry/target/release/forge \
......
{
"name": "@eth-optimism/foundry",
"version": "0.1.3",
"version": "0.2.0",
"scripts": {},
"license": "MIT",
"dependencies": {}
......
......@@ -17,7 +17,7 @@ Bytes_Test:test_toNibbles_expectedResult128Bytes_works() (gas: 129885)
Bytes_Test:test_toNibbles_expectedResult5Bytes_works() (gas: 6132)
Bytes_Test:test_toNibbles_zeroLengthInput_works() (gas: 966)
CrossDomainMessenger_BaseGas_Test:test_baseGas_succeeds() (gas: 20120)
CrossDomainOwnableThroughPortal_Test:test_depositTransaction_crossDomainOwner_succeeds() (gas: 61876)
CrossDomainOwnableThroughPortal_Test:test_depositTransaction_crossDomainOwner_succeeds() (gas: 61882)
CrossDomainOwnable_Test:test_onlyOwner_notOwner_reverts() (gas: 10530)
CrossDomainOwnable_Test:test_onlyOwner_succeeds() (gas: 34861)
CrossDomainOwnable2_Test:test_onlyOwner_notMessenger_reverts() (gas: 8416)
......@@ -368,14 +368,14 @@ RLPWriter_Test:test_writeUint_smallint3_succeeds() (gas: 7311)
RLPWriter_Test:test_writeUint_smallint4_succeeds() (gas: 7312)
RLPWriter_Test:test_writeUint_smallint_succeeds() (gas: 7290)
RLPWriter_Test:test_writeUint_zero_succeeds() (gas: 7802)
ResourceMetering_Test:test_meter_initialResourceParams_succeeds() (gas: 8965)
ResourceMetering_Test:test_meter_updateNoGasDelta_succeeds() (gas: 2008101)
ResourceMetering_Test:test_meter_updateOneEmptyBlock_succeeds() (gas: 18152)
ResourceMetering_Test:test_meter_updateParamsNoChange_succeeds() (gas: 13911)
ResourceMetering_Test:test_meter_updateTenEmptyBlocks_succeeds() (gas: 20900)
ResourceMetering_Test:test_meter_updateTwoEmptyBlocks_succeeds() (gas: 20923)
ResourceMetering_Test:test_meter_useMax_succeeds() (gas: 8017204)
ResourceMetering_Test:test_meter_useMoreThanMax_reverts() (gas: 16023)
ResourceMetering_Test:test_meter_initialResourceParams_succeeds() (gas: 8983)
ResourceMetering_Test:test_meter_updateNoGasDelta_succeeds() (gas: 2008119)
ResourceMetering_Test:test_meter_updateOneEmptyBlock_succeeds() (gas: 18148)
ResourceMetering_Test:test_meter_updateParamsNoChange_succeeds() (gas: 13859)
ResourceMetering_Test:test_meter_updateTenEmptyBlocks_succeeds() (gas: 20918)
ResourceMetering_Test:test_meter_updateTwoEmptyBlocks_succeeds() (gas: 20941)
ResourceMetering_Test:test_meter_useMax_succeeds() (gas: 8017151)
ResourceMetering_Test:test_meter_useMoreThanMax_reverts() (gas: 16045)
Semver_Test:test_behindProxy_succeeds() (gas: 506725)
Semver_Test:test_version_succeeds() (gas: 9396)
SequencerFeeVault_Test:test_constructor_succeeds() (gas: 5504)
......
This diff is collapsed.
......@@ -57,7 +57,7 @@ We work on this repository with a combination of [Hardhat](https://hardhat.org)
1. Install Foundry by following [the instructions located here](https://getfoundry.sh/).
A specific version must be used.
```shell
foundryup -C c06b53287dc23c4e5b1b3e57c937a90114bbe166
foundryup -C 2ff99025abade470a795724c10648c800a41025e
```
2. Install node modules with yarn (v1) and Node.js (16+):
......
......@@ -188,6 +188,23 @@ contract SystemDictator is OwnableUpgradeable {
config.proxyAddressConfig.l1StandardBridgeProxy,
ProxyAdmin.ProxyType.CHUGSPLASH
);
// Upgrade and initialize the SystemConfig so the Sequencer can start up.
config.globalConfig.proxyAdmin.upgradeAndCall(
payable(config.proxyAddressConfig.systemConfigProxy),
address(config.implementationAddressConfig.systemConfigImpl),
abi.encodeCall(
SystemConfig.initialize,
(
config.systemConfigConfig.owner,
config.systemConfigConfig.overhead,
config.systemConfigConfig.scalar,
config.systemConfigConfig.batcherHash,
config.systemConfigConfig.gasLimit,
config.systemConfigConfig.unsafeBlockSigner
)
)
);
}
/**
......@@ -343,23 +360,6 @@ contract SystemDictator is OwnableUpgradeable {
address(config.implementationAddressConfig.l1ERC721BridgeImpl)
);
// Upgrade and initialize the SystemConfig.
config.globalConfig.proxyAdmin.upgradeAndCall(
payable(config.proxyAddressConfig.systemConfigProxy),
address(config.implementationAddressConfig.systemConfigImpl),
abi.encodeCall(
SystemConfig.initialize,
(
config.systemConfigConfig.owner,
config.systemConfigConfig.overhead,
config.systemConfigConfig.scalar,
config.systemConfigConfig.batcherHash,
config.systemConfigConfig.gasLimit,
config.systemConfigConfig.unsafeBlockSigner
)
)
);
// Pause the L1CrossDomainMessenger, chance to check that everything is OK.
L1CrossDomainMessenger(config.proxyAddressConfig.l1CrossDomainMessengerProxy).pause();
}
......
......@@ -108,4 +108,18 @@ contract ResourceMetering_Test is CommonTest {
vm.expectRevert("ResourceMetering: cannot buy more gas than available gas limit");
meter.use(target * elasticity + 1);
}
// Demonstrates that the resource metering arithmetic can tolerate very large gaps between
// deposits.
function testFuzz_meter_largeBlockDiff_succeeds(uint64 _amount, uint256 _blockDiff) external {
// This test fails if the following line is commented out.
// At 12 seconds per block, this number is effectively unreachable.
vm.assume(_blockDiff < 433576281058164217753225238677900874458691);
uint64 target = uint64(uint256(meter.TARGET_RESOURCE_LIMIT()));
uint64 elasticity = uint64(uint256(meter.ELASTICITY_MULTIPLIER()));
vm.assume(_amount < target * elasticity);
vm.roll(initialBlockNum + _blockDiff);
meter.use(_amount);
}
}
......@@ -28,6 +28,7 @@ const deployFn: DeployFunction = async (hre) => {
L1StandardBridgeProxyWithSigner,
L1ERC721BridgeProxy,
L1ERC721BridgeProxyWithSigner,
SystemConfigProxy,
] = await getContractsFromArtifacts(hre, [
{
name: 'SystemDictatorProxy',
......@@ -61,6 +62,11 @@ const deployFn: DeployFunction = async (hre) => {
name: 'L1ERC721BridgeProxy',
signerOrProvider: deployer,
},
{
name: 'SystemConfigProxy',
iface: 'SystemConfig',
signerOrProvider: deployer,
},
])
// If we have the key for the controller then we don't need to wait for external txns.
......@@ -251,6 +257,36 @@ const deployFn: DeployFunction = async (hre) => {
getDeploymentAddress(hre, 'Proxy__OVM_L1StandardBridge')
)) === 1
)
// Check the SystemConfig was initialized properly.
await assertContractVariable(
SystemConfigProxy,
'owner',
hre.deployConfig.finalSystemOwner
)
await assertContractVariable(
SystemConfigProxy,
'overhead',
hre.deployConfig.gasPriceOracleOverhead
)
await assertContractVariable(
SystemConfigProxy,
'scalar',
hre.deployConfig.gasPriceOracleScalar
)
await assertContractVariable(
SystemConfigProxy,
'batcherHash',
ethers.utils.hexZeroPad(
hre.deployConfig.batchSenderAddress.toLowerCase(),
32
)
)
await assertContractVariable(
SystemConfigProxy,
'gasLimit',
hre.deployConfig.l2GenesisBlockGasLimit
)
},
})
......
......@@ -30,7 +30,6 @@ const deployFn: DeployFunction = async (hre) => {
OptimismPortal,
OptimismMintableERC20Factory,
L1ERC721Bridge,
SystemConfigProxy,
] = await getContractsFromArtifacts(hre, [
{
name: 'SystemDictatorProxy',
......@@ -78,11 +77,6 @@ const deployFn: DeployFunction = async (hre) => {
iface: 'L1ERC721Bridge',
signerOrProvider: deployer,
},
{
name: 'SystemConfigProxy',
iface: 'SystemConfig',
signerOrProvider: deployer,
},
])
// If we have the key for the controller then we don't need to wait for external txns.
......@@ -286,40 +280,6 @@ const deployFn: DeployFunction = async (hre) => {
'messenger',
L1CrossDomainMessenger.address
)
// Check the SystemConfig was initialized properly.
await assertContractVariable(
SystemConfigProxy,
'owner',
hre.deployConfig.finalSystemOwner
)
await assertContractVariable(
SystemConfigProxy,
'overhead',
hre.deployConfig.gasPriceOracleOverhead
)
await assertContractVariable(
SystemConfigProxy,
'scalar',
hre.deployConfig.gasPriceOracleScalar
)
await assertContractVariable(
SystemConfigProxy,
'batcherHash',
ethers.utils.hexZeroPad(
hre.deployConfig.batchSenderAddress.toLowerCase(),
32
)
)
await assertContractVariable(
SystemConfigProxy,
'gasLimit',
hre.deployConfig.l2GenesisBlockGasLimit
)
},
})
......
......@@ -29,7 +29,7 @@
"coverage:lcov": "yarn build:differential && yarn build:fuzz && forge coverage --report lcov",
"gas-snapshot": "yarn build:differential && yarn build:fuzz && forge snapshot --no-match-test 'testDiff|testFuzz|invariant'",
"storage-snapshot": "./scripts/storage-snapshot.sh",
"validate-spacers": "hardhat validate-spacers",
"validate-spacers": "hardhat compile && hardhat validate-spacers",
"slither": "./scripts/slither.sh",
"clean": "rm -rf ./dist ./artifacts ./forge-artifacts ./cache ./tsconfig.tsbuildinfo ./tsconfig.build.tsbuildinfo ./src/contract-artifacts.ts ./test-case-generator/fuzz",
"lint:ts:check": "eslint . --max-warnings=0",
......
......@@ -56,7 +56,7 @@ interface RequiredDeployConfig {
sequencerWindowSize: number
/**
* Number of seconds (w.r.t. L1 time) that a frame can be valid when included in L1.
* Number of L1 blocks that a frame stays valid when included in L1.
*/
channelTimeout: number
......
......@@ -48,26 +48,31 @@ task('wait-for-final-batch', 'Waits for the final batch to be submitted')
const wait = async (contract: Contract) => {
let height = await l2Provider.getBlockNumber()
let totalElements = await contract.getTotalElements()
// The genesis block was not batch submitted so subtract 1 from the height
// when comparing with the total elements
while (totalElements !== height - 1) {
console.log(` - height: ${height}`)
console.log(` - totalElements: ${totalElements}`)
while (totalElements.toNumber() !== height) {
console.log('Total elements does not match')
console.log(` - real height: ${height}`)
console.log(` - height: ${height - 1}`)
console.log(` - height: ${height}`)
console.log(` - totalElements: ${totalElements}`)
console.log(
`Waiting for ${height - totalElements} elements to be submitted`
)
totalElements = await contract.getTotalElements()
height = await l2Provider.getBlockNumber()
await sleep(2 * 1000)
await sleep(5 * 1000)
}
}
console.log('Waiting for the CanonicalTransactionChain...')
await wait(CanonicalTransactionChain)
console.log('All transaction batches have been submitted')
console.log()
console.log('Waiting for the StateCommitmentChain...')
await wait(StateCommitmentChain)
console.log('All state root batches have been submitted')
console.log()
console.log('All batches have been submitted')
})
......@@ -22,6 +22,7 @@ task('wait-for-final-deposit', 'Waits for the final deposit to be ingested')
const l1Provider = new hre.ethers.providers.StaticJsonRpcProvider(
args.l1RpcUrl
)
const l2Provider = new hre.ethers.providers.StaticJsonRpcProvider(
args.l2RpcUrl
)
......@@ -63,6 +64,9 @@ task('wait-for-final-deposit', 'Waits for the final deposit to be ingested')
console.log(`DTL shutoff block ${dtlShutoffBlock.toString()}`)
let pending = await CanonicalTransactionChain.getNumPendingQueueElements()
console.log(`${pending} deposits must be batch submitted`)
// Now query the number of queue elements in the CTC
const queueLength = await CanonicalTransactionChain.getQueueLength()
console.log(`Total number of deposits: ${queueLength}`)
......@@ -80,11 +84,10 @@ task('wait-for-final-deposit', 'Waits for the final deposit to be ingested')
if (tx.queueOrigin === 'l1') {
const queueIndex = BigNumber.from(tx.queueIndex).toNumber()
if (queueIndex === queueLength) {
if (queueIndex === queueLength - 1) {
break
}
if (queueIndex < queueLength) {
console.log()
throw new Error(
`Missed the final deposit. queueIndex ${queueIndex}, queueLength ${queueLength}`
)
......@@ -94,4 +97,6 @@ task('wait-for-final-deposit', 'Waits for the final deposit to be ingested')
}
console.log('Final deposit has been ingested by l2geth')
pending = await CanonicalTransactionChain.getNumPendingQueueElements()
console.log(`${pending} deposits must be batch submitted`)
})
......@@ -3,6 +3,10 @@
- **Chain ID**: 420
- **Public RPC**: https://goerli.optimism.io
- **Block Explorer**: https://goerli-optimism.etherscan.io/
**Note:** This list is out of date, now that Goerli is on bedrock.
[The valid list is here](https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts-bedrock/deployments/goerli).
## Layer 1 Contracts
<table>
<tr>
......
# data transport layer
## 0.5.51
### Patch Changes
- 4396e187d: Fixes a bug in the DTL that would cause it to not be able to sync beyond the deposit shutoff block.
## 0.5.50
### Patch Changes
......
{
"private": true,
"name": "@eth-optimism/data-transport-layer",
"version": "0.5.50",
"version": "0.5.51",
"description": "[Optimism] Service for shuttling data from L1 into L2",
"main": "dist/index",
"types": "dist/index",
......
......@@ -268,16 +268,16 @@ export class L1IngestionService extends BaseService<L1IngestionServiceOptions> {
)
}
// I prefer to do this in serial to avoid non-determinism. We could have a discussion about
// using Promise.all if necessary, but I don't see a good reason to do so unless parsing is
// really, really slow for all event types.
await this._syncEvents(
'CanonicalTransactionChain',
'TransactionEnqueued',
highestSyncedL1Block,
depositTargetL1Block,
handleEventsTransactionEnqueued
)
// We should not sync TransactionEnqueued events beyond the deposit shutoff block.
if (depositTargetL1Block >= highestSyncedL1Block) {
await this._syncEvents(
'CanonicalTransactionChain',
'TransactionEnqueued',
highestSyncedL1Block,
depositTargetL1Block,
handleEventsTransactionEnqueued
)
}
await this._syncEvents(
'CanonicalTransactionChain',
......
......@@ -4,10 +4,10 @@
The batch submitter, also referred to as the batcher, is the entity submitting the L2 sequencer data to L1,
to make it available for verifiers.
[derivation-spec]: ./derivation.md
[derivation spec]: derivation.md
The format of the data transactions is defined in the [derivation spec]: the data is constructed from L2 blocks
in the reverse order as it is derived from data into L2 blocks.
The format of the data transactions is defined in the [derivation spec]:
the data is constructed from L2 blocks in the reverse order as it is derived from data into L2 blocks.
The timing, operation and transaction signing is implementation-specific: any data can be submitted at any time,
but only the data that matches the [derivation spec] rules will be valid from the verifier perspective.
......
This diff is collapsed.
......@@ -479,7 +479,7 @@ transaction must also be signed by a recognized batch submitter account.
[channel-timeout]: glossary.md#channel-timeout
The channel timeout is a duration (in seconds) during which [channel frames][channel-frame may land on L1 within
The channel timeout is a duration (in L1 blocks) during which [channel frames][channel-frame] may land on L1 within
[batcher transactions][batcher-transaction].
The acceptable time range for the frames of a [channel][channel] is `[channel_id.timestamp, channel_id.timestamp +
......
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