diff --git a/foundry.toml b/foundry.toml index 7ba3e2bb..1bb66e83 100644 --- a/foundry.toml +++ b/foundry.toml @@ -8,14 +8,20 @@ optimizer = true optimizer_runs = 100 via_ir = false solc_version = '0.8.9' +remappings = ['ds-test/=lib/forge-std/lib/ds-test/src/', + 'forge-std/=lib/forge-std/src/', + '@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/', + '@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/'] [profile.yul] src = 'yul' out = 'out/yul' libs = ['node_modules', 'lib'] cache_path = 'forge-cache/yul' +remappings = [] +auto_detect_remappings = false [fmt] number_underscore = 'thousands' line_length = 100 -# See more config options https://github.com/foundry-rs/foundry/tree/master/config \ No newline at end of file +# See more config options https://github.com/foundry-rs/foundry/tree/master/config diff --git a/package.json b/package.json index 475926f8..72a41056 100644 --- a/package.json +++ b/package.json @@ -41,13 +41,15 @@ "postinstall": "patch-package", "deploy-factory": "hardhat run scripts/deployment.ts", "deploy-eth-rollup": "hardhat run scripts/createEthRollup.ts", - "deploy-erc20-rollup": "hardhat run scripts/createERC20Rollup.ts" + "deploy-erc20-rollup": "hardhat run scripts/createERC20Rollup.ts", + "create-rollup-testnode": "hardhat run scripts/local-deployment/deployCreatorAndCreateRollup.ts" }, "dependencies": { "@offchainlabs/upgrade-executor": "1.1.0-beta.0", "@openzeppelin/contracts": "4.5.0", "@openzeppelin/contracts-upgradeable": "4.5.2", - "patch-package": "^6.4.7" + "patch-package": "^6.4.7", + "solady": "0.0.182" }, "private": false, "devDependencies": { diff --git a/remappings.txt b/remappings.txt deleted file mode 100644 index f02afee2..00000000 --- a/remappings.txt +++ /dev/null @@ -1,5 +0,0 @@ -ds-test/=lib/forge-std/lib/ds-test/src/ -forge-std/=lib/forge-std/src/ - -@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/ -@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/ \ No newline at end of file diff --git a/scripts/config.ts.example b/scripts/config.ts.example index cf5d8704..78d020e0 100644 --- a/scripts/config.ts.example +++ b/scripts/config.ts.example @@ -29,5 +29,6 @@ export const config = { '0x1234123412341234123412341234123412341234', '0x1234512345123451234512345123451234512345', ], - batchPoster: '0x1234123412341234123412341234123412341234', + batchPosters: ['0x1234123412341234123412341234123412341234'], + batchPosterManager: '0x1234123412341234123412341234123412341234' } diff --git a/scripts/createERC20Rollup.ts b/scripts/createERC20Rollup.ts index 604fa17e..49947b27 100644 --- a/scripts/createERC20Rollup.ts +++ b/scripts/createERC20Rollup.ts @@ -29,8 +29,18 @@ async function main() { ) } + const rollupCreatorAddress = process.env.ROLLUP_CREATOR_ADDRESS + if (!rollupCreatorAddress) { + throw new Error('ROLLUP_CREATOR_ADDRESS not set') + } + console.log('Creating new rollup with', customFeeTokenAddress, 'as fee token') - await createRollup(customFeeTokenAddress) + await createRollup( + deployer, + false, + rollupCreatorAddress, + customFeeTokenAddress + ) } main() diff --git a/scripts/createEthRollup.ts b/scripts/createEthRollup.ts index eb11e83d..4248ac21 100644 --- a/scripts/createEthRollup.ts +++ b/scripts/createEthRollup.ts @@ -1,8 +1,17 @@ +import { ethers } from 'hardhat' import '@nomiclabs/hardhat-ethers' import { createRollup } from './rollupCreation' async function main() { - await createRollup() + const feeToken = undefined + const rollupCreatorAddress = process.env.ROLLUP_CREATOR_ADDRESS + if (!rollupCreatorAddress) { + throw new Error('ROLLUP_CREATOR_ADDRESS not set') + } + + const [signer] = await ethers.getSigners() + + await createRollup(signer, false, rollupCreatorAddress, feeToken) } main() diff --git a/scripts/deployment.ts b/scripts/deployment.ts index fdb51877..8da3acc2 100644 --- a/scripts/deployment.ts +++ b/scripts/deployment.ts @@ -1,13 +1,18 @@ import { ethers } from 'hardhat' import '@nomiclabs/hardhat-ethers' import { deployAllContracts } from './deploymentUtils' +import { maxDataSize } from './config' async function main() { const [signer] = await ethers.getSigners() try { // Deploying all contracts - const contracts = await deployAllContracts(signer) + const contracts = await deployAllContracts( + signer, + ethers.BigNumber.from(maxDataSize), + true + ) // Call setTemplates with the deployed contract addresses console.log('Waiting for the Template to be set on the Rollup Creator') diff --git a/scripts/deploymentUtils.ts b/scripts/deploymentUtils.ts index 7757fa25..51ce36ea 100644 --- a/scripts/deploymentUtils.ts +++ b/scripts/deploymentUtils.ts @@ -1,12 +1,11 @@ import { ethers } from 'hardhat' -import { ContractFactory, Contract, Overrides } from 'ethers' +import { ContractFactory, Contract, Overrides, BigNumber } from 'ethers' import '@nomiclabs/hardhat-ethers' import { run } from 'hardhat' import { abi as UpgradeExecutorABI, bytecode as UpgradeExecutorBytecode, } from '@offchainlabs/upgrade-executor/build/contracts/src/UpgradeExecutor.sol/UpgradeExecutor.json' -import { maxDataSize } from './config' import { Toolkit4844 } from '../test/contract/toolkit4844' import { ArbSys__factory } from '../build/types' import { ARB_SYS_ADDRESS } from '@arbitrum/sdk/dist/lib/dataEntities/constants' @@ -88,85 +87,139 @@ export async function deployUpgradeExecutor(signer: any): Promise { // Function to handle all deployments of core contracts using deployContract function export async function deployAllContracts( signer: any, + maxDataSize: BigNumber, + verify: boolean = true, hotshotAddr?: string ): Promise> { const isOnArb = await _isRunningOnArbitrum(signer) - const ethBridge = await deployContract('Bridge', signer, []) + const ethBridge = await deployContract('Bridge', signer, [], verify) const reader4844 = isOnArb ? ethers.constants.AddressZero : (await Toolkit4844.deployReader4844(signer)).address - const ethSequencerInbox = await deployContract('SequencerInbox', signer, [ - maxDataSize, - reader4844, - false, - ]) + const ethSequencerInbox = await deployContract( + 'SequencerInbox', + signer, + [maxDataSize, reader4844, false], + verify + ) - const ethInbox = await deployContract('Inbox', signer, [maxDataSize]) + const ethInbox = await deployContract('Inbox', signer, [maxDataSize], verify) const ethRollupEventInbox = await deployContract( 'RollupEventInbox', signer, - [] + [], + verify ) - const ethOutbox = await deployContract('Outbox', signer, []) + const ethOutbox = await deployContract('Outbox', signer, [], verify) - const erc20Bridge = await deployContract('ERC20Bridge', signer, []) - const erc20SequencerInbox = await deployContract('SequencerInbox', signer, [ - maxDataSize, - reader4844, - true, - ]) - const erc20Inbox = await deployContract('ERC20Inbox', signer, [maxDataSize]) + const erc20Bridge = await deployContract('ERC20Bridge', signer, [], verify) + const erc20SequencerInbox = await deployContract( + 'SequencerInbox', + signer, + [maxDataSize, reader4844, true], + verify + ) + const erc20Inbox = await deployContract( + 'ERC20Inbox', + signer, + [maxDataSize], + verify + ) const erc20RollupEventInbox = await deployContract( 'ERC20RollupEventInbox', signer, - [] + [], + verify ) - const erc20Outbox = await deployContract('ERC20Outbox', signer, []) + const erc20Outbox = await deployContract('ERC20Outbox', signer, [], verify) - const bridgeCreator = await deployContract('BridgeCreator', signer, [ - [ - ethBridge.address, - ethSequencerInbox.address, - ethInbox.address, - ethRollupEventInbox.address, - ethOutbox.address, - ], + const bridgeCreator = await deployContract( + 'BridgeCreator', + signer, [ - erc20Bridge.address, - erc20SequencerInbox.address, - erc20Inbox.address, - erc20RollupEventInbox.address, - erc20Outbox.address, + [ + ethBridge.address, + ethSequencerInbox.address, + ethInbox.address, + ethRollupEventInbox.address, + ethOutbox.address, + ], + [ + erc20Bridge.address, + erc20SequencerInbox.address, + erc20Inbox.address, + erc20RollupEventInbox.address, + erc20Outbox.address, + ], ], - ]) - const prover0 = await deployContract('OneStepProver0', signer) - const proverMem = await deployContract('OneStepProverMemory', signer) - const proverMath = await deployContract('OneStepProverMath', signer) + verify + ) + const prover0 = await deployContract('OneStepProver0', signer, [], verify) + const proverMem = await deployContract( + 'OneStepProverMemory', + signer, + [], + verify + ) + const proverMath = await deployContract( + 'OneStepProverMath', + signer, + [], + verify + ) const hostIoArg = hotshotAddr ? [hotshotAddr] : [] const proverHostIo = await deployContract( 'OneStepProverHostIo', signer, - hostIoArg - ) - const osp: Contract = await deployContract('OneStepProofEntry', signer, [ - prover0.address, - proverMem.address, - proverMath.address, - proverHostIo.address, - ]) - const challengeManager = await deployContract('ChallengeManager', signer) - const rollupAdmin = await deployContract('RollupAdminLogic', signer) - const rollupUser = await deployContract('RollupUserLogic', signer) + hostIoArg, + verify + ) + const osp: Contract = await deployContract( + 'OneStepProofEntry', + signer, + [ + prover0.address, + proverMem.address, + proverMath.address, + proverHostIo.address, + ], + verify + ) + const challengeManager = await deployContract( + 'ChallengeManager', + signer, + [], + verify + ) + const rollupAdmin = await deployContract( + 'RollupAdminLogic', + signer, + [], + verify + ) + const rollupUser = await deployContract('RollupUserLogic', signer, [], verify) const upgradeExecutor = await deployUpgradeExecutor(signer) - const validatorUtils = await deployContract('ValidatorUtils', signer) + const validatorUtils = await deployContract( + 'ValidatorUtils', + signer, + [], + verify + ) const validatorWalletCreator = await deployContract( 'ValidatorWalletCreator', - signer + signer, + [], + verify + ) + const rollupCreator = await deployContract( + 'RollupCreator', + signer, + [], + verify ) - const rollupCreator = await deployContract('RollupCreator', signer) - const deployHelper = await deployContract('DeployHelper', signer) + const deployHelper = await deployContract('DeployHelper', signer, [], verify) return { bridgeCreator, prover0, @@ -186,7 +239,7 @@ export async function deployAllContracts( } // Check if we're deploying to an Arbitrum chain -async function _isRunningOnArbitrum(signer: any): Promise { +export async function _isRunningOnArbitrum(signer: any): Promise { const arbSys = ArbSys__factory.connect(ARB_SYS_ADDRESS, signer) try { await arbSys.arbOSVersion() diff --git a/scripts/local-deployment/deployCreatorAndCreateRollup.ts b/scripts/local-deployment/deployCreatorAndCreateRollup.ts new file mode 100644 index 00000000..0b7f7367 --- /dev/null +++ b/scripts/local-deployment/deployCreatorAndCreateRollup.ts @@ -0,0 +1,117 @@ +import { ethers } from 'hardhat' +import '@nomiclabs/hardhat-ethers' +import { deployAllContracts } from '../deploymentUtils' +import { createRollup } from '../rollupCreation' +import { promises as fs } from 'fs' +import { BigNumber } from 'ethers' + +async function main() { + /// read env vars needed for deployment + let childChainName = process.env.CHILD_CHAIN_NAME as string + if (!childChainName) { + throw new Error('CHILD_CHAIN_NAME not set') + } + + let deployerPrivKey = process.env.DEPLOYER_PRIVKEY as string + if (!deployerPrivKey) { + throw new Error('DEPLOYER_PRIVKEY not set') + } + + let parentChainRpc = process.env.PARENT_CHAIN_RPC as string + if (!parentChainRpc) { + throw new Error('PARENT_CHAIN_RPC not set') + } + + if (!process.env.PARENT_CHAIN_ID) { + throw new Error('PARENT_CHAIN_ID not set') + } + + const deployerWallet = new ethers.Wallet( + deployerPrivKey, + new ethers.providers.JsonRpcProvider(parentChainRpc) + ) + + const maxDataSize = + process.env.MAX_DATA_SIZE !== undefined + ? ethers.BigNumber.from(process.env.MAX_DATA_SIZE) + : ethers.BigNumber.from(117964) + + /// get fee token address, if undefined use address(0) to have ETH as fee token + let feeToken = process.env.FEE_TOKEN_ADDRESS as string + if (!feeToken) { + feeToken = ethers.constants.AddressZero + } + console.log('Fee token address:', feeToken) + + /// deploy templates and rollup creator + console.log('Deploy RollupCreator') + const contracts = await deployAllContracts(deployerWallet, maxDataSize, false) + + console.log('Set templates on the Rollup Creator') + await ( + await contracts.rollupCreator.setTemplates( + contracts.bridgeCreator.address, + contracts.osp.address, + contracts.challengeManager.address, + contracts.rollupAdmin.address, + contracts.rollupUser.address, + contracts.upgradeExecutor.address, + contracts.validatorUtils.address, + contracts.validatorWalletCreator.address, + contracts.deployHelper.address, + { gasLimit: BigNumber.from('300000') } + ) + ).wait() + + /// Create rollup + const chainId = (await deployerWallet.provider.getNetwork()).chainId + console.log( + 'Create rollup on top of chain', + chainId, + 'using RollupCreator', + contracts.rollupCreator.address + ) + const result = await createRollup( + deployerWallet, + true, + contracts.rollupCreator.address, + feeToken + ) + + if (!result) { + throw new Error('Rollup creation failed') + } + + const { rollupCreationResult, chainInfo } = result + + /// store deployment address + // chain deployment info + const chainDeploymentInfo = + process.env.CHAIN_DEPLOYMENT_INFO !== undefined + ? process.env.CHAIN_DEPLOYMENT_INFO + : 'deploy.json' + await fs.writeFile( + chainDeploymentInfo, + JSON.stringify(rollupCreationResult, null, 2), + 'utf8' + ) + + // child chain info + chainInfo['chain-name'] = childChainName + const childChainInfo = + process.env.CHILD_CHAIN_INFO !== undefined + ? process.env.CHILD_CHAIN_INFO + : 'l2_chain_info.json' + await fs.writeFile( + childChainInfo, + JSON.stringify([chainInfo], null, 2), + 'utf8' + ) +} + +main() + .then(() => process.exit(0)) + .catch((error: Error) => { + console.error(error) + process.exit(1) + }) diff --git a/scripts/rollupCreation.ts b/scripts/rollupCreation.ts index 82e40cb5..752b480a 100644 --- a/scripts/rollupCreation.ts +++ b/scripts/rollupCreation.ts @@ -3,9 +3,11 @@ import '@nomiclabs/hardhat-ethers' import { run } from 'hardhat' import { abi as rollupCreatorAbi } from '../build/contracts/src/rollup/RollupCreator.sol/RollupCreator.json' import { config, maxDataSize } from './config' -import { BigNumber } from 'ethers' +import { BigNumber, Signer } from 'ethers' import { IERC20__factory } from '../build/types' import { sleep } from './testSetup' +import { promises as fs } from 'fs' +import { _isRunningOnArbitrum } from './deploymentUtils' // 1 gwei const MAX_FER_PER_GAS = BigNumber.from('1000000000') @@ -15,6 +17,7 @@ interface RollupCreatedEvent { address: string args?: { rollupAddress: string + nativeToken: string inboxAddress: string outbox: string rollupEventInbox: string @@ -22,47 +25,62 @@ interface RollupCreatedEvent { adminProxy: string sequencerInbox: string bridge: string + upgradeExecutor: string validatorUtils: string validatorWalletCreator: string } } -export async function createRollup(feeToken?: string) { - const rollupCreatorAddress = process.env.ROLLUP_CREATOR_ADDRESS +interface RollupCreationResult { + bridge: string + inbox: string + 'sequencer-inbox': string + 'deployed-at': number + rollup: string + 'native-token': string + 'upgrade-executor': string + 'validator-utils': string + 'validator-wallet-creator': string +} - if (!rollupCreatorAddress) { - console.error( - 'Please provide ROLLUP_CREATOR_ADDRESS as an environment variable.' - ) - process.exit(1) - } +interface ChainInfo { + 'chain-name': string + 'parent-chain-id': number + 'parent-chain-is-arbitrum': boolean + 'chain-config': any + rollup: RollupCreationResult + 'sequencer-url': string + 'secondary-forwarding-target': string + 'feed-url': string + 'secondary-feed-url': string + 'das-index-url': string + 'has-genesis-state': boolean +} +export async function createRollup( + signer: Signer, + isDevDeployment: boolean, + rollupCreatorAddress: string, + feeToken: string +): Promise<{ + rollupCreationResult: RollupCreationResult + chainInfo: ChainInfo +} | null> { if (!rollupCreatorAbi) { throw new Error( 'You need to first run script to deploy and compile the contracts first' ) } - const [signer] = await ethers.getSigners() - const rollupCreator = new ethers.Contract( rollupCreatorAddress, rollupCreatorAbi, signer ) - - if (!feeToken) { - feeToken = ethers.constants.AddressZero - } + const validatorWalletCreator = await rollupCreator.validatorWalletCreator() try { - let vals: boolean[] = [] - for (let i = 0; i < config.validators.length; i++) { - vals.push(true) - } - //// funds for deploying L2 factories - // 0.13 ETH is enough to deploy L2 factories via retryables. Excess is refunded let feeCost = ethers.utils.parseEther('0.13') if (feeToken != ethers.constants.AddressZero) { @@ -78,15 +96,19 @@ export async function createRollup(feeToken?: string) { // Call the createRollup function console.log('Calling createRollup to generate a new rollup ...') - const deployParams = { - config: config.rollupConfig, - batchPoster: config.batchPoster, - validators: config.validators, - maxDataSize: maxDataSize, - nativeToken: feeToken, - deployFactoriesToL2: true, - maxFeePerGasForRetryables: MAX_FER_PER_GAS, - } + const deployParams = isDevDeployment + ? await _getDevRollupConfig(feeToken, validatorWalletCreator) + : { + config: config.rollupConfig, + validators: config.validators, + maxDataSize: ethers.BigNumber.from(maxDataSize), + nativeToken: feeToken, + deployFactoriesToL2: true, + maxFeePerGasForRetryables: MAX_FER_PER_GAS, + batchPosters: config.batchPosters, + batchPosterManager: config.batchPosterManager, + } + const createRollupTx = await rollupCreator.createRollup(deployParams, { value: feeCost, }) @@ -101,6 +123,7 @@ export async function createRollup(feeToken?: string) { // Checking for RollupCreated event for new rollup address if (rollupCreatedEvent) { const rollupAddress = rollupCreatedEvent.args?.rollupAddress + const nativeToken = rollupCreatedEvent.args?.nativeToken const inboxAddress = rollupCreatedEvent.args?.inboxAddress const outbox = rollupCreatedEvent.args?.outbox const rollupEventInbox = rollupCreatedEvent.args?.rollupEventInbox @@ -108,32 +131,37 @@ export async function createRollup(feeToken?: string) { const adminProxy = rollupCreatedEvent.args?.adminProxy const sequencerInbox = rollupCreatedEvent.args?.sequencerInbox const bridge = rollupCreatedEvent.args?.bridge + const upgradeExecutor = rollupCreatedEvent.args?.upgradeExecutor const validatorUtils = rollupCreatedEvent.args?.validatorUtils const validatorWalletCreator = rollupCreatedEvent.args?.validatorWalletCreator console.log("Congratulations! 🎉🎉🎉 All DONE! Here's your addresses:") console.log('RollupProxy Contract created at address:', rollupAddress) - console.log('Wait a minute before starting the contract verification') - await sleep(1 * 60 * 1000) - console.log( - `Attempting to verify Rollup contract at address ${rollupAddress}...` - ) - try { - await run('verify:verify', { - contract: 'src/rollup/RollupProxy.sol:RollupProxy', - address: rollupAddress, - constructorArguments: [], - }) - } catch (error: any) { - if (error.message.includes('Already Verified')) { - console.log(`Contract RollupProxy is already verified.`) - } else { - console.error( - `Verification for RollupProxy failed with the following error: ${error.message}` - ) + + if (!isDevDeployment) { + console.log('Wait a minute before starting the contract verification') + await sleep(1 * 60 * 1000) + console.log( + `Attempting to verify Rollup contract at address ${rollupAddress}...` + ) + try { + await run('verify:verify', { + contract: 'src/rollup/RollupProxy.sol:RollupProxy', + address: rollupAddress, + constructorArguments: [], + }) + } catch (error: any) { + if (error.message.includes('Already Verified')) { + console.log(`Contract RollupProxy is already verified.`) + } else { + console.error( + `Verification for RollupProxy failed with the following error: ${error.message}` + ) + } } } + console.log('Inbox (proxy) Contract created at address:', inboxAddress) console.log('Outbox (proxy) Contract created at address:', outbox) console.log( @@ -155,6 +183,34 @@ export async function createRollup(feeToken?: string) { const blockNumber = createRollupReceipt.blockNumber console.log('All deployed at block number:', blockNumber) + + const rollupCreationResult: RollupCreationResult = { + bridge: bridge, + inbox: inboxAddress, + 'sequencer-inbox': sequencerInbox, + 'deployed-at': blockNumber, + rollup: rollupAddress, + 'native-token': nativeToken, + 'upgrade-executor': upgradeExecutor, + 'validator-utils': validatorUtils, + 'validator-wallet-creator': validatorWalletCreator, + } + + const chainInfo: ChainInfo = { + 'chain-name': 'dev-chain', + 'parent-chain-id': +process.env.PARENT_CHAIN_ID!, + 'parent-chain-is-arbitrum': await _isRunningOnArbitrum(signer), + 'sequencer-url': '', + 'secondary-forwarding-target': '', + 'feed-url': '', + 'secondary-feed-url': '', + 'das-index-url': '', + 'has-genesis-state': false, + 'chain-config': JSON.parse(deployParams.config.chainConfig), + rollup: rollupCreationResult, + } + + return { rollupCreationResult, chainInfo } } else { console.error('RollupCreated event not found') } @@ -164,4 +220,121 @@ export async function createRollup(feeToken?: string) { error instanceof Error ? error.message : error ) } + + return null +} + +async function _getDevRollupConfig( + feeToken: string, + validatorWalletCreator: string +) { + // set up owner address + const ownerAddress = + process.env.OWNER_ADDRESS !== undefined ? process.env.OWNER_ADDRESS : '' + + // set up max data size + const _maxDataSize = + process.env.MAX_DATA_SIZE !== undefined + ? ethers.BigNumber.from(process.env.MAX_DATA_SIZE) + : ethers.BigNumber.from(117964) + + // set up validators + const authorizeValidators: number = + parseInt(process.env.AUTHORIZE_VALIDATORS as string, 0) || 0 + const validators: string[] = [] + for (let i = 1; i <= authorizeValidators; i++) { + validators.push(_createValidatorAddress(validatorWalletCreator, i)) + } + + // get chain config + const childChainConfigPath = + process.env.CHILD_CHAIN_CONFIG_PATH !== undefined + ? process.env.CHILD_CHAIN_CONFIG_PATH + : 'l2_chain_config.json' + + const chainConfig = await fs.readFile(childChainConfigPath, { + encoding: 'utf8', + }) + + // get wasmModuleRoot + const wasmModuleRoot = + process.env.WASM_MODULE_ROOT !== undefined + ? process.env.WASM_MODULE_ROOT + : '' + + // set up batch posters + const sequencerAddress = + process.env.SEQUENCER_ADDRESS !== undefined + ? process.env.SEQUENCER_ADDRESS + : '' + const batchPostersString = + process.env.BATCH_POSTERS !== undefined ? process.env.BATCH_POSTERS : '' + let batchPosters: string[] = [] + if (batchPostersString.length == 0) { + batchPosters.push(sequencerAddress) + } else { + const batchPostesArr = batchPostersString.split(',') + for (let i = 0; i < batchPostesArr.length; i++) { + if (ethers.utils.isAddress(batchPostesArr[i])) { + batchPosters.push(batchPostesArr[i]) + } else { + throw new Error('Invalid address in batch posters array') + } + } + } + + // set up batch poster manager + const batchPosterManagerEnv = + process.env.BATCH_POSTER_MANAGER !== undefined + ? process.env.BATCH_POSTER_MANAGER + : '' + let batchPosterManager = '' + if (ethers.utils.isAddress(batchPosterManagerEnv)) { + batchPosterManager = batchPosterManagerEnv + } else { + if (batchPosterManagerEnv.length == 0) { + batchPosterManager = ownerAddress + } else { + throw new Error('Invalid address for batch poster manager') + } + } + + return { + config: { + confirmPeriodBlocks: ethers.BigNumber.from('20'), + extraChallengeTimeBlocks: ethers.BigNumber.from('200'), + stakeToken: ethers.constants.AddressZero, + baseStake: ethers.utils.parseEther('1'), + wasmModuleRoot: wasmModuleRoot, + owner: ownerAddress, + loserStakeEscrow: ethers.constants.AddressZero, + chainId: JSON.parse(chainConfig)['chainId'], + chainConfig: chainConfig, + genesisBlockNum: 0, + sequencerInboxMaxTimeVariation: { + delayBlocks: ethers.BigNumber.from('5760'), + futureBlocks: ethers.BigNumber.from('12'), + delaySeconds: ethers.BigNumber.from('86400'), + futureSeconds: ethers.BigNumber.from('3600'), + }, + }, + validators: validators, + maxDataSize: _maxDataSize, + nativeToken: feeToken, + deployFactoriesToL2: true, + maxFeePerGasForRetryables: MAX_FER_PER_GAS, + batchPosters: batchPosters, + batchPosterManager: batchPosterManager, + } + + function _createValidatorAddress( + deployerAddress: string, + nonce: number + ): string { + const nonceHex = BigNumber.from(nonce).toHexString() + return ethers.utils.getContractAddress({ + from: deployerAddress, + nonce: nonceHex, + }) + } } diff --git a/src/chain/CacheManager.sol b/src/chain/CacheManager.sol new file mode 100644 index 00000000..434560a8 --- /dev/null +++ b/src/chain/CacheManager.sol @@ -0,0 +1,208 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.0; +import "../precompiles/ArbOwnerPublic.sol"; +import "../precompiles/ArbWasm.sol"; +import "../precompiles/ArbWasmCache.sol"; +import "solady/src/utils/MinHeapLib.sol"; + +contract CacheManager { + using MinHeapLib for MinHeapLib.Heap; + + ArbOwnerPublic internal constant ARB_OWNER_PUBLIC = ArbOwnerPublic(address(0x6b)); + ArbWasm internal constant ARB_WASM = ArbWasm(address(0x71)); + ArbWasmCache internal constant ARB_WASM_CACHE = ArbWasmCache(address(0x72)); + uint64 internal constant MAX_MAKE_SPACE = 5 * 1024 * 1024; + + MinHeapLib.Heap internal bids; + Entry[] public entries; + + uint64 public cacheSize; + uint64 public queueSize; + uint64 public decay; + bool public isPaused; + + error NotChainOwner(address sender); + error AsmTooLarge(uint256 asm, uint256 queueSize, uint256 cacheSize); + error AlreadyCached(bytes32 codehash); + error BidTooSmall(uint192 bid, uint192 min); + error BidsArePaused(); + error MakeSpaceTooLarge(uint64 size, uint64 limit); + + event InsertBid(bytes32 indexed codehash, uint192 bid, uint64 size); + event DeleteBid(bytes32 indexed codehash, uint192 bid, uint64 size); + event SetCacheSize(uint64 size); + event SetDecayRate(uint64 decay); + event Pause(); + event Unpause(); + + struct Entry { + bytes32 code; + uint64 size; + } + + constructor(uint64 initCacheSize, uint64 initDecay) { + cacheSize = initCacheSize; + decay = initDecay; + } + + modifier onlyOwner() { + if (!ARB_OWNER_PUBLIC.isChainOwner(msg.sender)) { + revert NotChainOwner(msg.sender); + } + _; + } + + /// Sets the intended cache size. Note that the queue may temporarily be larger. + function setCacheSize(uint64 newSize) external onlyOwner { + cacheSize = newSize; + emit SetCacheSize(newSize); + } + + /// Sets the intended decay factor. Does not modify existing bids. + function setDecayRate(uint64 newDecay) external onlyOwner { + decay = newDecay; + emit SetDecayRate(newDecay); + } + + /// Disable new bids. + function paused() external onlyOwner { + isPaused = true; + emit Pause(); + } + + /// Enable new bids. + function unpause() external onlyOwner { + isPaused = false; + emit Unpause(); + } + + /// Evicts all programs in the cache. + function evictAll() external onlyOwner { + evictPrograms(type(uint256).max); + delete entries; + } + + /// Evicts up to `count` programs from the cache. + function evictPrograms(uint256 count) public onlyOwner { + while (bids.length() != 0 && count > 0) { + (uint192 bid, uint64 index) = _getBid(bids.pop()); + _deleteEntry(bid, index); + count -= 1; + } + } + + /// Sends all revenue to the network fee account. + function sweepFunds() external { + (bool success, bytes memory data) = ARB_OWNER_PUBLIC.getNetworkFeeAccount().call{ + value: address(this).balance + }(""); + if (!success) { + assembly { + revert(add(data, 32), mload(data)) + } + } + } + + /// Places a bid, reverting if payment is insufficient. + function placeBid(bytes32 codehash) external payable { + if (isPaused) { + revert BidsArePaused(); + } + if (_isCached(codehash)) { + revert AlreadyCached(codehash); + } + + uint64 asm = _asmSize(codehash); + (uint192 bid, uint64 index) = _makeSpace(asm); + return _addBid(bid, codehash, asm, index); + } + + /// Evicts entries until enough space exists in the cache, reverting if payment is insufficient. + /// Returns the new amount of space available on success. + /// Note: will revert for requests larger than 5Mb. Call repeatedly for more. + function makeSpace(uint64 size) external payable returns (uint64 space) { + if (isPaused) { + revert BidsArePaused(); + } + if (size > MAX_MAKE_SPACE) { + revert MakeSpaceTooLarge(size, MAX_MAKE_SPACE); + } + _makeSpace(size); + return cacheSize - queueSize; + } + + /// Evicts entries until enough space exists in the cache, reverting if payment is insufficient. + /// Returns the bid and the index to use for insertion. + function _makeSpace(uint64 size) internal returns (uint192 bid, uint64 index) { + // discount historical bids by the number of seconds + bid = uint192(msg.value + block.timestamp * uint256(decay)); + index = uint64(entries.length); + + uint192 min; + uint64 limit = cacheSize; + while (queueSize + size > limit) { + (min, index) = _getBid(bids.pop()); + _deleteEntry(min, index); + } + if (bid < min) { + revert BidTooSmall(bid, min); + } + } + + /// Adds a bid + function _addBid( + uint192 bid, + bytes32 code, + uint64 size, + uint64 index + ) internal { + if (queueSize + size > cacheSize) { + revert AsmTooLarge(size, queueSize, cacheSize); + } + + Entry memory entry = Entry({size: size, code: code}); + ARB_WASM_CACHE.cacheCodehash(code); + bids.push(_packBid(bid, index)); + queueSize += size; + if (index == entries.length) { + entries.push(entry); + } else { + entries[index] = entry; + } + emit InsertBid(code, bid, size); + } + + /// Clears the entry at the given index + function _deleteEntry(uint192 bid, uint64 index) internal { + Entry memory entry = entries[index]; + ARB_WASM_CACHE.evictCodehash(entry.code); + queueSize -= entry.size; + emit DeleteBid(entry.code, bid, entry.size); + delete entries[index]; + } + + /// Gets the bid and index from a packed bid item + function _getBid(uint256 info) internal pure returns (uint192 bid, uint64 index) { + bid = uint192(info >> 64); + index = uint64(info); + } + + /// Creates a packed bid item + function _packBid(uint192 bid, uint64 index) internal pure returns (uint256) { + return (uint256(bid) << 64) | uint256(index); + } + + /// Gets the size of the given program in bytes + function _asmSize(bytes32 codehash) internal view returns (uint64) { + uint32 size = ARB_WASM.codehashAsmSize(codehash); + return uint64(size >= 4096 ? size : 4096); // pretend it's at least 4Kb + } + + /// Determines whether a program is cached + function _isCached(bytes32 codehash) internal view returns (bool) { + return ARB_WASM_CACHE.codehashIsCached(codehash); + } +} diff --git a/src/challenge/ChallengeLib.sol b/src/challenge/ChallengeLib.sol index 25ff894d..6aaa3579 100644 --- a/src/challenge/ChallengeLib.sol +++ b/src/challenge/ChallengeLib.sol @@ -48,51 +48,6 @@ library ChallengeLib { return challenge.timeUsedSinceLastMove() > challenge.current.timeLeft; } - function getStartMachineHash(bytes32 globalStateHash, bytes32 wasmModuleRoot) - internal - pure - returns (bytes32) - { - // Start the value stack with the function call ABI for the entrypoint - Value[] memory startingValues = new Value[](3); - startingValues[0] = ValueLib.newRefNull(); - startingValues[1] = ValueLib.newI32(0); - startingValues[2] = ValueLib.newI32(0); - ValueArray memory valuesArray = ValueArray({inner: startingValues}); - ValueStack memory values = ValueStack({proved: valuesArray, remainingHash: 0}); - ValueStack memory internalStack; - StackFrameWindow memory frameStack; - - Machine memory mach = Machine({ - status: MachineStatus.RUNNING, - valueStack: values, - internalStack: internalStack, - frameStack: frameStack, - globalStateHash: globalStateHash, - moduleIdx: 0, - functionIdx: 0, - functionPc: 0, - modulesRoot: wasmModuleRoot - }); - return mach.hash(); - } - - function getEndMachineHash(MachineStatus status, bytes32 globalStateHash) - internal - pure - returns (bytes32) - { - if (status == MachineStatus.FINISHED) { - return keccak256(abi.encodePacked("Machine finished:", globalStateHash)); - } else if (status == MachineStatus.ERRORED) { - return keccak256(abi.encodePacked("Machine errored:")); - } else if (status == MachineStatus.TOO_FAR) { - return keccak256(abi.encodePacked("Machine too far:")); - } else { - revert("BAD_BLOCK_STATUS"); - } - } - function extractChallengeSegment(SegmentSelection calldata selection) internal pure diff --git a/src/challenge/ChallengeManager.sol b/src/challenge/ChallengeManager.sol index c5427e7e..fc57b5eb 100644 --- a/src/challenge/ChallengeManager.sol +++ b/src/challenge/ChallengeManager.sol @@ -35,6 +35,7 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { ISequencerInbox public sequencerInbox; IBridge public bridge; IOneStepProofEntry public osp; + mapping(bytes32 => IOneStepProofEntry) public ospCond; function challengeInfo(uint64 challengeIndex) external @@ -110,12 +111,28 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { osp = osp_; } - function postUpgradeInit(IOneStepProofEntry osp_) external onlyDelegated onlyProxyOwner { - // when updating to 4844 we need to create new osp contracts and set them here - // on the challenge manager + /// @dev A osp breaking change is introduced as part of Stylus upgrade, where the new osp would not support + /// pre-Stylus legacy wasmModuleRoot. To ensure that the new osp is not used for legacy wasmModuleRoot, + /// we introduce a conditional OSP where condRoot should be set to the pre-Stylus root and condOsp should + /// be set to the pre-Stylus osp. The correct value should be handled by the upgrade action contract. + function postUpgradeInit( + IOneStepProofEntry osp_, + bytes32 condRoot, + IOneStepProofEntry condOsp + ) external onlyDelegated onlyProxyOwner { + ospCond[condRoot] = condOsp; osp = osp_; } + function getOsp(bytes32 wasmModuleRoot) public view returns (IOneStepProofEntry) { + IOneStepProofEntry t = ospCond[wasmModuleRoot]; + if (address(t) == address(0)) { + return osp; + } else { + return t; + } + } + function createChallenge( bytes32 wasmModuleRoot_, MachineStatus[2] calldata startAndEndMachineStatuses_, @@ -233,11 +250,9 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { } bytes32[] memory segments = new bytes32[](2); - segments[0] = ChallengeLib.getStartMachineHash( - globalStateHashes[0], - challenge.wasmModuleRoot - ); - segments[1] = ChallengeLib.getEndMachineHash(machineStatuses[1], globalStateHashes[1]); + IOneStepProofEntry _osp = getOsp(challenge.wasmModuleRoot); + segments[0] = _osp.getStartMachineHash(globalStateHashes[0], challenge.wasmModuleRoot); + segments[1] = _osp.getEndMachineHash(machineStatuses[1], globalStateHashes[1]); challenge.mode = ChallengeLib.ChallengeMode.EXECUTION; @@ -259,7 +274,7 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { require(challengeLength == 1, "TOO_LONG"); } - bytes32 afterHash = osp.proveOneStep( + bytes32 afterHash = getOsp(challenge.wasmModuleRoot).proveOneStep( ExecutionContext({maxInboxMessagesRead: challenge.maxInboxMessages, bridge: bridge}), challengeStart, selection.oldSegments[selection.challengePosition], diff --git a/src/challenge/IChallengeManager.sol b/src/challenge/IChallengeManager.sol index b6f63d67..236829b1 100644 --- a/src/challenge/IChallengeManager.sol +++ b/src/challenge/IChallengeManager.sol @@ -47,6 +47,19 @@ interface IChallengeManager { IOneStepProofEntry osp_ ) external; + function postUpgradeInit( + IOneStepProofEntry osp_, + bytes32 condRoot, + IOneStepProofEntry condOsp + ) external; + + /// @notice Get the default osp, which is used for all wasm module roots that don't have a conditional OSP set + /// Use getOsp(wasmModuleRoot) to get the OSP for a specific wasm module root + function osp() external view returns (IOneStepProofEntry); + + /// @notice Get the OSP for a given wasm module root + function getOsp(bytes32 wasmModuleRoot) external view returns (IOneStepProofEntry); + function createChallenge( bytes32 wasmModuleRoot_, MachineStatus[2] calldata startAndEndMachineStatuses_, diff --git a/src/mocks/Benchmarks.sol b/src/mocks/Benchmarks.sol new file mode 100644 index 00000000..66d5ebba --- /dev/null +++ b/src/mocks/Benchmarks.sol @@ -0,0 +1,52 @@ +// Copyright 2022-2023, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.0; + +contract Benchmarks { + function fillBlockRecover() external payable { + bytes32 bridgeToNova = 0xeddecf107b5740cef7f5a01e3ea7e287665c4e75a8eb6afae2fda2e3d4367786; + address cryptoIsCute = 0x361594F5429D23ECE0A88E4fBE529E1c49D524d8; + uint8 v = 27; + bytes32 r = 0xc6178c2de1078cd36c3bd302cde755340d7f17fcb3fcc0b9c333ba03b217029f; + bytes32 s = 0x5fdbcefe2675e96219cdae57a7894280bf80fd40d44ce146a35e169ea6a78fd3; + while (true) { + require(ecrecover(bridgeToNova, v, r, s) == cryptoIsCute, "WRONG_ARBINAUT"); + } + } + + function fillBlockMulMod() external payable { + uint256 value = 0xeddecf107b5740cef7f5a01e3ea7e287665c4e75a8eb6afae2fda2e3d4367786; + while (true) { + value = mulmod( + value, + 0xc6178c2de1078cd36c3bd302cde755340d7f17fcb3fcc0b9c333ba03b217029f, + 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f + ); + } + } + + function fillBlockHash() external payable { + bytes32 hash = 0xeddecf107b5740cef7f5a01e3ea7e287665c4e75a8eb6afae2fda2e3d4367786; + while (true) { + hash = keccak256(abi.encodePacked(hash)); + } + } + + function fillBlockAdd() external payable { + uint256 value = 0; + while (true) { + unchecked { + value += 0xeddecf107b5740cef7f5a01e3ea7e287665c4e75a8eb6afae2fda2e3d4367786; + } + } + } + + function fillBlockQuickStep() external payable { + uint256 value = 0; + while (true) { + value = msg.value; + } + } +} diff --git a/src/mocks/PendingBlkTimeAndNrAdvanceCheck.sol b/src/mocks/PendingBlkTimeAndNrAdvanceCheck.sol index 423175ba..0676845a 100644 --- a/src/mocks/PendingBlkTimeAndNrAdvanceCheck.sol +++ b/src/mocks/PendingBlkTimeAndNrAdvanceCheck.sol @@ -9,14 +9,21 @@ import "../precompiles/ArbSys.sol"; contract PendingBlkTimeAndNrAdvanceCheck { uint256 immutable deployedAt; uint256 immutable deployedAtBlock; + ArbSys constant ARB_SYS = ArbSys(address(100)); constructor() { deployedAt = block.timestamp; - deployedAtBlock = ArbSys(address(100)).arbBlockNumber(); + deployedAtBlock = ARB_SYS.arbBlockNumber(); } function isAdvancing() external { require(block.timestamp > deployedAt, "Time didn't advance"); - require(ArbSys(address(100)).arbBlockNumber() > deployedAtBlock, "Block didn't advance"); + require(ARB_SYS.arbBlockNumber() > deployedAtBlock, "Block didn't advance"); + } + + function checkArbBlockHashReturnsLatest(bytes32 expected) external { + bytes32 gotBlockHash = ARB_SYS.arbBlockHash(ARB_SYS.arbBlockNumber() - 1); + require(gotBlockHash != bytes32(0), "ZERO_BLOCK_HASH"); + require(gotBlockHash == expected, "WRONG_BLOCK_HASH"); } } diff --git a/src/mocks/Program.sol b/src/mocks/Program.sol new file mode 100644 index 00000000..3966196d --- /dev/null +++ b/src/mocks/Program.sol @@ -0,0 +1,126 @@ +// Copyright 2022-2023, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.0; +import "../precompiles/ArbSys.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; + +contract ProgramTest { + event Hash(bytes32 result); + + function callKeccak(address program, bytes calldata data) external { + // in keccak.rs + // the input is the # of hashings followed by a preimage + // the output is the iterated hash of the preimage + (bool success, bytes memory result) = address(program).call(data); + require(success, "call failed"); + bytes32 hash = bytes32(result); + emit Hash(hash); + require(hash == keccak256(data[1:])); + } + + function staticcallProgram(address program, bytes calldata data) + external + view + returns (bytes memory) + { + (bool success, bytes memory result) = address(program).staticcall(data); + require(success, "call failed"); + return result; + } + + function assert256( + bytes memory data, + string memory text, + uint256 expected + ) internal pure returns (bytes memory) { + uint256 value = abi.decode(data, (uint256)); + require(value == expected, text); + + bytes memory rest = new bytes(data.length - 32); + for (uint256 i = 32; i < data.length; i++) { + rest[i - 32] = data[i]; + } + return rest; + } + + function staticcallEvmData( + address program, + address fundedAccount, + uint64 gas, + bytes calldata data + ) external view returns (bytes memory) { + (bool success, bytes memory result) = address(program).staticcall{gas: gas}(data); + require(success, "call failed"); + + address arbPrecompile = address(0x69); + address ethPrecompile = address(0x01); + + result = assert256(result, "block number ", block.number - 1); + result = assert256(result, "chain id ", block.chainid); + result = assert256(result, "base fee ", block.basefee); + result = assert256(result, "gas price ", tx.gasprice); + result = assert256(result, "gas limit ", block.gaslimit); + result = assert256(result, "value ", 0); + result = assert256(result, "timestamp ", block.timestamp); + result = assert256(result, "balance ", fundedAccount.balance); + result = assert256(result, "rust address ", uint256(uint160(program))); + result = assert256(result, "sender ", uint256(uint160(address(this)))); + result = assert256(result, "origin ", uint256(uint160(tx.origin))); + result = assert256(result, "coinbase ", uint256(uint160(address(block.coinbase)))); + result = assert256(result, "rust codehash", uint256(program.codehash)); + result = assert256(result, "arb codehash ", uint256(arbPrecompile.codehash)); + result = assert256(result, "eth codehash ", uint256(ethPrecompile.codehash)); + + bytes memory code = new bytes(program.code.length); + for (uint256 i = 0; i < program.code.length; i++) { + code[i] = result[i]; + } + require(keccak256(code) == keccak256(program.code), "code"); + bytes memory rest = new bytes(result.length - program.code.length); + for (uint256 i = program.code.length; i < result.length; i++) { + rest[i - program.code.length] = result[i]; + } + + result = rest; + return result; + } + + function checkRevertData( + address program, + bytes calldata data, + bytes calldata expected + ) external payable returns (bytes memory) { + (bool success, bytes memory result) = address(program).call{value: msg.value}(data); + require(!success, "unexpected success"); + require(result.length == expected.length, "wrong revert data length"); + for (uint256 i = 0; i < result.length; i++) { + require(result[i] == expected[i], "revert data mismatch"); + } + return result; + } + + function mathTest(address program) external { + uint256 value = 0xeddecf107b5740cef7f5a01e3ea7e287665c4e75a8eb6afae2fda2e3d4367786; + value = mulmod( + value, + 0xc6178c2de1078cd36c3bd302cde755340d7f17fcb3fcc0b9c333ba03b217029f, + 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f + ); + value = addmod( + value, + 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f, + 0xc6178c2de1078cd36c3bd302cde755340d7f17fcb3fcc0b9c333ba03b217029f + ); + unchecked { + value /= 0xeddecf107b5740ce; + value = value**0xfffffffefffffc2f; + value = value % 0xc6178c2de1078cd3; + } + + (bool success, bytes memory result) = address(program).call(""); + require(success, "call failed"); + require(keccak256(result) == keccak256(abi.encodePacked(value))); + } +} diff --git a/src/mocks/SdkStorage.sol b/src/mocks/SdkStorage.sol new file mode 100644 index 00000000..dac31592 --- /dev/null +++ b/src/mocks/SdkStorage.sol @@ -0,0 +1,176 @@ +// Copyright 2022-2023, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.0; + +contract SdkStorage { + bool flag; + address owner; + address other; + Struct sub; + Struct[] structs; + uint64[] vector; + uint40[][] nested; + bytes bytesFull; + bytes bytesLong; + string chars; + Maps maps; + Arrays arrays; + + struct Struct { + uint16 num; + int32 other; + bytes32 word; + } + + struct Maps { + mapping(uint256 => address) basic; + mapping(address => bool[]) vects; + mapping(int32 => address)[] array; + mapping(bytes1 => mapping(bool => uint256)) nested; + mapping(string => Struct) structs; + } + + struct Arrays { + string[4] strings; + uint8 spacer; + uint24[5] packed; + uint8 trail; + address[2] spill; + uint8[2][4] matrix; + int96[4][] vector; + int96[][4] vectors; + Struct[3] structs; + } + + function populate() external { + flag = true; + owner = address(0x70); + other = address(0x30); + + sub.num = 32; + sub.other = type(int32).max; + sub.word = bytes32(uint256(64)); + + for (uint64 i = 0; i < 32; i++) { + vector.push(i); + } + vector[7] = 77; + + for (uint256 w = 0; w < 10; w++) { + nested.push(new uint40[](w)); + for (uint256 i = 0; i < w; i++) { + nested[w][i] = uint40(i); + } + } + for (uint256 w = 0; w < 10; w++) { + for (uint256 i = 0; i < w; i++) { + nested[w][i] *= 2; + } + } + + for (uint8 i = 0; i < 31; i++) { + bytesFull = abi.encodePacked(bytesFull, i); + } + for (uint8 i = 0; i < 80; i++) { + bytesLong = abi.encodePacked(bytesLong, i); + } + chars = "arbitrum stylus"; + + for (uint256 i = 0; i < 16; i++) { + maps.basic[i] = address(uint160(i)); + } + + for (uint160 a = 0; a < 4; a++) { + maps.vects[address(a)] = new bool[](0); + for (uint256 i = 0; i <= a; i++) { + maps.vects[address(a)].push(true); + } + } + + for (int32 i = 0; i < 4; i++) { + maps.array.push(); + maps.array[uint256(uint32(i))][i] = address(uint160(uint32(i))); + } + + for (uint8 i = 0; i < 4; i++) { + maps.nested[bytes1(i)][i % 2 == 0] = i + 1; + } + + maps.structs["stylus"] = sub; + + for (uint256 i = 0; i < 4; i++) { + structs.push(sub); + } + + arrays.strings[2] = "L2 is for you!"; + + for (uint256 i = 0; i < 5; i++) { + arrays.packed[i] = uint24(i); + } + + for (uint256 i = 0; i < 2; i++) { + arrays.spill[i] = address(uint160(i)); + } + + for (uint256 i = 0; i < 4; i++) { + arrays.matrix[i][0] = uint8(i); + arrays.matrix[i][1] = arrays.matrix[i][0] + 1; + } + + for (uint256 w = 0; w < 3; w++) { + int96[4] memory array; + for (int256 i = 0; i < 4; i++) { + array[uint256(i)] = int96(i); + } + arrays.vector.push(array); + } + + for (uint256 w = 0; w < 4; w++) { + for (int96 i = 0; i < 4; i++) { + arrays.vectors[w].push(i); + } + } + + for (uint256 i = 0; i < 3; i++) { + arrays.structs[i] = sub; + } + } + + function remove() external { + while (bytesFull.length != 0) { + bytesFull.pop(); + } + + while (bytesLong.length > 16) { + bytesLong.pop(); + } + + chars = "wasm is cute <3"; + + while (vector.length != 0) { + vector.pop(); + } + + while (nested.length > 1) { + nested.pop(); + } + + for (uint256 i = 0; i < 8; i++) { + delete maps.basic[i]; + } + maps.basic[8] = address(32); + + for (uint160 i = 0; i < 4; i++) { + delete maps.vects[address(i)]; + } + + structs.pop(); + + delete arrays.matrix; + delete arrays.vector; + delete arrays.vectors; + delete arrays.structs; + } +} diff --git a/src/mocks/SimpleCacheManager.sol b/src/mocks/SimpleCacheManager.sol new file mode 100644 index 00000000..22b99533 --- /dev/null +++ b/src/mocks/SimpleCacheManager.sol @@ -0,0 +1,22 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.0; +import "../precompiles/ArbWasmCache.sol"; + +contract SimpleCacheManager { + function cacheProgram(address program) external { + ArbWasmCache(address(0x72)).cacheCodehash(codehash(program)); + } + + function evictProgram(address program) external { + ArbWasmCache(address(0x72)).evictCodehash(codehash(program)); + } + + function codehash(address program) internal view returns (bytes32 hash) { + assembly { + hash := extcodehash(program) + } + } +} diff --git a/src/osp/IOneStepProofEntry.sol b/src/osp/IOneStepProofEntry.sol index fb00b74f..552819cf 100644 --- a/src/osp/IOneStepProofEntry.sol +++ b/src/osp/IOneStepProofEntry.sol @@ -11,6 +11,16 @@ library OneStepProofEntryLib { } interface IOneStepProofEntry { + function getStartMachineHash(bytes32 globalStateHash, bytes32 wasmModuleRoot) + external + pure + returns (bytes32); + + function getEndMachineHash(MachineStatus status, bytes32 globalStateHash) + external + pure + returns (bytes32); + function proveOneStep( ExecutionContext calldata execCtx, uint256 machineStep, diff --git a/src/osp/OneStepProofEntry.sol b/src/osp/OneStepProofEntry.sol index 751518dd..fb311832 100644 --- a/src/osp/OneStepProofEntry.sol +++ b/src/osp/OneStepProofEntry.sol @@ -1,4 +1,4 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2023, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE // SPDX-License-Identifier: BUSL-1.1 @@ -7,12 +7,17 @@ pragma solidity ^0.8.0; import "../state/Deserialize.sol"; import "../state/Machine.sol"; import "../state/MerkleProof.sol"; +import "../state/MultiStack.sol"; import "./IOneStepProver.sol"; import "./IOneStepProofEntry.sol"; contract OneStepProofEntry is IOneStepProofEntry { using MerkleProofLib for MerkleProof; using MachineLib for Machine; + using MultiStackLib for MultiStack; + + using ValueStackLib for ValueStack; + using StackFrameLib for StackFrameWindow; IOneStepProver public prover0; IOneStepProver public proverMem; @@ -31,6 +36,58 @@ contract OneStepProofEntry is IOneStepProofEntry { proverHostIo = proverHostIo_; } + // Copied from ChallengeLib.sol + function getStartMachineHash(bytes32 globalStateHash, bytes32 wasmModuleRoot) + external + pure + returns (bytes32) + { + // Start the value stack with the function call ABI for the entrypoint + Value[] memory startingValues = new Value[](3); + startingValues[0] = ValueLib.newRefNull(); + startingValues[1] = ValueLib.newI32(0); + startingValues[2] = ValueLib.newI32(0); + ValueArray memory valuesArray = ValueArray({inner: startingValues}); + ValueStack memory values = ValueStack({proved: valuesArray, remainingHash: 0}); + ValueStack memory internalStack; + StackFrameWindow memory frameStack; + MultiStack memory emptyMultiStack; + emptyMultiStack.setEmpty(); + + Machine memory mach = Machine({ + status: MachineStatus.RUNNING, + valueStack: values, + valueMultiStack: emptyMultiStack, + internalStack: internalStack, + frameStack: frameStack, + frameMultiStack: emptyMultiStack, + globalStateHash: globalStateHash, + moduleIdx: 0, + functionIdx: 0, + functionPc: 0, + recoveryPc: MachineLib.NO_RECOVERY_PC, + modulesRoot: wasmModuleRoot + }); + return mach.hash(); + } + + // Copied from ChallengeLib.sol + function getEndMachineHash(MachineStatus status, bytes32 globalStateHash) + external + pure + returns (bytes32) + { + if (status == MachineStatus.FINISHED) { + return keccak256(abi.encodePacked("Machine finished:", globalStateHash)); + } else if (status == MachineStatus.ERRORED) { + return keccak256(abi.encodePacked("Machine errored:")); + } else if (status == MachineStatus.TOO_FAR) { + return keccak256(abi.encodePacked("Machine too far:")); + } else { + revert("BAD_BLOCK_STATUS"); + } + } + function proveOneStep( ExecutionContext calldata execCtx, uint256 machineStep, @@ -65,17 +122,22 @@ contract OneStepProofEntry is IOneStepProofEntry { ); { - MerkleProof memory instProof; + Instruction[] memory codeChunk; + MerkleProof memory codeProof; MerkleProof memory funcProof; - (inst, offset) = Deserialize.instruction(proof, offset); - (instProof, offset) = Deserialize.merkleProof(proof, offset); + (codeChunk, offset) = Deserialize.instructions(proof, offset); + (codeProof, offset) = Deserialize.merkleProof(proof, offset); (funcProof, offset) = Deserialize.merkleProof(proof, offset); - bytes32 codeHash = instProof.computeRootFromInstruction(mach.functionPc, inst); + bytes32 codeHash = codeProof.computeRootFromInstructions( + mach.functionPc / 64, + codeChunk + ); bytes32 recomputedRoot = funcProof.computeRootFromFunction( mach.functionIdx, codeHash ); require(recomputedRoot == mod.functionsMerkleRoot, "BAD_FUNCTIONS_ROOT"); + inst = codeChunk[mach.functionPc % 64]; } proof = proof[offset:]; } @@ -113,8 +175,8 @@ contract OneStepProofEntry is IOneStepProofEntry { } else if ( (opcode >= Instructions.GET_GLOBAL_STATE_BYTES32 && opcode <= Instructions.SET_GLOBAL_STATE_U64) || - (opcode >= Instructions.READ_PRE_IMAGE && - opcode <= Instructions.HALT_AND_SET_FINISHED) || + (opcode >= Instructions.READ_PRE_IMAGE && opcode <= Instructions.UNLINK_MODULE) || + (opcode >= Instructions.NEW_COTHREAD && opcode <= Instructions.SWITCH_COTHREAD) || (opcode == Instructions.READ_HOTSHOT_COMMITMENT) ) { prover = proverHostIo; @@ -124,7 +186,18 @@ contract OneStepProofEntry is IOneStepProofEntry { (mach, mod) = prover.executeOneStep(execCtx, mach, mod, inst, proof); - mach.modulesRoot = modProof.computeRootFromModule(oldModIdx, mod); + bool updateRoot = !(opcode == Instructions.LINK_MODULE || + opcode == Instructions.UNLINK_MODULE); + if (updateRoot) { + mach.modulesRoot = modProof.computeRootFromModule(oldModIdx, mod); + } + + if (mach.status == MachineStatus.ERRORED && mach.recoveryPc != MachineLib.NO_RECOVERY_PC) { + // capture error, recover into main thread. + mach.switchCoThreadStacks(); + mach.setPcFromRecovery(); + mach.status = MachineStatus.RUNNING; + } return mach.hash(); } diff --git a/src/osp/OneStepProver0.sol b/src/osp/OneStepProver0.sol index 107e4973..671ff074 100644 --- a/src/osp/OneStepProver0.sol +++ b/src/osp/OneStepProver0.sol @@ -1,4 +1,4 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2023, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE // SPDX-License-Identifier: BUSL-1.1 @@ -11,6 +11,7 @@ import "../state/Deserialize.sol"; import "./IOneStepProver.sol"; contract OneStepProver0 is IOneStepProver { + using MachineLib for Machine; using MerkleProofLib for MerkleProof; using StackFrameLib for StackFrameWindow; using ValueLib for Value; @@ -90,28 +91,11 @@ contract OneStepProver0 is IOneStepProver { bytes calldata ) internal pure { StackFrame memory frame = mach.frameStack.pop(); - if (frame.returnPc.valueType == ValueType.REF_NULL) { - mach.status = MachineStatus.ERRORED; - return; - } else if (frame.returnPc.valueType != ValueType.INTERNAL_REF) { - revert("INVALID_RETURN_PC_TYPE"); - } - uint256 data = frame.returnPc.contents; - uint32 pc = uint32(data); - uint32 func = uint32(data >> 32); - uint32 mod = uint32(data >> 64); - require(data >> 96 == 0, "INVALID_RETURN_PC_DATA"); - mach.functionPc = pc; - mach.functionIdx = func; - mach.moduleIdx = mod; + mach.setPc(frame.returnPc); } function createReturnValue(Machine memory mach) internal pure returns (Value memory) { - uint256 returnData = 0; - returnData |= mach.functionPc; - returnData |= uint256(mach.functionIdx) << 32; - returnData |= uint256(mach.moduleIdx) << 64; - return Value({valueType: ValueType.INTERNAL_REF, contents: returnData}); + return ValueLib.newPc(mach.functionPc, mach.functionIdx, mach.moduleIdx); } function executeCall( @@ -157,6 +141,62 @@ contract OneStepProver0 is IOneStepProver { mach.functionPc = 0; } + function executeCrossModuleForward( + Machine memory mach, + Module memory, + Instruction calldata inst, + bytes calldata + ) internal pure { + // Push the return pc to the stack + mach.valueStack.push(createReturnValue(mach)); + + // Push caller's caller module info to the stack + StackFrame memory frame = mach.frameStack.peek(); + mach.valueStack.push(ValueLib.newI32(frame.callerModule)); + mach.valueStack.push(ValueLib.newI32(frame.callerModuleInternals)); + + // Jump to the target + uint32 func = uint32(inst.argumentData); + uint32 module = uint32(inst.argumentData >> 32); + require(inst.argumentData >> 64 == 0, "BAD_CROSS_MODULE_CALL_DATA"); + mach.moduleIdx = module; + mach.functionIdx = func; + mach.functionPc = 0; + } + + function executeCrossModuleInternalCall( + Machine memory mach, + Module memory mod, + Instruction calldata inst, + bytes calldata proof + ) internal pure { + // Get the target from the stack + uint32 internalIndex = uint32(inst.argumentData); + uint32 moduleIndex = mach.valueStack.pop().assumeI32(); + Module memory calledMod; + + MerkleProof memory modProof; + uint256 offset = 0; + (calledMod, offset) = Deserialize.module(proof, offset); + (modProof, offset) = Deserialize.merkleProof(proof, offset); + require( + modProof.computeRootFromModule(moduleIndex, calledMod) == mach.modulesRoot, + "CROSS_MODULE_INTERNAL_MODULES_ROOT" + ); + + // Push the return pc to the stack + mach.valueStack.push(createReturnValue(mach)); + + // Push caller module info to the stack + mach.valueStack.push(ValueLib.newI32(mach.moduleIdx)); + mach.valueStack.push(ValueLib.newI32(mod.internalsOffset)); + + // Jump to the target + mach.moduleIdx = moduleIndex; + mach.functionIdx = internalIndex + calledMod.internalsOffset; + mach.functionPc = 0; + } + function executeCallerModuleInternalCall( Machine memory mach, Module memory mod, @@ -454,6 +494,10 @@ contract OneStepProver0 is IOneStepProver { impl = executeCall; } else if (opcode == Instructions.CROSS_MODULE_CALL) { impl = executeCrossModuleCall; + } else if (opcode == Instructions.CROSS_MODULE_FORWARD) { + impl = executeCrossModuleForward; + } else if (opcode == Instructions.CROSS_MODULE_INTERNAL_CALL) { + impl = executeCrossModuleInternalCall; } else if (opcode == Instructions.CALLER_MODULE_INTERNAL_CALL) { impl = executeCallerModuleInternalCall; } else if (opcode == Instructions.CALL_INDIRECT) { diff --git a/src/osp/OneStepProverHostIo.sol b/src/osp/OneStepProverHostIo.sol index 9a511814..53245b41 100644 --- a/src/osp/OneStepProverHostIo.sol +++ b/src/osp/OneStepProverHostIo.sol @@ -1,4 +1,4 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2024, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE // SPDX-License-Identifier: BUSL-1.1 @@ -6,6 +6,8 @@ pragma solidity ^0.8.0; import "../state/Value.sol"; import "../state/Machine.sol"; +import "../state/MerkleProof.sol"; +import "../state/MultiStack.sol"; import "../state/Deserialize.sol"; import "../state/ModuleMemory.sol"; import "./IOneStepProver.sol"; @@ -18,10 +20,13 @@ interface IHotShot { contract OneStepProverHostIo is IOneStepProver { using GlobalStateLib for GlobalState; + using MachineLib for Machine; using MerkleProofLib for MerkleProof; using ModuleMemoryLib for ModuleMemory; + using MultiStackLib for MultiStack; using ValueLib for Value; using ValueStackLib for ValueStack; + using StackFrameLib for StackFrameWindow; IHotShot public hotshot; @@ -66,7 +71,7 @@ contract OneStepProverHostIo is IOneStepProver { mach.status = MachineStatus.ERRORED; return; } - if (ptr + 32 > mod.moduleMemory.size || ptr % LEAF_SIZE != 0) { + if (!mod.moduleMemory.isValidLeaf(ptr)) { mach.status = MachineStatus.ERRORED; return; } @@ -455,6 +460,103 @@ contract OneStepProverHostIo is IOneStepProver { mach.status = MachineStatus.FINISHED; } + function isPowerOfTwo(uint256 value) internal pure returns (bool) { + return value != 0 && (value & (value - 1) == 0); + } + + function proveLastLeaf( + Machine memory mach, + uint256 offset, + bytes calldata proof + ) + internal + pure + returns ( + uint256 leaf, + MerkleProof memory leafProof, + MerkleProof memory zeroProof + ) + { + string memory prefix = "Module merkle tree:"; + bytes32 root = mach.modulesRoot; + + { + Module memory leafModule; + uint32 leaf32; + (leafModule, offset) = Deserialize.module(proof, offset); + (leaf32, offset) = Deserialize.u32(proof, offset); + (leafProof, offset) = Deserialize.merkleProof(proof, offset); + leaf = uint256(leaf32); + + bytes32 compRoot = leafProof.computeRootFromModule(leaf, leafModule); + require(compRoot == root, "WRONG_ROOT_FOR_LEAF"); + } + + // if tree is unbalanced, check that the next leaf is 0 + bool balanced = isPowerOfTwo(leaf + 1); + if (balanced) { + require(1 << leafProof.counterparts.length == leaf + 1, "WRONG_LEAF"); + } else { + (zeroProof, offset) = Deserialize.merkleProof(proof, offset); + bytes32 compRoot = zeroProof.computeRootUnsafe(leaf + 1, 0, prefix); + require(compRoot == root, "WRONG_ROOT_FOR_ZERO"); + } + + return (leaf, leafProof, zeroProof); + } + + function executeLinkModule( + ExecutionContext calldata, + Machine memory mach, + Module memory mod, + Instruction calldata, + bytes calldata proof + ) internal pure { + string memory prefix = "Module merkle tree:"; + bytes32 root = mach.modulesRoot; + + uint256 pointer = mach.valueStack.pop().assumeI32(); + if (!mod.moduleMemory.isValidLeaf(pointer)) { + mach.status = MachineStatus.ERRORED; + return; + } + (bytes32 userMod, uint256 offset, ) = mod.moduleMemory.proveLeaf( + pointer / LEAF_SIZE, + proof, + 0 + ); + + (uint256 leaf, , MerkleProof memory zeroProof) = proveLastLeaf(mach, offset, proof); + + bool balanced = isPowerOfTwo(leaf + 1); + if (balanced) { + mach.modulesRoot = MerkleProofLib.growToNewRoot(root, leaf + 1, userMod, 0, prefix); + } else { + mach.modulesRoot = zeroProof.computeRootUnsafe(leaf + 1, userMod, prefix); + } + + mach.valueStack.push(ValueLib.newI32(uint32(leaf + 1))); + } + + function executeUnlinkModule( + ExecutionContext calldata, + Machine memory mach, + Module memory, + Instruction calldata, + bytes calldata proof + ) internal pure { + string memory prefix = "Module merkle tree:"; + + (uint256 leaf, MerkleProof memory leafProof, ) = proveLastLeaf(mach, 0, proof); + + bool shrink = isPowerOfTwo(leaf); + if (shrink) { + mach.modulesRoot = leafProof.counterparts[leafProof.counterparts.length - 1]; + } else { + mach.modulesRoot = leafProof.computeRootUnsafe(leaf, 0, prefix); + } + } + function executeGlobalStateAccess( ExecutionContext calldata, Machine memory mach, @@ -485,6 +587,93 @@ contract OneStepProverHostIo is IOneStepProver { mach.globalStateHash = state.hash(); } + function executeNewCoThread( + ExecutionContext calldata, + Machine memory mach, + Module memory, + Instruction calldata, + bytes calldata + ) internal pure { + if (mach.recoveryPc != MachineLib.NO_RECOVERY_PC) { + // cannot create new cothread from inside cothread + mach.status = MachineStatus.ERRORED; + return; + } + mach.frameMultiStack.pushNew(); + mach.valueMultiStack.pushNew(); + } + + function provePopCothread(MultiStack memory multi, bytes calldata proof) internal pure { + uint256 proofOffset = 0; + bytes32 newInactiveCoThread; + bytes32 newRemaining; + (newInactiveCoThread, proofOffset) = Deserialize.b32(proof, proofOffset); + (newRemaining, proofOffset) = Deserialize.b32(proof, proofOffset); + if (newInactiveCoThread == MultiStackLib.NO_STACK_HASH) { + require(newRemaining == bytes32(0), "WRONG_COTHREAD_EMPTY"); + require(multi.remainingHash == bytes32(0), "WRONG_COTHREAD_EMPTY"); + } else { + require( + keccak256(abi.encodePacked("cothread:", newInactiveCoThread, newRemaining)) == + multi.remainingHash, + "WRONG_COTHREAD_POP" + ); + } + multi.remainingHash = newRemaining; + multi.inactiveStackHash = newInactiveCoThread; + } + + function executePopCoThread( + ExecutionContext calldata, + Machine memory mach, + Module memory, + Instruction calldata, + bytes calldata proof + ) internal pure { + if (mach.recoveryPc != MachineLib.NO_RECOVERY_PC) { + // cannot pop cothread from inside cothread + mach.status = MachineStatus.ERRORED; + return; + } + if (mach.frameMultiStack.inactiveStackHash == MultiStackLib.NO_STACK_HASH) { + // cannot pop cothread if there isn't one + mach.status = MachineStatus.ERRORED; + return; + } + provePopCothread(mach.valueMultiStack, proof); + provePopCothread(mach.frameMultiStack, proof[64:]); + } + + function executeSwitchCoThread( + ExecutionContext calldata, + Machine memory mach, + Module memory, + Instruction calldata inst, + bytes calldata + ) internal pure { + if (mach.frameMultiStack.inactiveStackHash == MultiStackLib.NO_STACK_HASH) { + // cannot switch cothread if there isn't one + mach.status = MachineStatus.ERRORED; + return; + } + if (inst.argumentData == 0) { + if (mach.recoveryPc == MachineLib.NO_RECOVERY_PC) { + // switching to main thread, from main thread + mach.status = MachineStatus.ERRORED; + return; + } + mach.recoveryPc = MachineLib.NO_RECOVERY_PC; + } else { + if (mach.recoveryPc != MachineLib.NO_RECOVERY_PC) { + // switching from cothread to cothread + mach.status = MachineStatus.ERRORED; + return; + } + mach.setRecoveryFromPc(uint32(inst.argumentData)); + } + mach.switchCoThreadStacks(); + } + function executeOneStep( ExecutionContext calldata execCtx, Machine calldata startMach, @@ -516,6 +705,16 @@ contract OneStepProverHostIo is IOneStepProver { impl = executeReadInboxMessage; } else if (opcode == Instructions.HALT_AND_SET_FINISHED) { impl = executeHaltAndSetFinished; + } else if (opcode == Instructions.LINK_MODULE) { + impl = executeLinkModule; + } else if (opcode == Instructions.UNLINK_MODULE) { + impl = executeUnlinkModule; + } else if (opcode == Instructions.NEW_COTHREAD) { + impl = executeNewCoThread; + } else if (opcode == Instructions.POP_COTHREAD) { + impl = executePopCoThread; + } else if (opcode == Instructions.SWITCH_COTHREAD) { + impl = executeSwitchCoThread; } else if (opcode == Instructions.READ_HOTSHOT_COMMITMENT) { impl = executeReadHotShotCommitment; } else { diff --git a/src/osp/OneStepProverMemory.sol b/src/osp/OneStepProverMemory.sol index 6fee2f93..3c966049 100644 --- a/src/osp/OneStepProverMemory.sol +++ b/src/osp/OneStepProverMemory.sol @@ -19,13 +19,6 @@ contract OneStepProverMemory is IOneStepProver { uint256 private constant LEAF_SIZE = 32; uint64 private constant PAGE_SIZE = 65536; - function pullLeafByte(bytes32 leaf, uint256 idx) internal pure returns (uint8) { - require(idx < LEAF_SIZE, "BAD_PULL_LEAF_BYTE_IDX"); - // Take into account that we are casting the leaf to a big-endian integer - uint256 leafShift = (LEAF_SIZE - 1 - idx) * 8; - return uint8(uint256(leaf) >> leafShift); - } - function setLeafByte( bytes32 oldLeaf, uint256 idx, @@ -109,35 +102,13 @@ contract OneStepProverMemory is IOneStepProver { revert("INVALID_MEMORY_LOAD_OPCODE"); } - // Neither of these can overflow as they're computed with much less than 256 bit integers. - uint256 startIdx = inst.argumentData + mach.valueStack.pop().assumeI32(); - if (startIdx + readBytes > mod.moduleMemory.size) { + uint256 index = inst.argumentData + mach.valueStack.pop().assumeI32(); + (bool err, uint256 value, ) = mod.moduleMemory.load(index, readBytes, proof, 0); + if (err) { mach.status = MachineStatus.ERRORED; return; } - - uint256 proofOffset = 0; - uint256 lastProvedLeafIdx = ~uint256(0); - bytes32 lastProvedLeafContents; - uint64 readValue; - for (uint256 i = 0; i < readBytes; i++) { - uint256 idx = startIdx + i; - uint256 leafIdx = idx / LEAF_SIZE; - if (leafIdx != lastProvedLeafIdx) { - // This hits the stack size if we phrase it as mod.moduleMemory.proveLeaf(...) - (lastProvedLeafContents, proofOffset, ) = ModuleMemoryLib.proveLeaf( - mod.moduleMemory, - leafIdx, - proof, - proofOffset - ); - lastProvedLeafIdx = leafIdx; - } - uint256 indexWithinLeaf = idx % LEAF_SIZE; - readValue |= - uint64(pullLeafByte(lastProvedLeafContents, indexWithinLeaf)) << - uint64(i * 8); - } + uint64 readValue = uint64(value); if (signed) { // Go down to the original uint size, change to signed, go up to correct size, convert back to unsigned diff --git a/src/precompiles/ArbDebug.sol b/src/precompiles/ArbDebug.sol index 9924eded..01ee127a 100644 --- a/src/precompiles/ArbDebug.sol +++ b/src/precompiles/ArbDebug.sol @@ -37,6 +37,8 @@ interface ArbDebug { function customRevert(uint64 number) external pure; + function panic() external; + function legacyError() external pure; error Custom(uint64, string, bool); diff --git a/src/precompiles/ArbOwner.sol b/src/precompiles/ArbOwner.sol index 3ff424f7..69273fc6 100644 --- a/src/precompiles/ArbOwner.sol +++ b/src/precompiles/ArbOwner.sol @@ -1,16 +1,18 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2024, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE // SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.4.21 <0.9.0; -/// @title Provides owners with tools for managing the rollup. -/// @notice Calls by non-owners will always revert. -/// Most of Arbitrum Classic's owner methods have been removed since they no longer make sense in Nitro: -/// - What were once chain parameters are now parts of ArbOS's state, and those that remain are set at genesis. -/// - ArbOS upgrades happen with the rest of the system rather than being independent -/// - Exemptions to address aliasing are no longer offered. Exemptions were intended to support backward compatibility for contracts deployed before aliasing was introduced, but no exemptions were ever requested. -/// Precompiled contract that exists in every Arbitrum chain at 0x0000000000000000000000000000000000000070. +/** + * @title Provides owners with tools for managing the rollup. + * @notice Calls by non-owners will always revert. + * Most of Arbitrum Classic's owner methods have been removed since they no longer make sense in Nitro: + * - What were once chain parameters are now parts of ArbOS's state, and those that remain are set at genesis. + * - ArbOS upgrades happen with the rest of the system rather than being independent + * - Exemptions to address aliasing are no longer offered. Exemptions were intended to support backward compatibility for contracts deployed before aliasing was introduced, but no exemptions were ever requested. + * Precompiled contract that exists in every Arbitrum chain at 0x0000000000000000000000000000000000000070. + **/ interface ArbOwner { /// @notice Add account as a chain owner function addChainOwner(address newOwner) external; @@ -90,9 +92,49 @@ interface ArbOwner { /// @notice Releases surplus funds from L1PricerFundsPoolAddress for use function releaseL1PricerSurplusFunds(uint256 maxWeiToRelease) external returns (uint256); + /// @notice Sets the amount of ink 1 gas buys + /// @param price the conversion rate (must fit in a uint24) + function setInkPrice(uint32 price) external; + + /// @notice Sets the maximum depth (in wasm words) a wasm stack may grow + function setWasmMaxStackDepth(uint32 depth) external; + + /// @notice Sets the number of free wasm pages a tx gets + function setWasmFreePages(uint16 pages) external; + + /// @notice Sets the base cost of each additional wasm page + function setWasmPageGas(uint16 gas) external; + + /// @notice Sets the maximum number of pages a wasm may allocate + function setWasmPageLimit(uint16 limit) external; + + /// @notice Sets the minimum costs to invoke a program + /// @param gas amount of gas paid in increments of 256 when not the program is not cached + /// @param cached amount of gas paid in increments of 64 when the program is cached + function setWasmMinInitGas(uint8 gas, uint16 cached) external; + + /// @notice Sets the linear adjustment made to program init costs. + /// @param percent the adjustment (100% = no adjustment). + function setWasmInitCostScalar(uint64 percent) external; + + /// @notice Sets the number of days after which programs deactivate + function setWasmExpiryDays(uint16 _days) external; + + /// @notice Sets the age a program must be to perform a keepalive + function setWasmKeepaliveDays(uint16 _days) external; + + /// @notice Sets the number of extra programs ArbOS caches during a given block + function setWasmBlockCacheSize(uint16 count) external; + + /// @notice Adds account as a wasm cache manager + function addWasmCacheManager(address manager) external; + + /// @notice Removes account from the list of wasm cache managers + function removeWasmCacheManager(address manager) external; + /// @notice Sets serialized chain config in ArbOS state function setChainConfig(string calldata chainConfig) external; - // Emitted when a successful call is made to this precompile + /// Emitted when a successful call is made to this precompile event OwnerActs(bytes4 indexed method, address indexed owner, bytes data); } diff --git a/src/precompiles/ArbWasm.sol b/src/precompiles/ArbWasm.sol new file mode 100644 index 00000000..b690ed24 --- /dev/null +++ b/src/precompiles/ArbWasm.sol @@ -0,0 +1,119 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.4.21 <0.9.0; + +/** + * @title Methods for managing user programs + * @notice Precompiled contract that exists in every Arbitrum chain at 0x0000000000000000000000000000000000000071. + */ +interface ArbWasm { + /// @notice Activate a wasm program + /// @param program the program to activate + /// @return version the stylus version the program was activated against + /// @return dataFee the data fee paid to store the activated program + function activateProgram(address program) + external + payable + returns (uint16 version, uint256 dataFee); + + /// @notice Gets the latest stylus version + /// @return version the stylus version + function stylusVersion() external view returns (uint16 version); + + /// @notice Gets the stylus version the program with codehash was most recently activated against + /// @return version the program version (reverts for EVM contracts) + function codehashVersion(bytes32 codehash) external view returns (uint16 version); + + /// @notice Extends a program's expiration date. + /// Reverts if too soon or if the program is not up to date. + function codehashKeepalive(bytes32 codehash) external payable; + + /// @notice Gets a program's asm size. + /// Reverts if program is not active. + /// @return size the size in bytes + function codehashAsmSize(bytes32 codehash) external view returns (uint32 size); + + /// @notice Gets the stylus version the program was most recently activated against + /// @return version the program version (reverts for EVM contracts) + function programVersion(address program) external view returns (uint16 version); + + /// @notice Gets the cost to invoke the program + /// @return gas the amount of gas + /// @return gasWhenCached the amount of gas if the program was recently used + function programInitGas(address program) + external + view + returns (uint64 gas, uint64 gasWhenCached); + + /// @notice Gets the memory footprint of the program at the given address in pages + /// @return footprint the memory footprint of program in pages (reverts for EVM contracts) + function programMemoryFootprint(address program) external view returns (uint16 footprint); + + /// @notice Gets the amount of time remaining until the program expires + /// @return _secs the time left in seconds (reverts for EVM contracts) + function programTimeLeft(address program) external view returns (uint64 _secs); + + /// @notice Gets the conversion rate between gas and ink + /// @return price the amount of ink 1 gas buys + function inkPrice() external view returns (uint32 price); + + /// @notice Gets the wasm stack size limit + /// @return depth the maximum depth (in wasm words) a wasm stack may grow + function maxStackDepth() external view returns (uint32 depth); + + /// @notice Gets the number of free wasm pages a program gets + /// @return pages the number of wasm pages (2^16 bytes) + function freePages() external view returns (uint16 pages); + + /// @notice Gets the base cost of each additional wasm page (2^16 bytes) + /// @return gas base amount of gas needed to grow another wasm page + function pageGas() external view returns (uint16 gas); + + /// @notice Gets the ramp that drives exponential memory costs + /// @return ramp bits representing the floating point value + function pageRamp() external view returns (uint64 ramp); + + /// @notice Gets the maximum number of pages a wasm may allocate + /// @return limit the number of pages + function pageLimit() external view returns (uint16 limit); + + /// @notice Gets the minimum costs to invoke a program + /// @return gas amount of gas in increments of 256 when not cached + /// @return cached amount of gas in increments of 64 when cached + function minInitGas() external view returns (uint8 gas, uint8 cached); + + /// @notice Gets the linear adjustment made to program init costs. + /// @return percent the adjustment (100% = no adjustment). + function initCostScalar() external view returns (uint64 percent); + + /// @notice Gets the number of days after which programs deactivate + /// @return _days the number of days + function expiryDays() external view returns (uint16 _days); + + /// @notice Gets the age a program must be to perform a keepalive + /// @return _days the number of days + function keepaliveDays() external view returns (uint16 _days); + + /// @notice Gets the number of extra programs ArbOS caches during a given block. + /// @return count the number of same-block programs. + function blockCacheSize() external view returns (uint16 count); + + event ProgramActivated( + bytes32 indexed codehash, + bytes32 moduleHash, + address program, + uint256 dataFee, + uint16 version + ); + event ProgramLifetimeExtended(bytes32 indexed codehash, uint256 dataFee); + + error ProgramNotWasm(); + error ProgramNotActivated(); + error ProgramNeedsUpgrade(uint16 version, uint16 stylusVersion); + error ProgramExpired(uint64 ageInSeconds); + error ProgramUpToDate(); + error ProgramKeepaliveTooSoon(uint64 ageInSeconds); + error ProgramInsufficientValue(uint256 have, uint256 want); +} diff --git a/src/precompiles/ArbWasmCache.sol b/src/precompiles/ArbWasmCache.sol new file mode 100644 index 00000000..7d1b8380 --- /dev/null +++ b/src/precompiles/ArbWasmCache.sol @@ -0,0 +1,33 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity >=0.4.21 <0.9.0; + +/** + * @title Methods for managing Stylus caches + * @notice Precompiled contract that exists in every Arbitrum chain at 0x0000000000000000000000000000000000000072. + */ +interface ArbWasmCache { + /// @notice See if the user is a cache manager. + function isCacheManager(address manager) external view returns (bool); + + /// @notice Retrieve all address managers. + /// @return managers the list of managers. + function allCacheManagers() external view returns (address[] memory managers); + + /// @notice Caches all programs with the given codehash. + /// @notice Reverts if the programs have expired. + /// @notice Caller must be a cache manager or chain owner. + /// @notice If you're looking for how to bid for position, interact with the chain's cache manager contract. + function cacheCodehash(bytes32 codehash) external; + + /// @notice Evicts all programs with the given codehash. + /// @notice Caller must be a cache manager or chain owner. + function evictCodehash(bytes32 codehash) external; + + /// @notice Gets whether a program is cached. Note that the program may be expired. + function codehashIsCached(bytes32 codehash) external view returns (bool); + + event UpdateProgramCache(address indexed manager, bytes32 indexed codehash, bool cached); +} diff --git a/src/state/Deserialize.sol b/src/state/Deserialize.sol index d708dc1f..364f5d76 100644 --- a/src/state/Deserialize.sol +++ b/src/state/Deserialize.sol @@ -7,6 +7,7 @@ pragma solidity ^0.8.0; import "./Value.sol"; import "./ValueStack.sol"; import "./Machine.sol"; +import "./MultiStack.sol"; import "./Instructions.sol"; import "./StackFrame.sol"; import "./MerkleProof.sol"; @@ -88,6 +89,16 @@ library Deserialize { ret = bytes32(retInt); } + function boolean(bytes calldata proof, uint256 startOffset) + internal + pure + returns (bool ret, uint256 offset) + { + offset = startOffset; + ret = uint8(proof[offset]) != 0; + offset++; + } + function value(bytes calldata proof, uint256 startOffset) internal pure @@ -119,17 +130,39 @@ library Deserialize { stack = ValueStack({proved: ValueArray(proved), remainingHash: remainingHash}); } - function instruction(bytes calldata proof, uint256 startOffset) + function multiStack(bytes calldata proof, uint256 startOffset) internal pure - returns (Instruction memory inst, uint256 offset) + returns (MultiStack memory multistack, uint256 offset) { offset = startOffset; - uint16 opcode; - uint256 data; - (opcode, offset) = u16(proof, offset); - (data, offset) = u256(proof, offset); - inst = Instruction({opcode: opcode, argumentData: data}); + bytes32 inactiveStackHash; + (inactiveStackHash, offset) = b32(proof, offset); + bytes32 remainingHash; + (remainingHash, offset) = b32(proof, offset); + multistack = MultiStack({ + inactiveStackHash: inactiveStackHash, + remainingHash: remainingHash + }); + } + + function instructions(bytes calldata proof, uint256 startOffset) + internal + pure + returns (Instruction[] memory code, uint256 offset) + { + offset = startOffset; + uint8 count; + (count, offset) = u8(proof, offset); + code = new Instruction[](count); + + for (uint256 i = 0; i < uint256(count); i++) { + uint16 opcode; + uint256 data; + (opcode, offset) = u16(proof, offset); + (data, offset) = u256(proof, offset); + code[i] = Instruction({opcode: opcode, argumentData: data}); + } } function stackFrame(bytes calldata proof, uint256 startOffset) @@ -199,17 +232,20 @@ library Deserialize { ModuleMemory memory mem; bytes32 tablesMerkleRoot; bytes32 functionsMerkleRoot; + bytes32 extraHash; uint32 internalsOffset; (globalsMerkleRoot, offset) = b32(proof, offset); (mem, offset) = moduleMemory(proof, offset); (tablesMerkleRoot, offset) = b32(proof, offset); (functionsMerkleRoot, offset) = b32(proof, offset); + (extraHash, offset) = b32(proof, offset); (internalsOffset, offset) = u32(proof, offset); mod = Module({ globalsMerkleRoot: globalsMerkleRoot, moduleMemory: mem, tablesMerkleRoot: tablesMerkleRoot, functionsMerkleRoot: functionsMerkleRoot, + extraHash: extraHash, internalsOffset: internalsOffset }); } @@ -240,49 +276,54 @@ library Deserialize { returns (Machine memory mach, uint256 offset) { offset = startOffset; - MachineStatus status; { - uint8 statusU8; - (statusU8, offset) = u8(proof, offset); - if (statusU8 == 0) { - status = MachineStatus.RUNNING; - } else if (statusU8 == 1) { - status = MachineStatus.FINISHED; - } else if (statusU8 == 2) { - status = MachineStatus.ERRORED; - } else if (statusU8 == 3) { - status = MachineStatus.TOO_FAR; - } else { - revert("UNKNOWN_MACH_STATUS"); + MachineStatus status; + { + uint8 statusU8; + (statusU8, offset) = u8(proof, offset); + if (statusU8 == 0) { + status = MachineStatus.RUNNING; + } else if (statusU8 == 1) { + status = MachineStatus.FINISHED; + } else if (statusU8 == 2) { + status = MachineStatus.ERRORED; + } else if (statusU8 == 3) { + status = MachineStatus.TOO_FAR; + } else { + revert("UNKNOWN_MACH_STATUS"); + } } + ValueStack memory values; + ValueStack memory internalStack; + MultiStack memory valuesMulti; + StackFrameWindow memory frameStack; + MultiStack memory framesMulti; + (values, offset) = valueStack(proof, offset); + (valuesMulti, offset) = multiStack(proof, offset); + (internalStack, offset) = valueStack(proof, offset); + (frameStack, offset) = stackFrameWindow(proof, offset); + (framesMulti, offset) = multiStack(proof, offset); + mach = Machine({ + status: status, + valueStack: values, + valueMultiStack: valuesMulti, + internalStack: internalStack, + frameStack: frameStack, + frameMultiStack: framesMulti, + globalStateHash: bytes32(0), // filled later + moduleIdx: 0, // filled later + functionIdx: 0, // filled later + functionPc: 0, // filled later + recoveryPc: bytes32(0), // filled later + modulesRoot: bytes32(0) // filled later + }); } - ValueStack memory values; - ValueStack memory internalStack; - bytes32 globalStateHash; - uint32 moduleIdx; - uint32 functionIdx; - uint32 functionPc; - StackFrameWindow memory frameStack; - bytes32 modulesRoot; - (values, offset) = valueStack(proof, offset); - (internalStack, offset) = valueStack(proof, offset); - (frameStack, offset) = stackFrameWindow(proof, offset); - (globalStateHash, offset) = b32(proof, offset); - (moduleIdx, offset) = u32(proof, offset); - (functionIdx, offset) = u32(proof, offset); - (functionPc, offset) = u32(proof, offset); - (modulesRoot, offset) = b32(proof, offset); - mach = Machine({ - status: status, - valueStack: values, - internalStack: internalStack, - frameStack: frameStack, - globalStateHash: globalStateHash, - moduleIdx: moduleIdx, - functionIdx: functionIdx, - functionPc: functionPc, - modulesRoot: modulesRoot - }); + (mach.globalStateHash, offset) = b32(proof, offset); + (mach.moduleIdx, offset) = u32(proof, offset); + (mach.functionIdx, offset) = u32(proof, offset); + (mach.functionPc, offset) = u32(proof, offset); + (mach.recoveryPc, offset) = b32(proof, offset); + (mach.modulesRoot, offset) = b32(proof, offset); } function merkleProof(bytes calldata proof, uint256 startOffset) diff --git a/src/state/Instructions.sol b/src/state/Instructions.sol index 0492e727..ffc1b18d 100644 --- a/src/state/Instructions.sol +++ b/src/state/Instructions.sol @@ -1,4 +1,4 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2023, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE // SPDX-License-Identifier: BUSL-1.1 @@ -134,6 +134,8 @@ library Instructions { uint16 internal constant DUP = 0x8008; uint16 internal constant CROSS_MODULE_CALL = 0x8009; uint16 internal constant CALLER_MODULE_INTERNAL_CALL = 0x800A; + uint16 internal constant CROSS_MODULE_FORWARD = 0x800B; + uint16 internal constant CROSS_MODULE_INTERNAL_CALL = 0x800C; uint16 internal constant GET_GLOBAL_STATE_BYTES32 = 0x8010; uint16 internal constant SET_GLOBAL_STATE_BYTES32 = 0x8011; @@ -143,13 +145,46 @@ library Instructions { uint16 internal constant READ_PRE_IMAGE = 0x8020; uint16 internal constant READ_INBOX_MESSAGE = 0x8021; uint16 internal constant HALT_AND_SET_FINISHED = 0x8022; + uint16 internal constant LINK_MODULE = 0x8023; + uint16 internal constant UNLINK_MODULE = 0x8024; + + uint16 internal constant NEW_COTHREAD = 0x8030; + uint16 internal constant POP_COTHREAD = 0x8031; + uint16 internal constant SWITCH_COTHREAD = 0x8032; uint16 internal constant READ_HOTSHOT_COMMITMENT = 0x9001; uint256 internal constant INBOX_INDEX_SEQUENCER = 0; uint256 internal constant INBOX_INDEX_DELAYED = 1; - function hash(Instruction memory inst) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("Instruction:", inst.opcode, inst.argumentData)); + function hash(Instruction[] memory code) internal pure returns (bytes32) { + // To avoid quadratic expense, we declare a `bytes` early and populate its contents. + bytes memory data = new bytes(13 + 1 + 34 * code.length); + assembly { + // Represents the string "Instructions:", which we place after the length word. + mstore( + add(data, 32), + 0x496e737472756374696f6e733a00000000000000000000000000000000000000 + ) + } + + // write the instruction count + uint256 offset = 13; + data[offset] = bytes1(uint8(code.length)); + offset++; + + // write each instruction + for (uint256 i = 0; i < code.length; i++) { + Instruction memory inst = code[i]; + data[offset] = bytes1(uint8(inst.opcode >> 8)); + data[offset + 1] = bytes1(uint8(inst.opcode)); + offset += 2; + uint256 argumentData = inst.argumentData; + assembly { + mstore(add(add(data, 32), offset), argumentData) + } + offset += 32; + } + return keccak256(data); } } diff --git a/src/state/Machine.sol b/src/state/Machine.sol index 9d80f45c..2a67e639 100644 --- a/src/state/Machine.sol +++ b/src/state/Machine.sol @@ -1,4 +1,4 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2023, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE // SPDX-License-Identifier: BUSL-1.1 @@ -6,6 +6,7 @@ pragma solidity ^0.8.0; import "./ValueStack.sol"; import "./Instructions.sol"; +import "./MultiStack.sol"; import "./StackFrame.sol"; enum MachineStatus { @@ -18,36 +19,49 @@ enum MachineStatus { struct Machine { MachineStatus status; ValueStack valueStack; + MultiStack valueMultiStack; ValueStack internalStack; StackFrameWindow frameStack; + MultiStack frameMultiStack; bytes32 globalStateHash; uint32 moduleIdx; uint32 functionIdx; uint32 functionPc; + bytes32 recoveryPc; bytes32 modulesRoot; } library MachineLib { using StackFrameLib for StackFrameWindow; using ValueStackLib for ValueStack; + using MultiStackLib for MultiStack; + + bytes32 internal constant NO_RECOVERY_PC = ~bytes32(0); function hash(Machine memory mach) internal pure returns (bytes32) { // Warning: the non-running hashes are replicated in Challenge if (mach.status == MachineStatus.RUNNING) { - return - keccak256( - abi.encodePacked( - "Machine running:", - mach.valueStack.hash(), - mach.internalStack.hash(), - mach.frameStack.hash(), - mach.globalStateHash, - mach.moduleIdx, - mach.functionIdx, - mach.functionPc, - mach.modulesRoot - ) - ); + bytes32 valueMultiHash = mach.valueMultiStack.hash( + mach.valueStack.hash(), + mach.recoveryPc != NO_RECOVERY_PC + ); + bytes32 frameMultiHash = mach.frameMultiStack.hash( + mach.frameStack.hash(), + mach.recoveryPc != NO_RECOVERY_PC + ); + bytes memory preimage = abi.encodePacked( + "Machine running:", + valueMultiHash, + mach.internalStack.hash(), + frameMultiHash, + mach.globalStateHash, + mach.moduleIdx, + mach.functionIdx, + mach.functionPc, + mach.recoveryPc, + mach.modulesRoot + ); + return keccak256(preimage); } else if (mach.status == MachineStatus.FINISHED) { return keccak256(abi.encodePacked("Machine finished:", mach.globalStateHash)); } else if (mach.status == MachineStatus.ERRORED) { @@ -58,4 +72,67 @@ library MachineLib { revert("BAD_MACH_STATUS"); } } + + function switchCoThreadStacks(Machine memory mach) internal pure { + bytes32 newActiveValue = mach.valueMultiStack.inactiveStackHash; + bytes32 newActiveFrame = mach.frameMultiStack.inactiveStackHash; + if ( + newActiveFrame == MultiStackLib.NO_STACK_HASH || + newActiveValue == MultiStackLib.NO_STACK_HASH + ) { + mach.status = MachineStatus.ERRORED; + return; + } + mach.frameMultiStack.inactiveStackHash = mach.frameStack.hash(); + mach.valueMultiStack.inactiveStackHash = mach.valueStack.hash(); + mach.frameStack.overwrite(newActiveFrame); + mach.valueStack.overwrite(newActiveValue); + } + + function setPcFromData(Machine memory mach, uint256 data) internal pure returns (bool) { + if (data >> 96 != 0) { + return false; + } + + mach.functionPc = uint32(data); + mach.functionIdx = uint32(data >> 32); + mach.moduleIdx = uint32(data >> 64); + return true; + } + + function setPcFromRecovery(Machine memory mach) internal pure returns (bool) { + if (!setPcFromData(mach, uint256(mach.recoveryPc))) { + return false; + } + mach.recoveryPc = NO_RECOVERY_PC; + return true; + } + + function setRecoveryFromPc(Machine memory mach, uint32 offset) internal pure returns (bool) { + if (mach.recoveryPc != NO_RECOVERY_PC) { + return false; + } + + uint256 result; + result = uint256(mach.moduleIdx) << 64; + result = result | (uint256(mach.functionIdx) << 32); + result = result | uint256(mach.functionPc + offset - 1); + mach.recoveryPc = bytes32(result); + return true; + } + + function setPc(Machine memory mach, Value memory pc) internal pure { + if (pc.valueType == ValueType.REF_NULL) { + mach.status = MachineStatus.ERRORED; + return; + } + if (pc.valueType != ValueType.INTERNAL_REF) { + mach.status = MachineStatus.ERRORED; + return; + } + if (!setPcFromData(mach, pc.contents)) { + mach.status = MachineStatus.ERRORED; + return; + } + } } diff --git a/src/state/MerkleProof.sol b/src/state/MerkleProof.sol index 09c405b2..26d979e0 100644 --- a/src/state/MerkleProof.sol +++ b/src/state/MerkleProof.sol @@ -1,4 +1,4 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2023, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE // SPDX-License-Identifier: BUSL-1.1 @@ -24,12 +24,12 @@ library MerkleProofLib { return computeRootUnsafe(proof, index, leaf.hash(), "Value merkle tree:"); } - function computeRootFromInstruction( + function computeRootFromInstructions( MerkleProof memory proof, uint256 index, - Instruction memory inst + Instruction[] memory code ) internal pure returns (bytes32) { - return computeRootUnsafe(proof, index, Instructions.hash(inst), "Instruction merkle tree:"); + return computeRootUnsafe(proof, index, Instructions.hash(code), "Instruction merkle tree:"); } function computeRootFromFunction( @@ -95,5 +95,23 @@ library MerkleProofLib { } index >>= 1; } + require(index == 0, "PROOF_TOO_SHORT"); + } + + function growToNewRoot( + bytes32 root, + uint256 leaf, + bytes32 hash, + bytes32 zero, + string memory prefix + ) internal pure returns (bytes32) { + bytes32 h = hash; + uint256 node = leaf; + while (node > 1) { + h = keccak256(abi.encodePacked(prefix, h, zero)); + zero = keccak256(abi.encodePacked(prefix, zero, zero)); + node >>= 1; + } + return keccak256(abi.encodePacked(prefix, root, h)); } } diff --git a/src/state/Module.sol b/src/state/Module.sol index e21fb018..07630067 100644 --- a/src/state/Module.sol +++ b/src/state/Module.sol @@ -1,4 +1,4 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2023, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE // SPDX-License-Identifier: BUSL-1.1 @@ -11,6 +11,7 @@ struct Module { ModuleMemory moduleMemory; bytes32 tablesMerkleRoot; bytes32 functionsMerkleRoot; + bytes32 extraHash; uint32 internalsOffset; } @@ -26,6 +27,7 @@ library ModuleLib { mod.moduleMemory.hash(), mod.tablesMerkleRoot, mod.functionsMerkleRoot, + mod.extraHash, mod.internalsOffset ) ); diff --git a/src/state/ModuleMemory.sol b/src/state/ModuleMemory.sol index 1f0a4434..0f7317ed 100644 --- a/src/state/ModuleMemory.sol +++ b/src/state/ModuleMemory.sol @@ -11,6 +11,8 @@ import "./ModuleMemoryCompact.sol"; library ModuleMemoryLib { using MerkleProofLib for MerkleProof; + uint256 private constant LEAF_SIZE = 32; + function hash(ModuleMemory memory mem) internal pure returns (bytes32) { return ModuleMemoryCompactLib.hash(mem); } @@ -35,4 +37,56 @@ library ModuleMemoryLib { bytes32 recomputedRoot = merkle.computeRootFromMemory(leafIdx, contents); require(recomputedRoot == mem.merkleRoot, "WRONG_MEM_ROOT"); } + + function isValidLeaf(ModuleMemory memory mem, uint256 pointer) internal pure returns (bool) { + return pointer + 32 <= mem.size && pointer % LEAF_SIZE == 0; + } + + function pullLeafByte(bytes32 leaf, uint256 idx) internal pure returns (uint8) { + require(idx < LEAF_SIZE, "BAD_PULL_LEAF_BYTE_IDX"); + // Take into account that we are casting the leaf to a big-endian integer + uint256 leafShift = (LEAF_SIZE - 1 - idx) * 8; + return uint8(uint256(leaf) >> leafShift); + } + + // loads a big-endian value from memory + function load( + ModuleMemory memory mem, + uint256 start, + uint256 width, + bytes calldata proof, + uint256 proofOffset + ) + internal + pure + returns ( + bool err, + uint256 value, + uint256 offset + ) + { + if (start + width > mem.size) { + return (true, 0, proofOffset); + } + + uint256 lastProvedLeafIdx = ~uint256(0); + bytes32 lastProvedLeafContents; + uint256 readValue; + for (uint256 i = 0; i < width; i++) { + uint256 idx = start + i; + uint256 leafIdx = idx / LEAF_SIZE; + if (leafIdx != lastProvedLeafIdx) { + (lastProvedLeafContents, proofOffset, ) = proveLeaf( + mem, + leafIdx, + proof, + proofOffset + ); + lastProvedLeafIdx = leafIdx; + } + uint256 indexWithinLeaf = idx % LEAF_SIZE; + readValue |= uint256(pullLeafByte(lastProvedLeafContents, indexWithinLeaf)) << (i * 8); + } + return (false, readValue, proofOffset); + } } diff --git a/src/state/MultiStack.sol b/src/state/MultiStack.sol new file mode 100644 index 00000000..45bc7e6e --- /dev/null +++ b/src/state/MultiStack.sol @@ -0,0 +1,58 @@ +// Copyright 2021-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.0; + +struct MultiStack { + bytes32 inactiveStackHash; // NO_STACK_HASH if no stack, 0 if empty stack + bytes32 remainingHash; // 0 if less than 2 cothreads exist +} + +library MultiStackLib { + bytes32 internal constant NO_STACK_HASH = ~bytes32(0); + + function hash( + MultiStack memory multi, + bytes32 activeStackHash, + bool cothread + ) internal pure returns (bytes32) { + require(activeStackHash != NO_STACK_HASH, "MULTISTACK_NOSTACK_ACTIVE"); + if (cothread) { + require(multi.inactiveStackHash != NO_STACK_HASH, "MULTISTACK_NOSTACK_MAIN"); + return + keccak256( + abi.encodePacked( + "multistack:", + multi.inactiveStackHash, + activeStackHash, + multi.remainingHash + ) + ); + } else { + return + keccak256( + abi.encodePacked( + "multistack:", + activeStackHash, + multi.inactiveStackHash, + multi.remainingHash + ) + ); + } + } + + function setEmpty(MultiStack memory multi) internal pure { + multi.inactiveStackHash = NO_STACK_HASH; + multi.remainingHash = 0; + } + + function pushNew(MultiStack memory multi) internal pure { + if (multi.inactiveStackHash != NO_STACK_HASH) { + multi.remainingHash = keccak256( + abi.encodePacked("cothread:", multi.inactiveStackHash, multi.remainingHash) + ); + } + multi.inactiveStackHash = 0; + } +} diff --git a/src/state/StackFrame.sol b/src/state/StackFrame.sol index 083376ec..eb72fe0d 100644 --- a/src/state/StackFrame.sol +++ b/src/state/StackFrame.sol @@ -1,4 +1,4 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2023, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE // SPDX-License-Identifier: BUSL-1.1 @@ -60,4 +60,9 @@ library StackFrameLib { newProved[window.proved.length] = frame; window.proved = newProved; } + + function overwrite(StackFrameWindow memory window, bytes32 root) internal pure { + window.remainingHash = root; + delete window.proved; + } } diff --git a/src/state/Value.sol b/src/state/Value.sol index a4cb832a..ca1b1a19 100644 --- a/src/state/Value.sol +++ b/src/state/Value.sol @@ -1,4 +1,4 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2023, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE // SPDX-License-Identifier: BUSL-1.1 @@ -61,4 +61,16 @@ library ValueLib { return newI32(uint32(0)); } } + + function newPc( + uint32 funcPc, + uint32 func, + uint32 module + ) internal pure returns (Value memory) { + uint256 data = 0; + data |= funcPc; + data |= uint256(func) << 32; + data |= uint256(module) << 64; + return Value({valueType: ValueType.INTERNAL_REF, contents: data}); + } } diff --git a/src/state/ValueStack.sol b/src/state/ValueStack.sol index ce676bd0..4ba135fb 100644 --- a/src/state/ValueStack.sol +++ b/src/state/ValueStack.sol @@ -1,4 +1,4 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2023, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE // SPDX-License-Identifier: BUSL-1.1 @@ -36,4 +36,9 @@ library ValueStackLib { function push(ValueStack memory stack, Value memory val) internal pure { return stack.proved.push(val); } + + function overwrite(ValueStack memory stack, bytes32 root) internal pure { + stack.remainingHash = root; + delete stack.proved; + } } diff --git a/test/foundry/CacheManager.t.sol b/test/foundry/CacheManager.t.sol new file mode 100644 index 00000000..abb3bdf9 --- /dev/null +++ b/test/foundry/CacheManager.t.sol @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.4; + +import "forge-std/Test.sol"; +import "../../src/chain/CacheManager.sol"; + +contract CacheManagerTest is Test { + CacheManager public cacheManager; + CachedItem[] public expectedCache; + + uint256 internal constant MAX_PAY = 100_000_000 ether; + + ArbWasmMock internal constant ARB_WASM = ArbWasmMock(address(0x71)); + ArbWasmCacheMock internal constant ARB_WASM_CACHE = ArbWasmCacheMock(address(0x72)); + + constructor() { + uint64 cacheSize = 1_000_000; + uint64 decay = 0.1 ether; + cacheManager = new CacheManager(cacheSize, decay); + require(cacheManager.cacheSize() == cacheSize, "wrong cache size"); + require(cacheManager.decay() == decay, "wrong decay rate"); + + vm.etch(address(0x6b), type(ArbOwnerPublicMock).runtimeCode); + vm.etch(address(0x71), type(ArbWasmMock).runtimeCode); + vm.etch(address(0x72), type(ArbWasmCacheMock).runtimeCode); + } + + struct CachedItem { + bytes32 codehash; + uint256 bid; + uint256 size; + } + + function test_randomBids() external { + for (uint256 epoch = 0; epoch < 4; epoch++) { + for (uint256 round = 0; round < 512; round++) { + // roll one of 256 random codehashes + bytes32 codehash = keccak256(abi.encodePacked("code", epoch, round)); + codehash = keccak256(abi.encodePacked(uint256(codehash) % 256)); + + // roll a random bid + uint256 pay = uint256(keccak256(abi.encodePacked("value", epoch, round))) % MAX_PAY; + uint256 bid = pay + block.timestamp * uint256(cacheManager.decay()); + + // determine the expected insertion index on success and the bid needed + uint256 index; + uint256 asmSize = ARB_WASM.codehashAsmSize(codehash); + uint256 cumulativeCacheSize = asmSize; + uint256 neededBid; + for (; index < expectedCache.length; index++) { + if (bid >= expectedCache[index].bid) { + break; + } + cumulativeCacheSize += expectedCache[index].size; + if (cumulativeCacheSize > cacheManager.cacheSize()) { + neededBid = expectedCache[index].bid; + break; + } + } + + if (ARB_WASM_CACHE.codehashIsCached(codehash)) { + vm.expectRevert( + abi.encodeWithSelector(CacheManager.AlreadyCached.selector, codehash) + ); + } else if (neededBid > 0) { + vm.expectRevert( + abi.encodeWithSelector(CacheManager.BidTooSmall.selector, bid, neededBid) + ); + } else { + // insert the item by moving over those to the right + expectedCache.push(CachedItem(bytes32(0), 0, 0)); + for (uint256 j = expectedCache.length - 1; j > index; j--) { + expectedCache[j] = expectedCache[j - 1]; + } + expectedCache[index] = CachedItem(codehash, bid, asmSize); + + // pop any excess cache elements + for (index++; index < expectedCache.length; index++) { + cumulativeCacheSize += expectedCache[index].size; + if (cumulativeCacheSize > cacheManager.cacheSize()) { + break; + } + } + while (index < expectedCache.length) { + expectedCache.pop(); + } + } + + cacheManager.placeBid{value: pay}(codehash); + + require( + ARB_WASM_CACHE.numCached() == expectedCache.length, + "wrong number of cached items" + ); + for (uint256 j = 0; j < expectedCache.length; j++) { + require( + ARB_WASM_CACHE.codehashIsCached(expectedCache[j].codehash), + "codehash not cached" + ); + } + + if (round == 768) { + uint256 newCacheSize = 500_000 + + (uint256(keccak256(abi.encodePacked("cacheSize", epoch))) % 1_000_000); + cacheManager.setCacheSize(uint64(newCacheSize)); + } + } + + cacheManager.evictAll(); + require(ARB_WASM_CACHE.numCached() == 0, "cached items after evictAll"); + delete expectedCache; + } + require(ARB_WASM_CACHE.uselessCalls() == 0, "useless ArbWasmCache calls"); + } +} + +contract ArbOwnerPublicMock { + address payable constant NETWORK_FEE_ACCOUNT = payable(address(0xba5eba11)); + + function getNetworkFeeAccount() external pure returns (address payable) { + return NETWORK_FEE_ACCOUNT; + } + + // pretend all smart contracts are chain owners + function isChainOwner(address addr) external view returns (bool) { + uint256 codeSize; + assembly { + codeSize := extcodesize(addr) + } + return codeSize > 0; + } +} + +contract ArbWasmMock { + // returns a non-uniform distribution of mock code sizes + function codehashAsmSize(bytes32 codehash) external pure returns (uint64) { + uint256 size; + for (uint256 i = 0; i < 3; i++) { + size += uint256(keccak256(abi.encodePacked(codehash, i))) % 65536; + } + return uint64(size); + } +} + +contract ArbWasmCacheMock { + mapping(bytes32 => bool) public codehashIsCached; + uint256 public numCached; + uint256 public uselessCalls; + + function cacheCodehash(bytes32 codehash) external { + if (codehashIsCached[codehash]) { + uselessCalls++; + return; + } + codehashIsCached[codehash] = true; + numCached++; + } + + function evictCodehash(bytes32 codehash) external { + if (!codehashIsCached[codehash]) { + uselessCalls++; + return; + } + codehashIsCached[codehash] = false; + numCached--; + } +} diff --git a/test/foundry/ChallengeManager.t.sol b/test/foundry/ChallengeManager.t.sol index 0a46b8af..88557db9 100644 --- a/test/foundry/ChallengeManager.t.sol +++ b/test/foundry/ChallengeManager.t.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.4; import "forge-std/Test.sol"; import "./util/TestUtil.sol"; import "../../src/challenge/ChallengeManager.sol"; +import "../../src/osp/OneStepProofEntry.sol"; contract ChallengeManagerTest is Test { IChallengeResultReceiver resultReceiver = IChallengeResultReceiver(address(137)); @@ -11,39 +12,135 @@ contract ChallengeManagerTest is Test { IBridge bridge = IBridge(address(139)); IOneStepProofEntry osp = IOneStepProofEntry(address(140)); IOneStepProofEntry newOsp = IOneStepProofEntry(address(141)); + IOneStepProofEntry condOsp = IOneStepProofEntry(address(142)); address proxyAdmin = address(141); ChallengeManager chalmanImpl = new ChallengeManager(); + bytes32 randomRoot = keccak256(abi.encodePacked("randomRoot")); + function deploy() public returns (ChallengeManager) { ChallengeManager chalman = ChallengeManager( address(new TransparentUpgradeableProxy(address(chalmanImpl), proxyAdmin, "")) ); chalman.initialize(resultReceiver, sequencerInbox, bridge, osp); assertEq( - address(chalman.resultReceiver()), - address(resultReceiver), - "Result receiver not set" + address(chalman.resultReceiver()), address(resultReceiver), "Result receiver not set" ); assertEq( - address(chalman.sequencerInbox()), - address(sequencerInbox), - "Sequencer inbox not set" + address(chalman.sequencerInbox()), address(sequencerInbox), "Sequencer inbox not set" ); assertEq(address(chalman.bridge()), address(bridge), "Bridge not set"); assertEq(address(chalman.osp()), address(osp), "OSP not set"); return chalman; } + function testCondOsp() public { + ChallengeManager chalman = deploy(); + + /// legacy root and OSP that will be used as conditional + IOneStepProofEntry legacyOSP = IOneStepProofEntry( + address( + new OneStepProofEntry( + IOneStepProver(makeAddr("0")), + IOneStepProver(makeAddr("mem")), + IOneStepProver(makeAddr("math")), + IOneStepProver(makeAddr("hostio")) + ) + ) + ); + bytes32 legacyRoot = keccak256(abi.encodePacked("legacyRoot")); + + // legacy hashes + bytes32 legacySegment0 = legacyOSP.getStartMachineHash( + keccak256(abi.encodePacked("globalStateHash[0]")), legacyRoot + ); + bytes32 legacySegment1 = legacyOSP.getEndMachineHash( + MachineStatus.FINISHED, keccak256(abi.encodePacked("globalStateHashes[1]")) + ); + + /// new OSP + IOneStepProofEntry _newOSP = IOneStepProofEntry( + address( + new OneStepProofEntry( + IOneStepProver(makeAddr("0")), + IOneStepProver(makeAddr("mem")), + IOneStepProver(makeAddr("math")), + IOneStepProver(makeAddr("hostio")) + ) + ) + ); + + // new hashes + bytes32 newSegment0 = _newOSP.getStartMachineHash( + keccak256(abi.encodePacked("globalStateHash[0]")), randomRoot + ); + bytes32 newSegment1 = _newOSP.getEndMachineHash( + MachineStatus.FINISHED, keccak256(abi.encodePacked("new_globalStateHashes[1]")) + ); + + /// do upgrade + vm.prank(proxyAdmin); + TransparentUpgradeableProxy(payable(address(chalman))).upgradeToAndCall( + address(chalmanImpl), + abi.encodeWithSelector( + ChallengeManager.postUpgradeInit.selector, _newOSP, legacyRoot, legacyOSP + ) + ); + + /// check cond osp + IOneStepProofEntry _condOsp = chalman.getOsp(legacyRoot); + assertEq(address(_condOsp), address(legacyOSP), "Legacy osp not set"); + assertEq( + _condOsp.getStartMachineHash( + keccak256(abi.encodePacked("globalStateHash[0]")), legacyRoot + ), + legacySegment0, + "Unexpected start machine hash" + ); + assertEq( + _condOsp.getEndMachineHash( + MachineStatus.FINISHED, keccak256(abi.encodePacked("globalStateHashes[1]")) + ), + legacySegment1, + "Unexpected end machine hash" + ); + + /// check new osp + IOneStepProofEntry _newOsp = chalman.getOsp(randomRoot); + assertEq(address(_newOsp), address(_newOSP), "New osp not set"); + assertEq( + _newOsp.getStartMachineHash( + keccak256(abi.encodePacked("globalStateHash[0]")), randomRoot + ), + newSegment0, + "Unexpected start machine hash" + ); + assertEq( + _newOsp.getEndMachineHash( + MachineStatus.FINISHED, keccak256(abi.encodePacked("new_globalStateHashes[1]")) + ), + newSegment1, + "Unexpected end machine hash" + ); + + /// check hashes are different + assertNotEq(legacySegment0, newSegment0, "Start machine hash should be different"); + assertNotEq(legacySegment1, newSegment1, "End machine hash should be different"); + } + function testPostUpgradeInit() public { ChallengeManager chalman = deploy(); vm.prank(proxyAdmin); TransparentUpgradeableProxy(payable(address(chalman))).upgradeToAndCall( address(chalmanImpl), - abi.encodeWithSelector(ChallengeManager.postUpgradeInit.selector, newOsp) + abi.encodeWithSelector( + ChallengeManager.postUpgradeInit.selector, newOsp, randomRoot, condOsp + ) ); - assertEq(address(chalman.osp()), address(newOsp), "New osp not set"); + assertEq(address(chalman.getOsp(bytes32(0))), address(newOsp), "New osp not set"); + assertEq(address(chalman.getOsp(randomRoot)), address(condOsp), "Cond osp not set"); } function testPostUpgradeInitFailsNotAdmin() public { @@ -51,12 +148,12 @@ contract ChallengeManagerTest is Test { vm.expectRevert(abi.encodeWithSelector(NotOwner.selector, address(151), proxyAdmin)); vm.prank(address(151)); - chalman.postUpgradeInit(osp); + chalman.postUpgradeInit(newOsp, randomRoot, condOsp); } function testPostUpgradeInitFailsNotDelCall() public { vm.expectRevert(bytes("Function must be called through delegatecall")); vm.prank(proxyAdmin); - chalmanImpl.postUpgradeInit(osp); + chalmanImpl.postUpgradeInit(newOsp, randomRoot, condOsp); } } diff --git a/test/foundry/RollupCreator.t.sol b/test/foundry/RollupCreator.t.sol index 367798db..62a1262e 100644 --- a/test/foundry/RollupCreator.t.sol +++ b/test/foundry/RollupCreator.t.sol @@ -35,6 +35,7 @@ contract RollupCreatorTest is Test { IRollupAdmin public rollupAdmin; IRollupUser public rollupUser; DeployHelper public deployHelper; + IReader4844 dummyReader4844 = IReader4844(address(137)); MockHotShot public hotshot = new MockHotShot(); // 1 gwei diff --git a/test/signatures/ChallengeManager b/test/signatures/ChallengeManager index 3807c4ff..1d582320 100644 --- a/test/signatures/ChallengeManager +++ b/test/signatures/ChallengeManager @@ -7,11 +7,13 @@ "clearChallenge(uint64)": "56e9df97", "createChallenge(bytes32,uint8[2],(bytes32[2],uint64[2])[2],uint64,address,address,uint256,uint256)": "14eab5e7", "currentResponder(uint64)": "23a9ef23", + "getOsp(bytes32)": "3690b011", "initialize(address,address,address,address)": "f8c8765e", "isTimedOut(uint64)": "9ede42b9", "oneStepProveExecution(uint64,(uint256,uint256,bytes32[],uint256),bytes)": "d248d124", "osp()": "f26a62c6", - "postUpgradeInit(address)": "c474d2c5", + "ospCond(bytes32)": "dc74bf8b", + "postUpgradeInit(address,bytes32,address)": "5038934d", "resultReceiver()": "3504f1d7", "sequencerInbox()": "ee35f327", "timeout(uint64)": "1b45c86a", diff --git a/test/signatures/OneStepProofEntry b/test/signatures/OneStepProofEntry new file mode 100644 index 00000000..404d208c --- /dev/null +++ b/test/signatures/OneStepProofEntry @@ -0,0 +1,9 @@ +{ + "getEndMachineHash(uint8,bytes32)": "d8558b87", + "getStartMachineHash(bytes32,bytes32)": "04997be4", + "proveOneStep((uint256,address),uint256,bytes32,bytes)": "5d3adcfb", + "prover0()": "30a5509f", + "proverHostIo()": "5f52fd7c", + "proverMath()": "66e5d9c3", + "proverMem()": "1f128bc0" +} diff --git a/test/signatures/test-sigs.bash b/test/signatures/test-sigs.bash index 4ff16cc3..70193d7f 100755 --- a/test/signatures/test-sigs.bash +++ b/test/signatures/test-sigs.bash @@ -1,6 +1,6 @@ #!/bin/bash output_dir="./test/signatures" -for CONTRACTNAME in Bridge Inbox Outbox RollupCore RollupUserLogic RollupAdminLogic SequencerInbox ChallengeManager ERC20Bridge ERC20Inbox ERC20Outbox BridgeCreator DeployHelper RollupCreator +for CONTRACTNAME in Bridge Inbox Outbox RollupCore RollupUserLogic RollupAdminLogic SequencerInbox ChallengeManager ERC20Bridge ERC20Inbox ERC20Outbox BridgeCreator DeployHelper RollupCreator OneStepProofEntry do echo "Checking for signature changes in $CONTRACTNAME" [ -f "$output_dir/$CONTRACTNAME" ] && mv "$output_dir/$CONTRACTNAME" "$output_dir/$CONTRACTNAME-old" diff --git a/test/storage/ChallengeManager b/test/storage/ChallengeManager index 85c7f035..15c3f116 100644 --- a/test/storage/ChallengeManager +++ b/test/storage/ChallengeManager @@ -6,3 +6,4 @@ | sequencerInbox | contract ISequencerInbox | 3 | 0 | 20 | src/challenge/ChallengeManager.sol:ChallengeManager | | bridge | contract IBridge | 4 | 0 | 20 | src/challenge/ChallengeManager.sol:ChallengeManager | | osp | contract IOneStepProofEntry | 5 | 0 | 20 | src/challenge/ChallengeManager.sol:ChallengeManager | +| ospCond | mapping(bytes32 => contract IOneStepProofEntry) | 6 | 0 | 32 | src/challenge/ChallengeManager.sol:ChallengeManager | diff --git a/yarn.lock b/yarn.lock index 4d72a239..117dabfe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -788,11 +788,6 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@fastify/busboy@^2.0.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.0.tgz#0709e9f4cb252351c609c6e6d8d6779a8d25edff" - integrity sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA== - "@ganache/ethereum-address@0.1.4": version "0.1.4" resolved "https://registry.yarnpkg.com/@ganache/ethereum-address/-/ethereum-address-0.1.4.tgz#0e6d66f4a24f64bf687cb3ff7358fb85b9d9005e" @@ -1365,10 +1360,12 @@ dependencies: antlr4ts "^0.5.0-alpha.4" -"@solidity-parser/parser@^0.18.0": - version "0.18.0" - resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.18.0.tgz#8e77a02a09ecce957255a2f48c9a7178ec191908" - integrity sha512-yfORGUIPgLck41qyN7nbwJRAx17/jAIXCTanHOJZhB6PJ1iAk/84b/xlsVKFSyNyLXIj0dhppoE0+CRws7wlzA== +"@solidity-parser/parser@^0.16.0": + version "0.16.1" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.16.1.tgz#f7c8a686974e1536da0105466c4db6727311253c" + integrity sha512-PdhRFNhbTtu3x8Axm0uYpqOy/lODYQK+MlYSgqIsq2L8SFYEHJPHNUiOTAJbDGzNjjr1/n9AcIayxafR/fWmYw== + dependencies: + antlr4ts "^0.5.0-alpha.4" "@tovarishfin/hardhat-yul@^3.0.5": version "3.0.5" @@ -1525,9 +1522,9 @@ "@types/lodash" "*" "@types/lodash@*": - version "4.14.202" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.202.tgz#f09dbd2fb082d507178b2f2a5c7e74bd72ff98f8" - integrity sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ== + version "4.17.0" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.0.tgz#d774355e41f372d5350a4d0714abb48194a489c3" + integrity sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA== "@types/lru-cache@5.1.1", "@types/lru-cache@^5.1.0": version "5.1.1" @@ -1622,9 +1619,9 @@ integrity sha512-giB9gzDeiCeloIXDgzFBCgjj1k4WxcDrZtGl6h1IqmUPlxF+Nx8Ve+96QCyDZ/HseB/uvDsKbpib9hU5cU53pw== "@types/semver@^7.5.0": - version "7.5.6" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.6.tgz#c65b2bfce1bec346582c07724e3f8c1017a20339" - integrity sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A== + version "7.5.8" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" + integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== "@types/sinon-chai@^3.2.3": version "3.2.8" @@ -1911,13 +1908,6 @@ amdefine@>=0.0.4: resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= -ansi-align@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" - integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== - dependencies: - string-width "^4.1.0" - ansi-colors@3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" @@ -1943,7 +1933,7 @@ ansi-escapes@^4.3.0: ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= ansi-regex@^3.0.0: version "3.0.1" @@ -2091,12 +2081,12 @@ async-eventemitter@^0.2.4: async@1.x: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" - integrity sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w== + integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= async@^2.4.0: - version "2.6.4" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" - integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== dependencies: lodash "^4.17.14" @@ -2267,29 +2257,15 @@ bn.js@^4.0.0, bn.js@^4.11.0, bn.js@^4.11.1, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@ resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== -bn.js@^5.0.0, bn.js@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" - integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== - -bn.js@^5.1.2, bn.js@^5.2.0: +bn.js@^5.0.0, bn.js@^5.1.2, bn.js@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== -boxen@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" - integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== - dependencies: - ansi-align "^3.0.0" - camelcase "^6.2.0" - chalk "^4.1.0" - cli-boxes "^2.2.1" - string-width "^4.2.2" - type-fest "^0.20.2" - widest-line "^3.1.0" - wrap-ansi "^7.0.0" +bn.js@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== brace-expansion@^1.1.7: version "1.1.11" @@ -2424,6 +2400,13 @@ builtin-modules@^1.1.1: resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" integrity sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ== +busboy@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" + integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== + dependencies: + streamsearch "^1.1.0" + bytes@3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" @@ -2471,7 +2454,7 @@ camelcase@^5.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.0.0, camelcase@^6.2.0: +camelcase@^6.0.0: version "6.3.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== @@ -2613,11 +2596,6 @@ clean-stack@^2.0.0: resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== -cli-boxes@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" - integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== - cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" @@ -2679,7 +2657,7 @@ cliui@^8.0.1: code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA== + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= color-convert@^1.9.0: version "1.9.3" @@ -2946,7 +2924,7 @@ debug@^4.0.1: decamelize@^1.1.1, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= decamelize@^4.0.0: version "4.0.0" @@ -3989,9 +3967,9 @@ follow-redirects@1.5.10: debug "=3.1.0" follow-redirects@^1.12.1, follow-redirects@^1.14.0: - version "1.15.5" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" - integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== + version "1.14.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" + integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== forever-agent@~0.6.1: version "0.6.1" @@ -4136,11 +4114,6 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -function-bind@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" - integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== - functional-red-black-tree@^1.0.1, functional-red-black-tree@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" @@ -4174,9 +4147,9 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-func-name@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" - integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: version "1.1.1" @@ -4449,9 +4422,9 @@ hardhat-ignore-warnings@^0.2.9: solidity-comments "^0.0.2" hardhat@^2.17.2: - version "2.19.5" - resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.19.5.tgz#6017c35ae2844b669e9bcc84c3d05346d4ef031c" - integrity sha512-vx8R7zWCYVgM56vA6o0Wqx2bIIptkN4TMs9QwDqZVNGRhMzBfzqUeEYbp+69gxWp1neg2V2nYQUaaUv7aom1kw== + version "2.17.2" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.17.2.tgz#250a8c8e76029e9bfbfb9b9abee68d5b350b5d4a" + integrity sha512-oUv40jBeHw0dKpbyQ+iH9cmNMziweLoTW3MnkNxJ2Gc0KGLrQR/1n4vV4xY60zn2LdmRgnwPqy3CgtY0mfwIIA== dependencies: "@ethersproject/abi" "^5.1.2" "@metamask/eth-sig-util" "^4.0.0" @@ -4472,7 +4445,6 @@ hardhat@^2.17.2: adm-zip "^0.4.16" aggregate-error "^3.0.0" ansi-escapes "^4.3.0" - boxen "^5.1.2" chalk "^2.4.2" chokidar "^3.4.0" ci-info "^2.0.0" @@ -4574,13 +4546,6 @@ hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: inherits "^2.0.3" minimalistic-assert "^1.0.1" -hasown@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.1.tgz#26f48f039de2c0f8d3356c223fb8d50253519faa" - integrity sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA== - dependencies: - function-bind "^1.1.2" - he@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -4830,14 +4795,7 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-core-module@^2.13.0: - version "2.13.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" - integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== - dependencies: - hasown "^2.0.0" - -is-core-module@^2.2.0: +is-core-module@^2.2.0, is-core-module@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== @@ -4881,7 +4839,7 @@ is-extglob@^2.1.1: is-fullwidth-code-point@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw== + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= dependencies: number-is-nan "^1.0.0" @@ -5000,7 +4958,7 @@ is-wsl@^2.1.1: isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= isarray@~1.0.0: version "1.0.0" @@ -5030,7 +4988,7 @@ js-sdsl@^4.1.4: js-sha3@0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" - integrity sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g== + integrity sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc= js-sha3@0.8.0, js-sha3@^0.8.0: version "0.8.0" @@ -5602,14 +5560,7 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= -"minimatch@2 || 3", minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimatch@3.0.4: +"minimatch@2 || 3", minimatch@3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -5623,7 +5574,21 @@ minimatch@5.0.1: dependencies: brace-expansion "^2.0.1" -minimatch@^5.0.1, minimatch@^5.1.0: +minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7" + integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^5.1.0: version "5.1.6" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== @@ -5661,7 +5626,37 @@ mnemonist@^0.38.0: dependencies: obliterator "^2.0.0" -mocha@^10.0.0, mocha@^10.2.0: +mocha@7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.1.2.tgz#8e40d198acf91a52ace122cd7599c9ab857b29e6" + integrity sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA== + dependencies: + ansi-colors "3.2.3" + browser-stdout "1.3.1" + chokidar "3.3.0" + debug "3.2.6" + diff "3.5.0" + escape-string-regexp "1.0.5" + find-up "3.0.0" + glob "7.1.3" + growl "1.10.5" + he "1.2.0" + js-yaml "3.13.1" + log-symbols "3.0.0" + minimatch "3.0.4" + mkdirp "0.5.5" + ms "2.1.1" + node-environment-flags "1.0.6" + object.assign "4.1.0" + strip-json-comments "2.0.1" + supports-color "6.0.0" + which "1.3.1" + wide-align "1.1.3" + yargs "13.3.2" + yargs-parser "13.1.2" + yargs-unparser "1.6.0" + +mocha@^10.0.0: version "10.2.0" resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8" integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg== @@ -6566,7 +6561,7 @@ require-from-string@^2.0.0, require-from-string@^2.0.2: require-main-filename@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" - integrity sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug== + integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= require-main-filename@^2.0.0: version "2.0.0" @@ -6604,11 +6599,11 @@ resolve@^1.1.6: path-parse "^1.0.6" resolve@^1.10.0: - version "1.22.8" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" - integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + version "1.22.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" + integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== dependencies: - is-core-module "^2.13.0" + is-core-module "^2.8.1" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -6783,29 +6778,36 @@ semaphore-async-await@^1.5.1: integrity sha512-b/ptP11hETwYWpeilHXXQiV5UJNJl7ZWWooKRE5eBIYWoom6dZ0SluCIdCtKycsMtZgKWE01/qAw6jblw1YVhg== semver@^5.3.0, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0: - version "5.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" - integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== semver@^6.3.0: - version "6.3.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== semver@^7.0.0, semver@^7.5.4: - version "7.5.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" - integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== - dependencies: - lru-cache "^6.0.0" - -semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: version "7.6.0" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== dependencies: lru-cache "^6.0.0" +semver@^7.3.4, semver@^7.3.5: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + +semver@^7.3.7: + version "7.3.7" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" + integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + dependencies: + lru-cache "^6.0.0" + serialize-javascript@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" @@ -6929,6 +6931,11 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" +solady@0.0.182: + version "0.0.182" + resolved "https://registry.yarnpkg.com/solady/-/solady-0.0.182.tgz#bd8c47f128a3a752358ad052782773966d74c400" + integrity sha512-FW6xo1akJoYpkXMzu58/56FcNU3HYYNamEbnFO3iSibXk0nSHo0DV2Gu/zI3FPg3So5CCX6IYli1TT1IWATnvg== + solc@0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/solc/-/solc-0.5.7.tgz#d84697ac5cc63d9b2139bfb349cec64b64861cdc" @@ -7085,12 +7092,12 @@ solidity-comments@^0.0.2: solidity-comments-win32-x64-msvc "0.0.2" solidity-coverage@^0.8.4: - version "0.8.6" - resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.8.6.tgz#c7b18dc9edfeba11064726c37d96265f689c9478" - integrity sha512-vV03mA/0nNMskOdVwNarUcqk0N/aYdelxAbf6RZ5l84FcYHbqDTr2JXyeYMp4bT48qHtAQjnKrygW1FrECyWNw== + version "0.8.4" + resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.8.4.tgz#c57a21979f5e86859c5198de9fbae2d3bc6324a5" + integrity sha512-xeHOfBOjdMF6hWTbt42iH4x+7j1Atmrf5OldDPMxI+i/COdExUxszOswD9qqvcBTaLGiOrrpnh9UZjSpt4rBsg== dependencies: "@ethersproject/abi" "^5.0.9" - "@solidity-parser/parser" "^0.18.0" + "@solidity-parser/parser" "^0.16.0" chalk "^2.4.2" death "^1.1.0" detect-port "^1.3.0" @@ -7101,7 +7108,7 @@ solidity-coverage@^0.8.4: globby "^10.0.1" jsonschema "^1.2.4" lodash "^4.17.15" - mocha "^10.2.0" + mocha "7.1.2" node-emoji "^1.10.0" pify "^4.0.1" recursive-readdir "^2.2.2" @@ -7212,6 +7219,11 @@ stream-combiner@^0.2.2: duplexer "~0.1.1" through "~2.3.4" +streamsearch@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" + integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== + string-format@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/string-format/-/string-format-2.0.0.tgz#f2df2e7097440d3b65de31b6d40d54c96eaffb9b" @@ -7243,7 +7255,7 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -7278,7 +7290,7 @@ string_decoder@^1.1.1: string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ== + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= string_decoder@~1.1.1: version "1.1.1" @@ -7290,7 +7302,7 @@ string_decoder@~1.1.1: strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= dependencies: ansi-regex "^2.0.0" @@ -7740,12 +7752,17 @@ unbox-primitive@^1.0.1: has-symbols "^1.0.2" which-boxed-primitive "^1.0.2" -undici@^5.14.0, undici@^5.4.0: - version "5.28.3" - resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.3.tgz#a731e0eff2c3fcfd41c1169a869062be222d1e5b" - integrity sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA== +undici@^5.14.0: + version "5.23.0" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.23.0.tgz#e7bdb0ed42cebe7b7aca87ced53e6eaafb8f8ca0" + integrity sha512-1D7w+fvRsqlQ9GscLBwcAJinqcZGHUKjbOmXdlE/v8BvEGXjeWAax+341q44EuTcHXXnfyKNbKRq4Lg7OzhMmg== dependencies: - "@fastify/busboy" "^2.0.0" + busboy "^1.6.0" + +undici@^5.4.0: + version "5.10.0" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.10.0.tgz#dd9391087a90ccfbd007568db458674232ebf014" + integrity sha512-c8HsD3IbwmjjbLvoZuRI26TZic+TSEe8FPMLLOkN1AfYRhdjnKBU6yL+IwcSCbdZiX4e5t0lfMDLDCqj4Sq70g== universalify@^0.1.0: version "0.1.2" @@ -7887,17 +7904,10 @@ wide-align@1.1.3: dependencies: string-width "^1.0.2 || 2" -widest-line@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" - integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== - dependencies: - string-width "^4.0.0" - word-wrap@^1.2.3, word-wrap@~1.2.3: - version "1.2.5" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" - integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== wordwrap@^1.0.0: version "1.0.0" @@ -7920,7 +7930,7 @@ workerpool@6.2.1: wrap-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" - integrity sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw== + integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= dependencies: string-width "^1.0.1" strip-ansi "^3.0.1"