UNPKG

deth

Version:

Ethereum node focused on Developer Experience

142 lines (141 loc) 5.83 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const primitives_1 = require("./primitives"); const model_1 = require("./model"); const TestVM_1 = require("./vm/TestVM"); const TestChainOptions_1 = require("./TestChainOptions"); const errors_1 = require("./errors"); const stepsLoggers_1 = require("./debugger/stepsLoggers"); const SnapshotObject_1 = require("./vm/storage/SnapshotObject"); const lodash_1 = require("lodash"); const ethereumjs_tx_1 = require("ethereumjs-tx"); /** * TestChain wraps TestVM and provides an API suitable for use by a provider. * It is separate from the provider so that there can be many provider instances * using the same TestChain instance. */ class TestChain { constructor(logger, options) { this.logger = logger; this.options = new SnapshotObject_1.SnapshotObject(TestChainOptions_1.getTestChainOptionsWithDefaults(options), lodash_1.cloneDeep); this.tvm = new TestVM_1.TestVM(this.options.value); } async init() { await this.tvm.init(); this.tvm.installStepHook(stepsLoggers_1.eventLogger(this.logger)); this.tvm.installStepHook(stepsLoggers_1.revertLogger(this.logger)); } makeSnapshot() { this.options.makeSnapshot(); return this.tvm.makeSnapshot(); } revertToSnapshot(id) { this.options.revert(id); return this.tvm.revertToSnapshot(id); } skewClock(delta) { this.options.value.clockSkew += delta; } startAutoMining() { this.options.value.autoMining = true; } stopAutoMining() { this.options.value.autoMining = false; } async mineBlock() { return this.tvm.mineBlock(this.options.value.clockSkew); } async getBlockNumber() { return this.tvm.getBlockNumber(); } getGasPrice() { return primitives_1.bnToQuantity(this.options.value.defaultGasPrice); } async getBalance(address, blockTag) { if (blockTag !== 'latest') { throw errors_1.unsupportedBlockTag('getBalance', blockTag, ['latest']); } return this.tvm.getBalance(address); } async getTransactionCount(address, blockTag) { if (blockTag !== 'latest' && blockTag !== 'pending') { throw errors_1.unsupportedBlockTag('getTransactionCount', blockTag, ['latest', 'pending']); } // TODO: handle pending better return this.tvm.getNonce(address); } async getCode(address, blockTag) { if (blockTag !== 'latest') { throw errors_1.unsupportedBlockTag('getCode', blockTag, ['latest']); } return this.tvm.getCode(address); } async getStorageAt(address, position, blockTag) { // @TODO: always assumes blockTag === latest return primitives_1.bufferToHexData(this.tvm.state.value.stateManger.getContractStorage(address, position)); } async sendTransaction(signedTransaction) { this.logger.logTransaction(this.parseTx(signedTransaction)); const hash = await this.tvm.addPendingTransaction(signedTransaction); if (this.options.value.autoMining) { await this.tvm.mineBlock(this.options.value.clockSkew); } return hash; } async call(transactionRequest, blockTag) { if (blockTag !== 'latest') { throw errors_1.unsupportedBlockTag('call', blockTag, ['latest']); } const tx = model_1.toFakeTransaction({ ...transactionRequest, gas: primitives_1.bnToQuantity(this.options.value.blockGasLimit), }); const result = await this.tvm.runIsolatedTransaction(tx, this.options.value.clockSkew); // TODO: handle errors return primitives_1.bufferToHexData(result.execResult.returnValue); } // @NOTE: this is very simplified implementation async estimateGas(transactionRequest) { if (!transactionRequest.gas) { transactionRequest.gas = primitives_1.bnToQuantity(this.options.value.blockGasLimit); } const tx = model_1.toFakeTransaction(transactionRequest); const result = await this.tvm.runIsolatedTransaction(tx, this.options.value.clockSkew); // TODO: handle errors return primitives_1.bnToQuantity(result.gasUsed); } async getBlock(blockTagOrHash, includeTransactions) { if (blockTagOrHash === 'pending') { throw errors_1.unsupportedBlockTag('call', blockTagOrHash); } const block = blockTagOrHash === 'latest' ? await this.tvm.getLatestBlock() : await this.tvm.getBlock(blockTagOrHash); if (!includeTransactions) { return block; } const transactions = block.transactions.map(tx => this.getTransaction(tx)); return { ...block, transactions }; } getTransaction(transactionHash) { const transaction = this.tvm.getTransaction(transactionHash); if (!transaction) { throw errors_1.transactionNotFound(transactionHash); } return transaction; } getTransactionReceipt(transactionHash) { return this.tvm.getTransactionReceipt(transactionHash); } async getLogs(filter) { throw errors_1.unsupportedOperation('getLogs'); } parseTx(signedTransaction) { var _a, _b; const tx = new ethereumjs_tx_1.Transaction(signedTransaction, { common: this.tvm.vm._common }); return { to: ((_a = tx.to) === null || _a === void 0 ? void 0 : _a.length) > 0 ? primitives_1.bufferToAddress(tx.to) : undefined, from: primitives_1.bufferToAddress(tx.from), data: ((_b = tx.data) === null || _b === void 0 ? void 0 : _b.length) > 0 ? primitives_1.bufferToHexData(tx.data) : undefined, }; } } exports.TestChain = TestChain;