From c2b8dada91c90788dcd81a0318c52a66b4b769d1 Mon Sep 17 00:00:00 2001 From: Sergey Ukustov Date: Fri, 29 Sep 2017 19:24:08 +0300 Subject: Add eth_signTypedData handler --- app/scripts/lib/typed-message-manager.js | 108 +++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 app/scripts/lib/typed-message-manager.js (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/typed-message-manager.js b/app/scripts/lib/typed-message-manager.js new file mode 100644 index 000000000..e3efdb45d --- /dev/null +++ b/app/scripts/lib/typed-message-manager.js @@ -0,0 +1,108 @@ +const EventEmitter = require('events') +const ObservableStore = require('obs-store') +const createId = require('./random-id') + + +module.exports = class TypedMessageManager extends EventEmitter { + constructor (opts) { + super() + this.memStore = new ObservableStore({ + unapprovedTypedMessages: {}, + unapprovedTypedMessagesCount: 0, + }) + this.messages = [] + } + + get unapprovedTypedMessagesCount () { + return Object.keys(this.getUnapprovedMsgs()).length + } + + getUnapprovedMsgs () { + return this.messages.filter(msg => msg.status === 'unapproved') + .reduce((result, msg) => { result[msg.id] = msg; return result }, {}) + } + + addUnapprovedMessage (msgParams) { + log.debug(`TypedMessageManager addUnapprovedMessage: ${JSON.stringify(msgParams)}`) + // create txData obj with parameters and meta data + var time = (new Date()).getTime() + var msgId = createId() + var msgData = { + id: msgId, + msgParams: msgParams, + time: time, + status: 'unapproved', + type: 'eth_signTypedData', + } + this.addMsg(msgData) + + // signal update + this.emit('update') + return msgId + } + + addMsg (msg) { + this.messages.push(msg) + this._saveMsgList() + } + + getMsg (msgId) { + return this.messages.find(msg => msg.id === msgId) + } + + approveMessage (msgParams) { + this.setMsgStatusApproved(msgParams.metamaskId) + return this.prepMsgForSigning(msgParams) + } + + setMsgStatusApproved (msgId) { + this._setMsgStatus(msgId, 'approved') + } + + setMsgStatusSigned (msgId, rawSig) { + const msg = this.getMsg(msgId) + msg.rawSig = rawSig + this._updateMsg(msg) + this._setMsgStatus(msgId, 'signed') + } + + prepMsgForSigning (msgParams) { + delete msgParams.metamaskId + return Promise.resolve(msgParams) + } + + rejectMsg (msgId) { + this._setMsgStatus(msgId, 'rejected') + } + + // + // PRIVATE METHODS + // + + _setMsgStatus (msgId, status) { + const msg = this.getMsg(msgId) + if (!msg) throw new Error('TypedMessageManager - Message not found for id: "${msgId}".') + msg.status = status + this._updateMsg(msg) + this.emit(`${msgId}:${status}`, msg) + if (status === 'rejected' || status === 'signed') { + this.emit(`${msgId}:finished`, msg) + } + } + + _updateMsg (msg) { + const index = this.messages.findIndex((message) => message.id === msg.id) + if (index !== -1) { + this.messages[index] = msg + } + this._saveMsgList() + } + + _saveMsgList () { + const unapprovedTypedMessages = this.getUnapprovedMsgs() + const unapprovedTypedMessagesCount = Object.keys(unapprovedTypedMessages).length + this.memStore.updateState({ unapprovedTypedMessages, unapprovedTypedMessagesCount }) + this.emit('updateBadge') + } + +} -- cgit From ec9c5283135d2cc3dcb633f19dd89ea79267f34a Mon Sep 17 00:00:00 2001 From: frankiebee Date: Thu, 5 Oct 2017 10:54:28 -0700 Subject: pending-tx - check time stamp instead of block number for resubmit --- app/scripts/lib/pending-tx-tracker.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/pending-tx-tracker.js b/app/scripts/lib/pending-tx-tracker.js index 6f1601586..3463d45bf 100644 --- a/app/scripts/lib/pending-tx-tracker.js +++ b/app/scripts/lib/pending-tx-tracker.js @@ -22,7 +22,8 @@ module.exports = class PendingTransactionTracker extends EventEmitter { super() this.query = new EthQuery(config.provider) this.nonceTracker = config.nonceTracker - this.retryLimit = config.retryLimit || Infinity + // default is one day + this.retryTimePeriod = config.retryTimePeriod || 86400000 this.getPendingTransactions = config.getPendingTransactions this.publishTransaction = config.publishTransaction } @@ -99,8 +100,8 @@ module.exports = class PendingTransactionTracker extends EventEmitter { } async _resubmitTx (txMeta) { - if (txMeta.retryCount > this.retryLimit) { - const err = new Error(`Gave up submitting after ${this.retryLimit} blocks un-mined.`) + if (Date.now() > txMeta.time + this.retryTimePeriod) { + const err = new Error(`Gave up submitting after ${this.retryTimePeriod / 3.6e+6} hours.`) return this.emit('tx:failed', txMeta.id, err) } -- cgit From 3cb9da2ae56ce0b3162e64dbcf69f5f9e39ff4e8 Mon Sep 17 00:00:00 2001 From: frankiebee Date: Thu, 5 Oct 2017 11:42:01 -0700 Subject: "fix" hours for message --- app/scripts/lib/pending-tx-tracker.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/pending-tx-tracker.js b/app/scripts/lib/pending-tx-tracker.js index 3463d45bf..8a626e222 100644 --- a/app/scripts/lib/pending-tx-tracker.js +++ b/app/scripts/lib/pending-tx-tracker.js @@ -101,7 +101,8 @@ module.exports = class PendingTransactionTracker extends EventEmitter { async _resubmitTx (txMeta) { if (Date.now() > txMeta.time + this.retryTimePeriod) { - const err = new Error(`Gave up submitting after ${this.retryTimePeriod / 3.6e+6} hours.`) + const hours = (this.retryTimePeriod / 3.6e+6).toFixed(1) + const err = new Error(`Gave up submitting after ${hours} hours.`) return this.emit('tx:failed', txMeta.id, err) } -- cgit From 9bc80d998eda937e3a8f95fa5e04fcba66e8a6f8 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 5 Oct 2017 14:39:35 -0700 Subject: Add signTypedData input validations --- app/scripts/lib/typed-message-manager.js | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/typed-message-manager.js b/app/scripts/lib/typed-message-manager.js index e3efdb45d..e041ae9f3 100644 --- a/app/scripts/lib/typed-message-manager.js +++ b/app/scripts/lib/typed-message-manager.js @@ -1,6 +1,7 @@ const EventEmitter = require('events') const ObservableStore = require('obs-store') const createId = require('./random-id') +const assert = require('assert') module.exports = class TypedMessageManager extends EventEmitter { @@ -23,6 +24,8 @@ module.exports = class TypedMessageManager extends EventEmitter { } addUnapprovedMessage (msgParams) { + this.validateParams(msgParams) + log.debug(`TypedMessageManager addUnapprovedMessage: ${JSON.stringify(msgParams)}`) // create txData obj with parameters and meta data var time = (new Date()).getTime() @@ -41,6 +44,14 @@ module.exports = class TypedMessageManager extends EventEmitter { return msgId } + validateParams (params) { + assert.equal(typeof params, 'object', 'Params should ben an object.') + assert.ok('data' in params, 'Params must include a data field.') + assert.ok('from' in params, 'Params must include a from field.') + assert.ok(Array.isArray(params.data), 'Data should be an array.') + assert.equal(typeof params.from, 'string', 'From field must be a string.') + } + addMsg (msg) { this.messages.push(msg) this._saveMsgList() -- cgit From 0146b55d6d7bd8717b3f3ad071c64744e21a93fd Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 6 Oct 2017 11:33:14 -0700 Subject: Check status of pending transactions on startup Fixes #1531 --- app/scripts/lib/pending-tx-tracker.js | 1 + 1 file changed, 1 insertion(+) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/pending-tx-tracker.js b/app/scripts/lib/pending-tx-tracker.js index 8a626e222..5049cc4b4 100644 --- a/app/scripts/lib/pending-tx-tracker.js +++ b/app/scripts/lib/pending-tx-tracker.js @@ -26,6 +26,7 @@ module.exports = class PendingTransactionTracker extends EventEmitter { this.retryTimePeriod = config.retryTimePeriod || 86400000 this.getPendingTransactions = config.getPendingTransactions this.publishTransaction = config.publishTransaction + this._checkPendingTxs() } // checks if a signed tx is in a block and -- cgit From be4f7b33f4f0885f2c0f5f4d537f6e9793f3fa30 Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 6 Oct 2017 12:36:08 -0700 Subject: nodeify - allow callback to be optional --- app/scripts/lib/nodeify.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/nodeify.js b/app/scripts/lib/nodeify.js index 832d6c6d3..19c3c8337 100644 --- a/app/scripts/lib/nodeify.js +++ b/app/scripts/lib/nodeify.js @@ -1,10 +1,18 @@ const promiseToCallback = require('promise-to-callback') +const noop = function(){} module.exports = function nodeify (fn, context) { return function(){ const args = [].slice.call(arguments) - const callback = args.pop() - if (typeof callback !== 'function') throw new Error('callback is not a function') + const lastArg = args[args.length-1] + const lastArgIsCallback = typeof lastArg === 'function' + let callback + if (lastArgIsCallback) { + callback = lastArg + args.pop() + } else { + callback = noop + } promiseToCallback(fn.apply(context, args))(callback) } } -- cgit From 94513cae7bf3c8310ae6a248e12a9b7dd73e306f Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 6 Oct 2017 12:50:33 -0700 Subject: Provide method for tx tracker to refer to all txs --- app/scripts/lib/tx-state-manager.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/tx-state-manager.js b/app/scripts/lib/tx-state-manager.js index cf8117864..2250403f6 100644 --- a/app/scripts/lib/tx-state-manager.js +++ b/app/scripts/lib/tx-state-manager.js @@ -46,6 +46,12 @@ module.exports = class TransactionStateManger extends EventEmitter { return this.getFilteredTxList(opts) } + getConfirmedTransactions (address) { + const opts = { status: 'confirmed' } + if (address) opts.from = address + return this.getFilteredTxList(opts) + } + addTx (txMeta) { this.once(`${txMeta.id}:signed`, function (txId) { this.removeAllListeners(`${txMeta.id}:rejected`) @@ -242,4 +248,4 @@ module.exports = class TransactionStateManger extends EventEmitter { _saveTxList (transactions) { this.store.updateState({ transactions }) } -} \ No newline at end of file +} -- cgit From a417fab0ebd71d22f51a8e30590c259b32164fd2 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 6 Oct 2017 12:51:13 -0700 Subject: When checking pending txs, check for successful txs with same nonce. If a successful tx with the same nonce exists, transition tx to the failed state. Fixes #2294 --- app/scripts/lib/pending-tx-tracker.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/pending-tx-tracker.js b/app/scripts/lib/pending-tx-tracker.js index 8a626e222..2d8f22ae8 100644 --- a/app/scripts/lib/pending-tx-tracker.js +++ b/app/scripts/lib/pending-tx-tracker.js @@ -25,6 +25,7 @@ module.exports = class PendingTransactionTracker extends EventEmitter { // default is one day this.retryTimePeriod = config.retryTimePeriod || 86400000 this.getPendingTransactions = config.getPendingTransactions + this.getCompletedTransactions = config.getCompletedTransactions this.publishTransaction = config.publishTransaction } @@ -120,6 +121,7 @@ module.exports = class PendingTransactionTracker extends EventEmitter { async _checkPendingTx (txMeta) { const txHash = txMeta.hash const txId = txMeta.id + // extra check in case there was an uncaught error during the // signature and submission process if (!txHash) { @@ -128,6 +130,15 @@ module.exports = class PendingTransactionTracker extends EventEmitter { this.emit('tx:failed', txId, noTxHashErr) return } + + // If another tx with the same nonce is mined, set as failed. + const taken = await this._checkIfNonceIsTaken(txMeta) + if (taken) { + const nonceTakenErr = new Error('Another transaction with this nonce has been mined.') + nonceTakenErr.name = 'NonceTakenErr' + return this.emit('tx:failed', txId, nonceTakenErr) + } + // get latest transaction status let txParams try { @@ -159,4 +170,13 @@ module.exports = class PendingTransactionTracker extends EventEmitter { } nonceGlobalLock.releaseLock() } + + async _checkIfNonceIsTaken (txMeta) { + const completed = this.getCompletedTransactions() + const sameNonce = completed.filter((otherMeta) => { + return otherMeta.txParams.nonce === txMeta.txParams.nonce + }) + return sameNonce.length > 0 + } + } -- cgit From bc396a7417ecfe9855ec84af0cb08fd033c42bf5 Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 6 Oct 2017 13:02:34 -0700 Subject: lint fix - nodeify --- app/scripts/lib/nodeify.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/nodeify.js b/app/scripts/lib/nodeify.js index 19c3c8337..d24e92206 100644 --- a/app/scripts/lib/nodeify.js +++ b/app/scripts/lib/nodeify.js @@ -4,7 +4,7 @@ const noop = function(){} module.exports = function nodeify (fn, context) { return function(){ const args = [].slice.call(arguments) - const lastArg = args[args.length-1] + const lastArg = args[args.length - 1] const lastArgIsCallback = typeof lastArg === 'function' let callback if (lastArgIsCallback) { -- cgit From a1696f89a8764f17c10298a45160abf8fc7dce5e Mon Sep 17 00:00:00 2001 From: Sergey Ukustov Date: Sat, 7 Oct 2017 00:38:13 +0300 Subject: Validate data format for eth_signTypedData --- app/scripts/lib/typed-message-manager.js | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/typed-message-manager.js b/app/scripts/lib/typed-message-manager.js index e041ae9f3..8b760790e 100644 --- a/app/scripts/lib/typed-message-manager.js +++ b/app/scripts/lib/typed-message-manager.js @@ -2,6 +2,7 @@ const EventEmitter = require('events') const ObservableStore = require('obs-store') const createId = require('./random-id') const assert = require('assert') +const sigUtil = require('eth-sig-util') module.exports = class TypedMessageManager extends EventEmitter { @@ -50,6 +51,9 @@ module.exports = class TypedMessageManager extends EventEmitter { assert.ok('from' in params, 'Params must include a from field.') assert.ok(Array.isArray(params.data), 'Data should be an array.') assert.equal(typeof params.from, 'string', 'From field must be a string.') + assert.doesNotThrow(() => { + sigUtil.typedSignatureHash(params.data) + }, 'Expected EIP712 typed data') } addMsg (msg) { -- cgit From e32d75965f848f8b26868b6476265e61b791c768 Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 10 Oct 2017 17:15:14 -0700 Subject: events-proxy - clean up --- app/scripts/lib/events-proxy.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/events-proxy.js b/app/scripts/lib/events-proxy.js index d1199a278..840b06b1a 100644 --- a/app/scripts/lib/events-proxy.js +++ b/app/scripts/lib/events-proxy.js @@ -1,6 +1,5 @@ -module.exports = function createEventEmitterProxy(eventEmitter, listeners) { +module.exports = function createEventEmitterProxy(eventEmitter, eventHandlers = {}) { let target = eventEmitter - const eventHandlers = listeners || {} const proxy = new Proxy({}, { get: (obj, name) => { // intercept listeners @@ -14,9 +13,12 @@ module.exports = function createEventEmitterProxy(eventEmitter, listeners) { return true }, }) + proxy.setTarget(eventEmitter) + return proxy + function setTarget (eventEmitter) { target = eventEmitter - // migrate listeners + // migrate eventHandlers Object.keys(eventHandlers).forEach((name) => { eventHandlers[name].forEach((handler) => target.on(name, handler)) }) @@ -26,6 +28,4 @@ module.exports = function createEventEmitterProxy(eventEmitter, listeners) { eventHandlers[name].push(handler) target.on(name, handler) } - if (listeners) proxy.setTarget(eventEmitter) - return proxy } \ No newline at end of file -- cgit From 7d50a56198f2992e908bc97b871210ec2b52123a Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 10 Oct 2017 17:15:52 -0700 Subject: util - add obj-proxy --- app/scripts/lib/obj-proxy.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 app/scripts/lib/obj-proxy.js (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/obj-proxy.js b/app/scripts/lib/obj-proxy.js new file mode 100644 index 000000000..29ca1269f --- /dev/null +++ b/app/scripts/lib/obj-proxy.js @@ -0,0 +1,19 @@ +module.exports = function createObjectProxy(obj) { + let target = obj + const proxy = new Proxy({}, { + get: (obj, name) => { + // intercept setTarget + if (name === 'setTarget') return setTarget + return target[name] + }, + set: (obj, name, value) => { + target[name] = value + return true + }, + }) + return proxy + + function setTarget (obj) { + target = obj + } +} \ No newline at end of file -- cgit From dcf10f3d7559b428cc189fb4406580d816eae365 Mon Sep 17 00:00:00 2001 From: kumavis Date: Wed, 11 Oct 2017 18:33:02 -0700 Subject: nonce-tracker - use blockTracker directly --- app/scripts/lib/nonce-tracker.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js index 0029ac953..2af40a27f 100644 --- a/app/scripts/lib/nonce-tracker.js +++ b/app/scripts/lib/nonce-tracker.js @@ -4,8 +4,9 @@ const Mutex = require('await-semaphore').Mutex class NonceTracker { - constructor ({ provider, getPendingTransactions, getConfirmedTransactions }) { + constructor ({ provider, blockTracker, getPendingTransactions, getConfirmedTransactions }) { this.provider = provider + this.blockTracker = blockTracker this.ethQuery = new EthQuery(provider) this.getPendingTransactions = getPendingTransactions this.getConfirmedTransactions = getConfirmedTransactions @@ -53,7 +54,7 @@ class NonceTracker { } async _getCurrentBlock () { - const blockTracker = this._getBlockTracker() + const blockTracker = this.blockTracker const currentBlock = blockTracker.getCurrentBlock() if (currentBlock) return currentBlock return await Promise((reject, resolve) => { @@ -139,11 +140,6 @@ class NonceTracker { return { name: 'local', nonce: highest, details: { startPoint, highest } } } - // this is a hotfix for the fact that the blockTracker will - // change when the network changes - _getBlockTracker () { - return this.provider._blockTracker - } } module.exports = NonceTracker -- cgit