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..bd2f2ec329 --- /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, 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, 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, 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) + } + + 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/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 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() 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, 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 } 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) { 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) } } }