Files: 8901e7fd06735d6b52b84477d86ed7d34d092b33 / index.js
4026 bytesRaw
1 | var ref = require('ssb-ref') |
2 | var ssbKeys = require('ssb-keys') |
3 | |
4 | exports.initial = function () { |
5 | return { |
6 | validated: 0, |
7 | queued: 0, |
8 | queue: [], |
9 | feeds: {}, |
10 | error: null |
11 | } |
12 | } |
13 | |
14 | exports.checkInvalidCheap = function (state, msg) { |
15 | //the message is just invalid |
16 | if(!ref.isFeedId(msg.author)) |
17 | return new Error('invalid message: must have author') |
18 | |
19 | //state is id, sequence, timestamp |
20 | if(state) { |
21 | //the message is possibly a fork, but only if the signature is valid. |
22 | if(msg.sequence != state.sequence + 1) |
23 | return new Error('invalid message: expected sequence ' + (state.sequence + 1) + ' but got:'+ msg.sequence + 'in state:'+JSON.stringify(state)) |
24 | if(msg.timestamp <= state.ts) |
25 | return new Error('invalid message: timestamp not increasing') |
26 | if(msg.previous != state.id) |
27 | return new Error('invalid message: expected different previous message') |
28 | //and check type, and length, and some other stuff. finaly check the signature. |
29 | } |
30 | else { |
31 | if(msg.previous !== null) |
32 | return new Error('initial message must have previous: null') |
33 | if(msg.sequence !== 1) |
34 | return new Error('initial message must have sequence: 1') |
35 | if('number' !== typeof msg.timestamp) |
36 | return new Error('initial message must have timestamp') |
37 | } |
38 | } |
39 | |
40 | exports.checkInvalid = function (state, msg) { |
41 | var err = exports.checkInvalidCheap(state, msg) |
42 | if(err) return err |
43 | if(!ssbKeys.verifyObj({public: msg.author.substring(1)}, msg)) |
44 | return new Error('invalid signature') |
45 | return false //not invalid |
46 | } |
47 | |
48 | /* |
49 | { |
50 | //an array of messages which have been validated, but not written to the database yet. |
51 | valid: [], |
52 | //a map of information needed to know if something should be appeneded to the valid queue. |
53 | feeds: { |
54 | <feed>: {id, sequence, ts} |
55 | }, |
56 | error: null |
57 | } |
58 | */ |
59 | |
60 | exports.queue = function (state, msg) { |
61 | var err |
62 | if(state.error = exports.checkInvalidCheap(flatState(state.feeds[msg.author]), msg)) |
63 | return state |
64 | state.feeds[msg.author] = state.feeds[msg.author] || { |
65 | id: null, sequence: null, timestamp: null, queue: [] |
66 | } |
67 | state.queued += 1 |
68 | state.feeds[msg.author].queue.push(msg) |
69 | return state |
70 | } |
71 | |
72 | function flatState (fstate) { |
73 | if(!fstate) return null |
74 | if(fstate.queue.length) { |
75 | var last = fstate.queue[fstate.queue.length - 1] |
76 | return { |
77 | id: exports.id(last), |
78 | timestamp: last.timestamp, |
79 | sequence: last.sequence |
80 | } |
81 | } |
82 | else |
83 | return fstate |
84 | } |
85 | |
86 | exports.append = function (state, msg) { |
87 | if(state.error = exports.checkInvalid(flatState(state.feeds[msg.author]), msg)) |
88 | return state |
89 | |
90 | else if(state.feeds[msg.author]) { |
91 | var a = state.feeds[msg.author] |
92 | a.id = exports.id(msg) |
93 | a.sequence = msg.sequence |
94 | a.timestamp = msg.timestamp |
95 | var q = state.feeds[msg.author].queue |
96 | state.validated += q.length |
97 | state.queued -= q.length |
98 | while(q.length) |
99 | state.queue.push(q.shift()) |
100 | } |
101 | else { |
102 | state.feeds[msg.author] = {id: exports.id(msg), sequence: msg.sequence, timestamp: msg.timestamp, queue: []} |
103 | } |
104 | state.queue.push(msg) |
105 | state.validated += 1 |
106 | return state |
107 | } |
108 | |
109 | exports.validate = function (state, feed) { |
110 | if(!state.feeds[feed] || !state.feeds[feed].queue.length) { |
111 | return state |
112 | } |
113 | var msg = state.feeds[feed].queue.pop() |
114 | state.queued -= 1 |
115 | return exports.append(state, msg) |
116 | } |
117 | |
118 | //pass in your own timestamp, so it's completely deterministic |
119 | exports.create = function (keys, hmac_key, state, content, timestamp) { |
120 | state = flatState(state) |
121 | return ssbKeys.signObj(keys, hmac_key, { |
122 | previous: state ? state.id : null, |
123 | sequence: state ? state.sequence + 1 : 1, |
124 | author: keys.id, |
125 | timestamp: timestamp, |
126 | hash: 'sha256', |
127 | content: content, |
128 | }) |
129 | } |
130 | |
131 | exports.id = function (msg) { |
132 | return '%'+ssbKeys.hash(JSON.stringify(msg, null, 2)) |
133 | } |
134 | |
135 | exports.appendNew = function (state, hmac_key, keys, content, timestamp) { |
136 | var msg = exports.create(keys, hmac_key, state.feeds[keys.id], content, timestamp) |
137 | state = exports.append(state, msg) |
138 | return state |
139 | } |
140 | |
141 | |
142 | |
143 |
Built with git-ssb-web