From 4802221c69d590ccdff678bf53f67392b40c7ad7 Mon Sep 17 00:00:00 2001 From: Alan Shaw Date: Mon, 4 Sep 2017 09:33:59 +0100 Subject: [PATCH 1/6] WIP support specify hash alg --- src/cli/commands/files/add.js | 8 +++++++- src/core/components/files.js | 2 +- src/http-api/resources/files.js | 7 +++++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/cli/commands/files/add.js b/src/cli/commands/files/add.js index 82fe7ecb18..28da67526b 100644 --- a/src/cli/commands/files/add.js +++ b/src/cli/commands/files/add.js @@ -151,6 +151,11 @@ module.exports = { type: 'boolean', default: false, describe: 'Write no output' + }, + hash: { + type: 'string', + // TODO: choices: [], + describe: 'Hash function to use. Will set Cid version to 1 if used. (experimental)' } }, @@ -161,7 +166,8 @@ module.exports = { strategy: argv.trickle ? 'trickle' : 'balanced', shardSplitThreshold: argv.enableShardingExperiment ? argv.shardSplitThreshold : Infinity, 'cid-version': argv['cid-version'], - 'raw-leaves': argv['raw-leaves'] + 'raw-leaves': argv['raw-leaves'], + hashAlg: argv.hash } // Temporary restriction on raw-leaves: diff --git a/src/core/components/files.js b/src/core/components/files.js index 6a9f5fbced..8570ea3412 100644 --- a/src/core/components/files.js +++ b/src/core/components/files.js @@ -122,7 +122,7 @@ function prepareFile (self, opts, file, callback) { (node, cb) => { let cid = new CID(node.multihash) - if (opts['cid-version'] === 1) { + if (opts.cidVersion === 1) { cid = cid.toV1() } diff --git a/src/http-api/resources/files.js b/src/http-api/resources/files.js index 35c53a32e6..4a2213323a 100644 --- a/src/http-api/resources/files.js +++ b/src/http-api/resources/files.js @@ -157,7 +157,9 @@ exports.add = { is: 1, then: Joi.boolean().valid(false).required(), otherwise: Joi.boolean().valid(false) - }) + }), + // TODO: Validate against multihash alg names + hash: Joi.string() }) // TODO: Necessary until validate "recursive", "stream-channels" etc. .options({ allowUnknown: true }) @@ -209,7 +211,8 @@ exports.add = { const options = { 'cid-version': request.query['cid-version'], - 'raw-leaves': request.query['raw-leaves'] + 'raw-leaves': request.query['raw-leaves'], + 'hashAlg': request.query['hash'] } pull( From 53aeea298d9c993ed2634b0db870d94cbb6736f7 Mon Sep 17 00:00:00 2001 From: Alan Shaw Date: Tue, 5 Sep 2017 10:39:15 +0100 Subject: [PATCH 2/6] Allow hash algorithm to be specified --- src/cli/commands/files/add.js | 11 ++++++----- src/core/components/files.js | 12 +++++++++++- src/http-api/resources/files.js | 9 ++++----- test/cli/files.js | 13 +++++++++++++ 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/cli/commands/files/add.js b/src/cli/commands/files/add.js index 28da67526b..ebd4c7035b 100644 --- a/src/cli/commands/files/add.js +++ b/src/cli/commands/files/add.js @@ -8,6 +8,7 @@ const pull = require('pull-stream') const paramap = require('pull-paramap') const zip = require('pull-zip') const toPull = require('stream-to-pull-stream') +const mh = require('multihashes') const utils = require('../../utils') const print = require('../../utils').print @@ -154,7 +155,7 @@ module.exports = { }, hash: { type: 'string', - // TODO: choices: [], + choices: [undefined].concat(Object.keys(mh.names)), describe: 'Hash function to use. Will set Cid version to 1 if used. (experimental)' } }, @@ -165,8 +166,8 @@ module.exports = { const options = { strategy: argv.trickle ? 'trickle' : 'balanced', shardSplitThreshold: argv.enableShardingExperiment ? argv.shardSplitThreshold : Infinity, - 'cid-version': argv['cid-version'], - 'raw-leaves': argv['raw-leaves'], + cidVersion: argv.cidVersion, + rawLeaves: argv.rawLeaves, hashAlg: argv.hash } @@ -178,11 +179,11 @@ module.exports = { // cid-version > 0 unless explicitly set to false. // // This retains feature parity without having to implement raw-leaves. - if (argv['cid-version'] > 0 && argv['raw-leaves'] !== false) { + if (argv.cidVersion > 0 && argv.rawLeaves !== false) { throw new Error('Implied argument raw-leaves must be passed and set to false when cid-version is > 0') } - if (argv['raw-leaves']) { + if (argv.rawLeaves) { throw new Error('Not implemented: raw-leaves') } diff --git a/src/core/components/files.js b/src/core/components/files.js index 8570ea3412..8bd2f8e1b2 100644 --- a/src/core/components/files.js +++ b/src/core/components/files.js @@ -20,6 +20,11 @@ module.exports = function files (self) { shardSplitThreshold: self._options.EXPERIMENTAL.sharding ? 1000 : Infinity }, options) + // CID v0 is for multihashes encoded with sha2-256 + if (opts.hashAlg && opts.hashAlg !== 'sha2-256' && opts.cidVersion !== 1) { + opts.cidVersion = 1 + } + return pull( pull.map(normalizeContent), pull.flatten(), @@ -65,6 +70,11 @@ module.exports = function files (self) { return callback(new Error('Invalid arguments, data must be an object, Buffer or readable stream')) } + // CID v0 is for multihashes encoded with sha2-256 + if (options && options.hashAlg && options.hashAlg !== 'sha2-256' && options.cidVersion !== 1) { + options.cidVersion = 1 + } + pull( pull.values(normalizeContent(data)), importer(self._ipldResolver, options), @@ -120,7 +130,7 @@ function prepareFile (self, opts, file, callback) { waterfall([ (cb) => self.object.get(file.multihash, cb), (node, cb) => { - let cid = new CID(node.multihash) + let cid = new CID(file.multihash) if (opts.cidVersion === 1) { cid = cid.toV1() diff --git a/src/http-api/resources/files.js b/src/http-api/resources/files.js index 4a2213323a..43cd2f0950 100644 --- a/src/http-api/resources/files.js +++ b/src/http-api/resources/files.js @@ -158,8 +158,7 @@ exports.add = { then: Joi.boolean().valid(false).required(), otherwise: Joi.boolean().valid(false) }), - // TODO: Validate against multihash alg names - hash: Joi.string() + hash: Joi.string().valid(Object.keys(mh.names)) }) // TODO: Necessary until validate "recursive", "stream-channels" etc. .options({ allowUnknown: true }) @@ -210,9 +209,9 @@ exports.add = { }) const options = { - 'cid-version': request.query['cid-version'], - 'raw-leaves': request.query['raw-leaves'], - 'hashAlg': request.query['hash'] + cidVersion: request.query['cid-version'], + rawLeaves: request.query['raw-leaves'], + hashAlg: request.query['hash'] } pull( diff --git a/test/cli/files.js b/test/cli/files.js index 85f7fbd9b4..9a9c4e3d66 100644 --- a/test/cli/files.js +++ b/test/cli/files.js @@ -6,6 +6,8 @@ const fs = require('fs') const path = require('path') const compareDir = require('dir-compare').compareSync const rimraf = require('rimraf').sync +const CID = require('cids') +const mh = require('multihashes') const runOnAndOff = require('../utils/on-and-off') describe('files', () => runOnAndOff((thing) => { @@ -222,6 +224,17 @@ describe('files', () => runOnAndOff((thing) => { }) }) + Object.keys(mh.names).forEach((name) => { + it('add with hash=' + name, () => { + return ipfs('add src/init-files/init-docs/readme --hash=' + name) + .then((out) => { + const hash = out.split(' ')[1] + const cid = new CID(hash) + expect(mh.decode(cid.multihash).name).to.equal(name) + }) + }) + }) + it('cat', () => { return ipfs('files cat QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB') .then((out) => { From 3f643472c6c1cfc2c9f7a28930c4d112f5a9e95c Mon Sep 17 00:00:00 2001 From: Alan Shaw Date: Thu, 7 Sep 2017 17:14:09 +0100 Subject: [PATCH 3/6] Allow hash algorithm to be passed, allow object get/data via CID --- src/cli/commands/files/add.js | 12 ++++++++++++ src/core/components/files.js | 21 ++++++++++---------- src/core/components/object.js | 33 +++++++++++++------------------- src/http-api/resources/files.js | 15 +++++++-------- src/http-api/resources/object.js | 5 ++--- test/cli/files.js | 18 ++++++++++++++--- 6 files changed, 60 insertions(+), 44 deletions(-) diff --git a/src/cli/commands/files/add.js b/src/cli/commands/files/add.js index ebd4c7035b..61b849ff06 100644 --- a/src/cli/commands/files/add.js +++ b/src/cli/commands/files/add.js @@ -183,6 +183,18 @@ module.exports = { throw new Error('Implied argument raw-leaves must be passed and set to false when cid-version is > 0') } + // Temporary restriction on raw-leaves: + // When hash != undefined then raw-leaves MUST be present and false. + // + // This is because raw-leaves is not yet implemented in js-ipfs, + // and go-ipfs changes the value of raw-leaves to true when + // hash != undefined unless explicitly set to false. + // + // This retains feature parity without having to implement raw-leaves. + if (argv.hash && argv.rawLeaves !== false) { + throw new Error('Implied argument raw-leaves must be passed and set to false when hash argument is specified') + } + if (argv.rawLeaves) { throw new Error('Not implemented: raw-leaves') } diff --git a/src/core/components/files.js b/src/core/components/files.js index 8bd2f8e1b2..754bce6d76 100644 --- a/src/core/components/files.js +++ b/src/core/components/files.js @@ -20,8 +20,7 @@ module.exports = function files (self) { shardSplitThreshold: self._options.EXPERIMENTAL.sharding ? 1000 : Infinity }, options) - // CID v0 is for multihashes encoded with sha2-256 - if (opts.hashAlg && opts.hashAlg !== 'sha2-256' && opts.cidVersion !== 1) { + if (opts.hashAlg && opts.cidVersion !== 1) { opts.cidVersion = 1 } @@ -70,8 +69,10 @@ module.exports = function files (self) { return callback(new Error('Invalid arguments, data must be an object, Buffer or readable stream')) } + options = options || {} + // CID v0 is for multihashes encoded with sha2-256 - if (options && options.hashAlg && options.hashAlg !== 'sha2-256' && options.cidVersion !== 1) { + if (options.hashAlg && options.cidVersion !== 1) { options.cidVersion = 1 } @@ -127,15 +128,15 @@ module.exports = function files (self) { function prepareFile (self, opts, file, callback) { opts = opts || {} - waterfall([ - (cb) => self.object.get(file.multihash, cb), - (node, cb) => { - let cid = new CID(file.multihash) + let cid = new CID(file.multihash) - if (opts.cidVersion === 1) { - cid = cid.toV1() - } + if (opts.cidVersion === 1) { + cid = cid.toV1() + } + waterfall([ + (cb) => self.object.get(cid, cb), + (node, cb) => { const b58Hash = cid.toBaseEncodedString() cb(null, { diff --git a/src/core/components/object.js b/src/core/components/object.js index 4c716c4504..67ce5c6be8 100644 --- a/src/core/components/object.js +++ b/src/core/components/object.js @@ -10,20 +10,6 @@ const mh = require('multihashes') const Unixfs = require('ipfs-unixfs') const assert = require('assert') -function normalizeMultihash (multihash, enc) { - if (typeof multihash === 'string') { - if (enc === 'base58' || !enc) { - return multihash - } - - return new Buffer(multihash, enc) - } else if (Buffer.isBuffer(multihash)) { - return multihash - } else { - throw new Error('unsupported multihash') - } -} - function parseBuffer (buf, encoding, callback) { switch (encoding) { case 'json': @@ -178,20 +164,19 @@ module.exports = function object (self) { } }), - get: promisify((multihash, options, callback) => { + get: promisify((hash, options, callback) => { if (typeof options === 'function') { callback = options options = {} } - let mh + let cid try { - mh = normalizeMultihash(multihash, options.enc) + cid = new CID(hash) } catch (err) { return callback(err) } - const cid = new CID(mh) self._ipldResolver.get(cid, (err, result) => { if (err) { @@ -204,13 +189,21 @@ module.exports = function object (self) { }) }), - data: promisify((multihash, options, callback) => { + data: promisify((hash, options, callback) => { if (typeof options === 'function') { callback = options options = {} } - self.object.get(multihash, options, (err, node) => { + let cid + + try { + cid = new CID(hash) + } catch (err) { + return callback(err) + } + + self.object.get(cid, options, (err, node) => { if (err) { return callback(err) } diff --git a/src/http-api/resources/files.js b/src/http-api/resources/files.js index 43cd2f0950..5b4b7fd7c4 100644 --- a/src/http-api/resources/files.js +++ b/src/http-api/resources/files.js @@ -145,20 +145,19 @@ exports.add = { query: Joi.object() .keys({ 'cid-version': Joi.number().integer().min(0).max(1), + hash: Joi.string().valid(Object.keys(mh.names)), // Temporary restriction on raw-leaves: - // When cid-version=1 then raw-leaves MUST be present and false. + // When cid-version > 0 or hash != undefined then raw-leaves MUST be + // present and false. // // This is because raw-leaves is not yet implemented in js-ipfs, // and go-ipfs changes the value of raw-leaves to true when - // cid-version > 0 unless explicitly set to false. + // cid-version > 0 or hash != undefined unless explicitly set to false. // // This retains feature parity without having to implement raw-leaves. - 'raw-leaves': Joi.any().when('cid-version', { - is: 1, - then: Joi.boolean().valid(false).required(), - otherwise: Joi.boolean().valid(false) - }), - hash: Joi.string().valid(Object.keys(mh.names)) + 'raw-leaves': Joi.boolean().valid(false) + .when('cid-version', { is: 1, then: Joi.required() }) + .when('hash', { is: Joi.string(), then: Joi.required() }) }) // TODO: Necessary until validate "recursive", "stream-channels" etc. .options({ allowUnknown: true }) diff --git a/src/http-api/resources/object.js b/src/http-api/resources/object.js index 4a366f4e60..091312b682 100644 --- a/src/http-api/resources/object.js +++ b/src/http-api/resources/object.js @@ -7,6 +7,7 @@ const DAGLink = dagPB.DAGLink const DAGNode = dagPB.DAGNode const waterfall = require('async/waterfall') const series = require('async/series') +const CID = require('cids') const debug = require('debug') const log = debug('jsipfs:http-api:object') log.error = debug('jsipfs:http-api:object:error') @@ -20,9 +21,7 @@ exports.parseKey = (request, reply) => { } try { - return reply({ - key: mh.fromB58String(request.query.arg) - }) + return reply({ key: new CID(request.query.arg) }) } catch (err) { log.error(err) return reply({ diff --git a/test/cli/files.js b/test/cli/files.js index 9a9c4e3d66..599868dc0c 100644 --- a/test/cli/files.js +++ b/test/cli/files.js @@ -10,6 +10,18 @@ const CID = require('cids') const mh = require('multihashes') const runOnAndOff = require('../utils/on-and-off') +// TODO: Test against all algorithms Object.keys(mh.names) +// This subset is known to work with both go-ipfs and js-ipfs as of 2017-09-05 +const HASH_ALGS = [ + 'sha1', + 'sha2-256', + 'sha2-512', + 'keccak-224', + 'keccak-256', + 'keccak-384', + 'keccak-512' +] + describe('files', () => runOnAndOff((thing) => { let ipfs const readme = fs.readFileSync(path.join(process.cwd(), '/src/init-files/init-docs/readme')) @@ -224,9 +236,9 @@ describe('files', () => runOnAndOff((thing) => { }) }) - Object.keys(mh.names).forEach((name) => { - it('add with hash=' + name, () => { - return ipfs('add src/init-files/init-docs/readme --hash=' + name) + HASH_ALGS.forEach((name) => { + it(`add with hash=${name} and raw-leaves=false`, () => { + return ipfs(`add src/init-files/init-docs/readme --hash=${name} --raw-leaves=false`) .then((out) => { const hash = out.split(' ')[1] const cid = new CID(hash) From 0c179f7d3e8a252500a9b25d1cfc40310aa9fa02 Mon Sep 17 00:00:00 2001 From: Alan Shaw Date: Fri, 8 Sep 2017 12:39:05 +0100 Subject: [PATCH 4/6] Update ipfs-unixfs-engine dependency --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 778a7b3263..e86042573b 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "ipfs-multipart": "~0.1.0", "ipfs-repo": "~0.17.0", "ipfs-unixfs": "~0.1.13", - "ipfs-unixfs-engine": "~0.22.3", + "ipfs-unixfs-engine": "~0.22.5", "ipld-resolver": "~0.13.2", "is-ipfs": "^0.3.0", "is-stream": "^1.1.0", From d558b858d52698be3dd0c74c3da031600888047f Mon Sep 17 00:00:00 2001 From: Alan Shaw Date: Fri, 8 Sep 2017 13:21:46 +0100 Subject: [PATCH 5/6] Update ipfs-api dependency --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e86042573b..76a600a12f 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "hapi": "^16.5.2", "hapi-set-header": "^1.0.2", "hoek": "^4.2.0", - "ipfs-api": "^14.3.3", + "ipfs-api": "^14.3.5", "ipfs-bitswap": "~0.17.2", "ipfs-block": "~0.6.0", "ipfs-block-service": "~0.12.0", From dc05d6e4101a2a81ff028b0b4f52a0ff2a6d8a63 Mon Sep 17 00:00:00 2001 From: Alan Shaw Date: Fri, 8 Sep 2017 13:22:08 +0100 Subject: [PATCH 6/6] Var name tweaks after review --- src/core/components/object.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/core/components/object.js b/src/core/components/object.js index 67ce5c6be8..8402e9befe 100644 --- a/src/core/components/object.js +++ b/src/core/components/object.js @@ -164,16 +164,14 @@ module.exports = function object (self) { } }), - get: promisify((hash, options, callback) => { + get: promisify((cid, options, callback) => { if (typeof options === 'function') { callback = options options = {} } - let cid - try { - cid = new CID(hash) + cid = new CID(cid) } catch (err) { return callback(err) } @@ -189,16 +187,14 @@ module.exports = function object (self) { }) }), - data: promisify((hash, options, callback) => { + data: promisify((cid, options, callback) => { if (typeof options === 'function') { callback = options options = {} } - let cid - try { - cid = new CID(hash) + cid = new CID(cid) } catch (err) { return callback(err) }