Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -868,9 +868,14 @@ jobs:
module:
description: Go Module Name
type: string
uses_artifacts:
description: Uses contract artifacts
type: boolean
default: false
docker:
- image: <<pipeline.parameters.ci_builder_image>>
resource_class: xlarge
circleci_ip_ranges: true
steps:
- checkout
- restore_cache:
Expand All @@ -881,6 +886,10 @@ jobs:
keys:
- golang-build-cache-test-<<parameters.module>>-{{ checksum "go.sum" }}
- golang-build-cache-test-
- when:
condition: <<parameters.uses_artifacts>>
steps:
- attach_workspace: { at: "." }
- run:
name: Install components
command: |
Expand All @@ -892,7 +901,7 @@ jobs:
- run:
name: run tests
command: |
gotestsum --format=testname --junitfile=/tmp/test-results/<<parameters.module>>.xml --jsonfile=/tmp/testlogs/log.json \
ENABLE_ANVIL=true SEPOLIA_RPC_URL="https://ci-sepolia-l1.optimism.io" gotestsum --format=testname --junitfile=/tmp/test-results/<<parameters.module>>.xml --jsonfile=/tmp/testlogs/log.json \
-- -parallel=8 -coverpkg=github.com/ethereum-optimism/optimism/... -coverprofile=coverage.out ./...
working_directory: <<parameters.module>>
- save_cache:
Expand Down Expand Up @@ -1416,7 +1425,6 @@ workflows:
parameters:
module:
- op-batcher
- op-chain-ops
- op-node
- op-proposer
- op-challenger
Expand All @@ -1428,6 +1436,13 @@ workflows:
- go-test:
name: semver-natspec-tests
module: packages/contracts-bedrock/scripts/checks/semver-natspec
- go-test:
name: op-chain-ops-tests
module: op-chain-ops
uses_artifacts: true
requires:
- go-mod-download
- contracts-bedrock-build
- go-test-kurtosis:
name: op-chain-ops-integration
module: op-chain-ops
Expand Down
84 changes: 73 additions & 11 deletions op-chain-ops/deployer/integration_test/apply_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ import (
"log/slog"
"math/big"
"net/url"
"os"
"path"
"runtime"
"testing"
"time"

"github.com/ethereum-optimism/optimism/op-service/testutils/anvil"
crypto "github.com/ethereum/go-ethereum/crypto"

"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
"github.com/holiman/uint256"
Expand Down Expand Up @@ -65,11 +70,6 @@ func TestEndToEndApply(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

_, testFilename, _, ok := runtime.Caller(0)
require.Truef(t, ok, "failed to get test filename")
monorepoDir := path.Join(path.Dir(testFilename), "..", "..", "..")
artifactsDir := path.Join(monorepoDir, "packages", "contracts-bedrock", "forge-artifacts")

enclaveCtx := kurtosisutil.StartEnclave(t, ctx, lgr, "github.com/ethpandaops/ethereum-package", TestParams)

service, err := enclaveCtx.GetServiceContext("el-1-geth-lighthouse")
Expand All @@ -81,9 +81,6 @@ func TestEndToEndApply(t *testing.T) {
l1Client, err := ethclient.Dial(rpcURL)
require.NoError(t, err)

artifactsURL, err := url.Parse(fmt.Sprintf("file://%s", artifactsDir))
require.NoError(t, err)

depKey := new(deployerKey)
l1ChainID := big.NewInt(77799777)
dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic)
Expand All @@ -106,7 +103,7 @@ func TestEndToEndApply(t *testing.T) {
}

t.Run("initial chain", func(t *testing.T) {
intent, st := makeIntent(t, l1ChainID, artifactsURL, dk, id)
intent, st := makeIntent(t, l1ChainID, dk, id)

require.NoError(t, deployer.ApplyPipeline(
ctx,
Expand Down Expand Up @@ -149,7 +146,7 @@ func TestEndToEndApply(t *testing.T) {

t.Run("subsequent chain", func(t *testing.T) {
newID := uint256.NewInt(2)
intent, st := makeIntent(t, l1ChainID, artifactsURL, dk, newID)
intent, st := makeIntent(t, l1ChainID, dk, newID)
env.Workdir = t.TempDir()

require.NoError(t, deployer.ApplyPipeline(
Expand Down Expand Up @@ -182,10 +179,16 @@ func TestEndToEndApply(t *testing.T) {
func makeIntent(
t *testing.T,
l1ChainID *big.Int,
artifactsURL *url.URL,
dk *devkeys.MnemonicDevKeys,
l2ChainID *uint256.Int,
) (*state.Intent, *state.State) {
_, testFilename, _, ok := runtime.Caller(0)
require.Truef(t, ok, "failed to get test filename")
monorepoDir := path.Join(path.Dir(testFilename), "..", "..", "..")
artifactsDir := path.Join(monorepoDir, "packages", "contracts-bedrock", "forge-artifacts")
artifactsURL, err := url.Parse(fmt.Sprintf("file://%s", artifactsDir))
require.NoError(t, err)

addrFor := func(key devkeys.Key) common.Address {
addr, err := dk.Address(key)
require.NoError(t, err)
Expand Down Expand Up @@ -261,3 +264,62 @@ func validateOPChainDeployment(t *testing.T, ctx context.Context, l1Client *ethc
})
}
}

func TestApplyExistingOPCM(t *testing.T) {
anvil.Test(t)

forkRPCUrl := os.Getenv("SEPOLIA_RPC_URL")
if forkRPCUrl == "" {
t.Skip("no fork RPC URL provided")
}

lgr := testlog.Logger(t, slog.LevelDebug)

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()

runner, err := anvil.New(
forkRPCUrl,
lgr,
)
require.NoError(t, err)

require.NoError(t, runner.Start(ctx))
t.Cleanup(func() {
require.NoError(t, runner.Stop())
})

l1Client, err := ethclient.Dial(runner.RPCUrl())
require.NoError(t, err)

l1ChainID := big.NewInt(11155111)
dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic)
require.NoError(t, err)
// index 0 from Anvil's test set
priv, err := crypto.HexToECDSA("ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80")
require.NoError(t, err)
signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(priv, l1ChainID))
deployerAddr := common.HexToAddress("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266")

l2ChainID := uint256.NewInt(1)

env := &pipeline.Env{
Workdir: t.TempDir(),
L1Client: l1Client,
Signer: signer,
Deployer: deployerAddr,
Logger: lgr,
}

intent, st := makeIntent(t, l1ChainID, dk, l2ChainID)
intent.ContractsRelease = "op-contracts/v1.6.0"

require.NoError(t, deployer.ApplyPipeline(
ctx,
env,
intent,
st,
))

validateOPChainDeployment(t, ctx, l1Client, st)
}
48 changes: 46 additions & 2 deletions op-chain-ops/deployer/opcm/standard.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"embed"
"fmt"

"github.com/BurntSushi/toml"

"github.com/ethereum-optimism/superchain-registry/superchain"
"github.com/ethereum/go-ethereum/common"
)
Expand All @@ -14,6 +16,36 @@ var StandardVersionsMainnetData string
//go:embed standard-versions-sepolia.toml
var StandardVersionsSepoliaData string

var StandardVersionsSepolia StandardVersions

var StandardVersionsMainnet StandardVersions

type StandardVersions struct {
Releases map[string]StandardVersionsReleases `toml:"releases"`
}

type StandardVersionsReleases struct {
OptimismPortal StandardVersionRelease `toml:"optimism_portal"`
SystemConfig StandardVersionRelease `toml:"system_config"`
AnchorStateRegistry StandardVersionRelease `toml:"anchor_state_registry"`
DelayedWETH StandardVersionRelease `toml:"delayed_weth"`
DisputeGameFactory StandardVersionRelease `toml:"dispute_game_factory"`
FaultDisputeGame StandardVersionRelease `toml:"fault_dispute_game"`
PermissionedDisputeGame StandardVersionRelease `toml:"permissioned_dispute_game"`
MIPS StandardVersionRelease `toml:"mips"`
PreimageOracle StandardVersionRelease `toml:"preimage_oracle"`
L1CrossDomainMessenger StandardVersionRelease `toml:"l1_cross_domain_messenger"`
L1ERC721Bridge StandardVersionRelease `toml:"l1_erc721_bridge"`
L1StandardBridge StandardVersionRelease `toml:"l1_standard_bridge"`
OptimismMintableERC20Factory StandardVersionRelease `toml:"optimism_mintable_erc20_factory"`
}

type StandardVersionRelease struct {
Version string `toml:"version"`
ImplementationAddress common.Address `toml:"implementation_address"`
Address common.Address `toml:"address"`
}

var _ embed.FS

func StandardVersionsFor(chainID uint64) (string, error) {
Expand Down Expand Up @@ -41,8 +73,8 @@ func SuperchainFor(chainID uint64) (*superchain.Superchain, error) {
func ManagerImplementationAddrFor(chainID uint64) (common.Address, error) {
switch chainID {
case 11155111:
// Generated using the bootstrap command on 09/26/2024.
return common.HexToAddress("0x0dc727671d5c08e4e41e8909983ebfa6f57aa0bf"), nil
// Generated using the bootstrap command on 10/02/2024.
return common.HexToAddress("0x0f29118caed0f72873701bcc079398c594b6f8e4"), nil
default:
return common.Address{}, fmt.Errorf("unsupported chain ID: %d", chainID)
}
Expand All @@ -60,3 +92,15 @@ func ManagerOwnerAddrFor(chainID uint64) (common.Address, error) {
return common.Address{}, fmt.Errorf("unsupported chain ID: %d", chainID)
}
}

func init() {
StandardVersionsMainnet = StandardVersions{}
if err := toml.Unmarshal([]byte(StandardVersionsMainnetData), &StandardVersionsMainnet); err != nil {
panic(err)
}

StandardVersionsSepolia = StandardVersions{}
if err := toml.Unmarshal([]byte(StandardVersionsSepoliaData), &StandardVersionsSepolia); err != nil {
panic(err)
}
}
108 changes: 108 additions & 0 deletions op-service/testutils/anvil/anvil.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package anvil

import (
"bufio"
"context"
"fmt"
"io"
"os"
"os/exec"
"strconv"
"strings"
"sync"
"testing"

"github.com/ethereum/go-ethereum/log"
)

func Test(t *testing.T) {
if os.Getenv("ENABLE_ANVIL") == "" {
t.Skip("skipping Anvil test")
}
}

const AnvilPort = 31967

type Runner struct {
proc *exec.Cmd
stdout io.ReadCloser
stderr io.ReadCloser
logger log.Logger
startedCh chan struct{}
wg sync.WaitGroup
}

func New(l1RPCURL string, logger log.Logger) (*Runner, error) {
proc := exec.Command(
"anvil",
"--fork-url", l1RPCURL,
"--port",
strconv.Itoa(AnvilPort),
)
stdout, err := proc.StdoutPipe()
if err != nil {
return nil, err
}
stderr, err := proc.StderrPipe()
if err != nil {
return nil, err
}

return &Runner{
proc: proc,
stdout: stdout,
stderr: stderr,
logger: logger,
startedCh: make(chan struct{}, 1),
}, nil
}

func (r *Runner) Start(ctx context.Context) error {
if err := r.proc.Start(); err != nil {
return err
}

r.wg.Add(2)
go r.outputStream(r.stdout)
go r.outputStream(r.stderr)

select {
case <-r.startedCh:
return nil
case <-ctx.Done():
return ctx.Err()
}
}

func (r *Runner) Stop() error {
err := r.proc.Process.Signal(os.Interrupt)
if err != nil {
return err
}

// make sure the output streams close
defer r.wg.Wait()
return r.proc.Wait()
}

func (r *Runner) outputStream(stream io.ReadCloser) {
defer r.wg.Done()
scanner := bufio.NewScanner(stream)
listenLine := fmt.Sprintf("Listening on 127.0.0.1:%d", AnvilPort)
started := sync.OnceFunc(func() {
r.startedCh <- struct{}{}
})

for scanner.Scan() {
line := scanner.Text()
if strings.Contains(line, listenLine) {
started()
}

r.logger.Debug("[ANVIL] " + scanner.Text())
}
}

func (r *Runner) RPCUrl() string {
return fmt.Sprintf("http://localhost:%d", AnvilPort)
}