From bffbc0385d0aef62097fd1bffeb9abdecdaa344d Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 9 Dec 2021 14:22:12 +0100 Subject: [PATCH] Implement eth_getProof (#1590) * vm: start on getProof * vm: stateManager: add getProof EIP-1186 * vm: stateManager: add verifyProof * vm: add getProof tests * vm: start on getProof * vm: stateManager: add getProof EIP-1186 * vm: stateManager: add verifyProof * vm: move changes of old state manager into new one * vm: fix proof test * vm: make getProof better readable * vm: stateManager cleanup verifyProof * vm: make proof/verifyproof optional * stateManager: add ropsten stateManager tests stateManager: fix getProof EIP1178 field * stateManager: more getProof tests / ensure geth compatibility * stateManager: partially fix verifyProof * stateManager: fix empty account proofs stateManager: fix storage slot proofs * stateManager: add tests for tampered proofs * stateManager: use Proof type of stateManage not MPT * client: add getProof endpoint client: add tests for getProof * vm: state: update interface * review - vm: * bolster invalid proof error messages * extract ProofStateManager tests to own file * move testdata files to own folder and use import syntax for improved type info * review - client: * already have array validator (usage: `validators.array(validators.hex)`) * add typedoc for getProof (thanks @gabrocheleau) * update getProof.spec.ts to match future test setup from PR #1598 (this slightly modifies the returned accountProof) Co-authored-by: Ryan Ghods --- packages/client/lib/rpc/modules/eth.ts | 41 +++ packages/client/test/rpc/eth/getProof.spec.ts | 127 +++++++++ packages/vm/src/state/index.ts | 4 +- packages/vm/src/state/interface.ts | 11 + packages/vm/src/state/stateManager.ts | 131 +++++++++- .../tests/api/state/proofStateManager.spec.ts | 241 ++++++++++++++++++ .../vm/tests/api/state/stateManager.spec.ts | 1 - .../testdata/ropsten_contractWithStorage.json | 17 ++ .../testdata/ropsten_nonexistentAccount.json | 9 + .../state/testdata/ropsten_validAccount.json | 9 + 10 files changed, 587 insertions(+), 4 deletions(-) create mode 100644 packages/client/test/rpc/eth/getProof.spec.ts create mode 100644 packages/vm/tests/api/state/proofStateManager.spec.ts create mode 100644 packages/vm/tests/api/state/testdata/ropsten_contractWithStorage.json create mode 100644 packages/vm/tests/api/state/testdata/ropsten_nonexistentAccount.json create mode 100644 packages/vm/tests/api/state/testdata/ropsten_validAccount.json diff --git a/packages/client/lib/rpc/modules/eth.ts b/packages/client/lib/rpc/modules/eth.ts index 6167c8942c..e08dfd69cd 100644 --- a/packages/client/lib/rpc/modules/eth.ts +++ b/packages/client/lib/rpc/modules/eth.ts @@ -31,6 +31,7 @@ import type { TxReceipt, } from '@ethereumjs/vm/dist/types' import type { Log } from '@ethereumjs/vm/dist/evm/types' +import type { Proof, ProofStateManager } from '@ethereumjs/vm/dist/state' import type { EthereumClient } from '../..' import type { Chain } from '../../blockchain' import type { EthProtocol } from '../../net/protocol' @@ -362,6 +363,12 @@ export class Eth { this.protocolVersion = middleware(this.protocolVersion.bind(this), 0, []) this.syncing = middleware(this.syncing.bind(this), 0, []) + + this.getProof = middleware(this.getProof.bind(this), 3, [ + [validators.address], + [validators.array(validators.hex)], + [validators.blockOption], + ]) } /** @@ -957,6 +964,40 @@ export class Eth { return bufferToHex(tx.hash()) } + + /** + * Returns an account object along with data about the proof. + * @param params An array of three parameters: + * 1. address of the account + * 2. array of storage keys which should be proofed and included + * 3. integer block number, or the string "latest" or "earliest" + * @returns The {@link Proof} + */ + async getProof(params: [string, string[], string]): Promise { + const [addressHex, slotsHex, blockOption] = params + + if (!this._vm) { + throw new Error('missing vm') + } + + // use a copy of the vm in case new blocks are executed + const vm = this._vm.copy() + if (blockOption !== 'latest') { + const latest = await vm.blockchain.getLatestHeader() + if (blockOption !== bnToHex(latest.number)) { + throw { + code: INVALID_PARAMS, + message: `Currently only "latest" block supported`, + } + } + } + + const address = Address.fromString(addressHex) + const slots = slotsHex.map((slotHex) => setLengthLeft(toBuffer(slotHex), 32)) + const proof = await (vm.stateManager as ProofStateManager).getProof(address, slots) + return proof + } + /** * Returns an object with data about the sync status or false. * @param params An empty array diff --git a/packages/client/test/rpc/eth/getProof.spec.ts b/packages/client/test/rpc/eth/getProof.spec.ts new file mode 100644 index 0000000000..13ac577091 --- /dev/null +++ b/packages/client/test/rpc/eth/getProof.spec.ts @@ -0,0 +1,127 @@ +import tape from 'tape' +import { Block } from '@ethereumjs/block' +import Blockchain from '@ethereumjs/blockchain' +import { Transaction } from '@ethereumjs/tx' +import { Address, BN, bnToHex } from 'ethereumjs-util' +import { FullSynchronizer } from '../../../lib/sync' +import { startRPC, createManager, createClient, params, baseRequest } from '../helpers' + +const method = 'eth_getProof' + +const expectedProof = { + address: '0x9288f8f702cbfb8cc5890819c1c1e2746e684d07', + balance: '0x0', + codeHash: '0x05698751a8fe928d7049ee0af6927f3ff6e398d7d11293ea4c6786d7cfc3dbd4', + nonce: '0x1', + storageHash: '0xb39609ba55cc225a26265fc5e80d51e07a4410c1725cf69dbf15a8b09ad1a0a0', + accountProof: [ + '0xf8718080808080a0b356351d60bc9894cf1f1d6cb68c815f0131d50f1da83c4023a09ec855cfff91808080a086a4665abc4f7e6f3a2da6a3c112616b1954be58ac4f6ff236b5b5f9ba295e4ca043a5b2616ae3a304fe34c5402d41893c49cb75b2ecd25b8b8b53f0926c957f23808080808080', + '0xf869a03bdcfb03f3efaf0a5250648861b575109e8bb8084a0b74b0ec15d41366a4a7abb846f8440180a0b39609ba55cc225a26265fc5e80d51e07a4410c1725cf69dbf15a8b09ad1a0a0a005698751a8fe928d7049ee0af6927f3ff6e398d7d11293ea4c6786d7cfc3dbd4', + ], + storageProof: [ + { + key: '0x0000000000000000000000000000000000000000000000000000000000000000', + value: '0x04d2', + proof: [ + '0xf8518080a036bb5f2fd6f99b186600638644e2f0396989955e201672f7e81e8c8f466ed5b9a010859880cfb38603690e8c4dfcc5595c203de6b901a503f944ef21a6120926a680808080808080808080808080', + '0xe5a0390decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563838204d2', + ], + }, + ], +} + +tape(`${method}: call with valid arguments`, async (t) => { + const blockchain = await Blockchain.create({ validateBlocks: false, validateConsensus: false }) + + const client = createClient({ blockchain, includeVM: true }) + const manager = createManager(client) + const server = startRPC(manager.getMethods()) + + const service = client.services.find((s) => s.name === 'eth') + const { vm } = (service!.synchronizer as FullSynchronizer).execution + + // genesis address with balance + const address = Address.fromString('0xccfd725760a68823ff1e062f4cc97e1360e8d997') + + // contract inspired from https://eth.wiki/json-rpc/API#example-14 + /* + // SPDX-License-Identifier: MIT + pragma solidity ^0.7.4; + + contract Storage { + uint pos0; + mapping(address => uint) pos1; + function store() public { + pos0 = 1234; + pos1[msg.sender] = 5678; + } + } + */ + const data = + '0x6080604052348015600f57600080fd5b5060bc8061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063975057e714602d575b600080fd5b60336035565b005b6104d260008190555061162e600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555056fea2646970667358221220b16fe0abdbdcae31fa05c5717ebc442024b20fb637907d1a05547ea2d8ec8e5964736f6c63430007060033' + + // construct block with tx + const gasLimit = 2000000 + const tx = Transaction.fromTxData({ gasLimit, data }, { freeze: false }) + tx.getSenderAddress = () => { + return address + } + const parent = await blockchain.getLatestHeader() + const block = Block.fromBlockData( + { + header: { + parentHash: parent.hash(), + number: 1, + gasLimit, + }, + }, + { calcDifficultyFromHeader: parent } + ) + block.transactions[0] = tx + + // deploy contract + let ranBlock: Block | undefined = undefined + vm.once('afterBlock', (result: any) => (ranBlock = result.block)) + const result = await vm.runBlock({ block, generate: true, skipBlockValidation: true }) + const { createdAddress } = result.results[0] + await vm.blockchain.putBlock(ranBlock!) + + // call store() method + const funcHash = '975057e7' // store() + const storeTxData = { + to: createdAddress!.toString(), + from: address.toString(), + data: `0x${funcHash}`, + gasLimit: bnToHex(new BN(530000)), + nonce: 1, + } + const storeTx = Transaction.fromTxData(storeTxData, { freeze: false }) + storeTx.getSenderAddress = () => { + return address + } + const block2 = Block.fromBlockData( + { + header: { + parentHash: ranBlock!.hash(), + number: 2, + gasLimit, + }, + }, + { calcDifficultyFromHeader: block.header } + ) + block2.transactions[0] = storeTx + + // run block + let ranBlock2: Block | undefined = undefined + vm.once('afterBlock', (result: any) => (ranBlock2 = result.block)) + await vm.runBlock({ block: block2, generate: true, skipBlockValidation: true }) + await vm.blockchain.putBlock(ranBlock2!) + + // verify proof is accurate + const req = params(method, [createdAddress!.toString(), ['0x0'], 'latest']) + const expectRes = (res: any) => { + const msg = 'should return the correct proof' + t.deepEqual(res.body.result, expectedProof, msg) + } + await baseRequest(t, server, req, 200, expectRes) +}) diff --git a/packages/vm/src/state/index.ts b/packages/vm/src/state/index.ts index 4557b689e9..d41ebe880e 100644 --- a/packages/vm/src/state/index.ts +++ b/packages/vm/src/state/index.ts @@ -1,3 +1,3 @@ -export { StateManager } from './interface' +export { StateManager, EIP2929StateManager, ProofStateManager } from './interface' export { BaseStateManager } from './baseStateManager' -export { default as DefaultStateManager } from './stateManager' +export { default as DefaultStateManager, Proof } from './stateManager' diff --git a/packages/vm/src/state/interface.ts b/packages/vm/src/state/interface.ts index 6cff8e10a9..349a803673 100644 --- a/packages/vm/src/state/interface.ts +++ b/packages/vm/src/state/interface.ts @@ -1,5 +1,6 @@ import { Account, Address } from 'ethereumjs-util' import { AccessList } from '@ethereumjs/tx' +import { Proof } from './stateManager' /** * Storage values of an account @@ -43,3 +44,13 @@ export interface EIP2929StateManager extends StateManager { clearWarmedAccounts(): void generateAccessList?(addressesRemoved: Address[], addressesOnlyStorage: Address[]): AccessList } + +/** + * Note: if a StateManager supports both EIP2929StateManager and + * the ProofStateManager interface, it can be cast as: + * (StateManager) + */ +export interface ProofStateManager extends StateManager { + getProof(address: Address, storageSlots: Buffer[]): Promise + verifyProof(proof: Proof): Promise +} diff --git a/packages/vm/src/state/stateManager.ts b/packages/vm/src/state/stateManager.ts index 153b6189fe..9142cc96be 100644 --- a/packages/vm/src/state/stateManager.ts +++ b/packages/vm/src/state/stateManager.ts @@ -7,12 +7,34 @@ import { KECCAK256_NULL, rlp, unpadBuffer, + PrefixedHexString, + bufferToHex, + bnToHex, + BN, + KECCAK256_RLP, + setLengthLeft, } from 'ethereumjs-util' import Common from '@ethereumjs/common' import { StateManager, StorageDump } from './interface' import Cache, { getCb, putCb } from './cache' +import { BaseStateManager } from './' import { short } from '../evm/opcodes' -import { BaseStateManager } from '.' + +type StorageProof = { + key: PrefixedHexString + proof: PrefixedHexString[] + value: PrefixedHexString +} + +export type Proof = { + address: PrefixedHexString + balance: PrefixedHexString + codeHash: PrefixedHexString + nonce: PrefixedHexString + storageHash: PrefixedHexString + accountProof: PrefixedHexString[] + storageProof: StorageProof[] +} /** * Options for constructing a {@link StateManager}. @@ -281,6 +303,113 @@ export default class DefaultStateManager extends BaseStateManager implements Sta await super.revert() } + /** + * Get an EIP-1186 proof + * @param address address to get proof of + * @param storageSlots storage slots to get proof of + */ + async getProof(address: Address, storageSlots: Buffer[] = []): Promise { + const account = await this.getAccount(address) + const accountProof: PrefixedHexString[] = (await Trie.createProof(this._trie, address.buf)).map( + (p) => bufferToHex(p) + ) + const storageProof: StorageProof[] = [] + const storageTrie = await this._getStorageTrie(address) + + for (const storageKey of storageSlots) { + const proof = (await Trie.createProof(storageTrie, storageKey)).map((p) => bufferToHex(p)) + let value = bufferToHex(await this.getContractStorage(address, storageKey)) + if (value === '0x') { + value = '0x0' + } + const proofItem: StorageProof = { + key: bufferToHex(storageKey), + value, + proof, + } + storageProof.push(proofItem) + } + + const returnValue: Proof = { + address: address.toString(), + balance: bnToHex(account.balance), + codeHash: bufferToHex(account.codeHash), + nonce: bnToHex(account.nonce), + storageHash: bufferToHex(account.stateRoot), + accountProof, + storageProof, + } + return returnValue + } + + /** + * Verify an EIP-1186 proof. Throws if proof is invalid, otherwise returns true. + * @param proof the proof to prove + */ + async verifyProof(proof: Proof): Promise { + const rootHash = keccak256(toBuffer(proof.accountProof[0])) + const key = toBuffer(proof.address) + const accountProof = proof.accountProof.map((rlpString: PrefixedHexString) => + toBuffer(rlpString) + ) + + // This returns the account if the proof is valid. + // Verify that it matches the reported account. + const value = await Trie.verifyProof(rootHash, key, accountProof) + + if (value === null) { + // Verify that the account is empty in the proof. + const emptyBuffer = Buffer.from('') + const notEmptyErrorMsg = 'Invalid proof provided: account is not empty' + const nonce = unpadBuffer(toBuffer(proof.nonce)) + if (!nonce.equals(emptyBuffer)) { + throw new Error(`${notEmptyErrorMsg} (nonce is not zero)`) + } + const balance = unpadBuffer(toBuffer(proof.balance)) + if (!balance.equals(emptyBuffer)) { + throw new Error(`${notEmptyErrorMsg} (balance is not zero)`) + } + const storageHash = toBuffer(proof.storageHash) + if (!storageHash.equals(KECCAK256_RLP)) { + throw new Error(`${notEmptyErrorMsg} (storageHash does not equal KECCAK256_RLP)`) + } + const codeHash = toBuffer(proof.codeHash) + if (!codeHash.equals(KECCAK256_NULL)) { + throw new Error(`${notEmptyErrorMsg} (codeHash does not equal KECCAK256_NULL)`) + } + } else { + const account = Account.fromRlpSerializedAccount(value) + const { nonce, balance, stateRoot, codeHash } = account + const invalidErrorMsg = 'Invalid proof provided:' + if (!nonce.eq(new BN(toBuffer(proof.nonce)))) { + throw new Error(`${invalidErrorMsg} nonce does not match`) + } + if (!balance.eq(new BN(toBuffer(proof.balance)))) { + throw new Error(`${invalidErrorMsg} balance does not match`) + } + if (!stateRoot.equals(toBuffer(proof.storageHash))) { + throw new Error(`${invalidErrorMsg} storageHash does not match`) + } + if (!codeHash.equals(toBuffer(proof.codeHash))) { + throw new Error(`${invalidErrorMsg} codeHash does not match`) + } + } + + const storageRoot = toBuffer(proof.storageHash) + + for (const stProof of proof.storageProof) { + const storageProof = stProof.proof.map((value: PrefixedHexString) => toBuffer(value)) + const storageValue = setLengthLeft(toBuffer(stProof.value), 32) + const storageKey = toBuffer(stProof.key) + const proofValue = await Trie.verifyProof(storageRoot, storageKey, storageProof) + const reportedValue = setLengthLeft(rlp.decode(proofValue as Buffer), 32) + if (!reportedValue.equals(storageValue)) { + throw new Error('Reported trie value does not match storage') + } + } + return true + } + /** * Gets the state-root of the Merkle-Patricia trie representation * of the state of this StateManager. Will error if there are uncommitted diff --git a/packages/vm/tests/api/state/proofStateManager.spec.ts b/packages/vm/tests/api/state/proofStateManager.spec.ts new file mode 100644 index 0000000000..412c363170 --- /dev/null +++ b/packages/vm/tests/api/state/proofStateManager.spec.ts @@ -0,0 +1,241 @@ +import tape from 'tape' +import { Address, BN, keccak256, toBuffer, zeros } from 'ethereumjs-util' +import { SecureTrie } from 'merkle-patricia-tree' +import { DefaultStateManager } from '../../../src/state' +import ropsten_validAccount from './testdata/ropsten_validAccount.json' +import ropsten_nonexistentAccount from './testdata/ropsten_nonexistentAccount.json' +import ropsten_contractWithStorage from './testdata/ropsten_contractWithStorage.json' + +tape('ProofStateManager', (t) => { + t.test('should get and verify EIP 1178 proofs', async (st) => { + const address = Address.zero() + const key = zeros(32) + const value = Buffer.from('0000aabb00', 'hex') + const code = Buffer.from('6000', 'hex') + const stateManager = new DefaultStateManager() + await stateManager.checkpoint() + await stateManager.putContractStorage(address, key, value) + await stateManager.putContractCode(address, code) + const account = await stateManager.getAccount(address) + account.balance = new BN(1) + account.nonce = new BN(2) + await stateManager.putAccount(address, account) + const address2 = new Address(Buffer.from('20'.repeat(20), 'hex')) + const account2 = await stateManager.getAccount(address2) + account.nonce = new BN(2) + await stateManager.putAccount(address2, account2) + await stateManager.commit() + + const proof = await stateManager.getProof(address, [key]) + st.ok(await stateManager.verifyProof(proof)) + st.end() + }) + + t.test('should report data equal to geth output for EIP 1178 proofs', async (st) => { + // Data source: Ropsten, retrieved with Geth eth_getProof + // Block: 11098094 (hash 0x1d9ea6981b8093a2b63f22f74426ceb6ba1acae3fddd7831442bbeba3fa4f146) + // Account: 0xc626553e7c821d0f8308c28d56c60e3c15f8d55a + // Storage slots: empty list + const address = Address.fromString('0xc626553e7c821d0f8308c28d56c60e3c15f8d55a') + const trie = new SecureTrie() + const stateManager = new DefaultStateManager({ trie }) + // Dump all the account proof data in the DB + let stateRoot: Buffer | undefined + for (const proofData of ropsten_validAccount.accountProof) { + const bufferData = toBuffer(proofData) + const key = keccak256(bufferData) + if (stateRoot === undefined) { + stateRoot = key + } + await trie.db.put(key, bufferData) + } + trie.root = stateRoot! + const proof = await stateManager.getProof(address) + st.deepEqual(ropsten_validAccount, proof) + st.ok(await stateManager.verifyProof(ropsten_validAccount)) + st.end() + }) + + t.test( + 'should report data equal to geth output for EIP 1178 proofs - nonexistent account', + async (st) => { + // Data source: Ropsten, retrieved with Geth eth_getProof + // Block: 11098094 (hash 0x1d9ea6981b8093a2b63f22f74426ceb6ba1acae3fddd7831442bbeba3fa4f146) + // Account: 0x68268f12253f69f66b188c95b8106b2f847859fc (this account does not exist) + // Storage slots: empty list + const address = Address.fromString('0x68268f12253f69f66b188c95b8106b2f847859fc') + const trie = new SecureTrie() + const stateManager = new DefaultStateManager({ trie }) + // Dump all the account proof data in the DB + let stateRoot: Buffer | undefined + for (const proofData of ropsten_nonexistentAccount.accountProof) { + const bufferData = toBuffer(proofData) + const key = keccak256(bufferData) + if (stateRoot === undefined) { + stateRoot = key + } + await trie.db.put(key, bufferData) + } + trie.root = stateRoot! + const proof = await stateManager.getProof(address) + st.deepEqual(ropsten_nonexistentAccount, proof) + st.ok(await stateManager.verifyProof(ropsten_nonexistentAccount)) + st.end() + } + ) + + t.test( + 'should report data equal to geth output for EIP 1178 proofs - account with storage', + async (st) => { + // Data source: Ropsten, retrieved with Geth eth_getProof + // eth.getProof("0x2D80502854FC7304c3E3457084DE549f5016B73f", ["0x1e8bf26b05059b66f11b6e0c5b9fe941f81181d6cc9f2af65ccee86e95cea1ca", "0x1e8bf26b05059b66f11b6e0c5b9fe941f81181d6cc9f2af65ccee86e95cea1cb"], 11098094) + // Note: the first slot has a value, but the second slot is empty + // Note: block hash 0x1d9ea6981b8093a2b63f22f74426ceb6ba1acae3fddd7831442bbeba3fa4f146 + const address = Address.fromString('0x2D80502854FC7304c3E3457084DE549f5016B73f') + const trie = new SecureTrie() + const stateManager = new DefaultStateManager({ trie }) + // Dump all the account proof data in the DB + let stateRoot: Buffer | undefined + for (const proofData of ropsten_contractWithStorage.accountProof) { + const bufferData = toBuffer(proofData) + const key = keccak256(bufferData) + if (stateRoot === undefined) { + stateRoot = key + } + await trie.db.put(key, bufferData) + } + const storageRoot = ropsten_contractWithStorage.storageHash + const storageTrie = new SecureTrie() + const storageKeys: Buffer[] = [] + for (const storageProofsData of ropsten_contractWithStorage.storageProof) { + storageKeys.push(toBuffer(storageProofsData.key)) + for (const storageProofData of storageProofsData.proof) { + const key = keccak256(toBuffer(storageProofData)) + await storageTrie.db.put(key, toBuffer(storageProofData)) + } + } + storageTrie.root = toBuffer(storageRoot) + const addressHex = address.buf.toString('hex') + stateManager._storageTries[addressHex] = storageTrie + trie.root = stateRoot! + + const proof = await stateManager.getProof(address, storageKeys) + st.deepEqual(ropsten_contractWithStorage, proof) + await stateManager.verifyProof(ropsten_contractWithStorage) + st.end() + } + ) + + t.test('should throw on invalid proofs - existing accounts/slots', async (st) => { + // Data source: Ropsten, retrieved with Geth eth_getProof + // eth.getProof("0x2D80502854FC7304c3E3457084DE549f5016B73f", ["0x1e8bf26b05059b66f11b6e0c5b9fe941f81181d6cc9f2af65ccee86e95cea1ca", "0x1e8bf26b05059b66f11b6e0c5b9fe941f81181d6cc9f2af65ccee86e95cea1cb"], 11098094) + // Note: the first slot has a value, but the second slot is empty + // Note: block hash 0x1d9ea6981b8093a2b63f22f74426ceb6ba1acae3fddd7831442bbeba3fa4f146 + const address = Address.fromString('0x2D80502854FC7304c3E3457084DE549f5016B73f') + const trie = new SecureTrie() + const stateManager = new DefaultStateManager({ trie }) + // Dump all the account proof data in the DB + let stateRoot: Buffer | undefined + for (const proofData of ropsten_contractWithStorage.accountProof) { + const bufferData = toBuffer(proofData) + const key = keccak256(bufferData) + if (stateRoot === undefined) { + stateRoot = key + } + await trie.db.put(key, bufferData) + } + const storageRoot = ropsten_contractWithStorage.storageHash + const storageTrie = new SecureTrie() + const storageKeys: Buffer[] = [] + for (const storageProofsData of ropsten_contractWithStorage.storageProof) { + storageKeys.push(toBuffer(storageProofsData.key)) + for (const storageProofData of storageProofsData.proof) { + const key = keccak256(toBuffer(storageProofData)) + await storageTrie.db.put(key, toBuffer(storageProofData)) + } + } + storageTrie.root = toBuffer(storageRoot) + const addressHex = address.buf.toString('hex') + stateManager._storageTries[addressHex] = storageTrie + trie.root = stateRoot! + + // tamper with account data + const testdata = ropsten_contractWithStorage as any + for (const tamper of ['nonce', 'balance', 'codeHash', 'storageHash']) { + const original = testdata[tamper] + try { + const newField = `0x9${original.slice(3)}` + testdata[tamper] = newField + await stateManager.verifyProof(testdata) + // note: this implicitly means that newField != original, + // if newField == original then the proof would be valid and test would fail + t.fail('should throw') + } catch (e) { + t.pass('threw on invalid proof') + } finally { + testdata[tamper] = original + } + } + + // tamper with storage slots + for (const slot of testdata.storageProof) { + const original = slot.value + slot.value = `0x9${original.slice(3)}` + try { + await stateManager.verifyProof(testdata) + st.fail('should throw') + } catch { + st.pass('threw on invalid proof') + } finally { + slot.value = original + } + } + st.end() + }) + + t.test('should throw on invalid proofs - nonexistent account', async (st) => { + // Data source: Ropsten, retrieved with Geth eth_getProof + // eth.getProof("0x2D80502854FC7304c3E3457084DE549f5016B73f", ["0x1e8bf26b05059b66f11b6e0c5b9fe941f81181d6cc9f2af65ccee86e95cea1ca", "0x1e8bf26b05059b66f11b6e0c5b9fe941f81181d6cc9f2af65ccee86e95cea1cb"], 11098094) + // Note: the first slot has a value, but the second slot is empty + // Note: block hash 0x1d9ea6981b8093a2b63f22f74426ceb6ba1acae3fddd7831442bbeba3fa4f146 + const address = Address.fromString('0x68268f12253f69f66b188c95b8106b2f847859fc') + const trie = new SecureTrie() + const stateManager = new DefaultStateManager({ trie }) + // Dump all the account proof data in the DB + let stateRoot: Buffer | undefined + for (const proofData of ropsten_nonexistentAccount.accountProof) { + const bufferData = toBuffer(proofData) + const key = keccak256(bufferData) + if (stateRoot === undefined) { + stateRoot = key + } + await trie.db.put(key, bufferData) + } + const storageRoot = ropsten_nonexistentAccount.storageHash + const storageTrie = new SecureTrie() + storageTrie.root = toBuffer(storageRoot) + const addressHex = address.buf.toString('hex') + stateManager._storageTries[addressHex] = storageTrie + trie.root = stateRoot! + + // tamper with account data + const testdata = ropsten_nonexistentAccount as any + for (const tamper of ['nonce', 'balance', 'codeHash', 'storageHash']) { + const original = testdata[tamper] + try { + const newField = `0x9${original.slice(3)}` + testdata[tamper] = newField + await stateManager.verifyProof(testdata) + // note: this implicitly means that newField != original, + // if newField == original then the proof would be valid and test would fail + st.fail('should throw') + } catch (e) { + st.pass('threw on invalid proof') + } finally { + // restore original valid proof + testdata[tamper] = original + } + } + st.end() + }) +}) diff --git a/packages/vm/tests/api/state/stateManager.spec.ts b/packages/vm/tests/api/state/stateManager.spec.ts index 97976c813c..e1136f5de1 100644 --- a/packages/vm/tests/api/state/stateManager.spec.ts +++ b/packages/vm/tests/api/state/stateManager.spec.ts @@ -385,7 +385,6 @@ tape('Original storage cache', async (t) => { tape('StateManager - Contract code', (tester) => { const it = tester.test - it('should set and get code', async (t) => { const stateManager = new DefaultStateManager() const address = new Address(Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex')) diff --git a/packages/vm/tests/api/state/testdata/ropsten_contractWithStorage.json b/packages/vm/tests/api/state/testdata/ropsten_contractWithStorage.json new file mode 100644 index 0000000000..6d6fc85d3b --- /dev/null +++ b/packages/vm/tests/api/state/testdata/ropsten_contractWithStorage.json @@ -0,0 +1,17 @@ +{ + "accountProof": ["0xf90211a08b35ef8aaf1001ffe7dff4f6221f26b66e34dd968abcee85a8ac32091d06251ba05cafd14da795cdf8a394b35defa13a536fdfac14b6030af3c37f6493c74f900fa084d432a820fa6ac48249fe05cf14cb1c2d147b632e88c55b08b0453b855e78dda0728563ec9378f52f06b03c4e95844719936356b4f2a8499ba9125823d1acd8aba0f9c8c6d587de5a4beb8beab7c268f1843004378dba0d282e7bcd8c85e792d7eaa0e85343ecaeea1eaff936300ccde67f35c496fa94ef598155b710f7a623a57936a08ee22057f1ecf34963bc50ae61316946a624647c22175727fc9391a20e9fd228a065baa728267c680520b7fc43e1f9139f907122d5573d66470d89fcd053ea3140a0bbf921d19e38c35e214ebaaf6a870d0a0150ca82dbc6856c78a30c484f19e7a0a0dee8ba0a60377b394b888faf60952d79662d5a3d858e2ed0a905064ec894dbdfa009d51219b8b2485c903ac06a42493e3541e406ccf34f57850f1096222165aadaa05c708ed7f6a0d0d3aa13d6f973bcdfc7cf0f6d26f1d4485d820de2d2311b3ee1a042f84164e5d78135eb89917b395a3c635c2421a864aaeeb6c20492b7109cf57da090dbe8366a32a6e13ac284ee7755c607833198aa7b7d0c6e2cb5c879aac8d187a026ca02d08826865db5d5ccb312e0daad3d49a2b7bff32dbc817119fe36f1b3bba061c0982c847660740a3a240d8ee893db660a50afe88f1d0aa404c75954d9c50780", "0xf90211a03ff1d5e821569f717e0c3455ac9756d6c94b150f74d158d272a8926656ee0a46a0b1834b5a5f289c70d4c518eaaef5626d0d4cd5a01fa898fba722306421d1bc2ea0913e93d8301aa9b7476e11fa0297ea2df84adf473d02aedb01682d0adf1f5a41a0d8d513bf3184b6e5bd01474ce11c7f14ec1054e6575a6adcbca3f5ee2629ded7a0a4cada2c7aa231409735ab131028efffe645969aeb0b5aabae38e5ff51bb04d1a09530aae594b70d16b49a04637b5688ec43cef24f9e1ed18081f5ab7759f13764a02af4b25e0f798fef6b2620dbeee9b47f16a6c8bf18d88078dba943d7922ccf50a0a23108fda2e48990f923b8438591c938c5eddd854f1b4ea4cf32eaf32fe0d0bfa0b487eff2f14bed1074ea4c7be8b13f83bfc516edd64bcc6efaf73f3b63aa8033a0adc54060504b9e25c51de1089eac90f4e74a972f50be77e43c69faefdbe0fa65a0385875af4011d7180b6ba1c7fbe1dd40bb141df13f85988900e5e96059b7da4fa0b6239b2d94701e20575be16ad8bdca89c67d4bf04b76345ab5c14e03547e79ffa0675aa416d2e234cd0426001ef9bae81e3ab1d8f3c3decf8f913ee8af6b97a2f2a034f71a3625128a542ebea41a78fe2d5a5f7e2a2ca3a9d90c6c787594b3c4c43ea0d85a723a13ebd258614effceacf53d82848f44a754683695dbdfebc1440ea1c4a0c7d3904cb429d7a385f95b357c75170901b216bed48baab71ff7882ab69274c380", "0xf90211a060ce1890c5589c7bb10ca032dad76f7d6cf2d17c875c5ff6d32d71b789653cf3a029006414deeeb9cfbf6fb15ccf7ebef05553b239b2bc2c3170ea225a236b2af5a02dfb57a167d346253bab122802a33a2eae3ef920a430e4e96e9a340e7f63a756a09107f83a0ee442b2c62948d35b4b0f544f69091438350aaa823995568a1c89f3a0bd9220f16641eec193aa8c3dd714a4dcc3274638265ba9cb215d1c17a944b0d7a0d691133c311a4c0f482cfe2925a3dec20a1dc9ffec6dd72711ff98991a588e54a0cc46c09cd026e878171da35022a4dca33acd704b041c7ac01bfb3d9c6b70d5c5a04cf327b313d05318bfccb4d35f5c99e468e9143bf1462c00fa4ae10c4cb8aeeda01658bd73b4011f1004db958885e91e96fa2a7ed22f8e7e9da2a6bf9ffb26775ea085189531bbc167cf0430ced9571a1641a27b0cd1248719974ee39b52705c7d9ba041f39a4d53f6ed4fb001ae3406cd5b2545bced53f153c557d8b0bbdfc08cb9eaa07220cec7308847b5278480eb211345f9fffa4e50227125e17edf82752fc9c13aa02fa99f9b53f6e5b3df6eb742eb50b76fe5335f712524f0cce6d2a3b868248195a0f44daf830bf4ba817cc76e5ca91105372e47aa2c7be4d4bbf5cd03bcad9b73e2a08d3419223c6d1d0a5cd12a29899cf42994f95ac14b493e41489830e18a4ccd27a0291f2e2f4d52b52780b5d1fb1f13f84bb616ea56039891c603541642d9c589f580", "0xf90211a05e9af3fa4daf76aa45191487c149dd97913de59bd5d32df39cb7e1f7b0ee161aa052081ab5443ce8a3a3c54c0c86ae898d743defdf2ae6cb448277cec7f1462914a029a11cd4bce736aee0139f9f09a4b0f3bfdd060adfe826e1c39416ada16fec7ca011e9fd511222c8f8bf6c371216156412b3a5925d85631f8da1ffdbf2234afb81a0bf1684b9c2d0a0fe04ff8aa02ce17786e0378b71a0a6c7f6c9d1a71975c5ec7ca0b0b52576ebec5d551e44b76968dbeaa68e526e6082418530b691135a51a70938a0ad70d7049b91aed29efab50b7181ac4112eaf6ede8197681fbab34a96f8a64bda02f52322c446625c55c3506889f9cca979d81a9219672f8d2641b721a934c168aa0874f0b164a8610b0c9e79270979ba27b2dfe8edf6529f831294430a1d1332aa9a0eae8e4e2263a5b7fa1c56164d39ef0a866323251d0beeb2bf9ab1cfadfe2c44ea005cd2f6463c7b651a4319cc00de1f89485d024a700af86cd083890bd8c165bb7a06505967a1421e287ea8000f2015a4c7706f0d85aac13fec0b9c08036da5a1b61a0509bb34713a64fa1e88e6f42da0890854ac634debc96242e1116c7de25f16d28a0683430342d3e9e30e5b4e6b80c549e6e1ccc5c8e5d8ab5fae969277ab4322f5fa02c658b343760ccd98ec13652d56eee1f41da0cbd1861b6b8c4cd9ca59ad47d2fa09492090e6d155f387377414d1e3adb7ffa86b9ff6956c596de25d3561172828580", "0xf90211a0a3c4c8e8a28fa9f79095584255ea592ac92d0f46ca41f9037ef3faf30c589d53a0a55da8ccff92a8b040d79fcb5aeaee00eb34298e50e727b19e3abb7bbad02fcda0edd2f888089504c285a4bc5cb26d27a4c3fa6c5029a25b170827b159057268e2a0e30546d5967e02c959e2f16e7a325a01eb7387283d31c5c1c227854b7be869c9a04d798ef146385867e5e73994a149d1128c5e9a5ea836bdb4a126e316b1d78697a0b4bcfc4eed3073d619068d9c11212d341af70b285d0effdd46ae3974b5469563a0b83d23a9398df49849a3e3a1875f5fc0170e89445cb631ecb3d2bbf6598f4058a064d91ab2ec8647ebc2962c608e97de4eb42a9dd5287e73060954f41c3c21eef4a0f2684b60c29b99f770969b79629ecf7bb9bbcfe6334d8b434003722c3fa3f1d9a06dd143bd7b3bb0e4a2ec97aea816164438d2adf6e2ddf369066e9e982f381856a0f2c4432bb29dd8b5bcb62ad1e015ad6a1fead0dddd34130e37caf041958959b4a0a88c99c58d8e1ca250461ed6acea756a560a8042471d6765d22a6e222ea373d8a085b070510e6d6b6bd566daa2667a8268fa2dbb33a7a65b2ca266e81b92a09cb5a0c38646039b637592291964855752032876415bcd441470cfad85f0c11ce762a6a0ee1397c1b1ea58b7822457e9ab3d8f5afbab0a4e1152ff5db2556db31dec3f43a00a3ff6b68ed4efc37c072af997a66e0a40a7ddb5613e963e16ab01603e0bad3780", "0xf90211a062e36560e7f46c890df78c109f2f7cfbcb4509b9178a3eba9e88915c00feace4a0e6adf7074dd244bce4dcb4f0975d03cb3a4b646d72c2791a22e41647ea768a75a02fdc4d277404171bfa9e03fd6c5fc3f02a032b75ac6d770c282009fe46b77b66a09ac4073845465687ecbccb1b9f9dd4c38569800fbea4b0b122ef95ebefd1a4f7a0fcc4219679cb3b994cb0a4128ff76a6e7d51fb683f960dca78faa76bbc267b30a0f535320889ffd8507aefe1d9b5befa33f68a9cf3dee7a98313bf0374052c7dcaa07dd5b4a9dd8bdacfba7712b05f0323d3c5cccfbb9387685a9311470ac15ae967a0313d18c661be30ea11a0bc6e0eb8433d63b08c0bd442d152ee6421a56b8631b7a04486626f943450848b0094dbe249a6aaac5947f1844fd8dc9618b8470fd61d89a0597a745df0d22f4594e307fc8980660ba682638800048855ebc19c607341aa2ba01e40240fb0a16209c8e7ff8733b57b88604df3192acd8c3afe2ef4b7c5ecc5e7a0808269b6888321b83d1d3b5884113cd8d171d4f77996126b606a9e8f1ae7b8d9a0183c0868aab0dec1a99bff0ac32667dc7981c952bd0f11f4788afaa9d37e1fd1a0413e4619bfacaf5064e4dac7945c14866ab95159aa975e52c778f7e759e55402a07a9cd4bf77948010d847d9fd9c31cd685a564a7bab08a691e94b4069a41be8b9a0cf38b79459ccae5d7f08cd1e21dff889433788703c3e54c371365b8433150bad80", "0xf8679e2034ecdb3552abd80419ba029c694dbeabec0218b671cb58f51f86ed8689b846f8440180a0e46839eb7240b70373cf860be4b3d1b96068d0b39421b17f3269daa8eef9a8b3a0f5cdc275a53e3e2d213e2da6d88401a9bb792bfc0168b59b7a3a512fcd781d5e"], + "address": "0x2d80502854fc7304c3e3457084de549f5016b73f", + "balance": "0x0", + "codeHash": "0xf5cdc275a53e3e2d213e2da6d88401a9bb792bfc0168b59b7a3a512fcd781d5e", + "nonce": "0x1", + "storageHash": "0xe46839eb7240b70373cf860be4b3d1b96068d0b39421b17f3269daa8eef9a8b3", + "storageProof": [{ + "key": "0x1e8bf26b05059b66f11b6e0c5b9fe941f81181d6cc9f2af65ccee86e95cea1ca", + "proof": ["0xf90211a02d6cac12800d8d73f86351c2b8b3fa40de9190d5f9b3d4f4233d5fd9a531e760a07c3d0443c97dfa5afd56f781bd62ca3cabdee8af991855850a0d188751c327a5a0b495365efaad08b5ceb9728004a1f285bfcff839cb65a49d212a438449ec59a8a0eef0cf656d468ed03c91f43d7d1b65019d851cacc73f6250e9ae22cd6f0d9d06a03cdfb80e3e9a29aad4da5ebfd15de6e93bd230a4bf39c2e6978f3cbd96297410a0e0ff13908ecb1d069ab8ac334a3fae732528650b7af0eb02cffe40fbca3df2c5a034917774cd9ac5b02a5258272416d4f4fc069bb742bcadf135c10d44ef98efc3a0e1c3c1c5de7f1623d07571fa2546c2d80373a5d16820f04f512095a9a1809627a03ab6bdd79e3b464f96e39499ff4acce9e2f37f6e435d9f53e192cc3f422022c6a00e1a5a0f3e8035f1195c952d5494accdb71704c5e190d52b2990acf851a5ad97a07719d957bf528a032c59abe3ef90c3c1c97bff8d836419cf0bd2ee5fed3a948ba0041e187841f9f0974841ae777ecea64241896db6e6d822bd04f1681f1bc386f3a02aec78f3c6bcffaac17ed045c6eff9c8af2e8fdf3e7cd3d4701c0745fd66439aa025fde32dee835e3c1ce3672650638641c7c9dd3903690a6b5bd2e03ee7abc4f6a0721810601d0168714596cdd85a2d62e959180b58f09fe65ad8ed4f1c2173b4daa0932d2d58604c9e6895ae21179fe935420afbc9a9b4a330c19da7ee710595673a80", "0xf8d180a011f300618845798a5582d460b48b52ed1f8935adaf954bdc1990878d8d5aeba4a0f0cc9f4cf7177dbc0b909430fe15af3dc795b7c14a33cafd30b0b71416bfe2b0a024429f5ad2fecccb82a1358e74d504bdbc2969e4ff3522b34b8eea003cc06e0e80a0ebcbbe62264e28140d29bd5ca7099b46be5822e7cd48a15a32ddcebf5badab1e8080808080808080a0dceebcf7eb6736b8155bc04026847aeee1b7595c1483eaa558bfcd09368c032ca0d5027c7feddfaf1144bd17aa19e3c5eee498a8b2d64ae8ca822c3f4c4e5fb3f380", "0xe7a0207effcd57f058fee0966d20611c58331f42ed187eee0a66f46c7e72a752facb85841e4ebdd7"], + "value": "0x1e4ebdd7" + }, { + "key": "0x1e8bf26b05059b66f11b6e0c5b9fe941f81181d6cc9f2af65ccee86e95cea1cb", + "proof": ["0xf90211a02d6cac12800d8d73f86351c2b8b3fa40de9190d5f9b3d4f4233d5fd9a531e760a07c3d0443c97dfa5afd56f781bd62ca3cabdee8af991855850a0d188751c327a5a0b495365efaad08b5ceb9728004a1f285bfcff839cb65a49d212a438449ec59a8a0eef0cf656d468ed03c91f43d7d1b65019d851cacc73f6250e9ae22cd6f0d9d06a03cdfb80e3e9a29aad4da5ebfd15de6e93bd230a4bf39c2e6978f3cbd96297410a0e0ff13908ecb1d069ab8ac334a3fae732528650b7af0eb02cffe40fbca3df2c5a034917774cd9ac5b02a5258272416d4f4fc069bb742bcadf135c10d44ef98efc3a0e1c3c1c5de7f1623d07571fa2546c2d80373a5d16820f04f512095a9a1809627a03ab6bdd79e3b464f96e39499ff4acce9e2f37f6e435d9f53e192cc3f422022c6a00e1a5a0f3e8035f1195c952d5494accdb71704c5e190d52b2990acf851a5ad97a07719d957bf528a032c59abe3ef90c3c1c97bff8d836419cf0bd2ee5fed3a948ba0041e187841f9f0974841ae777ecea64241896db6e6d822bd04f1681f1bc386f3a02aec78f3c6bcffaac17ed045c6eff9c8af2e8fdf3e7cd3d4701c0745fd66439aa025fde32dee835e3c1ce3672650638641c7c9dd3903690a6b5bd2e03ee7abc4f6a0721810601d0168714596cdd85a2d62e959180b58f09fe65ad8ed4f1c2173b4daa0932d2d58604c9e6895ae21179fe935420afbc9a9b4a330c19da7ee710595673a80", "0xf8b1808080808080a021a4e58f3181f0d96031aa0d8a691965aca344e7aaebb06a3ffd3aef1b9888e4a0fd3e792609c07b1c0ec42de6f0566bc499a2a624c299bccd9dd53cd650edc9a7a05bf53100c12d1de27e3760f722ff1ca9558efabe1bfee6ebeb654a101a386b6780a07a673c9312bbe9dd98d897bcb4f7415a9528f0613a3d3ec2e5b291fbe281381480808080a07399e2f0b2543088feb80260c1ba0d0cb47bfca119ba26e79e52b57d854d06d280"], + "value": "0x0" + }] + } \ No newline at end of file diff --git a/packages/vm/tests/api/state/testdata/ropsten_nonexistentAccount.json b/packages/vm/tests/api/state/testdata/ropsten_nonexistentAccount.json new file mode 100644 index 0000000000..181b97c46a --- /dev/null +++ b/packages/vm/tests/api/state/testdata/ropsten_nonexistentAccount.json @@ -0,0 +1,9 @@ +{ + "accountProof": ["0xf90211a08b35ef8aaf1001ffe7dff4f6221f26b66e34dd968abcee85a8ac32091d06251ba05cafd14da795cdf8a394b35defa13a536fdfac14b6030af3c37f6493c74f900fa084d432a820fa6ac48249fe05cf14cb1c2d147b632e88c55b08b0453b855e78dda0728563ec9378f52f06b03c4e95844719936356b4f2a8499ba9125823d1acd8aba0f9c8c6d587de5a4beb8beab7c268f1843004378dba0d282e7bcd8c85e792d7eaa0e85343ecaeea1eaff936300ccde67f35c496fa94ef598155b710f7a623a57936a08ee22057f1ecf34963bc50ae61316946a624647c22175727fc9391a20e9fd228a065baa728267c680520b7fc43e1f9139f907122d5573d66470d89fcd053ea3140a0bbf921d19e38c35e214ebaaf6a870d0a0150ca82dbc6856c78a30c484f19e7a0a0dee8ba0a60377b394b888faf60952d79662d5a3d858e2ed0a905064ec894dbdfa009d51219b8b2485c903ac06a42493e3541e406ccf34f57850f1096222165aadaa05c708ed7f6a0d0d3aa13d6f973bcdfc7cf0f6d26f1d4485d820de2d2311b3ee1a042f84164e5d78135eb89917b395a3c635c2421a864aaeeb6c20492b7109cf57da090dbe8366a32a6e13ac284ee7755c607833198aa7b7d0c6e2cb5c879aac8d187a026ca02d08826865db5d5ccb312e0daad3d49a2b7bff32dbc817119fe36f1b3bba061c0982c847660740a3a240d8ee893db660a50afe88f1d0aa404c75954d9c50780", "0xf90211a09b746fafbaec59bfe7531df4c3f7e90fccae2310b4a07244660180ebdae83740a00ca3a961e09eaa73937dd64b4cad6837ca8c0821a1916bdb88ae740d431f8224a025b14b34bb7bffb0569764ebae9fd9423e046c09e4fd23c5f4a3f98aae736865a08c67c635ed533f995a81d4f31e49657d6ee9a0c55908d83316bc02cc2b83ae36a048458384065d237f47bcdef04bf8b8e3da48b7b69918b2ad6140d531d917d8c9a02695404dbe42d4d236adb5bb9a0e8aa093f8dc852b43d6440e354cb1c60256d3a0695b968f22e3e0423faf0f40ccc3d2efff833d60cae01ee63ed9ee394b5b76a8a07ae0b7685ba88b1781e04cadeeb4c507b09cf697d1de7af3ee0f3e1b4a05b2ffa0210366b57186281985b8282d42a4f5a5a40dcf313d289b4eeb0e2daafad32f11a03ccafe0cf0444ded8aec013904c5eb769e6fd13e33b11d1987cf4b5b90ea1f71a09a1cb6c95fa9c023b69f99bfb349f8d42cc515670581dc4d89f59034d43e6f7ba00309f400a9bd16859ef8f05d66b7da5e352dcea7a8c78e0c80e313bc44e45e46a00b21a780fe8be342e1b174be47cb8f135bc1ffed6572b5e2a84898aed7ca5039a0cc9c1e379a9a07b241d69497feb4c1a11dcd7711e1cd5ed9000d7c06a14b56d1a02e12ef72e9479309bba56693075259ba16b344a67ba63b0b107e9ac21ee87935a09f09b7a10b78dfa3dd2a9056a68e5256fcf0aacc72a50d675903760e8f3873d580", "0xf90211a0c77f7f856b72389bb24bf62830a3d06970bb4f30434f5b30a8641b1f87dff3a9a0d5f62ad871afdf1cf9acbafe9dd59c8402d84f405210d29b64152d1d86c9f6f3a0b2ef592df3844f2658c985936fe25b36c5ccc20cfb5784cd0d6c5e48bd59a0fda05d5918d537707b8081857d49b41e46bfad4aa678b6e752fc1b6896c1a2b12037a053a2af0b50e5e7aebb906f594f9298c3021983b14d3658bddab310cae15ca04fa06632410e8baa78fd71b16ec9d51d393d5e235815f4098e1cb33fe59562c08cf2a07c4d680cf02d0335323bed8f68dddb7a1ea1f6b5ae8a073b86ea94725c7976efa064d41944b9d16a2a3874eccf6afddef309d49f817d4ac7ad157f580267dd98f5a0aea1cc543c6acfa6c791b2d10a2bc817bf8cc3bb4475c4b6643fc0c9edfc9636a097db9562b22b4df35b5657e486fefd4f37e61b5245bb01fcecce15fef354db34a03ed0dc09dbf21f2a34bbfd55c32763b73767c488b7ecd8438e612b1f1edafb15a03a68f87f03b402306513d84154268c8e47d9b4a82afdc166ed53ac68f45cb8c2a0125c451b9efc5652dfdbbcdcf4e510c8fb635c9ce6f38d4e1b0759e4148892d0a0f4890433b6e78937195db61187573a22929538d7568ca557a22a5481ce256896a0ca6e0cfe4b2bde21d0e19d84b76df64540bd8dba8f0520aedfb746e54a2110e4a096f0f6ef207acd392eeedeec3421d4c8209bba852c2794abed25ee0902b46a6980", "0xf90211a09328b9945087040f7239ab09f3ef8c37aac04ffe7ac9a5e26715df7d72e6cbd1a07bb098ea9c1c2209f0bae7b73813c72a85767e3537468aafa7347fb205d571cba02845790d87fd1ad9075fd73ecd1d189db5b5bb959e545f5a65450886e45a2692a03c9471e0f8c9b405f8050cfaa4ba012105a575052bd090f9364c59da6f8f73afa09d5f7bf6abb44400e271a6631686c1f0a3accf01337a6d45be3e02a6499c964aa0e4735aba6b2680dcbf311d4a91d41050928ec0a39ab21d8144883533b2681ee7a01b1544aba915c38085d2d1217cc9def8f227ff0e6ed9cbdb221de6d3972684efa0e42dc212adda224a2c4841437cecee740ed46b7663988524c89d9452936331baa0dd6438793fddc6097569f17c4da7260a8d7977ba491f9dc8865601dc407e8710a0210a0f858fdedbafbefe78fba9e79b1332d2f60c508f84b56f7374367dbc9528a0e061e2725c148fe2294075bf12c16cbd0c3b17cebb2762014fdf1852f3f7d69da02adec7fdf56143e4876ee8431172440a788e9c28ecfd96b47191615742203f89a0aead602208e4b64bc9db20f44aba1829ea3fba13435eb17472f16f123028b754a007f37c3fa6b6e59280b0abc26426486978e9e759b0419c31e1d26a5937580853a08de3670a41f86c916fbfc1232f27f7c714250006cd5d723f180480c6a778b294a0746c363bb9f2b65d27d8db4b9b65fb06b4d48543a342c84129205641148ce10e80", "0xf90211a0f9c18d36278289fe4db820f13f5a605506408e378eb053ffdc3002480ff4d96ca04ff083bc0c9b339b8b4996b0b2b486df5993999e5a77d9a3cc6cf28a67e7df9ca08f3ccbd74eb7e86029e407fd13ce97c7bfe59a1a4767557df4b3cecc0126e332a01260d2d4f4f47d7fa898eb2cc4d75ee522eda8cdde547d4570a93a0cac046283a088a4a891bdbcfae8339e3b8f5d8e46f927ef9c358253c7b97d199fa361457f03a0b00b53a7e81ae32247a187698b79ec5cb22acb0e367445cc3462df0c76d7ffe2a0cb9a847f5809b650cd7d6ad13a01077bde94b49272c3ea034de1497b19d4dca8a059d17a33e6a4160d637abaca94828162c38dbc847366463792e2bebde8abed8fa04b5b561e1ec26b9c12533443a309c01c7aa730d05669afc7fcc44a2e52474b63a08769244cf7e7b8f27c6ca04144535ea9a7ba78f303ee8bfed293c238fe9089faa0ec80e65ade132afdbe583f93bcce7d3c259de6091a8472d0727f53e17fa36aa5a075d650f6462355ce62e26d00eefc229c7ae7ef632e3bc30e1639513065d0c70ea073ad244d3638a66e709df7660c91f0f06a0eefcf3e187ca668cba5e506e14e53a08f8235b788373bb22e28bd8900a0c95c0deb805e79a6e5997a8c7535ed09d6dba0b19d4e1503d582f33e9706edb6dfa3d1d0bf9d80a75a980bc533a7c1f858d15da0dca273067882c2be8da00e75a8a98393b3343959591182e51d3805ad55f768ca80", "0xf901b1a0583a7d00c9bb314f3ee940090655de5e26777d4de3540786b8956f5c52e4bc6680a0829054045be406c1ad4261ed7d43f09a182ae78060af0fa7f840d4fe115e2fa2a03becf44de9957e41c3b33de2610fb6df58d5e4f8f3f13cdc0c078fbfdede638aa0c8062f45f4f9ba1ac9115a677edb3dbe0fa636f8100776ae672540cecf47271fa0ca4fe21d1d7fcf0c507105cdac4a650c0d64a635119bf0eb44395949b4741791a0180d174727317dae6b6cff12125fd35cfd1ce4ac9e6723b11f40930d7e46293ea0ff62b84d1c8daec04456279571ca023d8e267a9679ce2bd3fb8909b35b7b6565a0852dcaf6235f0f026165787f6a41efe25328236f2f02e41614c0302041a0a93ba00334192a13b3d391bafd35a753d791b92deba86fe7e925a4583d012e315fa8a6a011b478c60b475ac17e3f8af6843c70afdc0940bfa5492dcbcb74219887b33d5ca0b1d221035f1d25b6d3eb8630030da1d71e18b43484bd817fa50b3d42cb970681a00bbddbe47206773634d383e6ec10e0730f58ac0106b4850f32500cb40d17a72aa008c7b471b980fc999674830ce9d45372937663c55c4290d175194eff1b036c47808080"], + "address": "0x68268f12253f69f66b188c95b8106b2f847859fc", + "balance": "0x0", + "codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": "0x0", + "storageHash": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "storageProof": [] +} diff --git a/packages/vm/tests/api/state/testdata/ropsten_validAccount.json b/packages/vm/tests/api/state/testdata/ropsten_validAccount.json new file mode 100644 index 0000000000..fca381680b --- /dev/null +++ b/packages/vm/tests/api/state/testdata/ropsten_validAccount.json @@ -0,0 +1,9 @@ +{ + "accountProof": ["0xf90211a08b35ef8aaf1001ffe7dff4f6221f26b66e34dd968abcee85a8ac32091d06251ba05cafd14da795cdf8a394b35defa13a536fdfac14b6030af3c37f6493c74f900fa084d432a820fa6ac48249fe05cf14cb1c2d147b632e88c55b08b0453b855e78dda0728563ec9378f52f06b03c4e95844719936356b4f2a8499ba9125823d1acd8aba0f9c8c6d587de5a4beb8beab7c268f1843004378dba0d282e7bcd8c85e792d7eaa0e85343ecaeea1eaff936300ccde67f35c496fa94ef598155b710f7a623a57936a08ee22057f1ecf34963bc50ae61316946a624647c22175727fc9391a20e9fd228a065baa728267c680520b7fc43e1f9139f907122d5573d66470d89fcd053ea3140a0bbf921d19e38c35e214ebaaf6a870d0a0150ca82dbc6856c78a30c484f19e7a0a0dee8ba0a60377b394b888faf60952d79662d5a3d858e2ed0a905064ec894dbdfa009d51219b8b2485c903ac06a42493e3541e406ccf34f57850f1096222165aadaa05c708ed7f6a0d0d3aa13d6f973bcdfc7cf0f6d26f1d4485d820de2d2311b3ee1a042f84164e5d78135eb89917b395a3c635c2421a864aaeeb6c20492b7109cf57da090dbe8366a32a6e13ac284ee7755c607833198aa7b7d0c6e2cb5c879aac8d187a026ca02d08826865db5d5ccb312e0daad3d49a2b7bff32dbc817119fe36f1b3bba061c0982c847660740a3a240d8ee893db660a50afe88f1d0aa404c75954d9c50780", "0xf90211a0ce7a6755a8443c9fd0cc08f374556033a1e6ce60aa5979fe8df7146c6c1fdec4a04531ebafd19bc1fc4a523cc3cdae1a0aa9e4d0ebb9c967c04038cc7b32341de1a035dabb7ae61124027aa8c587703217401680d24d141a76ac2e3f8eec4024d8fca04d6af591b8050bb4ad2f37d7e4cffc443643917e5775199c383b1e772b1949d0a01bd02db2b2b80e98c03e3991eeaad519e6fd280f9b59214cc6f09a961d6c8d98a04af90b5fdc1c45420c30696bd0b5de4fb67e4ae6fb6ea5eee82cd34d484f9ab2a00d9eb4c1b6a1c3e2206c80b5d199de4d16c711bdc5d9421f377aa90c76d31da0a0d3466cf77c90edd1c996fc6132de73dfd5c0f7969939fd59b7acc5ad69fb353ba055d9c98f5c78471f4fbe5f692ea0f810da8c5225a7bfd7818a686c7646446b00a030852522d75897983eca2893388ff1c45b8de755472a3af1d8228f6e35770d61a0714d8230137c266b283f9dedda3f91afd0ffb06e3188d1592b51dbeb366725f0a0fafb282a71f6b66114bded6045e92b2ca6af245981c551df0461018e7f7c2057a00ba8d9e99596bdfc8276a2840e6bfd52f6ea1f3a7618452e14bb8fc908f82084a06b7159184ba75c6ba7785bafd1a541c335274db48418c72661ab1b248ee30076a00ae332520383587d2a5d9ed45cd934b4bf2625d7ac2bca2479210adca1cef6bba038cd99d11c174ddb97c6b175b75da8ff95061a419a538a7a94e87851f9fb67c680", "0xf90211a0c8e37dd9aacf55947b7fad9a03195b05e02a1df6779bb809ef7cf7e3d4c0103aa0897aef3ff6c7db1f412e87f4bb98b2ffede1f23a17ea63d4201c2913482e92e4a05c089263749e659ef8068fd3cc8461b703400b70b4fb3e0e685196a04e242fcea0491b802967c46150575fc36766a8e3cfa5da0be00fef5b6b39d870d2dbf88408a0466d8527d6613510f1a06864a409451d54c07f4031cbaea18c5383b2e9924842a03b701a4ce3fb065eb2ddff36ba4f1bb2ca997aa86cbbb493af4201588ff317b5a03eb54ed71abe6a3ebca0b5ff956ea05814029ffc85ff624ccd4e2b3120aa5f52a0aea074cbeff6c6e2790aa70ca280c8c9c832e8c8c3ed7e0a3ded48a5bb75ea71a0d2a7453c0a56fa6b8a10e4eeb9abbe833fb0e6bf3c8f3ac059f769b4b23482b6a097b7af459384cf53aa499f741de7f52404207523eae2bb73cd7b5f72528c0edea0c9e0c7fe11eff50eb7aa794961900935fdb98af0b0c493a7174b78e201d2bf5ca0c0e5edccaf22e3207c5e4a4880989da92491245ff314ad1b513cc75316f00249a0b97e872b44f81b37c93f7b964e6cdb9a0e3391988901f8ad4587ee5e5ceafcaca0ea9bca9ce0e48044f5635b17f1d05e2a24cd4d79bdb93812c5ef4d1b82e8d653a056542cc749f058326a35351fb036eb6973e4b1df6a5e3907af05209ed087bf38a0823dfc5b36a11e2fedf7dbb763206f378b71d84a4c9c16428e96b3b6f5de431380", "0xf90211a035202c8e4a28b2b360e50c812412dd1c81be0b25e15de11af8d167be083a4605a038c7bdae901b4c9bb75e1568174827e2f4690b4daa996a3bbacdcfac7651cd2aa030df4afb38e44e9a3e26f53fffbd00e7c373097580a14a348c3eb3bbdea4cebea09445e3d5c72e5149bc03d5ea7391957451f45711890d31c8dfad2d0605230fd7a09e48c03b090f5bbd304e3995fef75e774a50786f1acef63aac1e2993d55ed2e9a02fb3f9660c33b686484fef1a4cde7d717c48dd8f62b7138e96fdbec57fc2aee0a0af720636864c9adeee8cd0fbaedf2a8a60a5a1cf3749e20b0a469d4306312f4da078cb02067b8c7c2c562073a61632c4df75142d127afe4b7373211a213afbeb6fa0bbfde4af6bb830609dd172a293e036082f507f867fcb1d287587e4a02592b2a2a02e32951295e013e536d5f7c15875e34cfa1e1f3fa43a2b36876475e1f61e142aa0c8eb4b24d2eb20183a5c30d433f5d8993751706edbad7d1867c5d964af52cb97a046d6bd9e5246d10c34b36f28c0fbfa4be81e1815ae8dbbd7abd78df11d29c753a09cd2d9c811fddb32ba31902d9d2e091a970602fb2ed827648b75195a9cae8b1ca07e1c080ad7292e95afd4a4063f8411e3533ad8375885582acb4c8039175149cca0aa317a285400917f6b1e24234c7e8fdf64e6e326667664e61e8dce872789d6faa08f78ad2f58b27881381993c033749cd065944a586304fe8118695ccd2bb9ee9b80", "0xf90211a01b1aec789d90bc913f8162c53c9fcda2f9aa45c0999f7ade0ec0985c420a1c56a0f601cbd4213e4e81878cef66305380f706b5bc24facdc0a5ab97cca8c295a884a02e0a5612bf79363ff21157063c7115b56c3ef00edf68299c16f0673db6dcbaffa0ad2ed3f0757259cd6fe64ded5f0538d57576089a36d3015a60337d85c9de93e9a0dd5d143dfdc4e735b4fa6844972f3d946b641a2d28ef9584b8cd66188503d609a02b0ccc1c5bfa79bc88bd38aa33ea32561d2e94f71c7bae372a1aace745ac192da0fda4979a04e4b360620cc6678a27dfc448c8d51a564eefb9192837c4292906bea0ee0174a1d4c6492c970603958016b9ff003d7c10fca26b7f9ddd7622c89e5752a06eed4d9df81427c83112c65c1a2e11ca12d10b9db22501586027d643cbccb989a074eb0e051454404d7e36b84ebe3ad27dfcf04506cc59f5c3611c80afbf5d73fba03bc1e213b78ea2b1db4d2a23acade8e495796468a3a06bd82eecc32c478b4339a0950122838f7ccda0ee77748fa5bc747c795bc850e89e6837bd3b539479d386c2a0ea8aa9d7c3445a7c9367992b598c210b6a05cee4acc9042c78ff296ac6324f0aa0a8713d37d7b216a20ace107f1612af4d2477b3e2c02b3a27dc2fbc487db6cc5da012c9c4863f0fa618268de346b96671b7e1568d9a41295926d1bbe1a3552dfa86a026309e68d1a4c82bbae891ba689f3488be71b5f2dc8d466961697554668a031880", "0xf901d1a001cc3f0f8a6587690418355cc0d2bbb55e170221d16e12c0a538910d0a7356b7a072fc6e97a7914c63f79d5df4f052cee94227d5157d2ddf7e96a2d27a3fc643f380a09c6ed7ac3cbc93989e35c1a42227a08cebbd58306a7bde31d58c4601586f44c9a07151e5b77fbfc418a2e2ff920f2962925eb904166f1202a113d776bfe2a38dada0ff0e369b099c8d6b2946c219bd62364ff34df4f0846538fc3878df42f10fdc2aa01433c88065c239a26ff1152bfb21f00af7a7e0fa1dddb35494f2046ee9de8de3a0c0d7e1cbaa2b6e4e68797fd09a2e5173e08318a9e81a3050bb907f7a70e6000ca02727ef0d8ddc604f2f2a7c49f8a863a3ac42e06bc7e3ba5cfbd19f26efc3b03880a08ad2c34e584fc786f6bf59f3289cac4d684fb43029cb8a46d092471fac0b081da09cfaeb918831b0520bdf84a3a7451b926619b349646301b6e04fdfd22f618099a0957bcfb32c9f02d2eb0a599fb8f013f7fd08a93aa7a4a487713492ac245590aca0699fdb4f7820aaa430710adb626d3360e80a4e46e7c17cb2b85c3d0711c3896da083685a27a548ace44319b5e9a7d8ad0cc2655ee0c6b22b9a1cc9a803d7b70efda03ab16da1d2f81bc872aff69c2bb774313a74c4f8b0e5caecf9d142a8f0dfeef680", "0xf85180a06abbc406fba9a3fd5cea3732a250df379f9db45935e49ccf27c0d31c3c5ef497808080a0ad26dc113c2968bb2853036f34132fc00e95bb58e4940491234b8aea779670268080808080808080808080", "0xf86f9d347a234d6dc902b4ea893294e5494f9f61dbca75ab4834c2c747aa29d4b84ff84d80898cc8f68890288a3bf6a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"], + "address": "0xc626553e7c821d0f8308c28d56c60e3c15f8d55a", + "balance": "0x8cc8f68890288a3bf6", + "codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": "0x0", + "storageHash": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "storageProof": [] + } \ No newline at end of file