deth
Version:
Ethereum node focused on Developer Experience
113 lines (112 loc) • 4.61 kB
JavaScript
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);
}
}