From dd3bc72f4475d187e484d9da539c99dfb28248e3 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Fri, 15 Feb 2019 13:22:14 -0500 Subject: [PATCH 1/6] chore: convert from callbacks to async BREAKING CHANGE: All places in the API that used callbacks are now replaced with async/await --- README.md | 53 ++++---- src/index.js | 203 ++++++++++++++---------------- test/index.spec.js | 308 +++++++++++++++------------------------------ 3 files changed, 217 insertions(+), 347 deletions(-) diff --git a/README.md b/README.md index 2825d16..fd4e124 100644 --- a/README.md +++ b/README.md @@ -38,9 +38,8 @@ This module contains all the necessary code for creating, understanding and vali ```js const ipns = require('ipns') -ipns.create(privateKey, value, sequenceNumber, lifetime, (err, entryData) => { - // your code goes here -}) +const entryData = await ipns.create(privateKey, value, sequenceNumber, lifetime) +// your code goes here ``` #### Validate record @@ -48,10 +47,9 @@ ipns.create(privateKey, value, sequenceNumber, lifetime, (err, entryData) => { ```js const ipns = require('ipns') -ipns.validate(publicKey, ipnsEntry, (err) => { - // your code goes here - // if no error, the record is valid -}) +await ipns.validate(publicKey, ipnsEntry) +// your code goes here +// if no error thrown, the record is valid ``` #### Embed public key to record @@ -59,9 +57,8 @@ ipns.validate(publicKey, ipnsEntry, (err) => { ```js const ipns = require('ipns') -ipns.embedPublicKey(publicKey, ipnsEntry, (err, ipnsEntryWithEmbedPublicKey) => { - // your code goes here -}) +const ipnsEntryWithEmbedPublicKey = await ipns.embedPublicKey(publicKey, ipnsEntry) +// your code goes here ``` #### Extract public key from record @@ -69,9 +66,8 @@ ipns.embedPublicKey(publicKey, ipnsEntry, (err, ipnsEntryWithEmbedPublicKey) => ```js const ipns = require('ipns') -ipns.extractPublicKey(peerId, ipnsEntry, (err, publicKey) => { - // your code goes here -}) +const publicKey = ipns.extractPublicKey(peerId, ipnsEntry) +// your code goes here ``` #### Datastore key @@ -93,11 +89,10 @@ Returns a key to be used for storing the ipns entry locally, that is: ```js const ipns = require('ipns') -ipns.create(privateKey, value, sequenceNumber, lifetime, (err, entryData) => { - // ... - const marshalledData = ipns.marshal(entryData) - // ... -}) +const entryData = ipns.create(privateKey, value, sequenceNumber, lifetime) +// ... +const marshalledData = ipns.marshal(entryData) +// ... ``` Returns the entry data serialized. @@ -120,11 +115,11 @@ const ipns = require('ipns') const validator = ipns.validator ``` -Contains an object with `validate (marshalledData, key, callback)` and `select (dataA, dataB, [callback])` functions. +Contains an object with `validate (marshalledData, key)` and `select (dataA, dataB)` functions. 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). -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`. If a callback is not provided, the response is returned. +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`. ## API @@ -132,7 +127,7 @@ The `select` function is responsible for deciding which ipns record is the best ```js -ipns.create(privateKey, value, sequenceNumber, lifetime, [callback]) +ipns.create(privateKey, value, sequenceNumber, lifetime) ``` Create an IPNS record for being stored in a protocol buffer. @@ -141,9 +136,8 @@ Create an IPNS record for being stored in a protocol buffer. - `value` (string): ipfs path of the object to be published. - `sequenceNumber` (Number): number representing the current version of the record. - `lifetime` (string): lifetime of the record (in milliseconds). -- `callback` (function): operation result. -`callback` must follow `function (err, ipnsEntry) {}` signature, where `err` is an error if the operation was not successful. `ipnsEntry` is an object that contains the entry's properties, such as: +Returns a `Promise` that resolves to an object with the entry's properties eg: ```js { @@ -159,16 +153,15 @@ Create an IPNS record for being stored in a protocol buffer. ```js -ipns.validate(publicKey, ipnsEntry, [callback]) +ipns.validate(publicKey, ipnsEntry) ``` Validate an IPNS record previously stored in a protocol buffer. - `publicKey` (`PubKey` [RSA Instance](https://github.com/libp2p/js-libp2p-crypto/blob/master/src/keys/rsa-class.js)): key to be used for cryptographic operations. - `ipnsEntry` (Object): ipns entry record (obtained using the create function). -- `callback` (function): operation result. -`callback` must follow `function (err) {}` signature, where `err` is an error if the operation was not successful. This way, if no error, the validation was successful. +Throws an error if the validation was not successful. #### Datastore key @@ -203,16 +196,15 @@ Returns the entry data structure after being serialized. #### Embed public key to record ```js -ipns.embedPublicKey(publicKey, ipnsEntry, [callback]) +ipns.embedPublicKey(publicKey, ipnsEntry) ``` Embed a public key in an IPNS entry. If it is possible to extract the public key from the `peer-id`, there is no need to embed. - `publicKey` (`PubKey` [RSA Instance](https://github.com/libp2p/js-libp2p-crypto/blob/master/src/keys/rsa-class.js)): key to be used for cryptographic operations. - `ipnsEntry` (Object): ipns entry record (obtained using the create function). -- `callback` (function): operation result. -`callback` must follow `function (err, resultEntry) {}` signature, where `err` is an error if the operation was not successful. This way, if no error, the operation was successful. If the `resultEntry` is also null, the `peer-id` allows to extract the public key from the `peer-id` and there is no need in extracting it. + Returns a `Promise`. If the promise resolves to null it means the public key can be extracted directly from the `peer-id`. #### Extract public key from record @@ -224,9 +216,8 @@ Extract a public key from an IPNS entry. - `peerId` (`PeerId` [Instance](https://github.com/libp2p/js-peer-id)): peer identifier object. - `ipnsEntry` (Object): ipns entry record (obtained using the create function). -- `callback` (function): operation result. -`callback` must follow `function (err, publicKey) {}` signature, where `err` is an error if the operation was not successful. This way, if no error, the validation was successful. The public key (`PubKey` [RSA Instance](https://github.com/libp2p/js-libp2p-crypto/blob/master/src/keys/rsa-class.js)): may be used for cryptographic operations. +The returned public key (`PubKey` [RSA Instance](https://github.com/libp2p/js-libp2p-crypto/blob/master/src/keys/rsa-class.js)): may be used for cryptographic operations. #### Namespace diff --git a/src/index.js b/src/index.js index a7f265e..3c4962c 100644 --- a/src/index.js +++ b/src/index.js @@ -28,13 +28,13 @@ const namespace = '/ipns/' * @param {string} value value to be stored in the record. * @param {number} seq number representing the current version of the record. * @param {number|string} lifetime lifetime of the record (in milliseconds). - * @param {function(Error, entry)} [callback] + * @returns {Object} entry */ -const create = (privateKey, value, seq, lifetime, callback) => { +const create = (privateKey, value, seq, lifetime) => { // Validity in ISOString with nanoseconds precision and validity type EOL const isoValidity = new NanoDate(Date.now() + Number(lifetime)).toString() const validityType = ipnsEntryProto.ValidityType.EOL - _create(privateKey, value, seq, isoValidity, validityType, callback) + return _create(privateKey, value, seq, isoValidity, validityType) } /** @@ -44,19 +44,16 @@ const create = (privateKey, value, seq, lifetime, callback) => { * @param {string} value value to be stored in the record. * @param {number} seq number representing the current version of the record. * @param {string} expiration expiration datetime for record in the [RFC3339]{@link https://www.ietf.org/rfc/rfc3339.txt} with nanoseconds precision. - * @param {function(Error, entry)} [callback] + * @returns {Object} entry */ -const createWithExpiration = (privateKey, value, seq, expiration, callback) => { +const createWithExpiration = (privateKey, value, seq, expiration) => { const validityType = ipnsEntryProto.ValidityType.EOL - _create(privateKey, value, seq, expiration, validityType, callback) + return _create(privateKey, value, seq, expiration, validityType) } -const _create = (privateKey, value, seq, isoValidity, validityType, callback) => { - sign(privateKey, value, validityType, isoValidity, (error, signature) => { - if (error) { - log.error('record signature creation failed') - return callback(Object.assign(new Error('record signature verification failed'), { code: ERRORS.ERR_SIGNATURE_CREATION })) - } +const _create = async (privateKey, value, seq, isoValidity, validityType) => { + try { + const signature = await sign(privateKey, value, validityType, isoValidity) const entry = { value: value, @@ -67,8 +64,11 @@ const _create = (privateKey, value, seq, isoValidity, validityType, callback) => } log(`ipns entry for ${value} created`) - return callback(null, entry) - }) + return entry + } catch (error) { + log.error('record signature creation failed') + throw errorWithCode('record signature verification failed', ERRORS.ERR_SIGNATURE_CREATION) + } } /** @@ -76,41 +76,43 @@ const _create = (privateKey, value, seq, isoValidity, validityType, callback) => * * @param {Object} publicKey public key for validating the record. * @param {Object} entry ipns entry record. - * @param {function(Error)} [callback] + * @returns {Promise} the promise will reject if the entry is invalid. */ -const validate = (publicKey, entry, callback) => { +const validate = (publicKey, entry) => { const { value, validityType, validity } = entry const dataForSignature = ipnsEntryDataForSig(value, validityType, validity) - // Validate Signature - publicKey.verify(dataForSignature, entry.signature, (err, isValid) => { - if (err || !isValid) { - log.error('record signature verification failed') - return callback(Object.assign(new Error('record signature verification failed'), { code: ERRORS.ERR_SIGNATURE_VERIFICATION })) - } + return new Promise((resolve, reject) => { + // Validate Signature + publicKey.verify(dataForSignature, entry.signature, (err, isValid) => { + if (err || !isValid) { + log.error('record signature verification failed') + return reject(errorWithCode('record signature verification failed', ERRORS.ERR_SIGNATURE_VERIFICATION)) + } - // Validate according to the validity type - if (validityType === ipnsEntryProto.ValidityType.EOL) { - let validityDate + // Validate according to the validity type + if (validityType === ipnsEntryProto.ValidityType.EOL) { + let validityDate - try { - validityDate = parseRFC3339(validity.toString()) - } catch (e) { - log.error('unrecognized validity format (not an rfc3339 format)') - return callback(Object.assign(new Error('unrecognized validity format (not an rfc3339 format)'), { code: ERRORS.ERR_UNRECOGNIZED_FORMAT })) - } + try { + validityDate = parseRFC3339(validity.toString()) + } catch (e) { + log.error('unrecognized validity format (not an rfc3339 format)') + return reject(errorWithCode('unrecognized validity format (not an rfc3339 format)', ERRORS.ERR_UNRECOGNIZED_FORMAT)) + } - if (validityDate < Date.now()) { - log.error('record has expired') - return callback(Object.assign(new Error('record has expired'), { code: ERRORS.ERR_IPNS_EXPIRED_RECORD })) + if (validityDate < Date.now()) { + log.error('record has expired') + return reject(errorWithCode('record has expired', ERRORS.ERR_IPNS_EXPIRED_RECORD)) + } + } else if (validityType) { + log.error('unrecognized validity type') + return reject(errorWithCode('unrecognized validity type', ERRORS.ERR_UNRECOGNIZED_VALIDITY)) } - } else if (validityType) { - log.error('unrecognized validity type') - return callback(Object.assign(new Error('unrecognized validity type'), { code: ERRORS.ERR_UNRECOGNIZED_VALIDITY })) - } - log(`ipns entry for ${value} is valid`) - return callback(null, null) + log(`ipns entry for ${value} is valid`) + resolve() + }) }) } @@ -125,45 +127,46 @@ const validate = (publicKey, entry, callback) => { * * @param {Object} publicKey public key to embed. * @param {Object} entry ipns entry record. - * @param {function(Error)} [callback] - * @return {Void} + * @return {Object} entry with public key embedded */ -const embedPublicKey = (publicKey, entry, callback) => { +const embedPublicKey = async (publicKey, entry) => { if (!publicKey || !publicKey.bytes || !entry) { const error = 'one or more of the provided parameters are not defined' log.error(error) - return callback(Object.assign(new Error(error), { code: ERRORS.ERR_UNDEFINED_PARAMETER })) + throw Object.assign(new Error(error), { code: ERRORS.ERR_UNDEFINED_PARAMETER }) } - // Create a peer id from the public key. - PeerId.createFromPubKey(publicKey.bytes, (err, peerId) => { - if (err) { - log.error(err) - return callback(Object.assign(new Error(err), { code: ERRORS.ERR_PEER_ID_FROM_PUBLIC_KEY })) - } + return new Promise((resolve, reject) => { + // Create a peer id from the public key. + PeerId.createFromPubKey(publicKey.bytes, (err, peerId) => { + if (err) { + log.error(err) + reject(Object.assign(new Error(err), { code: ERRORS.ERR_PEER_ID_FROM_PUBLIC_KEY })) + } - // Try to extract the public key from the ID. If we can, no need to embed it - let extractedPublicKey - try { - extractedPublicKey = extractPublicKeyFromId(peerId) - } catch (err) { - log.error(err) - return callback(Object.assign(new Error(err), { code: ERRORS.ERR_PUBLIC_KEY_FROM_ID })) - } + // Try to extract the public key from the ID. If we can, no need to embed it + let extractedPublicKey + try { + extractedPublicKey = extractPublicKeyFromId(peerId) + } catch (err) { + log.error(err) + reject(Object.assign(new Error(err), { code: ERRORS.ERR_PUBLIC_KEY_FROM_ID })) + } - if (extractedPublicKey) { - return callback(null, null) - } + if (extractedPublicKey) { + return resolve(null) + } - // If we failed to extract the public key from the peer ID, embed it in the record. - try { - entry.pubKey = crypto.keys.marshalPublicKey(publicKey) - } catch (err) { - log.error(err) - return callback(err) - } - callback(null, entry) + // If we failed to extract the public key from the peer ID, embed it in the record. + try { + entry.pubKey = crypto.keys.marshalPublicKey(publicKey) + } catch (err) { + log.error(err) + reject(err) + } + resolve(entry) + }) }) } @@ -172,15 +175,14 @@ const embedPublicKey = (publicKey, entry, callback) => { * * @param {Object} peerId peer identifier object. * @param {Object} entry ipns entry record. - * @param {function(Error)} [callback] - * @return {Void} + * @returns {Object} the public key */ -const extractPublicKey = (peerId, entry, callback) => { +const extractPublicKey = (peerId, entry) => { if (!entry || !peerId) { const error = 'one or more of the provided parameters are not defined' log.error(error) - return callback(Object.assign(new Error(error), { code: ERRORS.ERR_UNDEFINED_PARAMETER })) + throw Object.assign(new Error(error), { code: ERRORS.ERR_UNDEFINED_PARAMETER }) } if (entry.pubKey) { @@ -189,16 +191,15 @@ const extractPublicKey = (peerId, entry, callback) => { pubKey = crypto.keys.unmarshalPublicKey(entry.pubKey) } catch (err) { log.error(err) - return callback(err) + throw err } - return callback(null, pubKey) + return pubKey } if (peerId.pubKey) { - callback(null, peerId.pubKey) - } else { - callback(Object.assign(new Error('no public key is available'), { code: ERRORS.ERR_UNDEFINED_PARAMETER })) + return peerId.pubKey } + throw Object.assign(new Error('no public key is available'), { code: ERRORS.ERR_UNDEFINED_PARAMETER }) } // rawStdEncoding with RFC4648 @@ -233,14 +234,11 @@ const getIdKeys = (pid) => { } // Sign ipns record data -const sign = (privateKey, value, validityType, validity, callback) => { +const sign = (privateKey, value, validityType, validity) => { const dataForSignature = ipnsEntryDataForSig(value, validityType, validity) - privateKey.sign(dataForSignature, (err, signature) => { - if (err) { - return callback(err) - } - return callback(null, signature) + return new Promise((resolve, reject) => { + privateKey.sign(dataForSignature, (err, signature) => err ? reject(err) : resolve(signature)) }) } @@ -275,49 +273,32 @@ const extractPublicKeyFromId = (peerId) => { return crypto.keys.unmarshalPublicKey(decodedId.digest) } +const errorWithCode = (err, code) => Object.assign(new Error(err), { code }) + const marshal = ipnsEntryProto.encode const unmarshal = ipnsEntryProto.decode const validator = { - validate: (marshalledData, key, callback) => { + validate: async (marshalledData, key) => { const receivedEntry = unmarshal(marshalledData) const bufferId = key.slice('/ipns/'.length) let peerId - try { - peerId = PeerId.createFromBytes(bufferId) - } catch (err) { - return callback(err) - } + peerId = PeerId.createFromBytes(bufferId) // extract public key - extractPublicKey(peerId, receivedEntry, (err, pubKey) => { - if (err) { - return callback(err) - } + const pubKey = extractPublicKey(peerId, receivedEntry) - // Record validation - validate(pubKey, receivedEntry, (err) => { - if (err) { - return callback(err) - } - - callback(null, true) - }) - }) + // Record validation + await validate(pubKey, receivedEntry) + return true }, - select: (dataA, dataB, callback) => { + select: (dataA, dataB) => { const entryA = unmarshal(dataA) const entryB = unmarshal(dataB) - const index = entryA.sequence > entryB.sequence ? 0 : 1 - - if (typeof callback !== 'function') { - return index - } - - callback(null, index) + return entryA.sequence > entryB.sequence ? 0 : 1 } } diff --git a/test/index.spec.js b/test/index.spec.js index 8229d96..4f06e53 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -64,115 +64,92 @@ describe('ipns', function () { } }) - it('should create an ipns record correctly', (done) => { + it('should create an ipns record correctly', async () => { const sequence = 0 const validity = 1000000 - ipns.create(rsa, cid, sequence, validity, (err, entry) => { - expect(err).to.not.exist() - expect(entry).to.deep.include({ - value: cid, - sequence: sequence - }) - expect(entry).to.have.a.property('validity') - expect(entry).to.have.a.property('signature') - expect(entry).to.have.a.property('validityType') - - done() + const entry = await ipns.create(rsa, cid, sequence, validity) + expect(entry).to.deep.include({ + value: cid, + sequence: sequence }) + expect(entry).to.have.a.property('validity') + expect(entry).to.have.a.property('signature') + expect(entry).to.have.a.property('validityType') }) - it('should be able to create a record with a fixed expiration', (done) => { + it('should be able to create a record with a fixed expiration', async () => { const sequence = 0 // 2033-05-18T03:33:20.000000000Z const expiration = '2033-05-18T03:33:20.000000000Z' - ipns.createWithExpiration(rsa, cid, sequence, expiration, (err, entry) => { - expect(err).to.not.exist() + const entry = await ipns.createWithExpiration(rsa, cid, sequence, expiration) - ipns.validate(rsa.public, entry, (err) => { - expect(err).to.not.exist() - expect(entry).to.have.a.property('validity') - expect(entry.validity).to.equal('2033-05-18T03:33:20.000000000Z') - done() - }) - }) + await ipns.validate(rsa.public, entry) + expect(entry).to.have.a.property('validity') + expect(entry.validity).to.equal('2033-05-18T03:33:20.000000000Z') }) - it('should create an ipns record and validate it correctly', (done) => { + it('should create an ipns record and validate it correctly', async () => { const sequence = 0 const validity = 1000000 - ipns.create(rsa, cid, sequence, validity, (err, entry) => { - expect(err).to.not.exist() - - ipns.validate(rsa.public, entry, (err) => { - expect(err).to.not.exist() - - done() - }) - }) + const entry = await ipns.create(rsa, cid, sequence, validity) + return ipns.validate(rsa.public, entry) }) - it('should fail to validate a bad record', (done) => { + it('should fail to validate a bad record', async () => { const sequence = 0 const validity = 1000000 - ipns.create(rsa, cid, sequence, validity, (err, entry) => { - expect(err).to.not.exist() - - // corrupt the record by changing the value to random bytes - entry.value = crypto.randomBytes(46).toString() + const entry = await ipns.create(rsa, cid, sequence, validity) - ipns.validate(rsa.public, entry, (err) => { - expect(err).to.exist() - expect(err).to.include({ - code: ERRORS.ERR_SIGNATURE_VERIFICATION - }) + // corrupt the record by changing the value to random bytes + entry.value = crypto.randomBytes(46).toString() - done() + try { + await ipns.validate(rsa.public, entry) + } catch (err) { + expect(err).to.exist() + expect(err).to.include({ + code: ERRORS.ERR_SIGNATURE_VERIFICATION }) - }) + } }) - it('should create an ipns record with a validity of 1 nanosecond correctly and it should not be valid 1ms later', (done) => { + it('should create an ipns record with a validity of 1 nanosecond correctly and it should not be valid 1ms later', async () => { const sequence = 0 const validity = 0.00001 - ipns.create(rsa, cid, sequence, validity, (err, entry) => { - expect(err).to.not.exist() + const entry = await ipns.create(rsa, cid, sequence, validity) - setTimeout(() => { - ipns.validate(rsa.public, entry, (err, res) => { - expect(err).to.exist() - done() - }) - }, 1) - }) + await new Promise(resolve => setTimeout(resolve, 1)) + try { + await ipns.validate(rsa.public, entry) + } catch (err) { + expect(err).to.exist() + expect(err).to.include({ + code: ERRORS.ERR_IPNS_EXPIRED_RECORD + }) + } }) - it('should create an ipns record, marshal and unmarshal it, as well as validate it correctly', (done) => { + it('should create an ipns record, marshal and unmarshal it, as well as validate it correctly', async () => { const sequence = 0 const validity = 1000000 - ipns.create(rsa, cid, sequence, validity, (err, entryDataCreated) => { - expect(err).to.not.exist() + const entryDataCreated = await ipns.create(rsa, cid, sequence, validity) - const marshalledData = ipns.marshal(entryDataCreated) - const unmarshalledData = ipns.unmarshal(marshalledData) + const marshalledData = ipns.marshal(entryDataCreated) + const unmarshalledData = ipns.unmarshal(marshalledData) - expect(entryDataCreated.value).to.equal(unmarshalledData.value.toString()) - expect(entryDataCreated.validity).to.equal(unmarshalledData.validity.toString()) - expect(entryDataCreated.validityType).to.equal(unmarshalledData.validityType) - expect(entryDataCreated.signature).to.equalBytes(unmarshalledData.signature) - expect(entryDataCreated.sequence).to.equal(unmarshalledData.sequence) + expect(entryDataCreated.value).to.equal(unmarshalledData.value.toString()) + expect(entryDataCreated.validity).to.equal(unmarshalledData.validity.toString()) + expect(entryDataCreated.validityType).to.equal(unmarshalledData.validityType) + expect(entryDataCreated.signature).to.equalBytes(unmarshalledData.signature) + expect(entryDataCreated.sequence).to.equal(unmarshalledData.sequence) - ipns.validate(rsa.public, unmarshalledData, (err, res) => { - expect(err).to.not.exist() - - done() - }) - }) + return ipns.validate(rsa.public, unmarshalledData) }) it('should get datastore key correctly', () => { @@ -195,20 +172,14 @@ describe('ipns', function () { expect(idKeys.routingKey).to.not.startsWith('/ipns/') }) - it('should be able to embed a public key in an ipns record', (done) => { + it('should be able to embed a public key in an ipns record', async () => { const sequence = 0 const validity = 1000000 - ipns.create(rsa, cid, sequence, validity, (err, entry) => { - expect(err).to.not.exist() - - ipns.embedPublicKey(rsa.public, entry, (err, entry) => { - expect(err).to.not.exist() - expect(entry).to.deep.include({ - pubKey: rsa.public.bytes - }) - done() - }) + const entry = await ipns.create(rsa, cid, sequence, validity) + const entryWithKey = await ipns.embedPublicKey(rsa.public, entry) + expect(entryWithKey).to.deep.include({ + pubKey: rsa.public.bytes }) }) @@ -222,166 +193,93 @@ describe('ipns', function () { const sequence = 0 const validity = 1000000 - crypto.keys.generateKeyPair('ed25519', 2048, (err, ed25519) => { - expect(err).to.not.exist() - - ipns.create(ed25519, cid, sequence, validity, (err, entry) => { - expect(err).to.not.exist() - - ipns.embedPublicKey(ed25519.public, entry, (err, entry) => { - expect(err).to.not.exist() - expect(entry).to.not.exist() // Should be null - done() - }) - }) - }) - }) - - it('should be able to export a previously embed public key from an ipns record', (done) => { - const sequence = 0 - const validity = 1000000 - - ipns.create(rsa, cid, sequence, validity, (err, entry) => { - expect(err).to.not.exist() - - ipns.embedPublicKey(rsa.public, entry, (err, entry) => { - expect(err).to.not.exist() - - ipns.extractPublicKey(ipfsId, entry, (err, publicKey) => { - expect(err).to.not.exist() - expect(publicKey.bytes).to.equalBytes(rsa.public.bytes) - done() - }) - }) - }) - }) - - it('validator with no valid public key should error', (done) => { - const sequence = 0 - const validity = 1000000 - - ipns.create(rsa, cid, sequence, validity, (err, entry) => { + crypto.keys.generateKeyPair('ed25519', 2048, async (err, ed25519) => { expect(err).to.not.exist() - const marshalledData = ipns.marshal(entry) - const key = Buffer.from(`/ipns/${ipfsId.id}`) - - ipns.validator.validate(marshalledData, key, (err, valid) => { - expect(err).to.exist() - expect(err.code).to.eql(ERRORS.ERR_UNDEFINED_PARAMETER) - expect(valid).to.not.exist() - done() - }) + const entry = await ipns.create(ed25519, cid, sequence, validity) + const entryWithKey = ipns.embedPublicKey(ed25519.public, entry) + expect(entryWithKey).to.not.exist() // Should be null }) }) - it('should use validator.validate to validate a record', (done) => { + it('validator with no valid public key should error', async () => { const sequence = 0 const validity = 1000000 - ipns.create(rsa, cid, sequence, validity, (err, entry) => { - expect(err).to.not.exist() + const entry = await ipns.create(rsa, cid, sequence, validity) - ipns.embedPublicKey(rsa.public, entry, (err, entry) => { - expect(err).to.not.exist() + const marshalledData = ipns.marshal(entry) + const key = Buffer.from(`/ipns/${ipfsId.id}`) - const marshalledData = ipns.marshal(entry) - const key = Buffer.from(`/ipns/${ipfsId.id}`) - - ipns.validator.validate(marshalledData, key, (err, valid) => { - expect(err).to.not.exist() - expect(valid).to.equal(true) - done() - }) - }) - }) + try { + await ipns.validator.validate(marshalledData, key) + } catch (err) { + expect(err.code).to.eql(ERRORS.ERR_UNDEFINED_PARAMETER) + return + } + expect.fail('Expected ERR_UNDEFINED_PARAMETER') }) - it('should use validator.validate to verify that a record is not valid', (done) => { + it('should be able to export a previously embed public key from an ipns record', async () => { const sequence = 0 const validity = 1000000 - ipns.create(rsa, cid, sequence, validity, (err, entry) => { - expect(err).to.not.exist() - - ipns.embedPublicKey(rsa.public, entry, (err, entry) => { - expect(err).to.not.exist() - - // corrupt the record by changing the value to random bytes - entry.value = crypto.randomBytes(46).toString() - const marshalledData = ipns.marshal(entry) - const key = Buffer.from(`/ipns/${ipfsId.id}`) - - ipns.validator.validate(marshalledData, key, (err) => { - expect(err).to.exist() // failed validation - done() - }) - }) - }) + const entry = await ipns.create(rsa, cid, sequence, validity) + await ipns.embedPublicKey(rsa.public, entry) + const publicKey = ipns.extractPublicKey(ipfsId, entry) + expect(publicKey.bytes).to.equalBytes(rsa.public.bytes) }) - it('should use validator.select to select the first record because it is newer', (done) => { + it('should use validator.validate to validate a record', async () => { const sequence = 0 const validity = 1000000 - ipns.create(rsa, cid, sequence, validity, (err, entry) => { - expect(err).to.not.exist() - - ipns.create(rsa, cid, (sequence + 1), validity, (err, newEntry) => { - expect(err).to.not.exist() + const entry = await ipns.create(rsa, cid, sequence, validity) + await ipns.embedPublicKey(rsa.public, entry) - const marshalledData = ipns.marshal(entry) - const marshalledNewData = ipns.marshal(newEntry) + const marshalledData = ipns.marshal(entry) + const key = Buffer.from(`/ipns/${ipfsId.id}`) - ipns.validator.select(marshalledNewData, marshalledData, (err, valid) => { - expect(err).to.not.exist() - expect(valid).to.equal(0) // new data is the selected one - done() - }) - }) - }) + const valid = await ipns.validator.validate(marshalledData, key) + expect(valid).to.equal(true) }) - it('should use validator.select to select the first record because it is newer without using callback', (done) => { + it('should use validator.validate to verify that a record is not valid', async () => { const sequence = 0 const validity = 1000000 - ipns.create(rsa, cid, sequence, validity, (err, entry) => { - expect(err).to.not.exist() - - ipns.create(rsa, cid, (sequence + 1), validity, (err, newEntry) => { - expect(err).to.not.exist() + const entry = await ipns.create(rsa, cid, sequence, validity) + await ipns.embedPublicKey(rsa.public, entry) - const marshalledData = ipns.marshal(entry) - const marshalledNewData = ipns.marshal(newEntry) + // corrupt the record by changing the value to random bytes + entry.value = crypto.randomBytes(46).toString() + const marshalledData = ipns.marshal(entry) + const key = Buffer.from(`/ipns/${ipfsId.id}`) - const valid = ipns.validator.select(marshalledNewData, marshalledData) - - expect(valid).to.equal(0) // new data is the selected one - done() + try { + await ipns.validator.validate(marshalledData, key) + } catch (err) { + expect(err).to.exist() + expect(err).to.include({ + code: ERRORS.ERR_SIGNATURE_VERIFICATION }) - }) + } }) - it('should use validator.select to select the second record because it is newer', (done) => { + it('should use validator.select to select the record with the highest sequence number', async () => { const sequence = 0 const validity = 1000000 - ipns.create(rsa, cid, sequence + 1, validity, (err, entry) => { - expect(err).to.not.exist() + const entry = await ipns.create(rsa, cid, sequence, validity) + const newEntry = await ipns.create(rsa, cid, (sequence + 1), validity) - ipns.create(rsa, cid, (sequence), validity, (err, newEntry) => { - expect(err).to.not.exist() + const marshalledData = ipns.marshal(entry) + const marshalledNewData = ipns.marshal(newEntry) - const marshalledData = ipns.marshal(entry) - const marshalledNewData = ipns.marshal(newEntry) + let valid = ipns.validator.select(marshalledNewData, marshalledData) + expect(valid).to.equal(0) // new data is the selected one - ipns.validator.select(marshalledNewData, marshalledData, (err, valid) => { - expect(err).to.not.exist() - expect(valid).to.equal(1) // old data is the selected one - done() - }) - }) - }) + valid = ipns.validator.select(marshalledData, marshalledNewData) + expect(valid).to.equal(1) // new data is the selected one }) }) From 061955b1cdb3fcd22f98a897be9ed6069916ba3f Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Mon, 18 Feb 2019 12:04:27 -0500 Subject: [PATCH 2/6] chore: depend on async branch of crypto / peer-id --- package.json | 3 +- src/index.js | 142 ++++++++++++++++++++++----------------------- test/index.spec.js | 10 +--- 3 files changed, 73 insertions(+), 82 deletions(-) diff --git a/package.json b/package.json index 62d2b1e..2d4fc71 100644 --- a/package.json +++ b/package.json @@ -36,9 +36,10 @@ "base32-encode": "^1.1.0", "debug": "^4.1.1", "interface-datastore": "~0.6.0", + "left-pad": "^1.3.0", "libp2p-crypto": "~0.16.0", "multihashes": "~0.4.14", - "peer-id": "~0.12.2", + "peer-id": "libp2p/js-peer-id#feat/async-await", "protons": "^1.0.1", "timestamp-nano": "^1.0.0" }, diff --git a/src/index.js b/src/index.js index 3c4962c..beacc59 100644 --- a/src/index.js +++ b/src/index.js @@ -76,44 +76,44 @@ const _create = async (privateKey, value, seq, isoValidity, validityType) => { * * @param {Object} publicKey public key for validating the record. * @param {Object} entry ipns entry record. - * @returns {Promise} the promise will reject if the entry is invalid. */ -const validate = (publicKey, entry) => { +const validate = async (publicKey, entry) => { const { value, validityType, validity } = entry const dataForSignature = ipnsEntryDataForSig(value, validityType, validity) - return new Promise((resolve, reject) => { - // Validate Signature - publicKey.verify(dataForSignature, entry.signature, (err, isValid) => { - if (err || !isValid) { - log.error('record signature verification failed') - return reject(errorWithCode('record signature verification failed', ERRORS.ERR_SIGNATURE_VERIFICATION)) - } - - // Validate according to the validity type - if (validityType === ipnsEntryProto.ValidityType.EOL) { - let validityDate - - try { - validityDate = parseRFC3339(validity.toString()) - } catch (e) { - log.error('unrecognized validity format (not an rfc3339 format)') - return reject(errorWithCode('unrecognized validity format (not an rfc3339 format)', ERRORS.ERR_UNRECOGNIZED_FORMAT)) - } - - if (validityDate < Date.now()) { - log.error('record has expired') - return reject(errorWithCode('record has expired', ERRORS.ERR_IPNS_EXPIRED_RECORD)) - } - } else if (validityType) { - log.error('unrecognized validity type') - return reject(errorWithCode('unrecognized validity type', ERRORS.ERR_UNRECOGNIZED_VALIDITY)) - } - - log(`ipns entry for ${value} is valid`) - resolve() - }) - }) + // Validate Signature + let isValid + try { + isValid = await publicKey.verify(dataForSignature, entry.signature) + } catch (err) { + isValid = false + } + if (!isValid) { + log.error('record signature verification failed') + throw errorWithCode('record signature verification failed', ERRORS.ERR_SIGNATURE_VERIFICATION) + } + + // Validate according to the validity type + if (validityType === ipnsEntryProto.ValidityType.EOL) { + let validityDate + + try { + validityDate = parseRFC3339(validity.toString()) + } catch (e) { + log.error('unrecognized validity format (not an rfc3339 format)') + throw errorWithCode('unrecognized validity format (not an rfc3339 format)', ERRORS.ERR_UNRECOGNIZED_FORMAT) + } + + if (validityDate < Date.now()) { + log.error('record has expired') + throw errorWithCode('record has expired', ERRORS.ERR_IPNS_EXPIRED_RECORD) + } + } else if (validityType) { + log.error('unrecognized validity type') + throw errorWithCode('unrecognized validity type', ERRORS.ERR_UNRECOGNIZED_VALIDITY) + } + + log(`ipns entry for ${value} is valid`) } /** @@ -132,42 +132,39 @@ const validate = (publicKey, entry) => { const embedPublicKey = async (publicKey, entry) => { if (!publicKey || !publicKey.bytes || !entry) { const error = 'one or more of the provided parameters are not defined' - log.error(error) throw Object.assign(new Error(error), { code: ERRORS.ERR_UNDEFINED_PARAMETER }) } - return new Promise((resolve, reject) => { - // Create a peer id from the public key. - PeerId.createFromPubKey(publicKey.bytes, (err, peerId) => { - if (err) { - log.error(err) - reject(Object.assign(new Error(err), { code: ERRORS.ERR_PEER_ID_FROM_PUBLIC_KEY })) - } - - // Try to extract the public key from the ID. If we can, no need to embed it - let extractedPublicKey - try { - extractedPublicKey = extractPublicKeyFromId(peerId) - } catch (err) { - log.error(err) - reject(Object.assign(new Error(err), { code: ERRORS.ERR_PUBLIC_KEY_FROM_ID })) - } - - if (extractedPublicKey) { - return resolve(null) - } - - // If we failed to extract the public key from the peer ID, embed it in the record. - try { - entry.pubKey = crypto.keys.marshalPublicKey(publicKey) - } catch (err) { - log.error(err) - reject(err) - } - resolve(entry) - }) - }) + // Create a peer id from the public key. + let peerId + try { + peerId = await PeerId.createFromPubKey(publicKey.bytes) + } catch (err) { + throw Object.assign(new Error(err), { code: ERRORS.ERR_PEER_ID_FROM_PUBLIC_KEY }) + } + + // Try to extract the public key from the ID. If we can, no need to embed it + let extractedPublicKey + try { + extractedPublicKey = extractPublicKeyFromId(peerId) + } catch (err) { + log.error(err) + throw Object.assign(new Error(err), { code: ERRORS.ERR_PUBLIC_KEY_FROM_ID }) + } + + if (extractedPublicKey) { + return null + } + + // If we failed to extract the public key from the peer ID, embed it in the record. + try { + entry.pubKey = crypto.keys.marshalPublicKey(publicKey) + } catch (err) { + log.error(err) + throw err + } + return entry } /** @@ -236,21 +233,18 @@ const getIdKeys = (pid) => { // Sign ipns record data const sign = (privateKey, value, validityType, validity) => { const dataForSignature = ipnsEntryDataForSig(value, validityType, validity) - - return new Promise((resolve, reject) => { - privateKey.sign(dataForSignature, (err, signature) => err ? reject(err) : resolve(signature)) - }) + return privateKey.sign(dataForSignature) } // Utility for getting the validity type code name of a validity const getValidityType = (validityType) => { if (validityType.toString() === '0') { return 'EOL' - } else { - const error = `unrecognized validity type ${validityType.toString()}` - log.error(error) - throw Object.assign(new Error(error), { code: ERRORS.ERR_UNRECOGNIZED_VALIDITY }) } + + const error = `unrecognized validity type ${validityType.toString()}` + log.error(error) + throw Object.assign(new Error(error), { code: ERRORS.ERR_UNRECOGNIZED_VALIDITY }) } // Utility for creating the record data for being signed diff --git a/test/index.spec.js b/test/index.spec.js index 4f06e53..3c64dd2 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -47,13 +47,9 @@ describe('ipns', function () { }) } - before(function (done) { - crypto.keys.generateKeyPair('RSA', 2048, (err, keypair) => { - expect(err).to.not.exist() - rsa = keypair - - spawnDaemon(done) - }) + before(async () => { + rsa = await crypto.keys.generateKeyPair('RSA', 2048) + return new Promise((resolve, reject) => spawnDaemon(err => err ? reject(err) : resolve())) }) after(function (done) { From 3a40ce8bf1d70a4b56c3787a5cff599d82939cc0 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Mon, 18 Feb 2019 12:10:07 -0500 Subject: [PATCH 3/6] chore: replace custom error code fn with npm module --- package.json | 1 + src/index.js | 23 +++++++++++------------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 2d4fc71..6005cbd 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "dependencies": { "base32-encode": "^1.1.0", "debug": "^4.1.1", + "err-code": "^1.1.2", "interface-datastore": "~0.6.0", "left-pad": "^1.3.0", "libp2p-crypto": "~0.16.0", diff --git a/src/index.js b/src/index.js index beacc59..b40dc36 100644 --- a/src/index.js +++ b/src/index.js @@ -6,6 +6,7 @@ const { Key } = require('interface-datastore') const crypto = require('libp2p-crypto') const PeerId = require('peer-id') const multihash = require('multihashes') +const errCode = require('err-code') const debug = require('debug') const log = debug('jsipns') @@ -67,7 +68,7 @@ const _create = async (privateKey, value, seq, isoValidity, validityType) => { return entry } catch (error) { log.error('record signature creation failed') - throw errorWithCode('record signature verification failed', ERRORS.ERR_SIGNATURE_CREATION) + throw errCode('record signature verification failed', ERRORS.ERR_SIGNATURE_CREATION) } } @@ -90,7 +91,7 @@ const validate = async (publicKey, entry) => { } if (!isValid) { log.error('record signature verification failed') - throw errorWithCode('record signature verification failed', ERRORS.ERR_SIGNATURE_VERIFICATION) + throw errCode('record signature verification failed', ERRORS.ERR_SIGNATURE_VERIFICATION) } // Validate according to the validity type @@ -101,16 +102,16 @@ const validate = async (publicKey, entry) => { validityDate = parseRFC3339(validity.toString()) } catch (e) { log.error('unrecognized validity format (not an rfc3339 format)') - throw errorWithCode('unrecognized validity format (not an rfc3339 format)', ERRORS.ERR_UNRECOGNIZED_FORMAT) + throw errCode('unrecognized validity format (not an rfc3339 format)', ERRORS.ERR_UNRECOGNIZED_FORMAT) } if (validityDate < Date.now()) { log.error('record has expired') - throw errorWithCode('record has expired', ERRORS.ERR_IPNS_EXPIRED_RECORD) + throw errCode('record has expired', ERRORS.ERR_IPNS_EXPIRED_RECORD) } } else if (validityType) { log.error('unrecognized validity type') - throw errorWithCode('unrecognized validity type', ERRORS.ERR_UNRECOGNIZED_VALIDITY) + throw errCode('unrecognized validity type', ERRORS.ERR_UNRECOGNIZED_VALIDITY) } log(`ipns entry for ${value} is valid`) @@ -133,7 +134,7 @@ const embedPublicKey = async (publicKey, entry) => { if (!publicKey || !publicKey.bytes || !entry) { const error = 'one or more of the provided parameters are not defined' log.error(error) - throw Object.assign(new Error(error), { code: ERRORS.ERR_UNDEFINED_PARAMETER }) + throw errCode(error, ERRORS.ERR_UNDEFINED_PARAMETER) } // Create a peer id from the public key. @@ -141,7 +142,7 @@ const embedPublicKey = async (publicKey, entry) => { try { peerId = await PeerId.createFromPubKey(publicKey.bytes) } catch (err) { - throw Object.assign(new Error(err), { code: ERRORS.ERR_PEER_ID_FROM_PUBLIC_KEY }) + throw errCode(err, ERRORS.ERR_PEER_ID_FROM_PUBLIC_KEY) } // Try to extract the public key from the ID. If we can, no need to embed it @@ -150,7 +151,7 @@ const embedPublicKey = async (publicKey, entry) => { extractedPublicKey = extractPublicKeyFromId(peerId) } catch (err) { log.error(err) - throw Object.assign(new Error(err), { code: ERRORS.ERR_PUBLIC_KEY_FROM_ID }) + throw errCode(err, ERRORS.ERR_PUBLIC_KEY_FROM_ID) } if (extractedPublicKey) { @@ -179,7 +180,7 @@ const extractPublicKey = (peerId, entry) => { const error = 'one or more of the provided parameters are not defined' log.error(error) - throw Object.assign(new Error(error), { code: ERRORS.ERR_UNDEFINED_PARAMETER }) + throw errCode(error, ERRORS.ERR_UNDEFINED_PARAMETER) } if (entry.pubKey) { @@ -244,7 +245,7 @@ const getValidityType = (validityType) => { const error = `unrecognized validity type ${validityType.toString()}` log.error(error) - throw Object.assign(new Error(error), { code: ERRORS.ERR_UNRECOGNIZED_VALIDITY }) + throw errCode(error, ERRORS.ERR_UNRECOGNIZED_VALIDITY) } // Utility for creating the record data for being signed @@ -267,8 +268,6 @@ const extractPublicKeyFromId = (peerId) => { return crypto.keys.unmarshalPublicKey(decodedId.digest) } -const errorWithCode = (err, code) => Object.assign(new Error(err), { code }) - const marshal = ipnsEntryProto.encode const unmarshal = ipnsEntryProto.decode From c2ae20d2b6d8e0dee2e7db4bd533777b10bdd021 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Tue, 19 Feb 2019 08:27:25 -0500 Subject: [PATCH 4/6] chore: doc fixes, refactor --- README.md | 12 ++++------ src/index.js | 57 +++++++++++++++++++++++++++------------------- test/index.spec.js | 43 +++++++++++++++++----------------- 3 files changed, 59 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index fd4e124..70314ae 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,6 @@ This module contains all the necessary code for creating, understanding and vali const ipns = require('ipns') const entryData = await ipns.create(privateKey, value, sequenceNumber, lifetime) -// your code goes here ``` #### Validate record @@ -48,7 +47,6 @@ const entryData = await ipns.create(privateKey, value, sequenceNumber, lifetime) const ipns = require('ipns') await ipns.validate(publicKey, ipnsEntry) -// your code goes here // if no error thrown, the record is valid ``` @@ -58,7 +56,6 @@ await ipns.validate(publicKey, ipnsEntry) const ipns = require('ipns') const ipnsEntryWithEmbedPublicKey = await ipns.embedPublicKey(publicKey, ipnsEntry) -// your code goes here ``` #### Extract public key from record @@ -67,7 +64,6 @@ const ipnsEntryWithEmbedPublicKey = await ipns.embedPublicKey(publicKey, ipnsEnt const ipns = require('ipns') const publicKey = ipns.extractPublicKey(peerId, ipnsEntry) -// your code goes here ``` #### Datastore key @@ -89,7 +85,7 @@ Returns a key to be used for storing the ipns entry locally, that is: ```js const ipns = require('ipns') -const entryData = ipns.create(privateKey, value, sequenceNumber, lifetime) +const entryData = await ipns.create(privateKey, value, sequenceNumber, lifetime) // ... const marshalledData = ipns.marshal(entryData) // ... @@ -117,7 +113,7 @@ const validator = ipns.validator Contains an object with `validate (marshalledData, key)` and `select (dataA, dataB)` functions. -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). +The `validate` async 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). 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`. @@ -161,7 +157,7 @@ Validate an IPNS record previously stored in a protocol buffer. - `publicKey` (`PubKey` [RSA Instance](https://github.com/libp2p/js-libp2p-crypto/blob/master/src/keys/rsa-class.js)): key to be used for cryptographic operations. - `ipnsEntry` (Object): ipns entry record (obtained using the create function). -Throws an error if the validation was not successful. +Returns a `Promise`, which may be rejected if the validation was not successful. #### Datastore key @@ -196,7 +192,7 @@ Returns the entry data structure after being serialized. #### Embed public key to record ```js -ipns.embedPublicKey(publicKey, ipnsEntry) +const recordWithPublicKey = await ipns.embedPublicKey(publicKey, ipnsEntry) ``` Embed a public key in an IPNS entry. If it is possible to extract the public key from the `peer-id`, there is no need to embed. diff --git a/src/index.js b/src/index.js index b40dc36..e6cc4f2 100644 --- a/src/index.js +++ b/src/index.js @@ -20,6 +20,16 @@ const ID_MULTIHASH_CODE = multihash.names.id const namespace = '/ipns/' +/** + * IPNS entry + * @typedef {Object} IpnsEntry + * @property {string} value - value to be stored in the record + * @property {Buffer} signature - signature of the record + * @property {number} validityType - Type of validation being used + * @property {string} validity - expiration datetime for the record in RFC3339 format + * @property {number} sequence - number representing the version of the record + */ + /** * Creates a new ipns entry and signs it with the given private key. * The ipns entry validity should follow the [RFC3339]{@link https://www.ietf.org/rfc/rfc3339.txt} with nanoseconds precision. @@ -29,7 +39,7 @@ const namespace = '/ipns/' * @param {string} value value to be stored in the record. * @param {number} seq number representing the current version of the record. * @param {number|string} lifetime lifetime of the record (in milliseconds). - * @returns {Object} entry + * @returns {Promise} entry */ const create = (privateKey, value, seq, lifetime) => { // Validity in ISOString with nanoseconds precision and validity type EOL @@ -45,7 +55,7 @@ const create = (privateKey, value, seq, lifetime) => { * @param {string} value value to be stored in the record. * @param {number} seq number representing the current version of the record. * @param {string} expiration expiration datetime for record in the [RFC3339]{@link https://www.ietf.org/rfc/rfc3339.txt} with nanoseconds precision. - * @returns {Object} entry + * @returns {Promise} entry */ const createWithExpiration = (privateKey, value, seq, expiration) => { const validityType = ipnsEntryProto.ValidityType.EOL @@ -53,30 +63,26 @@ const createWithExpiration = (privateKey, value, seq, expiration) => { } const _create = async (privateKey, value, seq, isoValidity, validityType) => { - try { - const signature = await sign(privateKey, value, validityType, isoValidity) - - const entry = { - value: value, - signature: signature, - validityType: validityType, - validity: isoValidity, - sequence: seq - } - - log(`ipns entry for ${value} created`) - return entry - } catch (error) { - log.error('record signature creation failed') - throw errCode('record signature verification failed', ERRORS.ERR_SIGNATURE_CREATION) + const signature = await sign(privateKey, value, validityType, isoValidity) + + const entry = { + value: value, + signature: signature, + validityType: validityType, + validity: isoValidity, + sequence: seq } + + log(`ipns entry for ${value} created`) + return entry } /** * Validates the given ipns entry against the given public key. * * @param {Object} publicKey public key for validating the record. - * @param {Object} entry ipns entry record. + * @param {IpnsEntry} entry ipns entry record. + * @returns {Promise} */ const validate = async (publicKey, entry) => { const { value, validityType, validity } = entry @@ -128,7 +134,7 @@ const validate = async (publicKey, entry) => { * * @param {Object} publicKey public key to embed. * @param {Object} entry ipns entry record. - * @return {Object} entry with public key embedded + * @return {IpnsEntry} entry with public key embedded */ const embedPublicKey = async (publicKey, entry) => { if (!publicKey || !publicKey.bytes || !entry) { @@ -172,7 +178,7 @@ const embedPublicKey = async (publicKey, entry) => { * Extracts a public key matching `pid` from the ipns record. * * @param {Object} peerId peer identifier object. - * @param {Object} entry ipns entry record. + * @param {IpnsEntry} entry ipns entry record. * @returns {Object} the public key */ const extractPublicKey = (peerId, entry) => { @@ -233,8 +239,13 @@ const getIdKeys = (pid) => { // Sign ipns record data const sign = (privateKey, value, validityType, validity) => { - const dataForSignature = ipnsEntryDataForSig(value, validityType, validity) - return privateKey.sign(dataForSignature) + try { + const dataForSignature = ipnsEntryDataForSig(value, validityType, validity) + return privateKey.sign(dataForSignature) + } catch (error) { + log.error('record signature creation failed') + throw errCode('record signature creation failed: ' + error.message, ERRORS.ERR_SIGNATURE_CREATION) + } } // Utility for getting the validity type code name of a validity diff --git a/test/index.spec.js b/test/index.spec.js index 3c64dd2..595b24e 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -30,26 +30,28 @@ describe('ipns', function () { let ipfsId = null let rsa = null - const spawnDaemon = (cb) => { - df.spawn({ initOptions: { bits: 512 } }, (err, _ipfsd) => { - expect(err).to.not.exist() - ipfsd = _ipfsd - ipfs = ipfsd.api - - ipfs.id((err, id) => { - if (err) { - return cb(err) - } - - ipfsId = id - cb() + const spawnDaemon = () => { + return new Promise((resolve, reject) => { + df.spawn({ initOptions: { bits: 512 } }, (err, _ipfsd) => { + expect(err).to.not.exist() + ipfsd = _ipfsd + ipfs = ipfsd.api + + ipfs.id((err, id) => { + if (err) { + return reject(err) + } + + ipfsId = id + resolve() + }) }) }) } before(async () => { rsa = await crypto.keys.generateKeyPair('RSA', 2048) - return new Promise((resolve, reject) => spawnDaemon(err => err ? reject(err) : resolve())) + return spawnDaemon() }) after(function (done) { @@ -185,17 +187,14 @@ describe('ipns', function () { // https://github.com/libp2p/go-libp2p-peer/blob/7f219a1e70011a258c5d3e502aef6896c60d03ce/peer.go#L80 // IDFromEd25519PublicKey is not currently implement on js-libp2p-peer // https://github.com/libp2p/go-libp2p-peer/pull/30 - it.skip('should be able to extract a public key directly from the peer', (done) => { + it.skip('should be able to extract a public key directly from the peer', async () => { const sequence = 0 const validity = 1000000 - crypto.keys.generateKeyPair('ed25519', 2048, async (err, ed25519) => { - expect(err).to.not.exist() - - const entry = await ipns.create(ed25519, cid, sequence, validity) - const entryWithKey = ipns.embedPublicKey(ed25519.public, entry) - expect(entryWithKey).to.not.exist() // Should be null - }) + const ed25519 = await crypto.keys.generateKeyPair('ed25519', 2048) + const entry = await ipns.create(ed25519, cid, sequence, validity) + const entryWithKey = ipns.embedPublicKey(ed25519.public, entry) + expect(entryWithKey).to.not.exist() // Should be null }) it('validator with no valid public key should error', async () => { From 70bcfe481a2d4a712919747be58f0cd4ad977bc8 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Tue, 19 Feb 2019 14:45:02 -0500 Subject: [PATCH 5/6] chore: point interface-datastore package at async/await branch --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6005cbd..c0f2e57 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "base32-encode": "^1.1.0", "debug": "^4.1.1", "err-code": "^1.1.2", - "interface-datastore": "~0.6.0", + "interface-datastore": "ipfs/interface-datastore#refactor/async-iterators", "left-pad": "^1.3.0", "libp2p-crypto": "~0.16.0", "multihashes": "~0.4.14", From 1cb0063ad91559575da4a3461a988e203b3bc336 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Thu, 18 Jul 2019 11:23:49 -0400 Subject: [PATCH 6/6] chore: update to async/await packages --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index c0f2e57..c6b70e8 100644 --- a/package.json +++ b/package.json @@ -36,11 +36,11 @@ "base32-encode": "^1.1.0", "debug": "^4.1.1", "err-code": "^1.1.2", - "interface-datastore": "ipfs/interface-datastore#refactor/async-iterators", + "interface-datastore": "~0.7.0", "left-pad": "^1.3.0", - "libp2p-crypto": "~0.16.0", + "libp2p-crypto": "~0.17.0", "multihashes": "~0.4.14", - "peer-id": "libp2p/js-peer-id#feat/async-await", + "peer-id": "~0.13.2", "protons": "^1.0.1", "timestamp-nano": "^1.0.0" },