Commit 03c618cf authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into jg/cleanup_proposer

parents 705c7ef8 6e1f4db2
---
'@eth-optimism/l2geth': patch
---
Use default cas gap of 25 million
---
'@eth-optimism/common-ts': patch
---
Fix unknown option error in base service v2
---
'@eth-optimism/contracts-periphery': patch
---
Update the attestation station impl to 1.1.0
......@@ -35,6 +35,7 @@
/.github @ethereum-optimism/infra-reviewers
/ops @ethereum-optimism/infra-reviewers
/ops-bedrock @ethereum-optimism/infra-reviewers
/op-signer @ethereum-optimism/infra-reviewers
# Misc
/proxyd @ethereum-optimism/infra-reviewers
......
......@@ -17,6 +17,7 @@ use (
./op-node
./op-proposer
./op-service
./op-signer
./op-wheel
./packages/contracts-bedrock/test-case-generator
./proxyd
......
......@@ -516,6 +516,7 @@ var (
}
RPCGlobalGasCap = cli.Uint64Flag{
Name: "rpc.gascap",
Value: eth.DefaultConfig.RPCGasCap.Uint64(),
Usage: "Sets a cap on gas that can be used in eth_call/estimateGas",
}
RPCGlobalEVMTimeoutFlag = &cli.DurationFlag{
......
......@@ -58,6 +58,7 @@ var DefaultConfig = Config{
Recommit: 3 * time.Second,
},
TxPool: core.DefaultTxPoolConfig,
RPCGasCap: new(big.Int).SetUint64(25_000_000),
RPCEVMTimeout: 5 * time.Second,
GPO: gasprice.Config{
Blocks: 20,
......
......@@ -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,
......
This diff is collapsed.
This diff is collapsed.
......@@ -170,3 +170,17 @@ func (w *LegacyWithdrawal) Value() (*big.Int, error) {
return value, nil
}
// CrossDomainMessage turns the LegacyWithdrawal into
// a CrossDomainMessage. LegacyWithdrawals do not have
// the concept of value or gaslimit, so set them to 0.
func (w *LegacyWithdrawal) CrossDomainMessage() *CrossDomainMessage {
return &CrossDomainMessage{
Nonce: w.Nonce,
Sender: w.Sender,
Target: w.Target,
Value: new(big.Int),
GasLimit: new(big.Int),
Data: []byte(w.Data),
}
}
......@@ -119,6 +119,11 @@ func PostCheckMigratedDB(
Preimages: true,
})
prevDB, err := state.New(prevHeader.Root, underlyingDB, nil)
if err != nil {
return fmt.Errorf("cannot open historical StateDB: %w", err)
}
db, err := state.New(header.Root, underlyingDB, nil)
if err != nil {
return fmt.Errorf("cannot open StateDB: %w", err)
......@@ -134,7 +139,7 @@ func PostCheckMigratedDB(
}
log.Info("checked untouchables")
if err := PostCheckPredeploys(db); err != nil {
if err := PostCheckPredeploys(prevDB, db); err != nil {
return err
}
log.Info("checked predeploys")
......@@ -209,13 +214,13 @@ func PostCheckUntouchables(udb state.Database, currDB *state.StateDB, prevRoot c
// PostCheckPredeploys will check that there is code at each predeploy
// address
func PostCheckPredeploys(db *state.StateDB) error {
func PostCheckPredeploys(prevDB, currDB *state.StateDB) error {
for i := uint64(0); i <= 2048; i++ {
// Compute the predeploy address
bigAddr := new(big.Int).Or(bigL2PredeployNamespace, new(big.Int).SetUint64(i))
addr := common.BigToAddress(bigAddr)
// Get the code for the predeploy
code := db.GetCode(addr)
code := currDB.GetCode(addr)
// There must be code for the predeploy
if len(code) == 0 {
return fmt.Errorf("no code found at predeploy %s", addr)
......@@ -227,11 +232,23 @@ func PostCheckPredeploys(db *state.StateDB) error {
}
// There must be an admin
admin := db.GetState(addr, AdminSlot)
admin := currDB.GetState(addr, AdminSlot)
adminAddr := common.BytesToAddress(admin.Bytes())
if addr != predeploys.ProxyAdminAddr && addr != predeploys.GovernanceTokenAddr && adminAddr != predeploys.ProxyAdminAddr {
return fmt.Errorf("expected admin for %s to be %s but got %s", addr, predeploys.ProxyAdminAddr, adminAddr)
}
// Balances and nonces should match legacy
oldNonce := prevDB.GetNonce(addr)
oldBalance := prevDB.GetBalance(addr)
newNonce := currDB.GetNonce(addr)
newBalance := currDB.GetBalance(addr)
if oldNonce != newNonce {
return fmt.Errorf("expected nonce for %s to be %d but got %d", addr, oldNonce, newNonce)
}
if oldBalance.Cmp(newBalance) != 0 {
return fmt.Errorf("expected balance for %s to be %d but got %d", addr, oldBalance, newBalance)
}
}
// For each predeploy, check that we've set the implementation correctly when
......@@ -248,7 +265,7 @@ func PostCheckPredeploys(db *state.StateDB) error {
}
if *proxyAddr == predeploys.ProxyAdminAddr {
implCode := db.GetCode(*proxyAddr)
implCode := currDB.GetCode(*proxyAddr)
if len(implCode) == 0 {
return errors.New("no code found at proxy admin")
}
......@@ -260,12 +277,12 @@ func PostCheckPredeploys(db *state.StateDB) error {
return fmt.Errorf("error converting to code namespace: %w", err)
}
implCode := db.GetCode(expImplAddr)
implCode := currDB.GetCode(expImplAddr)
if len(implCode) == 0 {
return fmt.Errorf("no code found at predeploy impl %s", *proxyAddr)
}
impl := db.GetState(*proxyAddr, ImplementationSlot)
impl := currDB.GetState(*proxyAddr, ImplementationSlot)
actImplAddr := common.BytesToAddress(impl.Bytes())
if expImplAddr != actImplAddr {
return fmt.Errorf("expected implementation for %s to be at %s, but got %s", *proxyAddr, expImplAddr, actImplAddr)
......
......@@ -92,7 +92,17 @@ func WipePredeployStorage(db vm.StateDB) error {
}
log.Info("wiping storage", "name", name, "address", *addr)
// We need to make sure that we preserve nonces.
oldNonce := db.GetNonce(*addr)
oldBalance := db.GetBalance(*addr)
db.CreateAccount(*addr)
if oldNonce > 0 {
db.SetNonce(*addr, oldNonce)
}
if oldBalance.Cmp(common.Big0) != 0 {
db.AddBalance(*addr, oldBalance)
}
}
return nil
......
......@@ -113,6 +113,14 @@ func (s *l2VerifierBackend) ResetDerivationPipeline(ctx context.Context) error {
return nil
}
func (s *l2VerifierBackend) StartSequencer(ctx context.Context, blockHash common.Hash) error {
return nil
}
func (s *l2VerifierBackend) StopSequencer(ctx context.Context) (common.Hash, error) {
return common.Hash{}, errors.New("stopping the L2Verifier sequencer is not supported")
}
func (s *L2Verifier) L2Finalized() eth.L2BlockRef {
return s.derivation.Finalized()
}
......
......@@ -1129,6 +1129,58 @@ func TestFees(t *testing.T) {
require.Equal(t, balanceDiff, totalFee, "balances should add up")
}
func TestStopStartSequencer(t *testing.T) {
parallel(t)
if !verboseGethNodes {
log.Root().SetHandler(log.DiscardHandler())
}
cfg := DefaultSystemConfig(t)
sys, err := cfg.Start()
require.Nil(t, err, "Error starting up system")
defer sys.Close()
l2Seq := sys.Clients["sequencer"]
rollupNode := sys.RollupNodes["sequencer"]
nodeRPC, err := rpc.DialContext(context.Background(), rollupNode.HTTPEndpoint())
require.Nil(t, err, "Error dialing node")
blockBefore := latestBlock(t, l2Seq)
time.Sleep(time.Duration(cfg.DeployConfig.L2BlockTime+1) * time.Second)
blockAfter := latestBlock(t, l2Seq)
require.Greaterf(t, blockAfter, blockBefore, "Chain did not advance")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
blockHash := common.Hash{}
err = nodeRPC.CallContext(ctx, &blockHash, "admin_stopSequencer")
require.Nil(t, err, "Error stopping sequencer")
blockBefore = latestBlock(t, l2Seq)
time.Sleep(time.Duration(cfg.DeployConfig.L2BlockTime+1) * time.Second)
blockAfter = latestBlock(t, l2Seq)
require.Equal(t, blockAfter, blockBefore, "Chain advanced after stopping sequencer")
ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
err = nodeRPC.CallContext(ctx, nil, "admin_startSequencer", blockHash)
require.Nil(t, err, "Error starting sequencer")
blockBefore = latestBlock(t, l2Seq)
time.Sleep(time.Duration(cfg.DeployConfig.L2BlockTime+1) * time.Second)
blockAfter = latestBlock(t, l2Seq)
require.Greater(t, blockAfter, blockBefore, "Chain did not advance after starting sequencer")
}
func safeAddBig(a *big.Int, b *big.Int) *big.Int {
return new(big.Int).Add(a, b)
}
func latestBlock(t *testing.T, client *ethclient.Client) uint64 {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
blockAfter, err := client.BlockNumber(ctx)
require.Nil(t, err, "Error getting latest block")
return blockAfter
}
......@@ -3,16 +3,17 @@ package main
import (
"context"
"net"
"net/http"
"os"
"os/signal"
"strconv"
"syscall"
"time"
"github.com/ethereum-optimism/optimism/op-node/cmd/doc"
"github.com/urfave/cli"
"github.com/ethereum/go-ethereum/log"
opnode "github.com/ethereum-optimism/optimism/op-node"
"github.com/ethereum-optimism/optimism/op-node/cmd/genesis"
"github.com/ethereum-optimism/optimism/op-node/cmd/p2p"
......@@ -21,7 +22,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/metrics"
"github.com/ethereum-optimism/optimism/op-node/node"
"github.com/ethereum-optimism/optimism/op-node/version"
"github.com/ethereum/go-ethereum/log"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof"
)
var (
......@@ -145,24 +146,14 @@ func RollupNodeMain(ctx *cli.Context) error {
}
if cfg.Pprof.Enabled {
var srv http.Server
srv.Addr = net.JoinHostPort(cfg.Pprof.ListenAddr, cfg.Pprof.ListenPort)
// Start pprof server + register it's shutdown
pprofCtx, pprofCancel := context.WithCancel(context.Background())
go func() {
log.Info("pprof server started", "addr", srv.Addr)
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
log.Error("error in pprof server", "err", err)
} else {
log.Info("pprof server shutting down")
log.Info("pprof server started", "addr", net.JoinHostPort(cfg.Pprof.ListenAddr, strconv.Itoa(cfg.Pprof.ListenPort)))
if err := oppprof.ListenAndServe(pprofCtx, cfg.Pprof.ListenAddr, cfg.Pprof.ListenPort); err != nil {
log.Error("error starting pprof", "err", err)
}
}()
defer func() {
shutCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
err := srv.Shutdown(shutCtx)
log.Info("pprof server shut down", "err", err)
}()
defer pprofCancel()
}
interruptChannel := make(chan os.Signal, 1)
......
......@@ -94,6 +94,11 @@ var (
Usage: "Enable sequencing of new L2 blocks. A separate batch submitter has to be deployed to publish the data for verifiers.",
EnvVar: prefixEnvVar("SEQUENCER_ENABLED"),
}
SequencerStoppedFlag = cli.BoolFlag{
Name: "sequencer.stopped",
Usage: "Initialize the sequencer in a stopped state. The sequencer can be started using the admin_startSequencer RPC",
EnvVar: prefixEnvVar("SEQUENCER_STOPPED"),
}
SequencerL1Confs = cli.Uint64Flag{
Name: "sequencer.l1-confs",
Usage: "Number of L1 blocks to keep distance from the L1 head as a sequencer for picking an L1 origin.",
......@@ -197,6 +202,7 @@ var optionalFlags = append([]cli.Flag{
L2EngineJWTSecret,
VerifierL1Confs,
SequencerEnabledFlag,
SequencerStoppedFlag,
SequencerL1Confs,
L1EpochPollIntervalFlag,
LogLevelFlag,
......
......@@ -26,6 +26,8 @@ type driverClient interface {
SyncStatus(ctx context.Context) (*eth.SyncStatus, error)
BlockRefWithStatus(ctx context.Context, num uint64) (eth.L2BlockRef, *eth.SyncStatus, error)
ResetDerivationPipeline(context.Context) error
StartSequencer(ctx context.Context, blockHash common.Hash) error
StopSequencer(context.Context) (common.Hash, error)
}
type rpcMetrics interface {
......@@ -51,6 +53,18 @@ func (n *adminAPI) ResetDerivationPipeline(ctx context.Context) error {
return n.dr.ResetDerivationPipeline(ctx)
}
func (n *adminAPI) StartSequencer(ctx context.Context, blockHash common.Hash) error {
recordDur := n.m.RecordRPCServerRequest("admin_startSequencer")
defer recordDur()
return n.dr.StartSequencer(ctx, blockHash)
}
func (n *adminAPI) StopSequencer(ctx context.Context) (common.Hash, error) {
recordDur := n.m.RecordRPCServerRequest("admin_stopSequencer")
defer recordDur()
return n.dr.StopSequencer(ctx)
}
type nodeAPI struct {
config *rollup.Config
client l2EthClient
......
......@@ -9,6 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/p2p"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/rollup/driver"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof"
)
type Config struct {
......@@ -29,7 +30,7 @@ type Config struct {
Metrics MetricsConfig
Pprof PprofConfig
Pprof oppprof.CLIConfig
// Used to poll the L1 for new finalized or safe blocks
L1EpochPollInterval time.Duration
......@@ -67,16 +68,6 @@ func (m MetricsConfig) Check() error {
return nil
}
type PprofConfig struct {
Enabled bool
ListenAddr string
ListenPort string
}
func (p PprofConfig) Check() error {
return nil
}
type HeartbeatConfig struct {
Enabled bool
Moniker string
......
......@@ -218,3 +218,11 @@ func (c *mockDriverClient) SyncStatus(ctx context.Context) (*eth.SyncStatus, err
func (c *mockDriverClient) ResetDerivationPipeline(ctx context.Context) error {
return c.Mock.MethodCalled("ResetDerivationPipeline").Get(0).(error)
}
func (c *mockDriverClient) StartSequencer(ctx context.Context, blockHash common.Hash) error {
return c.Mock.MethodCalled("StartSequencer").Get(0).(error)
}
func (c *mockDriverClient) StopSequencer(ctx context.Context) (common.Hash, error) {
return c.Mock.MethodCalled("StopSequencer").Get(0).(common.Hash), nil
}
......@@ -13,4 +13,7 @@ type Config struct {
// SequencerEnabled is true when the driver should sequence new blocks.
SequencerEnabled bool `json:"sequencer_enabled"`
// SequencerStopped is false when the driver should sequence new blocks.
SequencerStopped bool `json:"sequencer_stopped"`
}
......@@ -97,6 +97,8 @@ func NewDriver(driverCfg *Config, cfg *rollup.Config, l2 L2Chain, l1 L1Chain, ne
idleDerivation: false,
stateReq: make(chan chan struct{}),
forceReset: make(chan chan struct{}, 10),
startSequencer: make(chan hashAndErrorChannel, 10),
stopSequencer: make(chan chan hashAndError, 10),
config: cfg,
driverConfig: driverCfg,
done: make(chan struct{}),
......
package driver
import (
"bytes"
"context"
"encoding/json"
"errors"
......@@ -9,6 +10,7 @@ import (
gosync "sync"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-node/eth"
......@@ -42,6 +44,14 @@ type Driver struct {
// It tells the caller that the reset occurred by closing the passed in channel.
forceReset chan chan struct{}
// Upon receiving a hash in this channel, the sequencer is started at the given hash.
// It tells the caller that the sequencer started by closing the passed in channel (or returning an error).
startSequencer chan hashAndErrorChannel
// Upon receiving a channel in this channel, the sequencer is stopped.
// It tells the caller that the sequencer stopped by returning the latest sequenced L2 block hash.
stopSequencer chan chan hashAndError
// Rollup config: rollup chain configuration
config *rollup.Config
......@@ -274,7 +284,7 @@ func (s *Driver) eventLoop() {
for {
// If we are sequencing, update the trigger for the next sequencer action.
// This may adjust at any time based on fork-choice changes or previous errors.
if s.driverConfig.SequencerEnabled {
if s.driverConfig.SequencerEnabled && !s.driverConfig.SequencerStopped {
// update sequencer time if the head changed
if sequencingPlannedOnto != s.derivation.UnsafeL2Head().ID() {
planSequencerAction()
......@@ -367,6 +377,26 @@ func (s *Driver) eventLoop() {
s.derivation.Reset()
s.metrics.RecordPipelineReset()
close(respCh)
case resp := <-s.startSequencer:
unsafeHead := s.derivation.UnsafeL2Head().Hash
if !s.driverConfig.SequencerStopped {
resp.err <- errors.New("sequencer already running")
} else if !bytes.Equal(unsafeHead[:], resp.hash[:]) {
resp.err <- fmt.Errorf("block hash does not match: head %s, received %s", unsafeHead.String(), resp.hash.String())
} else {
s.log.Info("Sequencer has been started")
s.driverConfig.SequencerStopped = false
sequencingPlannedOnto = eth.BlockID{}
close(resp.err)
}
case respCh := <-s.stopSequencer:
if s.driverConfig.SequencerStopped {
respCh <- hashAndError{err: errors.New("sequencer not running")}
} else {
s.log.Warn("Sequencer has been stopped")
s.driverConfig.SequencerStopped = true
respCh <- hashAndError{hash: s.derivation.UnsafeL2Head().Hash}
}
case <-s.done:
return
}
......@@ -391,6 +421,45 @@ func (s *Driver) ResetDerivationPipeline(ctx context.Context) error {
}
}
func (s *Driver) StartSequencer(ctx context.Context, blockHash common.Hash) error {
if !s.driverConfig.SequencerEnabled {
return errors.New("sequencer is not enabled")
}
h := hashAndErrorChannel{
hash: blockHash,
err: make(chan error, 1),
}
select {
case <-ctx.Done():
return ctx.Err()
case s.startSequencer <- h:
select {
case <-ctx.Done():
return ctx.Err()
case e := <-h.err:
return e
}
}
}
func (s *Driver) StopSequencer(ctx context.Context) (common.Hash, error) {
if !s.driverConfig.SequencerEnabled {
return common.Hash{}, errors.New("sequencer is not enabled")
}
respCh := make(chan hashAndError, 1)
select {
case <-ctx.Done():
return common.Hash{}, ctx.Err()
case s.stopSequencer <- respCh:
select {
case <-ctx.Done():
return common.Hash{}, ctx.Err()
case he := <-respCh:
return he.hash, he.err
}
}
}
// syncStatus returns the current sync status, and should only be called synchronously with
// the driver event loop to avoid retrieval of an inconsistent status.
func (s *Driver) syncStatus() *eth.SyncStatus {
......@@ -455,3 +524,13 @@ func (s *Driver) snapshot(event string) {
"l2Safe", deferJSONString{s.derivation.SafeL2Head()},
"l2FinalizedHead", deferJSONString{s.derivation.Finalized()})
}
type hashAndError struct {
hash common.Hash
err error
}
type hashAndErrorChannel struct {
hash common.Hash
err chan error
}
......@@ -10,6 +10,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-node/sources"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof"
"github.com/urfave/cli"
......@@ -75,10 +76,10 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) {
ListenAddr: ctx.GlobalString(flags.MetricsAddrFlag.Name),
ListenPort: ctx.GlobalInt(flags.MetricsPortFlag.Name),
},
Pprof: node.PprofConfig{
Pprof: oppprof.CLIConfig{
Enabled: ctx.GlobalBool(flags.PprofEnabledFlag.Name),
ListenAddr: ctx.GlobalString(flags.PprofAddrFlag.Name),
ListenPort: ctx.GlobalString(flags.PprofPortFlag.Name),
ListenPort: ctx.GlobalInt(flags.PprofPortFlag.Name),
},
P2P: p2pConfig,
P2PSigner: p2pSignerSetup,
......@@ -138,6 +139,7 @@ func NewDriverConfig(ctx *cli.Context) (*driver.Config, error) {
VerifierConfDepth: ctx.GlobalUint64(flags.VerifierL1Confs.Name),
SequencerConfDepth: ctx.GlobalUint64(flags.SequencerL1Confs.Name),
SequencerEnabled: ctx.GlobalBool(flags.SequencerEnabledFlag.Name),
SequencerStopped: ctx.GlobalBool(flags.SequencerStoppedFlag.Name),
}, nil
}
......
......@@ -3,6 +3,8 @@ package tls
import (
"errors"
"fmt"
"strings"
"github.com/urfave/cli"
......@@ -15,22 +17,33 @@ const (
TLSKeyFlagName = "tls.key"
)
// CLIFlags returns flags with env var envPrefix
// This should be used for server TLS configs, or when client and server tls configs are the same
func CLIFlags(envPrefix string) []cli.Flag {
return CLIFlagsWithFlagPrefix(envPrefix, "")
}
// CLIFlagsWithFlagPrefix returns flags with env var and cli flag prefixes
// Should be used for client TLS configs when different from server on the same process
func CLIFlagsWithFlagPrefix(envPrefix string, flagPrefix string) []cli.Flag {
prefixFunc := func(flagName string) string {
return strings.Trim(fmt.Sprintf("%s.%s", flagPrefix, flagName), ".")
}
return []cli.Flag{
cli.StringFlag{
Name: TLSCaCertFlagName,
Name: prefixFunc(TLSCaCertFlagName),
Usage: "tls ca cert path",
Value: "tls/ca.crt",
EnvVar: opservice.PrefixEnvVar(envPrefix, "TLS_CA"),
},
cli.StringFlag{
Name: TLSCertFlagName,
Name: prefixFunc(TLSCertFlagName),
Usage: "tls cert path",
Value: "tls/tls.crt",
EnvVar: opservice.PrefixEnvVar(envPrefix, "TLS_CERT"),
},
cli.StringFlag{
Name: TLSKeyFlagName,
Name: prefixFunc(TLSKeyFlagName),
Usage: "tls key",
Value: "tls/tls.key",
EnvVar: opservice.PrefixEnvVar(envPrefix, "TLS_KEY"),
......@@ -56,6 +69,8 @@ func (c CLIConfig) TLSEnabled() bool {
return !(c.TLSCaCert == "" && c.TLSCert == "" && c.TLSKey == "")
}
// ReadCLIConfig reads tls cli configs
// This should be used for server TLS configs, or when client and server tls configs are the same
func ReadCLIConfig(ctx *cli.Context) CLIConfig {
return CLIConfig{
TLSCaCert: ctx.GlobalString(TLSCaCertFlagName),
......@@ -63,3 +78,16 @@ func ReadCLIConfig(ctx *cli.Context) CLIConfig {
TLSKey: ctx.GlobalString(TLSKeyFlagName),
}
}
// ReadCLIConfigWithPrefix reads tls cli configs with flag prefix
// Should be used for client TLS configs when different from server on the same process
func ReadCLIConfigWithPrefix(ctx *cli.Context, flagPrefix string) CLIConfig {
prefixFunc := func(flagName string) string {
return strings.Trim(fmt.Sprintf("%s.%s", flagPrefix, flagName), ".")
}
return CLIConfig{
TLSCaCert: ctx.GlobalString(prefixFunc(TLSCaCertFlagName)),
TLSCert: ctx.GlobalString(prefixFunc(TLSCertFlagName)),
TLSKey: ctx.GlobalString(prefixFunc(TLSKeyFlagName)),
}
}
mocks/
mock_*
tls/
# op-signer
op-signer service client
package client
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"net/http"
"os"
"time"
"github.com/dyson/certman"
optls "github.com/ethereum-optimism/optimism/op-service/tls"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
)
type SignerClient struct {
client *rpc.Client
status string
logger log.Logger
}
// ethLogger wraps a geth style logger for certman.
type ethLogger struct{ logger log.Logger }
func (l ethLogger) Printf(format string, v ...interface{}) { l.logger.Info(fmt.Sprintf(format, v...)) }
func NewSignerClient(logger log.Logger, endpoint string, tlsConfig optls.CLIConfig) (*SignerClient, error) {
caCert, err := os.ReadFile(tlsConfig.TLSCaCert)
if err != nil {
return nil, fmt.Errorf("failed to read tls.ca: %w", err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
// certman watches for newer client certifictes and automatically reloads them
cm, err := certman.New(tlsConfig.TLSCert, tlsConfig.TLSKey)
cm.Logger(ethLogger{logger: logger})
if err != nil {
logger.Error("failed to read tls cert or key", "err", err)
return nil, err
}
if err := cm.Watch(); err != nil {
logger.Error("failed to start certman watcher", "err", err)
return nil, err
}
httpClient := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS13,
RootCAs: caCertPool,
GetClientCertificate: func(_ *tls.CertificateRequestInfo) (*tls.Certificate, error) {
return cm.GetCertificate(nil)
},
},
},
}
rpcClient, err := rpc.DialOptions(context.Background(), endpoint, rpc.WithHTTPClient(httpClient))
if err != nil {
return nil, err
}
signer := &SignerClient{logger: logger, client: rpcClient}
// Check if reachable
version, err := signer.pingVersion()
if err != nil {
return nil, err
}
signer.status = fmt.Sprintf("ok [version=%v]", version)
return signer, nil
}
func NewSignerClientFromConfig(logger log.Logger, config CLIConfig) (*SignerClient, error) {
return NewSignerClient(logger, config.Endpoint, config.TLSConfig)
}
func (s *SignerClient) pingVersion() (string, error) {
var v string
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()
if err := s.client.CallContext(ctx, &v, "health_status"); err != nil {
return "", err
}
return v, nil
}
func (s *SignerClient) SignTransaction(ctx context.Context, tx *types.Transaction) (*types.Transaction, error) {
args := NewTransactionArgsFromTransaction(tx)
var result hexutil.Bytes
if err := s.client.CallContext(ctx, &result, "eth_signTransaction", args); err != nil {
return nil, fmt.Errorf("eth_signTransaction failed: %w", err)
}
signed := &types.Transaction{}
if err := signed.UnmarshalBinary(result); err != nil {
return nil, err
}
return signed, nil
}
package client
import (
"errors"
"github.com/urfave/cli"
opservice "github.com/ethereum-optimism/optimism/op-service"
optls "github.com/ethereum-optimism/optimism/op-service/tls"
)
const (
EndpointFlagName = "signer.endpoint"
AddressFlagName = "signer.address"
)
func CLIFlags(envPrefix string) []cli.Flag {
envPrefix += "_SIGNER"
flags := []cli.Flag{
cli.StringFlag{
Name: EndpointFlagName,
Usage: "Signer endpoint the client will connect to",
EnvVar: opservice.PrefixEnvVar(envPrefix, "ENDPOINT"),
},
cli.StringFlag{
Name: AddressFlagName,
Usage: "Address the signer is signing transactions for",
EnvVar: opservice.PrefixEnvVar(envPrefix, "ADDRESS"),
},
}
flags = append(flags, optls.CLIFlagsWithFlagPrefix(envPrefix, "signer")...)
return flags
}
type CLIConfig struct {
Endpoint string
Address string
TLSConfig optls.CLIConfig
}
func (c CLIConfig) Check() error {
if err := c.TLSConfig.Check(); err != nil {
return err
}
if !((c.Endpoint == "" && c.Address == "") || (c.Endpoint != "" && c.Address != "")) {
return errors.New("signer endpoint and address must both be set or not set")
}
return nil
}
func (c CLIConfig) Enabled() bool {
if c.Endpoint != "" && c.Address != "" {
return true
}
return false
}
func ReadCLIConfig(ctx *cli.Context) CLIConfig {
cfg := CLIConfig{
Endpoint: ctx.String(EndpointFlagName),
Address: ctx.String(AddressFlagName),
TLSConfig: optls.ReadCLIConfigWithPrefix(ctx, "signer"),
}
return cfg
}
package client
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
)
// TransactionArgs represents the arguments to construct a new transaction
// or a message call.
type TransactionArgs struct {
From *common.Address `json:"from"`
To *common.Address `json:"to"`
Gas *hexutil.Uint64 `json:"gas"`
GasPrice *hexutil.Big `json:"gasPrice"`
MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"`
MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"`
Value *hexutil.Big `json:"value"`
Nonce *hexutil.Uint64 `json:"nonce"`
// We accept "data" and "input" for backwards-compatibility reasons.
// "input" is the newer name and should be preferred by clients.
// Issue detail: https://github.com/ethereum/go-ethereum/issues/15628
Data *hexutil.Bytes `json:"data"`
Input *hexutil.Bytes `json:"input"`
AccessList *types.AccessList `json:"accessList,omitempty"`
ChainID *hexutil.Big `json:"chainId,omitempty"`
}
// NewTransactionArgsFromTransaction creates a TransactionArgs struct from an EIP-1559 transaction
func NewTransactionArgsFromTransaction(tx *types.Transaction) *TransactionArgs {
data := hexutil.Bytes(tx.Data())
nonce := hexutil.Uint64(tx.Nonce())
gas := hexutil.Uint64(tx.Gas())
accesses := tx.AccessList()
args := &TransactionArgs{
Input: &data,
Nonce: &nonce,
Value: (*hexutil.Big)(tx.Value()),
Gas: &gas,
To: tx.To(),
ChainID: (*hexutil.Big)(tx.ChainId()),
MaxFeePerGas: (*hexutil.Big)(tx.GasFeeCap()),
MaxPriorityFeePerGas: (*hexutil.Big)(tx.GasTipCap()),
AccessList: &accesses,
}
return args
}
// data retrieves the transaction calldata. Input field is preferred.
func (args *TransactionArgs) data() []byte {
if args.Input != nil {
return *args.Input
}
if args.Data != nil {
return *args.Data
}
return nil
}
// ToTransaction converts the arguments to a transaction.
func (args *TransactionArgs) ToTransaction() *types.Transaction {
var data types.TxData
al := types.AccessList{}
if args.AccessList != nil {
al = *args.AccessList
}
data = &types.DynamicFeeTx{
To: args.To,
ChainID: (*big.Int)(args.ChainID),
Nonce: uint64(*args.Nonce),
Gas: uint64(*args.Gas),
GasFeeCap: (*big.Int)(args.MaxFeePerGas),
GasTipCap: (*big.Int)(args.MaxPriorityFeePerGas),
Value: (*big.Int)(args.Value),
Data: args.data(),
AccessList: al,
}
return types.NewTx(data)
}
module github.com/ethereum-optimism/optimism/op-signer
go 1.18
require (
github.com/dyson/certman v0.3.0
github.com/ethereum-optimism/optimism/op-service v0.10.10
github.com/ethereum/go-ethereum v1.10.26
github.com/urfave/cli v1.22.9
)
require (
github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/deckarep/golang-set v1.8.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.2.1-0.20211004051800-57c86be7915a // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/tklauser/numcpus v0.4.0 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 // indirect
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
)
replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20221104231810-30db39cae2be
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/VictoriaMetrics/fastcache v1.9.0 h1:oMwsS6c8abz98B7ytAewQ7M1ZN/Im/iwKoE1euaFvhs=
github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k=
github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4=
github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc=
github.com/dyson/certman v0.3.0 h1:S7WCUim5faT/OiBhiY3u5cMaiC9MNKiA+8PJDXLaIYQ=
github.com/dyson/certman v0.3.0/go.mod h1:RMWlyA9op6D9SxOBRRX3sxnParehv9gf52WWUJPd1JA=
github.com/ethereum-optimism/op-geth v0.0.0-20221104231810-30db39cae2be h1:8TdM3M7FjZkrYeGGX9nEVtDDlZ5RiuHtc0mbi5bGKyY=
github.com/ethereum-optimism/op-geth v0.0.0-20221104231810-30db39cae2be/go.mod h1:1g9UmZgEINqvYfXmWOUCRJX9fxegeOHudVkLCRAXO5Y=
github.com/ethereum-optimism/optimism/op-service v0.10.10 h1:B5mGpATX6zPkDABoh6smCjh6Z5mA2KWh71MD1i6T5ww=
github.com/ethereum-optimism/optimism/op-service v0.10.10/go.mod h1:wbtHqi1fv00B3agj7a2zdP3OFanEfGZ23zPgGgFCF/c=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e h1:pIYdhNkDh+YENVNi3gto8n9hAmRxKxoar0iE6BLucjw=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/tsdb v0.10.0 h1:If5rVCMTp6W2SiRAQFlbpJNgVlgMEd+U2GZckwK38ic=
github.com/rivo/uniseg v0.2.1-0.20211004051800-57c86be7915a h1:s7GrsqeorVkFR1vGmQ6WVL9nup0eyQCC+YVUeSQLH/Q=
github.com/rivo/uniseg v0.2.1-0.20211004051800-57c86be7915a/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5fl+nylMaIr9PVV1w343YRDtsy+Rwu7XI=
github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw=
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o=
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
github.com/urfave/cli v1.22.9 h1:cv3/KhXGBGjEXLC4bH0sLuJ9BewaAbpk5oyMOveu4pw=
github.com/urfave/cli v1.22.9/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 h1:OK7RB6t2WQX54srQQYSXMW8dF5C6/8+oA/s5QBmmto4=
golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
......@@ -65,6 +65,7 @@ services:
--metrics.addr=0.0.0.0
--metrics.port=7300
--pprof.enabled
--rpc.enable-admin
ports:
- "7545:8545"
- "9003:9003"
......
......@@ -156,7 +156,7 @@ export abstract class BaseServiceV2<
// Use commander as a way to communicate info about the service. We don't actually *use*
// commander for anything besides the ability to run `ts-node ./service.ts --help`.
const program = new Command()
const program = new Command().allowUnknownOption(true)
for (const [optionName, optionSpec] of Object.entries(params.optionsSpec)) {
// Skip options that are not meant to be used by the user.
if (['useEnv', 'useArgv'].includes(optionName)) {
......
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 261344)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 75851)
GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_0() (gas: 348151)
GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_1() (gas: 112583)
GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_0() (gas: 348173)
GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_1() (gas: 112604)
GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_0() (gas: 348207)
GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_1() (gas: 112639)
GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_0() (gas: 348229)
GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_1() (gas: 112660)
GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (gas: 40502)
GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 88513)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 74998)
......@@ -92,11 +92,11 @@ L1ERC721Bridge_Test:test_finalizeBridgeERC721_notFromRemoteMessenger_reverts() (
L1ERC721Bridge_Test:test_finalizeBridgeERC721_notViaLocalMessenger_reverts() (gas: 16093)
L1ERC721Bridge_Test:test_finalizeBridgeERC721_selfToken_reverts() (gas: 17593)
L1ERC721Bridge_Test:test_finalizeBridgeERC721_succeeds() (gas: 323814)
L1StandardBridge_DepositERC20To_Test:test_depositERC20To_succeeds() (gas: 575959)
L1StandardBridge_DepositERC20_Test:test_depositERC20_succeeds() (gas: 573786)
L1StandardBridge_DepositERC20To_Test:test_depositERC20To_succeeds() (gas: 576276)
L1StandardBridge_DepositERC20_Test:test_depositERC20_succeeds() (gas: 574103)
L1StandardBridge_DepositERC20_TestFail:test_depositERC20_notEoa_reverts() (gas: 22320)
L1StandardBridge_DepositETHTo_Test:test_depositETHTo_succeeds() (gas: 324712)
L1StandardBridge_DepositETH_Test:test_depositETH_succeeds() (gas: 367539)
L1StandardBridge_DepositETHTo_Test:test_depositETHTo_succeeds() (gas: 324839)
L1StandardBridge_DepositETH_Test:test_depositETH_succeeds() (gas: 367666)
L1StandardBridge_DepositETH_TestFail:test_depositETH_notEoa_reverts() (gas: 40780)
L1StandardBridge_FinalizeBridgeETH_TestFail:test_finalizeBridgeETH_incorrectValue_reverts() (gas: 34207)
L1StandardBridge_FinalizeBridgeETH_TestFail:test_finalizeBridgeETH_sendToMessenger_reverts() (gas: 34288)
......@@ -105,9 +105,9 @@ L1StandardBridge_FinalizeERC20Withdrawal_Test:test_finalizeERC20Withdrawal_succe
L1StandardBridge_FinalizeERC20Withdrawal_TestFail:test_finalizeERC20Withdrawal_notMessenger_reverts() (gas: 31148)
L1StandardBridge_FinalizeERC20Withdrawal_TestFail:test_finalizeERC20Withdrawal_notOtherBridge_reverts() (gas: 31504)
L1StandardBridge_FinalizeETHWithdrawal_Test:test_finalizeETHWithdrawal_succeeds() (gas: 58686)
L1StandardBridge_Getter_Test:test_getters_succeeds() (gas: 31449)
L1StandardBridge_Getter_Test:test_getters_succeeds() (gas: 32151)
L1StandardBridge_Initialize_Test:test_initialize_succeeds() (gas: 22005)
L1StandardBridge_Receive_Test:test_receive_succeeds() (gas: 514475)
L1StandardBridge_Receive_Test:test_receive_succeeds() (gas: 519995)
L2CrossDomainMessenger_Test:test_messageVersion_succeeds() (gas: 8389)
L2CrossDomainMessenger_Test:test_pause_notOwner_reverts() (gas: 10837)
L2CrossDomainMessenger_Test:test_pause_succeeds() (gas: 31846)
......@@ -168,11 +168,11 @@ L2StandardBridge_Test:test_finalizeBridgeETH_sendToMessenger_reverts() (gas: 239
L2StandardBridge_Test:test_finalizeBridgeETH_sendToSelf_reverts() (gas: 23893)
L2StandardBridge_Test:test_finalizeDeposit_succeeds() (gas: 89473)
L2StandardBridge_Test:test_initialize_succeeds() (gas: 24270)
L2StandardBridge_Test:test_receive_succeeds() (gas: 131905)
L2StandardBridge_Test:test_withdrawTo_succeeds() (gas: 344660)
L2StandardBridge_Test:test_withdraw_insufficientValue_reverts() (gas: 19630)
L2StandardBridge_Test:test_receive_succeeds() (gas: 141940)
L2StandardBridge_Test:test_withdrawTo_succeeds() (gas: 344914)
L2StandardBridge_Test:test_withdraw_insufficientValue_reverts() (gas: 19627)
L2StandardBridge_Test:test_withdraw_notEOA_reverts() (gas: 251798)
L2StandardBridge_Test:test_withdraw_succeeds() (gas: 343975)
L2StandardBridge_Test:test_withdraw_succeeds() (gas: 344228)
L2ToL1MessagePasserTest:test_burn_succeeds() (gas: 112572)
L2ToL1MessagePasserTest:test_initiateWithdrawal_fromContract_succeeds() (gas: 70423)
L2ToL1MessagePasserTest:test_initiateWithdrawal_fromEOA_succeeds() (gas: 75874)
......
......@@ -91,15 +91,22 @@ contract L1StandardBridge is StandardBridge, Semver {
);
/**
* @custom:semver 1.0.0
* @custom:semver 1.1.0
*
* @param _messenger Address of the L1CrossDomainMessenger.
*/
constructor(address payable _messenger)
Semver(1, 0, 0)
Semver(1, 1, 0)
StandardBridge(_messenger, payable(Predeploys.L2_STANDARD_BRIDGE))
{}
/**
* @notice Allows EOAs to bridge ETH by sending directly to the bridge.
*/
receive() external payable override onlyEOA {
_initiateETHDeposit(msg.sender, msg.sender, RECEIVE_DEFAULT_GAS_LIMIT, bytes(""));
}
/**
* @custom:legacy
* @notice Finalizes a withdrawal of ERC20 tokens from L2.
......@@ -261,7 +268,7 @@ contract L1StandardBridge is StandardBridge, Semver {
address _from,
address _to,
uint32 _minGasLimit,
bytes calldata _extraData
bytes memory _extraData
) internal {
emit ETHDepositInitiated(_from, _to, msg.value, _extraData);
_initiateBridgeETH(_from, _to, msg.value, _minGasLimit, _extraData);
......@@ -285,7 +292,7 @@ contract L1StandardBridge is StandardBridge, Semver {
address _to,
uint256 _amount,
uint32 _minGasLimit,
bytes calldata _extraData
bytes memory _extraData
) internal {
emit ERC20DepositInitiated(_l1Token, _l2Token, _from, _to, _amount, _extraData);
_initiateBridgeERC20(_l1Token, _l2Token, _from, _to, _amount, _minGasLimit, _extraData);
......
......@@ -59,15 +59,29 @@ contract L2StandardBridge is StandardBridge, Semver {
);
/**
* @custom:semver 1.0.0
* @custom:semver 1.1.0
*
* @param _otherBridge Address of the L1StandardBridge.
*/
constructor(address payable _otherBridge)
Semver(1, 0, 0)
Semver(1, 1, 0)
StandardBridge(payable(Predeploys.L2_CROSS_DOMAIN_MESSENGER), _otherBridge)
{}
/**
* @notice Allows EOAs to bridge ETH by sending directly to the bridge.
*/
receive() external payable override onlyEOA {
_initiateWithdrawal(
Predeploys.LEGACY_ERC20_ETH,
msg.sender,
msg.sender,
msg.value,
RECEIVE_DEFAULT_GAS_LIMIT,
bytes("")
);
}
/**
* @custom:legacy
* @notice Initiates a withdrawal from L2 to L1.
......@@ -165,7 +179,7 @@ contract L2StandardBridge is StandardBridge, Semver {
address _to,
uint256 _amount,
uint32 _minGasLimit,
bytes calldata _extraData
bytes memory _extraData
) internal {
address l1Token = OptimismMintableERC20(_l2Token).l1Token();
if (_l2Token == Predeploys.LEGACY_ERC20_ETH) {
......
......@@ -16,7 +16,7 @@ contract L1StandardBridge_Getter_Test is Bridge_Initializer {
assert(L1Bridge.OTHER_BRIDGE() == L2Bridge);
assert(L1Bridge.messenger() == L1Messenger);
assert(L1Bridge.MESSENGER() == L1Messenger);
assertEq(L1Bridge.version(), "1.0.0");
assertEq(L1Bridge.version(), "1.1.0");
}
}
......@@ -38,6 +38,10 @@ contract L1StandardBridge_Receive_Test is Bridge_Initializer {
function test_receive_succeeds() external {
assertEq(address(op).balance, 0);
// The legacy event must be emitted for backwards compatibility
vm.expectEmit(true, true, true, true);
emit ETHDepositInitiated(alice, alice, 100, hex"");
vm.expectEmit(true, true, true, true);
emit ETHBridgeInitiated(alice, alice, 100, hex"");
......
......@@ -33,6 +33,9 @@ contract L2StandardBridge_Test is Bridge_Initializer {
// TODO: withdrawal hash should be computed correctly
// TODO: events from each contract
vm.expectEmit(true, true, true, true);
emit WithdrawalInitiated(address(0), Predeploys.LEGACY_ERC20_ETH, alice, alice, 100, hex"");
vm.prank(alice, alice);
(bool success, ) = address(L2Bridge).call{ value: 100 }(hex"");
assertEq(success, true);
......
......@@ -164,11 +164,10 @@ abstract contract StandardBridge {
}
/**
* @notice Allows EOAs to deposit ETH by sending directly to the bridge.
* @notice Allows EOAs to bridge ETH by sending directly to the bridge.
* Must be implemented by contracts that inherit.
*/
receive() external payable onlyEOA {
_initiateBridgeETH(msg.sender, msg.sender, msg.value, RECEIVE_DEFAULT_GAS_LIMIT, bytes(""));
}
receive() external payable virtual;
/**
* @custom:legacy
......@@ -401,7 +400,7 @@ abstract contract StandardBridge {
address _to,
uint256 _amount,
uint32 _minGasLimit,
bytes calldata _extraData
bytes memory _extraData
) internal {
if (_isOptimismMintableERC20(_localToken)) {
require(
......
......@@ -14,6 +14,7 @@
"contracts/**/*.sol"
],
"scripts": {
"bindings": "cd ../../op-bindings && make",
"build:forge": "forge build",
"build:with-metadata": "FOUNDRY_PROFILE=echidna yarn build:forge",
"build:differential": "tsc scripts/differential-testing.ts --outDir dist --moduleResolution node --esModuleInterop",
......
......@@ -328,7 +328,7 @@ const check = {
signer
)
await assertSemver(L2StandardBridge, 'L2StandardBridge')
await assertSemver(L2StandardBridge, 'L2StandardBridge', '1.1.0')
const OTHER_BRIDGE = await L2StandardBridge.OTHER_BRIDGE()
assert(OTHER_BRIDGE !== hre.ethers.constants.AddressZero)
......
......@@ -10,22 +10,23 @@ AssetReceiverTest:test_withdrawERC20withAmount() (gas: 182146)
AssetReceiverTest:test_withdrawERC721() (gas: 49097)
AssetReceiverTest:test_withdrawETH() (gas: 26179)
AssetReceiverTest:test_withdrawETHwithAmount() (gas: 26108)
AssetReceiverTest:test_attest_bulk() (gas: 592013)
AssetReceiverTest:test_attest_single() (gas: 539644)
OptimistTest:test_optimist_baseURI() (gas: 117016)
OptimistTest:test_optimist_burn() (gas: 77691)
AssetReceiverTest:test_attest_bulk() (gas: 611440)
AssetReceiverTest:test_attest_individual() (gas: 538514)
AssetReceiverTest:test_attest_single() (gas: 558962)
OptimistTest:test_optimist_baseURI() (gas: 116809)
OptimistTest:test_optimist_burn() (gas: 77526)
OptimistTest:test_optimist_initialize() (gas: 23095)
OptimistTest:test_optimist_is_on_allow_list() (gas: 52822)
OptimistTest:test_optimist_mint_already_minted() (gas: 99125)
OptimistTest:test_optimist_mint_happy_path() (gas: 99381)
OptimistTest:test_optimist_is_on_allow_list() (gas: 52616)
OptimistTest:test_optimist_mint_already_minted() (gas: 98911)
OptimistTest:test_optimist_mint_happy_path() (gas: 99175)
OptimistTest:test_optimist_mint_no_attestation() (gas: 15897)
OptimistTest:test_optimist_mint_secondary_minter() (gas: 100782)
OptimistTest:test_optimist_sbt_approve() (gas: 97490)
OptimistTest:test_optimist_sbt_transfer() (gas: 102537)
OptimistTest:test_optimist_set_approval_for_all() (gas: 101119)
OptimistTest:test_optimist_mint_secondary_minter() (gas: 100576)
OptimistTest:test_optimist_sbt_approve() (gas: 97284)
OptimistTest:test_optimist_sbt_transfer() (gas: 102331)
OptimistTest:test_optimist_set_approval_for_all() (gas: 100907)
OptimistTest:test_optimist_supports_interface() (gas: 5797)
OptimistTest:test_optimist_token_id_of_owner() (gas: 95251)
OptimistTest:test_optimist_token_uri() (gas: 214371)
OptimistTest:test_optimist_token_id_of_owner() (gas: 95045)
OptimistTest:test_optimist_token_uri() (gas: 213950)
TransactorTest:testFail_CALL() (gas: 15658)
TransactorTest:testFail_DELEGATECALLL() (gas: 15632)
TransactorTest:test_CALL() (gas: 26977)
......
......@@ -26,6 +26,23 @@ contract AssetReceiverTest is AssetReceiver_Initializer {
super._setUp();
}
event AttestationCreated(
address indexed creator,
address indexed about,
bytes32 indexed key,
bytes val
);
function test_attest_individual() external {
AttestationStation attestationStation = new AttestationStation();
vm.expectEmit(true, true, true, true);
emit AttestationCreated(alice_attestor, bob, bytes32("foo"), bytes("bar"));
vm.prank(alice_attestor);
attestationStation.attest({ _about: bob, _key: bytes32("foo"), _val: bytes("bar") });
}
function test_attest_single() external {
AttestationStation attestationStation = new AttestationStation();
......
......@@ -44,27 +44,38 @@ contract AttestationStation is Semver {
);
/**
* @custom:semver 1.0.0
* @custom:semver 1.1.0
*/
constructor() Semver(1, 0, 0) {}
constructor() Semver(1, 1, 0) {}
/**
* @notice Allows anyone to create an attestation.
*
* @param _about Address that the attestation is about.
* @param _key A key used to namespace the attestation.
* @param _val An arbitrary value stored as part of the attestation.
*/
function attest(
address _about,
bytes32 _key,
bytes memory _val
) public {
attestations[msg.sender][_about][_key] = _val;
emit AttestationCreated(msg.sender, _about, _key, _val);
}
/**
* @notice Allows anyone to create attestations.
*
* @param _attestations An array of attestation data.
*/
function attest(AttestationData[] memory _attestations) public {
function attest(AttestationData[] calldata _attestations) external {
uint256 length = _attestations.length;
for (uint256 i = 0; i < length; ) {
AttestationData memory attestation = _attestations[i];
attestations[msg.sender][attestation.about][attestation.key] = attestation.val;
emit AttestationCreated(
msg.sender,
attestation.about,
attestation.key,
attestation.val
);
attest(attestation.about, attestation.key, attestation.val);
unchecked {
++i;
......
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