diff --git a/Dockerfile b/Dockerfile index 96bcb22952..b05cb3d4d9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,7 +29,7 @@ RUN apt-get update && \ apt-get install -y git python3 make g++ WORKDIR /workspace COPY contracts/package.json contracts/yarn.lock contracts/ -RUN cd contracts && yarn install --ignore-optional +RUN cd contracts && yarn install COPY contracts contracts/ COPY Makefile . RUN NITRO_BUILD_IGNORE_TIMESTAMPS=1 make build-solidity @@ -77,6 +77,7 @@ COPY ./fastcache ./fastcache COPY ./go-ethereum ./go-ethereum COPY --from=brotli-wasm-export / target/ COPY --from=contracts-builder workspace/contracts/build/contracts/src/precompiles/ contracts/build/contracts/src/precompiles/ +COPY --from=contracts-builder workspace/contracts/node_modules/@offchainlabs/upgrade-executor/build/contracts/src/UpgradeExecutor.sol/UpgradeExecutor.json contracts/ COPY --from=contracts-builder workspace/.make/ .make/ RUN PATH="$PATH:/usr/local/go/bin" NITRO_BUILD_IGNORE_TIMESTAMPS=1 make build-wasm-bin @@ -180,6 +181,7 @@ COPY fastcache/go.mod fastcache/go.sum fastcache/ RUN go mod download COPY . ./ COPY --from=contracts-builder workspace/contracts/build/ contracts/build/ +COPY --from=contracts-builder workspace/contracts/node_modules/@offchainlabs/upgrade-executor/build/contracts/src/UpgradeExecutor.sol/UpgradeExecutor.json contracts/node_modules/@offchainlabs/upgrade-executor/build/contracts/src/UpgradeExecutor.sol/ COPY --from=contracts-builder workspace/.make/ .make/ COPY --from=prover-header-export / target/ COPY --from=brotli-library-export / target/ diff --git a/arbnode/node.go b/arbnode/node.go index 4fbfaf4bb2..0782e8ecb7 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -41,6 +41,7 @@ import ( "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" @@ -61,21 +62,23 @@ func andTxSucceeded(ctx context.Context, l1Reader *headerreader.HeaderReader, tx return nil } -func deployBridgeCreator(ctx context.Context, l1Reader *headerreader.HeaderReader, auth *bind.TransactOpts) (common.Address, error) { +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) + 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) + 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) @@ -93,16 +96,51 @@ func deployBridgeCreator(ctx context.Context, l1Reader *headerreader.HeaderReade return common.Address{}, fmt.Errorf("outbox deploy error: %w", err) } - bridgeCreatorAddr, tx, bridgeCreator, err := rollupgen.DeployBridgeCreator(auth, client) + 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 creator deploy error: %w", err) + 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, } - tx, err = bridgeCreator.UpdateTemplates(auth, bridgeTemplate, seqInboxTemplate, inboxTemplate, rollupEventBridgeTemplate, outboxTemplate) + 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 update templates error: %w", err) + return common.Address{}, fmt.Errorf("bridge creator deploy error: %w", err) } return bridgeCreatorAddr, nil @@ -149,10 +187,10 @@ func deployChallengeFactory(ctx context.Context, l1Reader *headerreader.HeaderRe return ospEntryAddr, challengeManagerAddr, nil } -func deployRollupCreator(ctx context.Context, l1Reader *headerreader.HeaderReader, auth *bind.TransactOpts) (*rollupgen.RollupCreator, common.Address, common.Address, common.Address, error) { - bridgeCreator, err := deployBridgeCreator(ctx, l1Reader, auth) +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{}, err + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("bridge creator deploy error: %w", err) } ospEntryAddr, challengeManagerAddr, err := deployChallengeFactory(ctx, l1Reader, auth) @@ -178,6 +216,12 @@ func deployRollupCreator(ctx context.Context, l1Reader *headerreader.HeaderReade 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 { @@ -190,6 +234,12 @@ func deployRollupCreator(ctx context.Context, l1Reader *headerreader.HeaderReade 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, @@ -197,8 +247,10 @@ func deployRollupCreator(ctx context.Context, l1Reader *headerreader.HeaderReade challengeManagerAddr, rollupAdminLogic, rollupUserLogic, + upgradeExecutor, validatorUtils, validatorWalletCreator, + l2FactoriesDeployHelper, ) err = andTxSucceeded(ctx, l1Reader, tx, err) if err != nil { @@ -235,12 +287,12 @@ 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) (*chaininfo.RollupAddresses, error) { +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) + rollupCreator, _, validatorUtils, validatorWalletCreator, err := deployRollupCreator(ctx, parentChainReader, deployAuth, maxDataSize) if err != nil { return nil, fmt.Errorf("error deploying rollup creator: %w", err) } @@ -250,11 +302,19 @@ func DeployOnL1(ctx context.Context, parentChainReader *headerreader.HeaderReade 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, - config, - batchPoster, - validatorAddrs, + deployParams, ) if err != nil { return nil, fmt.Errorf("error submitting create rollup tx: %w", err) @@ -274,6 +334,8 @@ func DeployOnL1(ctx context.Context, parentChainReader *headerreader.HeaderReade SequencerInbox: info.SequencerInbox, DeployedAt: receipt.BlockNumber.Uint64(), Rollup: info.RollupAddress, + NativeToken: nativeToken, + UpgradeExecutor: info.UpgradeExecutor, ValidatorUtils: validatorUtils, ValidatorWalletCreator: validatorWalletCreator, }, nil diff --git a/cmd/chaininfo/chain_info.go b/cmd/chaininfo/chain_info.go index f75779b4aa..cc13321513 100644 --- a/cmd/chaininfo/chain_info.go +++ b/cmd/chaininfo/chain_info.go @@ -106,6 +106,8 @@ type RollupAddresses struct { Inbox common.Address `json:"inbox"` SequencerInbox common.Address `json:"sequencer-inbox"` Rollup common.Address `json:"rollup"` + NativeToken common.Address `json:"native-token"` + UpgradeExecutor common.Address `json:"upgrade-executor"` ValidatorUtils common.Address `json:"validator-utils"` ValidatorWalletCreator common.Address `json:"validator-wallet-creator"` DeployedAt uint64 `json:"deployed-at"` diff --git a/cmd/deploy/deploy.go b/cmd/deploy/deploy.go index d687821e8b..0b72038908 100644 --- a/cmd/deploy/deploy.go +++ b/cmd/deploy/deploy.go @@ -40,6 +40,8 @@ func main() { deployAccount := flag.String("l1DeployAccount", "", "l1 seq account to use (default is first account in keystore)") ownerAddressString := flag.String("ownerAddress", "", "the rollup owner's address") sequencerAddressString := flag.String("sequencerAddress", "", "the sequencer's address") + nativeTokenAddressString := flag.String("nativeTokenAddress", "0x0000000000000000000000000000000000000000", "address of the ERC20 token which is used as native L2 currency") + maxDataSizeUint := flag.Uint64("maxDataSize", 117964, "maximum data size of a batch or a cross-chain message (default = 90% of Geth's 128KB tx size limit)") loserEscrowAddressString := flag.String("loserEscrowAddress", "", "the address which half of challenge loser's funds accumulate at") wasmmoduleroot := flag.String("wasmmoduleroot", "", "WASM module root hash") wasmrootpath := flag.String("wasmrootpath", "", "path to machine folders") @@ -55,6 +57,7 @@ func main() { prod := flag.Bool("prod", false, "Whether to configure the rollup for production or testing") flag.Parse() l1ChainId := new(big.Int).SetUint64(*l1ChainIdUint) + maxDataSize := new(big.Int).SetUint64(*maxDataSizeUint) if *prod { if *wasmmoduleroot == "" { @@ -137,6 +140,7 @@ func main() { l1Reader.Start(ctx) defer l1Reader.StopAndWait() + nativeToken := common.HexToAddress(*nativeTokenAddressString) deployedAddresses, err := arbnode.DeployOnL1( ctx, l1Reader, @@ -144,6 +148,8 @@ func main() { sequencerAddress, *authorizevalidators, arbnode.GenerateRollupConfig(*prod, moduleRoot, ownerAddress, &chainConfig, chainConfigJson, loserEscrowAddress), + nativeToken, + maxDataSize, ) if err != nil { flag.Usage() diff --git a/contracts b/contracts index b16bf0b737..695750067b 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit b16bf0b737468382854dac28346fec8b65b55989 +Subproject commit 695750067b2b7658556bdf61ec8cf16132d83dd0 diff --git a/nitro-testnode b/nitro-testnode index aee6ceff9c..3a7b2cee29 160000 --- a/nitro-testnode +++ b/nitro-testnode @@ -1 +1 @@ -Subproject commit aee6ceff9c9d3fb2749da55a7d7842f23d1bfc8e +Subproject commit 3a7b2cee29cf591cc4c3f6c5ddc00bf5be367bd9 diff --git a/solgen/gen.go b/solgen/gen.go index c29db93039..5d43946fa5 100644 --- a/solgen/gen.go +++ b/solgen/gen.go @@ -96,6 +96,27 @@ func main() { modInfo.addArtifact(artifact) } + // add upgrade executor module which is not compiled locally, but imported from 'nitro-contracts' depedencies + upgExecutorPath := filepath.Join(parent, "contracts", "node_modules", "@offchainlabs", "upgrade-executor", "build", "contracts", "src", "UpgradeExecutor.sol", "UpgradeExecutor.json") + _, err = os.Stat(upgExecutorPath) + if !os.IsNotExist(err) { + data, err := os.ReadFile(upgExecutorPath) + if err != nil { + // log.Fatal(string(output)) + log.Fatal("could not read", upgExecutorPath, "for contract", "UpgradeExecutor", err) + } + artifact := HardHatArtifact{} + if err := json.Unmarshal(data, &artifact); err != nil { + log.Fatal("failed to parse contract", "UpgradeExecutor", err) + } + modInfo := modules["upgrade_executorgen"] + if modInfo == nil { + modInfo = &moduleInfo{} + modules["upgrade_executorgen"] = modInfo + } + modInfo.addArtifact(artifact) + } + for module, info := range modules { code, err := bind.Bind( diff --git a/system_tests/common_test.go b/system_tests/common_test.go index 0d7cd2dfaa..6d41f8e101 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -12,6 +12,7 @@ import ( "net" "os" "strconv" + "strings" "testing" "time" @@ -30,6 +31,7 @@ import ( "github.com/offchainlabs/nitro/validator/server_common" "github.com/offchainlabs/nitro/validator/valnode" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/common" @@ -52,6 +54,7 @@ import ( "github.com/offchainlabs/nitro/solgen/go/bridgegen" "github.com/offchainlabs/nitro/solgen/go/mocksgen" "github.com/offchainlabs/nitro/solgen/go/precompilesgen" + "github.com/offchainlabs/nitro/solgen/go/upgrade_executorgen" "github.com/offchainlabs/nitro/statetransfer" "github.com/offchainlabs/nitro/util/testhelpers" ) @@ -641,6 +644,8 @@ func DeployOnTestL1( l1Reader.Start(ctx) defer l1Reader.StopAndWait() + nativeToken := common.Address{} + maxDataSize := big.NewInt(117964) addresses, err := arbnode.DeployOnL1( ctx, l1Reader, @@ -648,11 +653,14 @@ func DeployOnTestL1( l1info.GetAddress("Sequencer"), 0, arbnode.GenerateRollupConfig(false, locator.LatestWasmModuleRoot(), l1info.GetAddress("RollupOwner"), chainConfig, serializedChainConfig, common.Address{}), + nativeToken, + maxDataSize, ) Require(t, err) l1info.SetContract("Bridge", addresses.Bridge) l1info.SetContract("SequencerInbox", addresses.SequencerInbox) l1info.SetContract("Inbox", addresses.Inbox) + l1info.SetContract("UpgradeExecutor", addresses.UpgradeExecutor) initMessage := getInitMessage(ctx, t, l1client, addresses) return addresses, initMessage } @@ -952,11 +960,19 @@ func authorizeDASKeyset( err := keyset.Serialize(wr) Require(t, err, "unable to serialize DAS keyset") keysetBytes := wr.Bytes() - sequencerInbox, err := bridgegen.NewSequencerInbox(l1info.Accounts["SequencerInbox"].Address, l1client) - Require(t, err, "unable to create sequencer inbox") + + sequencerInboxABI, err := abi.JSON(strings.NewReader(bridgegen.SequencerInboxABI)) + Require(t, err, "unable to parse sequencer inbox ABI") + setKeysetCalldata, err := sequencerInboxABI.Pack("setValidKeyset", keysetBytes) + Require(t, err, "unable to generate calldata") + + upgradeExecutor, err := upgrade_executorgen.NewUpgradeExecutor(l1info.Accounts["UpgradeExecutor"].Address, l1client) + Require(t, err, "unable to bind upgrade executor") + trOps := l1info.GetDefaultTransactOpts("RollupOwner", ctx) - tx, err := sequencerInbox.SetValidKeyset(&trOps, keysetBytes) + tx, err := upgradeExecutor.ExecuteCall(&trOps, l1info.Accounts["SequencerInbox"].Address, setKeysetCalldata) Require(t, err, "unable to set valid keyset") + _, err = EnsureTxSucceeded(ctx, l1client, tx) Require(t, err, "unable to ensure transaction success for setting valid keyset") } diff --git a/system_tests/full_challenge_impl_test.go b/system_tests/full_challenge_impl_test.go index 99064d1913..f22960c39d 100644 --- a/system_tests/full_challenge_impl_test.go +++ b/system_tests/full_challenge_impl_test.go @@ -207,6 +207,7 @@ func setupSequencerInboxStub(ctx context.Context, t *testing.T, l1Info *Blockcha bridgeAddr, l1Info.GetAddress("sequencer"), timeBounds, + big.NewInt(117964), ) Require(t, err) _, err = EnsureTxSucceeded(ctx, l1Client, tx) diff --git a/system_tests/staker_test.go b/system_tests/staker_test.go index e9eb884c95..6b120e3ec0 100644 --- a/system_tests/staker_test.go +++ b/system_tests/staker_test.go @@ -16,6 +16,7 @@ import ( "testing" "time" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" @@ -29,6 +30,7 @@ import ( "github.com/offchainlabs/nitro/arbos/l2pricing" "github.com/offchainlabs/nitro/solgen/go/mocksgen" "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" @@ -122,13 +124,23 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) rollup, err := rollupgen.NewRollupAdminLogic(l2nodeA.DeployInfo.Rollup, builder.L1.Client) Require(t, err) - tx, err := rollup.SetValidator(&deployAuth, []common.Address{valWalletAddrA, l1authB.From}, []bool{true, true}) - Require(t, err) + + upgradeExecutor, err := upgrade_executorgen.NewUpgradeExecutor(l2nodeA.DeployInfo.UpgradeExecutor, builder.L1.Client) + Require(t, err, "unable to bind upgrade executor") + rollupABI, err := abi.JSON(strings.NewReader(rollupgen.RollupAdminLogicABI)) + Require(t, err, "unable to parse rollup ABI") + + setValidatorCalldata, err := rollupABI.Pack("setValidator", []common.Address{valWalletAddrA, l1authB.From}, []bool{true, true}) + Require(t, err, "unable to generate setValidator calldata") + tx, err := upgradeExecutor.ExecuteCall(&deployAuth, l2nodeA.DeployInfo.Rollup, setValidatorCalldata) + Require(t, err, "unable to set validators") _, err = builder.L1.EnsureTxSucceeded(tx) Require(t, err) - tx, err = rollup.SetMinimumAssertionPeriod(&deployAuth, big.NewInt(1)) - Require(t, err) + setMinAssertPeriodCalldata, err := rollupABI.Pack("setMinimumAssertionPeriod", big.NewInt(1)) + Require(t, err, "unable to generate setMinimumAssertionPeriod calldata") + tx, err = upgradeExecutor.ExecuteCall(&deployAuth, l2nodeA.DeployInfo.Rollup, setMinAssertPeriodCalldata) + Require(t, err, "unable to set minimum assertion period") _, err = builder.L1.EnsureTxSucceeded(tx) Require(t, err) @@ -320,9 +332,11 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) Fatal(t, "failed to get challenge manager proxy admin") } - proxyAdmin, err := mocksgen.NewProxyAdminForBinding(proxyAdminAddr, builder.L1.Client) + proxyAdminABI, err := abi.JSON(strings.NewReader(mocksgen.ProxyAdminForBindingABI)) + Require(t, err) + upgradeCalldata, err := proxyAdminABI.Pack("upgrade", managerAddr, mockImpl) Require(t, err) - tx, err = proxyAdmin.Upgrade(&deployAuth, managerAddr, mockImpl) + tx, err = upgradeExecutor.ExecuteCall(&deployAuth, proxyAdminAddr, upgradeCalldata) Require(t, err) _, err = builder.L1.EnsureTxSucceeded(tx) Require(t, err)