-
Notifications
You must be signed in to change notification settings - Fork 0
/
EventChain.js
72 lines (60 loc) · 2.4 KB
/
EventChain.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
var crypto = require('crypto');
const { v4: uuidv4 } = require('uuid');
class EventChain {
eventChain = [];
MakeHash(eventObject) {
return crypto
.createHash('sha256')
.update(JSON.stringify(eventObject))
.digest('base64');
}
VerifyHash(eventObject, expectedHashValue) {
return this.MakeHash(eventObject) === expectedHashValue;
}
isValid() {
let eventChainIsValid = true;
// Verify hash validity for each event
for (const event of this.eventChain) {
const reconstructedEvent = { eventType: event.eventType, data: event.data, metaData: event.metaData };
if (eventChainIsValid && !this.VerifyHash(reconstructedEvent, event.hash)) {
eventChainIsValid = false;
console.warn('Invalid event found', event);
console.warn(`Expected to have hash ${this.MakeHash(reconstructedEvent)}, but it was ${event.hash}`);
}
};
// Verify hash chain is in the correct order and none are missing
let expectedPreviousHash = '0';
this.eventChain.forEach(event => {
if (eventChainIsValid && event.metaData.previousHash !== expectedPreviousHash) {
eventChainIsValid = false;
console.warn('Invalid event chain found', event);
console.warn(`Expected previous hash to be ${expectedPreviousHash}`);
}
expectedPreviousHash = event.hash;
});
return eventChainIsValid;
}
AddEvent(eventType, data) {
const rightNow = new Date();
const timeStamp = rightNow.toISOString();
const metaData = {
createdBy: "Ryan",
createdDate: timeStamp,
previousHash: this.eventChain.length === 0 ? "0" : [...this.eventChain].pop().hash
};
const newEvent = { eventType, data, metaData };
const newHash = this.MakeHash(newEvent);
const newEventId = uuidv4();
this.eventChain.push({...newEvent, id: newEventId, hash: newHash });
}
// Default projection. Allow custom projection functions in the future?
// Relies on the event type to be in the expected format of ENTITYTYPE_ACTION
GetProjection(entityType, id) {
const eventsForId = this.eventChain.filter(event => event.data.id === id && event.eventType.split('_')[0] === entityType);
return eventsForId.reduce((summation, event) => ({ ...summation, ...event.data }), {});
}
PrintChain() {
console.log(JSON.stringify(this.eventChain, null, 3))
}
}
module.exports = EventChain;