Skip to content
This repository was archived by the owner on Jul 21, 2023. It is now read-only.

Commit 3b99ee1

Browse files
authored
fix: support uint8arrays in place of node buffers (#23)
BREAKING CHANGE: takes Uint8Arrays as well as Node Buffers
1 parent a60c685 commit 3b99ee1

File tree

13 files changed

+108
-75
lines changed

13 files changed

+108
-75
lines changed

package.json

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,19 @@
3737
},
3838
"homepage": "https://github.com/libp2p/js-libp2p-record",
3939
"devDependencies": {
40-
"aegir": "^22.0.0",
40+
"aegir": "^25.0.0",
4141
"chai": "^4.2.0",
4242
"dirty-chai": "^2.0.1",
43+
"ipfs-utils": "^2.3.1",
4344
"libp2p-crypto": "~0.17.0",
45+
"multibase": "^2.0.0",
4446
"peer-id": "~0.13.6"
4547
},
4648
"dependencies": {
47-
"buffer": "^5.6.0",
4849
"err-code": "^2.0.0",
49-
"multihashes": "~0.4.15",
50-
"multihashing-async": "^0.8.0",
51-
"protons": "^1.0.1"
50+
"multihashes": "^1.0.1",
51+
"multihashing-async": "^1.0.0",
52+
"protons": "^1.2.1"
5253
},
5354
"contributors": [
5455
"Vasco Santos <vasco.santos@moxy.studio>",

src/record.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
11
'use strict'
22

33
const protons = require('protons')
4-
const { Buffer } = require('buffer')
54
const pb = protons(require('./record.proto')).Record
65
const utils = require('./utils')
76

87
class Record {
98
/**
10-
* @param {Buffer} [key]
11-
* @param {Buffer} [value]
9+
* @param {Uint8Array} [key]
10+
* @param {Uint8Array} [value]
1211
* @param {Date} [recvtime]
1312
*/
1413
constructor (key, value, recvtime) {
15-
if (key && !Buffer.isBuffer(key)) {
16-
throw new Error('key must be a Buffer')
14+
if (!(key instanceof Uint8Array)) {
15+
throw new Error('key must be a Uint8Array')
1716
}
1817

19-
if (value && !Buffer.isBuffer(value)) {
20-
throw new Error('value must be a buffer')
18+
if (!(value instanceof Uint8Array)) {
19+
throw new Error('value must be a Uint8Array')
2120
}
2221

2322
this.key = key
@@ -26,7 +25,7 @@ class Record {
2625
}
2726

2827
/**
29-
* @returns {Buffer}
28+
* @returns {Uint8Array}
3029
*/
3130
serialize () {
3231
return pb.encode(this.prepareSerialize())
@@ -48,7 +47,7 @@ class Record {
4847
/**
4948
* Decode a protobuf encoded record.
5049
*
51-
* @param {Buffer} raw
50+
* @param {Uint8Array} raw
5251
* @returns {Record}
5352
*/
5453
static deserialize (raw) {

src/selection.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
'use strict'
22

33
const errcode = require('err-code')
4+
const { utf8Decoder } = require('./utils')
5+
46
/**
57
* Select the best record out of the given records.
68
*
79
* @param {Object} selectors
8-
* @param {Buffer} k
9-
* @param {Array<Buffer>} records
10+
* @param {Uint8Array} k
11+
* @param {Array<Uint8Array>} records
1012
* @returns {number} - The index of the best record.
1113
*/
1214
const bestRecord = (selectors, k, records) => {
@@ -16,7 +18,8 @@ const bestRecord = (selectors, k, records) => {
1618
throw errcode(new Error(errMsg), 'ERR_NO_RECORDS_RECEIVED')
1719
}
1820

19-
const parts = k.toString().split('/')
21+
const kStr = utf8Decoder.decode(k)
22+
const parts = kStr.split('/')
2023

2124
if (parts.length < 3) {
2225
const errMsg = 'Record key does not have a selector function'

src/selectors/public-key.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
* Simply returns the first record, as all valid public key
66
* records are equal.
77
*
8-
* @param {Buffer} k
9-
* @param {Array<Buffer>} records
8+
* @param {Uint8Array} k
9+
* @param {Array<Uint8Array>} records
1010
* @returns {number}
1111
*/
1212
const publicKeySelector = (k, records) => {

src/utils.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
'use strict'
22

3+
const TextDecoder = require('ipfs-utils/src/text-decoder')
4+
const utf8Decoder = new TextDecoder('utf8')
5+
6+
module.exports.utf8Decoder = utf8Decoder
7+
38
/**
49
* Convert a JavaScript date into an `RFC3339Nano` formatted
510
* string.
@@ -52,3 +57,21 @@ module.exports.parseRFC3339 = (time) => {
5257

5358
return new Date(Date.UTC(year, month, date, hour, minute, second, millisecond))
5459
}
60+
61+
module.exports.uint8ArraysEqual = (arr1, arr2) => {
62+
if (arr1 === arr2) {
63+
return true
64+
}
65+
66+
if (arr1.byteLength !== arr2.byteLength) {
67+
return false
68+
}
69+
70+
for (let i = 0; i < arr1.byteLength; i++) {
71+
if (arr1[i] !== arr2[i]) {
72+
return false
73+
}
74+
}
75+
76+
return true
77+
}

src/validator.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict'
22

33
const errcode = require('err-code')
4+
const { utf8Decoder } = require('./utils')
45
/**
56
* Checks a record and ensures it is still valid.
67
* It runs the needed validators.
@@ -12,7 +13,8 @@ const errcode = require('err-code')
1213
*/
1314
const verifyRecord = (validators, record) => {
1415
const key = record.key
15-
const parts = key.toString().split('/')
16+
const keyString = utf8Decoder.decode(key)
17+
const parts = keyString.split('/')
1618

1719
if (parts.length < 3) {
1820
// No validator available

src/validators/public-key.js

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,28 @@
22

33
const multihashing = require('multihashing-async')
44
const errcode = require('err-code')
5-
const { Buffer } = require('buffer')
5+
const { utf8Decoder, uint8ArraysEqual } = require('../utils')
6+
67
/**
7-
* Validator for publick key records.
8+
* Validator for public key records.
89
* Verifies that the passed in record value is the PublicKey
910
* that matches the passed in key.
1011
* If validation fails the returned Promise will reject with the error.
1112
*
12-
* @param {Buffer} key - A valid key is of the form `'/pk/<keymultihash>'`
13-
* @param {Buffer} publicKey - The public key to validate against (protobuf encoded).
13+
* @param {Uint8Array} key - A valid key is of the form `'/pk/<keymultihash>'`
14+
* @param {Uint8Array} publicKey - The public key to validate against (protobuf encoded).
1415
* @returns {Promise}
1516
*/
1617
const validatePublicKeyRecord = async (key, publicKey) => {
17-
if (!Buffer.isBuffer(key)) {
18-
throw errcode(new Error('"key" must be a Buffer'), 'ERR_INVALID_RECORD_KEY_NOT_BUFFER')
18+
if (!(key instanceof Uint8Array)) {
19+
throw errcode(new Error('"key" must be a Uint8Array'), 'ERR_INVALID_RECORD_KEY_NOT_BUFFER')
1920
}
2021

21-
if (key.length < 5) {
22+
if (key.byteLength < 5) {
2223
throw errcode(new Error('invalid public key record'), 'ERR_INVALID_RECORD_KEY_TOO_SHORT')
2324
}
2425

25-
const prefix = key.slice(0, 4).toString()
26+
const prefix = utf8Decoder.decode(key.subarray(0, 4))
2627

2728
if (prefix !== '/pk/') {
2829
throw errcode(new Error('key was not prefixed with /pk/'), 'ERR_INVALID_RECORD_KEY_BAD_PREFIX')
@@ -32,7 +33,7 @@ const validatePublicKeyRecord = async (key, publicKey) => {
3233

3334
const publicKeyHash = await multihashing(publicKey, 'sha2-256')
3435

35-
if (!keyhash.equals(publicKeyHash)) {
36+
if (!uint8ArraysEqual(keyhash, publicKeyHash)) {
3637
throw errcode(new Error('public key does not match passed in key'), 'ERR_INVALID_RECORD_HASH_MISMATCH')
3738
}
3839
}

test/fixtures/go-key-records.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
'use strict'
2-
const { Buffer } = require('buffer')
2+
3+
const multibase = require('multibase')
4+
35
module.exports = {
4-
publicKey: Buffer.from(
5-
'CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDjXAQQMal4SB2tSnX6NJIPmC69/BT8A8jc7/gDUZNkEhdhYHvc7k7S4vntV/c92nJGxNdop9fKJyevuNMuXhhHAgMBAAE=',
6-
'base64'
6+
publicKey: multibase.decode(
7+
'MCAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDjXAQQMal4SB2tSnX6NJIPmC69/BT8A8jc7/gDUZNkEhdhYHvc7k7S4vntV/c92nJGxNdop9fKJyevuNMuXhhHAgMBAAE='
78
)
89
}

test/fixtures/go-record.js

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
'use strict'
2-
const { Buffer } = require('buffer')
2+
const multibase = require('multibase')
33
// Fixtures generated using gore (https://github.com/motemen/gore)
44
//
55
// :import github.com/libp2p/go-libp2p-record
@@ -18,12 +18,10 @@ const { Buffer } = require('buffer')
1818
// ioutil.WriteFile("js-libp2p-record/test/fixtures/record.bin", enc, 0644)
1919
// ioutil.WriteFile("js-libp2p-record/test/fixtures/record-signed.bin", enc2, 0644)
2020
module.exports = {
21-
serialized: Buffer.from(
22-
'0a0568656c6c6f1205776f726c641a2212201bd5175b1d4123ee29665348c60ea5cf5ac62e2e05215b97a7b9a9b0cf71d116',
23-
'hex'
21+
serialized: multibase.decode(
22+
'f0a0568656c6c6f1205776f726c641a2212201bd5175b1d4123ee29665348c60ea5cf5ac62e2e05215b97a7b9a9b0cf71d116'
2423
),
25-
serializedSigned: Buffer.from(
26-
'0a0568656c6c6f1205776f726c641a2212201bd5175b1d4123ee29665348c60ea5cf5ac62e2e05215b97a7b9a9b0cf71d116228001500fe7505698b8a873ccde6f1d36a2be662d57807490d9a9959540f2645a454bf615215092e10123f6ffc4ed694711bfbb1d5ccb62f3da83cf4528ee577a96b6cf0272eef9a920bd56459993690060353b72c22b8c03ad2a33894522dac338905b201179a85cb5e2fc68ed58be96cf89beec6dc0913887dddc10f202a2a1b117',
27-
'hex'
24+
serializedSigned: multibase.decode(
25+
'f0a0568656c6c6f1205776f726c641a2212201bd5175b1d4123ee29665348c60ea5cf5ac62e2e05215b97a7b9a9b0cf71d116228001500fe7505698b8a873ccde6f1d36a2be662d57807490d9a9959540f2645a454bf615215092e10123f6ffc4ed694711bfbb1d5ccb62f3da83cf4528ee577a96b6cf0272eef9a920bd56459993690060353b72c22b8c03ad2a33894522dac338905b201179a85cb5e2fc68ed58be96cf89beec6dc0913887dddc10f202a2a1b117'
2826
)
2927
}

test/record.spec.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
const chai = require('chai')
55
chai.use(require('dirty-chai'))
66
const expect = chai.expect
7-
const { Buffer } = require('buffer')
7+
const { utf8TextEncoder } = require('./utils')
88
const libp2pRecord = require('../src')
99
const Record = libp2pRecord.Record
1010

@@ -15,28 +15,28 @@ const date = new Date(Date.UTC(2012, 1, 25, 10, 10, 10, 10))
1515
describe('record', () => {
1616
it('new', () => {
1717
const rec = new Record(
18-
Buffer.from('hello'),
19-
Buffer.from('world')
18+
utf8TextEncoder.encode('hello'),
19+
utf8TextEncoder.encode('world')
2020
)
2121

22-
expect(rec).to.have.property('key').eql(Buffer.from('hello'))
23-
expect(rec).to.have.property('value').eql(Buffer.from('world'))
22+
expect(rec).to.have.property('key').eql(utf8TextEncoder.encode('hello'))
23+
expect(rec).to.have.property('value').eql(utf8TextEncoder.encode('world'))
2424
})
2525

2626
it('serialize & deserialize', () => {
27-
const rec = new Record(Buffer.from('hello'), Buffer.from('world'), date)
27+
const rec = new Record(utf8TextEncoder.encode('hello'), utf8TextEncoder.encode('world'), date)
2828
const dec = Record.deserialize(rec.serialize())
2929

30-
expect(dec).to.have.property('key').eql(Buffer.from('hello'))
31-
expect(dec).to.have.property('value').eql(Buffer.from('world'))
30+
expect(dec).to.have.property('key').eql(utf8TextEncoder.encode('hello'))
31+
expect(dec).to.have.property('value').eql(utf8TextEncoder.encode('world'))
3232
expect(dec.timeReceived).to.be.eql(date)
3333
})
3434

3535
describe('go interop', () => {
3636
it('no signature', () => {
3737
const dec = Record.deserialize(fixture.serialized)
38-
expect(dec).to.have.property('key').eql(Buffer.from('hello'))
39-
expect(dec).to.have.property('value').eql(Buffer.from('world'))
38+
expect(dec).to.have.property('key').eql(utf8TextEncoder.encode('hello'))
39+
expect(dec).to.have.property('value').eql(utf8TextEncoder.encode('world'))
4040
})
4141
})
4242
})

test/selection.spec.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,33 @@
33
'use strict'
44

55
var expect = require('chai').expect
6-
const { Buffer } = require('buffer')
6+
const { utf8TextEncoder } = require('./utils')
77
const libp2pRecord = require('../src')
88
const selection = libp2pRecord.selection
99

10-
const records = [Buffer.alloc(0), Buffer.from('hello')]
10+
const records = [new Uint8Array(), utf8TextEncoder.encode('hello')]
1111

1212
describe('selection', () => {
1313
describe('bestRecord', () => {
1414
it('throws no records given when no records received', () => {
1515
expect(
16-
() => selection.bestRecord({}, Buffer.from('/'), [])
16+
() => selection.bestRecord({}, utf8TextEncoder.encode('/'), [])
1717
).to.throw(
1818
/No records given/
1919
)
2020
})
2121

2222
it('throws on missing selector in the record key', () => {
2323
expect(
24-
() => selection.bestRecord({}, Buffer.from('/'), records)
24+
() => selection.bestRecord({}, utf8TextEncoder.encode('/'), records)
2525
).to.throw(
2626
/Record key does not have a selector function/
2727
)
2828
})
2929

3030
it('throws on unknown key prefix', () => {
3131
expect(
32-
() => selection.bestRecord({ world () {} }, Buffer.from('/hello/'), records)
32+
() => selection.bestRecord({ world () {} }, utf8TextEncoder.encode('/hello/'), records)
3333
).to.throw(
3434
/Unrecognized key prefix: hello/
3535
)
@@ -38,15 +38,15 @@ describe('selection', () => {
3838
it('returns the index from the matching selector', () => {
3939
const selectors = {
4040
hello (k, recs) {
41-
expect(k).to.be.eql(Buffer.from('/hello/world'))
41+
expect(k).to.be.eql(utf8TextEncoder.encode('/hello/world'))
4242
expect(recs).to.be.eql(records)
4343

4444
return 1
4545
}
4646
}
4747

4848
expect(
49-
selection.bestRecord(selectors, Buffer.from('/hello/world'), records)
49+
selection.bestRecord(selectors, utf8TextEncoder.encode('/hello/world'), records)
5050
).to.equal(
5151
1
5252
)
@@ -56,7 +56,7 @@ describe('selection', () => {
5656
describe('selectors', () => {
5757
it('public key', () => {
5858
expect(
59-
selection.selectors.pk(Buffer.from('/hello/world'), records)
59+
selection.selectors.pk(utf8TextEncoder.encode('/hello/world'), records)
6060
).to.equal(
6161
0
6262
)

test/utils.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
'use strict'
2+
3+
const TextEncoder = require('ipfs-utils/src/text-encoder')
4+
const utf8TextEncoder = new TextEncoder('utf8')
5+
6+
module.exports.utf8TextEncoder = utf8TextEncoder

0 commit comments

Comments
 (0)