diff options
author | Brandon Millman <brandon.millman@gmail.com> | 2018-02-07 07:36:00 +0800 |
---|---|---|
committer | Brandon Millman <brandon.millman@gmail.com> | 2018-02-07 07:36:00 +0800 |
commit | 36a49966eb4d5b78192cb5fa76c98755ef092f8b (patch) | |
tree | 7100b22aa1c4ac824bd054fefdae53e9df0aab9a /packages/subproviders/src | |
parent | f818d06b43ae9279c4ee022685e3bcb9f27ba2fd (diff) | |
parent | 548246c05f128d23d701d9b738ce53deaf783f63 (diff) | |
download | dexon-sol-tools-36a49966eb4d5b78192cb5fa76c98755ef092f8b.tar dexon-sol-tools-36a49966eb4d5b78192cb5fa76c98755ef092f8b.tar.gz dexon-sol-tools-36a49966eb4d5b78192cb5fa76c98755ef092f8b.tar.bz2 dexon-sol-tools-36a49966eb4d5b78192cb5fa76c98755ef092f8b.tar.lz dexon-sol-tools-36a49966eb4d5b78192cb5fa76c98755ef092f8b.tar.xz dexon-sol-tools-36a49966eb4d5b78192cb5fa76c98755ef092f8b.tar.zst dexon-sol-tools-36a49966eb4d5b78192cb5fa76c98755ef092f8b.zip |
Merge branch 'development' into feature/testnet-faucets/order-dispenser
* development:
Attribute the origins of NonceTracker
Move BlockParamLiteral to shared types package
Fixes
Move BlockParam and BlockParamLiteral to shared types
Rename called to something more readable
Newline prettier/lint
Yarn.lock
Refactor tests for reuse of the fixture subprovider
Remove re-fetch of transaction count on error
Disable linter for multiple class declarations
Remove double declaration
Enable CIRCLECI and declare web3
Test faucets to use new NonceTracker
Update changelog
Readability and prettier
Prettify
Nonce tracker subprovider Caches the nonce when a request to getTransactionCount is made and increments the pending nonce after successful transactions
Diffstat (limited to 'packages/subproviders/src')
-rw-r--r-- | packages/subproviders/src/globals.d.ts | 20 | ||||
-rw-r--r-- | packages/subproviders/src/index.ts | 3 | ||||
-rw-r--r-- | packages/subproviders/src/subproviders/ledger.ts | 2 | ||||
-rw-r--r-- | packages/subproviders/src/subproviders/nonce_tracker.ts | 103 | ||||
-rw-r--r-- | packages/subproviders/src/subproviders/redundant_rpc.ts | 1 | ||||
-rw-r--r-- | packages/subproviders/src/types.ts | 8 |
6 files changed, 136 insertions, 1 deletions
diff --git a/packages/subproviders/src/globals.d.ts b/packages/subproviders/src/globals.d.ts index 53457fa24..6f344dcd3 100644 --- a/packages/subproviders/src/globals.d.ts +++ b/packages/subproviders/src/globals.d.ts @@ -13,7 +13,9 @@ declare module 'ethereumjs-tx' { public r: Buffer; public s: Buffer; public v: Buffer; + public nonce: Buffer; public serialize(): Buffer; + public getSenderAddress(): Buffer; constructor(txParams: any); } export = EthereumTx; @@ -97,6 +99,24 @@ declare module 'web3-provider-engine' { } export = Web3ProviderEngine; } +declare module 'web3-provider-engine/util/rpc-cache-utils' { + class ProviderEngineRpcUtils { + public static blockTagForPayload(payload: any): string | null; + } + export = ProviderEngineRpcUtils; +} +declare module 'web3-provider-engine/subproviders/fixture' { + import * as Web3 from 'web3'; + class FixtureSubprovider { + constructor(staticResponses: any); + public handleRequest( + payload: Web3.JSONRPCRequestPayload, + next: () => void, + end: (err: Error | null, data?: any) => void, + ): void; + } + export = FixtureSubprovider; +} // hdkey declarations declare module 'hdkey' { diff --git a/packages/subproviders/src/index.ts b/packages/subproviders/src/index.ts index 720c4362f..67d52ee25 100644 --- a/packages/subproviders/src/index.ts +++ b/packages/subproviders/src/index.ts @@ -9,7 +9,8 @@ import { LedgerEthereumClient } from './types'; export { InjectedWeb3Subprovider } from './subproviders/injected_web3'; export { RedundantRPCSubprovider } from './subproviders/redundant_rpc'; export { LedgerSubprovider } from './subproviders/ledger'; -export { ECSignature, LedgerWalletSubprovider, LedgerCommunicationClient } from './types'; +export { NonceTrackerSubprovider } from './subproviders/nonce_tracker'; +export { ECSignature, LedgerWalletSubprovider, LedgerCommunicationClient, NonceSubproviderErrors } from './types'; /** * A factory method for creating a LedgerEthereumClient usable in a browser context. diff --git a/packages/subproviders/src/subproviders/ledger.ts b/packages/subproviders/src/subproviders/ledger.ts index 7267a793e..5966a88bb 100644 --- a/packages/subproviders/src/subproviders/ledger.ts +++ b/packages/subproviders/src/subproviders/ledger.ts @@ -60,6 +60,8 @@ export class LedgerSubprovider extends Subprovider { public setPathIndex(pathIndex: number) { this._derivationPathIndex = pathIndex; } + // Required to implement this public interface which doesn't conform to our linting rule. + // tslint:disable-next-line:async-suffix public async handleRequest( payload: Web3.JSONRPCRequestPayload, next: () => void, diff --git a/packages/subproviders/src/subproviders/nonce_tracker.ts b/packages/subproviders/src/subproviders/nonce_tracker.ts new file mode 100644 index 000000000..d967d40f2 --- /dev/null +++ b/packages/subproviders/src/subproviders/nonce_tracker.ts @@ -0,0 +1,103 @@ +import * as _ from 'lodash'; + +import EthereumTx = require('ethereumjs-tx'); +import ethUtil = require('ethereumjs-util'); +import providerEngineUtils = require('web3-provider-engine/util/rpc-cache-utils'); + +import { BlockParamLiteral } from '@0xproject/types'; + +import { ErrorCallback, JSONRPCPayload, NonceSubproviderErrors, OptionalNextCallback } from '../types'; + +import { Subprovider } from './subprovider'; + +// We do not export this since this is not our error, and we do not throw this error +const NONCE_TOO_LOW_ERROR_MESSAGE = 'Transaction nonce is too low'; +/* + This class is heavily inspiried by the Web3ProviderEngine NonceSubprovider + We have added the additional feature of clearing any nonce balues when an error message + describes a nonce value being too low. +*/ +export class NonceTrackerSubprovider extends Subprovider { + private _nonceCache: { [address: string]: string } = {}; + private static _reconstructTransaction(payload: JSONRPCPayload): EthereumTx { + const raw = payload.params[0]; + if (_.isUndefined(raw)) { + throw new Error(NonceSubproviderErrors.EmptyParametersFound); + } + const rawData = ethUtil.toBuffer(raw); + const transaction = new EthereumTx(rawData); + return transaction; + } + private static _determineAddress(payload: JSONRPCPayload): string { + let address: string; + switch (payload.method) { + case 'eth_getTransactionCount': + address = payload.params[0].toLowerCase(); + return address; + case 'eth_sendRawTransaction': + const transaction = NonceTrackerSubprovider._reconstructTransaction(payload); + const addressRaw = transaction + .getSenderAddress() + .toString('hex') + .toLowerCase(); + address = `0x${addressRaw}`; + return address; + default: + throw new Error(NonceSubproviderErrors.CannotDetermineAddressFromPayload); + } + } + // Required to implement this public interface which doesn't conform to our linting rule. + // tslint:disable-next-line:async-suffix + public async handleRequest(payload: JSONRPCPayload, next: OptionalNextCallback, end: ErrorCallback): Promise<void> { + switch (payload.method) { + case 'eth_getTransactionCount': + const requestDefaultBlock = providerEngineUtils.blockTagForPayload(payload); + if (requestDefaultBlock === BlockParamLiteral.Pending) { + const address = NonceTrackerSubprovider._determineAddress(payload); + const cachedResult = this._nonceCache[address]; + if (!_.isUndefined(cachedResult)) { + return end(null, cachedResult); + } else { + return next((requestError: Error | null, requestResult: any, cb: any) => { + if (_.isNull(requestError)) { + this._nonceCache[address] = requestResult as string; + } + cb(); + }); + } + } else { + return next(); + } + case 'eth_sendRawTransaction': + return next((sendTransactionError: Error | null, txResult: any, cb: any) => { + if (_.isNull(sendTransactionError)) { + this._handleSuccessfulTransaction(payload); + } else { + this._handleSendTransactionError(payload, sendTransactionError); + } + cb(); + }); + default: + return next(); + } + } + private _handleSuccessfulTransaction(payload: JSONRPCPayload): void { + const address = NonceTrackerSubprovider._determineAddress(payload); + const transaction = NonceTrackerSubprovider._reconstructTransaction(payload); + // Increment the nonce from the previous successfully submitted transaction + let nonce = ethUtil.bufferToInt(transaction.nonce); + nonce++; + let nextHexNonce = nonce.toString(16); + if (nextHexNonce.length % 2) { + nextHexNonce = `0${nextHexNonce}`; + } + const nextPrefixedHexNonce = `0x${nextHexNonce}`; + this._nonceCache[address] = nextPrefixedHexNonce; + } + private _handleSendTransactionError(payload: JSONRPCPayload, err: Error): void { + const address = NonceTrackerSubprovider._determineAddress(payload); + if (this._nonceCache[address] && _.includes(err.message, NONCE_TOO_LOW_ERROR_MESSAGE)) { + delete this._nonceCache[address]; + } + } +} diff --git a/packages/subproviders/src/subproviders/redundant_rpc.ts b/packages/subproviders/src/subproviders/redundant_rpc.ts index a3cb463a8..5a94f93d7 100644 --- a/packages/subproviders/src/subproviders/redundant_rpc.ts +++ b/packages/subproviders/src/subproviders/redundant_rpc.ts @@ -35,6 +35,7 @@ export class RedundantRPCSubprovider extends Subprovider { }); }); } + // Required to implement this public interface which doesn't conform to our linting rule. // tslint:disable-next-line:async-suffix public async handleRequest( payload: JSONRPCPayload, diff --git a/packages/subproviders/src/types.ts b/packages/subproviders/src/types.ts index 3db8be943..65b7f6c8f 100644 --- a/packages/subproviders/src/types.ts +++ b/packages/subproviders/src/types.ts @@ -112,3 +112,11 @@ export enum LedgerSubproviderErrors { SenderInvalidOrNotSupplied = 'SENDER_INVALID_OR_NOT_SUPPLIED', MultipleOpenConnectionsDisallowed = 'MULTIPLE_OPEN_CONNECTIONS_DISALLOWED', } + +export enum NonceSubproviderErrors { + EmptyParametersFound = 'EMPTY_PARAMETERS_FOUND', + CannotDetermineAddressFromPayload = 'CANNOT_DETERMINE_ADDRESS_FROM_PAYLOAD', +} + +export type OptionalNextCallback = (callback?: (err: Error | null, result: any, cb: any) => void) => void; +export type ErrorCallback = (err: Error | null, data?: any) => void; |