git ssb

0+

Dominic / ssb-validate



Tree: a7051e65b56caff3ad3d3a29c4bf59526525e225

Files: a7051e65b56caff3ad3d3a29c4bf59526525e225 / index.js

4842 bytesRaw
1var ref = require('ssb-ref')
2var ssbKeys = require('ssb-keys')
3
4exports.initial = function () {
5 return {
6 validated: 0,
7 queued: 0,
8 queue: [],
9 feeds: {},
10 error: null,
11 waiting: []
12 }
13}
14
15var 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
30exports.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
56exports.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
76exports.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
88function 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
102exports.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
129exports.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
139exports.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
151exports.id = function (msg) {
152 return '%'+ssbKeys.hash(JSON.stringify(msg, null, 2))
153}
154
155exports.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?
163exports.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