UNPKG

deth

Version:

Ethereum node focused on Developer Experience

113 lines (112 loc) 4.61 kB
import { BN, toBuffer } from 'ethereumjs-util'; import { Transaction } from 'ethereumjs-tx'; import { toBlockResponse } from '../model'; import { bufferToHash, bufferToQuantity, bufferToHexData } from '../primitives'; import { initializeVM } from './initializeVM'; import { getLatestBlock } from './getLatestBlock'; import { putBlock } from './putBlock'; import { runIsolatedTransaction } from './runIsolatedTransaction'; import { DethStateManger } from './storage/DethStateManger'; import { DethBlockchain } from './storage/DethBlockchain'; // eslint-disable-next-line import PStateManager from 'ethereumts-vm/dist/state/promisified'; import { BlockchainAdapter } from './storage/BlockchainAdapter'; import { StateManagerAdapter } from './storage/StateManagerAdapter'; import { SnapshotObject } from './storage/SnapshotObject'; /** * TestVM is a wrapper around ethereumts-vm (our fork). It provides a promise-based * interface and abstracts away weird ethereumjs specific details */ export class TestVM { constructor(options) { this.options = options; this.pendingTransactions = []; this.transactions = new Map(); this.receipts = new Map(); this.snapshots = []; this.state = new SnapshotObject({ stateManger: new DethStateManger(), blockchain: new DethBlockchain(), }, (t) => ({ blockchain: t.blockchain.copy(), stateManger: t.stateManger.copy(), })); } async init() { this.vm = await initializeVM(this.options, this.state.value.stateManger, this.state.value.blockchain); } // @todo this requires better typings (whole step hooks mechanism) installStepHook(hook) { this.vm.on('step', hook); } makeSnapshot() { return this.state.makeSnapshot(); } revertToSnapshot(id) { this.state.revert(id); this.hotswapStateStorageForVm(); } // change internals of VM to point to new blockchain and state machine hotswapStateStorageForVm() { const blockchainAdapter = new BlockchainAdapter(this.state.value.blockchain); this.vm.blockchain = blockchainAdapter; const stateManagerAdapter = new StateManagerAdapter(this.state.value.stateManger); this.vm.stateManager = stateManagerAdapter; this.vm.pStateManager = new PStateManager(stateManagerAdapter); } async getBlockNumber() { const block = await getLatestBlock(this.vm); return bufferToQuantity(block.header.number); } async getLatestBlock() { const block = await getLatestBlock(this.vm); return toBlockResponse(block); } async addPendingTransaction(signedTransaction) { const transaction = new Transaction(signedTransaction, { common: this.vm._common }); this.pendingTransactions.push(transaction); return bufferToHash(transaction.hash()); } async mineBlock(clockSkew) { const transactions = this.pendingTransactions; this.pendingTransactions = []; const { receipts, responses } = await putBlock(this.vm, transactions, this.options, clockSkew); for (const receipt of receipts) { this.receipts.set(receipt.transactionHash, receipt); } for (const response of responses) { this.transactions.set(response.hash, response); } } getTransaction(hash) { return this.transactions.get(hash); } getTransactionReceipt(hash) { return this.receipts.get(hash); } async getNonce(address) { const account = await this.getAccount(address); return bufferToQuantity(account.nonce); } async getBalance(address) { const account = await this.getAccount(address); return bufferToQuantity(account.balance); } async getAccount(address) { return this.state.value.stateManger.getAccount(address); } async getCode(address) { const code = this.state.value.stateManger.getContractCode(address); return bufferToHexData(code); } async runIsolatedTransaction(transaction, clockSkew) { return runIsolatedTransaction(this.vm, transaction, this.options, clockSkew); } async getBlock(hashOrNumber) { const query = hashOrNumber.length === 66 ? toBuffer(hashOrNumber) : new BN(hashOrNumber.substr(2), 'hex'); const block = await new Promise((resolve, reject) => { this.vm.blockchain.getBlock(query, (err, block) => (err != null ? reject(err) : resolve(block))); }); return toBlockResponse(block); } }