Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions audit-ci.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@
// https://github.com/cryptocoinjs/secp256k1-node/commit/dc37f41f2abfe87853b54bcd7d1b556db41b0c64#diff-7ae45ad102eab3b6d7e7896acd08c427a9b25b346470d7bc6507b6481575d519R35
// from: @arbitrum/token-bridge-contracts > @openzeppelin/upgrades-core > ethereumjs-util > ethereum-cryptography
"GHSA-584q-6j8j-r5pm",
// https://github.com/advisories/GHSA-848j-6mx2-7j84
// elliptic has no patched release yet (affected: <=6.6.1)
// transitive via ethereumjs-util -> ethereum-cryptography -> secp256k1 -> elliptic
// from: @safe-global/protocol-kit>ethereumjs-util>ethereum-cryptography>secp256k1>elliptic
"GHSA-848j-6mx2-7j84",
// https://github.com/advisories/GHSA-3xgq-45jj-v275
// cross-spawn command injection vulnerability
// Only used during development via audit-ci, nyc, and patch-package
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ exports[`creates a config for a chain on top of a custom parent chain 1`] = `
"futureSeconds": 5n,
},
"stakeToken": "0x0000000000000000000000000000000000000000",
"wasmModuleRoot": "0x28b6ad83ed87b21a87c73f7a0296a135ebc7074e449efb289ececccad771ccd6",
"wasmModuleRoot": "0xc2c02df561d4afaf9a1d6785f70098ec3874765c638e3cb6dbe8d3c83333e14c",
}
`;

Expand All @@ -38,7 +38,7 @@ exports[`creates config for a chain on top of arbitrum one with defaults 1`] = `
"futureSeconds": 3600n,
},
"stakeToken": "0x0000000000000000000000000000000000000000",
"wasmModuleRoot": "0x28b6ad83ed87b21a87c73f7a0296a135ebc7074e449efb289ececccad771ccd6",
"wasmModuleRoot": "0xc2c02df561d4afaf9a1d6785f70098ec3874765c638e3cb6dbe8d3c83333e14c",
}
`;

Expand Down Expand Up @@ -80,7 +80,7 @@ exports[`creates config for a chain on top of arbitrum sepolia with defaults 1`]
"futureSeconds": 3600n,
},
"stakeToken": "0x0000000000000000000000000000000000000000",
"wasmModuleRoot": "0x28b6ad83ed87b21a87c73f7a0296a135ebc7074e449efb289ececccad771ccd6",
"wasmModuleRoot": "0xc2c02df561d4afaf9a1d6785f70098ec3874765c638e3cb6dbe8d3c83333e14c",
}
`;

Expand Down Expand Up @@ -122,7 +122,7 @@ exports[`creates config for a chain on top of base sepolia with defaults 1`] = `
"futureSeconds": 3600n,
},
"stakeToken": "0x0000000000000000000000000000000000000000",
"wasmModuleRoot": "0x28b6ad83ed87b21a87c73f7a0296a135ebc7074e449efb289ececccad771ccd6",
"wasmModuleRoot": "0xc2c02df561d4afaf9a1d6785f70098ec3874765c638e3cb6dbe8d3c83333e14c",
}
`;

Expand All @@ -143,6 +143,6 @@ exports[`creates config for a chain on top of base with defaults 1`] = `
"futureSeconds": 3600n,
},
"stakeToken": "0x0000000000000000000000000000000000000000",
"wasmModuleRoot": "0x28b6ad83ed87b21a87c73f7a0296a135ebc7074e449efb289ececccad771ccd6",
"wasmModuleRoot": "0xc2c02df561d4afaf9a1d6785f70098ec3874765c638e3cb6dbe8d3c83333e14c",
}
`;
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ exports[`creates a config for a chain on top of a custom parent chain 1`] = `
},
"stakeToken": "0xabaF3D9f5cbDcE54cDc43b429d8f7154A3E19Afa",
"validatorAfkBlocks": 4n,
"wasmModuleRoot": "0x28b6ad83ed87b21a87c73f7a0296a135ebc7074e449efb289ececccad771ccd6",
"wasmModuleRoot": "0xc2c02df561d4afaf9a1d6785f70098ec3874765c638e3cb6dbe8d3c83333e14c",
}
`;

Expand Down Expand Up @@ -100,7 +100,7 @@ exports[`creates config for a chain on top of arbitrum one with defaults 1`] = `
},
"stakeToken": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
"validatorAfkBlocks": 201600n,
"wasmModuleRoot": "0x28b6ad83ed87b21a87c73f7a0296a135ebc7074e449efb289ececccad771ccd6",
"wasmModuleRoot": "0xc2c02df561d4afaf9a1d6785f70098ec3874765c638e3cb6dbe8d3c83333e14c",
}
`;

Expand Down Expand Up @@ -204,7 +204,7 @@ exports[`creates config for a chain on top of arbitrum sepolia with defaults 1`]
},
"stakeToken": "0x980B62Da83eFf3D4576C647993b0c1D7faf17c73",
"validatorAfkBlocks": 201600n,
"wasmModuleRoot": "0x28b6ad83ed87b21a87c73f7a0296a135ebc7074e449efb289ececccad771ccd6",
"wasmModuleRoot": "0xc2c02df561d4afaf9a1d6785f70098ec3874765c638e3cb6dbe8d3c83333e14c",
}
`;

Expand Down Expand Up @@ -308,7 +308,7 @@ exports[`creates config for a chain on top of base sepolia with defaults 1`] = `
},
"stakeToken": "0x4200000000000000000000000000000000000006",
"validatorAfkBlocks": 1209600n,
"wasmModuleRoot": "0x28b6ad83ed87b21a87c73f7a0296a135ebc7074e449efb289ececccad771ccd6",
"wasmModuleRoot": "0xc2c02df561d4afaf9a1d6785f70098ec3874765c638e3cb6dbe8d3c83333e14c",
}
`;

Expand Down Expand Up @@ -360,6 +360,6 @@ exports[`creates config for a chain on top of base with defaults 1`] = `
},
"stakeToken": "0x4200000000000000000000000000000000000006",
"validatorAfkBlocks": 1209600n,
"wasmModuleRoot": "0x28b6ad83ed87b21a87c73f7a0296a135ebc7074e449efb289ececccad771ccd6",
"wasmModuleRoot": "0xc2c02df561d4afaf9a1d6785f70098ec3874765c638e3cb6dbe8d3c83333e14c",
}
`;
41 changes: 40 additions & 1 deletion src/createRollupPrepareTransactionRequest-v2.1.unit.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { it, expect } from 'vitest';
import { createPublicClient, http, zeroAddress } from 'viem';
import { arbitrumSepolia } from 'viem/chains';
import { arbitrum, arbitrumSepolia } from 'viem/chains';

import { generateChainId } from './utils';
import { prepareChainConfig } from './prepareChainConfig';
Expand Down Expand Up @@ -353,6 +353,45 @@ it(`fails to prepare transaction request if "params.maxDataSize" is not provided
).rejects.toThrowError(`"params.maxDataSize" must be provided when using a custom parent chain.`);
});

it(`fails to prepare transaction request if "params.wasmModuleRoot" is blacklisted`, async () => {
const chainId = 123456;

// create the chain config
const chainConfig = prepareChainConfig({
chainId,
arbitrum: { InitialChainOwner: deployer.address, DataAvailabilityCommittee: true },
});

const wasmModuleRoot = '0x28b6ad83ed87b21a87c73f7a0296a135ebc7074e449efb289ececccad771ccd6';
const arbOSVersion = chainConfig.arbitrum.InitialArbOSVersion;

await expect(
createRollupPrepareTransactionRequest({
params: {
config: createRollupPrepareDeploymentParamsConfig(
publicClient,
{
chainId: BigInt(chainId),
owner: deployer.address,
chainConfig,
wasmModuleRoot,
},
'v2.1',
),
batchPosters: [deployer.address],
validators: [deployer.address],
},
value: createRollupDefaultRetryablesFees,
account: deployer.address,
publicClient,
gasOverrides: { gasLimit: { base: 1_000n } },
rollupCreatorVersion: 'v2.1',
}),
).rejects.toThrowError(
`Wasm module root ${wasmModuleRoot} is not supported. Please update your "wasmModuleRoot" to that of a Consensus version compatible with ArbOS ${arbOSVersion}.`,
);
});

it(`successfully prepares a transaction request with the default rollup creator and a gas limit override`, async () => {
// generate a random chain id
const chainId = generateChainId();
Expand Down
36 changes: 35 additions & 1 deletion src/createRollupPrepareTransactionRequest-v3.1.unit.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { it, expect } from 'vitest';
import { createPublicClient, http, zeroAddress } from 'viem';
import { arbitrumSepolia } from 'viem/chains';
import { arbitrum, arbitrumSepolia } from 'viem/chains';

import { generateChainId } from './utils';
import { prepareChainConfig } from './prepareChainConfig';
Expand Down Expand Up @@ -311,6 +311,40 @@ it(`fails to prepare transaction request if "params.maxDataSize" is not provided
).rejects.toThrowError(`"params.maxDataSize" must be provided when using a custom parent chain.`);
});

it(`fails to prepare transaction request if "params.wasmModuleRoot" is blacklisted`, async () => {
const chainId = 123456;

// create the chain config
const chainConfig = prepareChainConfig({
chainId,
arbitrum: { InitialChainOwner: deployer.address, DataAvailabilityCommittee: true },
});

const wasmModuleRoot = '0x28b6ad83ed87b21a87c73f7a0296a135ebc7074e449efb289ececccad771ccd6';
const arbOSVersion = chainConfig.arbitrum.InitialArbOSVersion;

await expect(
createRollupPrepareTransactionRequest({
params: {
config: createRollupPrepareDeploymentParamsConfig(publicClient, {
chainId: BigInt(chainId),
owner: deployer.address,
chainConfig,
wasmModuleRoot,
}),
batchPosters: [deployer.address],
validators: [deployer.address],
},
value: createRollupDefaultRetryablesFees,
account: deployer.address,
publicClient,
gasOverrides: { gasLimit: { base: 1_000n } },
}),
).rejects.toThrowError(
`Wasm module root ${wasmModuleRoot} is not supported. Please update your "wasmModuleRoot" to that of a Consensus version compatible with ArbOS ${arbOSVersion}.`,
);
});

it(`successfully prepares a transaction request with the default rollup creator and a gas limit override`, async () => {
// generate a random chain id
const chainId = generateChainId();
Expand Down
7 changes: 7 additions & 0 deletions src/createRollupPrepareTransactionRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ChainConfig } from './types/ChainConfig';
import { getRollupCreatorAddress } from './utils/getRollupCreatorAddress';
import { fetchDecimals } from './utils/erc20';
import { TransactionRequestGasOverrides, applyPercentIncrease } from './utils/gasOverrides';
import { isDisabledWasmModuleRoot } from './wasmModuleRoot';

import {
CreateRollupParams,
Expand Down Expand Up @@ -136,6 +137,12 @@ export async function createRollupPrepareTransactionRequest<TChain extends Chain
}
}

if (isDisabledWasmModuleRoot(wasmModuleRoot)) {
throw new Error(
`Wasm module root ${wasmModuleRoot} is not supported. Please update your "wasmModuleRoot" to that of a Consensus version compatible with ArbOS ${arbOSVersion}.`,
);
}

const paramsWithDefaults = { ...defaults, ...params, maxDataSize };
const createRollupGetCallValueParams = { ...paramsWithDefaults, account };

Expand Down
27 changes: 24 additions & 3 deletions src/wasmModuleRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ type ConsensusRelease = {
version: number;
wasmModuleRoot: Hex;
maxArbOSVersion: number;
isDisabled?: boolean;
};

const consensusReleases = [
Expand Down Expand Up @@ -78,12 +79,19 @@ const consensusReleases = [
version: 51,
wasmModuleRoot: '0x8a7513bf7bb3e3db04b0d982d0e973bcf57bf8b88aef7c6d03dba3a81a56a499',
maxArbOSVersion: 51,
isDisabled: true,
},
{
// https://github.com/OffchainLabs/nitro/releases/tag/consensus-v51.1
version: 51.1,
wasmModuleRoot: '0x28b6ad83ed87b21a87c73f7a0296a135ebc7074e449efb289ececccad771ccd6',
maxArbOSVersion: 51.1,
maxArbOSVersion: 51,
isDisabled: true,
},
{
// https://github.com/OffchainLabs/nitro/releases/tag/consensus-v51.1
version: 51.1,
wasmModuleRoot: '0xc2c02df561d4afaf9a1d6785f70098ec3874765c638e3cb6dbe8d3c83333e14c',
maxArbOSVersion: 51,
},
] as const satisfies readonly ConsensusRelease[];

Expand All @@ -102,7 +110,10 @@ export function getConsensusReleaseByVersion<TConsensusVersion extends Consensus
): GetConsensusReleaseByVersion<TConsensusVersion> {
const consensusRelease = consensusReleases
//
.find((release) => release.version === consensusVersion);
.find(
(release) =>
release.version === consensusVersion && !('isDisabled' in release && release.isDisabled),
);

return consensusRelease as GetConsensusReleaseByVersion<TConsensusVersion>;
}
Expand All @@ -129,3 +140,13 @@ export function isKnownWasmModuleRoot(wasmModuleRoot: Hex): wasmModuleRoot is Wa
.includes(wasmModuleRoot)
);
}

export function isDisabledWasmModuleRoot(wasmModuleRoot: Hex): boolean {
const lowerCaseWasmModuleRoot = wasmModuleRoot.toLowerCase();
return consensusReleases.some(
(release) =>
'isDisabled' in release &&
release.isDisabled &&
release.wasmModuleRoot.toLowerCase() === lowerCaseWasmModuleRoot,
);
}
20 changes: 19 additions & 1 deletion src/wasmModuleRoot.unit.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, it, expect } from 'vitest';

import { isKnownWasmModuleRoot } from './wasmModuleRoot';
import { isDisabledWasmModuleRoot, isKnownWasmModuleRoot } from './wasmModuleRoot';

describe('isKnownWasmModuleRoot', () => {
it('returns true for a known wasm module root', () => {
Expand Down Expand Up @@ -28,3 +28,21 @@ describe('isKnownWasmModuleRoot', () => {
).toEqual(false);
});
});

describe('isDisabledWasmModuleRoot', () => {
it('returns true for a blacklisted wasm module root', () => {
expect(
isDisabledWasmModuleRoot(
'0x8a7513bf7bb3e3db04b0d982d0e973bcf57bf8b88aef7c6d03dba3a81a56a499',
),
).toEqual(true);
});

it('returns false for a non-blacklisted wasm module root', () => {
expect(
isDisabledWasmModuleRoot(
'0xdb698a2576298f25448bc092e52cf13b1e24141c997135d70f217d674bbeb69a',
),
).toEqual(false);
});
});
Loading
Loading