Skip to content

Commit

Permalink
feat: Add support for ipfs addresses.
Browse files Browse the repository at this point in the history
  • Loading branch information
dignifiedquire committed Apr 18, 2016
1 parent 9ee0683 commit d37b73b
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 18 deletions.
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@
},
"homepage": "https://github.com/jbenet/js-multiaddr",
"dependencies": {
"babel-runtime": "^6.6.1",
"bs58": "^3.0.0",
"ip": "^1.0.2",
"lodash.filter": "^4.2.1",
"lodash.map": "^4.2.1",
"multihashes": "^0.2.1",
"varint": "^4.0.0",
"xtend": "^4.0.1"
},
Expand All @@ -52,4 +55,4 @@
"Stephen Whitmore <stephen.whitmore@gmail.com>",
"dignifiedquire <dignifiedquire@gmail.com>"
]
}
}
51 changes: 37 additions & 14 deletions src/codec.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ function stringToStringTuples (str) {
for (var p = 0; p < parts.length; p++) {
var part = parts[p]
var proto = protocols(part)

if (proto.size === 0) {
tuples.push([part])
return tuples
continue
}

p++ // advance addr part
Expand All @@ -53,6 +54,7 @@ function stringToStringTuples (str) {

tuples.push([part, parts[p]])
}

return tuples
}

Expand All @@ -66,9 +68,6 @@ function stringTuplesToString (tuples) {
parts.push(tup[1])
}
})
if (parts[parts.length - 1] === '') {
parts.pop()
}

return '/' + parts.join('/')
}
Expand Down Expand Up @@ -103,32 +102,56 @@ function tuplesToBuffer (tuples) {
return fromBuffer(Buffer.concat(map(tuples, function (tup) {
var proto = protoFromTuple(tup)
var buf = new Buffer(varint.encode(proto.code))

if (tup.length > 1) {
buf = Buffer.concat([buf, tup[1]]) // add address buffer
}

return buf
})))
}

function sizeForAddr (p, addr) {
if (p.size > 0) {
return p.size / 8
} else if (p.size === 0) {
return 0
} else {
const size = varint.decode(addr)
return size + varint.decode.bytes
}
}

// Buffer -> [[int code, Buffer ]... ]
function bufferToTuples (buf) {
var tuples = []
for (var i = 0; i < buf.length;) {
var code = varint.decode(buf, i)

var proto = protocols(code)
var size = (proto.size / 8)
code = Number(code)
var addr = buf.slice(i + 1, i + 1 + size)
i += 1 + size
const tuples = []
let i = 0
while (i < buf.length) {
const code = varint.decode(buf, i)
const n = varint.decode.bytes

const p = protocols(code)

const size = sizeForAddr(p, buf.slice(i + n))

if (size === 0) {
tuples.push([code])
i += n
continue
}

const addr = buf.slice(i + n, i + n + size)

i += (size + n)

if (i > buf.length) { // did not end _exactly_ at buffer.length
throw ParseError('Invalid address buffer: ' + buf.toString('hex'))
}

// ok, tuple seems good.
tuples.push([code, addr])
i = i + varint.decode.bytes - 1
}

return tuples
}

Expand Down
26 changes: 26 additions & 0 deletions src/convert.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

var ip = require('ip')
var protocols = require('./protocols')
var bs58 = require('bs58')
var varint = require('varint')

module.exports = Convert

Expand All @@ -26,6 +28,9 @@ Convert.toString = function convertToString (proto, buf) {
case 33: // dccp
case 132: // sctp
return buf2port(buf)

case 421: // ipfs
return buf2mh(buf)
default:
return buf.toString('hex') // no clue. convert to hex
}
Expand All @@ -43,6 +48,9 @@ Convert.toBuffer = function convertToBuffer (proto, str) {
case 33: // dccp
case 132: // sctp
return port2buf(parseInt(str, 10))

case 421: // ipfs
return mh2buf(str)
default:
return new Buffer(str, 'hex') // no clue. convert from hex
}
Expand All @@ -57,3 +65,21 @@ function port2buf (port) {
function buf2port (buf) {
return buf.readUInt16BE(0)
}

function mh2buf (hash) {
// the address is a varint prefixed multihash string representation
const mh = new Buffer(bs58.decode(hash))
const size = new Buffer(varint.encode(mh.length))
return Buffer.concat([size, mh])
}

function buf2mh (buf) {
const size = varint.decode(buf)
const address = buf.slice(varint.decode.bytes)

if (address.length !== size) {
throw new Error('inconsistent lengths')
}

return bs58.encode(address)
}
3 changes: 3 additions & 0 deletions src/protocols.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ function Protocols (proto) {
throw new Error('invalid protocol id type: ' + proto)
}

Protocols.lengthPrefixedVarSize = -1

// replicating table here to:
// 1. avoid parsing the csv
// 2. ensuring errors in the csv don't screw up code.
Expand All @@ -36,6 +38,7 @@ Protocols.table = [
[132, 16, 'sctp'],
// these require varint for the protocol code
[302, 0, 'utp'],
[421, Protocols.lengthPrefixedVarSize, 'ipfs'],
[480, 0, 'http'],
[443, 0, 'https'],
[477, 0, 'websockets']
Expand Down
19 changes: 19 additions & 0 deletions test/convert.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,23 @@ describe('convert', () => {
)
})
})

describe('.toString', () => {
it('throws on inconsistent ipfs links', () => {
const valid = new Buffer('03221220d52ebb89d85b02a284948203a62ff28389c57c9f42beec4ec20db76a68911c0b', 'hex')
expect(
() => convert.toString('ipfs', valid.slice(0, valid.length - 8))
).to.throw(
/inconsistent length/
)
})

it('defaults to hex conversion', () => {
expect(
convert.toString('websockets', new Buffer([192, 168, 0, 1]))
).to.be.eql(
'c0a80001'
)
})
})
})
35 changes: 32 additions & 3 deletions test/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,20 @@ describe('variants', () => {
expect(addr.toString()).to.equal(str)
})

it('ip4 + ipfs', () => {
const str = '/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234'
const addr = multiaddr(str)
expect(addr).to.have.property('buffer')
expect(addr.toString()).to.equal(str)
})

it('ip6 + ipfs', () => {
const str = '/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234'
const addr = multiaddr(str)
expect(addr).to.have.property('buffer')
expect(addr.toString()).to.equal(str)
})

it.skip('ip4 + dccp', () => {})
it.skip('ip6 + dccp', () => {})

Expand Down Expand Up @@ -207,6 +221,21 @@ describe('variants', () => {
expect(addr).to.have.property('buffer')
expect(addr.toString()).to.equal(str)
})

it('ip6 + tcp + websockets + ipfs', () => {
const str = '/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095/tcp/8000/websockets/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC'
const addr = multiaddr(str)
expect(addr).to.have.property('buffer')
expect(addr.toString()).to.equal(str)
})

it('ipfs', () => {
const str = '/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC'
const addr = multiaddr(str)
expect(addr).to.have.property('buffer')
console.log(addr.buffer.toString('hex'))
expect(addr.toString()).to.equal(str)
})
})

describe('helpers', () => {
Expand Down Expand Up @@ -254,7 +283,7 @@ describe('helpers', () => {
multiaddr('/ip4/0.0.0.0/utp').tuples()
).to.be.eql([
[4, new Buffer([0, 0, 0, 0])],
[302, new Buffer([])]
[302]
])
})
})
Expand All @@ -265,7 +294,7 @@ describe('helpers', () => {
multiaddr('/ip4/0.0.0.0/utp').stringTuples()
).to.be.eql([
[4, '0.0.0.0'],
[302, '']
[302]
])
})
})
Expand Down Expand Up @@ -389,7 +418,7 @@ describe('helpers', () => {
)

expect(
multiaddr('/http/0.0.0.0/utp').isThinWaistAddress()
multiaddr('/http/utp').isThinWaistAddress()
).to.be.eql(
false
)
Expand Down

0 comments on commit d37b73b

Please sign in to comment.