UNPKG

@nomicfoundation/ethereumjs-vm

Version:
776 lines (704 loc) 25.9 kB
import { Block } from '@nomicfoundation/ethereumjs-block' import { ConsensusType, Hardfork } from '@nomicfoundation/ethereumjs-common' import { RLP } from '@nomicfoundation/ethereumjs-rlp' import { StatelessVerkleStateManager } from '@nomicfoundation/ethereumjs-statemanager' import { Trie } from '@nomicfoundation/ethereumjs-trie' import { TransactionType } from '@nomicfoundation/ethereumjs-tx' import { Account, Address, BIGINT_0, BIGINT_8, GWEI_TO_WEI, KECCAK256_RLP, bigIntToBytes, bytesToHex, concatBytes, equalsBytes, hexToBytes, intToBytes, setLengthLeft, short, unprefixedHexToBytes, } from '@nomicfoundation/ethereumjs-util' import debugDefault from 'debug' import { Bloom } from './bloom/index.js' import type { AfterBlockEvent, PostByzantiumTxReceipt, PreByzantiumTxReceipt, RunBlockOpts, RunBlockResult, TxReceipt, } from './types.js' import type { VM } from './vm.js' import type { EVM, EVMInterface } from '@nomicfoundation/ethereumjs-evm' const { debug: createDebugLogger } = debugDefault const debug = createDebugLogger('vm:block') const parentBeaconBlockRootAddress = Address.fromString( '0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02' ) let enableProfiler = false const stateRootCPLabel = 'New state root, DAO HF, checkpoints, block validation' const processTxsLabel = 'Tx processing [ use per-tx profiler for more details ]' const withdrawalsRewardsCommitLabel = 'Withdrawals, Rewards, EVM journal commit' const entireBlockLabel = 'Entire block' /** * @ignore */ export async function runBlock(this: VM, opts: RunBlockOpts): Promise<RunBlockResult> { if (this._opts.profilerOpts?.reportAfterBlock === true) { enableProfiler = true // eslint-disable-next-line no-console console.time(entireBlockLabel) } const state = this.stateManager const { root } = opts const clearCache = opts.clearCache ?? true const setHardfork = opts.setHardfork ?? false let { block } = opts const generateFields = opts.generate === true if (enableProfiler) { const title = `Profiler run - Block ${block.header.number} (${bytesToHex(block.hash())} with ${ block.transactions.length } txs` // eslint-disable-next-line no-console console.log(title) // eslint-disable-next-line no-console console.time(stateRootCPLabel) } /** * The `beforeBlock` event. * * @event Event: beforeBlock * @type {Object} * @property {Block} block emits the block that is about to be processed */ await this._emit('beforeBlock', block) if (setHardfork !== false || this._setHardfork !== false) { const setHardforkUsed = setHardfork ?? this._setHardfork if (setHardforkUsed === true) { this.common.setHardforkBy({ blockNumber: block.header.number, timestamp: block.header.timestamp, }) } else if (typeof setHardforkUsed !== 'boolean') { this.common.setHardforkBy({ blockNumber: block.header.number, td: setHardforkUsed, timestamp: block.header.timestamp, }) } } if (this.DEBUG) { debug('-'.repeat(100)) debug( `Running block hash=${bytesToHex(block.hash())} number=${ block.header.number } hardfork=${this.common.hardfork()}` ) } // Set state root if provided if (root) { if (this.DEBUG) { debug(`Set provided state root ${bytesToHex(root)} clearCache=${clearCache}`) } await state.setStateRoot(root, clearCache) } if (this.common.isActivatedEIP(6800)) { if (!(state instanceof StatelessVerkleStateManager)) { throw Error(`StatelessVerkleStateManager needed for execution of verkle blocks`) } if (this.DEBUG) { debug(`Initializing StatelessVerkleStateManager executionWitness`) } ;(this._opts.stateManager as StatelessVerkleStateManager).initVerkleExecutionWitness( block.executionWitness ) } else { if (state instanceof StatelessVerkleStateManager) { throw Error(`StatelessVerkleStateManager can't execute merkle blocks`) } } // check for DAO support and if we should apply the DAO fork if ( this.common.hardforkIsActiveOnBlock(Hardfork.Dao, block.header.number) === true && block.header.number === this.common.hardforkBlock(Hardfork.Dao)! ) { if (this.DEBUG) { debug(`Apply DAO hardfork`) } await this.evm.journal.checkpoint() await _applyDAOHardfork(this.evm) await this.evm.journal.commit() } // Checkpoint state await this.evm.journal.checkpoint() if (this.DEBUG) { debug(`block checkpoint`) } let result: Awaited<ReturnType<typeof applyBlock>> try { result = await applyBlock.bind(this)(block, opts) if (this.DEBUG) { debug( `Received block results gasUsed=${result.gasUsed} bloom=${short(result.bloom.bitvector)} (${ result.bloom.bitvector.length } bytes) receiptsRoot=${bytesToHex(result.receiptsRoot)} receipts=${ result.receipts.length } txResults=${result.results.length}` ) } } catch (err: any) { await this.evm.journal.revert() if (this.DEBUG) { debug(`block checkpoint reverted`) } if (enableProfiler) { // eslint-disable-next-line no-console console.timeEnd(withdrawalsRewardsCommitLabel) } throw err } // Persist state await this.evm.journal.commit() if (this.DEBUG) { debug(`block checkpoint committed`) } const stateRoot = await state.getStateRoot() // Given the generate option, either set resulting header // values to the current block, or validate the resulting // header values against the current block. if (generateFields) { const bloom = result.bloom.bitvector const gasUsed = result.gasUsed const receiptTrie = result.receiptsRoot const transactionsTrie = await _genTxTrie(block) const generatedFields = { stateRoot, bloom, gasUsed, receiptTrie, transactionsTrie } const blockData = { ...block, header: { ...block.header, ...generatedFields }, } block = Block.fromBlockData(blockData, { common: this.common }) } else if (this.common.isActivatedEIP(6800) === false) { // Only validate the following headers if verkle blocks aren't activated if (equalsBytes(result.receiptsRoot, block.header.receiptTrie) === false) { if (this.DEBUG) { debug( `Invalid receiptTrie received=${bytesToHex(result.receiptsRoot)} expected=${bytesToHex( block.header.receiptTrie )}` ) } const msg = _errorMsg('invalid receiptTrie', this, block) throw new Error(msg) } if (!(equalsBytes(result.bloom.bitvector, block.header.logsBloom) === true)) { if (this.DEBUG) { debug( `Invalid bloom received=${bytesToHex(result.bloom.bitvector)} expected=${bytesToHex( block.header.logsBloom )}` ) } const msg = _errorMsg('invalid bloom', this, block) throw new Error(msg) } if (result.gasUsed !== block.header.gasUsed) { if (this.DEBUG) { debug(`Invalid gasUsed received=${result.gasUsed} expected=${block.header.gasUsed}`) } const msg = _errorMsg('invalid gasUsed', this, block) throw new Error(msg) } if (!(equalsBytes(stateRoot, block.header.stateRoot) === true)) { if (this.DEBUG) { debug( `Invalid stateRoot received=${bytesToHex(stateRoot)} expected=${bytesToHex( block.header.stateRoot )}` ) } const msg = _errorMsg( `invalid block stateRoot, got: ${bytesToHex(stateRoot)}, want: ${bytesToHex( block.header.stateRoot )}`, this, block ) throw new Error(msg) } } else if (this.common.isActivatedEIP(6800) === true) { // If verkle is activated, only validate the post-state if ((this._opts.stateManager as StatelessVerkleStateManager).verifyPostState() === false) { throw new Error(`Verkle post state verification failed on block ${block.header.number}`) } debug(`Verkle post state verification succeeded`) } if (enableProfiler) { // eslint-disable-next-line no-console console.timeEnd(withdrawalsRewardsCommitLabel) } const results: RunBlockResult = { receipts: result.receipts, logsBloom: result.bloom.bitvector, results: result.results, stateRoot, gasUsed: result.gasUsed, receiptsRoot: result.receiptsRoot, } const afterBlockEvent: AfterBlockEvent = { ...results, block } /** * The `afterBlock` event * * @event Event: afterBlock * @type {AfterBlockEvent} * @property {AfterBlockEvent} result emits the results of processing a block */ await this._emit('afterBlock', afterBlockEvent) if (this.DEBUG) { debug( `Running block finished hash=${bytesToHex(block.hash())} number=${ block.header.number } hardfork=${this.common.hardfork()}` ) } if (enableProfiler) { // eslint-disable-next-line no-console console.timeEnd(entireBlockLabel) const logs = (<EVM>this.evm).getPerformanceLogs() if (logs.precompiles.length === 0 && logs.opcodes.length === 0) { // eslint-disable-next-line no-console console.log('No block txs with precompile or opcode execution.') } this.emitEVMProfile(logs.precompiles, 'Precompile performance') this.emitEVMProfile(logs.opcodes, 'Opcodes performance') ;(<EVM>this.evm).clearPerformanceLogs() } return results } /** * Validates and applies a block, computing the results of * applying its transactions. This method doesn't modify the * block itself. It computes the block rewards and puts * them on state (but doesn't persist the changes). * @param {Block} block * @param {RunBlockOpts} opts */ async function applyBlock(this: VM, block: Block, opts: RunBlockOpts) { // Validate block if (opts.skipBlockValidation !== true) { if (block.header.gasLimit >= BigInt('0x8000000000000000')) { const msg = _errorMsg('Invalid block with gas limit greater than (2^63 - 1)', this, block) throw new Error(msg) } else { if (this.DEBUG) { debug(`Validate block`) } // TODO: decide what block validation method is appropriate here if (opts.skipHeaderValidation !== true) { if (typeof (<any>this.blockchain).validateHeader === 'function') { await (<any>this.blockchain).validateHeader(block.header) } else { throw new Error('cannot validate header: blockchain has no `validateHeader` method') } } await block.validateData() } } if (this.common.isActivatedEIP(4788)) { if (this.DEBUG) { debug(`accumulate parentBeaconBlockRoot`) } await accumulateParentBeaconBlockRoot.bind(this)( block.header.parentBeaconBlockRoot!, block.header.timestamp ) } if (enableProfiler) { // eslint-disable-next-line no-console console.timeEnd(stateRootCPLabel) } // Apply transactions if (this.DEBUG) { debug(`Apply transactions`) } const blockResults = await applyTransactions.bind(this)(block, opts) if (enableProfiler) { // eslint-disable-next-line no-console console.time(withdrawalsRewardsCommitLabel) } if (this.common.isActivatedEIP(4895)) { await assignWithdrawals.bind(this)(block) await this.evm.journal.cleanup() } // Pay ommers and miners if (block.common.consensusType() === ConsensusType.ProofOfWork) { await assignBlockRewards.bind(this)(block) } return blockResults } export async function accumulateParentBeaconBlockRoot( this: VM, root: Uint8Array, timestamp: bigint ) { // Save the parentBeaconBlockRoot to the beaconroot stateful precompile ring buffers const historicalRootsLength = BigInt(this.common.param('vm', 'historicalRootsLength')) const timestampIndex = timestamp % historicalRootsLength const timestampExtended = timestampIndex + historicalRootsLength /** * Note: (by Jochem) * If we don't do this (put account if undefined / non-existant), block runner crashes because the beacon root address does not exist * This is hence (for me) again a reason why it should /not/ throw if the address does not exist * All ethereum accounts have empty storage by default */ if ((await this.stateManager.getAccount(parentBeaconBlockRootAddress)) === undefined) { await this.evm.journal.putAccount(parentBeaconBlockRootAddress, new Account()) } await this.stateManager.putContractStorage( parentBeaconBlockRootAddress, setLengthLeft(bigIntToBytes(timestampIndex), 32), bigIntToBytes(timestamp) ) await this.stateManager.putContractStorage( parentBeaconBlockRootAddress, setLengthLeft(bigIntToBytes(timestampExtended), 32), root ) } /** * Applies the transactions in a block, computing the receipts * as well as gas usage and some relevant data. This method is * side-effect free (it doesn't modify the block nor the state). * @param {Block} block * @param {RunBlockOpts} opts */ async function applyTransactions(this: VM, block: Block, opts: RunBlockOpts) { if (enableProfiler) { // eslint-disable-next-line no-console console.time(processTxsLabel) } const bloom = new Bloom(undefined, this.common) // the total amount of gas used processing these transactions let gasUsed = BIGINT_0 let receiptTrie: Trie | undefined = undefined if (block.transactions.length !== 0) { receiptTrie = new Trie({ common: this.common }) } const receipts = [] const txResults = [] /* * Process transactions */ for (let txIdx = 0; txIdx < block.transactions.length; txIdx++) { const tx = block.transactions[txIdx] let maxGasLimit if (this.common.isActivatedEIP(1559) === true) { maxGasLimit = block.header.gasLimit * this.common.param('gasConfig', 'elasticityMultiplier') } else { maxGasLimit = block.header.gasLimit } const gasLimitIsHigherThanBlock = maxGasLimit < tx.gasLimit + gasUsed if (gasLimitIsHigherThanBlock) { const msg = _errorMsg('tx has a higher gas limit than the block', this, block) throw new Error(msg) } // Run the tx through the VM const { skipBalance, skipNonce, skipHardForkValidation } = opts const txRes = await this.runTx({ tx, block, skipBalance, skipNonce, skipHardForkValidation, blockGasUsed: gasUsed, }) txResults.push(txRes) if (this.DEBUG) { debug('-'.repeat(100)) } // Add to total block gas usage gasUsed += txRes.totalGasSpent if (this.DEBUG) { debug(`Add tx gas used (${txRes.totalGasSpent}) to total block gas usage (-> ${gasUsed})`) } // Combine blooms via bitwise OR bloom.or(txRes.bloom) // Add receipt to trie to later calculate receipt root receipts.push(txRes.receipt) const encodedReceipt = encodeReceipt(txRes.receipt, tx.type) await receiptTrie!.put(RLP.encode(txIdx), encodedReceipt) } if (enableProfiler) { // eslint-disable-next-line no-console console.timeEnd(processTxsLabel) } const receiptsRoot = receiptTrie !== undefined ? receiptTrie.root() : KECCAK256_RLP return { bloom, gasUsed, receiptsRoot, receipts, results: txResults, } } async function assignWithdrawals(this: VM, block: Block): Promise<void> { const withdrawals = block.withdrawals! for (const withdrawal of withdrawals) { const { address, amount } = withdrawal // Withdrawal amount is represented in Gwei so needs to be // converted to wei // Note: event if amount is 0, still reward the account // such that the account is touched and marked for cleanup if it is empty await rewardAccount(this.evm, address, amount * GWEI_TO_WEI) } } /** * Calculates block rewards for miner and ommers and puts * the updated balances of their accounts to state. */ async function assignBlockRewards(this: VM, block: Block): Promise<void> { if (this.DEBUG) { debug(`Assign block rewards`) } const minerReward = this.common.param('pow', 'minerReward') const ommers = block.uncleHeaders // Reward ommers for (const ommer of ommers) { const reward = calculateOmmerReward(ommer.number, block.header.number, minerReward) const account = await rewardAccount(this.evm, ommer.coinbase, reward) if (this.DEBUG) { debug(`Add uncle reward ${reward} to account ${ommer.coinbase} (-> ${account.balance})`) } } // Reward miner const reward = calculateMinerReward(minerReward, ommers.length) const account = await rewardAccount(this.evm, block.header.coinbase, reward) if (this.DEBUG) { debug(`Add miner reward ${reward} to account ${block.header.coinbase} (-> ${account.balance})`) } } function calculateOmmerReward( ommerBlockNumber: bigint, blockNumber: bigint, minerReward: bigint ): bigint { const heightDiff = blockNumber - ommerBlockNumber let reward = ((BIGINT_8 - heightDiff) * minerReward) / BIGINT_8 if (reward < BIGINT_0) { reward = BIGINT_0 } return reward } export function calculateMinerReward(minerReward: bigint, ommersNum: number): bigint { // calculate nibling reward const niblingReward = minerReward / BigInt(32) const totalNiblingReward = niblingReward * BigInt(ommersNum) const reward = minerReward + totalNiblingReward return reward } export async function rewardAccount( evm: EVMInterface, address: Address, reward: bigint ): Promise<Account> { let account = await evm.stateManager.getAccount(address) if (account === undefined) { account = new Account() } account.balance += reward await evm.journal.putAccount(address, account) return account } /** * Returns the encoded tx receipt. */ export function encodeReceipt(receipt: TxReceipt, txType: TransactionType) { const encoded = RLP.encode([ (receipt as PreByzantiumTxReceipt).stateRoot ?? ((receipt as PostByzantiumTxReceipt).status === 0 ? Uint8Array.from([]) : hexToBytes('0x01')), bigIntToBytes(receipt.cumulativeBlockGasUsed), receipt.bitvector, receipt.logs, ]) if (txType === TransactionType.Legacy) { return encoded } // Serialize receipt according to EIP-2718: // `typed-receipt = tx-type || receipt-data` return concatBytes(intToBytes(txType), encoded) } /** * Apply the DAO fork changes to the VM */ async function _applyDAOHardfork(evm: EVMInterface) { const state = evm.stateManager /* DAO account list */ const DAOAccountList = DAOConfig.DAOAccounts const DAORefundContract = DAOConfig.DAORefundContract const DAORefundContractAddress = new Address(unprefixedHexToBytes(DAORefundContract)) if ((await state.getAccount(DAORefundContractAddress)) === undefined) { await evm.journal.putAccount(DAORefundContractAddress, new Account()) } let DAORefundAccount = await state.getAccount(DAORefundContractAddress) if (DAORefundAccount === undefined) { DAORefundAccount = new Account() } for (const addr of DAOAccountList) { // retrieve the account and add it to the DAO's Refund accounts' balance. const address = new Address(unprefixedHexToBytes(addr)) let account = await state.getAccount(address) if (account === undefined) { account = new Account() } DAORefundAccount.balance += account.balance // clear the accounts' balance account.balance = BIGINT_0 await evm.journal.putAccount(address, account) } // finally, put the Refund Account await evm.journal.putAccount(DAORefundContractAddress, DAORefundAccount) } async function _genTxTrie(block: Block) { if (block.transactions.length === 0) { return KECCAK256_RLP } const trie = new Trie({ common: block.common }) for (const [i, tx] of block.transactions.entries()) { await trie.put(RLP.encode(i), tx.serialize()) } return trie.root() } /** * Internal helper function to create an annotated error message * * @param msg Base error message * @hidden */ function _errorMsg(msg: string, vm: VM, block: Block) { const blockErrorStr = 'errorStr' in block ? block.errorStr() : 'block' const errorMsg = `${msg} (${vm.errorStr()} -> ${blockErrorStr})` return errorMsg } const DAOConfig = { DAOAccounts: [ 'd4fe7bc31cedb7bfb8a345f31e668033056b2728', 'b3fb0e5aba0e20e5c49d252dfd30e102b171a425', '2c19c7f9ae8b751e37aeb2d93a699722395ae18f', 'ecd135fa4f61a655311e86238c92adcd779555d2', '1975bd06d486162d5dc297798dfc41edd5d160a7', 'a3acf3a1e16b1d7c315e23510fdd7847b48234f6', '319f70bab6845585f412ec7724b744fec6095c85', '06706dd3f2c9abf0a21ddcc6941d9b86f0596936', '5c8536898fbb74fc7445814902fd08422eac56d0', '6966ab0d485353095148a2155858910e0965b6f9', '779543a0491a837ca36ce8c635d6154e3c4911a6', '2a5ed960395e2a49b1c758cef4aa15213cfd874c', '5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5', '9c50426be05db97f5d64fc54bf89eff947f0a321', '200450f06520bdd6c527622a273333384d870efb', 'be8539bfe837b67d1282b2b1d61c3f723966f049', '6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb', 'f1385fb24aad0cd7432824085e42aff90886fef5', 'd1ac8b1ef1b69ff51d1d401a476e7e612414f091', '8163e7fb499e90f8544ea62bbf80d21cd26d9efd', '51e0ddd9998364a2eb38588679f0d2c42653e4a6', '627a0a960c079c21c34f7612d5d230e01b4ad4c7', 'f0b1aa0eb660754448a7937c022e30aa692fe0c5', '24c4d950dfd4dd1902bbed3508144a54542bba94', '9f27daea7aca0aa0446220b98d028715e3bc803d', 'a5dc5acd6a7968a4554d89d65e59b7fd3bff0f90', 'd9aef3a1e38a39c16b31d1ace71bca8ef58d315b', '63ed5a272de2f6d968408b4acb9024f4cc208ebf', '6f6704e5a10332af6672e50b3d9754dc460dfa4d', '77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6', '492ea3bb0f3315521c31f273e565b868fc090f17', '0ff30d6de14a8224aa97b78aea5388d1c51c1f00', '9ea779f907f0b315b364b0cfc39a0fde5b02a416', 'ceaeb481747ca6c540a000c1f3641f8cef161fa7', 'cc34673c6c40e791051898567a1222daf90be287', '579a80d909f346fbfb1189493f521d7f48d52238', 'e308bd1ac5fda103967359b2712dd89deffb7973', '4cb31628079fb14e4bc3cd5e30c2f7489b00960c', 'ac1ecab32727358dba8962a0f3b261731aad9723', '4fd6ace747f06ece9c49699c7cabc62d02211f75', '440c59b325d2997a134c2c7c60a8c61611212bad', '4486a3d68fac6967006d7a517b889fd3f98c102b', '9c15b54878ba618f494b38f0ae7443db6af648ba', '27b137a85656544b1ccb5a0f2e561a5703c6a68f', '21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241', '23b75c2f6791eef49c69684db4c6c1f93bf49a50', '1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b', 'b9637156d330c0d605a791f1c31ba5890582fe1c', '6131c42fa982e56929107413a9d526fd99405560', '1591fc0f688c81fbeb17f5426a162a7024d430c2', '542a9515200d14b68e934e9830d91645a980dd7a', 'c4bbd073882dd2add2424cf47d35213405b01324', '782495b7b3355efb2833d56ecb34dc22ad7dfcc4', '58b95c9a9d5d26825e70a82b6adb139d3fd829eb', '3ba4d81db016dc2890c81f3acec2454bff5aada5', 'b52042c8ca3f8aa246fa79c3feaa3d959347c0ab', 'e4ae1efdfc53b73893af49113d8694a057b9c0d1', '3c02a7bc0391e86d91b7d144e61c2c01a25a79c5', '0737a6b837f97f46ebade41b9bc3e1c509c85c53', '97f43a37f595ab5dd318fb46e7a155eae057317a', '52c5317c848ba20c7504cb2c8052abd1fde29d03', '4863226780fe7c0356454236d3b1c8792785748d', '5d2b2e6fcbe3b11d26b525e085ff818dae332479', '5f9f3392e9f62f63b8eac0beb55541fc8627f42c', '057b56736d32b86616a10f619859c6cd6f59092a', '9aa008f65de0b923a2a4f02012ad034a5e2e2192', '304a554a310c7e546dfe434669c62820b7d83490', '914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79', '4deb0033bb26bc534b197e61d19e0733e5679784', '07f5c1e1bc2c93e0402f23341973a0e043f7bf8a', '35a051a0010aba705c9008d7a7eff6fb88f6ea7b', '4fa802324e929786dbda3b8820dc7834e9134a2a', '9da397b9e80755301a3b32173283a91c0ef6c87e', '8d9edb3054ce5c5774a420ac37ebae0ac02343c6', '0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9', '5dc28b15dffed94048d73806ce4b7a4612a1d48f', 'bcf899e6c7d9d5a215ab1e3444c86806fa854c76', '12e626b0eebfe86a56d633b9864e389b45dcb260', 'a2f1ccba9395d7fcb155bba8bc92db9bafaeade7', 'ec8e57756626fdc07c63ad2eafbd28d08e7b0ca5', 'd164b088bd9108b60d0ca3751da4bceb207b0782', '6231b6d0d5e77fe001c2a460bd9584fee60d409b', '1cba23d343a983e9b5cfd19496b9a9701ada385f', 'a82f360a8d3455c5c41366975bde739c37bfeb8a', '9fcd2deaff372a39cc679d5c5e4de7bafb0b1339', '005f5cee7a43331d5a3d3eec71305925a62f34b6', '0e0da70933f4c7849fc0d203f5d1d43b9ae4532d', 'd131637d5275fd1a68a3200f4ad25c71a2a9522e', 'bc07118b9ac290e4622f5e77a0853539789effbe', '47e7aa56d6bdf3f36be34619660de61275420af8', 'acd87e28b0c9d1254e868b81cba4cc20d9a32225', 'adf80daec7ba8dcf15392f1ac611fff65d94f880', '5524c55fb03cf21f549444ccbecb664d0acad706', '40b803a9abce16f50f36a77ba41180eb90023925', 'fe24cdd8648121a43a7c86d289be4dd2951ed49f', '17802f43a0137c506ba92291391a8a8f207f487d', '253488078a4edf4d6f42f113d1e62836a942cf1a', '86af3e9626fce1957c82e88cbf04ddf3a2ed7915', 'b136707642a4ea12fb4bae820f03d2562ebff487', 'dbe9b615a3ae8709af8b93336ce9b477e4ac0940', 'f14c14075d6c4ed84b86798af0956deef67365b5', 'ca544e5c4687d109611d0f8f928b53a25af72448', 'aeeb8ff27288bdabc0fa5ebb731b6f409507516c', 'cbb9d3703e651b0d496cdefb8b92c25aeb2171f7', '6d87578288b6cb5549d5076a207456a1f6a63dc0', 'b2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e', 'accc230e8a6e5be9160b8cdf2864dd2a001c28b6', '2b3455ec7fedf16e646268bf88846bd7a2319bb2', '4613f3bca5c44ea06337a9e439fbc6d42e501d0a', 'd343b217de44030afaa275f54d31a9317c7f441e', '84ef4b2357079cd7a7c69fd7a37cd0609a679106', 'da2fef9e4a3230988ff17df2165440f37e8b1708', 'f4c64518ea10f995918a454158c6b61407ea345c', '7602b46df5390e432ef1c307d4f2c9ff6d65cc97', 'bb9bc244d798123fde783fcc1c72d3bb8c189413', '807640a13483f8ac783c557fcdf27be11ea4ac7a', ], DAORefundContract: 'bf4ed7b27f1d666546e30d74d50d173d20bca754', }