mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
transaction controller - use nonce-tracker
This commit is contained in:
parent
dab2fccc78
commit
b3492d9c17
@ -4,9 +4,10 @@ const extend = require('xtend')
|
|||||||
const Semaphore = require('semaphore')
|
const Semaphore = require('semaphore')
|
||||||
const ObservableStore = require('obs-store')
|
const ObservableStore = require('obs-store')
|
||||||
const ethUtil = require('ethereumjs-util')
|
const ethUtil = require('ethereumjs-util')
|
||||||
|
const denodeify = require('denodeify')
|
||||||
const TxProviderUtil = require('../lib/tx-utils')
|
const TxProviderUtil = require('../lib/tx-utils')
|
||||||
const createId = require('../lib/random-id')
|
const createId = require('../lib/random-id')
|
||||||
const denodeify = require('denodeify')
|
const NonceTracker = require('../lib/nonce-tracker')
|
||||||
|
|
||||||
const RETRY_LIMIT = 200
|
const RETRY_LIMIT = 200
|
||||||
|
|
||||||
@ -22,6 +23,11 @@ module.exports = class TransactionController extends EventEmitter {
|
|||||||
this.txHistoryLimit = opts.txHistoryLimit
|
this.txHistoryLimit = opts.txHistoryLimit
|
||||||
this.provider = opts.provider
|
this.provider = opts.provider
|
||||||
this.blockTracker = opts.blockTracker
|
this.blockTracker = opts.blockTracker
|
||||||
|
this.nonceTracker = new NonceTracker({
|
||||||
|
provider: this.provider,
|
||||||
|
blockTracker: this.blockTracker,
|
||||||
|
getPendingTransactions: (address) => this.getFilteredTxList({ from: address, status: 'submitted' }),
|
||||||
|
})
|
||||||
this.query = opts.ethQuery
|
this.query = opts.ethQuery
|
||||||
this.txProviderUtils = new TxProviderUtil(this.query)
|
this.txProviderUtils = new TxProviderUtil(this.query)
|
||||||
this.blockTracker.on('block', this.checkForTxInBlock.bind(this))
|
this.blockTracker.on('block', this.checkForTxInBlock.bind(this))
|
||||||
@ -169,29 +175,58 @@ module.exports = class TransactionController extends EventEmitter {
|
|||||||
}, {})
|
}, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
approveTransaction (txId, cb = warn) {
|
// approveTransaction (txId, cb = warn) {
|
||||||
const self = this
|
// promiseToCallback((async () => {
|
||||||
|
// // approve
|
||||||
|
// self.setTxStatusApproved(txId)
|
||||||
|
// // get next nonce
|
||||||
|
// const txMeta = this.getTx(txId)
|
||||||
|
// const fromAddress = txMeta.txParams.from
|
||||||
|
// const { nextNonce, releaseLock } = await this.nonceTracker.getNonceLock(fromAddress)
|
||||||
|
// txMeta.txParams.nonce = nonce
|
||||||
|
// this.updateTx(txMeta)
|
||||||
|
// // sign transaction
|
||||||
|
// const rawTx = await denodeify(self.signTransaction.bind(self))(txId)
|
||||||
|
// await denodeify(self.publishTransaction.bind(self))(txId, rawTx)
|
||||||
|
// })())((err) => {
|
||||||
|
// if (err) {
|
||||||
|
// this.setTxStatusFailed(txId, {
|
||||||
|
// errCode: err.errCode || err,
|
||||||
|
// message: err.message || 'Transaction failed during approval',
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// // must set transaction to submitted/failed before releasing lock
|
||||||
|
// releaseLock()
|
||||||
|
// cb(err)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
async approveTransaction (txId) {
|
||||||
|
let nonceLock
|
||||||
|
try {
|
||||||
// approve
|
// approve
|
||||||
self.setTxStatusApproved(txId)
|
this.setTxStatusApproved(txId)
|
||||||
// only allow one tx at a time for atomic nonce usage
|
// get next nonce
|
||||||
self.nonceLock.take(() => {
|
const txMeta = this.getTx(txId)
|
||||||
// begin signature process
|
const fromAddress = txMeta.txParams.from
|
||||||
async.waterfall([
|
nonceLock = await this.nonceTracker.getNonceLock(fromAddress)
|
||||||
(cb) => self.fillInTxParams(txId, cb),
|
txMeta.txParams.nonce = nonceLock.nextNonce
|
||||||
(cb) => self.signTransaction(txId, cb),
|
this.updateTx(txMeta)
|
||||||
(rawTx, cb) => self.publishTransaction(txId, rawTx, cb),
|
// sign transaction
|
||||||
], (err) => {
|
const rawTx = await denodeify(this.signTransaction.bind(this))(txId)
|
||||||
self.nonceLock.leave()
|
await denodeify(this.publishTransaction.bind(this))(txId, rawTx)
|
||||||
if (err) {
|
// must set transaction to submitted/failed before releasing lock
|
||||||
|
nonceLock.releaseLock()
|
||||||
|
} catch (err) {
|
||||||
this.setTxStatusFailed(txId, {
|
this.setTxStatusFailed(txId, {
|
||||||
errCode: err.errCode || err,
|
errCode: err.errCode || err,
|
||||||
message: err.message || 'Transaction failed during approval',
|
message: err.message || 'Transaction failed during approval',
|
||||||
})
|
})
|
||||||
return cb(err)
|
// must set transaction to submitted/failed before releasing lock
|
||||||
|
if (nonceLock) nonceLock.releaseLock()
|
||||||
|
// continue with error chain
|
||||||
|
throw err
|
||||||
}
|
}
|
||||||
cb()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelTransaction (txId, cb = warn) {
|
cancelTransaction (txId, cb = warn) {
|
||||||
@ -199,15 +234,6 @@ module.exports = class TransactionController extends EventEmitter {
|
|||||||
cb()
|
cb()
|
||||||
}
|
}
|
||||||
|
|
||||||
fillInTxParams (txId, cb) {
|
|
||||||
const txMeta = this.getTx(txId)
|
|
||||||
this.txProviderUtils.fillInTxParams(txMeta.txParams, (err) => {
|
|
||||||
if (err) return cb(err)
|
|
||||||
this.updateTx(txMeta)
|
|
||||||
cb()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
getChainId () {
|
getChainId () {
|
||||||
const networkState = this.networkStore.getState()
|
const networkState = this.networkStore.getState()
|
||||||
const getChainId = parseInt(networkState)
|
const getChainId = parseInt(networkState)
|
||||||
|
@ -290,7 +290,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
exportAccount: nodeify(keyringController.exportAccount).bind(keyringController),
|
exportAccount: nodeify(keyringController.exportAccount).bind(keyringController),
|
||||||
|
|
||||||
// txController
|
// txController
|
||||||
approveTransaction: txController.approveTransaction.bind(txController),
|
approveTransaction: nodeify(txController.approveTransaction).bind(txController),
|
||||||
cancelTransaction: txController.cancelTransaction.bind(txController),
|
cancelTransaction: txController.cancelTransaction.bind(txController),
|
||||||
updateAndApproveTransaction: this.updateAndApproveTx.bind(this),
|
updateAndApproveTransaction: this.updateAndApproveTx.bind(this),
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
const assert = require('assert')
|
const assert = require('assert')
|
||||||
const EventEmitter = require('events')
|
|
||||||
const ethUtil = require('ethereumjs-util')
|
const ethUtil = require('ethereumjs-util')
|
||||||
const EthTx = require('ethereumjs-tx')
|
const EthTx = require('ethereumjs-tx')
|
||||||
const EthQuery = require('eth-query')
|
const EthQuery = require('eth-query')
|
||||||
@ -19,13 +18,15 @@ describe('Transaction Controller', function () {
|
|||||||
txController = new TransactionController({
|
txController = new TransactionController({
|
||||||
networkStore: new ObservableStore(currentNetworkId),
|
networkStore: new ObservableStore(currentNetworkId),
|
||||||
txHistoryLimit: 10,
|
txHistoryLimit: 10,
|
||||||
blockTracker: new EventEmitter(),
|
blockTracker: { getCurrentBlock: noop, on: noop },
|
||||||
ethQuery: new EthQuery(new EventEmitter()),
|
provider: { sendAsync: noop },
|
||||||
|
ethQuery: new EthQuery({ sendAsync: noop }),
|
||||||
signTransaction: (ethTx) => new Promise((resolve) => {
|
signTransaction: (ethTx) => new Promise((resolve) => {
|
||||||
ethTx.sign(privKey)
|
ethTx.sign(privKey)
|
||||||
resolve()
|
resolve()
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
txController.nonceTracker.getNonceLock = () => Promise.resolve({ nextNonce: 0, releaseLock: noop })
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('#validateTxParams', function () {
|
describe('#validateTxParams', function () {
|
||||||
|
Loading…
Reference in New Issue
Block a user