From ce37f8067856989ec77d42d6bb1156d37b3fd5f8 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Thu, 16 Nov 2023 17:28:51 -0700 Subject: [PATCH 1/7] deploy: move to own package --- arbnode/node.go | 267 ---------------------------------- cmd/deploy/deploy.go | 3 +- deploy/deploy.go | 283 ++++++++++++++++++++++++++++++++++++ system_tests/common_test.go | 3 +- 4 files changed, 287 insertions(+), 269 deletions(-) create mode 100644 deploy/deploy.go diff --git a/arbnode/node.go b/arbnode/node.go index 0782e8ecb7..d7b1d83ba3 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -18,7 +18,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" @@ -37,11 +36,8 @@ import ( "github.com/offchainlabs/nitro/execution" "github.com/offchainlabs/nitro/execution/gethexec" "github.com/offchainlabs/nitro/solgen/go/bridgegen" - "github.com/offchainlabs/nitro/solgen/go/challengegen" - "github.com/offchainlabs/nitro/solgen/go/ospgen" "github.com/offchainlabs/nitro/solgen/go/precompilesgen" "github.com/offchainlabs/nitro/solgen/go/rollupgen" - "github.com/offchainlabs/nitro/solgen/go/upgrade_executorgen" "github.com/offchainlabs/nitro/staker" "github.com/offchainlabs/nitro/staker/validatorwallet" "github.com/offchainlabs/nitro/util/contracts" @@ -51,215 +47,6 @@ import ( "github.com/offchainlabs/nitro/wsbroadcastserver" ) -func andTxSucceeded(ctx context.Context, l1Reader *headerreader.HeaderReader, tx *types.Transaction, err error) error { - if err != nil { - return fmt.Errorf("error submitting tx: %w", err) - } - _, err = l1Reader.WaitForTxApproval(ctx, tx) - if err != nil { - return fmt.Errorf("error executing tx: %w", err) - } - return nil -} - -func deployBridgeCreator(ctx context.Context, l1Reader *headerreader.HeaderReader, auth *bind.TransactOpts, maxDataSize *big.Int) (common.Address, error) { - client := l1Reader.Client() - - /// deploy eth based templates - bridgeTemplate, tx, _, err := bridgegen.DeployBridge(auth, client) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return common.Address{}, fmt.Errorf("bridge deploy error: %w", err) - } - - seqInboxTemplate, tx, _, err := bridgegen.DeploySequencerInbox(auth, client, maxDataSize) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return common.Address{}, fmt.Errorf("sequencer inbox deploy error: %w", err) - } - - inboxTemplate, tx, _, err := bridgegen.DeployInbox(auth, client, maxDataSize) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return common.Address{}, fmt.Errorf("inbox deploy error: %w", err) - } - - rollupEventBridgeTemplate, tx, _, err := rollupgen.DeployRollupEventInbox(auth, client) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return common.Address{}, fmt.Errorf("rollup event bridge deploy error: %w", err) - } - - outboxTemplate, tx, _, err := bridgegen.DeployOutbox(auth, client) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return common.Address{}, fmt.Errorf("outbox deploy error: %w", err) - } - - ethBasedTemplates := rollupgen.BridgeCreatorBridgeContracts{ - Bridge: bridgeTemplate, - SequencerInbox: seqInboxTemplate, - Inbox: inboxTemplate, - RollupEventInbox: rollupEventBridgeTemplate, - Outbox: outboxTemplate, - } - - /// deploy ERC20 based templates - erc20BridgeTemplate, tx, _, err := bridgegen.DeployERC20Bridge(auth, client) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return common.Address{}, fmt.Errorf("bridge deploy error: %w", err) - } - - erc20InboxTemplate, tx, _, err := bridgegen.DeployERC20Inbox(auth, client, maxDataSize) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return common.Address{}, fmt.Errorf("inbox deploy error: %w", err) - } - - erc20RollupEventBridgeTemplate, tx, _, err := rollupgen.DeployERC20RollupEventInbox(auth, client) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return common.Address{}, fmt.Errorf("rollup event bridge deploy error: %w", err) - } - - erc20OutboxTemplate, tx, _, err := bridgegen.DeployERC20Outbox(auth, client) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return common.Address{}, fmt.Errorf("outbox deploy error: %w", err) - } - - erc20BasedTemplates := rollupgen.BridgeCreatorBridgeContracts{ - Bridge: erc20BridgeTemplate, - SequencerInbox: seqInboxTemplate, - Inbox: erc20InboxTemplate, - RollupEventInbox: erc20RollupEventBridgeTemplate, - Outbox: erc20OutboxTemplate, - } - - bridgeCreatorAddr, tx, _, err := rollupgen.DeployBridgeCreator(auth, client, ethBasedTemplates, erc20BasedTemplates) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return common.Address{}, fmt.Errorf("bridge creator deploy error: %w", err) - } - - return bridgeCreatorAddr, nil -} - -func deployChallengeFactory(ctx context.Context, l1Reader *headerreader.HeaderReader, auth *bind.TransactOpts) (common.Address, common.Address, error) { - client := l1Reader.Client() - osp0, tx, _, err := ospgen.DeployOneStepProver0(auth, client) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return common.Address{}, common.Address{}, fmt.Errorf("osp0 deploy error: %w", err) - } - - ospMem, _, _, err := ospgen.DeployOneStepProverMemory(auth, client) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return common.Address{}, common.Address{}, fmt.Errorf("ospMemory deploy error: %w", err) - } - - ospMath, _, _, err := ospgen.DeployOneStepProverMath(auth, client) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return common.Address{}, common.Address{}, fmt.Errorf("ospMath deploy error: %w", err) - } - - ospHostIo, _, _, err := ospgen.DeployOneStepProverHostIo(auth, client) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return common.Address{}, common.Address{}, fmt.Errorf("ospHostIo deploy error: %w", err) - } - - ospEntryAddr, tx, _, err := ospgen.DeployOneStepProofEntry(auth, client, osp0, ospMem, ospMath, ospHostIo) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return common.Address{}, common.Address{}, fmt.Errorf("ospEntry deploy error: %w", err) - } - - challengeManagerAddr, tx, _, err := challengegen.DeployChallengeManager(auth, client) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return common.Address{}, common.Address{}, fmt.Errorf("ospEntry deploy error: %w", err) - } - - return ospEntryAddr, challengeManagerAddr, nil -} - -func deployRollupCreator(ctx context.Context, l1Reader *headerreader.HeaderReader, auth *bind.TransactOpts, maxDataSize *big.Int) (*rollupgen.RollupCreator, common.Address, common.Address, common.Address, error) { - bridgeCreator, err := deployBridgeCreator(ctx, l1Reader, auth, maxDataSize) - if err != nil { - return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("bridge creator deploy error: %w", err) - } - - ospEntryAddr, challengeManagerAddr, err := deployChallengeFactory(ctx, l1Reader, auth) - if err != nil { - return nil, common.Address{}, common.Address{}, common.Address{}, err - } - - rollupAdminLogic, tx, _, err := rollupgen.DeployRollupAdminLogic(auth, l1Reader.Client()) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("rollup admin logic deploy error: %w", err) - } - - rollupUserLogic, tx, _, err := rollupgen.DeployRollupUserLogic(auth, l1Reader.Client()) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("rollup user logic deploy error: %w", err) - } - - rollupCreatorAddress, tx, rollupCreator, err := rollupgen.DeployRollupCreator(auth, l1Reader.Client()) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("rollup creator deploy error: %w", err) - } - - upgradeExecutor, tx, _, err := upgrade_executorgen.DeployUpgradeExecutor(auth, l1Reader.Client()) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("upgrade executor deploy error: %w", err) - } - - validatorUtils, tx, _, err := rollupgen.DeployValidatorUtils(auth, l1Reader.Client()) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("validator utils deploy error: %w", err) - } - - validatorWalletCreator, tx, _, err := rollupgen.DeployValidatorWalletCreator(auth, l1Reader.Client()) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("validator wallet creator deploy error: %w", err) - } - - l2FactoriesDeployHelper, tx, _, err := rollupgen.DeployDeployHelper(auth, l1Reader.Client()) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("deploy helper creator deploy error: %w", err) - } - - tx, err = rollupCreator.SetTemplates( - auth, - bridgeCreator, - ospEntryAddr, - challengeManagerAddr, - rollupAdminLogic, - rollupUserLogic, - upgradeExecutor, - validatorUtils, - validatorWalletCreator, - l2FactoriesDeployHelper, - ) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("rollup set template error: %w", err) - } - - return rollupCreator, rollupCreatorAddress, validatorUtils, validatorWalletCreator, nil -} - func GenerateRollupConfig(prod bool, wasmModuleRoot common.Hash, rollupOwner common.Address, chainConfig *params.ChainConfig, serializedChainConfig []byte, loserStakeEscrow common.Address) rollupgen.Config { var confirmPeriod uint64 if prod { @@ -287,60 +74,6 @@ func GenerateRollupConfig(prod bool, wasmModuleRoot common.Hash, rollupOwner com } } -func DeployOnL1(ctx context.Context, parentChainReader *headerreader.HeaderReader, deployAuth *bind.TransactOpts, batchPoster common.Address, authorizeValidators uint64, config rollupgen.Config, nativeToken common.Address, maxDataSize *big.Int) (*chaininfo.RollupAddresses, error) { - if config.WasmModuleRoot == (common.Hash{}) { - return nil, errors.New("no machine specified") - } - - rollupCreator, _, validatorUtils, validatorWalletCreator, err := deployRollupCreator(ctx, parentChainReader, deployAuth, maxDataSize) - if err != nil { - return nil, fmt.Errorf("error deploying rollup creator: %w", err) - } - - var validatorAddrs []common.Address - for i := uint64(1); i <= authorizeValidators; i++ { - validatorAddrs = append(validatorAddrs, crypto.CreateAddress(validatorWalletCreator, i)) - } - - deployParams := rollupgen.RollupCreatorRollupDeploymentParams{ - Config: config, - BatchPoster: batchPoster, - Validators: validatorAddrs, - MaxDataSize: maxDataSize, - NativeToken: nativeToken, - DeployFactoriesToL2: false, - MaxFeePerGasForRetryables: big.NewInt(0), // needed when utility factories are deployed - } - - tx, err := rollupCreator.CreateRollup( - deployAuth, - deployParams, - ) - if err != nil { - return nil, fmt.Errorf("error submitting create rollup tx: %w", err) - } - receipt, err := parentChainReader.WaitForTxApproval(ctx, tx) - if err != nil { - return nil, fmt.Errorf("error executing create rollup tx: %w", err) - } - info, err := rollupCreator.ParseRollupCreated(*receipt.Logs[len(receipt.Logs)-1]) - if err != nil { - return nil, fmt.Errorf("error parsing rollup created log: %w", err) - } - - return &chaininfo.RollupAddresses{ - Bridge: info.Bridge, - Inbox: info.InboxAddress, - SequencerInbox: info.SequencerInbox, - DeployedAt: receipt.BlockNumber.Uint64(), - Rollup: info.RollupAddress, - NativeToken: nativeToken, - UpgradeExecutor: info.UpgradeExecutor, - ValidatorUtils: validatorUtils, - ValidatorWalletCreator: validatorWalletCreator, - }, nil -} - type Config struct { Sequencer bool `koanf:"sequencer"` ParentChainReader headerreader.Config `koanf:"parent-chain-reader" reload:"hot"` diff --git a/cmd/deploy/deploy.go b/cmd/deploy/deploy.go index 0b72038908..afbcddec62 100644 --- a/cmd/deploy/deploy.go +++ b/cmd/deploy/deploy.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/offchainlabs/nitro/arbnode" "github.com/offchainlabs/nitro/cmd/util" + deploycode "github.com/offchainlabs/nitro/deploy" ) func main() { @@ -141,7 +142,7 @@ func main() { defer l1Reader.StopAndWait() nativeToken := common.HexToAddress(*nativeTokenAddressString) - deployedAddresses, err := arbnode.DeployOnL1( + deployedAddresses, err := deploycode.DeployOnL1( ctx, l1Reader, l1TransactionOpts, diff --git a/deploy/deploy.go b/deploy/deploy.go new file mode 100644 index 0000000000..abdf4c06ba --- /dev/null +++ b/deploy/deploy.go @@ -0,0 +1,283 @@ +package deploy + +import ( + "context" + "errors" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/offchainlabs/nitro/cmd/chaininfo" + "github.com/offchainlabs/nitro/solgen/go/bridgegen" + "github.com/offchainlabs/nitro/solgen/go/challengegen" + "github.com/offchainlabs/nitro/solgen/go/ospgen" + "github.com/offchainlabs/nitro/solgen/go/rollupgen" + "github.com/offchainlabs/nitro/solgen/go/upgrade_executorgen" + "github.com/offchainlabs/nitro/util/headerreader" +) + +func andTxSucceeded(ctx context.Context, l1Reader *headerreader.HeaderReader, tx *types.Transaction, err error) error { + if err != nil { + return fmt.Errorf("error submitting tx: %w", err) + } + _, err = l1Reader.WaitForTxApproval(ctx, tx) + if err != nil { + return fmt.Errorf("error executing tx: %w", err) + } + return nil +} + +func deployBridgeCreator(ctx context.Context, l1Reader *headerreader.HeaderReader, auth *bind.TransactOpts, maxDataSize *big.Int) (common.Address, error) { + client := l1Reader.Client() + + /// deploy eth based templates + bridgeTemplate, tx, _, err := bridgegen.DeployBridge(auth, client) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("bridge deploy error: %w", err) + } + + seqInboxTemplate, tx, _, err := bridgegen.DeploySequencerInbox(auth, client, maxDataSize) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("sequencer inbox deploy error: %w", err) + } + + inboxTemplate, tx, _, err := bridgegen.DeployInbox(auth, client, maxDataSize) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("inbox deploy error: %w", err) + } + + rollupEventBridgeTemplate, tx, _, err := rollupgen.DeployRollupEventInbox(auth, client) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("rollup event bridge deploy error: %w", err) + } + + outboxTemplate, tx, _, err := bridgegen.DeployOutbox(auth, client) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("outbox deploy error: %w", err) + } + + ethBasedTemplates := rollupgen.BridgeCreatorBridgeContracts{ + Bridge: bridgeTemplate, + SequencerInbox: seqInboxTemplate, + Inbox: inboxTemplate, + RollupEventInbox: rollupEventBridgeTemplate, + Outbox: outboxTemplate, + } + + /// deploy ERC20 based templates + erc20BridgeTemplate, tx, _, err := bridgegen.DeployERC20Bridge(auth, client) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("bridge deploy error: %w", err) + } + + erc20InboxTemplate, tx, _, err := bridgegen.DeployERC20Inbox(auth, client, maxDataSize) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("inbox deploy error: %w", err) + } + + erc20RollupEventBridgeTemplate, tx, _, err := rollupgen.DeployERC20RollupEventInbox(auth, client) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("rollup event bridge deploy error: %w", err) + } + + erc20OutboxTemplate, tx, _, err := bridgegen.DeployERC20Outbox(auth, client) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("outbox deploy error: %w", err) + } + + erc20BasedTemplates := rollupgen.BridgeCreatorBridgeContracts{ + Bridge: erc20BridgeTemplate, + SequencerInbox: seqInboxTemplate, + Inbox: erc20InboxTemplate, + RollupEventInbox: erc20RollupEventBridgeTemplate, + Outbox: erc20OutboxTemplate, + } + + bridgeCreatorAddr, tx, _, err := rollupgen.DeployBridgeCreator(auth, client, ethBasedTemplates, erc20BasedTemplates) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("bridge creator deploy error: %w", err) + } + + return bridgeCreatorAddr, nil +} + +func deployChallengeFactory(ctx context.Context, l1Reader *headerreader.HeaderReader, auth *bind.TransactOpts) (common.Address, common.Address, error) { + client := l1Reader.Client() + osp0, tx, _, err := ospgen.DeployOneStepProver0(auth, client) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return common.Address{}, common.Address{}, fmt.Errorf("osp0 deploy error: %w", err) + } + + ospMem, _, _, err := ospgen.DeployOneStepProverMemory(auth, client) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return common.Address{}, common.Address{}, fmt.Errorf("ospMemory deploy error: %w", err) + } + + ospMath, _, _, err := ospgen.DeployOneStepProverMath(auth, client) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return common.Address{}, common.Address{}, fmt.Errorf("ospMath deploy error: %w", err) + } + + ospHostIo, _, _, err := ospgen.DeployOneStepProverHostIo(auth, client) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return common.Address{}, common.Address{}, fmt.Errorf("ospHostIo deploy error: %w", err) + } + + challengeManagerAddr, tx, _, err := challengegen.DeployChallengeManager(auth, client) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return common.Address{}, common.Address{}, fmt.Errorf("challenge manager deploy error: %w", err) + } + + ospEntryAddr, tx, _, err := ospgen.DeployOneStepProofEntry(auth, client, osp0, ospMem, ospMath, ospHostIo) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return common.Address{}, common.Address{}, fmt.Errorf("ospEntry deploy error: %w", err) + } + + return ospEntryAddr, challengeManagerAddr, nil +} + +func deployRollupCreator(ctx context.Context, l1Reader *headerreader.HeaderReader, auth *bind.TransactOpts, maxDataSize *big.Int) (*rollupgen.RollupCreator, common.Address, common.Address, common.Address, error) { + bridgeCreator, err := deployBridgeCreator(ctx, l1Reader, auth, maxDataSize) + if err != nil { + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("bridge creator deploy error: %w", err) + } + + ospEntryAddr, challengeManagerAddr, err := deployChallengeFactory(ctx, l1Reader, auth) + if err != nil { + return nil, common.Address{}, common.Address{}, common.Address{}, err + } + + rollupAdminLogic, tx, _, err := rollupgen.DeployRollupAdminLogic(auth, l1Reader.Client()) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("rollup admin logic deploy error: %w", err) + } + + rollupUserLogic, tx, _, err := rollupgen.DeployRollupUserLogic(auth, l1Reader.Client()) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("rollup user logic deploy error: %w", err) + } + + rollupCreatorAddress, tx, rollupCreator, err := rollupgen.DeployRollupCreator(auth, l1Reader.Client()) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("rollup creator deploy error: %w", err) + } + + upgradeExecutor, tx, _, err := upgrade_executorgen.DeployUpgradeExecutor(auth, l1Reader.Client()) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("upgrade executor deploy error: %w", err) + } + + validatorUtils, tx, _, err := rollupgen.DeployValidatorUtils(auth, l1Reader.Client()) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("validator utils deploy error: %w", err) + } + + validatorWalletCreator, tx, _, err := rollupgen.DeployValidatorWalletCreator(auth, l1Reader.Client()) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("validator wallet creator deploy error: %w", err) + } + + l2FactoriesDeployHelper, tx, _, err := rollupgen.DeployDeployHelper(auth, l1Reader.Client()) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("deploy helper creator deploy error: %w", err) + } + + tx, err = rollupCreator.SetTemplates( + auth, + bridgeCreator, + ospEntryAddr, + challengeManagerAddr, + rollupAdminLogic, + rollupUserLogic, + upgradeExecutor, + validatorUtils, + validatorWalletCreator, + l2FactoriesDeployHelper, + ) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("rollup set template error: %w", err) + } + + return rollupCreator, rollupCreatorAddress, validatorUtils, validatorWalletCreator, nil +} + +func DeployOnL1(ctx context.Context, parentChainReader *headerreader.HeaderReader, deployAuth *bind.TransactOpts, batchPoster common.Address, authorizeValidators uint64, config rollupgen.Config, nativeToken common.Address, maxDataSize *big.Int) (*chaininfo.RollupAddresses, error) { + if config.WasmModuleRoot == (common.Hash{}) { + return nil, errors.New("no machine specified") + } + + rollupCreator, _, validatorUtils, validatorWalletCreator, err := deployRollupCreator(ctx, parentChainReader, deployAuth, maxDataSize) + if err != nil { + return nil, fmt.Errorf("error deploying rollup creator: %w", err) + } + + var validatorAddrs []common.Address + for i := uint64(1); i <= authorizeValidators; i++ { + validatorAddrs = append(validatorAddrs, crypto.CreateAddress(validatorWalletCreator, i)) + } + + deployParams := rollupgen.RollupCreatorRollupDeploymentParams{ + Config: config, + BatchPoster: batchPoster, + Validators: validatorAddrs, + MaxDataSize: maxDataSize, + NativeToken: nativeToken, + DeployFactoriesToL2: false, + MaxFeePerGasForRetryables: big.NewInt(0), // needed when utility factories are deployed + } + + tx, err := rollupCreator.CreateRollup( + deployAuth, + deployParams, + ) + if err != nil { + return nil, fmt.Errorf("error submitting create rollup tx: %w", err) + } + receipt, err := parentChainReader.WaitForTxApproval(ctx, tx) + if err != nil { + return nil, fmt.Errorf("error executing create rollup tx: %w", err) + } + info, err := rollupCreator.ParseRollupCreated(*receipt.Logs[len(receipt.Logs)-1]) + if err != nil { + return nil, fmt.Errorf("error parsing rollup created log: %w", err) + } + + return &chaininfo.RollupAddresses{ + Bridge: info.Bridge, + Inbox: info.InboxAddress, + SequencerInbox: info.SequencerInbox, + DeployedAt: receipt.BlockNumber.Uint64(), + Rollup: info.RollupAddress, + NativeToken: nativeToken, + UpgradeExecutor: info.UpgradeExecutor, + ValidatorUtils: validatorUtils, + ValidatorWalletCreator: validatorWalletCreator, + }, nil +} diff --git a/system_tests/common_test.go b/system_tests/common_test.go index 327ce94e06..b0074a268d 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -23,6 +23,7 @@ import ( "github.com/offchainlabs/nitro/cmd/chaininfo" "github.com/offchainlabs/nitro/cmd/genericconf" "github.com/offchainlabs/nitro/das" + "github.com/offchainlabs/nitro/deploy" "github.com/offchainlabs/nitro/execution/gethexec" "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/util/headerreader" @@ -656,7 +657,7 @@ func DeployOnTestL1( nativeToken := common.Address{} maxDataSize := big.NewInt(117964) - addresses, err := arbnode.DeployOnL1( + addresses, err := deploy.DeployOnL1( ctx, l1Reader, &l1TransactionOpts, From 5fe74348b2f4f3d3b79f158e7ba3c30437e10049 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Thu, 16 Nov 2023 19:27:52 -0700 Subject: [PATCH 2/7] headerreader: use directly don't need interface since we separated headerreader from arbnode --- staker/staker.go | 5 +++-- staker/stateless_block_validator.go | 8 -------- staker/validatorwallet/contract.go | 9 +-------- 3 files changed, 4 insertions(+), 18 deletions(-) diff --git a/staker/staker.go b/staker/staker.go index 7918e80008..4f35c1bc9a 100644 --- a/staker/staker.go +++ b/staker/staker.go @@ -26,6 +26,7 @@ import ( "github.com/offchainlabs/nitro/cmd/genericconf" "github.com/offchainlabs/nitro/staker/txbuilder" "github.com/offchainlabs/nitro/util/arbmath" + "github.com/offchainlabs/nitro/util/headerreader" "github.com/offchainlabs/nitro/util/stopwaiter" "github.com/offchainlabs/nitro/validator" ) @@ -240,7 +241,7 @@ type LatestConfirmedNotifier interface { type Staker struct { *L1Validator stopwaiter.StopWaiter - l1Reader L1ReaderInterface + l1Reader *headerreader.HeaderReader stakedNotifiers []LatestStakedNotifier confirmedNotifiers []LatestConfirmedNotifier activeChallenge *ChallengeManager @@ -277,7 +278,7 @@ type ValidatorWalletInterface interface { } func NewStaker( - l1Reader L1ReaderInterface, + l1Reader *headerreader.HeaderReader, wallet ValidatorWalletInterface, callOpts bind.CallOpts, config L1ValidatorConfig, diff --git a/staker/stateless_block_validator.go b/staker/stateless_block_validator.go index c4968ca9e4..acd86f8627 100644 --- a/staker/stateless_block_validator.go +++ b/staker/stateless_block_validator.go @@ -19,7 +19,6 @@ import ( "github.com/offchainlabs/nitro/validator" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" @@ -71,13 +70,6 @@ type InboxReaderInterface interface { GetSequencerMessageBytes(ctx context.Context, seqNum uint64) ([]byte, error) } -type L1ReaderInterface interface { - Client() arbutil.L1Interface - Subscribe(bool) (<-chan *types.Header, func()) - WaitForTxApproval(ctx context.Context, tx *types.Transaction) (*types.Receipt, error) - UseFinalityData() bool -} - type GlobalStatePosition struct { BatchNumber uint64 PosInBatch uint64 diff --git a/staker/validatorwallet/contract.go b/staker/validatorwallet/contract.go index 302e4fb439..774e9ab407 100644 --- a/staker/validatorwallet/contract.go +++ b/staker/validatorwallet/contract.go @@ -400,19 +400,12 @@ func (b *Contract) DataPoster() *dataposter.DataPoster { return b.dataPoster } -type L1ReaderInterface interface { - Client() arbutil.L1Interface - Subscribe(bool) (<-chan *types.Header, func()) - WaitForTxApproval(ctx context.Context, tx *types.Transaction) (*types.Receipt, error) - UseFinalityData() bool -} - func GetValidatorWalletContract( ctx context.Context, validatorWalletFactoryAddr common.Address, fromBlock int64, transactAuth *bind.TransactOpts, - l1Reader L1ReaderInterface, + l1Reader *headerreader.HeaderReader, createIfMissing bool, ) (*common.Address, error) { client := l1Reader.Client() From 03725c0c370a4ee1ab6be3abc1175bdc7b234c73 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Fri, 17 Nov 2023 18:03:51 -0700 Subject: [PATCH 3/7] deploy: test all transactions --- deploy/deploy.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deploy/deploy.go b/deploy/deploy.go index abdf4c06ba..bd2f2ec329 100644 --- a/deploy/deploy.go +++ b/deploy/deploy.go @@ -122,19 +122,19 @@ func deployChallengeFactory(ctx context.Context, l1Reader *headerreader.HeaderRe return common.Address{}, common.Address{}, fmt.Errorf("osp0 deploy error: %w", err) } - ospMem, _, _, err := ospgen.DeployOneStepProverMemory(auth, client) + ospMem, tx, _, err := ospgen.DeployOneStepProverMemory(auth, client) err = andTxSucceeded(ctx, l1Reader, tx, err) if err != nil { return common.Address{}, common.Address{}, fmt.Errorf("ospMemory deploy error: %w", err) } - ospMath, _, _, err := ospgen.DeployOneStepProverMath(auth, client) + ospMath, tx, _, err := ospgen.DeployOneStepProverMath(auth, client) err = andTxSucceeded(ctx, l1Reader, tx, err) if err != nil { return common.Address{}, common.Address{}, fmt.Errorf("ospMath deploy error: %w", err) } - ospHostIo, _, _, err := ospgen.DeployOneStepProverHostIo(auth, client) + ospHostIo, tx, _, err := ospgen.DeployOneStepProverHostIo(auth, client) err = andTxSucceeded(ctx, l1Reader, tx, err) if err != nil { return common.Address{}, common.Address{}, fmt.Errorf("ospHostIo deploy error: %w", err) From ee65d7fdec958dfce67fd60ac0cc0074abd1979b Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Fri, 17 Nov 2023 18:05:03 -0700 Subject: [PATCH 4/7] gethexec: fix test headerreader configs --- execution/gethexec/node.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/execution/gethexec/node.go b/execution/gethexec/node.go index 5a99d59c5a..a29adbdb62 100644 --- a/execution/gethexec/node.go +++ b/execution/gethexec/node.go @@ -99,7 +99,7 @@ var ConfigDefault = Config{ func ConfigDefaultNonSequencerTest() *Config { config := ConfigDefault - config.ParentChainReader = headerreader.Config{} + config.ParentChainReader = headerreader.TestConfig config.Sequencer.Enable = false config.Forwarder = DefaultTestForwarderConfig config.ForwardingTarget = "null" @@ -111,7 +111,6 @@ func ConfigDefaultNonSequencerTest() *Config { func ConfigDefaultTest() *Config { config := ConfigDefault - config.ParentChainReader = headerreader.Config{} config.Sequencer = TestSequencerConfig config.ForwardingTarget = "null" config.ParentChainReader = headerreader.TestConfig From b9c715823bb9752f62fff15fabe3044aac9f12be Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Fri, 17 Nov 2023 18:08:54 -0700 Subject: [PATCH 5/7] headerreader: in tests, wait for block to become safe In tests all blocks are marked safe as published immediately but not atomically, which creates some race conditions. Avoid race conditions by only considering blocks published if consensus saw them. --- util/headerreader/header_reader.go | 86 +++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 19 deletions(-) diff --git a/util/headerreader/header_reader.go b/util/headerreader/header_reader.go index ff3b420a1c..678c3099fa 100644 --- a/util/headerreader/header_reader.go +++ b/util/headerreader/header_reader.go @@ -55,13 +55,18 @@ type cachedHeader struct { } type Config struct { - Enable bool `koanf:"enable"` - PollOnly bool `koanf:"poll-only" reload:"hot"` - PollInterval time.Duration `koanf:"poll-interval" reload:"hot"` - SubscribeErrInterval time.Duration `koanf:"subscribe-err-interval" reload:"hot"` - TxTimeout time.Duration `koanf:"tx-timeout" reload:"hot"` - OldHeaderTimeout time.Duration `koanf:"old-header-timeout" reload:"hot"` - UseFinalityData bool `koanf:"use-finality-data" reload:"hot"` + Enable bool `koanf:"enable"` + PollOnly bool `koanf:"poll-only" reload:"hot"` + PollInterval time.Duration `koanf:"poll-interval" reload:"hot"` + SubscribeErrInterval time.Duration `koanf:"subscribe-err-interval" reload:"hot"` + TxTimeout time.Duration `koanf:"tx-timeout" reload:"hot"` + OldHeaderTimeout time.Duration `koanf:"old-header-timeout" reload:"hot"` + UseFinalityData bool `koanf:"use-finality-data" reload:"hot"` + Dangerous DangerousConfig `koanf:"dangerous"` +} + +type DangerousConfig struct { + WaitForTxApprovalSafePoll time.Duration `koanf:"wait-for-tx-approval-safe-poll"` } type ConfigFetcher func() *Config @@ -74,6 +79,9 @@ var DefaultConfig = Config{ TxTimeout: 5 * time.Minute, OldHeaderTimeout: 5 * time.Minute, UseFinalityData: true, + Dangerous: DangerousConfig{ + WaitForTxApprovalSafePoll: 0, + }, } func AddOptions(prefix string, f *flag.FlagSet) { @@ -84,6 +92,11 @@ func AddOptions(prefix string, f *flag.FlagSet) { f.Duration(prefix+".subscribe-err-interval", DefaultConfig.SubscribeErrInterval, "interval for subscribe error") f.Duration(prefix+".tx-timeout", DefaultConfig.TxTimeout, "timeout when waiting for a transaction") f.Duration(prefix+".old-header-timeout", DefaultConfig.OldHeaderTimeout, "warns if the latest l1 block is at least this old") + AddDangerousOptions(prefix+".dangerous", f) +} + +func AddDangerousOptions(prefix string, f *flag.FlagSet) { + f.Duration(prefix+".wait-for-tx-approval-safe-poll", DefaultConfig.Dangerous.WaitForTxApprovalSafePoll, "Dangerous! only meant to be used by system tests") } var TestConfig = Config{ @@ -93,6 +106,9 @@ var TestConfig = Config{ TxTimeout: time.Second * 5, OldHeaderTimeout: 5 * time.Minute, UseFinalityData: false, + Dangerous: DangerousConfig{ + WaitForTxApprovalSafePoll: time.Millisecond * 100, + }, } func New(ctx context.Context, client arbutil.L1Interface, config ConfigFetcher, arbSysPrecompile ArbSysInterface) (*HeaderReader, error) { @@ -120,6 +136,8 @@ func New(ctx context.Context, client arbutil.L1Interface, config ConfigFetcher, }, nil } +func (s *HeaderReader) Config() *Config { return s.config() } + // Subscribe to block header updates. // Subscribers are notified when there is a change. // Channel could be missing headers and have duplicates. @@ -328,22 +346,52 @@ func (s *HeaderReader) WaitForTxApproval(ctxIn context.Context, tx *types.Transa ctx, cancel := context.WithTimeout(ctxIn, s.config().TxTimeout) defer cancel() txHash := tx.Hash() + waitForBlock := false + waitForSafePoll := s.config().Dangerous.WaitForTxApprovalSafePoll for { - receipt, err := s.client.TransactionReceipt(ctx, txHash) - if err == nil && receipt.BlockNumber.IsUint64() { - receiptBlockNr := receipt.BlockNumber.Uint64() - callBlockNr := s.LastPendingCallBlockNr() - if callBlockNr > receiptBlockNr { - return receipt, arbutil.DetailTxError(ctx, s.client, tx, receipt) + if waitForBlock { + select { + case _, ok := <-headerchan: + if !ok { + return nil, fmt.Errorf("waiting for %v: channel closed", txHash) + } + case <-ctx.Done(): + return nil, ctx.Err() } } - select { - case _, ok := <-headerchan: - if !ok { - return nil, fmt.Errorf("waiting for %v: channel closed", txHash) + waitForBlock = true + receipt, err := s.client.TransactionReceipt(ctx, txHash) + if err != nil || receipt == nil { + continue + } + if !receipt.BlockNumber.IsUint64() { + continue + } + receiptBlockNr := receipt.BlockNumber.Uint64() + callBlockNr := s.LastPendingCallBlockNr() + if callBlockNr <= receiptBlockNr { + continue + } + if waitForSafePoll != 0 { + safeBlock, err := s.client.BlockByNumber(ctx, big.NewInt(int64(rpc.SafeBlockNumber))) + if err != nil || safeBlock == nil { + log.Warn("parent chain: failed getting safeblock", "err", err) + continue } - case <-ctx.Done(): - return nil, ctx.Err() + if safeBlock.NumberU64() < receiptBlockNr { + log.Info("parent chain: waiting for safe block (see wait-for-tx-approval-safe-poll)", "waiting", receiptBlockNr, "safe", safeBlock.NumberU64()) + select { + case <-time.After(time.Millisecond * 100): + case <-ctx.Done(): + return nil, ctx.Err() + } + waitForBlock = false + continue + } + } + block, err := s.client.BlockByHash(ctx, receipt.BlockHash) + if block != nil && err == nil { + return receipt, arbutil.DetailTxError(ctx, s.client, tx, receipt) } } } From 2f635b2cea98ce8924f457f2a1cef0aead0f0eb7 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Fri, 17 Nov 2023 18:10:31 -0700 Subject: [PATCH 6/7] system tests: EnsureWaTxSucceeds wait for safe blocks on l1 To avoid race conditions, make sure simulated consensus also saw the block. --- system_tests/wrap_transaction_test.go | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/system_tests/wrap_transaction_test.go b/system_tests/wrap_transaction_test.go index e4ce6a4bb8..85cf015211 100644 --- a/system_tests/wrap_transaction_test.go +++ b/system_tests/wrap_transaction_test.go @@ -15,8 +15,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rpc" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/solgen/go/precompilesgen" + "github.com/offchainlabs/nitro/util/headerreader" ) func GetPendingBlockNumber(ctx context.Context, client arbutil.L1Interface) (*big.Int, error) { @@ -77,11 +79,27 @@ func EnsureTxSucceeded(ctx context.Context, client arbutil.L1Interface, tx *type } func EnsureTxSucceededWithTimeout(ctx context.Context, client arbutil.L1Interface, tx *types.Transaction, timeout time.Duration) (*types.Receipt, error) { - txRes, err := WaitForTx(ctx, client, tx.Hash(), timeout) + receipt, err := WaitForTx(ctx, client, tx.Hash(), timeout) if err != nil { return nil, fmt.Errorf("waitFoxTx (tx=%s) got: %w", tx.Hash().Hex(), err) } - return txRes, arbutil.DetailTxError(ctx, client, tx, txRes) + if receipt.Status == types.ReceiptStatusSuccessful && tx.ChainId().Cmp(simulatedChainID) == 0 { + for { + safeBlock, err := client.HeaderByNumber(ctx, big.NewInt(int64(rpc.SafeBlockNumber))) + if err != nil { + return receipt, err + } + if safeBlock.Number.Cmp(receipt.BlockNumber) >= 0 { + break + } + select { + case <-time.After(headerreader.TestConfig.Dangerous.WaitForTxApprovalSafePoll): + case <-ctx.Done(): + return receipt, ctx.Err() + } + } + } + return receipt, arbutil.DetailTxError(ctx, client, tx, receipt) } func headerSubscribeMainLoop(chanOut chan<- *types.Header, ctx context.Context, client ethereum.ChainReader) { From 6b42be964a7ff89f0508d6c06a2aac669b0f8a06 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Fri, 17 Nov 2023 18:11:34 -0700 Subject: [PATCH 7/7] full_challenge: use EnsureTxSuccess on the custom deployer make sure each transaction is received before moving forward. --- system_tests/full_challenge_impl_test.go | 45 ++++++++++++------------ 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/system_tests/full_challenge_impl_test.go b/system_tests/full_challenge_impl_test.go index 98e776487a..43ba6a056c 100644 --- a/system_tests/full_challenge_impl_test.go +++ b/system_tests/full_challenge_impl_test.go @@ -42,30 +42,31 @@ import ( ) func DeployOneStepProofEntry(t *testing.T, ctx context.Context, auth *bind.TransactOpts, client *ethclient.Client) common.Address { - osp0, _, _, err := ospgen.DeployOneStepProver0(auth, client) - if err != nil { - Fatal(t, err) - } - ospMem, _, _, err := ospgen.DeployOneStepProverMemory(auth, client) - if err != nil { - Fatal(t, err) - } - ospMath, _, _, err := ospgen.DeployOneStepProverMath(auth, client) - if err != nil { - Fatal(t, err) - } - ospHostIo, _, _, err := ospgen.DeployOneStepProverHostIo(auth, client) - if err != nil { - Fatal(t, err) - } + osp0, tx, _, err := ospgen.DeployOneStepProver0(auth, client) + Require(t, err) + _, err = EnsureTxSucceeded(ctx, client, tx) + Require(t, err) + + ospMem, tx, _, err := ospgen.DeployOneStepProverMemory(auth, client) + Require(t, err) + _, err = EnsureTxSucceeded(ctx, client, tx) + Require(t, err) + + ospMath, tx, _, err := ospgen.DeployOneStepProverMath(auth, client) + Require(t, err) + _, err = EnsureTxSucceeded(ctx, client, tx) + Require(t, err) + + ospHostIo, tx, _, err := ospgen.DeployOneStepProverHostIo(auth, client) + Require(t, err) + _, err = EnsureTxSucceeded(ctx, client, tx) + Require(t, err) + ospEntry, tx, _, err := ospgen.DeployOneStepProofEntry(auth, client, osp0, ospMem, ospMath, ospHostIo) - if err != nil { - Fatal(t, err) - } + Require(t, err) _, err = EnsureTxSucceeded(ctx, client, tx) - if err != nil { - Fatal(t, err) - } + Require(t, err) + return ospEntry }