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

Commit fb36a86

Browse files
dirkmchugomrdias
authored andcommitted
fix: async/await improvements
Address comments on [parent PR](#37)
1 parent 3a6288b commit fb36a86

File tree

8 files changed

+139
-105
lines changed

8 files changed

+139
-105
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,8 @@ const mh = await multihashing(buf, 'sha1')
8989
const digest = await multihashing.digest(buf, 'sha1')
9090

9191
// Use `.createHash(...)` for the raw hash functions
92-
const h = multihashing.createHash('sha1')
93-
const digest = await h(buf)
92+
const hash = multihashing.createHash('sha1')
93+
const digest = await hash(buf)
9494
```
9595

9696
## Examples

package.json

+7-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
"browser": {
2121
"./src/sha.js": "./src/sha.browser.js"
2222
},
23+
"pre-push": [
24+
"lint",
25+
"test"
26+
],
2327
"scripts": {
2428
"test": "aegir test",
2529
"test:browser": "aegir test -t browser",
@@ -37,9 +41,11 @@
3741
"repository": "github:multiformats/js-multihashing-async",
3842
"dependencies": {
3943
"blakejs": "^1.1.0",
44+
"err-code": "^1.1.2",
4045
"js-sha3": "~0.8.0",
4146
"multihashes": "~0.4.13",
42-
"murmurhash3js-revisited": "^3.0.0"
47+
"murmurhash3js-revisited": "^3.0.0",
48+
"sinon": "^7.2.7"
4349
},
4450
"devDependencies": {
4551
"aegir": "^18.0.3",

src/blake.js

+7-10
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,13 @@ const blake2s = {
1717
digest: blake.blake2sFinal
1818
}
1919

20-
const makeB2Hash = (size, hf) => (data) => {
21-
return new Promise((resolve, reject) => {
22-
try {
23-
const ctx = hf.init(size, null)
24-
hf.update(ctx, data)
25-
resolve(Buffer.from(hf.digest(ctx)))
26-
} catch (error) {
27-
reject(error)
28-
}
29-
})
20+
// Note that although this function doesn't do any asynchronous work, we mark
21+
// the function as async because it must return a Promise to match the API
22+
// for other functions that do perform asynchronous work (see sha.browser.js)
23+
const makeB2Hash = (size, hf) => async (data) => {
24+
const ctx = hf.init(size, null)
25+
hf.update(ctx, data)
26+
return Buffer.from(hf.digest(ctx))
3027
}
3128

3229
module.exports = (table) => {

src/crypto.js

+32-35
Original file line numberDiff line numberDiff line change
@@ -5,42 +5,39 @@ const mur = require('murmurhash3js-revisited')
55
const sha = require('./sha')
66
const { fromNumberTo32BitBuf } = require('./utils')
77

8-
const hash = (algorithm) => (data) => {
9-
return new Promise((resolve, reject) => {
10-
try {
11-
switch (algorithm) {
12-
case 'sha3-224':
13-
return resolve(Buffer.from(sha3.sha3_224.arrayBuffer(data)))
14-
case 'sha3-256':
15-
return resolve(Buffer.from(sha3.sha3_256.arrayBuffer(data)))
16-
case 'sha3-384':
17-
return resolve(Buffer.from(sha3.sha3_384.arrayBuffer(data)))
18-
case 'sha3-512':
19-
return resolve(Buffer.from(sha3.sha3_512.arrayBuffer(data)))
20-
case 'shake-128':
21-
return resolve(Buffer.from(sha3.shake128.create(128).update(data).arrayBuffer()))
22-
case 'shake-256':
23-
return resolve(Buffer.from(sha3.shake256.create(256).update(data).arrayBuffer()))
24-
case 'keccak-224':
25-
return resolve(Buffer.from(sha3.keccak224.arrayBuffer(data)))
26-
case 'keccak-256':
27-
return resolve(Buffer.from(sha3.keccak256.arrayBuffer(data)))
28-
case 'keccak-384':
29-
return resolve(Buffer.from(sha3.keccak384.arrayBuffer(data)))
30-
case 'keccak-512':
31-
return resolve(Buffer.from(sha3.keccak512.arrayBuffer(data)))
32-
case 'murmur3-128':
33-
return resolve(Buffer.from(mur.x64.hash128(data), 'hex'))
34-
case 'murmur3-32':
35-
return resolve(fromNumberTo32BitBuf(mur.x86.hash32(data)))
8+
// Note that although this function doesn't do any asynchronous work, we mark
9+
// the function as async because it must return a Promise to match the API
10+
// for other functions that do perform asynchronous work (see sha.browser.js)
11+
const hash = (algorithm) => async (data) => {
12+
switch (algorithm) {
13+
case 'sha3-224':
14+
return Buffer.from(sha3.sha3_224.arrayBuffer(data))
15+
case 'sha3-256':
16+
return Buffer.from(sha3.sha3_256.arrayBuffer(data))
17+
case 'sha3-384':
18+
return Buffer.from(sha3.sha3_384.arrayBuffer(data))
19+
case 'sha3-512':
20+
return Buffer.from(sha3.sha3_512.arrayBuffer(data))
21+
case 'shake-128':
22+
return Buffer.from(sha3.shake128.create(128).update(data).arrayBuffer())
23+
case 'shake-256':
24+
return Buffer.from(sha3.shake256.create(256).update(data).arrayBuffer())
25+
case 'keccak-224':
26+
return Buffer.from(sha3.keccak224.arrayBuffer(data))
27+
case 'keccak-256':
28+
return Buffer.from(sha3.keccak256.arrayBuffer(data))
29+
case 'keccak-384':
30+
return Buffer.from(sha3.keccak384.arrayBuffer(data))
31+
case 'keccak-512':
32+
return Buffer.from(sha3.keccak512.arrayBuffer(data))
33+
case 'murmur3-128':
34+
return Buffer.from(mur.x64.hash128(data), 'hex')
35+
case 'murmur3-32':
36+
return fromNumberTo32BitBuf(mur.x86.hash32(data))
3637

37-
default:
38-
throw new TypeError(`${algorithm} is not a supported algorithm`)
39-
}
40-
} catch (error) {
41-
return reject(error)
42-
}
43-
})
38+
default:
39+
throw new TypeError(`${algorithm} is not a supported algorithm`)
40+
}
4441
}
4542

4643
module.exports = {

src/index.js

+24-28
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
'use strict'
22

3+
const errcode = require('err-code')
34
const multihash = require('multihashes')
45
const crypto = require('./crypto')
56

67
/**
7-
* Hash the given `buf` using the algorithm specified
8-
* by `func`.
8+
* Hash the given `buf` using the algorithm specified by `alg`.
99
* @param {Buffer} buf - The value to hash.
10-
* @param {number|string} func - The algorithm to use.
10+
* @param {number|string} alg - The algorithm to use eg 'sha1'
1111
* @param {number} [length] - Optionally trim the result to this length.
1212
* @returns {Promise<Buffer>}
1313
*/
14-
function Multihashing (buf, func, length) {
15-
return Multihashing.digest(buf, func, length)
16-
.then(digest => multihash.encode(digest, func, length))
14+
async function Multihashing (buf, alg, length) {
15+
const digest = await Multihashing.digest(buf, alg, length)
16+
return multihash.encode(digest, alg, length)
1717
}
1818

1919
/**
@@ -30,38 +30,34 @@ Multihashing.multihash = multihash
3030

3131
/**
3232
* @param {Buffer} buf - The value to hash.
33-
* @param {number|string} func - The algorithm to use.
33+
* @param {number|string} alg - The algorithm to use eg 'sha1'
3434
* @param {number} [length] - Optionally trim the result to this length.
35-
* @returns {Promise}
35+
* @returns {Promise<Buffer>}
3636
*/
37-
Multihashing.digest = (buf, func, length) => {
38-
try {
39-
return Multihashing.createHash(func)(buf)
40-
.then(digest => {
41-
if (length) {
42-
return digest.slice(0, length)
43-
}
44-
return digest
45-
})
46-
} catch (err) {
47-
return Promise.reject(err)
48-
}
37+
Multihashing.digest = async (buf, alg, length) => {
38+
const hash = Multihashing.createHash(alg)
39+
const digest = await hash(buf)
40+
return length ? digest.slice(0, length) : digest
4941
}
5042

5143
/**
52-
* Creates a function that hashs with the provided algorithm
44+
* Creates a function that hashes with the given algorithm
5345
*
54-
* @param {string|number} func
46+
* @param {string|number} alg - The algorithm to use eg 'sha1'
5547
*
56-
* @returns {function} - The to `func` corresponding hash function.
48+
* @returns {function} - The hash function corresponding to `alg`
5749
*/
58-
Multihashing.createHash = function (func) {
59-
func = multihash.coerceCode(func)
60-
if (!Multihashing.functions[func]) {
61-
throw new Error('multihash function ' + func + ' not yet supported')
50+
Multihashing.createHash = function (alg) {
51+
if (!alg) {
52+
throw errcode('hash algorithm must be specified', 'ERR_HASH_ALGORITHM_NOT_SPECIFIED')
53+
}
54+
55+
alg = multihash.coerceCode(alg)
56+
if (!Multihashing.functions[alg]) {
57+
throw errcode(`multihash function '${alg}' not yet supported`, 'ERR_HASH_ALGORITHM_NOT_SUPPORTED')
6258
}
6359

64-
return Multihashing.functions[func]
60+
return Multihashing.functions[alg]
6561
}
6662

6763
/**

src/sha.browser.js

+6-7
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,17 @@ module.exports = (algorithm) => {
88
)
99
}
1010

11-
return (data) => {
11+
return async (data) => {
1212
switch (algorithm) {
1313
case 'sha1':
14-
return crypto.subtle.digest({ name: 'SHA-1' }, data).then(Buffer.from)
14+
return Buffer.from(await crypto.subtle.digest({ name: 'SHA-1' }, data))
1515
case 'sha2-256':
16-
return crypto.subtle.digest({ name: 'SHA-256' }, data).then(Buffer.from)
16+
return Buffer.from(await crypto.subtle.digest({ name: 'SHA-256' }, data))
1717
case 'sha2-512':
18-
return crypto.subtle.digest({ name: 'SHA-512' }, data).then(Buffer.from)
18+
return Buffer.from(await crypto.subtle.digest({ name: 'SHA-512' }, data))
1919
case 'dbl-sha2-256': {
20-
return crypto.subtle.digest({ name: 'SHA-256' }, data)
21-
.then(d => crypto.subtle.digest({ name: 'SHA-256' }, d))
22-
.then(Buffer.from)
20+
const d = await crypto.subtle.digest({ name: 'SHA-256' }, data)
21+
return Buffer.from(await crypto.subtle.digest({ name: 'SHA-256' }, d))
2322
}
2423
default:
2524
throw new TypeError(`${algorithm} is not a supported algorithm`)

src/sha.js

+17-20
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,22 @@
11
'use strict'
22
const crypto = require('crypto')
33

4-
module.exports = (algorithm) => (data) => {
5-
return new Promise((resolve, reject) => {
6-
try {
7-
switch (algorithm) {
8-
case 'sha1':
9-
return resolve(crypto.createHash('sha1').update(data).digest())
10-
case 'sha2-256':
11-
return resolve(crypto.createHash('sha256').update(data).digest())
12-
case 'sha2-512':
13-
return resolve(crypto.createHash('sha512').update(data).digest())
14-
case 'dbl-sha2-256': {
15-
const first = crypto.createHash('sha256').update(data).digest()
16-
return resolve(crypto.createHash('sha256').update(first).digest())
17-
}
18-
default:
19-
throw new TypeError(`${algorithm} is not a supported algorithm`)
20-
}
21-
} catch (error) {
22-
return reject(error)
4+
// Note that although this function doesn't do any asynchronous work, we mark
5+
// the function as async because it must return a Promise to match the API
6+
// for other functions that do perform asynchronous work (see sha.browser.js)
7+
module.exports = (algorithm) => async (data) => {
8+
switch (algorithm) {
9+
case 'sha1':
10+
return crypto.createHash('sha1').update(data).digest()
11+
case 'sha2-256':
12+
return crypto.createHash('sha256').update(data).digest()
13+
case 'sha2-512':
14+
return crypto.createHash('sha512').update(data).digest()
15+
case 'dbl-sha2-256': {
16+
const first = crypto.createHash('sha256').update(data).digest()
17+
return crypto.createHash('sha256').update(first).digest()
2318
}
24-
})
19+
default:
20+
throw new TypeError(`${algorithm} is not a supported algorithm`)
21+
}
2522
}

test/index.spec.js

+44-2
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ const chai = require('chai')
55
const dirtyChai = require('dirty-chai')
66
chai.use(dirtyChai)
77
const expect = chai.expect
8+
const sinon = require('sinon')
89

910
const multihashing = require('../src')
1011
const fixtures = require('./fixtures/encodes')
1112

1213
describe('multihashing', () => {
13-
fixtures.forEach((fixture) => {
14+
for (const fixture of fixtures) {
1415
const raw = fixture[0]
1516
const func = fixture[1]
1617
const encoded = fixture[2]
@@ -19,7 +20,7 @@ describe('multihashing', () => {
1920
const digest = await multihashing(Buffer.from(raw), func)
2021
expect(digest.toString('hex')).to.eql(encoded)
2122
})
22-
})
23+
}
2324

2425
it('cuts the length', async () => {
2526
const buf = Buffer.from('beep boop')
@@ -40,3 +41,44 @@ describe('multihashing', () => {
4041
)
4142
})
4243
})
44+
45+
describe('error handling', () => {
46+
const methods = {
47+
multihashing: multihashing,
48+
digest: multihashing.digest,
49+
createHash: (buff, alg) => multihashing.createHash(alg)
50+
}
51+
52+
for (const [name, fn] of Object.entries(methods)) {
53+
describe(name, () => {
54+
it('throws an error when there is no hashing algorithm specified', async () => {
55+
const buf = Buffer.from('beep boop')
56+
57+
try {
58+
await fn(buf)
59+
} catch (err) {
60+
expect(err).to.exist()
61+
expect(err.code).to.eql('ERR_HASH_ALGORITHM_NOT_SPECIFIED')
62+
return
63+
}
64+
expect.fail('Did not throw')
65+
})
66+
67+
it('throws an error when the hashing algorithm is not supported', async () => {
68+
const buf = Buffer.from('beep boop')
69+
70+
const stub = sinon.stub(require('multihashes'), 'coerceCode').returns('snake-oil')
71+
try {
72+
await fn(buf, 'snake-oil')
73+
} catch (err) {
74+
expect(err).to.exist()
75+
expect(err.code).to.eql('ERR_HASH_ALGORITHM_NOT_SUPPORTED')
76+
return
77+
} finally {
78+
stub.restore()
79+
}
80+
expect.fail('Did not throw')
81+
})
82+
})
83+
}
84+
})

0 commit comments

Comments
 (0)