Skip to content

Commit 34468e1

Browse files
authored
feat: add records validator (#5)
* feat: add records validator
1 parent 3d868fe commit 34468e1

File tree

4 files changed

+138
-2
lines changed

4 files changed

+138
-2
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
dist/
2+
docs/
23

34
# Logs
45
logs

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,20 @@ const data = ipns.unmarshal(storedData)
112112

113113
Returns the entry data structure after being serialized.
114114

115+
#### Validator
116+
117+
```js
118+
const ipns = require('ipns')
119+
120+
const validator = ipns.validator
121+
```
122+
123+
Contains an object with `validate (marshalledData, peerId, callback)` and `select (dataA, dataB, callback)` functions.
124+
125+
The `validate` function aims to verify if an IPNS record is valid. First the record is unmarshalled, then the public key is obtained and finally the record is validated (signature and validity are verified).
126+
127+
The `select` function is responsible for deciding which ipns record is the best (newer) between two records. Both records are unmarshalled and their sequence numbers are compared. If the first record provided is the newer, the operation result will be `0`, otherwise the operation result will be `1`.
128+
115129
## API
116130

117131
#### Create record

src/index.js

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,38 @@ const extractPublicKeyFromId = (peerId) => {
258258
return crypto.keys.unmarshalPublicKey(decodedId.digest)
259259
}
260260

261+
const marshal = ipnsEntryProto.encode
262+
263+
const unmarshal = ipnsEntryProto.decode
264+
265+
const validator = {
266+
validate: (marshalledData, peerId, callback) => {
267+
const receivedEntry = unmarshal(marshalledData)
268+
269+
// extract public key
270+
extractPublicKey(peerId, receivedEntry, (err, pubKey) => {
271+
if (err) {
272+
return callback(err)
273+
}
274+
275+
// Record validation
276+
validate(pubKey, receivedEntry, (err) => {
277+
if (err) {
278+
return callback(err)
279+
}
280+
281+
callback(null, true)
282+
})
283+
})
284+
},
285+
select: (dataA, dataB, callback) => {
286+
const entryA = unmarshal(dataA)
287+
const entryB = unmarshal(dataB)
288+
289+
callback(null, entryA.sequence > entryB.sequence ? 0 : 1)
290+
}
291+
}
292+
261293
module.exports = {
262294
// create ipns entry record
263295
create,
@@ -272,7 +304,9 @@ module.exports = {
272304
// get keys for routing
273305
getIdKeys,
274306
// marshal
275-
marshal: ipnsEntryProto.encode,
307+
marshal,
276308
// unmarshal
277-
unmarshal: ipnsEntryProto.decode
309+
unmarshal,
310+
// validator
311+
validator
278312
}

test/index.spec.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,4 +236,91 @@ describe('ipns', function () {
236236
})
237237
})
238238
})
239+
240+
it('should use validator.validate to validate a record', (done) => {
241+
const sequence = 0
242+
const validity = 1000000
243+
244+
ipns.create(rsa, cid, sequence, validity, (err, entry) => {
245+
expect(err).to.not.exist()
246+
247+
ipns.embedPublicKey(rsa.public, entry, (err, entry) => {
248+
expect(err).to.not.exist()
249+
250+
const marshalledData = ipns.marshal(entry)
251+
252+
ipns.validator.validate(marshalledData, ipfsId, (err, valid) => {
253+
expect(err).to.not.exist()
254+
expect(valid).to.equal(true)
255+
done()
256+
})
257+
})
258+
})
259+
})
260+
261+
it('should use validator.validate to verify that a record is not valid', (done) => {
262+
const sequence = 0
263+
const validity = 1000000
264+
265+
ipns.create(rsa, cid, sequence, validity, (err, entry) => {
266+
expect(err).to.not.exist()
267+
268+
ipns.embedPublicKey(rsa.public, entry, (err, entry) => {
269+
expect(err).to.not.exist()
270+
271+
// corrupt the record by changing the value to random bytes
272+
entry.value = crypto.randomBytes(46).toString()
273+
const marshalledData = ipns.marshal(entry)
274+
275+
ipns.validator.validate(marshalledData, ipfsId, (err) => {
276+
expect(err).to.exist() // failed validation
277+
done()
278+
})
279+
})
280+
})
281+
})
282+
283+
it('should use validator.select to select the first record because it is newer', (done) => {
284+
const sequence = 0
285+
const validity = 1000000
286+
287+
ipns.create(rsa, cid, sequence, validity, (err, entry) => {
288+
expect(err).to.not.exist()
289+
290+
ipns.create(rsa, cid, (sequence + 1), validity, (err, newEntry) => {
291+
expect(err).to.not.exist()
292+
293+
const marshalledData = ipns.marshal(entry)
294+
const marshalledNewData = ipns.marshal(newEntry)
295+
296+
ipns.validator.select(marshalledNewData, marshalledData, (err, valid) => {
297+
expect(err).to.not.exist()
298+
expect(valid).to.equal(0) // new data is the selected one
299+
done()
300+
})
301+
})
302+
})
303+
})
304+
305+
it('should use validator.select to select the second record because it is newer', (done) => {
306+
const sequence = 0
307+
const validity = 1000000
308+
309+
ipns.create(rsa, cid, sequence + 1, validity, (err, entry) => {
310+
expect(err).to.not.exist()
311+
312+
ipns.create(rsa, cid, (sequence), validity, (err, newEntry) => {
313+
expect(err).to.not.exist()
314+
315+
const marshalledData = ipns.marshal(entry)
316+
const marshalledNewData = ipns.marshal(newEntry)
317+
318+
ipns.validator.select(marshalledNewData, marshalledData, (err, valid) => {
319+
expect(err).to.not.exist()
320+
expect(valid).to.equal(1) // old data is the selected one
321+
done()
322+
})
323+
})
324+
})
325+
})
239326
})

0 commit comments

Comments
 (0)