From 139e66932d38808bd0115ac0416979189dcf1b89 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Thu, 17 Nov 2016 14:05:11 +0100 Subject: [PATCH] Add trace_{call, rawTransaction, replayTransaction} --- js/src/api/format/input.js | 8 +++ js/src/api/format/input.spec.js | 14 ++++- js/src/api/format/output.js | 22 ++++++++ js/src/api/rpc/trace/trace.e2e.js | 24 +++++--- js/src/api/rpc/trace/trace.js | 40 ++++++++++---- js/src/jsonrpc/interfaces/trace.js | 88 +++++++++++++++++++++++++----- 6 files changed, 162 insertions(+), 34 deletions(-) diff --git a/js/src/api/format/input.js b/js/src/api/format/input.js index 830ca0e21..4cd1c8a56 100644 --- a/js/src/api/format/input.js +++ b/js/src/api/format/input.js @@ -166,3 +166,11 @@ export function inTraceFilter (filterObject) { return filterObject; } + +export function inTraceType (whatTrace) { + if (isString(whatTrace)) { + return [whatTrace]; + } + + return whatTrace; +} diff --git a/js/src/api/format/input.spec.js b/js/src/api/format/input.spec.js index 219886d05..a22c8d131 100644 --- a/js/src/api/format/input.spec.js +++ b/js/src/api/format/input.spec.js @@ -16,7 +16,7 @@ import BigNumber from 'bignumber.js'; -import { inAddress, inBlockNumber, inData, inFilter, inHex, inNumber10, inNumber16, inOptions } from './input'; +import { inAddress, inBlockNumber, inData, inFilter, inHex, inNumber10, inNumber16, inOptions, inTraceType } from './input'; import { isAddress } from '../../../test/types'; describe('api/format/input', () => { @@ -242,4 +242,16 @@ describe('api/format/input', () => { }); }); }); + + describe('inTraceType', () => { + it('returns array of types as is', () => { + const types = ['vmTrace', 'trace', 'stateDiff']; + expect(inTraceType(types)).to.deep.equal(types); + }); + + it('formats single string type into array', () => { + const type = 'vmTrace'; + expect(inTraceType(type)).to.deep.equal([type]); + }); + }); }); diff --git a/js/src/api/format/output.js b/js/src/api/format/output.js index 8461df20f..262a275a0 100644 --- a/js/src/api/format/output.js +++ b/js/src/api/format/output.js @@ -254,3 +254,25 @@ export function outTrace (trace) { return trace; } + +export function outTraces (traces) { + if (traces) { + return traces.map(outTrace); + } + + return traces; +} + +export function outTraceReplay (trace) { + if (trace) { + Object.keys(trace).forEach((key) => { + switch (key) { + case 'trace': + trace[key] = outTraces(trace[key]); + break; + } + }); + } + + return trace; +} diff --git a/js/src/api/rpc/trace/trace.e2e.js b/js/src/api/rpc/trace/trace.e2e.js index 1a0720927..8da243cd9 100644 --- a/js/src/api/rpc/trace/trace.e2e.js +++ b/js/src/api/rpc/trace/trace.e2e.js @@ -16,19 +16,29 @@ import { createHttpApi } from '../../../../test/e2e/ethapi'; -describe('ethapi.trace', () => { +describe.only('ethapi.trace', () => { const ethapi = createHttpApi(); describe('block', () => { - it('returns the latest block', () => { - return ethapi.trace.block().then((block) => { - expect(block).to.be.ok; + it('returns the latest block traces', () => { + return ethapi.trace.block().then((traces) => { + expect(traces).to.be.ok; }); }); - it('returns a specified block', () => { - return ethapi.trace.block('0x65432').then((block) => { - expect(block).to.be.ok; + it('returns traces for a specified block', () => { + return ethapi.trace.block('0x65432').then((traces) => { + expect(traces).to.be.ok; + }); + }); + }); + + describe('replayTransaction', () => { + it('returns traces for a specific transaction', () => { + return ethapi.eth.getBlockByNumber().then((latestBlock) => { + return ethapi.trace.replayTransaction(latestBlock.transactions[0]).then((traces) => { + expect(traces).to.be.ok; + }); }); }); }); diff --git a/js/src/api/rpc/trace/trace.js b/js/src/api/rpc/trace/trace.js index 95fed4230..5c693c0b5 100644 --- a/js/src/api/rpc/trace/trace.js +++ b/js/src/api/rpc/trace/trace.js @@ -14,35 +14,53 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import { inBlockNumber, inHex, inNumber16, inTraceFilter } from '../../format/input'; -import { outTrace } from '../../format/output'; +import { inBlockNumber, inData, inHex, inNumber16, inOptions, inTraceFilter, inTraceType } from '../../format/input'; +import { outTraces, outTraceReplay } from '../../format/output'; export default class Trace { constructor (transport) { this._transport = transport; } + block (blockNumber = 'latest') { + return this._transport + .execute('trace_block', inBlockNumber(blockNumber)) + .then(outTraces); + } + + call (options, blockNumber = 'latest', whatTrace = ['trace']) { + return this._transport + .execute('trace_call', inOptions(options), inBlockNumber(blockNumber), inTraceType(whatTrace)) + .then(outTraceReplay); + } + filter (filterObj) { return this._transport .execute('trace_filter', inTraceFilter(filterObj)) - .then(traces => traces.map(trace => outTrace(trace))); + .then(outTraces); } get (txHash, position) { return this._transport .execute('trace_get', inHex(txHash), inNumber16(position)) - .then(trace => outTrace(trace)); + .then(outTraces); + } + + rawTransaction (data, whatTrace = ['trace']) { + return this._transport + .execute('trace_rawTransaction', inData(data), inTraceType(whatTrace)) + .then(outTraceReplay); + } + + replayTransaction (txHash, whatTrace = ['trace']) { + return this._transport + .execute('trace_replayTransaction', txHash, inTraceType(whatTrace)) + .then(outTraceReplay); } transaction (txHash) { return this._transport .execute('trace_transaction', inHex(txHash)) - .then(traces => traces.map(trace => outTrace(trace))); - } - - block (blockNumber = 'latest') { - return this._transport - .execute('trace_block', inBlockNumber(blockNumber)) - .then(traces => traces.map(trace => outTrace(trace))); + .then(outTraces); } } diff --git a/js/src/jsonrpc/interfaces/trace.js b/js/src/jsonrpc/interfaces/trace.js index 3dc4451f0..efe45f34e 100644 --- a/js/src/jsonrpc/interfaces/trace.js +++ b/js/src/jsonrpc/interfaces/trace.js @@ -14,9 +14,45 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import { BlockNumber, Hash, Integer } from '../types'; +import { BlockNumber, Data, Hash, Integer } from '../types'; export default { + block: { + desc: 'Returns traces created at given block', + params: [ + { + type: BlockNumber, + desc: 'Integer block number, or \'latest\' for the last mined block or \'pending\', \'earliest\' for not yet mined transactions' + } + ], + returns: { + type: Array, + desc: 'Block traces' + } + }, + + call: { + desc: 'Returns traces for a specific call', + params: [ + { + type: Object, + desc: 'Call options' + }, + { + type: BlockNumber, + desc: 'The blockNumber' + }, + { + type: Array, + desc: 'Type of trace, one or more of \'vmTrace\', \'trace\' and/or \'stateDiff\'' + } + ], + returns: { + type: Array, + desc: 'Block traces' + } + }, + filter: { desc: 'Returns traces matching given filter', params: [ @@ -49,6 +85,42 @@ export default { } }, + rawTransaction: { + desc: 'Traces a call to eth_sendRawTransaction without making the call, returning the traces', + params: [ + { + type: Data, + desc: 'Transaction data' + }, + { + type: Array, + desc: 'Type of trace, one or more of \'vmTrace\', \'trace\' and/or \'stateDiff\'' + } + ], + returns: { + type: Array, + desc: 'Block traces' + } + }, + + replayTransaction: { + desc: 'Replays a transaction, returning the traces', + params: [ + { + type: Hash, + desc: 'Transaction hash' + }, + { + type: Array, + desc: 'Type of trace, one or more of \'vmTrace\', \'trace\' and/or \'stateDiff\'' + } + ], + returns: { + type: Array, + desc: 'Block traces' + } + }, + transaction: { desc: 'Returns all traces of given transaction', params: [ @@ -61,19 +133,5 @@ export default { type: Array, desc: 'Traces of given transaction' } - }, - - block: { - desc: 'Returns traces created at given block', - params: [ - { - type: BlockNumber, - desc: 'Integer block number, or \'latest\' for the last mined block or \'pending\', \'earliest\' for not yet mined transactions' - } - ], - returns: { - type: Array, - desc: 'Block traces' - } } };