mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
introduce tx-state-history-helper and diff-based history
This commit is contained in:
parent
68c6b2d666
commit
fdffb6fedc
@ -8,6 +8,7 @@ const TxProviderUtil = require('../lib/tx-utils')
|
||||
const PendingTransactionTracker = require('../lib/pending-tx-tracker')
|
||||
const createId = require('../lib/random-id')
|
||||
const NonceTracker = require('../lib/nonce-tracker')
|
||||
const txStateHistoryHelper = require('../lib/tx-state-history-helper')
|
||||
|
||||
module.exports = class TransactionController extends EventEmitter {
|
||||
constructor (opts) {
|
||||
@ -128,19 +129,17 @@ module.exports = class TransactionController extends EventEmitter {
|
||||
|
||||
updateTx (txMeta) {
|
||||
// create txMeta snapshot for history
|
||||
const txMetaForHistory = clone(txMeta)
|
||||
// dont include previous history in this snapshot
|
||||
delete txMetaForHistory.history
|
||||
// add snapshot to tx history
|
||||
if (!txMeta.history) txMeta.history = []
|
||||
txMeta.history.push(txMetaForHistory)
|
||||
const currentState = txStateHistoryHelper.snapshotFromTxMeta(txMeta)
|
||||
// recover previous tx state obj
|
||||
const previousState = txStateHistoryHelper.replayHistory(txMeta.history)
|
||||
// generate history entry and add to history
|
||||
const entry = txStateHistoryHelper.generateHistoryEntry(previousState, currentState)
|
||||
txMeta.history.push(entry)
|
||||
|
||||
// commit txMeta to state
|
||||
const txId = txMeta.id
|
||||
const txList = this.getFullTxList()
|
||||
const index = txList.findIndex(txData => txData.id === txId)
|
||||
if (!txMeta.history) txMeta.history = []
|
||||
txMeta.history.push(txMetaForHistory)
|
||||
|
||||
txList[index] = txMeta
|
||||
this._saveTxList(txList)
|
||||
this.emit('update')
|
||||
@ -148,16 +147,22 @@ module.exports = class TransactionController extends EventEmitter {
|
||||
|
||||
// Adds a tx to the txlist
|
||||
addTx (txMeta) {
|
||||
const txCount = this.getTxCount()
|
||||
const network = this.getNetwork()
|
||||
const fullTxList = this.getFullTxList()
|
||||
const txHistoryLimit = this.txHistoryLimit
|
||||
// initialize history
|
||||
txMeta.history = []
|
||||
// capture initial snapshot of txMeta for history
|
||||
const snapshot = txStateHistoryHelper.snapshotFromTxMeta(txMeta)
|
||||
txMeta.history.push(snapshot)
|
||||
|
||||
// checks if the length of the tx history is
|
||||
// longer then desired persistence limit
|
||||
// and then if it is removes only confirmed
|
||||
// or rejected tx's.
|
||||
// not tx's that are pending or unapproved
|
||||
const txCount = this.getTxCount()
|
||||
const network = this.getNetwork()
|
||||
const fullTxList = this.getFullTxList()
|
||||
const txHistoryLimit = this.txHistoryLimit
|
||||
|
||||
if (txCount > txHistoryLimit - 1) {
|
||||
const index = fullTxList.findIndex((metaTx) => ((metaTx.status === 'confirmed' || metaTx.status === 'rejected') && network === txMeta.metamaskNetworkId))
|
||||
fullTxList.splice(index, 1)
|
||||
@ -206,7 +211,6 @@ module.exports = class TransactionController extends EventEmitter {
|
||||
status: 'unapproved',
|
||||
metamaskNetworkId: this.getNetwork(),
|
||||
txParams: txParams,
|
||||
history: [],
|
||||
}
|
||||
// add default tx params
|
||||
await this.addTxDefaults(txMeta)
|
||||
|
37
app/scripts/lib/tx-state-history-helper.js
Normal file
37
app/scripts/lib/tx-state-history-helper.js
Normal file
@ -0,0 +1,37 @@
|
||||
const jsonDiffer = require('fast-json-patch')
|
||||
const clone = require('clone')
|
||||
|
||||
module.exports = {
|
||||
generateHistoryEntry,
|
||||
replayHistory,
|
||||
snapshotFromTxMeta,
|
||||
migrateFromSnapshotsToDiffs,
|
||||
}
|
||||
|
||||
|
||||
function migrateFromSnapshotsToDiffs(longHistory) {
|
||||
return (
|
||||
longHistory
|
||||
// convert non-initial history entries into diffs
|
||||
.map((entry, index) => {
|
||||
if (index === 0) return entry
|
||||
return generateHistoryEntry(longHistory[index-1], entry)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
function generateHistoryEntry(previousState, newState) {
|
||||
return jsonDiffer.compare(previousState, newState)
|
||||
}
|
||||
|
||||
function replayHistory(shortHistory) {
|
||||
return shortHistory.reduce((val, entry) => jsonDiffer.applyPatch(val, entry).newDocument)
|
||||
}
|
||||
|
||||
function snapshotFromTxMeta(txMeta) {
|
||||
// create txMeta snapshot for history
|
||||
const snapshot = clone(txMeta)
|
||||
// dont include previous history in this snapshot
|
||||
delete snapshot.history
|
||||
return snapshot
|
||||
}
|
@ -82,6 +82,7 @@
|
||||
"express": "^4.14.0",
|
||||
"extension-link-enabler": "^1.0.0",
|
||||
"extensionizer": "^1.0.0",
|
||||
"fast-json-patch": "^2.0.4",
|
||||
"fast-levenshtein": "^2.0.6",
|
||||
"gulp": "github:gulpjs/gulp#4.0",
|
||||
"gulp-eslint": "^4.0.0",
|
||||
|
3053
test/data/v17-long-history.json
Normal file
3053
test/data/v17-long-history.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -47,7 +47,7 @@ describe('Transaction Controller', function () {
|
||||
metamaskNetworkId: currentNetworkId,
|
||||
txParams,
|
||||
}
|
||||
txController._saveTxList([txMeta])
|
||||
txController.addTx(txMeta)
|
||||
stub = sinon.stub(txController, 'addUnapprovedTransaction').returns(Promise.resolve(txMeta))
|
||||
})
|
||||
|
||||
@ -279,9 +279,12 @@ describe('Transaction Controller', function () {
|
||||
it('replaces the tx with the same id', function () {
|
||||
txController.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
|
||||
txController.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
|
||||
txController.updateTx({ id: '1', status: 'blah', hash: 'foo', metamaskNetworkId: currentNetworkId, txParams: {} })
|
||||
var result = txController.getTx('1')
|
||||
assert.equal(result.hash, 'foo')
|
||||
const tx1 = txController.getTx('1')
|
||||
tx1.status = 'blah'
|
||||
tx1.hash = 'foo'
|
||||
txController.updateTx(tx1)
|
||||
const savedResult = txController.getTx('1')
|
||||
assert.equal(savedResult.hash, 'foo')
|
||||
})
|
||||
|
||||
it('updates gas price', function () {
|
||||
@ -297,9 +300,9 @@ describe('Transaction Controller', function () {
|
||||
},
|
||||
}
|
||||
|
||||
const updatedMeta = clone(txMeta)
|
||||
|
||||
txController.addTx(txMeta)
|
||||
const updatedMeta = txController.getTx('1')
|
||||
updatedMeta.txParams.gasPrice = desiredGasPrice
|
||||
txController.updateTx(updatedMeta)
|
||||
var result = txController.getTx('1')
|
||||
|
23
test/unit/tx-state-history-helper.js
Normal file
23
test/unit/tx-state-history-helper.js
Normal file
@ -0,0 +1,23 @@
|
||||
const assert = require('assert')
|
||||
const txStateHistoryHelper = require('../../app/scripts/lib/tx-state-history-helper')
|
||||
const testVault = require('../data/v17-long-history.json')
|
||||
|
||||
|
||||
describe('history-differ', function () {
|
||||
it('migrates history to diffs and can recover original values', function () {
|
||||
testVault.data.TransactionController.transactions.forEach((tx, index) => {
|
||||
const newHistory = txStateHistoryHelper.migrateFromSnapshotsToDiffs(tx.history)
|
||||
newHistory.forEach((newEntry, index) => {
|
||||
if (index === 0) {
|
||||
assert.equal(Array.isArray(newEntry), false, 'initial history item IS NOT a json patch obj')
|
||||
} else {
|
||||
assert.equal(Array.isArray(newEntry), true, 'non-initial history entry IS a json patch obj')
|
||||
}
|
||||
const oldEntry = tx.history[index]
|
||||
const historySubset = newHistory.slice(0, index + 1)
|
||||
const reconstructedValue = txStateHistoryHelper.replayHistory(historySubset)
|
||||
assert.deepEqual(oldEntry, reconstructedValue, 'was able to reconstruct old entry from diffs')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue
Block a user