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:
parent
f17dc5f452
commit
9026651224
@ -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)
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
@ -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')
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
@ -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)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user