diff --git a/docker-compose.yaml b/docker-compose.yaml index f8facec..f595b66 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -367,6 +367,54 @@ services: - "config:/config" - /var/run/docker.sock:/var/run/docker.sock + datool: + image: nitro-node-dev-testnode + entrypoint: /usr/local/bin/datool + volumes: + - "config:/config" + - "das-committee-a-data:/das-committee-a" + - "das-committee-b-data:/das-committee-b" + - "das-mirror-data:/das-mirror" + command: + + das-committee-a: + pid: host # allow debugging + image: nitro-node-dev-testnode + entrypoint: /usr/local/bin/daserver + ports: + - "127.0.0.1:9876:9876" + - "127.0.0.1:9877:9877" + volumes: + - "config:/config" + - "das-committee-a-data:/das" + command: + - --conf.file=/config/l2_das_committee.json + + das-committee-b: + pid: host # allow debugging + image: nitro-node-dev-testnode + entrypoint: /usr/local/bin/daserver + ports: + - "127.0.0.1:8876:9876" + - "127.0.0.1:8877:9877" + volumes: + - "config:/config" + - "das-committee-b-data:/das" + command: + - --conf.file=/config/l2_das_committee.json + + das-mirror: + pid: host # allow debugging + image: nitro-node-dev-testnode + entrypoint: /usr/local/bin/daserver + ports: + - "127.0.0.1:7877:9877" + volumes: + - "config:/config" + - "das-mirror-data:/das" + command: + - --conf.file=/config/l2_das_mirror.json + volumes: l1data: consensus: @@ -383,3 +431,6 @@ volumes: config: postgres-data: tokenbridge-data: + das-committee-a-data: + das-committee-b-data: + das-mirror-data: diff --git a/scripts/config.ts b/scripts/config.ts index 3edb7a9..15bc6f7 100644 --- a/scripts/config.ts +++ b/scripts/config.ts @@ -1,5 +1,6 @@ import * as fs from 'fs'; import * as consts from './consts' +import { ethers } from "ethers"; import { namedAccount, namedAddress } from './accounts' const path = require("path"); @@ -160,10 +161,22 @@ function writeGethGenesisConfig(argv: any) { fs.writeFileSync(path.join(consts.configpath, "val_jwt.hex"), val_jwt) } +type ChainInfo = { + [key: string]: any; +}; + +// Define a function to return ChainInfo +function getChainInfo(): ChainInfo { + const filePath = path.join(consts.configpath, "l2_chain_info.json"); + const fileContents = fs.readFileSync(filePath).toString(); + const chainInfo: ChainInfo = JSON.parse(fileContents); + return chainInfo; +} + function writeConfigs(argv: any) { const valJwtSecret = path.join(consts.configpath, "val_jwt.hex") const chainInfoFile = path.join(consts.configpath, "l2_chain_info.json") - const baseConfig = { + let baseConfig = { "parent-chain": { "connection": { "url": argv.l1url, @@ -181,7 +194,7 @@ function writeConfigs(argv: any) { "parent-chain-wallet" : { "account": namedAddress("validator"), "password": consts.l1passphrase, - "pathname": consts.l1keystore, + "pathname": consts.l1keystore, }, "disable-challenge": false, "enable": false, @@ -215,7 +228,7 @@ function writeConfigs(argv: any) { "parent-chain-wallet" : { "account": namedAddress("sequencer"), "password": consts.l1passphrase, - "pathname": consts.l1keystore, + "pathname": consts.l1keystore, }, "data-poster": { "redis-signer": { @@ -229,6 +242,17 @@ function writeConfigs(argv: any) { "url": argv.validationNodeUrl, "jwtsecret": valJwtSecret, } + }, + "data-availability": { + "enable": argv.anytrust, + "rpc-aggregator": dasBackendsJsonConfig(argv), + "rest-aggregator": { + "enable": true, + "urls": ["http://das-mirror:9877"], + }, + // TODO Fix das config to not need this redundant config + "parent-chain-node-url": argv.l1url, + "sequencer-inbox-address": "not_set" } }, "execution": { @@ -250,6 +274,7 @@ function writeConfigs(argv: any) { }, } + baseConfig.node["data-availability"]["sequencer-inbox-address"] = ethers.utils.hexlify(getChainInfo()[0]["rollup"]["sequencer-inbox"]); const baseConfJSON = JSON.stringify(baseConfig) @@ -264,6 +289,9 @@ function writeConfigs(argv: any) { simpleConfig.node["batch-poster"].enable = true simpleConfig.node["batch-poster"]["redis-url"] = "" simpleConfig.execution["sequencer"].enable = true + if (argv.anytrust) { + simpleConfig.node["data-availability"]["rpc-aggregator"].enable = true + } fs.writeFileSync(path.join(consts.configpath, "sequencer_config.json"), JSON.stringify(simpleConfig)) } else { let validatorConfig = JSON.parse(baseConfJSON) @@ -286,6 +314,9 @@ function writeConfigs(argv: any) { let posterConfig = JSON.parse(baseConfJSON) posterConfig.node["seq-coordinator"].enable = true posterConfig.node["batch-poster"].enable = true + if (argv.anytrust) { + posterConfig.node["data-availability"]["rpc-aggregator"].enable = true + } fs.writeFileSync(path.join(consts.configpath, "poster_config.json"), JSON.stringify(posterConfig)) } @@ -353,7 +384,7 @@ function writeL2ChainConfig(argv: any) { "arbitrum": { "EnableArbOS": true, "AllowDebugPrecompiles": true, - "DataAvailabilityCommittee": false, + "DataAvailabilityCommittee": argv.anytrust, "InitialArbOSVersion": 32, "InitialChainOwner": argv.l2owner, "GenesisBlockNum": 0 @@ -396,6 +427,93 @@ function writeL3ChainConfig(argv: any) { fs.writeFileSync(path.join(consts.configpath, "l3_chain_config.json"), l3ChainConfigJSON) } +function writeL2DASCommitteeConfig(argv: any) { + const sequencerInboxAddr = ethers.utils.hexlify(getChainInfo()[0]["rollup"]["sequencer-inbox"]); + const l2DASCommitteeConfig = { + "data-availability": { + "key": { + "key-dir": "/das/keys" + }, + "local-file-storage": { + "data-dir": "/das/data", + "enable": true, + "enable-expiry": true + }, + "sequencer-inbox-address": sequencerInboxAddr, + "parent-chain-node-url": argv.l1url + }, + "enable-rest": true, + "enable-rpc": true, + "log-level": "INFO", + "rest-addr": "0.0.0.0", + "rest-port": "9877", + "rpc-addr": "0.0.0.0", + "rpc-port": "9876" + } + const l2DASCommitteeConfigJSON = JSON.stringify(l2DASCommitteeConfig) + + fs.writeFileSync(path.join(consts.configpath, "l2_das_committee.json"), l2DASCommitteeConfigJSON) +} + +function writeL2DASMirrorConfig(argv: any, sequencerInboxAddr: string) { + const l2DASMirrorConfig = { + "data-availability": { + "local-file-storage": { + "data-dir": "/das/data", + "enable": true, + "enable-expiry": false + }, + "sequencer-inbox-address": sequencerInboxAddr, + "parent-chain-node-url": argv.l1url, + "rest-aggregator": { + "enable": true, + "sync-to-storage": { + "eager": false, + "ignore-write-errors": false, + "state-dir": "/das/metadata", + "sync-expired-data": true + }, + "urls": ["http://das-committee-a:9877", "http://das-committee-b:9877"], + } + }, + "enable-rest": true, + "enable-rpc": false, + "log-level": "INFO", + "rest-addr": "0.0.0.0", + "rest-port": "9877" + } + const l2DASMirrorConfigJSON = JSON.stringify(l2DASMirrorConfig) + + fs.writeFileSync(path.join(consts.configpath, "l2_das_mirror.json"), l2DASMirrorConfigJSON) +} + +function writeL2DASKeysetConfig(argv: any) { + const l2DASKeysetConfig = { + "keyset": dasBackendsJsonConfig(argv) + } + const l2DASKeysetConfigJSON = JSON.stringify(l2DASKeysetConfig) + + fs.writeFileSync(path.join(consts.configpath, "l2_das_keyset.json"), l2DASKeysetConfigJSON) +} + +function dasBackendsJsonConfig(argv: any) { + const backends = { + "enable": false, + "assumed-honest": 1, + "backends": [ + { + "url": "http://das-committee-a:9876", + "pubkey": argv.dasBlsA + }, + { + "url": "http://das-committee-b:9876", + "pubkey": argv.dasBlsB + } + ] + } + return backends +} + export const writeConfigCommand = { command: "write-config", describe: "writes config files", @@ -405,7 +523,23 @@ export const writeConfigCommand = { describe: "simple config (sequencer is also poster, validator)", default: false, }, - }, + anytrust: { + boolean: true, + describe: "run nodes in anytrust mode", + default: false + }, + dasBlsA: { + string: true, + describe: "DAS committee member A BLS pub key", + default: "" + }, + dasBlsB: { + string: true, + describe: "DAS committee member B BLS pub key", + default: "" + }, + + }, handler: (argv: any) => { writeConfigs(argv) } @@ -430,6 +564,13 @@ export const writeGethGenesisCommand = { export const writeL2ChainConfigCommand = { command: "write-l2-chain-config", describe: "writes l2 chain config file", + builder: { + anytrust: { + boolean: true, + describe: "enable anytrust in chainconfig", + default: false + }, + }, handler: (argv: any) => { writeL2ChainConfig(argv) } @@ -442,3 +583,40 @@ export const writeL3ChainConfigCommand = { writeL3ChainConfig(argv) } } + +export const writeL2DASCommitteeConfigCommand = { + command: "write-l2-das-committee-config", + describe: "writes daserver committee member config file", + handler: (argv: any) => { + writeL2DASCommitteeConfig(argv) + } +} + +export const writeL2DASMirrorConfigCommand = { + command: "write-l2-das-mirror-config", + describe: "writes daserver mirror config file", + handler: (argv: any) => { + const sequencerInboxAddr = ethers.utils.hexlify(getChainInfo()[0]["rollup"]["sequencer-inbox"]); + writeL2DASMirrorConfig(argv, sequencerInboxAddr) + } +} + +export const writeL2DASKeysetConfigCommand = { + command: "write-l2-das-keyset-config", + describe: "writes DAS keyset config", + builder: { + dasBlsA: { + string: true, + describe: "DAS committee member A BLS pub key", + default: "" + }, + dasBlsB: { + string: true, + describe: "DAS committee member B BLS pub key", + default: "" + }, + }, + handler: (argv: any) => { + writeL2DASKeysetConfig(argv) + } +} diff --git a/scripts/ethcommands.ts b/scripts/ethcommands.ts index ac85ed6..06285e7 100644 --- a/scripts/ethcommands.ts +++ b/scripts/ethcommands.ts @@ -372,6 +372,25 @@ export const createERC20Command = { }, }; +// Will revert if the keyset is already valid. +async function setValidKeyset(argv: any, upgradeExecutorAddr: string, sequencerInboxAddr: string, keyset: string){ + const innerIface = new ethers.utils.Interface(["function setValidKeyset(bytes)"]) + const innerData = innerIface.encodeFunctionData("setValidKeyset", [keyset]); + + // The Executor contract is the owner of the SequencerInbox so calls must be made + // through it. + const outerIface = new ethers.utils.Interface(["function executeCall(address,bytes)"]) + argv.data = outerIface.encodeFunctionData("executeCall", [sequencerInboxAddr, innerData]); + + argv.from = "l2owner"; + argv.to = "address_" + upgradeExecutorAddr + argv.ethamount = "0" + + await sendTransaction(argv, 0); + + argv.provider.destroy(); +} + export const transferERC20Command = { command: "transfer-erc20", describe: "transfers ERC20 token", @@ -526,6 +545,27 @@ export const sendRPCCommand = { } } +export const setValidKeysetCommand = { + command: "set-valid-keyset", + describe: "sets the anytrust keyset", + handler: async (argv: any) => { + argv.provider = new ethers.providers.WebSocketProvider(argv.l1url); + const deploydata = JSON.parse( + fs + .readFileSync(path.join(consts.configpath, "deployment.json")) + .toString() + ); + const sequencerInboxAddr = ethers.utils.hexlify(deploydata["sequencer-inbox"]); + const upgradeExecutorAddr = ethers.utils.hexlify(deploydata["upgrade-executor"]); + + const keyset = fs + .readFileSync(path.join(consts.configpath, "l2_das_keyset.hex")) + .toString() + + await setValidKeyset(argv, upgradeExecutorAddr, sequencerInboxAddr, keyset) + } +}; + export const waitForSyncCommand = { command: "wait-for-sync", describe: "wait for rpc to sync", diff --git a/scripts/index.ts b/scripts/index.ts index 69cfe15..e9c8531 100644 --- a/scripts/index.ts +++ b/scripts/index.ts @@ -2,7 +2,7 @@ import { hideBin } from "yargs/helpers"; import Yargs from "yargs/yargs"; import { stressOptions } from "./stress"; import { redisReadCommand, redisInitCommand } from "./redis"; -import { writeConfigCommand, writeGethGenesisCommand, writePrysmCommand, writeL2ChainConfigCommand, writeL3ChainConfigCommand } from "./config"; +import { writeConfigCommand, writeGethGenesisCommand, writePrysmCommand, writeL2ChainConfigCommand, writeL3ChainConfigCommand, writeL2DASCommitteeConfigCommand, writeL2DASMirrorConfigCommand, writeL2DASKeysetConfigCommand } from "./config"; import { printAddressCommand, namedAccountHelpString, @@ -19,6 +19,7 @@ import { sendL2Command, sendL3Command, sendRPCCommand, + setValidKeysetCommand, waitForSyncCommand, transferL3ChainOwnershipCommand, } from "./ethcommands"; @@ -32,6 +33,7 @@ async function main() { l3url: { string: true, default: "ws://l3node:3348" }, validationNodeUrl: { string: true, default: "ws://validation_node:8549" }, l2owner: { string: true, default: "0x3f1Eae7D46d88F08fc2F8ed27FCb2AB183EB2d0E" }, + committeeMember: { string: true, default: "not_set" }, }) .options(stressOptions) .command(bridgeFundsCommand) @@ -43,11 +45,15 @@ async function main() { .command(sendL2Command) .command(sendL3Command) .command(sendRPCCommand) + .command(setValidKeysetCommand) .command(transferL3ChainOwnershipCommand) .command(writeConfigCommand) .command(writeGethGenesisCommand) .command(writeL2ChainConfigCommand) .command(writeL3ChainConfigCommand) + .command(writeL2DASCommitteeConfigCommand) + .command(writeL2DASMirrorConfigCommand) + .command(writeL2DASKeysetConfigCommand) .command(writePrysmCommand) .command(writeAccountsCommand) .command(printAddressCommand) diff --git a/test-node.bash b/test-node.bash index 12062b5..9c7204c 100755 --- a/test-node.bash +++ b/test-node.bash @@ -50,6 +50,7 @@ batchposters=1 devprivkey=b6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659 l1chainid=1337 simple=true +l2anytrust=false # Use the dev versions of nitro/blockscout dev_nitro=false @@ -222,6 +223,10 @@ while [[ $# -gt 0 ]]; do l3_token_bridge=true shift ;; + --l2-anytrust) + l2anytrust=true + shift + ;; --redundantsequencers) simple=false redundantsequencers=$2 @@ -255,6 +260,7 @@ while [[ $# -gt 0 ]]; do echo --l3-fee-token L3 chain is set up to use custom fee token. Only valid if also '--l3node' is provided echo --l3-fee-token-decimals Number of decimals to use for custom fee token. Only valid if also '--l3-fee-token' is provided echo --l3-token-bridge Deploy L2-L3 token bridge. Only valid if also '--l3node' is provided + echo --l2-anytrust run the L2 as an AnyTrust chain echo --batchposters batch posters [0-3] echo --redundantsequencers redundant sequencers [0-3] echo --detach detach from nodes after running them @@ -317,7 +323,6 @@ if $blockscout; then NODES="$NODES blockscout" fi - if $dev_nitro && $build_dev_nitro; then echo == Building Nitro if ! [ -n "${NITRO_SRC+set}" ]; then @@ -425,8 +430,13 @@ if $force_init; then l2ownerAddress=`docker compose run scripts print-address --account l2owner | tail -n 1 | tr -d '\r\n'` - echo == Writing l2 chain config - docker compose run scripts --l2owner $l2ownerAddress write-l2-chain-config + if $l2anytrust; then + echo "== Writing l2 chain config (anytrust enabled)" + docker compose run scripts --l2owner $l2ownerAddress write-l2-chain-config --anytrust + else + echo == Writing l2 chain config + docker compose run scripts --l2owner $l2ownerAddress write-l2-chain-config + fi sequenceraddress=`docker compose run scripts print-address --account sequencer | tail -n 1 | tr -d '\r\n'` l2ownerKey=`docker compose run scripts print-private-key --account l2owner | tail -n 1 | tr -d '\r\n'` @@ -436,12 +446,44 @@ if $force_init; then docker compose run -e PARENT_CHAIN_RPC="http://geth:8545" -e DEPLOYER_PRIVKEY=$l2ownerKey -e PARENT_CHAIN_ID=$l1chainid -e CHILD_CHAIN_NAME="arb-dev-test" -e MAX_DATA_SIZE=117964 -e OWNER_ADDRESS=$l2ownerAddress -e WASM_MODULE_ROOT=$wasmroot -e SEQUENCER_ADDRESS=$sequenceraddress -e AUTHORIZE_VALIDATORS=10 -e CHILD_CHAIN_CONFIG_PATH="/config/l2_chain_config.json" -e CHAIN_DEPLOYMENT_INFO="/config/deployment.json" -e CHILD_CHAIN_INFO="/config/deployed_chain_info.json" rollupcreator create-rollup-testnode docker compose run --entrypoint sh rollupcreator -c "jq [.[]] /config/deployed_chain_info.json > /config/l2_chain_info.json" +fi # $force_init + +anytrustNodeConfigLine="" + +# Remaining init may require AnyTrust committee/mirrors to have been started +if $l2anytrust; then + if $force_init; then + echo == Generating AnyTrust Config + docker compose run --user root --entrypoint sh datool -c "mkdir /das-committee-a/keys /das-committee-a/data /das-committee-a/metadata /das-committee-b/keys /das-committee-b/data /das-committee-b/metadata /das-mirror/data /das-mirror/metadata" + docker compose run --user root --entrypoint sh datool -c "chown -R 1000:1000 /das*" + docker compose run datool keygen --dir /das-committee-a/keys + docker compose run datool keygen --dir /das-committee-b/keys + docker compose run scripts write-l2-das-committee-config + docker compose run scripts write-l2-das-mirror-config + + das_bls_a=`docker compose run --entrypoint sh datool -c "cat /das-committee-a/keys/das_bls.pub"` + das_bls_b=`docker compose run --entrypoint sh datool -c "cat /das-committee-b/keys/das_bls.pub"` + + docker compose run scripts write-l2-das-keyset-config --dasBlsA $das_bls_a --dasBlsB $das_bls_b + docker compose run --entrypoint sh datool -c "/usr/local/bin/datool dumpkeyset --conf.file /config/l2_das_keyset.json | grep 'Keyset: ' | awk '{ printf \"%s\", \$2 }' > /config/l2_das_keyset.hex" + docker compose run scripts set-valid-keyset + + anytrustNodeConfigLine="--anytrust --dasBlsA $das_bls_a --dasBlsB $das_bls_b" + fi + + if $run; then + echo == Starting AnyTrust committee and mirror + docker compose up --wait das-committee-a das-committee-b das-mirror + fi +fi + +if $force_init; then if $simple; then echo == Writing configs - docker compose run scripts write-config --simple + docker compose run scripts write-config --simple $anytrustNodeConfigLine else echo == Writing configs - docker compose run scripts write-config + docker compose run scripts write-config $anytrustNodeConfigLine echo == Initializing redis docker compose up --wait redis