Commit e108ab0e authored by Kelvin Fichter's avatar Kelvin Fichter

Started work on new dump structure

parent f4325fad
...@@ -12,5 +12,5 @@ import { makeStateDump } from '../src' ...@@ -12,5 +12,5 @@ import { makeStateDump } from '../src'
mkdirp.sync(outdir) mkdirp.sync(outdir)
const dump = await makeStateDump() const dump = await makeStateDump()
fs.writeFileSync(outfile, JSON.stringify(dump)) fs.writeFileSync(outfile, JSON.stringify(dump, null, 4))
})() })()
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_ECDSAContractAccount } from "../../iOVM/accounts/iOVM_ECDSAContractAccount.sol";
import { iOVM_ExecutionManager } from "../../iOVM/execution/iOVM_ExecutionManager.sol";
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_ECDSAUtils } from "../../libraries/utils/Lib_ECDSAUtils.sol";
/**
* @title mockOVM_ECDSAContractAccount
*/
contract mockOVM_ECDSAContractAccount is iOVM_ECDSAContractAccount {
/********************
* Public Functions *
********************/
/**
* Executes a signed transaction.
* @param _transaction Signed EOA transaction.
* @param _signatureType Hashing scheme used for the transaction (e.g., ETH signed message).
* @param _v Signature `v` parameter.
* @param _r Signature `r` parameter.
* @param _s Signature `s` parameter.
* @return _success Whether or not the call returned (rather than reverted).
* @return _returndata Data returned by the call.
*/
function execute(
bytes memory _transaction,
Lib_OVMCodec.EOASignatureType _signatureType,
uint8 _v,
bytes32 _r,
bytes32 _s
)
override
public
returns (
bool _success,
bytes memory _returndata
)
{
iOVM_ExecutionManager ovmExecutionManager = iOVM_ExecutionManager(msg.sender);
// Skip signature validation within the mock.
Lib_OVMCodec.EOATransaction memory decodedTx = Lib_OVMCodec.decodeEOATransaction(_transaction);
// Need to make sure that the transaction nonce is right and bump it if so.
require(
decodedTx.nonce == ovmExecutionManager.ovmGETNONCE() + 1,
"Transaction nonce does not match the expected nonce."
);
ovmExecutionManager.ovmSETNONCE(decodedTx.nonce);
// Contract creations are signalled by sending a transaction to the zero address.
if (decodedTx.target == address(0)) {
address created = ovmExecutionManager.ovmCREATE{gas: decodedTx.gasLimit}(
decodedTx.data
);
// EVM doesn't tell us whether a contract creation failed, even if it reverted during
// initialization. Always return `true` for our success value here.
return (true, abi.encode(created));
} else {
return ovmExecutionManager.ovmCALL(
decodedTx.gasLimit,
decodedTx.target,
decodedTx.data
);
}
}
}
{ {
"name": "optimism", "name": "@eth-optimism/contracts",
"version": "1.0.0", "version": "1.0.0",
"main": "index.js", "main": "build/src/index.js",
"files": [
"build/**/*.js",
"build/contracts/*",
"build/dumps/*json",
"build/artifacts/*json"
],
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"build": "yarn run build:contracts && yarn run build:typescript && yarn run build:copy", "build": "yarn run build:contracts && yarn run build:typescript && yarn run build:copy",
......
...@@ -20,6 +20,7 @@ export interface RollupDeployConfig { ...@@ -20,6 +20,7 @@ export interface RollupDeployConfig {
owner: string | Signer owner: string | Signer
allowArbitraryContractDeployment: boolean allowArbitraryContractDeployment: boolean
} }
dependencies?: string[]
} }
export interface ContractDeployParameters { export interface ContractDeployParameters {
...@@ -64,6 +65,22 @@ export const makeContractDeployConfig = async ( ...@@ -64,6 +65,22 @@ export const makeContractDeployConfig = async (
factory: getContractFactory('OVM_StateCommitmentChain'), factory: getContractFactory('OVM_StateCommitmentChain'),
params: [AddressManager.address], params: [AddressManager.address],
}, },
OVM_DeployerWhitelist: {
factory: getContractFactory('OVM_DeployerWhitelist'),
params: [],
},
OVM_L1MessageSender: {
factory: getContractFactory('OVM_L1MessageSender'),
params: [],
},
OVM_L2ToL1MessagePasser: {
factory: getContractFactory('OVM_L2ToL1MessagePasser'),
params: [],
},
OVM_SafetyChecker: {
factory: getContractFactory('OVM_SafetyChecker'),
params: [],
},
OVM_ExecutionManager: { OVM_ExecutionManager: {
factory: getContractFactory('OVM_ExecutionManager'), factory: getContractFactory('OVM_ExecutionManager'),
params: [AddressManager.address], params: [AddressManager.address],
......
...@@ -38,6 +38,10 @@ export const deploy = async ( ...@@ -38,6 +38,10 @@ export const deploy = async (
for (const [name, contractDeployParameters] of Object.entries( for (const [name, contractDeployParameters] of Object.entries(
contractDeployConfig contractDeployConfig
)) { )) {
if (config.dependencies && !config.dependencies.includes(name)) {
continue
}
const SimpleProxy = await Factory__SimpleProxy.deploy() const SimpleProxy = await Factory__SimpleProxy.deploy()
await AddressManager.setAddress(name, SimpleProxy.address) await AddressManager.setAddress(name, SimpleProxy.address)
...@@ -51,7 +55,11 @@ export const deploy = async ( ...@@ -51,7 +55,11 @@ export const deploy = async (
} }
} }
for (const contractDeployParameters of Object.values(contractDeployConfig)) { for (const [name, contractDeployParameters] of Object.entries(contractDeployConfig)) {
if (config.dependencies && !config.dependencies.includes(name)) {
continue
}
if (contractDeployParameters.afterDeploy) { if (contractDeployParameters.afterDeploy) {
await contractDeployParameters.afterDeploy(contracts) await contractDeployParameters.afterDeploy(contracts)
} }
......
...@@ -2,57 +2,28 @@ ...@@ -2,57 +2,28 @@
import * as path from 'path' import * as path from 'path'
import { ethers } from 'ethers' import { ethers } from 'ethers'
import * as Ganache from 'ganache-core' import * as Ganache from 'ganache-core'
import { keccak256 } from 'ethers/lib/utils'
/* Internal Imports */ /* Internal Imports */
import { deploy, RollupDeployConfig } from './contract-deployment' import { deploy, RollupDeployConfig } from './contract-deployment'
import { getContractDefinition } from './contract-defs' import { fromHexString, toHexString, remove0x } from '../test/helpers/utils'
import { keccak256 } from 'ethers/lib/utils'
type Accounts = Array<{
originalAddress: string
address: string
code: string
}>
interface StorageDump { interface StorageDump {
[key: string]: string [key: string]: string
} }
export interface StateDump { export interface StateDump {
contracts: {
ovmExecutionManager: string
ovmStateManager: string
}
accounts: { accounts: {
[address: string]: { [name: string]: {
balance: number address: string
nonce: number
code: string code: string
codeHash: string
storage: StorageDump storage: StorageDump
abi: any
} }
} }
} }
/**
* Finds the addresses of all accounts changed in the state.
* @param cStateManager Instance of the callback-based internal vm StateManager.
* @returns Array of changed addresses.
*/
const getChangedAccounts = async (cStateManager: any): Promise<string[]> => {
return new Promise<string[]>((resolve, reject) => {
const accounts: string[] = []
const stream = cStateManager._trie.createReadStream()
stream.on('data', (val: any) => {
accounts.push(val.key.toString('hex'))
})
stream.on('end', () => {
resolve(accounts)
})
})
}
/** /**
* Generates a storage dump for a given address. * Generates a storage dump for a given address.
* @param cStateManager Instance of the callback-based internal vm StateManager. * @param cStateManager Instance of the callback-based internal vm StateManager.
...@@ -90,15 +61,23 @@ const getStorageDump = async ( ...@@ -90,15 +61,23 @@ const getStorageDump = async (
*/ */
const sanitizeStorageDump = ( const sanitizeStorageDump = (
storageDump: StorageDump, storageDump: StorageDump,
accounts: Accounts accounts: Array<{
originalAddress: string
deadAddress: string
}>
): StorageDump => { ): StorageDump => {
for (let i = 0; i < accounts.length; i++) {
accounts[i].originalAddress = remove0x(accounts[i].originalAddress).toLowerCase()
accounts[i].deadAddress = remove0x(accounts[i].deadAddress).toLowerCase()
}
for (const [key, value] of Object.entries(storageDump)) { for (const [key, value] of Object.entries(storageDump)) {
let parsedKey = key let parsedKey = key
let parsedValue = value let parsedValue = value
for (const account of accounts) { for (const account of accounts) {
const re = new RegExp(`${account.originalAddress}`, 'g') const re = new RegExp(`${account.originalAddress}`, 'g')
parsedValue = parsedValue.replace(re, account.address) parsedValue = parsedValue.replace(re, account.deadAddress)
parsedKey = parsedKey.replace(re, account.address) parsedKey = parsedKey.replace(re, account.deadAddress)
} }
if (parsedKey !== key) { if (parsedKey !== key) {
...@@ -143,96 +122,67 @@ export const makeStateDump = async (): Promise<any> => { ...@@ -143,96 +122,67 @@ export const makeStateDump = async (): Promise<any> => {
owner: signer, owner: signer,
allowArbitraryContractDeployment: true, allowArbitraryContractDeployment: true,
}, },
dependencies: [
'Lib_AddressManager',
'OVM_DeployerWhitelist',
'OVM_L1MessageSender',
'OVM_L2ToL1MessagePasser',
'OVM_L2CrossDomainMessenger',
'OVM_SafetyChecker',
'OVM_ExecutionManager',
'OVM_StateManager',
'OVM_ECDSAContractAccount'
]
}
const precompiles = {
OVM_L2ToL1MessagePasser: '4200000000000000000000000000000000000000',
OVM_L1MessageSender: '4200000000000000000000000000000000000001',
OVM_DeployerWhitelist: '4200000000000000000000000000000000000002'
} }
const deploymentResult = await deploy(config) const deploymentResult = await deploy(config)
deploymentResult.contracts['Lib_AddressManager'] = deploymentResult.AddressManager
const pStateManager = ganache.engine.manager.state.blockchain.vm.pStateManager const pStateManager = ganache.engine.manager.state.blockchain.vm.pStateManager
const cStateManager = pStateManager._wrapped const cStateManager = pStateManager._wrapped
const ovmExecutionManagerOriginalAddress = deploymentResult.contracts.OVM_ExecutionManager.address
.slice(2)
.toLowerCase()
const ovmExecutionManagerAddress = 'c0dec0dec0dec0dec0dec0dec0dec0dec0de0000'
const ovmStateManagerOriginalAddress = deploymentResult.contracts.OVM_StateManager.address
.slice(2)
.toLowerCase()
const ovmStateManagerAddress = 'c0dec0dec0dec0dec0dec0dec0dec0dec0de0001'
const l2ToL1MessagePasserDef = getContractDefinition(
'OVM_L2ToL1MessagePasser'
)
const l2ToL1MessagePasserHash = keccak256(
l2ToL1MessagePasserDef.deployedBytecode
)
const l2ToL1MessagePasserAddress = '4200000000000000000000000000000000000000'
const l1MessageSenderDef = getContractDefinition('OVM_L1MessageSender')
const l1MessageSenderHash = keccak256(l1MessageSenderDef.deployedBytecode)
const l1MessageSenderAddress = '4200000000000000000000000000000000000001'
const changedAccounts = await getChangedAccounts(cStateManager)
let deadAddressIndex = 0
let accounts: Accounts = []
for (const originalAddress of changedAccounts) {
const code = (
await pStateManager.getContractCode(originalAddress)
).toString('hex')
const codeHash = keccak256('0x' + code)
if (code.length === 0) {
continue
}
// Sorry for this one!
let address = originalAddress
if (codeHash === l2ToL1MessagePasserHash) {
address = l2ToL1MessagePasserAddress
} else if (codeHash === l1MessageSenderHash) {
address = l1MessageSenderAddress
} else if (originalAddress === ovmExecutionManagerOriginalAddress) {
address = ovmExecutionManagerAddress
} else if (originalAddress === ovmStateManagerOriginalAddress) {
address = ovmStateManagerAddress
} else {
address = `deaddeaddeaddeaddeaddeaddeaddeaddead${deadAddressIndex
.toString(16)
.padStart(4, '0')}`
deadAddressIndex++
}
accounts.push({
originalAddress,
address,
code: code,
})
}
const dump: StateDump = { const dump: StateDump = {
contracts: {
ovmExecutionManager: '0x' + ovmExecutionManagerAddress,
ovmStateManager: '0x' + ovmStateManagerAddress,
},
accounts: {}, accounts: {},
} }
for (const account of accounts) { for (let i = 0; i < Object.keys(deploymentResult.contracts).length; i++) {
const storageDump = sanitizeStorageDump( const name = Object.keys(deploymentResult.contracts)[i]
await getStorageDump(cStateManager, account.originalAddress), const contract = deploymentResult.contracts[name]
accounts
) const codeBuf = await pStateManager.getContractCode(fromHexString(contract.address))
const code = toHexString(codeBuf)
const deadAddress = precompiles[name] || `deaddeaddeaddeaddeaddeaddeaddeaddead${i.toString(16).padStart(4, '0')}`
dump.accounts[account.address] = { dump.accounts[name] = {
balance: 0, address: deadAddress,
nonce: 0, code,
code: account.code, codeHash: keccak256(code),
storage: storageDump, storage: await getStorageDump(cStateManager, contract.address),
abi: JSON.parse(contract.interface.format('json') as string),
} }
} }
const addressMap = Object.keys(dump.accounts).map((name) => {
return {
originalAddress: deploymentResult.contracts[name].address,
deadAddress: dump.accounts[name].address
}
})
for (const name of Object.keys(dump.accounts)) {
dump.accounts[name].storage = sanitizeStorageDump(
dump.accounts[name].storage,
addressMap
)
}
return dump return dump
} }
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment