Commit da669074 authored by Matthew Slipper's avatar Matthew Slipper Committed by GitHub

op-chain-ops,op-node,devnet: Deploy contracts into L1 genesis (#3333)

parent 58975283
......@@ -221,7 +221,7 @@ jobs:
name: Check if we should run
command: |
shopt -s inherit_errexit
CHANGED=$(check-changed "op-(batcher|bindings|e2e|node|proposer)")
CHANGED=$(check-changed "op-(batcher|bindings|e2e|node|proposer|chain-ops)")
if [[ "$CHANGED" = "FALSE" ]]; then
circleci step halt
fi
......@@ -255,6 +255,11 @@ jobs:
command: |
golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell ./...
working_directory: op-service
- run:
name: lint op-chain-ops
command: |
golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell ./...
working_directory: op-chain-ops
- run:
name: prep results dir
command: mkdir -p /test-results
......@@ -283,6 +288,11 @@ jobs:
command: |
gotestsum --junitfile /test-results/op-service.xml -- -coverpkg=github.com/ethereum-optimism/optimism/... -coverprofile=coverage.out -covermode=atomic ./...
working_directory: op-service
- run:
name: test op-chain-ops
command: |
gotestsum --junitfile /test-results/op-chain-ops.xml -- -coverpkg=github.com/ethereum-optimism/optimism/... -coverprofile=coverage.out -covermode=atomic ./...
working_directory: op-chain-ops
- store_test_results:
path: /test-results
- run:
......@@ -497,19 +507,6 @@ jobs:
name: Bring up the stack
command: |
make devnet-up
- run:
name: Check L2 Config
command: npx hardhat check-l2-config --network devnetL1
working_directory: packages/contracts-bedrock
- run:
name: Do a deposit
command: |
timeout 5m npx hardhat deposit \
--to 0xB79f76EF2c5F0286176833E7B2eEe103b1CC3244 \
--amount-eth 1 \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
--network devnetL1
working_directory: packages/contracts-bedrock
- run:
name: Deposit ERC20 through the bridge
command: timeout 5m npx hardhat deposit-erc20 --network devnetL1
......@@ -809,6 +806,17 @@ workflows:
- gcr
requires:
- contracts-bedrock-tests
- docker-publish:
name: contract-artifacts-bedrock-publish-dev
docker_file: ops/docker/Dockerfile.packages
docker_tags: us-central1-docker.pkg.dev/bedrock-goerli-development/images/contract-artifacts-bedrock:<<pipeline.git.revision>>
target: contract-artifacts-bedrock
docker_context: .
repo: us-central1-docker.pkg.dev
context:
- gcr
requires:
- contracts-bedrock-tests
- hive-test:
name: hive-test-rpc
version: <<pipeline.git.revision>>
......
......@@ -126,10 +126,12 @@ tag-bedrock-go-modules:
git tag "op-bindings/$(VERSION)"
git tag "op-batcher/$(VERSION)"
git tag "op-service/$(VERSION)"
git tag "op-chain-ops/$(VERSION)"
git push $(BEDROCK_TAGS_REMOTE) "op-proposer/$(VERSION)"
git push $(BEDROCK_TAGS_REMOTE) "op-node/$(VERSION)"
git push $(BEDROCK_TAGS_REMOTE) "op-e2e/$(VERSION)"
git push $(BEDROCK_TAGS_REMOTE) "op-bindings/$(VERSION)"
git push $(BEDROCK_TAGS_REMOTE) "op-batcher/$(VERSION)"
git push $(BEDROCK_TAGS_REMOTE) "op-service/$(VERSION)"
git push $(BEDROCK_TAGS_REMOTE) "op-chain-ops/$(VERSION)"
......@@ -5,6 +5,7 @@ pkg := bindings
all: mkdir bindings deployed
bindings: l1block-bindings \
l1-cross-domain-messenger-bindings \
l1-standard-bridge-bindings \
l2-to-l1-message-passer-bindings \
optimism-portal-bindings \
......@@ -16,6 +17,8 @@ bindings: l1block-bindings \
sequencer-fee-vault-bindings \
optimism-mintable-erc20-factory-bindings \
optimism-mintable-erc20-bindings \
proxy-bindings \
proxy-admin-bindings \
erc20-bindings \
weth9-bindings
......@@ -27,6 +30,9 @@ deployed: l1-block-deployed \
l1-block-deployed: l1block-bindings
./gen_deployed_bytecode.sh L1Block $(pkg)
l1-cross-domain-messenger-bindings:
./gen_bindings.sh contracts/L1/L1CrossDomainMessenger.sol:L1CrossDomainMessenger $(pkg)
l1-standard-bridge-bindings:
./gen_bindings.sh contracts/L1/L1StandardBridge.sol:L1StandardBridge $(pkg)
......@@ -72,6 +78,12 @@ optimism-mintable-erc20-factory-bindings:
optimism-mintable-erc20-bindings:
./gen_bindings.sh contracts/universal/OptimismMintableERC20.sol:OptimismMintableERC20 $(pkg)
proxy-bindings:
./gen_bindings.sh contracts/universal/Proxy.sol:Proxy $(pkg)
proxy-admin-bindings:
./gen_bindings.sh contracts/universal/ProxyAdmin.sol:ProxyAdmin $(pkg)
erc20-bindings:
./gen_bindings.sh ERC20 $(pkg)
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This diff is collapsed.
package predeploys
import "github.com/ethereum/go-ethereum/common"
const (
DevL2OutputOracle = "0x6900000000000000000000000000000000000000"
DevOptimismPortal = "0x6900000000000000000000000000000000000001"
DevL1CrossDomainMessenger = "0x6900000000000000000000000000000000000002"
DevL1StandardBridge = "0x6900000000000000000000000000000000000003"
DevOptimismMintableERC20Factory = "0x6900000000000000000000000000000000000004"
DevAddressManager = "0x6900000000000000000000000000000000000005"
DevProxyAdmin = "0x6900000000000000000000000000000000000006"
)
var (
DevL2OutputOracleAddr = common.HexToAddress(DevL2OutputOracle)
DevOptimismPortalAddr = common.HexToAddress(DevOptimismPortal)
DevL1CrossDomainMessengerAddr = common.HexToAddress(DevL1CrossDomainMessenger)
DevL1StandardBridgeAddr = common.HexToAddress(DevL1StandardBridge)
DevOptimismMintableERC20FactoryAddr = common.HexToAddress(DevOptimismMintableERC20Factory)
DevAddressManagerAddr = common.HexToAddress(DevAddressManager)
DevProxyAdminAddr = common.HexToAddress(DevProxyAdmin)
DevPredeploys = make(map[string]*common.Address)
)
func init() {
DevPredeploys["L2OutputOracle"] = &DevL2OutputOracleAddr
DevPredeploys["OptimismPortal"] = &DevOptimismPortalAddr
DevPredeploys["L1CrossDomainMessenger"] = &DevL1CrossDomainMessengerAddr
DevPredeploys["L1StandardBridge"] = &DevL1StandardBridgeAddr
DevPredeploys["OptimismMintableERC20Factory"] = &DevOptimismMintableERC20FactoryAddr
DevPredeploys["AddressManager"] = &DevAddressManagerAddr
DevPredeploys["Admin"] = &DevProxyAdminAddr
}
......@@ -243,6 +243,9 @@ func Migrate(dataDir, outDir string, genesis *core.Genesis, addrLists, allowance
log.Info("trie dumping started", "root", root)
tr, err := backingStateDB.OpenTrie(root)
if err != nil {
return err
}
it := trie.NewIterator(tr.NodeIterator(nil))
totalMigrated := new(big.Int)
logAccountProgress := ProgressLogger(1000, "imported accounts")
......
package deployer
import (
"context"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
)
// TestKey is the same test key that geth uses
var TestKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
// ChainID is the chain id used for simulated backends
var ChainID = big.NewInt(1337)
var TestAddress = crypto.PubkeyToAddress(TestKey.PublicKey)
var thousandETH = new(big.Int).Mul(big.NewInt(params.Ether), big.NewInt(1000))
type Constructor struct {
Name string
Args []interface{}
}
type Deployment struct {
Name string
Bytecode hexutil.Bytes
Address common.Address
}
type Deployer func(*backends.SimulatedBackend, *bind.TransactOpts, Constructor) (common.Address, error)
func NewBackend() *backends.SimulatedBackend {
return backends.NewSimulatedBackendWithCacheConfig(
&core.CacheConfig{
Preimages: true,
},
core.GenesisAlloc{
crypto.PubkeyToAddress(TestKey.PublicKey): {Balance: thousandETH},
},
15000000,
)
}
func Deploy(backend *backends.SimulatedBackend, constructors []Constructor, cb Deployer) ([]Deployment, error) {
results := make([]Deployment, len(constructors))
opts, err := bind.NewKeyedTransactorWithChainID(TestKey, ChainID)
if err != nil {
return nil, err
}
opts.GasLimit = 15_000_000
for i, deployment := range constructors {
addr, err := cb(backend, opts, deployment)
if err != nil {
return nil, err
}
backend.Commit()
if addr == (common.Address{}) {
return nil, fmt.Errorf("no address for %s", deployment.Name)
}
code, err := backend.CodeAt(context.Background(), addr, nil)
if len(code) == 0 {
return nil, fmt.Errorf("no code found for %s", deployment.Name)
}
if err != nil {
return nil, fmt.Errorf("cannot fetch code for %s", deployment.Name)
}
results[i] = Deployment{
Name: deployment.Name,
Bytecode: code,
Address: addr,
}
}
return results, nil
}
......@@ -2,66 +2,73 @@ package genesis
import (
"encoding/json"
"math/big"
"os"
"path/filepath"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/hardhat"
"github.com/ethereum-optimism/optimism/op-chain-ops/state"
"github.com/ethereum/go-ethereum/common"
)
// DeployConfig represents the deployment configuration for Optimism
type DeployConfig struct {
L1StartingBlockTag rpc.BlockNumberOrHash `json:"l1StartingBlockTag"`
L1ChainID *big.Int `json:"l1ChainID"`
L2ChainID *big.Int `json:"l2ChainID"`
L2BlockTime uint `json:"l2BlockTime"`
MaxSequencerDrift uint `json:"maxSequencerDrift"`
SequencerWindowSize uint `json:"sequencerWindowSize"`
ChannelTimeout uint `json:"channelTimeout"`
L1StartingBlockTag *rpc.BlockNumberOrHash `json:"l1StartingBlockTag"`
L1ChainID uint64 `json:"l1ChainID"`
L2ChainID uint64 `json:"l2ChainID"`
L2BlockTime uint64 `json:"l2BlockTime"`
FinalizationPeriodSeconds uint64 `json:"finalizationPeriodSeconds"`
MaxSequencerDrift uint64 `json:"maxSequencerDrift"`
SequencerWindowSize uint64 `json:"sequencerWindowSize"`
ChannelTimeout uint64 `json:"channelTimeout"`
P2PSequencerAddress common.Address `json:"p2pSequencerAddress"`
OptimismL2FeeRecipient common.Address `json:"optimismL2FeeRecipient"`
BatchInboxAddress common.Address `json:"batchInboxAddress"`
BatchSenderAddress common.Address `json:"batchSenderAddress"`
L2OutputOracleSubmissionInterval uint `json:"l2OutputOracleSubmissionInterval"`
L2OutputOracleSubmissionInterval uint64 `json:"l2OutputOracleSubmissionInterval"`
L2OutputOracleStartingTimestamp int `json:"l2OutputOracleStartingTimestamp"`
L2OutputOracleProposer common.Address `json:"l2OutputOracleProposer"`
L2OutputOracleOwner common.Address `json:"l2OutputOracleOwner"`
L2OutputOracleGenesisL2Output common.Hash `json:"l2OutputOracleGenesisL2Output"`
L1BlockTime uint64 `json:"l1BlockTime"`
L1GenesisBlockTimestamp hexutil.Uint64 `json:"l1GenesisBlockTimestamp"`
L1GenesisBlockNonce hexutil.Uint64 `json:"l1GenesisBlockNonce"`
CliqueSignerAddress common.Address `json:"cliqueSignerAddress"`
L1GenesisBlockGasLimit hexutil.Uint64 `json:"l1GenesisBlockGasLimit"`
L1GenesisBlockDifficulty *hexutil.Big `json:"l1GenesisBlockDifficulty"`
L1GenesisBlockMixHash common.Hash `json:"l1GenesisBlockMixHash"`
L1GenesisBlockCoinbase common.Address `json:"l1GenesisBlockCoinbase"`
L1GenesisBlockNumber hexutil.Uint64 `json:"l1GenesisBlockNumber"`
L1GenesisBlockGasUsed hexutil.Uint64 `json:"l1GenesisBlockGasUsed"`
L1GenesisBlockParentHash common.Hash `json:"l1GenesisBlockParentHash"`
L1GenesisBlockBaseFeePerGas *hexutil.Big `json:"l1GenesisBlockBaseFeePerGas"`
L2GenesisBlockNonce hexutil.Uint64 `json:"l2GenesisBlockNonce"`
L2GenesisBlockExtraData hexutil.Bytes `json:"l2GenesisBlockExtraData"`
L2GenesisBlockGasLimit hexutil.Uint64 `json:"l2GenesisBlockGasLimit"`
L2GenesisBlockDifficulty *hexutil.Big `json:"l2GenesisBlockDifficulty"`
L2GenesisBlockMixHash common.Hash `json:"l2GenesisBlockMixHash"`
L2GenesisBlockCoinbase common.Address `json:"l2GenesisBlockCoinbase"`
L2GenesisBlockNumber hexutil.Uint64 `json:"l2GenesisBlockNumber"`
L2GenesisBlockGasUsed hexutil.Uint64 `json:"l2GenesisBlockGasUsed"`
L2GenesisBlockParentHash common.Hash `json:"l2GenesisBlockParentHash"`
L2GenesisBlockBaseFeePerGas *hexutil.Big `json:"l2GenesisBlockBaseFeePerGas"`
L2CrossDomainMessengerOwner common.Address `json:"l2CrossDomainMessengerOwner"`
OptimismBaseFeeRecipient common.Address `json:"optimismBaseFeeRecipient"`
OptimismL1FeeRecipient common.Address `json:"optimismL1FeeRecipient"`
GasPriceOracleOwner common.Address `json:"gasPriceOracleOwner"`
GasPriceOracleOverhead uint `json:"gasPriceOracleOverhead"`
GasPriceOracleScalar uint `json:"gasPriceOracleScalar"`
GasPriceOracleDecimals uint `json:"gasPriceOracleDecimals"`
L2CrossDomainMessengerOwner common.Address `json:"l2CrossDomainMessengerOwner"`
L2GenesisBlockNonce uint64 `json:"l2GenesisBlockNonce"`
L2GenesisBlockExtraData hexutil.Bytes `json:"l2GenesisBlockExtraData"`
L2GenesisBlockGasLimit uint64 `json:"l2GenesisBlockGasLimit"`
L2GenesisBlockDifficulty *big.Int `json:"l2GenesisBlockDifficulty"`
L2GenesisBlockMixHash common.Hash `json:"l2GenesisBlockMixHash"`
L2GenesisBlockCoinbase common.Address `json:"l2GenesisBlockCoinbase"`
L2GenesisBlockNumber uint64 `json:"l2GenesisBlockNumber"`
L2GenesisBlockGasUsed uint64 `json:"l2GenesisBlockGasUsed"`
L2GenesisBlockParentHash common.Hash `json:"l2GenesisBlockParentHash"`
L2GenesisBlockBaseFeePerGas *big.Int `json:"l2GenesisBlockBaseFeePerGas"`
L1GenesisBlockTimestamp uint64 `json:"l1GenesisBlockTimestamp"`
L1GenesisBlockNonce uint64 `json:"l1GenesisBlockNonce"`
L1GenesisBlockGasLimit uint64 `json:"l1GenesisBlockGasLimit"`
L1GenesisBlockDifficulty *big.Int `json:"l1GenesisBlockDifficulty"`
L1GenesisBlockMixHash common.Hash `json:"l1GenesisBlockMixHash"`
L1GenesisBlockCoinbase common.Address `json:"l1GenesisBlockCoinbase"`
L1GenesisBlockNumber uint64 `json:"l1GenesisBlockNumber"`
L1GenesisBlockGasUsed uint64 `json:"l1GenesisBlockGasUsed"`
L1GenesisBlockParentHash common.Hash `json:"l1GenesisBlockParentHash"`
L1GenesisBlockBaseFeePerGas *big.Int `json:"l1GenesisBlockBaseFeePerGas"`
DeploymentWaitConfirmations int `json:"deploymentWaitConfirmations"`
}
// NewDeployConfig reads a config file given a path on the filesystem.
......@@ -91,25 +98,11 @@ func NewDeployConfigWithNetwork(network, path string) (*DeployConfig, error) {
// contracts.
type StorageConfig map[string]state.StorageValues
// NewStorageConfig will create a StorageConfig given an instance of a
// NewL2StorageConfig will create a StorageConfig given an instance of a
// Hardhat and a DeployConfig.
func NewStorageConfig(hh *hardhat.Hardhat, config *DeployConfig, chain ethereum.ChainReader) (StorageConfig, error) {
func NewL2StorageConfig(config *DeployConfig, block *types.Block, proxyL1StandardBridge common.Address, proxyL1CrossDomainMessenger common.Address) (StorageConfig, error) {
storage := make(StorageConfig)
proxyL1StandardBridge, err := hh.GetDeployment("L1StandardBridgeProxy")
if err != nil {
return storage, err
}
proxyL1CrossDomainMessenger, err := hh.GetDeployment("L1CrossDomainMessengerProxy")
if err != nil {
return storage, err
}
block, err := getBlockFromTag(chain, config.L1StartingBlockTag)
if err != nil {
return storage, err
}
storage["L2ToL1MessagePasser"] = state.StorageValues{
"nonce": 0,
}
......@@ -122,7 +115,7 @@ func NewStorageConfig(hh *hardhat.Hardhat, config *DeployConfig, chain ethereum.
"_paused": false,
"xDomainMsgSender": "0x000000000000000000000000000000000000dEaD",
"msgNonce": 0,
"otherMessenger": proxyL1CrossDomainMessenger.Address,
"otherMessenger": proxyL1CrossDomainMessenger,
"blockedSystemAddresses": map[any]any{
predeploys.L2CrossDomainMessenger: true,
predeploys.L2ToL1MessagePasser: true,
......@@ -138,7 +131,7 @@ func NewStorageConfig(hh *hardhat.Hardhat, config *DeployConfig, chain ethereum.
"_initialized": true,
"_initializing": false,
"messenger": predeploys.L2CrossDomainMessenger,
"otherBridge": proxyL1StandardBridge.Address,
"otherBridge": proxyL1StandardBridge,
}
storage["SequencerFeeVault"] = state.StorageValues{
"l1FeeWallet": config.OptimismL1FeeRecipient,
......
package genesis
import (
"bytes"
"encoding/json"
"fmt"
"os"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rpc"
"github.com/stretchr/testify/require"
)
func TestConfigMarshalUnmarshal(t *testing.T) {
// NOTE: the l1 starting block tag is set to null
// in the test since the type is not JSON serializable.
// Rather than bloat the code by introducing a marshalable
// block tag type that's only used in test, I created a separate
// test to validate that the starting block tag unmarshals
// correctly.
b, err := os.ReadFile("testdata/test-deploy-config-full.json")
require.NoError(t, err)
dec := json.NewDecoder(bytes.NewReader(b))
decoded := new(DeployConfig)
require.NoError(t, dec.Decode(decoded))
encoded, err := json.MarshalIndent(decoded, "", " ")
require.NoError(t, err)
require.JSONEq(t, string(b), string(encoded))
}
func TestUnmarshalL1StartingBlockTag(t *testing.T) {
decoded := new(DeployConfig)
require.NoError(t, json.Unmarshal([]byte(`{"l1StartingBlockTag": "earliest"}`), decoded))
require.EqualValues(t, rpc.EarliestBlockNumber, *decoded.L1StartingBlockTag.BlockNumber)
h := "0x86c7263d87140ca7cd9bf1bc9e95a435a7a0efc0ae2afaf64920c5b59a6393d4"
require.NoError(t, json.Unmarshal([]byte(fmt.Sprintf(`{"l1StartingBlockTag": "%s"}`, h)), decoded))
require.EqualValues(t, common.HexToHash(h), *decoded.L1StartingBlockTag.BlockHash)
}
......@@ -5,7 +5,9 @@ import (
"math/big"
"time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
......@@ -13,13 +15,13 @@ import (
)
// NewL2Genesis will create a new L2 genesis
func NewL2Genesis(config *DeployConfig, chain ethereum.ChainReader) (*core.Genesis, error) {
if config.L2ChainID == nil {
func NewL2Genesis(config *DeployConfig, block *types.Block) (*core.Genesis, error) {
if config.L2ChainID == 0 {
return nil, errors.New("must define L2 ChainID")
}
optimismChainConfig := params.ChainConfig{
ChainID: config.L2ChainID,
ChainID: new(big.Int).SetUint64(config.L2ChainID),
HomesteadBlock: big.NewInt(0),
DAOForkBlock: nil,
DAOForkSupport: false,
......@@ -53,42 +55,37 @@ func NewL2Genesis(config *DeployConfig, chain ethereum.ChainReader) (*core.Genes
}
gasLimit := config.L2GenesisBlockGasLimit
if gasLimit == 0 {
gasLimit = uint64(15_000_000)
gasLimit = 15_000_000
}
baseFee := config.L2GenesisBlockBaseFeePerGas
if baseFee == nil {
baseFee = big.NewInt(params.InitialBaseFee)
baseFee = newHexBig(params.InitialBaseFee)
}
difficulty := config.L2GenesisBlockDifficulty
if difficulty == nil {
difficulty = big.NewInt(1)
}
block, err := getBlockFromTag(chain, config.L1StartingBlockTag)
if err != nil {
return nil, err
difficulty = newHexBig(1)
}
return &core.Genesis{
Config: &optimismChainConfig,
Nonce: config.L2GenesisBlockNonce,
Nonce: uint64(config.L2GenesisBlockNonce),
Timestamp: block.Time(),
ExtraData: extraData,
GasLimit: gasLimit,
Difficulty: difficulty,
GasLimit: uint64(gasLimit),
Difficulty: difficulty.ToInt(),
Mixhash: config.L2GenesisBlockMixHash,
Coinbase: config.L2GenesisBlockCoinbase,
Number: config.L2GenesisBlockNumber,
GasUsed: config.L2GenesisBlockGasUsed,
Number: uint64(config.L2GenesisBlockNumber),
GasUsed: uint64(config.L2GenesisBlockGasUsed),
ParentHash: config.L2GenesisBlockParentHash,
BaseFee: baseFee,
BaseFee: baseFee.ToInt(),
Alloc: map[common.Address]core.GenesisAccount{},
}, nil
}
// NewL1Genesis will create a new L1 genesis config
func NewL1Genesis(config *DeployConfig) (*core.Genesis, error) {
if config.L1ChainID == nil {
if config.L1ChainID == 0 {
return nil, errors.New("must define L1 ChainID")
}
......@@ -97,40 +94,40 @@ func NewL1Genesis(config *DeployConfig) (*core.Genesis, error) {
Period: config.L1BlockTime,
Epoch: 30000,
}
chainConfig.ChainID = config.L1ChainID
chainConfig.ChainID = uint642Big(config.L1ChainID)
gasLimit := config.L1GenesisBlockGasLimit
if gasLimit == 0 {
gasLimit = uint64(15_000_000)
gasLimit = 15_000_000
}
baseFee := config.L1GenesisBlockBaseFeePerGas
if baseFee == nil {
baseFee = big.NewInt(params.InitialBaseFee)
baseFee = newHexBig(params.InitialBaseFee)
}
difficulty := config.L1GenesisBlockDifficulty
if difficulty == nil {
difficulty = big.NewInt(1)
difficulty = newHexBig(1)
}
timestamp := config.L1GenesisBlockTimestamp
if timestamp == 0 {
timestamp = uint64(time.Now().Unix())
timestamp = hexutil.Uint64(time.Now().Unix())
}
extraData := append(append(make([]byte, 32), config.CliqueSignerAddress[:]...), make([]byte, crypto.SignatureLength)...)
return &core.Genesis{
Config: &chainConfig,
Nonce: config.L1GenesisBlockNonce,
Timestamp: timestamp,
Nonce: uint64(config.L1GenesisBlockNonce),
Timestamp: uint64(timestamp),
ExtraData: extraData,
GasLimit: gasLimit,
Difficulty: difficulty,
GasLimit: uint64(gasLimit),
Difficulty: difficulty.ToInt(),
Mixhash: config.L1GenesisBlockMixHash,
Coinbase: config.L1GenesisBlockCoinbase,
Number: config.L1GenesisBlockNumber,
GasUsed: config.L1GenesisBlockGasUsed,
Number: uint64(config.L1GenesisBlockNumber),
GasUsed: uint64(config.L1GenesisBlockGasUsed),
ParentHash: config.L1GenesisBlockParentHash,
BaseFee: baseFee,
BaseFee: baseFee.ToInt(),
Alloc: map[common.Address]core.GenesisAccount{},
}, nil
}
......@@ -16,10 +16,14 @@ import (
var (
// codeNamespace represents the namespace of implementations of predeploys
codeNamespace = common.HexToAddress("0xc0D3C0d3C0d3C0D3c0d3C0d3c0D3C0d3c0d30000")
// predeployNamespace represents the namespace of predeploys
predeployNamespace = common.HexToAddress("0x4200000000000000000000000000000000000000")
// bigPredeployNamespace represents the predeploy namespace as a big.Int
bigPredeployNamespace = new(big.Int).SetBytes(predeployNamespace.Bytes())
// l2PredeployNamespace represents the namespace of L2 predeploys
l2PredeployNamespace = common.HexToAddress("0x4200000000000000000000000000000000000000")
// l1PredeployNamespace represents the namespace of L1 predeploys
l1PredeployNamespace = common.HexToAddress("0x6900000000000000000000000000000000000000")
// bigL2PredeployNamespace represents the predeploy namespace as a big.Int
bigL2PredeployNamespace = new(big.Int).SetBytes(l2PredeployNamespace.Bytes())
// bigL1PredeployNamespace represents the predeploy namespace as a big.Int
bigL1PredeployNamespace = new(big.Int).SetBytes(l1PredeployNamespace.Bytes())
// bigCodeNamespace represents the predeploy namespace as a big.Int
bigCodeNameSpace = new(big.Int).SetBytes(codeNamespace.Bytes())
// implementationSlot represents the EIP 1967 implementation storage slot
......@@ -59,19 +63,26 @@ var DevAccounts = []common.Address{
var devBalance = hexutil.MustDecodeBig("0x200000000000000000000000000000000000000000000000000000000000000")
// AddressToCodeNamespace takes a predeploy address and computes
// the implmentation address that the implementation should be deployed at
// the implementation address that the implementation should be deployed at
func AddressToCodeNamespace(addr common.Address) (common.Address, error) {
bytesAddr := addr.Bytes()
if !bytes.Equal(bytesAddr[0:2], []byte{0x42, 0x00}) {
if !IsL1DevPredeploy(addr) && !IsL2DevPredeploy(addr) {
return common.Address{}, fmt.Errorf("cannot handle non predeploy: %s", addr)
}
bigAddress := new(big.Int).SetBytes(bytesAddr[18:])
bigAddress := new(big.Int).SetBytes(addr[18:])
num := new(big.Int).Or(bigCodeNameSpace, bigAddress)
return common.BigToAddress(num), nil
}
// getBlockFromTag will resolve a Block given an rpc block tag
func getBlockFromTag(chain ethereum.ChainReader, tag rpc.BlockNumberOrHash) (*types.Block, error) {
func IsL1DevPredeploy(addr common.Address) bool {
return bytes.Equal(addr[0:2], []byte{0x69, 0x00})
}
func IsL2DevPredeploy(addr common.Address) bool {
return bytes.Equal(addr[0:2], []byte{0x42, 0x00})
}
// GetBlockFromTag will resolve a Block given an rpc block tag
func GetBlockFromTag(chain ethereum.ChainReader, tag *rpc.BlockNumberOrHash) (*types.Block, error) {
if hash, ok := tag.Hash(); ok {
block, err := chain.BlockByHash(context.Background(), hash)
if err != nil {
......@@ -89,3 +100,14 @@ func getBlockFromTag(chain ethereum.ChainReader, tag rpc.BlockNumberOrHash) (*ty
return nil, fmt.Errorf("invalid block tag: %v", tag)
}
}
// uint642Big creates a new *big.Int from a uint64.
func uint642Big(in uint64) *big.Int {
return new(big.Int).SetUint64(in)
}
func newHexBig(in uint64) *hexutil.Big {
b := new(big.Int).SetUint64(in)
hb := hexutil.Big(*b)
return &hb
}
package genesis
import (
"errors"
"fmt"
"math/big"
"strings"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
"github.com/ethereum-optimism/optimism/op-chain-ops/hardhat"
"github.com/ethereum-optimism/optimism/op-chain-ops/state"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/trie"
)
// TODO(tynes): need bindings for all of the L1 contracts to be able
// to create a genesis file with the L1 contracts predeployed.
// This would speed up testing as deployments take time when
// running tests.
func BuildL1DeveloperGenesis(config *DeployConfig) (*core.Genesis, error) {
var proxies = []string{
"L2OutputOracleProxy",
"L1CrossDomainMessengerProxy",
"L1StandardBridgeProxy",
"OptimismPortalProxy",
"OptimismMintableERC20FactoryProxy",
}
var portalMeteringSlot = common.Hash{31: 0x01}
var zeroHash common.Hash
func BuildL1DeveloperGenesis(hh *hardhat.Hardhat, config *DeployConfig) (*core.Genesis, error) {
if config.L2OutputOracleStartingTimestamp != -1 {
return nil, errors.New("l2oo starting timestamp must be -1")
}
if config.L1GenesisBlockTimestamp == 0 {
return nil, errors.New("must specify l1 genesis block timestamp")
}
genesis, err := NewL1Genesis(config)
if err != nil {
return nil, err
}
db := state.NewMemoryStateDB(genesis)
backend := deployer.NewBackend()
deployments, err := deployL1Contracts(config, backend)
if err != nil {
return nil, err
}
depsByName := make(map[string]deployer.Deployment)
depsByAddr := make(map[common.Address]deployer.Deployment)
for _, dep := range deployments {
depsByName[dep.Name] = dep
depsByAddr[dep.Address] = dep
}
opts, err := bind.NewKeyedTransactorWithChainID(deployer.TestKey, deployer.ChainID)
if err != nil {
return nil, err
}
l2ooABI, err := bindings.L2OutputOracleMetaData.GetAbi()
if err != nil {
return nil, err
}
data, err := l2ooABI.Pack(
"initialize",
config.L2OutputOracleGenesisL2Output,
big.NewInt(0),
config.L2OutputOracleProposer,
config.L2OutputOracleOwner,
)
if err != nil {
return nil, err
}
if err := upgradeProxy(
backend,
opts,
depsByName["L2OutputOracleProxy"].Address,
depsByName["L2OutputOracle"].Address,
data,
); err != nil {
return nil, err
}
portalABI, err := bindings.OptimismPortalMetaData.GetAbi()
if err != nil {
return nil, err
}
data, err = portalABI.Pack("initialize")
if err != nil {
return nil, err
}
if err := upgradeProxy(
backend,
opts,
depsByName["OptimismPortalProxy"].Address,
depsByName["OptimismPortal"].Address,
data,
); err != nil {
return nil, err
}
l1XDMABI, err := bindings.L1CrossDomainMessengerMetaData.GetAbi()
if err != nil {
return nil, err
}
data, err = l1XDMABI.Pack("initialize")
if err != nil {
return nil, err
}
if err := upgradeProxy(
backend,
opts,
depsByName["L1CrossDomainMessengerProxy"].Address,
depsByName["L1CrossDomainMessenger"].Address,
data,
); err != nil {
return nil, err
}
l1SBrABI, err := bindings.L1StandardBridgeMetaData.GetAbi()
if err != nil {
return nil, err
}
data, err = l1SBrABI.Pack("initialize", predeploys.DevL1CrossDomainMessengerAddr)
if err != nil {
return nil, err
}
if err := upgradeProxy(
backend,
opts,
depsByName["L1StandardBridgeProxy"].Address,
depsByName["L1StandardBridge"].Address,
data,
); err != nil {
return nil, err
}
if err := upgradeProxy(
backend,
opts,
depsByName["OptimismMintableERC20FactoryProxy"].Address,
depsByName["OptimismMintableERC20Factory"].Address,
nil,
); err != nil {
return nil, err
}
backend.Commit()
memDB := state.NewMemoryStateDB(genesis)
if err := SetL1Proxies(hh, memDB, predeploys.DevProxyAdminAddr); err != nil {
return nil, err
}
FundDevAccounts(memDB)
SetPrecompileBalances(memDB)
for name, proxyAddr := range predeploys.DevPredeploys {
memDB.SetState(*proxyAddr, ImplementationSlot, depsByName[name].Address.Hash())
}
stateDB, err := backend.Blockchain().State()
if err != nil {
return nil, err
}
for _, dep := range deployments {
st := stateDB.StorageTrie(dep.Address)
iter := trie.NewIterator(st.NodeIterator(nil))
FundDevAccounts(db)
SetPrecompileBalances(db)
depAddr := dep.Address
if strings.HasSuffix(dep.Name, "Proxy") {
depAddr = *predeploys.DevPredeploys[strings.TrimSuffix(dep.Name, "Proxy")]
}
return db.Genesis(), nil
memDB.CreateAccount(depAddr)
memDB.SetCode(depAddr, dep.Bytecode)
for iter.Next() {
_, data, _, err := rlp.Split(iter.Value)
if err != nil {
return nil, err
}
key := common.BytesToHash(st.GetKey(iter.Key))
value := common.BytesToHash(data)
if depAddr == predeploys.DevOptimismPortalAddr && key == portalMeteringSlot {
// We need to manually set the block number in the resource
// metering storage slot to zero. Otherwise, deposits will
// revert.
copy(value[:24], zeroHash[:])
}
memDB.SetState(depAddr, key, value)
}
}
return memDB.Genesis(), nil
}
func deployL1Contracts(config *DeployConfig, backend *backends.SimulatedBackend) ([]deployer.Deployment, error) {
constructors := make([]deployer.Constructor, 0)
for _, proxy := range proxies {
constructors = append(constructors, deployer.Constructor{
Name: proxy,
})
}
constructors = append(constructors, []deployer.Constructor{
{
Name: "L2OutputOracle",
Args: []interface{}{
uint642Big(config.L2OutputOracleSubmissionInterval),
[32]byte(config.L2OutputOracleGenesisL2Output),
big.NewInt(0),
big.NewInt(0),
uint642Big(uint64(config.L1GenesisBlockTimestamp)),
uint642Big(config.L2BlockTime),
config.L2OutputOracleProposer,
config.L2OutputOracleOwner,
},
},
{
Name: "OptimismPortal",
Args: []interface{}{
uint642Big(config.FinalizationPeriodSeconds),
},
},
{
Name: "L1CrossDomainMessenger",
},
{
Name: "L1StandardBridge",
},
{
Name: "OptimismMintableERC20Factory",
},
{
Name: "AddressManager",
},
{
Name: "ProxyAdmin",
Args: []interface{}{
common.Address{19: 0x01},
},
},
}...)
return deployer.Deploy(backend, constructors, l1Deployer)
}
func l1Deployer(backend *backends.SimulatedBackend, opts *bind.TransactOpts, deployment deployer.Constructor) (common.Address, error) {
var addr common.Address
var err error
switch deployment.Name {
case "L2OutputOracle":
addr, _, _, err = bindings.DeployL2OutputOracle(
opts,
backend,
deployment.Args[0].(*big.Int),
deployment.Args[1].([32]byte),
deployment.Args[2].(*big.Int),
deployment.Args[3].(*big.Int),
deployment.Args[4].(*big.Int),
deployment.Args[5].(*big.Int),
deployment.Args[6].(common.Address),
deployment.Args[7].(common.Address),
)
case "OptimismPortal":
addr, _, _, err = bindings.DeployOptimismPortal(
opts,
backend,
predeploys.DevL2OutputOracleAddr,
deployment.Args[0].(*big.Int),
)
case "L1CrossDomainMessenger":
addr, _, _, err = bindings.DeployL1CrossDomainMessenger(
opts,
backend,
predeploys.DevOptimismPortalAddr,
)
case "L1StandardBridge":
addr, _, _, err = bindings.DeployL1StandardBridge(
opts,
backend,
predeploys.DevL1CrossDomainMessengerAddr,
)
case "OptimismMintableERC20Factory":
addr, _, _, err = bindings.DeployOptimismMintableERC20Factory(
opts,
backend,
predeploys.DevL1StandardBridgeAddr,
)
case "AddressManager":
addr, _, _, err = bindings.DeployAddressManager(
opts,
backend,
)
case "ProxyAdmin":
addr, _, _, err = bindings.DeployProxyAdmin(
opts,
backend,
common.Address{},
)
default:
if strings.HasSuffix(deployment.Name, "Proxy") {
addr, _, _, err = bindings.DeployProxy(opts, backend, deployer.TestAddress)
} else {
err = fmt.Errorf("unknown contract %s", deployment.Name)
}
}
return addr, err
}
func upgradeProxy(backend *backends.SimulatedBackend, opts *bind.TransactOpts, proxyAddr common.Address, implAddr common.Address, callData []byte) error {
proxy, err := bindings.NewProxy(proxyAddr, backend)
if err != nil {
return err
}
if callData == nil {
_, err = proxy.UpgradeTo(opts, implAddr)
} else {
_, err = proxy.UpgradeToAndCall(
opts,
implAddr,
callData,
)
}
if err == nil {
backend.Commit()
}
return err
}
package genesis
import (
"bytes"
"encoding/json"
"fmt"
"math/big"
"os"
"path/filepath"
"testing"
"time"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/hardhat"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/stretchr/testify/require"
)
func TestBuildL1DeveloperGenesis(t *testing.T) {
b, err := os.ReadFile("testdata/test-deploy-config-full.json")
require.NoError(t, err)
dec := json.NewDecoder(bytes.NewReader(b))
config := new(DeployConfig)
require.NoError(t, dec.Decode(config))
config.L1GenesisBlockTimestamp = hexutil.Uint64(time.Now().Unix())
tmpdir := filepath.Join(os.TempDir(), fmt.Sprintf("l2-test-%d", time.Now().Unix()))
require.NoError(t, Untar("testdata/artifacts.tar.gz", tmpdir))
hh, err := hardhat.New(
"goerli",
[]string{
filepath.Join(tmpdir, "contracts-bedrock"),
filepath.Join(tmpdir, "contracts-governance"),
},
[]string{"../../packages/contracts-bedrock/deployments"},
)
require.Nil(t, err)
genesis, err := BuildL1DeveloperGenesis(hh, config)
require.NoError(t, err)
sim := backends.NewSimulatedBackend(
genesis.Alloc,
15000000,
)
callOpts := &bind.CallOpts{}
oracle, err := bindings.NewL2OutputOracle(predeploys.DevL2OutputOracleAddr, sim)
require.NoError(t, err)
portal, err := bindings.NewOptimismPortal(predeploys.DevOptimismPortalAddr, sim)
require.NoError(t, err)
proposer, err := oracle.Proposer(callOpts)
require.NoError(t, err)
require.Equal(t, config.L2OutputOracleProposer, proposer)
owner, err := oracle.Owner(callOpts)
require.NoError(t, err)
require.Equal(t, config.L2OutputOracleOwner, owner)
// Same set of tests as exist in the deployment scripts
interval, err := oracle.SUBMISSIONINTERVAL(callOpts)
require.NoError(t, err)
require.EqualValues(t, config.L2OutputOracleSubmissionInterval, interval.Uint64())
histBlocks, err := oracle.HISTORICALTOTALBLOCKS(callOpts)
require.NoError(t, err)
require.EqualValues(t, 0, histBlocks.Uint64())
startBlock, err := oracle.STARTINGBLOCKNUMBER(callOpts)
require.NoError(t, err)
require.EqualValues(t, 0, startBlock.Uint64())
l2BlockTime, err := oracle.L2BLOCKTIME(callOpts)
require.NoError(t, err)
require.EqualValues(t, 2, l2BlockTime.Uint64())
oracleAddr, err := portal.L2ORACLE(callOpts)
require.NoError(t, err)
require.EqualValues(t, predeploys.DevL2OutputOracleAddr, oracleAddr)
msgr, err := bindings.NewL1CrossDomainMessenger(predeploys.DevL1CrossDomainMessengerAddr, sim)
require.NoError(t, err)
portalAddr, err := msgr.Portal(callOpts)
require.NoError(t, err)
require.Equal(t, predeploys.DevOptimismPortalAddr, portalAddr)
bridge, err := bindings.NewL1StandardBridge(predeploys.DevL1StandardBridgeAddr, sim)
require.NoError(t, err)
msgrAddr, err := bridge.Messenger(callOpts)
require.NoError(t, err)
require.Equal(t, predeploys.DevL1CrossDomainMessengerAddr, msgrAddr)
otherBridge, err := bridge.OtherBridge(callOpts)
require.NoError(t, err)
require.Equal(t, predeploys.L2StandardBridgeAddr, otherBridge)
factory, err := bindings.NewOptimismMintableERC20(predeploys.DevOptimismMintableERC20FactoryAddr, sim)
require.NoError(t, err)
bridgeAddr, err := factory.Bridge(callOpts)
require.NoError(t, err)
require.Equal(t, predeploys.DevL1StandardBridgeAddr, bridgeAddr)
// test that we can do deposits, etc.
priv, err := crypto.HexToECDSA("ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80")
require.NoError(t, err)
tOpts, err := bind.NewKeyedTransactorWithChainID(priv, deployer.ChainID)
require.NoError(t, err)
tOpts.Value = big.NewInt(0.001 * params.Ether)
tOpts.GasLimit = 1_000_000
_, err = bridge.DepositETH(tOpts, 200000, nil)
require.NoError(t, err)
}
......@@ -3,15 +3,22 @@ package genesis
import (
"github.com/ethereum-optimism/optimism/op-chain-ops/hardhat"
"github.com/ethereum-optimism/optimism/op-chain-ops/state"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/core"
)
// BuildOptimismDeveloperGenesis will build the developer Optimism Genesis
type L2Addresses struct {
ProxyAdmin common.Address
L1StandardBridgeProxy common.Address
L1CrossDomainMessengerProxy common.Address
}
// BuildL2DeveloperGenesis will build the developer Optimism Genesis
// Block. Suitable for devnets.
func BuildOptimismDeveloperGenesis(hh *hardhat.Hardhat, config *DeployConfig, chain ethereum.ChainReader) (*core.Genesis, error) {
genspec, err := NewL2Genesis(config, chain)
func BuildL2DeveloperGenesis(hh *hardhat.Hardhat, config *DeployConfig, l1StartBlock *types.Block, l2Addrs *L2Addresses) (*core.Genesis, error) {
genspec, err := NewL2Genesis(config, l1StartBlock)
if err != nil {
return nil, err
}
......@@ -21,20 +28,25 @@ func BuildOptimismDeveloperGenesis(hh *hardhat.Hardhat, config *DeployConfig, ch
FundDevAccounts(db)
SetPrecompileBalances(db)
return BuildOptimismGenesis(db, hh, config, chain)
return BuildL2Genesis(db, hh, config, l1StartBlock, l2Addrs)
}
// BuildOptimismGenesis will build the L2 Optimism Genesis Block
func BuildOptimismGenesis(db *state.MemoryStateDB, hh *hardhat.Hardhat, config *DeployConfig, chain ethereum.ChainReader) (*core.Genesis, error) {
// BuildL2Genesis will build the L2 Optimism Genesis Block
func BuildL2Genesis(db *state.MemoryStateDB, hh *hardhat.Hardhat, config *DeployConfig, l1Block *types.Block, l2Addrs *L2Addresses) (*core.Genesis, error) {
// TODO(tynes): need a function for clearing old, unused storage slots.
// Each deployed contract on L2 needs to have its existing storage
// inspected and then cleared if they are no longer used.
if err := SetProxies(hh, db); err != nil {
if err := SetL2Proxies(hh, db, l2Addrs.ProxyAdmin); err != nil {
return nil, err
}
storage, err := NewStorageConfig(hh, config, chain)
storage, err := NewL2StorageConfig(
config,
l1Block,
l2Addrs.L1StandardBridgeProxy,
l2Addrs.L1CrossDomainMessengerProxy,
)
if err != nil {
return nil, err
}
......
package genesis_test
import (
"context"
"encoding/json"
"flag"
"io/ioutil"
"fmt"
"math/big"
"os"
"path/filepath"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/core"
......@@ -25,18 +31,26 @@ func init() {
var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
func TestBuildOptimismGenesis(t *testing.T) {
func TestBuildL2DeveloperGenesis(t *testing.T) {
tmpdir := filepath.Join(os.TempDir(), fmt.Sprintf("l2-test-%d", time.Now().Unix()))
require.NoError(t, genesis.Untar("testdata/artifacts.tar.gz", tmpdir))
hh, err := hardhat.New(
"goerli",
[]string{
"../../packages/contracts-bedrock/artifacts",
"../../packages/contracts-governance/artifacts",
filepath.Join(tmpdir, "contracts-bedrock"),
filepath.Join(tmpdir, "contracts-governance"),
},
[]string{"../../packages/contracts-bedrock/deployments"},
)
require.Nil(t, err)
config, err := genesis.NewDeployConfig("../../packages/contracts-bedrock/deploy-config/devnetL1.json")
config, err := genesis.NewDeployConfig("./testdata/test-deploy-config-devnet-l1.json")
require.Nil(t, err)
proxyAdmin, err := hh.GetDeployment("ProxyAdmin")
require.Nil(t, err)
proxy, err := hh.GetArtifact("Proxy")
require.Nil(t, err)
backend := backends.NewSimulatedBackend(
......@@ -45,16 +59,14 @@ func TestBuildOptimismGenesis(t *testing.T) {
},
15000000,
)
gen, err := genesis.BuildOptimismDeveloperGenesis(hh, config, backend)
block, err := backend.BlockByNumber(context.Background(), common.Big0)
require.NoError(t, err)
gen, err := genesis.BuildL2DeveloperGenesis(hh, config, block, &genesis.L2Addresses{
ProxyAdmin: proxyAdmin.Address,
})
require.Nil(t, err)
require.NotNil(t, gen)
proxyAdmin, err := hh.GetDeployment("ProxyAdmin")
require.Nil(t, err)
proxy, err := hh.GetArtifact("Proxy")
require.Nil(t, err)
for name, address := range predeploys.Predeploys {
addr := *address
......@@ -74,6 +86,6 @@ func TestBuildOptimismGenesis(t *testing.T) {
if writeFile {
file, _ := json.MarshalIndent(gen, "", " ")
_ = ioutil.WriteFile("genesis.json", file, 0644)
_ = os.WriteFile("genesis.json", file, 0644)
}
}
......@@ -20,22 +20,30 @@ func FundDevAccounts(db vm.StateDB) {
}
}
// SetProxies will set each of the proxies in the state. It requires
// SetL2Proxies will set each of the proxies in the state. It requires
// a Proxy and ProxyAdmin deployment present so that the Proxy bytecode
// can be set in state and the ProxyAdmin can be set as the admin of the
// Proxy.
func SetProxies(hh *hardhat.Hardhat, db vm.StateDB) error {
func SetL2Proxies(hh *hardhat.Hardhat, db vm.StateDB, proxyAdminAddr common.Address) error {
return setProxies(hh, db, proxyAdminAddr, bigL2PredeployNamespace, 2048)
}
// SetL1Proxies will set each of the proxies in the state. It requires
// a Proxy and ProxyAdmin deployment present so that the Proxy bytecode
// can be set in state and the ProxyAdmin can be set as the admin of the
// Proxy.
func SetL1Proxies(hh *hardhat.Hardhat, db vm.StateDB, proxyAdminAddr common.Address) error {
return setProxies(hh, db, proxyAdminAddr, bigL1PredeployNamespace, 2048)
}
func setProxies(hh *hardhat.Hardhat, db vm.StateDB, proxyAdminAddr common.Address, namespace *big.Int, count uint64) error {
proxy, err := hh.GetArtifact("Proxy")
if err != nil {
return err
}
proxyAdmin, err := hh.GetDeployment("ProxyAdmin")
if err != nil {
return err
}
for i := uint64(0); i <= 2048; i++ {
bigAddr := new(big.Int).Or(bigPredeployNamespace, new(big.Int).SetUint64(i))
for i := uint64(0); i <= count; i++ {
bigAddr := new(big.Int).Or(namespace, new(big.Int).SetUint64(i))
addr := common.BigToAddress(bigAddr)
// There is no proxy at the governance token address
......@@ -45,7 +53,7 @@ func SetProxies(hh *hardhat.Hardhat, db vm.StateDB) error {
db.CreateAccount(addr)
db.SetCode(addr, proxy.DeployedBytecode)
db.SetState(addr, AdminSlot, proxyAdmin.Address.Hash())
db.SetState(addr, AdminSlot, proxyAdminAddr.Hash())
}
return nil
}
......@@ -78,7 +86,7 @@ func SetImplementations(hh *hardhat.Hardhat, db vm.StateDB, storage StorageConfi
if err != nil {
return err
}
// Set the implmentation slot in the predeploy proxy
// Set the implementation slot in the predeploy proxy
db.SetState(*address, ImplementationSlot, addr.Hash())
}
......@@ -138,14 +146,13 @@ func MigrateDepositHashes(hh *hardhat.Hardhat, db vm.StateDB) error {
ignore[encoded] = true
}
db.ForEachStorage(predeploys.L2ToL1MessagePasserAddr, func(key, value common.Hash) bool {
return db.ForEachStorage(predeploys.L2ToL1MessagePasserAddr, func(key, value common.Hash) bool {
if _, ok := ignore[key]; ok {
return true
}
// TODO(tynes): Do the value migration here
return true
})
return nil
}
// SetPrecompileBalances will set a single wei at each precompile address.
......
package genesis
import (
"archive/tar"
"compress/gzip"
"io"
"os"
"path/filepath"
)
func Untar(tarball, target string) error {
f, err := os.Open(tarball)
if err != nil {
return err
}
defer f.Close()
r, err := gzip.NewReader(f)
if err != nil {
return err
}
tarReader := tar.NewReader(r)
for {
header, err := tarReader.Next()
if err == io.EOF {
break
} else if err != nil {
return err
}
path := filepath.Join(target, header.Name)
info := header.FileInfo()
if info.IsDir() {
if err = os.MkdirAll(path, info.Mode()); err != nil {
return err
}
continue
}
file, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, info.Mode())
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(file, tarReader)
if err != nil {
return err
}
}
return nil
}
{
"l1StartingBlockTag": "earliest",
"l1ChainID": 900,
"l2ChainID": 901,
"l2BlockTime": 2,
"maxSequencerDrift": 100,
"sequencerWindowSize": 4,
"channelTimeout": 40,
"p2pSequencerAddress": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc",
"optimismL2FeeRecipient": "0xd9c09e21b57c98e58a80552c170989b426766aa7",
"batchInboxAddress": "0xff00000000000000000000000000000000000000",
"batchSenderAddress": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC",
"l2OutputOracleSubmissionInterval": 20,
"l2OutputOracleStartingTimestamp": -1,
"l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
"l2OutputOracleOwner": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
"l1BlockTime": 15,
"cliqueSignerAddress": "0xca062b0fd91172d89bcd4bb084ac4e21972cc467",
"optimismBaseFeeRecipient": "0xBcd4042DE499D14e55001CcbB24a551F3b954096",
"optimismL1FeeRecipient": "0x71bE63f3384f5fb98995898A86B02Fb2426c5788",
"deploymentWaitConfirmations": 1
}
{
"l1StartingBlockTag": null,
"l1ChainID": 901,
"l2ChainID": 902,
"l2BlockTime": 2,
"maxSequencerDrift": 20,
"sequencerWindowSize": 100,
"channelTimeout": 30,
"p2pSequencerAddress": "0x0000000000000000000000000000000000000000",
"optimismL2FeeRecipient": "0x42000000000000000000000000000000000000f0",
"batchInboxAddress": "0x42000000000000000000000000000000000000ff",
"batchSenderAddress": "0x0000000000000000000000000000000000000000",
"l2OutputOracleSubmissionInterval": 6,
"l2OutputOracleStartingTimestamp": -1,
"l2OutputOracleProposer": "0x7770000000000000000000000000000000000001",
"l2OutputOracleOwner": "0x7770000000000000000000000000000000000002",
"l1BlockTime": 15,
"l1GenesisBlockNonce": "0x0",
"cliqueSignerAddress": "0x0000000000000000000000000000000000000000",
"l1GenesisBlockGasLimit": "0xe4e1c0",
"l1GenesisBlockDifficulty": "0x1",
"finalizationPeriodSeconds": 2,
"l1GenesisBlockMixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"l1GenesisBlockCoinbase": "0x0000000000000000000000000000000000000000",
"l1GenesisBlockNumber": "0x0",
"l1GenesisBlockGasUsed": "0x0",
"l1GenesisBlockParentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"l1GenesisBlockTimestamp": "0x0",
"l1GenesisBlockBaseFeePerGas": "0x3b9aca00",
"l2GenesisBlockNonce": "0x0",
"l2GenesisBlockExtraData": "0x",
"l2GenesisBlockGasLimit": "0xe4e1c0",
"l2GenesisBlockDifficulty": "0x1",
"l2GenesisBlockMixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"l2GenesisBlockCoinbase": "0x42000000000000000000000000000000000000f0",
"l2OutputOracleGenesisL2Output": "0x0000000000000000000000000000000000000000000000000000000000000000",
"l2GenesisBlockNumber": "0x0",
"l2GenesisBlockGasUsed": "0x0",
"l2GenesisBlockParentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"l2GenesisBlockBaseFeePerGas": "0x3b9aca00",
"optimismBaseFeeRecipient": "0x42000000000000000000000000000000000000f1",
"optimismL1FeeRecipient": "0x0000000000000000000000000000000000000000",
"l2CrossDomainMessengerOwner": "0x42000000000000000000000000000000000000f2",
"gasPriceOracleOwner": "0x42000000000000000000000000000000000000f3",
"gasPriceOracleOverhead": 2100,
"gasPriceOracleScalar": 1000000,
"gasPriceOracleDecimals": 6,
"deploymentWaitConfirmations": 1
}
\ No newline at end of file
......@@ -52,3 +52,5 @@ require (
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
replace github.com/ethereum/go-ethereum v1.10.21 => github.com/ethereum-optimism/op-geth v0.0.0-20220904174542-4311f9d2cead
......@@ -26,7 +26,7 @@ type Hardhat struct {
artifacts []*Artifact
deployments []*Deployment
buildInfos []*BuildInfo
buildInfos []*BuildInfo //nolint:unused
}
// New creates a new `Hardhat` struct and reads all of the files from
......@@ -202,7 +202,7 @@ func (h *Hardhat) GetBuildInfo(name string) (*BuildInfo, error) {
for _, artifactPath := range h.ArtifactPaths {
fileSystem := os.DirFS(artifactPath)
fs.WalkDir(fileSystem, ".", func(path string, d fs.DirEntry, err error) error {
err := fs.WalkDir(fileSystem, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
......@@ -249,6 +249,9 @@ func (h *Hardhat) GetBuildInfo(name string) (*BuildInfo, error) {
return nil
})
if err != nil {
return nil, err
}
}
// TODO(tynes): handle multiple contracts with same name when required
......
......@@ -57,7 +57,7 @@ type Log struct {
}
// Artifact represents a hardhat compilation artifact
// The Bytecode and DeployedBytecode are not guranteed
// The Bytecode and DeployedBytecode are not guaranteed
// to be hexutil.Bytes when there are link references.
// In the future, custom json marshalling can be used
// to place the link reference values in the correct location.
......
package immutables
import (
"context"
"fmt"
"math/big"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
)
// testKey is the same test key that geth uses
var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
// chainID is the chain id used for simulated backends
var chainID = big.NewInt(1337)
// TODO(tynes): we are planning on making some constructor arguments
// into immutables before the final deployment of the system. This
// means that this struct will need to be updated with an additional
// parameter: Args []interface{}{} and each step will need to typecast
// each argument before doing the simulated deployment
type Deployment struct {
Name string
}
// DeploymentResults represents the output of deploying each of the
// contracts so that the immutables can be set properly in the bytecode.
type DeploymentResults map[string]hexutil.Bytes
......@@ -40,7 +21,7 @@ type DeploymentResults map[string]hexutil.Bytes
// a JSON file/cli flags and then populate the Deployment
// Args.
func BuildOptimism() (DeploymentResults, error) {
deployments := []Deployment{
deployments := []deployer.Constructor{
{
Name: "GasPriceOracle",
},
......@@ -63,52 +44,41 @@ func BuildOptimism() (DeploymentResults, error) {
Name: "OptimismMintableERC20Factory",
},
}
return Build(deployments)
return BuildL2(deployments)
}
// Build will deploy contracts to a simulated backend so that their immutables
// BuildL2 will deploy contracts to a simulated backend so that their immutables
// can be properly set. The bytecode returned in the results is suitable to be
// inserted into the state via state surgery.
func Build(deployments []Deployment) (DeploymentResults, error) {
backend := backends.NewSimulatedBackend(
core.GenesisAlloc{
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)},
},
15000000,
)
results := make(DeploymentResults)
opts, err := bind.NewKeyedTransactorWithChainID(testKey, chainID)
func BuildL2(constructors []deployer.Constructor) (DeploymentResults, error) {
deployments, err := deployer.Deploy(deployer.NewBackend(), constructors, l2Deployer)
if err != nil {
return nil, err
}
results := make(DeploymentResults)
for _, dep := range deployments {
results[dep.Name] = dep.Bytecode
}
return results, nil
}
for _, deployment := range deployments {
func l2Deployer(backend *backends.SimulatedBackend, opts *bind.TransactOpts, deployment deployer.Constructor) (common.Address, error) {
var addr common.Address
var err error
switch deployment.Name {
case "GasPriceOracle":
// The owner of the gas price oracle is not immutable, not required
// to be set here. It cannot be `address(0)`
owner := common.Address{1}
addr, _, _, err = bindings.DeployGasPriceOracle(opts, backend, owner)
if err != nil {
return nil, err
}
case "L1Block":
// No arguments required for the L1Block contract
addr, _, _, err = bindings.DeployL1Block(opts, backend)
if err != nil {
return nil, err
}
case "L2CrossDomainMessenger":
// The L1CrossDomainMessenger value is not immutable, no need to set
// it here correctly
l1CrossDomainMessenger := common.Address{}
addr, _, _, err = bindings.DeployL2CrossDomainMessenger(opts, backend, l1CrossDomainMessenger)
if err != nil {
return nil, err
}
case "L2StandardBridge":
// The OtherBridge value is not immutable, no need to set
otherBridge := common.Address{}
......@@ -122,22 +92,8 @@ func Build(deployments []Deployment) (DeploymentResults, error) {
case "OptimismMintableERC20Factory":
addr, _, _, err = bindings.DeployOptimismMintableERC20Factory(opts, backend, predeploys.L2StandardBridgeAddr)
default:
return nil, fmt.Errorf("unknown contract: %s", deployment.Name)
return addr, fmt.Errorf("unknown contract: %s", deployment.Name)
}
backend.Commit()
if addr == (common.Address{}) {
return nil, fmt.Errorf("no address for %s", deployment.Name)
}
code, err := backend.CodeAt(context.Background(), addr, nil)
if len(code) == 0 {
return nil, fmt.Errorf("no code found for %s", deployment.Name)
}
if err != nil {
return nil, fmt.Errorf("cannot fetch code for %s", deployment.Name)
}
results[deployment.Name] = code
}
return results, nil
return addr, err
}
......@@ -55,7 +55,7 @@ type StorageLayoutEntry struct {
Label string `json:"label"`
Offset uint `json:"offset"`
Slot uint `json:"slot,string"`
Type string `json"type"`
Type string `json:"type"`
}
type StorageLayoutType struct {
......@@ -73,7 +73,7 @@ type CompilerOutputEvm struct {
MethodIdentifiers map[string]string `json:"methodIdentifiers"`
}
// Object must be a string because its not guranteed to be
// Object must be a string because its not guaranteed to be
// a hex string
type CompilerOutputBytecode struct {
Object string `json:"object"`
......
......@@ -21,7 +21,7 @@ type EncodedStorage struct {
Value common.Hash
}
// EncodedStorage will encode a storage layout
// EncodeStorage will encode a storage layout
func EncodeStorage(entry solc.StorageLayoutEntry, value any, storageType solc.StorageLayoutType) ([]*EncodedStorage, error) {
if storageType.NumberOfBytes > 32 {
return nil, fmt.Errorf("%s is larger than 32 bytes", storageType.Encoding)
......
......@@ -624,4 +624,3 @@ func (_Testdata *TestdataSession) SetStorage(key [32]byte, value [32]byte) (*typ
func (_Testdata *TestdataTransactorSession) SetStorage(key [32]byte, value [32]byte) (*types.Transaction, error) {
return _Testdata.Contract.SetStorage(&_Testdata.TransactOpts, key, value)
}
......@@ -2,147 +2,120 @@ package genesis
import (
"encoding/json"
"fmt"
"math/big"
"os"
"strings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/urfave/cli"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum-optimism/optimism/op-chain-ops/hardhat"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/ethclient"
)
var Subcommands = cli.Commands{
{
Name: "devnet-l2",
Usage: "Initialized a new L2 devnet genesis file",
Name: "devnet",
Usage: "Initialize new L1 and L2 genesis files and rollup config suitable for a local devnet",
Flags: []cli.Flag{
cli.StringFlag{
Name: "artifacts",
Usage: "Comma delimeted list of hardhat artifact directories",
Usage: "Comma delimited list of hardhat artifact directories",
},
cli.StringFlag{
Name: "network",
Usage: "Name of hardhat deploy network",
Name: "deploy-config",
Usage: "Path to hardhat deploy config file",
},
cli.StringFlag{
Name: "deployments",
Usage: "Comma delimated list of hardhat deploy artifact directories",
Name: "outfile.l1",
Usage: "Path to L1 genesis output file",
},
cli.StringFlag{
Name: "deploy-config",
Usage: "Path to hardhat deploy config directory",
Name: "outfile.l2",
Usage: "Path to L2 genesis output file",
},
cli.StringFlag{
Name: "rpc-url",
Usage: "L1 RPC URL",
},
cli.StringFlag{
Name: "outfile",
Usage: "Path to file to write output to",
Name: "outfile.rollup",
Usage: "Path to rollup output file",
},
},
Action: func(ctx *cli.Context) error {
// Turn off logging for this command unless it is a critical
// error so that the output can be piped to jq
log.Root().SetHandler(
log.LvlFilterHandler(
log.LvlCrit,
log.StreamHandler(os.Stdout, log.TerminalFormat(true)),
),
)
artifact := ctx.String("artifacts")
artifacts := strings.Split(artifact, ",")
deployment := ctx.String("deployments")
deployments := strings.Split(deployment, ",")
network := ctx.String("network")
hh, err := hardhat.New(network, artifacts, deployments)
hh, err := hardhat.New("", artifacts, nil)
if err != nil {
return err
}
deployConfig := ctx.String("deploy-config")
config, err := genesis.NewDeployConfigWithNetwork(network, deployConfig)
config, err := genesis.NewDeployConfig(deployConfig)
if err != nil {
return err
}
rpcUrl := ctx.String("rpc-url")
client, err := ethclient.Dial(rpcUrl)
l1Genesis, err := genesis.BuildL1DeveloperGenesis(hh, config)
if err != nil {
return err
}
gen, err := genesis.BuildOptimismDeveloperGenesis(hh, config, client)
if err != nil {
return err
l1StartBlock := l1Genesis.ToBlock()
l2Addrs := &genesis.L2Addresses{
ProxyAdmin: predeploys.DevProxyAdminAddr,
L1StandardBridgeProxy: predeploys.DevL1StandardBridgeAddr,
L1CrossDomainMessengerProxy: predeploys.DevL1CrossDomainMessengerAddr,
}
file, err := json.MarshalIndent(gen, "", " ")
l2Genesis, err := genesis.BuildL2DeveloperGenesis(hh, config, l1StartBlock, l2Addrs)
if err != nil {
return err
}
outfile := ctx.String("outfile")
if outfile == "" {
fmt.Println(string(file))
} else {
if err := os.WriteFile(outfile, file, 0644); err != nil {
return err
}
}
return nil
},
},
{
Name: "devnet-l1",
Usage: "Initialized a new L1 devnet genesis file",
Flags: []cli.Flag{
cli.StringFlag{
Name: "network",
Usage: "Name of hardhat deploy network",
rollupConfig := &rollup.Config{
Genesis: rollup.Genesis{
L1: eth.BlockID{
Hash: l1StartBlock.Hash(),
Number: 0,
},
cli.StringFlag{
Name: "deploy-config",
Usage: "Path to hardhat deploy config directory",
L2: eth.BlockID{
Hash: l2Genesis.ToBlock().Hash(),
Number: 0,
},
cli.StringFlag{
Name: "outfile",
Usage: "Path to file to write output to",
L2Time: uint64(config.L1GenesisBlockTimestamp),
},
},
Action: func(ctx *cli.Context) error {
network := ctx.String("network")
deployConfig := ctx.String("deploy-config")
BlockTime: config.L2BlockTime,
MaxSequencerDrift: config.MaxSequencerDrift,
SeqWindowSize: config.SequencerWindowSize,
ChannelTimeout: config.ChannelTimeout,
L1ChainID: new(big.Int).SetUint64(config.L1ChainID),
L2ChainID: new(big.Int).SetUint64(config.L2ChainID),
P2PSequencerAddress: config.P2PSequencerAddress,
FeeRecipientAddress: config.OptimismL2FeeRecipient,
BatchInboxAddress: config.BatchInboxAddress,
BatchSenderAddress: config.BatchSenderAddress,
DepositContractAddress: predeploys.DevOptimismPortalAddr,
}
config, err := genesis.NewDeployConfigWithNetwork(network, deployConfig)
if err != nil {
if err := writeGenesisFile(ctx.String("outfile.l1"), l1Genesis); err != nil {
return err
}
gen, err := genesis.BuildL1DeveloperGenesis(config)
if err != nil {
if err := writeGenesisFile(ctx.String("outfile.l2"), l2Genesis); err != nil {
return err
}
return writeGenesisFile(ctx.String("outfile.rollup"), rollupConfig)
},
},
}
file, err := json.MarshalIndent(gen, "", " ")
func writeGenesisFile(outfile string, input interface{}) error {
f, err := os.OpenFile(outfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o755)
if err != nil {
return err
}
defer f.Close()
outfile := ctx.String("outfile")
if outfile == "" {
fmt.Println(string(file))
} else {
if err := os.WriteFile(outfile, file, 0644); err != nil {
return err
}
}
return nil
},
},
enc := json.NewEncoder(f)
enc.SetIndent("", " ")
return enc.Encode(input)
}
......@@ -62,14 +62,21 @@ mkdir -p ./.devnet
# Regenerate the L1 genesis file if necessary. The existence of the genesis
# file is used to determine if we need to recreate the devnet's state folder.
if [ ! -f $DEVNET/genesis-l1.json ]; then
echo "Regenerating L1 genesis."
if [ ! -f "$DEVNET/done" ]; then
echo "Regenerating genesis files"
TIMESTAMP=$(date +%s | xargs printf '0x%x')
cat "$CONTRACTS_BEDROCK/deploy-config/devnetL1.json" | jq -r ".l1GenesisBlockTimestamp = \"$TIMESTAMP\"" > /tmp/bedrock-devnet-deploy-config.json
(
cd "$OP_NODE"
go run cmd/main.go genesis devnet-l1 \
--network $NETWORK \
--deploy-config $CONTRACTS_BEDROCK/deploy-config \
--outfile $DEVNET/genesis-l1.json
go run cmd/main.go genesis devnet \
--artifacts "$CONTRACTS_BEDROCK/artifacts,$CONTRACTS_GOVERNANCE/artifacts" \
--deploy-config /tmp/bedrock-devnet-deploy-config.json \
--outfile.l1 $DEVNET/genesis-l1.json \
--outfile.l2 $DEVNET/genesis-l2.json \
--outfile.rollup $DEVNET/rollup.json
touch "$DEVNET/done"
)
fi
......@@ -82,34 +89,6 @@ fi
wait_up $L1_URL
)
# Deploy contracts using Hardhat.
if [ ! -d "$CONTRACTS_BEDROCK/deployments/$NETWORK" ]; then
(
echo "Deploying contracts."
cd "$CONTRACTS_BEDROCK"
yarn hardhat --network $NETWORK deploy
)
else
echo "Contracts already deployed, skipping."
fi
if [ ! -f "$DEVNET/genesis-l2.json" ]; then
(
echo "Creating L2 genesis file."
cd "$OP_NODE"
go run cmd/main.go genesis devnet-l2 \
--artifacts "$CONTRACTS_BEDROCK/artifacts,$CONTRACTS_GOVERNANCE/artifacts" \
--network $NETWORK \
--deployments "$CONTRACTS_BEDROCK/deployments" \
--deploy-config "$CONTRACTS_BEDROCK/deploy-config" \
--rpc-url http://localhost:8545 \
--outfile "$DEVNET/genesis-l2.json"
echo "Created L2 genesis."
)
else
echo "L2 genesis already exists."
fi
# Bring up L2.
(
cd ops-bedrock
......@@ -118,19 +97,7 @@ fi
wait_up $L2_URL
)
# Start putting together the rollup config.
if [ ! -f "$DEVNET/rollup.json" ]; then
(
echo "Building rollup config..."
cd "$CONTRACTS_BEDROCK"
npx hardhat --network $NETWORK rollup-config
mv rollup.json "$DEVNET/rollup.json"
)
else
echo "Rollup config already exists"
fi
L2OO_ADDRESS=$(jq -r .address < "$CONTRACTS_BEDROCK/deployments/$NETWORK/L2OutputOracleProxy.json")
L2OO_ADDRESS="0x6900000000000000000000000000000000000000"
SEQUENCER_GENESIS_HASH="$(jq -r '.l2.hash' < $DEVNET/rollup.json)"
SEQUENCER_BATCH_INBOX_ADDRESS="$(cat $DEVNET/rollup.json | jq -r '.batch_inbox_address')"
......
......@@ -67,6 +67,13 @@ WORKDIR /opt/optimism/packages/contracts
COPY ./ops/scripts/deployer.sh .
CMD ["yarn", "run", "deploy"]
FROM alpine:3.16.2 as contract-artifacts-bedrock
RUN mkdir -p /artifacts
WORKDIR /artifacts
COPY --from=base /opt/optimism/packages/contracts-bedrock/artifacts /artifacts/contracts-bedrock
COPY --from=base /opt/optimism/packages/contracts-governance/artifacts /artifacts/contracts-governance
CMD ["echo", "0"]
FROM base as deployer-bedrock
WORKDIR /opt/optimism/packages/contracts-bedrock
CMD ["yarn", "run", "deploy"]
......
{
"l1StartingBlockTag": "earliest",
"l1ChainID": 900,
"l2ChainID": 901,
"l2BlockTime": 2,
......@@ -16,6 +15,7 @@
"l2OutputOracleStartingTimestamp": -1,
"l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
"l2OutputOracleOwner": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
"l2GenesisBlockCoinbase": "0x42000000000000000000000000000000000000f0",
"l1BlockTime": 15,
"cliqueSignerAddress": "0xca062b0fd91172d89bcd4bb084ac4e21972cc467",
......
......@@ -130,17 +130,17 @@ export const CONTRACT_ADDRESSES: {
},
[L2ChainID.OPTIMISM_BEDROCK_LOCAL_DEVNET]: {
l1: {
AddressManager: '0x610178dA211FEF7D417bC0e6FeD39F05609AD788' as const,
AddressManager: '0x6900000000000000000000000000000000000005' as const,
L1CrossDomainMessenger:
'0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512' as const,
L1StandardBridge: '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0' as const,
'0x6900000000000000000000000000000000000002' as const,
L1StandardBridge: '0x6900000000000000000000000000000000000003' as const,
StateCommitmentChain:
'0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9' as const,
'0x0000000000000000000000000000000000000000' as const,
CanonicalTransactionChain:
'0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9' as const,
BondManager: '0x5FC8d32690cc91D4c39d9d3abcBD16989F875707' as const,
OptimismPortal: '0x0165878A594ca255338adfa4d48449f69242Eb8F' as const,
L2OutputOracle: '0x5FbDB2315678afecb367f032d93F642f64180aa3' as const,
'0x0000000000000000000000000000000000000000' as const,
BondManager: '0x0000000000000000000000000000000000000000' as const,
OptimismPortal: '0x6900000000000000000000000000000000000001' as const,
L2OutputOracle: '0x6900000000000000000000000000000000000000' as const,
},
l2: DEFAULT_L2_CONTRACT_ADDRESSES,
},
......
......@@ -2,14 +2,13 @@ import { task, types } from 'hardhat/config'
import { HardhatRuntimeEnvironment } from 'hardhat/types'
import '@nomiclabs/hardhat-ethers'
import 'hardhat-deploy'
import { predeploys } from '@eth-optimism/contracts-bedrock'
import {
predeploys,
getContractDefinition,
} from '@eth-optimism/contracts-bedrock'
import { Event, Contract, Wallet, providers, utils } from 'ethers'
import {
CrossChainMessenger,
StandardBridgeAdapter,
MessageStatus,
} from '../src'
import { CrossChainMessenger, MessageStatus, CONTRACT_ADDRESSES } from '../src'
const deployWETH9 = async (
hre: HardhatRuntimeEnvironment,
......@@ -18,7 +17,7 @@ const deployWETH9 = async (
const signers = await hre.ethers.getSigners()
const signer = signers[0]
const Artifact__WETH9 = await hre.deployments.getArtifact('WETH9')
const Artifact__WETH9 = await getContractDefinition('WETH9')
const Factory__WETH9 = new hre.ethers.ContractFactory(
Artifact__WETH9.abi,
Artifact__WETH9.bytecode,
......@@ -44,15 +43,16 @@ const createOptimismMintableERC20 = async (
L1ERC20: Contract,
l2Signer: Wallet
): Promise<Contract> => {
const Deployment__OptimismMintableERC20TokenFactory =
await hre.deployments.get('OptimismMintableERC20Factory')
const Artifact__OptimismMintableERC20Token = await getContractDefinition(
'OptimismMintableERC20'
)
const Artifact__OptimismMintableERC20Token =
await hre.deployments.getArtifact('OptimismMintableERC20')
const Artifact__OptimismMintableERC20TokenFactory =
await getContractDefinition('OptimismMintableERC20Factory')
const OptimismMintableERC20TokenFactory = await hre.ethers.getContractAt(
Deployment__OptimismMintableERC20TokenFactory.abi,
const OptimismMintableERC20TokenFactory = new Contract(
predeploys.OptimismMintableERC20Factory,
Artifact__OptimismMintableERC20TokenFactory.abi,
l2Signer
)
......@@ -80,13 +80,11 @@ const createOptimismMintableERC20 = async (
const l2WethAddress = event.args.remoteToken
console.log(`Deployed to ${l2WethAddress}`)
const contract = new Contract(
return new Contract(
l2WethAddress,
Artifact__OptimismMintableERC20Token.abi,
l2Signer
)
return contract
}
// TODO(tynes): this task could be modularized in the future
......@@ -130,61 +128,48 @@ task('deposit-erc20', 'Deposits WETH9 onto L2.')
l2Provider
)
const Deployment__L2OutputOracleProxy = await hre.deployments.get(
'L2OutputOracleProxy'
)
const l2ChainId = await l2Signer.getChainId()
const contractAddrs = CONTRACT_ADDRESSES[l2ChainId]
const Artifact__L2ToL1MessagePasser = await hre.deployments.getArtifact(
const Artifact__L2ToL1MessagePasser = await getContractDefinition(
'L2ToL1MessagePasser'
)
const Artifact__L2CrossDomainMessenger = await hre.deployments.getArtifact(
const Artifact__L2CrossDomainMessenger = await getContractDefinition(
'L2CrossDomainMessenger'
)
const Artifact__L2StandardBridge = await hre.deployments.getArtifact(
const Artifact__L2StandardBridge = await getContractDefinition(
'L2StandardBridge'
)
const Deployment__OptimismPortal = await hre.deployments.get(
const Artifact__OptimismPortal = await getContractDefinition(
'OptimismPortal'
)
const Deployment__OptimismPortalProxy = await hre.deployments.get(
'OptimismPortalProxy'
)
const Deployment__L1StandardBridgeProxy = await hre.deployments.get(
'L1StandardBridgeProxy'
)
const Deployment__L1CrossDomainMessenger = await hre.deployments.get(
const Artifact__L1CrossDomainMessenger = await getContractDefinition(
'L1CrossDomainMessenger'
)
const Deployment__L1CrossDomainMessengerProxy = await hre.deployments.get(
'L1CrossDomainMessengerProxy'
)
const Deployment__L1StandardBridge = await hre.deployments.get(
const Artifact__L1StandardBridge = await getContractDefinition(
'L1StandardBridge'
)
const OptimismPortal = new hre.ethers.Contract(
Deployment__OptimismPortalProxy.address,
Deployment__OptimismPortal.abi,
contractAddrs.l1.OptimismPortal,
Artifact__OptimismPortal.abi,
signer
)
const L1CrossDomainMessenger = new hre.ethers.Contract(
Deployment__L1CrossDomainMessengerProxy.address,
Deployment__L1CrossDomainMessenger.abi,
contractAddrs.l1.L1CrossDomainMessenger,
Artifact__L1CrossDomainMessenger.abi,
signer
)
const L1StandardBridge = new hre.ethers.Contract(
Deployment__L1StandardBridgeProxy.address,
Deployment__L1StandardBridge.abi,
contractAddrs.l1.L1StandardBridge,
Artifact__L1StandardBridge.abi,
signer
)
......@@ -207,23 +192,7 @@ task('deposit-erc20', 'Deposits WETH9 onto L2.')
l1SignerOrProvider: signer,
l2SignerOrProvider: l2Signer,
l1ChainId: await signer.getChainId(),
l2ChainId: await l2Signer.getChainId(),
bridges: {
Standard: {
Adapter: StandardBridgeAdapter,
l1Bridge: Deployment__L1StandardBridgeProxy.address,
l2Bridge: predeploys.L2StandardBridge,
},
},
contracts: {
l1: {
L1StandardBridge: Deployment__L1StandardBridgeProxy.address,
L1CrossDomainMessenger:
Deployment__L1CrossDomainMessengerProxy.address,
L2OutputOracle: Deployment__L2OutputOracleProxy.address,
OptimismPortal: Deployment__OptimismPortalProxy.address,
},
},
l2ChainId,
bedrock: true,
})
......
import { task, types } from 'hardhat/config'
import '@nomiclabs/hardhat-ethers'
import 'hardhat-deploy'
import { predeploys } from '@eth-optimism/contracts-bedrock'
import {
predeploys,
getContractDefinition,
} from '@eth-optimism/contracts-bedrock'
import { providers, utils } from 'ethers'
import {
CrossChainMessenger,
StandardBridgeAdapter,
MessageStatus,
} from '../src'
import { CrossChainMessenger, MessageStatus, CONTRACT_ADDRESSES } from '../src'
task('deposit-eth', 'Deposits WETH9 onto L2.')
.addParam(
......@@ -56,10 +55,6 @@ task('deposit-eth', 'Deposits WETH9 onto L2.')
const l2Provider = new providers.StaticJsonRpcProvider(args.l2ProviderUrl)
const Deployment__L2OutputOracleProxy = await hre.deployments.get(
'L2OutputOracleProxy'
)
// send to self if not specified
const to = args.to ? args.to : address
const amount = args.amount
......@@ -74,57 +69,48 @@ task('deposit-eth', 'Deposits WETH9 onto L2.')
l2Provider
)
const Artifact__L2ToL1MessagePasser = await hre.deployments.getArtifact(
const l2ChainId = await l2Signer.getChainId()
const contractAddrs = CONTRACT_ADDRESSES[l2ChainId]
const Artifact__L2ToL1MessagePasser = await getContractDefinition(
'L2ToL1MessagePasser'
)
const Artifact__L2CrossDomainMessenger = await hre.deployments.getArtifact(
const Artifact__L2CrossDomainMessenger = await getContractDefinition(
'L2CrossDomainMessenger'
)
const Artifact__L2StandardBridge = await hre.deployments.getArtifact(
const Artifact__L2StandardBridge = await getContractDefinition(
'L2StandardBridge'
)
const Deployment__OptimismPortal = await hre.deployments.get(
const Artifact__OptimismPortal = await getContractDefinition(
'OptimismPortal'
)
const Deployment__OptimismPortalProxy = await hre.deployments.get(
'OptimismPortalProxy'
)
const Deployment__L1StandardBridgeProxy = await hre.deployments.get(
'L1StandardBridgeProxy'
)
const Deployment__L1CrossDomainMessenger = await hre.deployments.get(
const Artifact__L1CrossDomainMessenger = await getContractDefinition(
'L1CrossDomainMessenger'
)
const Deployment__L1CrossDomainMessengerProxy = await hre.deployments.get(
'L1CrossDomainMessengerProxy'
)
const Deployment__L1StandardBridge = await hre.deployments.get(
const Artifact__L1StandardBridge = await getContractDefinition(
'L1StandardBridge'
)
const OptimismPortal = new hre.ethers.Contract(
Deployment__OptimismPortalProxy.address,
Deployment__OptimismPortal.abi,
contractAddrs.l1.OptimismPortal,
Artifact__OptimismPortal.abi,
signer
)
const L1CrossDomainMessenger = new hre.ethers.Contract(
Deployment__L1CrossDomainMessengerProxy.address,
Deployment__L1CrossDomainMessenger.abi,
contractAddrs.l1.L1CrossDomainMessenger,
Artifact__L1CrossDomainMessenger.abi,
signer
)
const L1StandardBridge = new hre.ethers.Contract(
Deployment__L1StandardBridgeProxy.address,
Deployment__L1StandardBridge.abi,
contractAddrs.l1.L1StandardBridge,
Artifact__L1StandardBridge.abi,
signer
)
......@@ -147,23 +133,7 @@ task('deposit-eth', 'Deposits WETH9 onto L2.')
l1SignerOrProvider: signer,
l2SignerOrProvider: l2Signer,
l1ChainId: await signer.getChainId(),
l2ChainId: await l2Signer.getChainId(),
bridges: {
Standard: {
Adapter: StandardBridgeAdapter,
l1Bridge: Deployment__L1StandardBridgeProxy.address,
l2Bridge: predeploys.L2StandardBridge,
},
},
contracts: {
l1: {
L1StandardBridge: Deployment__L1StandardBridgeProxy.address,
L1CrossDomainMessenger:
Deployment__L1CrossDomainMessengerProxy.address,
L2OutputOracle: Deployment__L2OutputOracleProxy.address,
OptimismPortal: Deployment__OptimismPortalProxy.address,
},
},
l2ChainId,
bedrock: true,
})
......@@ -238,15 +208,19 @@ task('deposit-eth', 'Deposits WETH9 onto L2.')
)
console.log('Waiting to be able to withdraw')
setInterval(async () => {
const interval = setInterval(async () => {
const currentStatus = await messenger.getMessageStatus(ethWithdrawReceipt)
console.log(`Message status: ${MessageStatus[currentStatus]}`)
}, 3000)
try {
await messenger.waitForMessageStatus(
ethWithdrawReceipt,
MessageStatus.READY_FOR_RELAY
)
} finally {
clearInterval(interval)
}
const ethFinalize = await messenger.finalizeMessage(ethWithdrawReceipt)
const ethFinalizeReceipt = await ethFinalize.wait()
......
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