1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

add time stamps to transaction history log entries

This commit is contained in:
Csaba Solya 2018-05-10 13:26:02 +02:00
parent f17dc5f452
commit 9026651224
5 changed files with 157 additions and 81 deletions

View File

@ -25,26 +25,31 @@ function migrateFromSnapshotsToDiffs (longHistory) {
} }
/** /**
generates an array of history objects sense the previous state. Generates an array of history objects sense the previous state.
The object has the keys opp(the operation preformed), The object has the keys
path(the key and if a nested object then each key will be seperated with a `/`) op (the operation performed),
value path (the key and if a nested object then each key will be seperated with a `/`)
with the first entry having the note value
with the first entry having the note and a timestamp when the change took place
@param previousState {object} - the previous state of the object @param previousState {object} - the previous state of the object
@param newState {object} - the update object @param newState {object} - the update object
@param note {string} - a optional note for the state change @param note {string} - a optional note for the state change
@reurns {array} @returns {array}
*/ */
function generateHistoryEntry (previousState, newState, note) { function generateHistoryEntry (previousState, newState, note) {
const entry = jsonDiffer.compare(previousState, newState) const entry = jsonDiffer.compare(previousState, newState)
// Add a note to the first op, since it breaks if we append it to the entry // Add a note to the first op, since it breaks if we append it to the entry
if (note && entry[0]) entry[0].note = note if (entry[0]) {
if (note) entry[0].note = note
entry[0].timestamp = (new Date()).getTime()
}
return entry return entry
} }
/** /**
Recovers previous txMeta state obj Recovers previous txMeta state obj
@return {object} @returns {object}
*/ */
function replayHistory (_shortHistory) { function replayHistory (_shortHistory) {
const shortHistory = clone(_shortHistory) const shortHistory = clone(_shortHistory)

View File

@ -2,6 +2,7 @@ const extend = require('xtend')
const EventEmitter = require('events') const EventEmitter = require('events')
const ObservableStore = require('obs-store') const ObservableStore = require('obs-store')
const ethUtil = require('ethereumjs-util') const ethUtil = require('ethereumjs-util')
const log = require('loglevel')
const txStateHistoryHelper = require('./lib/tx-state-history-helper') const txStateHistoryHelper = require('./lib/tx-state-history-helper')
const createId = require('../../lib/random-id') const createId = require('../../lib/random-id')
const { getFinalStates } = require('./lib/util') const { getFinalStates } = require('./lib/util')
@ -158,7 +159,7 @@ class TransactionStateManager extends EventEmitter {
/** /**
updates the txMeta in the list and adds a history entry updates the txMeta in the list and adds a history entry
@param txMeta {Object} - the txMeta to update @param txMeta {Object} - the txMeta to update
@param [note] {string} - a not about the update for history @param [note] {string} - a note about the update for history
*/ */
updateTx (txMeta, note) { updateTx (txMeta, note) {
// validate txParams // validate txParams
@ -398,13 +399,19 @@ class TransactionStateManager extends EventEmitter {
_setTxStatus (txId, status) { _setTxStatus (txId, status) {
const txMeta = this.getTx(txId) const txMeta = this.getTx(txId)
txMeta.status = status txMeta.status = status
this.emit(`${txMeta.id}:${status}`, txId) setTimeout(() => {
this.emit(`tx:status-update`, txId, status) try {
if (['submitted', 'rejected', 'failed'].includes(status)) { this.updateTx(txMeta, `txStateManager: setting status to ${status}`)
this.emit(`${txMeta.id}:finished`, txMeta) this.emit(`${txMeta.id}:${status}`, txId)
} this.emit(`tx:status-update`, txId, status)
this.updateTx(txMeta, `txStateManager: setting status to ${status}`) if (['submitted', 'rejected', 'failed'].includes(status)) {
this.emit('update:badge') this.emit(`${txMeta.id}:finished`, txMeta)
}
this.emit('update:badge')
} catch (error) {
log.error(error)
}
})
} }
/** /**

View File

@ -1,26 +1,129 @@
const assert = require('assert') const assert = require('assert')
const clone = require('clone')
const txStateHistoryHelper = require('../../app/scripts/controllers/transactions/lib/tx-state-history-helper') const txStateHistoryHelper = require('../../app/scripts/controllers/transactions/lib/tx-state-history-helper')
const testVault = require('../data/v17-long-history.json')
describe('deepCloneFromTxMeta', function () { describe ('Transaction state history helper', function () {
it('should clone deep', function () {
const input = { describe('#snapshotFromTxMeta', function () {
foo: { it('should clone deep', function () {
bar: { const input = {
bam: 'baz' foo: {
bar: {
bam: 'baz'
}
} }
} }
} const output = txStateHistoryHelper.snapshotFromTxMeta(input)
const output = txStateHistoryHelper.snapshotFromTxMeta(input) assert('foo' in output, 'has a foo key')
assert('foo' in output, 'has a foo key') assert('bar' in output.foo, 'has a bar key')
assert('bar' in output.foo, 'has a bar key') assert('bam' in output.foo.bar, 'has a bar key')
assert('bam' in output.foo.bar, 'has a bar key') assert.equal(output.foo.bar.bam, 'baz', 'has a baz value')
assert.equal(output.foo.bar.bam, 'baz', 'has a baz value') })
it('should remove the history key', function () {
const input = { foo: 'bar', history: 'remembered' }
const output = txStateHistoryHelper.snapshotFromTxMeta(input)
assert(typeof output.history, 'undefined', 'should remove history')
})
}) })
it('should remove the history key', function () { describe('#migrateFromSnapshotsToDiffs', function () {
const input = { foo: 'bar', history: 'remembered' } it('migrates history to diffs and can recover original values', function () {
const output = txStateHistoryHelper.snapshotFromTxMeta(input) testVault.data.TransactionController.transactions.forEach((tx, index) => {
assert(typeof output.history, 'undefined', 'should remove history') 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')
})
})
})
}) })
})
describe('#replayHistory', function () {
it('replaying history does not mutate the original obj', function () {
const initialState = { test: true, message: 'hello', value: 1 }
const diff1 = [{
"op": "replace",
"path": "/message",
"value": "haay",
}]
const diff2 = [{
"op": "replace",
"path": "/value",
"value": 2,
}]
const history = [initialState, diff1, diff2]
const beforeStateSnapshot = JSON.stringify(initialState)
const latestState = txStateHistoryHelper.replayHistory(history)
const afterStateSnapshot = JSON.stringify(initialState)
assert.notEqual(initialState, latestState, 'initial state is not the same obj as the latest state')
assert.equal(beforeStateSnapshot, afterStateSnapshot, 'initial state is not modified during run')
})
})
describe('#generateHistoryEntry', function () {
function generateHistoryEntryTest(note) {
const prevState = {
someValue: 'value 1',
foo: {
bar: {
bam: 'baz'
}
}
}
const nextState = {
newPropRoot: 'new property - root',
someValue: 'value 2',
foo: {
newPropFirstLevel: 'new property - first level',
bar: {
bam: 'baz'
}
}
}
const before = new Date().getTime()
const result = txStateHistoryHelper.generateHistoryEntry(prevState, nextState, note)
const after = new Date().getTime()
assert.ok(Array.isArray(result))
assert.equal(result.length, 3)
const expectedEntry1 = { op: 'add', path: '/foo/newPropFirstLevel', value: 'new property - first level' }
assert.equal(result[0].op, expectedEntry1.op)
assert.equal(result[0].path, expectedEntry1.path)
assert.equal(result[0].value, expectedEntry1.value)
assert.equal(result[0].value, expectedEntry1.value)
if (note)
assert.equal(result[0].note, note)
assert.ok(result[0].timestamp >= before && result[0].timestamp <= after)
const expectedEntry2 = { op: 'replace', path: '/someValue', value: 'value 2' }
assert.deepEqual(result[1], expectedEntry2)
const expectedEntry3 = { op: 'add', path: '/newPropRoot', value: 'new property - root' }
assert.deepEqual(result[2], expectedEntry3)
}
it('should generate history entries', function () {
generateHistoryEntryTest()
})
it('should add note to first entry', function () {
generateHistoryEntryTest('custom note')
})
})
})

View File

@ -1,46 +0,0 @@
const assert = require('assert')
const txStateHistoryHelper = require('../../app/scripts/controllers/transactions/lib/tx-state-history-helper')
const testVault = require('../data/v17-long-history.json')
describe('tx-state-history-helper', 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')
})
})
})
it('replaying history does not mutate the original obj', function () {
const initialState = { test: true, message: 'hello', value: 1 }
const diff1 = [{
"op": "replace",
"path": "/message",
"value": "haay",
}]
const diff2 = [{
"op": "replace",
"path": "/value",
"value": 2,
}]
const history = [initialState, diff1, diff2]
const beforeStateSnapshot = JSON.stringify(initialState)
const latestState = txStateHistoryHelper.replayHistory(history)
const afterStateSnapshot = JSON.stringify(initialState)
assert.notEqual(initialState, latestState, 'initial state is not the same obj as the latest state')
assert.equal(beforeStateSnapshot, afterStateSnapshot, 'initial state is not modified during run')
})
})

View File

@ -176,14 +176,21 @@ describe('TransactionStateManager', function () {
assert.deepEqual(updatedTx.history[0], txStateHistoryHelper.snapshotFromTxMeta(updatedTx), 'first history item is initial state') assert.deepEqual(updatedTx.history[0], txStateHistoryHelper.snapshotFromTxMeta(updatedTx), 'first history item is initial state')
// modify value and updateTx // modify value and updateTx
updatedTx.txParams.gasPrice = desiredGasPrice updatedTx.txParams.gasPrice = desiredGasPrice
const before = new Date().getTime()
txStateManager.updateTx(updatedTx) txStateManager.updateTx(updatedTx)
const after = new Date().getTime()
// check updated value // check updated value
const result = txStateManager.getTx('1') const result = txStateManager.getTx('1')
assert.equal(result.txParams.gasPrice, desiredGasPrice, 'gas price updated') assert.equal(result.txParams.gasPrice, desiredGasPrice, 'gas price updated')
// validate history was updated // validate history was updated
assert.equal(result.history.length, 2, 'two history items (initial + diff)') assert.equal(result.history.length, 2, 'two history items (initial + diff)')
assert.equal(result.history[1].length, 1, 'two history state items (initial + diff)')
const expectedEntry = { op: 'replace', path: '/txParams/gasPrice', value: desiredGasPrice } const expectedEntry = { op: 'replace', path: '/txParams/gasPrice', value: desiredGasPrice }
assert.deepEqual(result.history[1], [expectedEntry], 'two history items (initial + diff)') assert.deepEqual(result.history[1][0].op, expectedEntry.op, 'two history items (initial + diff) operation')
assert.deepEqual(result.history[1][0].path, expectedEntry.path, 'two history items (initial + diff) path')
assert.deepEqual(result.history[1][0].value, expectedEntry.value, 'two history items (initial + diff) value')
assert.ok(result.history[1][0].timestamp >= before && result.history[1][0].timestamp <= after)
}) })
}) })