1 |
|
2 |
|
3 | const ObservedRemoveMap = require('./map');
|
4 | const getVerifier = require('./verifier');
|
5 | const { InvalidSignatureError } = require('./signed-error');
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 | class SignedObservedRemoveMap extends ObservedRemoveMap {
|
15 | constructor(entries , options ) {
|
16 | super([], options);
|
17 | if (!options || !options.key) {
|
18 | throw new Error('Missing required options.key parameter');
|
19 | }
|
20 | this.verify = getVerifier(options.key, options.format);
|
21 | this.insertionSignatureMap = new Map();
|
22 | this.deletionSignatureMap = new Map();
|
23 | if (!entries) {
|
24 | return;
|
25 | }
|
26 | for (const [key, value, id, signature] of entries) {
|
27 | this.setSigned(key, value, id, signature);
|
28 | }
|
29 | }
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 | dump() {
|
36 | const [insertQueue, deleteQueue] = super.dump();
|
37 | const signedInsertQueue = insertQueue.map(([key, [id, value]]) => {
|
38 | const signature = this.insertionSignatureMap.get(id);
|
39 | if (!signature) {
|
40 | throw new Error(`Missing signature for insertion key "${JSON.stringify(key)}" with id "${id}" and value "${JSON.stringify(value)}"`);
|
41 | }
|
42 | return [signature, id, key, value];
|
43 | });
|
44 | const signedDeleteQueue = deleteQueue.map(([id, key]) => {
|
45 | const signature = this.deletionSignatureMap.get(id);
|
46 | if (!signature) {
|
47 | throw new Error(`Missing signature for deletion key "${JSON.stringify(key)}" with id "${id}"`);
|
48 | }
|
49 | return [signature, id, key];
|
50 | });
|
51 | const queue = [signedInsertQueue, signedDeleteQueue];
|
52 | return queue;
|
53 | }
|
54 |
|
55 | flush() {
|
56 | const now = Date.now();
|
57 | for (const [id] of this.deletions) {
|
58 | const timestamp = parseInt(id.slice(0, 9), 36);
|
59 | if (now - timestamp >= this.maxAge) {
|
60 | this.deletions.delete(id);
|
61 | this.deletionSignatureMap.delete(id);
|
62 | }
|
63 | }
|
64 | }
|
65 |
|
66 | process(signedQueue , skipFlush = false) {
|
67 | const [signedInsertQueue, signedDeleteQueue] = signedQueue;
|
68 | const insertQueue = signedInsertQueue.map(([signature, id, key, value]) => {
|
69 | if (!this.verify(signature, key, value, id)) {
|
70 | throw new InvalidSignatureError(`Signature does not match for key "${key}" with value ${JSON.stringify(value)}`);
|
71 | }
|
72 | this.insertionSignatureMap.set(id, signature);
|
73 | return [key, [id, value]];
|
74 | });
|
75 | const deleteQueue = signedDeleteQueue.map(([signature, id, key]) => {
|
76 | if (!this.verify(signature, key, id)) {
|
77 | throw new InvalidSignatureError(`Signature does not match for id ${JSON.stringify(id)}`);
|
78 | }
|
79 | this.deletionSignatureMap.set(id, signature);
|
80 | return [id, key];
|
81 | });
|
82 | const queue = [insertQueue, deleteQueue];
|
83 | super.process(queue, skipFlush);
|
84 | for (const [signature, id, key] of signedInsertQueue) {
|
85 | const pair = this.pairs.get(key);
|
86 | if (!pair || pair[0] !== id) {
|
87 | this.insertionSignatureMap.delete(id);
|
88 | }
|
89 | }
|
90 | }
|
91 |
|
92 | setSigned(key , value , id , signature ) {
|
93 | const message = [signature, id, key, value];
|
94 | this.process([[message], []], true);
|
95 | this.insertQueue.push(message);
|
96 | this.dequeue();
|
97 | return this;
|
98 | }
|
99 |
|
100 | deleteSigned(key , id , signature ) {
|
101 | const message = [signature, id, key];
|
102 | this.process([[], [message]], true);
|
103 | this.deleteQueue.push(message);
|
104 | this.dequeue();
|
105 | }
|
106 |
|
107 | clear() {
|
108 | throw new Error('Unsupported method clear()');
|
109 | }
|
110 |
|
111 | set() {
|
112 | throw new Error('Unsupported method set(), use setSigned()');
|
113 | }
|
114 |
|
115 | delete() {
|
116 | throw new Error('Unsupported method delete(), use deleteSignedId()');
|
117 | }
|
118 | }
|
119 |
|
120 | module.exports = SignedObservedRemoveMap;
|