From 9432a8446891e5e71be925687e0ab552df176f03 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Sun, 10 Nov 2019 20:53:55 -0800 Subject: [PATCH 1/3] Upgrade @types/ethereumjs-tx and add DebugSubprovider --- packages/subproviders/package.json | 2 +- packages/subproviders/src/index.ts | 6 ++ .../src/subproviders/debug_subprovider.ts | 70 +++++++++++++++ .../test/unit/debug_subprovider_test.ts | 87 +++++++++++++++++++ .../subproviders/test/utils/fixture_data.ts | 22 +++++ yarn.lock | 7 +- 6 files changed, 190 insertions(+), 4 deletions(-) create mode 100644 packages/subproviders/src/subproviders/debug_subprovider.ts create mode 100644 packages/subproviders/test/unit/debug_subprovider_test.ts diff --git a/packages/subproviders/package.json b/packages/subproviders/package.json index 4806314b3c..0d4b54f568 100644 --- a/packages/subproviders/package.json +++ b/packages/subproviders/package.json @@ -59,7 +59,7 @@ "@0x/tslint-config": "^3.1.0-beta.1", "@types/bip39": "^2.4.0", "@types/bn.js": "^4.11.0", - "@types/ethereumjs-tx": "^1.0.0", + "@types/ethereumjs-tx": "^1.0.1", "@types/hdkey": "^0.7.0", "@types/lodash": "4.14.104", "@types/mocha": "^5.2.7", diff --git a/packages/subproviders/src/index.ts b/packages/subproviders/src/index.ts index 5b5c8b0847..6d720e3f9d 100644 --- a/packages/subproviders/src/index.ts +++ b/packages/subproviders/src/index.ts @@ -24,6 +24,12 @@ export { LedgerSubprovider } from './subproviders/ledger'; export { RPCSubprovider } from './subproviders/rpc_subprovider'; export { GanacheSubprovider } from './subproviders/ganache'; export { Subprovider } from './subproviders/subprovider'; +export { + DebugPayloadRawTransactionAttributes, + DebugPayload, + DebugSubprovider, + WithDebugPayload, +} from './subproviders/debug_subprovider'; export { NonceTrackerSubprovider } from './subproviders/nonce_tracker'; export { PrivateKeyWalletSubprovider } from './subproviders/private_key_wallet'; export { MnemonicWalletSubprovider } from './subproviders/mnemonic_wallet'; diff --git a/packages/subproviders/src/subproviders/debug_subprovider.ts b/packages/subproviders/src/subproviders/debug_subprovider.ts new file mode 100644 index 0000000000..a516c5f8ae --- /dev/null +++ b/packages/subproviders/src/subproviders/debug_subprovider.ts @@ -0,0 +1,70 @@ +import { BigNumber } from '@0x/utils'; +import { JSONRPCRequestPayload } from 'ethereum-types'; +import EthereumTx = require('ethereumjs-tx'); + +import { Callback, ErrorCallback } from '../types'; + +import { Subprovider } from './subprovider'; + +const HEX_BASE = 16; + +export interface DebugPayloadRawTransactionAttributes { + gasPrice: BigNumber; + gasLimit: BigNumber; + nonce: BigNumber; + value: BigNumber; + to: string; +} +export interface DebugPayload extends JSONRPCRequestPayload { + rawTransactionAttributes?: DebugPayloadRawTransactionAttributes; +} + +export type WithDebugPayload = (debugPayload: DebugPayload) => void; +// tslint:disable-next-line:no-console +const defaultDebugCallback = (debugPayload: DebugPayload) => console.debug(debugPayload); + +/** + * This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine) subprovider interface. + * For every request, a object for debugging will be sent to the function specified in the constructor + * Useful for debugging RPC requests which are not expecting as you expect. + */ +export class DebugSubprovider extends Subprovider { + private readonly _debugCallback: WithDebugPayload; + + private static _generateRawTransactionAttributes(txn: EthereumTx): DebugPayloadRawTransactionAttributes { + const hexBufferToBN = (value: Buffer) => new BigNumber(value.toString('hex'), HEX_BASE); + + return { + gasLimit: hexBufferToBN(txn.gasLimit), + gasPrice: hexBufferToBN(txn.gasPrice), + nonce: hexBufferToBN(txn.nonce), + value: hexBufferToBN(txn.value), + to: `0x${txn.to.toString('hex')}`, + }; + } + + public constructor(debugCallback: WithDebugPayload = defaultDebugCallback) { + super(); + this._debugCallback = debugCallback; + } + + /** + * This method conforms to the web3-provider-engine interface. + * It is called internally by the ProviderEngine when it is this subproviders + * turn to handle a JSON RPC request. + * @param payload JSON RPC payload + * @param next Callback to call if this subprovider decides not to handle the request + * @param end Callback to call if subprovider handled the request and wants to pass back the request. + */ + // tslint:disable-next-line:prefer-function-over-method async-suffix + public async handleRequest(payload: JSONRPCRequestPayload, next: Callback, end: ErrorCallback): Promise { + const debugPayload: DebugPayload = payload; + if (payload.method === 'eth_sendRawTransaction' && payload.params[0]) { + const txn = new EthereumTx(payload.params[0]); + debugPayload.rawTransactionAttributes = DebugSubprovider._generateRawTransactionAttributes(txn); + } + this._debugCallback(debugPayload); + + next(); + } +} diff --git a/packages/subproviders/test/unit/debug_subprovider_test.ts b/packages/subproviders/test/unit/debug_subprovider_test.ts new file mode 100644 index 0000000000..67948b0484 --- /dev/null +++ b/packages/subproviders/test/unit/debug_subprovider_test.ts @@ -0,0 +1,87 @@ +import * as chai from 'chai'; + +import { chaiSetup } from '../chai_setup'; +import { fixtureData } from '../utils/fixture_data'; + +import { DebugPayload, DebugSubprovider } from './../../src/subproviders/debug_subprovider'; + +chaiSetup.configure(); +const expect = chai.expect; + +const blankCallback = () => { + return; +}; + +describe('DebugSubprovider', () => { + describe('sends debug message to callback', async () => { + let sentDebugData: DebugPayload | undefined; + const debugCallback = (curDebugData: DebugPayload) => { + sentDebugData = curDebugData; + return; + }; + before(() => { + sentDebugData = undefined; + }); + it('for ERC20 transfer', async () => { + const fixtureRpcPayload = fixtureData.ERC20_TRANSFER_RPC_PAYLOAD; + const debugSubprovider = new DebugSubprovider(debugCallback); + await debugSubprovider.handleRequest(fixtureRpcPayload, blankCallback, blankCallback); + + if (!sentDebugData) { + fail('No debug data sent'); + } else { + expect(sentDebugData.id).to.eql(fixtureRpcPayload.id); + expect(sentDebugData.jsonrpc).to.eql(fixtureRpcPayload.jsonrpc); + expect(sentDebugData.params).to.eql(fixtureRpcPayload.params); + expect(sentDebugData.method).to.eql(fixtureRpcPayload.method); + + const rawTxnAttrs = sentDebugData.rawTransactionAttributes; + if (!rawTxnAttrs) { + fail('No rawTransactionAttributes'); + } else { + expect(rawTxnAttrs.gasLimit.toString()).to.eql('37428'); + expect(rawTxnAttrs.gasPrice.toString()).to.eql('1000000000'); + expect(rawTxnAttrs.nonce.toString()).to.eql('32'); + expect(rawTxnAttrs.value.toString()).to.eql('0'); + expect(rawTxnAttrs.to.toString()).to.eql('0x2002d3812f58e35f0ea1ffbf80a75a38c32175fa'); + } + } + }); + it('for eth_blockNumber command', async () => { + const fixtureRpcPayload = fixtureData.ETH_GETBLOCK_RPC_PAYLOAD; + const debugSubprovider = new DebugSubprovider(debugCallback); + await debugSubprovider.handleRequest(fixtureRpcPayload, blankCallback, blankCallback); + + if (!sentDebugData) { + fail('No debug data sent'); + } else { + expect(sentDebugData).to.eql(fixtureRpcPayload); + } + }); + it('for regular ETH transfer', async () => { + const fixtureRpcPayload = fixtureData.ETH_TRANSFER_PAYLOAD; + const debugSubprovider = new DebugSubprovider(debugCallback); + await debugSubprovider.handleRequest(fixtureRpcPayload, blankCallback, blankCallback); + + if (!sentDebugData) { + fail('No debug data sent'); + } else { + expect(sentDebugData.id).to.eql(fixtureRpcPayload.id); + expect(sentDebugData.jsonrpc).to.eql(fixtureRpcPayload.jsonrpc); + expect(sentDebugData.params).to.eql(fixtureRpcPayload.params); + expect(sentDebugData.method).to.eql(fixtureRpcPayload.method); + + const rawTxnAttrs = sentDebugData.rawTransactionAttributes; + if (!rawTxnAttrs) { + fail('No rawTransactionAttributes'); + } else { + expect(rawTxnAttrs.gasLimit.toString()).to.eql('21000'); + expect(rawTxnAttrs.gasPrice.toString()).to.eql('8000000000'); + expect(rawTxnAttrs.nonce.toString()).to.eql('38'); + expect(rawTxnAttrs.value.toString()).to.eql('410000000000000'); + expect(rawTxnAttrs.to.toString()).to.eql('0x8a333a18b924554d6e83ef9e9944de6260f61d3b'); + } + } + }); + }); +}); diff --git a/packages/subproviders/test/utils/fixture_data.ts b/packages/subproviders/test/utils/fixture_data.ts index d23fb73a77..7a8d907e3d 100644 --- a/packages/subproviders/test/utils/fixture_data.ts +++ b/packages/subproviders/test/utils/fixture_data.ts @@ -63,4 +63,26 @@ export const fixtureData = { EIP712_TEST_TYPED_DATA_HASH: '0xb460d69ca60383293877cd765c0f97bd832d66bca720f7e32222ce1118832493', EIP712_TEST_TYPED_DATA_SIGNED_RESULT: '0x20af5b6bfc3658942198d6eeda159b4ed589f90cee6eac3ba117818ffba5fd7e354a353aad93faabd6eb6c66e17921c92bd1cd09c92a770f554470dc3e254ce701', + ERC20_TRANSFER_RPC_PAYLOAD: { + id: 1573248819933307, + jsonrpc: '2.0', + params: [ + '0xf8a820843b9aca00829234942002d3812f58e35f0ea1ffbf80a75a38c32175fa80b844a9059cbb0000000000000000000000008a333a18b924554d6e83ef9e9944de6260f61d3b00000000000000000000000000000000000000000000000000005af3107a40001ba0aef7ea75bfc9c8fd6ecd9572e78de6aabfe856a69658ce259a64cffd5b31ac22a0386d4669313a21a59e27d629810fc4ab4e1ff08eb7c20f5fa4f533a23fd5533f', + ], + method: 'eth_sendRawTransaction', + }, + ETH_TRANSFER_PAYLOAD: { + id: 1573451366422343, + jsonrpc: '2.0', + params: [ + '0xf86b268501dcd65000825208948a333a18b924554d6e83ef9e9944de6260f61d3b870174e4905ba000801ba0b71c9f67a42b53288cbf8d73741e8d189e79031c00f0e029f6501057fdb71affa035f306598dbc3f1f60db8ca0a4fe0d2e189c4caead7c6179da512e6abc481cbb', + ], + method: 'eth_sendRawTransaction', + }, + ETH_GETBLOCK_RPC_PAYLOAD: { + id: 1, + params: [], + jsonrpc: '2.0', + method: 'eth_blockNumber', + }, }; diff --git a/yarn.lock b/yarn.lock index 44eab8d016..a8bf0c1082 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2157,10 +2157,11 @@ dependencies: bignumber.js "7.2.1" -"@types/ethereumjs-tx@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/ethereumjs-tx/-/ethereumjs-tx-1.0.0.tgz#ae2ce90a145cc1a6849656db38baf0e793ff7927" +"@types/ethereumjs-tx@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/ethereumjs-tx/-/ethereumjs-tx-1.0.1.tgz#31a46b858a51ec4395959b2ae37f5064a8688fbd" dependencies: + "@types/bn.js" "*" "@types/node" "*" "@types/events@*": From 657c35fb8685dd0c186816234d7794bfeb6ccc3f Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Sun, 10 Nov 2019 22:05:32 -0800 Subject: [PATCH 2/3] Adds CHANGELOG --- packages/subproviders/CHANGELOG.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/subproviders/CHANGELOG.json b/packages/subproviders/CHANGELOG.json index 74c7de5b14..6d4e234332 100644 --- a/packages/subproviders/CHANGELOG.json +++ b/packages/subproviders/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "5.1.0-beta.2", + "changes": [ + { + "note": "Adds DebugSubprovider", + "pr": 2329 + } + ] + }, { "version": "5.1.0-beta.1", "changes": [ From 8d423be22320d113550ea3874813cccb6065c140 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Tue, 12 Nov 2019 11:30:24 -0800 Subject: [PATCH 3/3] Use strings instead of bignumbers, and add JSON.stringify call --- .../src/subproviders/debug_subprovider.ts | 20 +++++++++---------- .../test/unit/debug_subprovider_test.ts | 20 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/subproviders/src/subproviders/debug_subprovider.ts b/packages/subproviders/src/subproviders/debug_subprovider.ts index a516c5f8ae..332bcd81a8 100644 --- a/packages/subproviders/src/subproviders/debug_subprovider.ts +++ b/packages/subproviders/src/subproviders/debug_subprovider.ts @@ -9,10 +9,10 @@ import { Subprovider } from './subprovider'; const HEX_BASE = 16; export interface DebugPayloadRawTransactionAttributes { - gasPrice: BigNumber; - gasLimit: BigNumber; - nonce: BigNumber; - value: BigNumber; + gasPrice: string; + gasLimit: string; + nonce: string; + value: string; to: string; } export interface DebugPayload extends JSONRPCRequestPayload { @@ -21,7 +21,7 @@ export interface DebugPayload extends JSONRPCRequestPayload { export type WithDebugPayload = (debugPayload: DebugPayload) => void; // tslint:disable-next-line:no-console -const defaultDebugCallback = (debugPayload: DebugPayload) => console.debug(debugPayload); +const defaultDebugCallback = (debugPayload: DebugPayload) => console.debug(JSON.stringify(debugPayload, null, 2)); /** * This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine) subprovider interface. @@ -32,13 +32,13 @@ export class DebugSubprovider extends Subprovider { private readonly _debugCallback: WithDebugPayload; private static _generateRawTransactionAttributes(txn: EthereumTx): DebugPayloadRawTransactionAttributes { - const hexBufferToBN = (value: Buffer) => new BigNumber(value.toString('hex'), HEX_BASE); + const hexBufferToString = (value: Buffer): string => new BigNumber(value.toString('hex'), HEX_BASE).toString(); return { - gasLimit: hexBufferToBN(txn.gasLimit), - gasPrice: hexBufferToBN(txn.gasPrice), - nonce: hexBufferToBN(txn.nonce), - value: hexBufferToBN(txn.value), + gasLimit: hexBufferToString(txn.gasLimit), + gasPrice: hexBufferToString(txn.gasPrice), + nonce: hexBufferToString(txn.nonce), + value: hexBufferToString(txn.value), to: `0x${txn.to.toString('hex')}`, }; } diff --git a/packages/subproviders/test/unit/debug_subprovider_test.ts b/packages/subproviders/test/unit/debug_subprovider_test.ts index 67948b0484..1c2e178639 100644 --- a/packages/subproviders/test/unit/debug_subprovider_test.ts +++ b/packages/subproviders/test/unit/debug_subprovider_test.ts @@ -39,11 +39,11 @@ describe('DebugSubprovider', () => { if (!rawTxnAttrs) { fail('No rawTransactionAttributes'); } else { - expect(rawTxnAttrs.gasLimit.toString()).to.eql('37428'); - expect(rawTxnAttrs.gasPrice.toString()).to.eql('1000000000'); - expect(rawTxnAttrs.nonce.toString()).to.eql('32'); - expect(rawTxnAttrs.value.toString()).to.eql('0'); - expect(rawTxnAttrs.to.toString()).to.eql('0x2002d3812f58e35f0ea1ffbf80a75a38c32175fa'); + expect(rawTxnAttrs.gasLimit).to.eql('37428'); + expect(rawTxnAttrs.gasPrice).to.eql('1000000000'); + expect(rawTxnAttrs.nonce).to.eql('32'); + expect(rawTxnAttrs.value).to.eql('0'); + expect(rawTxnAttrs.to).to.eql('0x2002d3812f58e35f0ea1ffbf80a75a38c32175fa'); } } }); @@ -75,11 +75,11 @@ describe('DebugSubprovider', () => { if (!rawTxnAttrs) { fail('No rawTransactionAttributes'); } else { - expect(rawTxnAttrs.gasLimit.toString()).to.eql('21000'); - expect(rawTxnAttrs.gasPrice.toString()).to.eql('8000000000'); - expect(rawTxnAttrs.nonce.toString()).to.eql('38'); - expect(rawTxnAttrs.value.toString()).to.eql('410000000000000'); - expect(rawTxnAttrs.to.toString()).to.eql('0x8a333a18b924554d6e83ef9e9944de6260f61d3b'); + expect(rawTxnAttrs.gasLimit).to.eql('21000'); + expect(rawTxnAttrs.gasPrice).to.eql('8000000000'); + expect(rawTxnAttrs.nonce).to.eql('38'); + expect(rawTxnAttrs.value).to.eql('410000000000000'); + expect(rawTxnAttrs.to).to.eql('0x8a333a18b924554d6e83ef9e9944de6260f61d3b'); } } });