diff --git a/src/cli/ls.js b/src/cli/ls.js index 3238a81c0f..5532dd0962 100644 --- a/src/cli/ls.js +++ b/src/cli/ls.js @@ -20,6 +20,11 @@ module.exports = { default: false, coerce: asBoolean, describe: 'Use long listing format.' + }, + cidBase: { + alias: 'cid-base', + default: 'base58btc', + describe: 'CID base to use.' } }, @@ -27,12 +32,14 @@ module.exports = { let { path, ipfs, - long + long, + cidBase } = argv argv.resolve( ipfs.files.ls(path || FILE_SEPARATOR, { - long + long, + cidBase }) .then(files => { if (long) { diff --git a/src/cli/stat.js b/src/cli/stat.js index 2a5c80995e..2cf3cbc06d 100644 --- a/src/cli/stat.js +++ b/src/cli/stat.js @@ -41,6 +41,11 @@ Type: `, default: false, coerce: asBoolean, describe: 'Compute the amount of the dag that is local, and if possible the total size' + }, + cidBase: { + alias: 'cid-base', + default: 'base58btc', + describe: 'CID base to use.' } }, diff --git a/src/core/ls.js b/src/core/ls.js index 0f6cf11754..9d4f103fbb 100644 --- a/src/core/ls.js +++ b/src/core/ls.js @@ -2,17 +2,18 @@ const waterfall = require('async/waterfall') const map = require('async/map') -const bs58 = require('bs58') const UnixFs = require('ipfs-unixfs') const { traverseTo, loadNode, + formatCid, FILE_SEPARATOR, FILE_TYPES } = require('./utils') const defaultOptions = { - long: false + long: false, + cidBase: 'base58btc' } module.exports = (ipfs) => { @@ -47,7 +48,7 @@ module.exports = (ipfs) => { done(null, { name: link.name, type: meta.type, - hash: bs58.encode(node.multihash), + hash: formatCid(node.multihash, options.cidBase), size: meta.fileSize() || 0 }) } @@ -57,7 +58,7 @@ module.exports = (ipfs) => { cb(null, [{ name: result.name, type: meta.type, - hash: bs58.encode(result.node.multihash), + hash: formatCid(result.node.multihash, options.cidBase), size: meta.fileSize() || 0 }]) } diff --git a/src/core/stat.js b/src/core/stat.js index 584fb9ff38..fa5ead9bd3 100644 --- a/src/core/stat.js +++ b/src/core/stat.js @@ -1,9 +1,9 @@ 'use strict' const unmarshal = require('ipfs-unixfs').unmarshal -const bs58 = require('bs58') const { - traverseTo + traverseTo, + formatCid } = require('./utils') const waterfall = require('async/waterfall') const log = require('debug')('ipfs:mfs:stat') @@ -11,7 +11,8 @@ const log = require('debug')('ipfs:mfs:stat') const defaultOptions = { hash: false, size: false, - withLocal: false + withLocal: false, + cidBase: 'base58btc' } module.exports = (ipfs) => { @@ -32,7 +33,7 @@ module.exports = (ipfs) => { ({ node }, done) => { if (options.hash) { return done(null, { - hash: bs58.encode(node.multihash) + hash: formatCid(node.multihash, options.cidBase) }) } else if (options.size) { return done(null, { @@ -49,7 +50,7 @@ module.exports = (ipfs) => { } done(null, { - hash: bs58.encode(node.multihash), + hash: formatCid(node.multihash, options.cidBase), size: meta.fileSize() || 0, cumulativeSize: node.size, blocks: blocks, diff --git a/src/core/utils/format-cid.js b/src/core/utils/format-cid.js new file mode 100644 index 0000000000..ab9bae3ea3 --- /dev/null +++ b/src/core/utils/format-cid.js @@ -0,0 +1,15 @@ +'use strict' + +const CID = require('cids') + +module.exports = (cid, base) => { + if (Buffer.isBuffer(cid)) { + cid = new CID(cid) + } + + if (base === 'base58btc') { + return cid.toBaseEncodedString() + } + + return cid.toV1().toBaseEncodedString(base) +} diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 707906cc1d..a0a31e0357 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -9,6 +9,7 @@ module.exports = { createLock: require('./create-lock'), createNode: require('./create-node'), endPullStream: require('./end-pull-stream'), + formatCid: require('./format-cid'), limitStreamBytes: require('./limit-stream-bytes'), loadNode: require('./load-node'), toSourcesAndDestination: require('./to-sources-and-destination'), diff --git a/src/http/ls.js b/src/http/ls.js index 810085db08..0c7be69e3b 100644 --- a/src/http/ls.js +++ b/src/http/ls.js @@ -13,11 +13,13 @@ const mfsLs = (api) => { } = request.server.app const { arg, - long + long, + cidBase } = request.query return ipfs.files.ls(arg, { - long + long, + cidBase }) .then(files => { reply({ @@ -44,7 +46,8 @@ const mfsLs = (api) => { }, query: Joi.object().keys({ arg: Joi.string().default('/'), - long: Joi.boolean().default(false) + long: Joi.boolean().default(false), + cidBase: Joi.string().default('base58btc') }) .rename('l', 'long', { override: true, diff --git a/src/http/stat.js b/src/http/stat.js index 09d47b4c96..7294806438 100644 --- a/src/http/stat.js +++ b/src/http/stat.js @@ -15,13 +15,15 @@ const mfsStat = (api) => { arg, hash, size, - withLocal + withLocal, + cidBase } = request.query return ipfs.files.stat(arg, { hash, size, - withLocal + withLocal, + cidBase }) .then(stats => { reply({ @@ -52,7 +54,8 @@ const mfsStat = (api) => { arg: Joi.string().default('/'), hash: Joi.boolean().default(false), size: Joi.boolean().default(false), - withLocal: Joi.boolean().default(false) + withLocal: Joi.boolean().default(false), + cidBase: Joi.string().default('base58btc') }) } } diff --git a/test/helpers/index.js b/test/helpers/index.js index 84e761f4d1..4ef9e27977 100644 --- a/test/helpers/index.js +++ b/test/helpers/index.js @@ -40,5 +40,6 @@ module.exports = { createMfs, bufferStream: require('./buffer-stream'), collectLeafCids: require('./collect-leaf-cids'), - EMPTY_DIRECTORY_HASH: 'QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn' + EMPTY_DIRECTORY_HASH: 'QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn', + EMPTY_DIRECTORY_HASH_BASE32: 'bafybeiczsscdsbs7ffqz55asqdf3smv6klcw3gofszvwlyarci47bgf354' } diff --git a/test/ls.spec.js b/test/ls.spec.js index 32361841c0..c5036325ac 100644 --- a/test/ls.spec.js +++ b/test/ls.spec.js @@ -139,6 +139,26 @@ describe('ls', function () { }) }) + it('lists a file with a base32 hash', () => { + const fileName = `small-file-${Math.random()}.txt` + const content = Buffer.from('Hello world') + + return mfs.write(`/${fileName}`, content, { + create: true + }) + .then(() => mfs.ls(`/${fileName}`, { + long: true, + cidBase: 'base32' + })) + .then(files => { + expect(files.length).to.equal(1) + expect(files[0].name).to.equal(fileName) + expect(files[0].type).to.equal(FILE_TYPES.file) + expect(files[0].size).to.equal(content.length) + expect(files[0].hash.startsWith('b')).to.equal(true) + }) + }) + it('fails to list non-existent file', () => { return mfs.ls('/i-do-not-exist') .then(() => { diff --git a/test/stat.spec.js b/test/stat.spec.js index 7c902ca2b0..bd3847ff48 100644 --- a/test/stat.spec.js +++ b/test/stat.spec.js @@ -9,7 +9,8 @@ const loadFixture = require('aegir/fixtures') const { createMfs, - EMPTY_DIRECTORY_HASH + EMPTY_DIRECTORY_HASH, + EMPTY_DIRECTORY_HASH_BASE32 } = require('./helpers') describe('stat', function () { @@ -86,6 +87,20 @@ describe('stat', function () { }) }) + it('returns only a base32 hash', () => { + const path = `/directory-${Math.random()}` + + return mfs.mkdir(path) + .then(() => mfs.stat(path, { + hash: true, + cidBase: 'base32' + })) + .then(stats => { + expect(Object.keys(stats).length).to.equal(1) + expect(stats.hash).to.equal(EMPTY_DIRECTORY_HASH_BASE32) + }) + }) + it('returns only the size', () => { const path = `/directory-${Math.random()}` @@ -134,4 +149,23 @@ describe('stat', function () { expect(stats.type).to.equal('file') }) }) + + it('stats a large file with base32', () => { + const filePath = '/stat/large-file.txt' + + return mfs.write(filePath, largeFile, { + create: true, + parents: true + }) + .then(() => mfs.stat(filePath, { + cidBase: 'base32' + })) + .then((stats) => { + expect(stats.hash.startsWith('b')).to.equal(true) + expect(stats.size).to.equal(largeFile.length) + expect(stats.cumulativeSize).to.equal(490800) + expect(stats.blocks).to.equal(2) + expect(stats.type).to.equal('file') + }) + }) })