Commit 0395c9f4 authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into jg/cleanup_txmgr_apis

parents 0b72447b 37dddde6
---
'@eth-optimism/hardhat-deploy-config': patch
---
Add getter for other network's deploy config
...@@ -1123,6 +1123,9 @@ workflows: ...@@ -1123,6 +1123,9 @@ workflows:
- go-lint: - go-lint:
name: op-proposer-lint name: op-proposer-lint
module: op-proposer module: op-proposer
- go-lint:
name: op-program-lint
module: op-program
- go-lint: - go-lint:
name: op-service-lint name: op-service-lint
module: op-service module: op-service
...@@ -1145,6 +1148,9 @@ workflows: ...@@ -1145,6 +1148,9 @@ workflows:
- go-test: - go-test:
name: op-proposer-tests name: op-proposer-tests
module: op-proposer module: op-proposer
- go-test:
name: op-program-tests
module: op-program
- go-test: - go-test:
name: op-service-tests name: op-service-tests
module: op-service module: op-service
...@@ -1164,12 +1170,14 @@ workflows: ...@@ -1164,12 +1170,14 @@ workflows:
- op-e2e-lint - op-e2e-lint
- op-node-lint - op-node-lint
- op-proposer-lint - op-proposer-lint
- op-program-lint
- op-service-lint - op-service-lint
- op-batcher-tests - op-batcher-tests
- op-bindings-tests - op-bindings-tests
- op-chain-ops-tests - op-chain-ops-tests
- op-node-tests - op-node-tests
- op-proposer-tests - op-proposer-tests
- op-program-tests
- op-service-tests - op-service-tests
- op-e2e-WS-tests - op-e2e-WS-tests
- op-e2e-HTTP-tests - op-e2e-HTTP-tests
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
/op-node @ethereum-optimism/go-reviewers /op-node @ethereum-optimism/go-reviewers
/op-node/rollup @protolambda @trianglesphere /op-node/rollup @protolambda @trianglesphere
/op-proposer @ethereum-optimism/go-reviewers /op-proposer @ethereum-optimism/go-reviewers
/op-program @ethereum-optimism/go-reviewers
/op-service @ethereum-optimism/go-reviewers /op-service @ethereum-optimism/go-reviewers
# Ops # Ops
......
...@@ -75,6 +75,7 @@ jobs: ...@@ -75,6 +75,7 @@ jobs:
uses: changesets/action@v1 uses: changesets/action@v1
id: changesets id: changesets
with: with:
createGithubReleases: false
publish: yarn changeset publish --tag canary publish: yarn changeset publish --tag canary
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
......
...@@ -67,6 +67,7 @@ jobs: ...@@ -67,6 +67,7 @@ jobs:
uses: changesets/action@v1 uses: changesets/action@v1
id: changesets id: changesets
with: with:
createGithubReleases: false
publish: yarn release publish: yarn release
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
......
...@@ -40,6 +40,10 @@ op-proposer: ...@@ -40,6 +40,10 @@ op-proposer:
make -C ./op-proposer op-proposer make -C ./op-proposer op-proposer
.PHONY: op-proposer .PHONY: op-proposer
op-program:
make -C ./op-program op-program
.PHONY: op-program
mod-tidy: mod-tidy:
# Below GOPRIVATE line allows mod-tidy to be run immediately after # Below GOPRIVATE line allows mod-tidy to be run immediately after
# releasing new versions. This bypasses the Go modules proxy, which # releasing new versions. This bypasses the Go modules proxy, which
......
This diff is collapsed.
package main
import (
"context"
"errors"
"fmt"
"math/big"
"os"
"sync"
"time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
legacy_bindings "github.com/ethereum-optimism/optimism/op-bindings/legacy-bindings"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum-optimism/optimism/op-chain-ops/util"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
"github.com/urfave/cli/v2"
)
func main() {
app := cli.NewApp()
app.Name = "rollover"
app.Usage = "Commands for assisting in the rollover of the system"
var flags []cli.Flag
flags = append(flags, util.ClientsFlags...)
flags = append(flags, util.AddressesFlags...)
app.Commands = []*cli.Command{
{
Name: "deposits",
Usage: "Ensures that all deposits have been ingested into L2",
Flags: flags,
Action: func(cliCtx *cli.Context) error {
clients, err := util.NewClients(cliCtx)
if err != nil {
return err
}
addresses, err := util.NewAddresses(cliCtx)
if err != nil {
return err
}
log.Info("Connecting to AddressManager", "address", addresses.AddressManager)
addressManager, err := bindings.NewAddressManager(addresses.AddressManager, clients.L1Client)
if err != nil {
return err
}
for {
shutoffBlock, err := addressManager.GetAddress(&bind.CallOpts{}, "DTL_SHUTOFF_BLOCK")
if err != nil {
return err
}
if num := shutoffBlock.Big(); num.Cmp(common.Big0) != 0 {
log.Info("DTL_SHUTOFF_BLOCK is set", "number", num.Uint64())
break
}
log.Info("DTL_SHUTOFF_BLOCK not set yet")
time.Sleep(3 * time.Second)
}
log.Info("Connecting to CanonicalTransactionChain", "address", addresses.CanonicalTransactionChain)
ctc, err := legacy_bindings.NewCanonicalTransactionChain(addresses.CanonicalTransactionChain, clients.L1Client)
if err != nil {
return err
}
queueLength, err := ctc.GetQueueLength(&bind.CallOpts{})
if err != nil {
return err
}
totalElements, err := ctc.GetTotalElements(&bind.CallOpts{})
if err != nil {
return err
}
totalBatches, err := ctc.GetTotalBatches(&bind.CallOpts{})
if err != nil {
return err
}
pending, err := ctc.GetNumPendingQueueElements(&bind.CallOpts{})
if err != nil {
return err
}
log.Info(
"CanonicalTransactionChain",
"address", addresses.CanonicalTransactionChain,
"queue-length", queueLength,
"total-elements", totalElements,
"total-batches", totalBatches,
"pending", pending,
)
blockNumber, err := clients.L2Client.BlockNumber(context.Background())
if err != nil {
return err
}
log.Info("Searching backwards for final deposit", "start", blockNumber)
for {
bn := new(big.Int).SetUint64(blockNumber)
log.Info("Checking L2 block", "number", bn)
block, err := clients.L2Client.BlockByNumber(context.Background(), bn)
if err != nil {
return err
}
if length := len(block.Transactions()); length != 1 {
return fmt.Errorf("unexpected number of transactions in block: %d", length)
}
tx := block.Transactions()[0]
hash := tx.Hash()
json, err := legacyTransactionByHash(clients.L2RpcClient, hash)
if err != nil {
return err
}
if json.QueueOrigin == "l1" {
if json.QueueIndex == nil {
// This should never happen
return errors.New("queue index is nil")
}
queueIndex := uint64(*json.QueueIndex)
if queueIndex == queueLength.Uint64()-1 {
log.Info("Found final deposit in l2geth", "queue-index", queueIndex)
break
}
if queueIndex < queueLength.Uint64() {
return errors.New("missed final deposit")
}
}
blockNumber--
}
finalPending, err := ctc.GetNumPendingQueueElements(&bind.CallOpts{})
if err != nil {
return err
}
log.Info("Remaining deposits that must be submitted", "count", finalPending)
return nil
},
},
{
Name: "batches",
Usage: "Ensures that all batches have been submitted to L1",
Flags: flags,
Action: func(cliCtx *cli.Context) error {
clients, err := util.NewClients(cliCtx)
if err != nil {
return err
}
addresses, err := util.NewAddresses(cliCtx)
if err != nil {
return err
}
log.Info("Connecting to CanonicalTransactionChain", "address", addresses.CanonicalTransactionChain)
ctc, err := legacy_bindings.NewCanonicalTransactionChain(addresses.CanonicalTransactionChain, clients.L1Client)
if err != nil {
return err
}
log.Info("Connecting to StateCommitmentChain", "address", addresses.StateCommitmentChain)
scc, err := legacy_bindings.NewStateCommitmentChain(addresses.StateCommitmentChain, clients.L1Client)
if err != nil {
return err
}
var wg sync.WaitGroup
log.Info("Waiting for CanonicalTransactionChain")
wg.Add(1)
go waitForTotalElements(&wg, ctc, clients.L2Client)
log.Info("Waiting for StateCommitmentChain")
wg.Add(1)
go waitForTotalElements(&wg, scc, clients.L2Client)
wg.Wait()
log.Info("All batches have been submitted")
return nil
},
},
}
if err := app.Run(os.Args); err != nil {
log.Crit("Application failed", "message", err)
}
}
// RollupContract represents a legacy rollup contract interface that
// exposes the GetTotalElements function. Both the StateCommitmentChain
// and the CanonicalTransactionChain implement this interface.
type RollupContract interface {
GetTotalElements(opts *bind.CallOpts) (*big.Int, error)
}
// waitForTotalElements will poll to see
func waitForTotalElements(wg *sync.WaitGroup, contract RollupContract, client *ethclient.Client) {
defer wg.Done()
for {
bn, err := client.BlockNumber(context.Background())
if err != nil {
log.Error("cannot fetch blocknumber", "error", err)
time.Sleep(3 * time.Second)
continue
}
totalElements, err := contract.GetTotalElements(&bind.CallOpts{})
if err != nil {
log.Error("cannot fetch total elements", "error", err)
time.Sleep(3 * time.Second)
continue
}
if totalElements.Uint64() == bn {
return
}
log.Info("Waiting for elements to be submitted", "count", totalElements.Uint64()-bn, "height", bn, "total-elements", totalElements.Uint64())
time.Sleep(3 * time.Second)
}
}
// legacyTransactionByHash will fetch a transaction by hash and be sure to decode
// the additional fields added to legacy transactions.
func legacyTransactionByHash(client *rpc.Client, hash common.Hash) (*RPCTransaction, error) {
var json *RPCTransaction
err := client.CallContext(context.Background(), &json, "eth_getTransactionByHash", hash)
if err != nil {
return nil, err
}
return json, nil
}
// RPCTransaction represents a transaction that will serialize to the RPC representation of a
// transaction. This handles the extra legacy fields added to transactions.
type RPCTransaction struct {
BlockHash *common.Hash `json:"blockHash"`
BlockNumber *hexutil.Big `json:"blockNumber"`
From common.Address `json:"from"`
Gas hexutil.Uint64 `json:"gas"`
GasPrice *hexutil.Big `json:"gasPrice"`
Hash common.Hash `json:"hash"`
Input hexutil.Bytes `json:"input"`
Nonce hexutil.Uint64 `json:"nonce"`
To *common.Address `json:"to"`
TransactionIndex *hexutil.Uint64 `json:"transactionIndex"`
Value *hexutil.Big `json:"value"`
V *hexutil.Big `json:"v"`
R *hexutil.Big `json:"r"`
S *hexutil.Big `json:"s"`
QueueOrigin string `json:"queueOrigin"`
L1TxOrigin *common.Address `json:"l1TxOrigin"`
L1BlockNumber *hexutil.Big `json:"l1BlockNumber"`
L1Timestamp hexutil.Uint64 `json:"l1Timestamp"`
Index *hexutil.Uint64 `json:"index"`
QueueIndex *hexutil.Uint64 `json:"queueIndex"`
RawTransaction hexutil.Bytes `json:"rawTransaction"`
}
...@@ -18,6 +18,7 @@ import ( ...@@ -18,6 +18,7 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/predeploys" "github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain" "github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis" "github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum-optimism/optimism/op-chain-ops/util"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
...@@ -25,9 +26,7 @@ import ( ...@@ -25,9 +26,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/ethclient/gethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
) )
// abiTrue represents the storage representation of the boolean // abiTrue represents the storage representation of the boolean
...@@ -115,7 +114,7 @@ func main() { ...@@ -115,7 +114,7 @@ func main() {
}, },
}, },
Action: func(ctx *cli.Context) error { Action: func(ctx *cli.Context) error {
clients, err := newClients(ctx) clients, err := util.NewClients(ctx)
if err != nil { if err != nil {
return err return err
} }
...@@ -433,7 +432,7 @@ func main() { ...@@ -433,7 +432,7 @@ func main() {
} }
// callTrace will call `debug_traceTransaction` on a remote node // callTrace will call `debug_traceTransaction` on a remote node
func callTrace(c *clients, receipt *types.Receipt) (callFrame, error) { func callTrace(c *util.Clients, receipt *types.Receipt) (callFrame, error) {
var finalizationTrace callFrame var finalizationTrace callFrame
tracer := "callTracer" tracer := "callTracer"
traceConfig := tracers.TraceConfig{ traceConfig := tracers.TraceConfig{
...@@ -558,7 +557,7 @@ func handleFinalizeERC20Withdrawal(args []any, receipt *types.Receipt, l1Standar ...@@ -558,7 +557,7 @@ func handleFinalizeERC20Withdrawal(args []any, receipt *types.Receipt, l1Standar
// proveWithdrawalTransaction will build the data required for proving a // proveWithdrawalTransaction will build the data required for proving a
// withdrawal and then send the transaction and make sure that it is included // withdrawal and then send the transaction and make sure that it is included
// and successful and then wait for the finalization period to elapse. // and successful and then wait for the finalization period to elapse.
func proveWithdrawalTransaction(c *contracts, cl *clients, opts *bind.TransactOpts, withdrawal *crossdomain.Withdrawal, bn, finalizationPeriod *big.Int) error { func proveWithdrawalTransaction(c *contracts, cl *util.Clients, opts *bind.TransactOpts, withdrawal *crossdomain.Withdrawal, bn, finalizationPeriod *big.Int) error {
l2OutputIndex, outputRootProof, trieNodes, err := createOutput(withdrawal, c.L2OutputOracle, bn, cl) l2OutputIndex, outputRootProof, trieNodes, err := createOutput(withdrawal, c.L2OutputOracle, bn, cl)
if err != nil { if err != nil {
return err return err
...@@ -614,7 +613,7 @@ func proveWithdrawalTransaction(c *contracts, cl *clients, opts *bind.TransactOp ...@@ -614,7 +613,7 @@ func proveWithdrawalTransaction(c *contracts, cl *clients, opts *bind.TransactOp
func finalizeWithdrawalTransaction( func finalizeWithdrawalTransaction(
c *contracts, c *contracts,
cl *clients, cl *util.Clients,
opts *bind.TransactOpts, opts *bind.TransactOpts,
wd *crossdomain.LegacyWithdrawal, wd *crossdomain.LegacyWithdrawal,
withdrawal *crossdomain.Withdrawal, withdrawal *crossdomain.Withdrawal,
...@@ -699,67 +698,6 @@ func newContracts(ctx *cli.Context, l1Backend, l2Backend bind.ContractBackend) ( ...@@ -699,67 +698,6 @@ func newContracts(ctx *cli.Context, l1Backend, l2Backend bind.ContractBackend) (
}, nil }, nil
} }
// clients represents a set of initialized RPC clients
type clients struct {
L1Client *ethclient.Client
L2Client *ethclient.Client
L1RpcClient *rpc.Client
L2RpcClient *rpc.Client
L1GethClient *gethclient.Client
L2GethClient *gethclient.Client
}
// newClients will create new RPC clients
func newClients(ctx *cli.Context) (*clients, error) {
l1RpcURL := ctx.String("l1-rpc-url")
l1Client, err := ethclient.Dial(l1RpcURL)
if err != nil {
return nil, err
}
l1ChainID, err := l1Client.ChainID(context.Background())
if err != nil {
return nil, err
}
l2RpcURL := ctx.String("l2-rpc-url")
l2Client, err := ethclient.Dial(l2RpcURL)
if err != nil {
return nil, err
}
l2ChainID, err := l2Client.ChainID(context.Background())
if err != nil {
return nil, err
}
l1RpcClient, err := rpc.DialContext(context.Background(), l1RpcURL)
if err != nil {
return nil, err
}
l2RpcClient, err := rpc.DialContext(context.Background(), l2RpcURL)
if err != nil {
return nil, err
}
l1GethClient := gethclient.New(l1RpcClient)
l2GethClient := gethclient.New(l2RpcClient)
log.Info(
"Set up RPC clients",
"l1-chain-id", l1ChainID,
"l2-chain-id", l2ChainID,
)
return &clients{
L1Client: l1Client,
L2Client: l2Client,
L1RpcClient: l1RpcClient,
L2RpcClient: l2RpcClient,
L1GethClient: l1GethClient,
L2GethClient: l2GethClient,
}, nil
}
// newWithdrawals will create a set of legacy withdrawals // newWithdrawals will create a set of legacy withdrawals
func newWithdrawals(ctx *cli.Context, l1ChainID *big.Int) ([]*crossdomain.LegacyWithdrawal, error) { func newWithdrawals(ctx *cli.Context, l1ChainID *big.Int) ([]*crossdomain.LegacyWithdrawal, error) {
ovmMsgs := ctx.String("ovm-messages") ovmMsgs := ctx.String("ovm-messages")
...@@ -849,7 +787,7 @@ func createOutput( ...@@ -849,7 +787,7 @@ func createOutput(
withdrawal *crossdomain.Withdrawal, withdrawal *crossdomain.Withdrawal,
oracle *bindings.L2OutputOracle, oracle *bindings.L2OutputOracle,
blockNumber *big.Int, blockNumber *big.Int,
clients *clients, clients *util.Clients,
) (*big.Int, bindings.TypesOutputRootProof, [][]byte, error) { ) (*big.Int, bindings.TypesOutputRootProof, [][]byte, error) {
// compute the storage slot that the withdrawal is stored in // compute the storage slot that the withdrawal is stored in
slot, err := withdrawal.StorageSlot() slot, err := withdrawal.StorageSlot()
......
package util package util
import ( import (
"context"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/ethclient/gethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
"github.com/urfave/cli/v2"
) )
func ProgressLogger(n int, msg string) func(...any) { func ProgressLogger(n int, msg string) func(...any) {
...@@ -15,3 +23,171 @@ func ProgressLogger(n int, msg string) func(...any) { ...@@ -15,3 +23,171 @@ func ProgressLogger(n int, msg string) func(...any) {
log.Info(msg, append([]any{"count", i}, args...)...) log.Info(msg, append([]any{"count", i}, args...)...)
} }
} }
// clients represents a set of initialized RPC clients
type Clients struct {
L1Client *ethclient.Client
L2Client *ethclient.Client
L1RpcClient *rpc.Client
L2RpcClient *rpc.Client
L1GethClient *gethclient.Client
L2GethClient *gethclient.Client
}
// NewClients will create new RPC clients from a CLI context
func NewClients(ctx *cli.Context) (*Clients, error) {
l1RpcURL := ctx.String("l1-rpc-url")
l1Client, err := ethclient.Dial(l1RpcURL)
if err != nil {
return nil, err
}
l1ChainID, err := l1Client.ChainID(context.Background())
if err != nil {
return nil, err
}
l2RpcURL := ctx.String("l2-rpc-url")
l2Client, err := ethclient.Dial(l2RpcURL)
if err != nil {
return nil, err
}
l2ChainID, err := l2Client.ChainID(context.Background())
if err != nil {
return nil, err
}
l1RpcClient, err := rpc.DialContext(context.Background(), l1RpcURL)
if err != nil {
return nil, err
}
l2RpcClient, err := rpc.DialContext(context.Background(), l2RpcURL)
if err != nil {
return nil, err
}
l1GethClient := gethclient.New(l1RpcClient)
l2GethClient := gethclient.New(l2RpcClient)
log.Info(
"Set up RPC clients",
"l1-chain-id", l1ChainID,
"l2-chain-id", l2ChainID,
)
return &Clients{
L1Client: l1Client,
L2Client: l2Client,
L1RpcClient: l1RpcClient,
L2RpcClient: l2RpcClient,
L1GethClient: l1GethClient,
L2GethClient: l2GethClient,
}, nil
}
// ClientsFlags represent the flags associated with creating RPC clients.
var ClientsFlags = []cli.Flag{
&cli.StringFlag{
Name: "l1-rpc-url",
Required: true,
Usage: "L1 RPC URL",
EnvVars: []string{"L1_RPC_URL"},
},
&cli.StringFlag{
Name: "l2-rpc-url",
Required: true,
Usage: "L2 RPC URL",
EnvVars: []string{"L2_RPC_URL"},
},
}
// Addresses represents the address values of various contracts. The values can
// be easily populated via a [cli.Context].
type Addresses struct {
AddressManager common.Address
OptimismPortal common.Address
L1StandardBridge common.Address
L1CrossDomainMessenger common.Address
CanonicalTransactionChain common.Address
StateCommitmentChain common.Address
}
// AddressesFlags represent the flags associated with address parsing.
var AddressesFlags = []cli.Flag{
&cli.StringFlag{
Name: "address-manager-address",
Usage: "AddressManager address",
EnvVars: []string{"ADDRESS_MANAGER_ADDRESS"},
},
&cli.StringFlag{
Name: "optimism-portal-address",
Usage: "OptimismPortal address",
EnvVars: []string{"OPTIMISM_PORTAL_ADDRESS"},
},
&cli.StringFlag{
Name: "l1-standard-bridge-address",
Usage: "L1StandardBridge address",
EnvVars: []string{"L1_STANDARD_BRIDGE_ADDRESS"},
},
&cli.StringFlag{
Name: "l1-crossdomain-messenger-address",
Usage: "L1CrossDomainMessenger address",
EnvVars: []string{"L1_CROSSDOMAIN_MESSENGER_ADDRESS"},
},
&cli.StringFlag{
Name: "canonical-transaction-chain-address",
Usage: "CanonicalTransactionChain address",
EnvVars: []string{"CANONICAL_TRANSACTION_CHAIN_ADDRESS"},
},
&cli.StringFlag{
Name: "state-commitment-chain-address",
Usage: "StateCommitmentChain address",
EnvVars: []string{"STATE_COMMITMENT_CHAIN_ADDRESS"},
},
}
// NewAddresses populates an Addresses struct given a [cli.Context].
// This is useful for writing scripts that interact with smart contracts.
func NewAddresses(ctx *cli.Context) (*Addresses, error) {
var addresses Addresses
var err error
addresses.AddressManager, err = parseAddress(ctx, "address-manager-address")
if err != nil {
return nil, err
}
addresses.OptimismPortal, err = parseAddress(ctx, "optimism-portal-address")
if err != nil {
return nil, err
}
addresses.L1StandardBridge, err = parseAddress(ctx, "l1-standard-bridge-address")
if err != nil {
return nil, err
}
addresses.L1CrossDomainMessenger, err = parseAddress(ctx, "l1-crossdomain-messenger-address")
if err != nil {
return nil, err
}
addresses.CanonicalTransactionChain, err = parseAddress(ctx, "canonical-transaction-chain-address")
if err != nil {
return nil, err
}
addresses.StateCommitmentChain, err = parseAddress(ctx, "state-commitment-chain-address")
if err != nil {
return nil, err
}
return &addresses, nil
}
// parseAddress will parse a [common.Address] from a [cli.Context] and return
// an error if the configured address is not correct.
func parseAddress(ctx *cli.Context, name string) (common.Address, error) {
value := ctx.String(name)
if value == "" {
return common.Address{}, nil
}
if !common.IsHexAddress(value) {
return common.Address{}, fmt.Errorf("invalid address: %s", value)
}
return common.HexToAddress(value), nil
}
...@@ -338,27 +338,15 @@ func BuildBlocksValidator(log log.Logger, cfg *rollup.Config, runCfg GossipRunti ...@@ -338,27 +338,15 @@ func BuildBlocksValidator(log log.Logger, cfg *rollup.Config, runCfg GossipRunti
} }
func verifyBlockSignature(log log.Logger, cfg *rollup.Config, runCfg GossipRuntimeConfig, id peer.ID, signatureBytes []byte, payloadBytes []byte) pubsub.ValidationResult { func verifyBlockSignature(log log.Logger, cfg *rollup.Config, runCfg GossipRuntimeConfig, id peer.ID, signatureBytes []byte, payloadBytes []byte) pubsub.ValidationResult {
result := verifyBlockSignatureWithHasher(nil, cfg, runCfg, id, signatureBytes, payloadBytes, LegacyBlockSigningHash) signingHash, err := BlockSigningHash(cfg, payloadBytes)
if result != pubsub.ValidationAccept {
return verifyBlockSignatureWithHasher(log, cfg, runCfg, id, signatureBytes, payloadBytes, BlockSigningHash)
}
return result
}
func verifyBlockSignatureWithHasher(log log.Logger, cfg *rollup.Config, runCfg GossipRuntimeConfig, id peer.ID, signatureBytes []byte, payloadBytes []byte, hasher func(cfg *rollup.Config, payloadBytes []byte) (common.Hash, error)) pubsub.ValidationResult {
signingHash, err := hasher(cfg, payloadBytes)
if err != nil { if err != nil {
if log != nil { log.Warn("failed to compute block signing hash", "err", err, "peer", id)
log.Warn("failed to compute block signing hash", "err", err, "peer", id)
}
return pubsub.ValidationReject return pubsub.ValidationReject
} }
pub, err := crypto.SigToPub(signingHash[:], signatureBytes) pub, err := crypto.SigToPub(signingHash[:], signatureBytes)
if err != nil { if err != nil {
if log != nil { log.Warn("invalid block signature", "err", err, "peer", id)
log.Warn("invalid block signature", "err", err, "peer", id)
}
return pubsub.ValidationReject return pubsub.ValidationReject
} }
addr := crypto.PubkeyToAddress(*pub) addr := crypto.PubkeyToAddress(*pub)
...@@ -369,14 +357,10 @@ func verifyBlockSignatureWithHasher(log log.Logger, cfg *rollup.Config, runCfg G ...@@ -369,14 +357,10 @@ func verifyBlockSignatureWithHasher(log log.Logger, cfg *rollup.Config, runCfg G
// This means we may drop old payloads upon key rotation, // This means we may drop old payloads upon key rotation,
// but this can be recovered from like any other missed unsafe payload. // but this can be recovered from like any other missed unsafe payload.
if expected := runCfg.P2PSequencerAddress(); expected == (common.Address{}) { if expected := runCfg.P2PSequencerAddress(); expected == (common.Address{}) {
if log != nil { log.Warn("no configured p2p sequencer address, ignoring gossiped block", "peer", id, "addr", addr)
log.Warn("no configured p2p sequencer address, ignoring gossiped block", "peer", id, "addr", addr)
}
return pubsub.ValidationIgnore return pubsub.ValidationIgnore
} else if addr != expected { } else if addr != expected {
if log != nil { log.Warn("unexpected block author", "err", err, "peer", id, "addr", addr, "expected", expected)
log.Warn("unexpected block author", "err", err, "peer", id, "addr", addr, "expected", expected)
}
return pubsub.ValidationReject return pubsub.ValidationReject
} }
return pubsub.ValidationAccept return pubsub.ValidationAccept
......
...@@ -2,7 +2,6 @@ package p2p ...@@ -2,7 +2,6 @@ package p2p
import ( import (
"context" "context"
"crypto/ecdsa"
"math/big" "math/big"
"testing" "testing"
...@@ -42,21 +41,6 @@ func TestGuardGossipValidator(t *testing.T) { ...@@ -42,21 +41,6 @@ func TestGuardGossipValidator(t *testing.T) {
} }
func TestVerifyBlockSignature(t *testing.T) { func TestVerifyBlockSignature(t *testing.T) {
// Should accept signatures over both the legacy and updated signature hashes
tests := []struct {
name string
newSigner func(priv *ecdsa.PrivateKey) *LocalSigner
}{
{
name: "Legacy",
newSigner: newLegacyLocalSigner,
},
{
name: "Updated",
newSigner: NewLocalSigner,
},
}
logger := testlog.Logger(t, log.LvlCrit) logger := testlog.Logger(t, log.LvlCrit)
cfg := &rollup.Config{ cfg := &rollup.Config{
L2ChainID: big.NewInt(100), L2ChainID: big.NewInt(100),
...@@ -66,43 +50,37 @@ func TestVerifyBlockSignature(t *testing.T) { ...@@ -66,43 +50,37 @@ func TestVerifyBlockSignature(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
msg := []byte("any msg") msg := []byte("any msg")
for _, test := range tests { t.Run("Valid", func(t *testing.T) {
t.Run("Valid "+test.name, func(t *testing.T) { runCfg := &testutils.MockRuntimeConfig{P2PSeqAddress: crypto.PubkeyToAddress(secrets.SequencerP2P.PublicKey)}
runCfg := &testutils.MockRuntimeConfig{P2PSeqAddress: crypto.PubkeyToAddress(secrets.SequencerP2P.PublicKey)} signer := &PreparedSigner{Signer: NewLocalSigner(secrets.SequencerP2P)}
signer := &PreparedSigner{Signer: test.newSigner(secrets.SequencerP2P)} sig, err := signer.Sign(context.Background(), SigningDomainBlocksV1, cfg.L2ChainID, msg)
sig, err := signer.Sign(context.Background(), SigningDomainBlocksV1, cfg.L2ChainID, msg) require.NoError(t, err)
require.NoError(t, err) result := verifyBlockSignature(logger, cfg, runCfg, peerId, sig[:65], msg)
result := verifyBlockSignature(logger, cfg, runCfg, peerId, sig[:65], msg) require.Equal(t, pubsub.ValidationAccept, result)
require.Equal(t, pubsub.ValidationAccept, result) })
})
t.Run("WrongSigner "+test.name, func(t *testing.T) {
runCfg := &testutils.MockRuntimeConfig{P2PSeqAddress: common.HexToAddress("0x1234")}
signer := &PreparedSigner{Signer: test.newSigner(secrets.SequencerP2P)}
sig, err := signer.Sign(context.Background(), SigningDomainBlocksV1, cfg.L2ChainID, msg)
require.NoError(t, err)
result := verifyBlockSignature(logger, cfg, runCfg, peerId, sig[:65], msg)
require.Equal(t, pubsub.ValidationReject, result)
})
t.Run("InvalidSignature "+test.name, func(t *testing.T) { t.Run("WrongSigner", func(t *testing.T) {
runCfg := &testutils.MockRuntimeConfig{P2PSeqAddress: crypto.PubkeyToAddress(secrets.SequencerP2P.PublicKey)} runCfg := &testutils.MockRuntimeConfig{P2PSeqAddress: common.HexToAddress("0x1234")}
sig := make([]byte, 65) signer := &PreparedSigner{Signer: NewLocalSigner(secrets.SequencerP2P)}
result := verifyBlockSignature(logger, cfg, runCfg, peerId, sig, msg) sig, err := signer.Sign(context.Background(), SigningDomainBlocksV1, cfg.L2ChainID, msg)
require.Equal(t, pubsub.ValidationReject, result) require.NoError(t, err)
}) result := verifyBlockSignature(logger, cfg, runCfg, peerId, sig[:65], msg)
require.Equal(t, pubsub.ValidationReject, result)
})
t.Run("NoSequencer "+test.name, func(t *testing.T) { t.Run("InvalidSignature", func(t *testing.T) {
runCfg := &testutils.MockRuntimeConfig{} runCfg := &testutils.MockRuntimeConfig{P2PSeqAddress: crypto.PubkeyToAddress(secrets.SequencerP2P.PublicKey)}
signer := &PreparedSigner{Signer: test.newSigner(secrets.SequencerP2P)} sig := make([]byte, 65)
sig, err := signer.Sign(context.Background(), SigningDomainBlocksV1, cfg.L2ChainID, msg) result := verifyBlockSignature(logger, cfg, runCfg, peerId, sig, msg)
require.NoError(t, err) require.Equal(t, pubsub.ValidationReject, result)
result := verifyBlockSignature(logger, cfg, runCfg, peerId, sig[:65], msg) })
require.Equal(t, pubsub.ValidationIgnore, result)
})
}
}
func newLegacyLocalSigner(priv *ecdsa.PrivateKey) *LocalSigner { t.Run("NoSequencer", func(t *testing.T) {
return &LocalSigner{priv: priv, hasher: LegacySigningHash} runCfg := &testutils.MockRuntimeConfig{}
signer := &PreparedSigner{Signer: NewLocalSigner(secrets.SequencerP2P)}
sig, err := signer.Sign(context.Background(), SigningDomainBlocksV1, cfg.L2ChainID, msg)
require.NoError(t, err)
result := verifyBlockSignature(logger, cfg, runCfg, peerId, sig[:65], msg)
require.Equal(t, pubsub.ValidationIgnore, result)
})
} }
...@@ -20,21 +20,6 @@ type Signer interface { ...@@ -20,21 +20,6 @@ type Signer interface {
io.Closer io.Closer
} }
func LegacySigningHash(domain [32]byte, chainID *big.Int, payloadBytes []byte) (common.Hash, error) {
var msgInput [32 + 32 + 32]byte
// domain: first 32 bytes
copy(msgInput[:32], domain[:])
// chain_id: second 32 bytes
if chainID.BitLen() > 256 {
return common.Hash{}, errors.New("chain_id is too large")
}
chainID.FillBytes(msgInput[32:64])
// payload_hash: third 32 bytes, hash of encoded payload
copy(msgInput[32:], crypto.Keccak256(payloadBytes))
return crypto.Keccak256Hash(msgInput[:]), nil
}
func SigningHash(domain [32]byte, chainID *big.Int, payloadBytes []byte) (common.Hash, error) { func SigningHash(domain [32]byte, chainID *big.Int, payloadBytes []byte) (common.Hash, error) {
var msgInput [32 + 32 + 32]byte var msgInput [32 + 32 + 32]byte
// domain: first 32 bytes // domain: first 32 bytes
...@@ -54,10 +39,6 @@ func BlockSigningHash(cfg *rollup.Config, payloadBytes []byte) (common.Hash, err ...@@ -54,10 +39,6 @@ func BlockSigningHash(cfg *rollup.Config, payloadBytes []byte) (common.Hash, err
return SigningHash(SigningDomainBlocksV1, cfg.L2ChainID, payloadBytes) return SigningHash(SigningDomainBlocksV1, cfg.L2ChainID, payloadBytes)
} }
func LegacyBlockSigningHash(cfg *rollup.Config, payloadBytes []byte) (common.Hash, error) {
return LegacySigningHash(SigningDomainBlocksV1, cfg.L2ChainID, payloadBytes)
}
// LocalSigner is suitable for testing // LocalSigner is suitable for testing
type LocalSigner struct { type LocalSigner struct {
priv *ecdsa.PrivateKey priv *ecdsa.PrivateKey
......
MIT License
Copyright (c) 2023 Optimism
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
GITCOMMIT := $(shell git rev-parse HEAD)
GITDATE := $(shell git show -s --format='%ct')
VERSION := v0.0.0
LDFLAGSSTRING +=-X main.GitCommit=$(GITCOMMIT)
LDFLAGSSTRING +=-X main.GitDate=$(GITDATE)
LDFLAGSSTRING +=-X github.com/ethereum-optimism/optimism/op-program/version.Version=$(VERSION)
LDFLAGSSTRING +=-X github.com/ethereum-optimism/optimism/op-program/version.Meta=$(VERSION_META)
LDFLAGS := -ldflags "$(LDFLAGSSTRING)"
op-program:
env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build -v $(LDFLAGS) -o ./bin/op-program ./cmd/main.go
clean:
rm -rf bin
test:
go test -v ./...
lint:
golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell,errorlint -e "errors.As" -e "errors.Is"
.PHONY: \
op-program \
clean \
test \
lint
# op-program
Implements a fault proof program that runs through the rollup state-transition to verify an L2 output from L1 inputs.
This verifiable output can then resolve a disputed output on L1.
The program is designed such that it can be run in a deterministic way such that two invocations with the same input
data wil result in not only the same output, but the same program execution trace. This allows it to be run in an
on-chain VM as part of the dispute resolution process.
## Compiling
To build op-program, from within the `op-program` directory run:
```shell
make op-program
```
This resulting executable will be in `./bin/op-program`
## Testing
To run op-program unit tests, from within the `op-program` directory run:
```shell
make test
```
## Lint
To run the linter, from within the `op-program` directory run:
```shell
make lint
```
This requires having `golangci-lint` installed.
## Running
From within the `op-program` directory, options can be reviewed with:
```shell
./bin/op-program --help
```
package main
import (
"fmt"
"os"
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-program/config"
"github.com/ethereum-optimism/optimism/op-program/flags"
"github.com/ethereum-optimism/optimism/op-program/version"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli"
)
var (
GitCommit = ""
GitDate = ""
)
// VersionWithMeta holds the textual version string including the metadata.
var VersionWithMeta = func() string {
v := version.Version
if GitCommit != "" {
v += "-" + GitCommit[:8]
}
if GitDate != "" {
v += "-" + GitDate
}
if version.Meta != "" {
v += "-" + version.Meta
}
return v
}()
func main() {
args := os.Args
err := run(args, FaultProofProgram)
if err != nil {
log.Crit("Application failed", "message", err)
}
}
type ConfigAction func(log log.Logger, config *config.Config) error
// run parses the supplied args to create a config.Config instance, sets up logging
// then calls the supplied ConfigAction.
// This allows testing the translation from CLI arguments to Config
func run(args []string, action ConfigAction) error {
// Set up logger with a default INFO level in case we fail to parse flags,
// otherwise the final critical log won't show what the parsing error was.
oplog.SetupDefaults()
app := cli.NewApp()
app.Version = VersionWithMeta
app.Flags = flags.Flags
app.Name = "op-program"
app.Usage = "Optimism Fault Proof Program"
app.Description = "The Optimism Fault Proof Program fault proof program that runs through the rollup state-transition to verify an L2 output from L1 inputs."
app.Action = func(ctx *cli.Context) error {
logger, err := setupLogging(ctx)
if err != nil {
return err
}
logger.Info("Starting fault proof program", "version", VersionWithMeta)
cfg, err := config.NewConfigFromCLI(ctx)
if err != nil {
return err
}
return action(logger, cfg)
}
return app.Run(args)
}
func setupLogging(ctx *cli.Context) (log.Logger, error) {
logCfg := oplog.ReadCLIConfig(ctx)
if err := logCfg.Check(); err != nil {
return nil, fmt.Errorf("log config error: %w", err)
}
logger := oplog.NewLogger(logCfg)
return logger, nil
}
// FaultProofProgram is the programmatic entry-point for the fault proof program
func FaultProofProgram(log log.Logger, cfg *config.Config) error {
cfg.Rollup.LogDescription(log, chaincfg.L2ChainIDToNetworkName)
return nil
}
package main
import (
"encoding/json"
"os"
"testing"
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-program/config"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
)
func TestLogLevel(t *testing.T) {
t.Run("RejectInvalid", func(t *testing.T) {
verifyArgsInvalid(t, "unknown level: foo", addRequiredArgs("--log.level=foo"))
})
for _, lvl := range []string{"trace", "debug", "info", "error", "crit"} {
lvl := lvl
t.Run("AcceptValid_"+lvl, func(t *testing.T) {
logger, _, err := runWithArgs(addRequiredArgs("--log.level", lvl))
require.NoError(t, err)
require.NotNil(t, logger)
})
}
}
func TestDefaultCLIOptionsMatchDefaultConfig(t *testing.T) {
cfg := configForArgs(t, addRequiredArgs())
require.Equal(t, config.NewConfig(&chaincfg.Goerli), cfg)
}
func TestNetwork(t *testing.T) {
t.Run("Unknown", func(t *testing.T) {
verifyArgsInvalid(t, "invalid network bar", replaceRequiredArg("--network", "bar"))
})
t.Run("Required", func(t *testing.T) {
verifyArgsInvalid(t, "flag rollup.config or network is required", addRequiredArgsExcept("--network"))
})
t.Run("DisallowNetworkAndRollupConfig", func(t *testing.T) {
verifyArgsInvalid(t, "cannot specify both rollup.config and network", addRequiredArgs("--rollup.config=foo"))
})
t.Run("RollupConfig", func(t *testing.T) {
dir := t.TempDir()
configJson, err := json.Marshal(chaincfg.Goerli)
require.NoError(t, err)
configFile := dir + "/config.json"
err = os.WriteFile(configFile, configJson, os.ModePerm)
require.NoError(t, err)
cfg := configForArgs(t, addRequiredArgsExcept("--network", "--rollup.config", configFile))
require.Equal(t, chaincfg.Goerli, *cfg.Rollup)
})
for name, cfg := range chaincfg.NetworksByName {
name := name
expected := cfg
t.Run("Network_"+name, func(t *testing.T) {
cfg := configForArgs(t, replaceRequiredArg("--network", name))
require.Equal(t, expected, *cfg.Rollup)
})
}
}
func verifyArgsInvalid(t *testing.T, messageContains string, cliArgs []string) {
_, _, err := runWithArgs(cliArgs)
require.ErrorContains(t, err, messageContains)
}
func configForArgs(t *testing.T, cliArgs []string) *config.Config {
_, cfg, err := runWithArgs(cliArgs)
require.NoError(t, err)
return cfg
}
func runWithArgs(cliArgs []string) (log.Logger, *config.Config, error) {
var cfg *config.Config
var logger log.Logger
fullArgs := append([]string{"op-program"}, cliArgs...)
err := run(fullArgs, func(log log.Logger, config *config.Config) error {
logger = log
cfg = config
return nil
})
return logger, cfg, err
}
func addRequiredArgs(args ...string) []string {
req := requiredArgs()
combined := toArgList(req)
return append(combined, args...)
}
func addRequiredArgsExcept(name string, optionalArgs ...string) []string {
req := requiredArgs()
delete(req, name)
return append(toArgList(req), optionalArgs...)
}
func replaceRequiredArg(name string, value string) []string {
req := requiredArgs()
req[name] = value
return toArgList(req)
}
// requiredArgs returns map of argument names to values which are the minimal arguments required
// to create a valid Config
func requiredArgs() map[string]string {
return map[string]string{
"--network": "goerli",
}
}
func toArgList(req map[string]string) []string {
var combined []string
for name, value := range req {
combined = append(combined, name)
combined = append(combined, value)
}
return combined
}
package config
import (
"errors"
opnode "github.com/ethereum-optimism/optimism/op-node"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-program/flags"
"github.com/urfave/cli"
)
var (
ErrMissingRollupConfig = errors.New("missing rollup config")
)
type Config struct {
Rollup *rollup.Config
}
func (c *Config) Check() error {
if c.Rollup == nil {
return ErrMissingRollupConfig
}
if err := c.Rollup.Check(); err != nil {
return err
}
return nil
}
// NewConfig creates a Config with all optional values set to the CLI default value
func NewConfig(rollupCfg *rollup.Config) *Config {
return &Config{
Rollup: rollupCfg,
}
}
func NewConfigFromCLI(ctx *cli.Context) (*Config, error) {
if err := flags.CheckRequired(ctx); err != nil {
return nil, err
}
rollupCfg, err := opnode.NewRollupConfig(ctx)
if err != nil {
return nil, err
}
return &Config{
Rollup: rollupCfg,
}, nil
}
package config
import (
"testing"
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/stretchr/testify/require"
)
func TestDefaultConfigIsValid(t *testing.T) {
err := NewConfig(&chaincfg.Goerli).Check()
require.NoError(t, err)
}
func TestRollupConfig(t *testing.T) {
t.Run("Required", func(t *testing.T) {
err := NewConfig(nil).Check()
require.ErrorIs(t, err, ErrMissingRollupConfig)
})
t.Run("Valid", func(t *testing.T) {
err := NewConfig(&rollup.Config{}).Check()
require.ErrorIs(t, err, rollup.ErrBlockTimeZero)
})
}
package flags
import (
"fmt"
"strings"
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
service "github.com/ethereum-optimism/optimism/op-service"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/urfave/cli"
)
const envVarPrefix = "OP_PROGRAM"
var (
RollupConfig = cli.StringFlag{
Name: "rollup.config",
Usage: "Rollup chain parameters",
EnvVar: service.PrefixEnvVar(envVarPrefix, "ROLLUP_CONFIG"),
}
Network = cli.StringFlag{
Name: "network",
Usage: fmt.Sprintf("Predefined network selection. Available networks: %s", strings.Join(chaincfg.AvailableNetworks(), ", ")),
EnvVar: service.PrefixEnvVar(envVarPrefix, "NETWORK"),
}
)
// Flags contains the list of configuration options available to the binary.
var Flags []cli.Flag
var programFlags = []cli.Flag{
RollupConfig,
Network,
}
func init() {
Flags = append(Flags, oplog.CLIFlags(envVarPrefix)...)
Flags = append(Flags, programFlags...)
}
func CheckRequired(ctx *cli.Context) error {
rollupConfig := ctx.GlobalString(RollupConfig.Name)
network := ctx.GlobalString(Network.Name)
if rollupConfig == "" && network == "" {
return fmt.Errorf("flag %s or %s is required", RollupConfig.Name, Network.Name)
}
if rollupConfig != "" && network != "" {
return fmt.Errorf("cannot specify both %s and %s", RollupConfig.Name, Network.Name)
}
return nil
}
package flags
import (
"reflect"
"strings"
"testing"
)
// TestUniqueFlags asserts that all flag names are unique, to avoid accidental conflicts between the many flags.
func TestUniqueFlags(t *testing.T) {
seenCLI := make(map[string]struct{})
for _, flag := range Flags {
name := flag.GetName()
if _, ok := seenCLI[name]; ok {
t.Errorf("duplicate flag %s", name)
continue
}
seenCLI[name] = struct{}{}
}
}
func TestCorrectEnvVarPrefix(t *testing.T) {
for _, flag := range Flags {
values := reflect.ValueOf(flag)
envVarValue := values.FieldByName("EnvVar")
if envVarValue == (reflect.Value{}) {
t.Errorf("Failed to find EnvVar for flag %v", flag.GetName())
continue
}
envVar := envVarValue.String()
if envVar[:len("OP_PROGRAM_")] != "OP_PROGRAM_" {
t.Errorf("Flag %v env var (%v) does not start with OP_PROGRAM_", flag.GetName(), envVar)
}
if strings.Contains(envVar, "__") {
t.Errorf("Flag %v env var (%v) has duplicate underscores", flag.GetName(), envVar)
}
}
}
package version
var (
Version = "v0.10.14"
Meta = "dev"
)
...@@ -12,7 +12,7 @@ import { FeeVault } from "../universal/FeeVault.sol"; ...@@ -12,7 +12,7 @@ import { FeeVault } from "../universal/FeeVault.sol";
*/ */
contract L1FeeVault is FeeVault, Semver { contract L1FeeVault is FeeVault, Semver {
/** /**
* @custom:semver 1.0.0 * @custom:semver 1.1.0
* *
* @param _recipient Address that will receive the accumulated fees. * @param _recipient Address that will receive the accumulated fees.
*/ */
......
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { ethers } from 'ethers'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
await deploy({
hre,
name: 'L1Block',
args: [],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'DEPOSITOR_ACCOUNT',
ethers.utils.getAddress('0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001')
)
},
})
}
deployFn.tags = ['L1BlockImpl', 'l2']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { ethers } from 'ethers'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const Artifact__L1CrossDomainMessenger = await hre.companionNetworks[
'l1'
].deployments.get('L1CrossDomainMessengerProxy')
await deploy({
hre,
name: 'L2CrossDomainMessenger',
args: [Artifact__L1CrossDomainMessenger.address],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'OTHER_MESSENGER',
ethers.utils.getAddress(Artifact__L1CrossDomainMessenger.address)
)
},
})
}
deployFn.tags = ['L2CrossDomainMessengerImpl', 'l2']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { ethers } from 'ethers'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const Artifact__L1StandardBridge = await hre.companionNetworks[
'l1'
].deployments.get('L1StandardBridgeProxy')
await deploy({
hre,
name: 'L2StandardBridge',
args: [Artifact__L1StandardBridge.address],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'OTHER_BRIDGE',
ethers.utils.getAddress(Artifact__L1StandardBridge.address)
)
},
})
}
deployFn.tags = ['L2StandardBridgeImpl', 'l2']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
await deploy({
hre,
name: 'L2ToL1MessagePasser',
args: [],
postDeployAction: async (contract) => {
await assertContractVariable(contract, 'MESSAGE_VERSION', 1)
},
})
}
deployFn.tags = ['L2ToL1MessagePasserImpl', 'l2']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import { ethers } from 'ethers'
import { predeploys } from '../src/constants'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const Artifact__L1ERC721Bridge = await hre.companionNetworks[
'l1'
].deployments.get('L1ERC721BridgeProxy')
await deploy({
hre,
name: 'L2ERC721Bridge',
args: [predeploys.L2CrossDomainMessenger, Artifact__L1ERC721Bridge.address],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'MESSENGER',
ethers.utils.getAddress(predeploys.L2CrossDomainMessenger)
)
await assertContractVariable(
contract,
'OTHER_BRIDGE',
ethers.utils.getAddress(Artifact__L1ERC721Bridge.address)
)
},
})
}
deployFn.tags = ['L2ERC721BridgeImpl', 'l2']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
await deploy({
hre,
name: 'GasPriceOracle',
args: [],
postDeployAction: async (contract) => {
await assertContractVariable(contract, 'DECIMALS', 6)
},
})
}
deployFn.tags = ['GasPriceOracle', 'l2']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import { ethers } from 'ethers'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const l1 = hre.network.companionNetworks['l1']
const deployConfig = hre.getDeployConfig(l1)
const sequencerFeeVaultRecipient = deployConfig.sequencerFeeVaultRecipient
if (sequencerFeeVaultRecipient === ethers.constants.AddressZero) {
throw new Error(`SequencerFeeVault RECIPIENT undefined`)
}
await deploy({
hre,
name: 'SequencerFeeVault',
args: [sequencerFeeVaultRecipient],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'RECIPIENT',
ethers.utils.getAddress(sequencerFeeVaultRecipient)
)
},
})
}
deployFn.tags = ['SequencerFeeVaultImpl', 'l2']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import { ethers } from 'ethers'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const l1 = hre.network.companionNetworks['l1']
const deployConfig = hre.getDeployConfig(l1)
const baseFeeVaultRecipient = deployConfig.baseFeeVaultRecipient
if (baseFeeVaultRecipient === ethers.constants.AddressZero) {
throw new Error('BaseFeeVault RECIPIENT undefined')
}
await deploy({
hre,
name: 'BaseFeeVault',
args: [baseFeeVaultRecipient],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'RECIPIENT',
ethers.utils.getAddress(baseFeeVaultRecipient)
)
},
})
}
deployFn.tags = ['BaseFeeVaultImpl', 'l2']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import { ethers } from 'ethers'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const l1 = hre.network.companionNetworks['l1']
const deployConfig = hre.getDeployConfig(l1)
const l1FeeVaultRecipient = deployConfig.l1FeeVaultRecipient
if (l1FeeVaultRecipient === ethers.constants.AddressZero) {
throw new Error('L1FeeVault RECIPIENT undefined')
}
await deploy({
hre,
name: 'L1FeeVault',
args: [l1FeeVaultRecipient],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'RECIPIENT',
ethers.utils.getAddress(l1FeeVaultRecipient)
)
},
})
}
deployFn.tags = ['L1FeeVaultImpl', 'l2']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { ethers } from 'ethers'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import { assertContractVariable, deploy } from '../src/deploy-utils'
import { predeploys } from '../src/constants'
const deployFn: DeployFunction = async (hre) => {
await deploy({
hre,
name: 'OptimismMintableERC20Factory',
args: [predeploys.L2StandardBridge],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'BRIDGE',
ethers.utils.getAddress(predeploys.L2StandardBridge)
)
},
})
}
deployFn.tags = ['OptimismMintableERC20FactoryImpl', 'l2']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { ethers } from 'ethers'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import { assertContractVariable, deploy } from '../src/deploy-utils'
import { predeploys } from '../src/constants'
const deployFn: DeployFunction = async (hre) => {
const OptimismMintableERC721Factory = await hre.ethers.getContractAt(
'OptimismMintableERC721Factory',
predeploys.OptimismMintableERC721Factory
)
const remoteChainId = await OptimismMintableERC721Factory.REMOTE_CHAIN_ID()
await deploy({
hre,
name: 'OptimismMintableERC721Factory',
args: [predeploys.L2StandardBridge, remoteChainId],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'BRIDGE',
ethers.utils.getAddress(predeploys.L2StandardBridge)
)
await assertContractVariable(contract, 'REMOTE_CHAIN_ID', remoteChainId)
},
})
}
deployFn.tags = ['OptimismMintableERC721FactoryImpl', 'l2']
export default deployFn
...@@ -50,6 +50,17 @@ const config: HardhatUserConfig = { ...@@ -50,6 +50,17 @@ const config: HardhatUserConfig = {
chainId: 5, chainId: 5,
url: process.env.L1_RPC || '', url: process.env.L1_RPC || '',
accounts: [process.env.PRIVATE_KEY_DEPLOYER || ethers.constants.HashZero], accounts: [process.env.PRIVATE_KEY_DEPLOYER || ethers.constants.HashZero],
companionNetworks: {
l2: 'optimism-goerli',
},
},
'optimism-goerli': {
chainId: 420,
url: process.env.L2_RPC || '',
accounts: [process.env.PRIVATE_KEY_DEPLOYER || ethers.constants.HashZero],
companionNetworks: {
l1: 'goerli',
},
}, },
'alpha-1': { 'alpha-1': {
chainId: 5, chainId: 5,
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -8,6 +8,4 @@ import './solidity' ...@@ -8,6 +8,4 @@ import './solidity'
import './accounts' import './accounts'
import './check-l2' import './check-l2'
import './update-dynamic-oracle-config' import './update-dynamic-oracle-config'
import './wait-for-final-batch'
import './wait-for-final-deposit'
import './generate-deploy-config' import './generate-deploy-config'
This diff is collapsed.
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