Files: a7051e65b56caff3ad3d3a29c4bf59526525e225 / index.js
4842 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 | waiting: [] |
12 | } |
13 | } |
14 | |
15 | var isInvalidContent = exports.isInvalidContent = function (content) { |
16 | if(!isEncrypted(content)) { |
17 | |
18 | var type = content.type |
19 | |
20 | if (!(isString(type) && type.length <= 52 && type.length >= 3)) { |
21 | return new Error('type must be a string' + |
22 | '3 <= type.length < 52, was:' + type |
23 | ) |
24 | } |
25 | } |
26 | return false |
27 | |
28 | } |
29 | |
30 | exports.checkInvalidCheap = function (state, msg) { |
31 | //the message is just invalid |
32 | if(!ref.isFeedId(msg.author)) |
33 | return new Error('invalid message: must have author') |
34 | |
35 | //state is id, sequence, timestamp |
36 | if(state) { |
37 | //the message is possibly a fork, but only if the signature is valid. |
38 | if(msg.sequence != state.sequence + 1) |
39 | return new Error('invalid message: expected sequence ' + (state.sequence + 1) + ' but got:'+ msg.sequence + 'in state:'+JSON.stringify(state)) |
40 | if(msg.timestamp <= state.ts) |
41 | return new Error('invalid message: timestamp not increasing') |
42 | if(msg.previous != state.id) |
43 | return new Error('invalid message: expected different previous message') |
44 | //and check type, and length, and some other stuff. finaly check the signature. |
45 | } |
46 | else { |
47 | if(msg.previous !== null) |
48 | return new Error('initial message must have previous: null') |
49 | if(msg.sequence !== 1) |
50 | return new Error('initial message must have sequence: 1') |
51 | if('number' !== typeof msg.timestamp) |
52 | return new Error('initial message must have timestamp') |
53 | } |
54 | } |
55 | |
56 | exports.checkInvalid = function (state, msg) { |
57 | var err = exports.checkInvalidCheap(state, msg) |
58 | if(err) return err |
59 | if(!ssbKeys.verifyObj({public: msg.author.substring(1)}, msg)) |
60 | return new Error('invalid signature') |
61 | return false //not invalid |
62 | } |
63 | |
64 | /* |
65 | { |
66 | //an array of messages which have been validated, but not written to the database yet. |
67 | valid: [], |
68 | //a map of information needed to know if something should be appeneded to the valid queue. |
69 | feeds: { |
70 | <feed>: {id, sequence, ts} |
71 | }, |
72 | error: null |
73 | } |
74 | */ |
75 | |
76 | exports.queue = function (state, msg) { |
77 | var err |
78 | if(state.error = exports.checkInvalidCheap(flatState(state.feeds[msg.author]), msg)) |
79 | return state |
80 | state.feeds[msg.author] = state.feeds[msg.author] || { |
81 | id: null, sequence: null, timestamp: null, queue: [] |
82 | } |
83 | state.queued += 1 |
84 | state.feeds[msg.author].queue.push(msg) |
85 | return state |
86 | } |
87 | |
88 | function flatState (fstate) { |
89 | if(!fstate) return null |
90 | if(fstate.queue.length) { |
91 | var last = fstate.queue[fstate.queue.length - 1] |
92 | return { |
93 | id: exports.id(last), |
94 | timestamp: last.timestamp, |
95 | sequence: last.sequence |
96 | } |
97 | } |
98 | else |
99 | return fstate |
100 | } |
101 | |
102 | exports.append = function (state, msg) { |
103 | if(state.error = exports.checkInvalid(flatState(state.feeds[msg.author]), msg)) |
104 | return state |
105 | |
106 | else if(state.feeds[msg.author]) { |
107 | var a = state.feeds[msg.author] |
108 | a.id = exports.id(msg) |
109 | a.sequence = msg.sequence |
110 | a.timestamp = msg.timestamp |
111 | var q = state.feeds[msg.author].queue |
112 | state.validated += q.length |
113 | state.queued -= q.length |
114 | while(q.length) |
115 | state.queue.push(q.shift()) |
116 | } |
117 | else if(msg.sequence === 1) { |
118 | state.feeds[msg.author] = {id: exports.id(msg), sequence: msg.sequence, timestamp: msg.timestamp, queue: []} |
119 | } |
120 | else { |
121 | //waiting for initial state to be loaded |
122 | state.waiting.push(msg) |
123 | } |
124 | state.queue.push(msg) |
125 | state.validated += 1 |
126 | return state |
127 | } |
128 | |
129 | exports.validate = function (state, feed) { |
130 | if(!state.feeds[feed] || !state.feeds[feed].queue.length) { |
131 | return state |
132 | } |
133 | var msg = state.feeds[feed].queue.pop() |
134 | state.queued -= 1 |
135 | return exports.append(state, msg) |
136 | } |
137 | |
138 | //pass in your own timestamp, so it's completely deterministic |
139 | exports.create = function (state, keys, hmac_key, content, timestamp) { |
140 | state = flatState(state) |
141 | return ssbKeys.signObj(keys, hmac_key, { |
142 | previous: state ? state.id : null, |
143 | sequence: state ? state.sequence + 1 : 1, |
144 | author: keys.id, |
145 | timestamp: timestamp, |
146 | hash: 'sha256', |
147 | content: content, |
148 | }) |
149 | } |
150 | |
151 | exports.id = function (msg) { |
152 | return '%'+ssbKeys.hash(JSON.stringify(msg, null, 2)) |
153 | } |
154 | |
155 | exports.appendNew = function (state, hmac_key, keys, content, timestamp) { |
156 | var msg = exports.create(keys, hmac_key, state.feeds[keys.id], content, timestamp) |
157 | state = exports.append(state, msg) |
158 | return state |
159 | } |
160 | |
161 | /* |
162 | //thought maybe this is needed for tests? |
163 | exports.setup = function (state, feeds) { |
164 | for(var k in feeds) |
165 | state.feeds[k] = feeds[k] |
166 | var w = {} |
167 | while(state.waiting) { |
168 | var msg = state.waiting.shift() |
169 | w[msg.author] = true |
170 | state = exports.queue(state, msg) |
171 | } |
172 | for(var k in w) |
173 | state = exports.validate(state, k) |
174 | return state |
175 | } |
176 | */ |
177 | |
178 | |
179 | |
180 |
Built with git-ssb-web