Skip to content
This repository was archived by the owner on Aug 11, 2021. It is now read-only.

Commit 13323a2

Browse files
authored
feat: CBOR TAG (#38)
* feat: CBOR TAG
1 parent 48f0294 commit 13323a2

3 files changed

Lines changed: 113 additions & 18 deletions

File tree

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,16 @@
4040
"borc": "^2.0.2",
4141
"bs58": "^4.0.0",
4242
"cids": "^0.4.0",
43+
"is-circular": "^1.0.1",
4344
"multihashes": "^0.3.2",
4445
"multihashing-async": "^0.4.0",
4546
"traverse": "^0.6.6"
4647
},
4748
"devDependencies": {
4849
"aegir": "^9.4.0",
4950
"chai": "^3.5.0",
51+
"deep-freeze": "0.0.1",
52+
"garbage": "0.0.0",
5053
"ipfs-block": "^0.5.4",
5154
"pre-commit": "^1.2.2"
5255
},
@@ -59,4 +62,4 @@
5962
"npmcdn-to-unpkg-bot <npmcdn-to-unpkg-bot@users.noreply.github.com>",
6063
"wanderer <mjbecze@gmail.com>"
6164
]
62-
}
65+
}

src/util.js

Lines changed: 67 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,90 @@ const multihashing = require('multihashing-async')
55
const CID = require('cids')
66
const waterfall = require('async/waterfall')
77
const setImmediate = require('async/setImmediate')
8+
const isCircular = require('is-circular')
89

910
const resolver = require('./resolver')
1011

12+
// https://github.com/ipfs/go-ipfs/issues/3570#issuecomment-273931692
13+
const CID_CBOR_TAG = 42
14+
15+
function tagCID (cid) {
16+
return new cbor.Tagged(CID_CBOR_TAG, cid)
17+
}
18+
19+
const decoder = new cbor.Decoder({
20+
tags: {
21+
[CID_CBOR_TAG]: (val) => ({'/': val})
22+
}
23+
})
24+
25+
function replaceCIDbyTAG (dagNode) {
26+
if (isCircular(dagNode)) {
27+
throw new Error('The object passed has circular references')
28+
}
29+
30+
function transform (obj) {
31+
if (!obj || Buffer.isBuffer(obj) || typeof obj === 'string') {
32+
return obj
33+
}
34+
35+
if (Array.isArray(obj)) {
36+
return obj.map(transform)
37+
}
38+
39+
const keys = Object.keys(obj)
40+
41+
// only `{'/': 'link'}` are valid
42+
if (keys.length === 1 && keys[0] === '/') {
43+
// Multiaddr encoding
44+
// if (typeof link === 'string' && isMultiaddr(link)) {
45+
// link = new Multiaddr(link).buffer
46+
// }
47+
48+
return tagCID(obj['/'])
49+
} else if (keys.length > 0) {
50+
// Recursive transform
51+
let out = {}
52+
keys.forEach((key) => {
53+
if (typeof obj[key] === 'object') {
54+
out[key] = transform(obj[key])
55+
} else {
56+
out[key] = obj[key]
57+
}
58+
})
59+
return out
60+
} else {
61+
return obj
62+
}
63+
}
64+
65+
return transform(dagNode)
66+
}
67+
1168
exports = module.exports
1269

1370
exports.serialize = (dagNode, callback) => {
1471
let serialized
72+
1573
try {
16-
serialized = cbor.encode(dagNode)
74+
const dagNodeTagged = replaceCIDbyTAG(dagNode)
75+
serialized = cbor.encode(dagNodeTagged)
1776
} catch (err) {
18-
// return is important, otherwise in case of error the execution would continue
19-
return setImmediate(() => {
20-
callback(err)
21-
})
77+
return setImmediate(() => callback(err))
2278
}
23-
setImmediate(() => {
24-
callback(null, serialized)
25-
})
79+
setImmediate(() => callback(null, serialized))
2680
}
2781

2882
exports.deserialize = (data, callback) => {
29-
let res
83+
let deserialized
84+
3085
try {
31-
res = cbor.decodeFirst(data)
86+
deserialized = decoder.decodeFirst(data)
3287
} catch (err) {
33-
return setImmediate(() => {
34-
callback(err)
35-
})
88+
return setImmediate(() => callback(err))
3689
}
3790

38-
setImmediate(() => {
39-
callback(null, res)
40-
})
91+
setImmediate(() => callback(null, deserialized))
4192
}
4293

4394
exports.cid = (dagNode, callback) => {

test/util.spec.js

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,35 @@
22
'use strict'
33

44
const expect = require('chai').expect
5+
const deepFreeze = require('deep-freeze')
6+
const garbage = require('garbage')
7+
const map = require('async/map')
58
const dagCBOR = require('../src')
69

710
describe('util', () => {
811
const obj = {
912
someKey: 'someValue',
10-
link: { '/': 'aaaaa' }
13+
link: { '/': 'aaaaa' },
14+
links: [{'/': 1}, {'/': 2}],
15+
nested: {
16+
hello: 'world',
17+
link: { '/': 'mylink' }
18+
}
1119
}
1220

1321
it('.serialize and .deserialize', (done) => {
22+
// freeze, to ensure we don't change the source objecdt
23+
deepFreeze(obj)
1424
dagCBOR.util.serialize(obj, (err, serialized) => {
1525
expect(err).to.not.exist
1626
expect(Buffer.isBuffer(serialized)).to.be.true
1727

28+
// Check for the tag 42
29+
// d8 = tag, 2a = 42
30+
expect(
31+
serialized.toString('hex').match(/d82a/g)
32+
).to.have.length(4)
33+
1834
dagCBOR.util.deserialize(serialized, (err, deserialized) => {
1935
expect(err).to.not.exist
2036
expect(obj).to.eql(deserialized)
@@ -42,4 +58,29 @@ describe('util', () => {
4258
done()
4359
})
4460
})
61+
62+
it('serialize and deserialize - garbage', (done) => {
63+
const inputs = []
64+
for (let i = 0; i < 1000; i++) {
65+
inputs.push({in: garbage(100)})
66+
}
67+
68+
map(inputs, (input, cb) => {
69+
dagCBOR.util.serialize(input, cb)
70+
}, (err, encoded) => {
71+
if (err) {
72+
return done(err)
73+
}
74+
map(encoded, (enc, cb) => {
75+
dagCBOR.util.deserialize(enc, cb)
76+
}, (err, out) => {
77+
if (err) {
78+
return done(err)
79+
}
80+
81+
expect(inputs).to.be.eql(out)
82+
done()
83+
})
84+
})
85+
})
4586
})

0 commit comments

Comments
 (0)