Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions examples/generate-genesis-file/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#######################
# Chain configuration #
#######################

# If of the chain you're going to create
CHAIN_ID=31337

# Whether it'll be an Anytrust chain
IS_ANYTRUST=false

# ArbOS version to use
ARB_OS_VERSION=40

# Chain owner
CHAIN_OWNER=0x3f1Eae7D46d88F08fc2F8ed27FCb2AB183EB2d0E

############################
# Predeploys configuration #
############################

# Initial L1 base fee
# (you should set this to your estimate of the parent chain's gas price)
L1_BASE_FEE=1000000000 # 1 gwei

# Nitro node image
NITRO_NODE_IMAGE=offchainlabs/nitro-node:v3.7.4-9244576

#################################
# Rollup creation configuration #
#################################
CREATE_ROLLUP=true

# Required
DEPLOYER_PRIVATE_KEY=

# Optional (these will be randomly generated if not provided)
BATCH_POSTER_PRIVATE_KEY=
VALIDATOR_PRIVATE_KEY=

# (Optional) Infura RPC endpoint for the parent chain (Arbitrum Sepolia), if not provided, the script might fail with timeout
PARENT_CHAIN_RPC=https://arbitrum-sepolia.infura.io/v3/YOUR_API_KEY
1 change: 1 addition & 0 deletions examples/generate-genesis-file/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
genesis.json
30 changes: 30 additions & 0 deletions examples/generate-genesis-file/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Arbitrum Orbit SDK

## Generating a genesis.json file and genesis blockhash and sendRoot hash

This is an example for generating a genesis.json file and obtaining the genesis blockhash and sendRoot hash based on that file.

For the contents of the genesis.json file, and what predeploys it contains, you can see the [genesis-file-generator](https://github.com/OffchainLabs/genesis-file-generator) script.

## Setup

1. Install dependencies

```bash
yarn install
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to also yarn install in project root and yarn build for sdk dist

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added further instructions in 00d933a

```

2. Create .env file and add the env vars

```bash
cp .env.example .env
```

> [!NOTE]
> Make sure you set the correct values of the environment variables for your chain

3. Run the example

```bash
yarn dev
```
171 changes: 171 additions & 0 deletions examples/generate-genesis-file/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import { Address, createPublicClient, http, toHex } from 'viem';
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';
import { arbitrumSepolia } from 'viem/chains';
import { createRollupPrepareDeploymentParamsConfig, createRollup } from '@arbitrum/orbit-sdk';
import { sanitizePrivateKey } from '@arbitrum/orbit-sdk/utils';
import { config } from 'dotenv';
import { execSync } from 'child_process';
import * as fs from 'fs';
config();

// Env variables check
if (typeof process.env.NITRO_NODE_IMAGE === 'undefined') {
throw new Error(`Please provide the "NITRO_NODE_IMAGE" environment variable`);
}
if (typeof process.env.L1_BASE_FEE === 'undefined') {
throw new Error(`Please provide the "L1_BASE_FEE" environment variable`);
}
const nitroNodeImage = process.env.NITRO_NODE_IMAGE;
const l1BaseFee = Number(process.env.L1_BASE_FEE);

// Optional checks when creating a rollup
if (process.env.CREATE_ROLLUP === 'true') {
// Check environment variables
if (typeof process.env.DEPLOYER_PRIVATE_KEY === 'undefined') {
throw new Error(`Please provide the "DEPLOYER_PRIVATE_KEY" environment variable`);
}

if (typeof process.env.PARENT_CHAIN_RPC === 'undefined' || process.env.PARENT_CHAIN_RPC === '') {
console.warn(
`Warning: you may encounter timeout errors while running the script with the default rpc endpoint. Please provide the "PARENT_CHAIN_RPC" environment variable instead.`,
);
}
}

// Helpers
function withFallbackPrivateKey(privateKey: string | undefined): `0x${string}` {
if (typeof privateKey === 'undefined' || privateKey === '') {
return generatePrivateKey();
}

return sanitizePrivateKey(privateKey);
}

async function main() {
// Step 0 - Build genesis file generator container from Github
// Note: remove this step once we have a public image
console.log(`Build genesis file generator container...`);
execSync(
`docker build -t genesis-file-generator https://github.com/OffchainLabs/genesis-file-generator.git#containerization`,
);

// Step 1 - Generate genesis file
console.log(`Generate genesis file...`);
execSync(`docker run --env-file ./.env genesis-file-generator > genesis.json`);

// Step 2 - Obtain genesis block hash and sendRoot hash
console.log(`Obtain genesis block hash and sendRoot hash...`);
console.log(`Using image "${nitroNodeImage}" and L1 base fee "${l1BaseFee}".`);
const genesisHashes = execSync(
`docker run -v $(pwd):/data/genesisDir --entrypoint genesis-generator ${nitroNodeImage} --genesis-json-file /data/genesisDir/genesis.json --initial-l1-base-fee ${l1BaseFee}`,
);
Comment on lines +90 to +92
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we want to wrap a docker run command

prepareGenesisHashes(params: {
  genesis,
  nitroNodeImageTag,
  initialParentBaseFee
})


// Step 3 - Extract hashes and output results
// genesisHashes has the following structure:
// BlockHash: 0xabcd, SendRoot: 0x1234, Batch: 1, PosInBatch: 0
const genesisHashesStr = genesisHashes.toString();
const genesisBlockHash = genesisHashesStr.split(',')[0].split(':')[1].trim();
const sendRootHash = genesisHashesStr.split(',')[1].split(':')[1].trim();
Comment on lines +97 to +99
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we don't end up creating a function that wraps the call to run the docker container, we might do something like this:

parseGenesisHashes(hashes: Hex)


console.log('');
console.log('-------------------');
console.log('Genesis information');
console.log('-------------------');
console.log('Genesis file generated: genesis.json');
console.log(`Block hash: ${genesisBlockHash}`);
console.log(`SendRoot hash: ${sendRootHash}`);

// Step 4 - Create Rollup (optional)
if (process.env.CREATE_ROLLUP === 'true') {
// load or generate a random batch poster account
const batchPosterPrivateKey = withFallbackPrivateKey(process.env.BATCH_POSTER_PRIVATE_KEY);
const batchPoster = privateKeyToAccount(batchPosterPrivateKey).address;

// load or generate a random validator account
const validatorPrivateKey = withFallbackPrivateKey(process.env.VALIDATOR_PRIVATE_KEY);
const validator = privateKeyToAccount(validatorPrivateKey).address;

// set the parent chain and create a public client for it
const parentChain = arbitrumSepolia;
const parentChainPublicClient = createPublicClient({
chain: parentChain,
transport: http(process.env.PARENT_CHAIN_RPC),
});

// load the deployer account
const deployer = privateKeyToAccount(sanitizePrivateKey(process.env.DEPLOYER_PRIVATE_KEY!));

// The following env variables must exist, otherwise the genesis generation would have failed
const chainId = Number(process.env.CHAIN_ID!);
const chainOwner = process.env.CHAIN_OWNER as Address;

// Load chain config from the genesis file
let genesisFileContents: string;
try {
genesisFileContents = fs.readFileSync('genesis.json', { encoding: 'utf8' });
} catch (err) {
console.error('Failed to read generated genesis.json file');
throw err;
}
const genesisConfiguration = JSON.parse(genesisFileContents);

// Check whether or not we need to deploy the L2 factories (since they are included in the genesis file by default)
const l2Factories = [
// Arachnid's deterministic deployment proxy
'0x4e59b44847b379578588920cA78FbF26c0B4956C',
// Zoltu's deterministic deployment proxy
'0x7A0D94F55792C434d74a40883C6ed8545E406D12',
// ERC-2470 Singleton Factory
'0xce0042B868300000d44A59004Da54A005ffdcf9f',
// ERC-1820: Pseudo-introspection Registry Contract
'0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24',
] as const;

const allocKeys = new Set(
Object.keys(genesisConfiguration.alloc ?? {}).map((k) => k.toLowerCase()),
);
const hasAllFactories = l2Factories.every((a) => allocKeys.has(a.toLowerCase()));

// Prepare the genesis assertion state
const genesisAssertionState = {
globalState: {
bytes32Vals: [genesisBlockHash as `0x${string}`, sendRootHash as `0x${string}`] as [
`0x${string}`,
`0x${string}`,
],
// Set inbox position to 1
u64Vals: [1n, 0n] as [bigint, bigint],
},
machineStatus: 0, // Running
endHistoryRoot: toHex(0, { size: 32 }),
};
Comment on lines +165 to +176
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prepareGenesisAssertionState(params: { genesisBlockHash: Hex, sendRootHash: Hex }): GenesisAssertionState


console.log('');
console.log('------------------');
console.log('Creating Rollup...');
console.log('------------------');
const createRollupConfig = createRollupPrepareDeploymentParamsConfig(parentChainPublicClient, {
chainId: BigInt(chainId),
owner: chainOwner,
chainConfig: genesisConfiguration.config,
genesisAssertionState,
});

try {
await createRollup({
params: {
config: createRollupConfig,
batchPosters: [batchPoster],
validators: [validator],
deployFactoriesToL2: !hasAllFactories,
},
account: deployer,
parentChainPublicClient,
});
} catch (error) {
console.error(`Rollup creation failed with error: ${error}`);
}
}
}

main();
13 changes: 13 additions & 0 deletions examples/generate-genesis-file/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "generate-genesis-file",
"private": true,
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "tsc --outDir dist && node ./dist/index.js"
},
"devDependencies": {
"@types/node": "^20.9.0",
"typescript": "^5.2.2"
}
}
4 changes: 4 additions & 0 deletions examples/generate-genesis-file/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "../tsconfig.base.json",
"include": ["./**/*"]
}
Loading