diff --git a/src/cli/commands/files/add.js b/src/cli/commands/files/add.js index 23c9fdb60d..87cdbbdf35 100644 --- a/src/cli/commands/files/add.js +++ b/src/cli/commands/files/add.js @@ -10,6 +10,7 @@ const zip = require('pull-zip') const getFolderSize = require('get-folder-size') const byteman = require('byteman') const waterfall = require('async/waterfall') +const mh = require('multihashes') const utils = require('../../utils') const print = require('../../utils').print const createProgressBar = require('../../utils').createProgressBar @@ -162,6 +163,11 @@ module.exports = { type: 'integer', describe: 'Cid version. Non-zero value will change default of \'raw-leaves\' to true. (experimental)' }, + hash: { + type: 'string', + choices: Object.keys(mh.names), + describe: 'Hash function to use. Will set Cid version to 1 if used. (experimental)' + }, quiet: { alias: 'q', type: 'boolean', @@ -191,7 +197,8 @@ module.exports = { : Infinity, cidVersion: argv.cidVersion, rawLeaves: argv.rawLeaves, - onlyHash: argv.onlyHash + onlyHash: argv.onlyHash, + hashAlg: argv.hash } // Temporary restriction on raw-leaves: @@ -206,6 +213,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 (options.hash && options.rawLeaves !== false) { + throw new Error('Implied argument raw-leaves must be passed and set to false when hash argument is specified') + } + if (options.rawLeaves) { throw new Error('Not implemented: raw-leaves') } diff --git a/src/core/components/files.js b/src/core/components/files.js index 3ec915065d..ee7eca713b 100644 --- a/src/core/components/files.js +++ b/src/core/components/files.js @@ -22,15 +22,15 @@ function noop () {} function prepareFile (self, opts, file, callback) { opts = opts || {} - waterfall([ - (cb) => opts.onlyHash ? cb(null, file) : self.object.get(file.multihash, cb), - (node, cb) => { - let cid = new CID(node.multihash) + let cid = new CID(file.multihash) - if (opts['cid-version'] === 1) { - cid = cid.toV1() - } + if (opts.cidVersion === 1) { + cid = cid.toV1() + } + waterfall([ + (cb) => opts.onlyHash ? cb(null, file) : self.object.get(file.multihash, opts, cb), + (node, cb) => { const b58Hash = cid.toBaseEncodedString() cb(null, { @@ -110,6 +110,10 @@ module.exports = function files (self) { : Infinity }, options) + if (opts.hashAlg && opts.cidVersion !== 1) { + opts.cidVersion = 1 + } + let total = 0 let prog = opts.progress || (() => {}) const progress = (bytes) => { @@ -182,7 +186,7 @@ module.exports = function files (self) { } return { - add: promisify((data, options, callback) => { + add: promisify((data, options = {}, callback) => { if (typeof options === 'function') { callback = options options = {} @@ -200,6 +204,11 @@ module.exports = function files (self) { return callback(new Error('first arg must be a buffer, readable stream, an object or array of objects')) } + // CID v0 is for multihashes encoded with sha2-256 + if (options.hashAlg && options.cidVersion !== 1) { + options.cidVersion = 1 + } + pull( pull.values([data]), _addPullStream(options), diff --git a/src/core/components/object.js b/src/core/components/object.js index 5ce053128f..0677eeccc4 100644 --- a/src/core/components/object.js +++ b/src/core/components/object.js @@ -191,7 +191,11 @@ module.exports = function object (self) { } catch (err) { return callback(err) } - const cid = new CID(mh) + let cid = new CID(mh) + + if (options.cidVersion === 1) { + cid = cid.toV1() + } self._ipld.get(cid, (err, result) => { if (err) { @@ -214,6 +218,7 @@ module.exports = function object (self) { if (err) { return callback(err) } + callback(null, node.data) }) }), diff --git a/src/http/api/resources/files.js b/src/http/api/resources/files.js index 91861965f3..f74cf0a7fe 100644 --- a/src/http/api/resources/files.js +++ b/src/http/api/resources/files.js @@ -207,7 +207,8 @@ exports.add = { cidVersion: request.query['cid-version'], rawLeaves: request.query['raw-leaves'], progress: request.query.progress ? progressHandler : null, - onlyHash: request.query['only-hash'] + onlyHash: request.query['only-hash'], + hashAlg: request.query['hash'] } const aborter = abortable() diff --git a/test/cli/files.js b/test/cli/files.js index 2e9fec39b5..be9a6d96a5 100644 --- a/test/cli/files.js +++ b/test/cli/files.js @@ -7,8 +7,22 @@ const expect = require('chai').expect 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') +// 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')) @@ -300,6 +314,19 @@ describe('files', () => runOnAndOff((thing) => { }) }) + HASH_ALGS.forEach((name) => { + it(`add with hash=${name} and raw-leaves=false`, function () { + this.timeout(30 * 1000) + + 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) + expect(mh.decode(cid.multihash).name).to.equal(name) + }) + }) + }) + it('cat', function () { this.timeout(30 * 1000)