Skip to content
This repository was archived by the owner on Feb 12, 2024. It is now read-only.

Commit 219c260

Browse files
committed
feat: ipns over dht
1 parent 013f527 commit 219c260

File tree

9 files changed

+435
-202
lines changed

9 files changed

+435
-202
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,10 @@
127127
"joi": "^13.4.0",
128128
"joi-browser": "^13.4.0",
129129
"joi-multiaddr": "^3.0.0",
130-
"libp2p": "~0.24.0",
130+
"libp2p": "~0.24.1",
131131
"libp2p-bootstrap": "~0.9.3",
132132
"libp2p-crypto": "~0.14.1",
133-
"libp2p-kad-dht": "~0.11.1",
133+
"libp2p-kad-dht": "~0.12.1",
134134
"libp2p-keychain": "~0.3.3",
135135
"libp2p-mdns": "~0.12.0",
136136
"libp2p-mplex": "~0.8.4",

src/core/components/libp2p.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
const promisify = require('promisify-es6')
44
const get = require('lodash/get')
55
const defaultsDeep = require('@nodeutils/defaults-deep')
6+
const ipnsUtils = require('../ipns/routing/utils')
67

78
module.exports = function libp2p (self) {
89
return {
@@ -16,6 +17,7 @@ module.exports = function libp2p (self) {
1617

1718
const defaultBundle = (opts) => {
1819
const libp2pDefaults = {
20+
datastore: opts.datastore,
1921
peerInfo: opts.peerInfo,
2022
peerBook: opts.peerBook,
2123
config: {
@@ -43,6 +45,14 @@ module.exports = function libp2p (self) {
4345
get(opts.config, 'relay.hop.active', false))
4446
}
4547
},
48+
dht: {
49+
validators: {
50+
ipns: ipnsUtils.validator
51+
},
52+
selectors: {
53+
ipns: ipnsUtils.selector
54+
}
55+
},
4656
EXPERIMENTAL: {
4757
dht: get(opts.options, 'EXPERIMENTAL.dht', false),
4858
pubsub: get(opts.options, 'EXPERIMENTAL.pubsub', false)
@@ -72,6 +82,7 @@ module.exports = function libp2p (self) {
7282
self._libp2pNode = libp2pBundle({
7383
options: self._options,
7484
config: config,
85+
datastore: self._repo.datastore,
7586
peerInfo: self._peerInfo,
7687
peerBook: self._peerInfoBook
7788
})

src/core/components/start.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,14 @@ module.exports = (self) => {
5454
ipnsStores.push(pubsubDs)
5555
}
5656

57-
// NOTE: IPNS routing is being replaced by the local repo datastore while the IPNS over DHT is not ready
58-
// When DHT is added, if local option enabled, should receive offlineDatastore as well
59-
const offlineDatastore = new OfflineDatastore(self._repo)
60-
ipnsStores.push(offlineDatastore)
57+
// DHT should be added as routing if we are not running with local flag
58+
// TODO: Need to change this logic once DHT is enabled by default, for now fallback to Offline datastore
59+
if (get(self._options, 'EXPERIMENTAL.dht', false) && !self._options.local) {
60+
ipnsStores.push(self._libp2pNode.dht)
61+
} else {
62+
const offlineDatastore = new OfflineDatastore(self._repo)
63+
ipnsStores.push(offlineDatastore)
64+
}
6165

6266
// Create ipns routing with a set of datastores
6367
const routing = new TieredDatastore(ipnsStores)

src/core/ipns/publisher.js

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ class IpnsPublisher {
5656
log.error(errMsg)
5757
return callback(errcode(new Error(errMsg), 'ERR_INVALID_PEER_ID'))
5858
}
59-
6059
const publicKey = peerId._pubKey
6160

6261
ipns.embedPublicKey(publicKey, record, (err, embedPublicKeyRecord) => {
@@ -162,45 +161,55 @@ class IpnsPublisher {
162161
const checkRouting = !(options.checkRouting === false)
163162

164163
this._repo.datastore.get(ipns.getLocalKey(peerId.id), (err, dsVal) => {
165-
let result
166-
167164
if (err) {
168165
if (err.code !== 'ERR_NOT_FOUND') {
169166
const errMsg = `unexpected error getting the ipns record ${peerId.id} from datastore`
170167

171168
log.error(errMsg)
172169
return callback(errcode(new Error(errMsg), 'ERR_UNEXPECTED_DATASTORE_RESPONSE'))
173-
} else {
174-
if (!checkRouting) {
170+
}
171+
172+
if (!checkRouting) {
173+
return callback(null, null)
174+
}
175+
176+
// Try to get from routing
177+
let keys
178+
try {
179+
keys = ipns.getIdKeys(peerId.toBytes())
180+
} catch (err) {
181+
log.error(err)
182+
return callback(err)
183+
}
184+
185+
this._routing.get(keys.routingKey, (err, res) => {
186+
if (err) {
187+
log(`error when determining the last published IPNS record for ${peerId.id}`)
175188
return callback(null, null)
176-
} else {
177-
// TODO ROUTING - get from DHT
178-
return callback(new Error('not implemented yet'))
179189
}
180-
}
181-
}
182190

183-
if (Buffer.isBuffer(dsVal)) {
184-
result = dsVal
191+
// unmarshal data
192+
this._unmarshalData(res, callback)
193+
})
185194
} else {
186-
const errMsg = `found ipns record that we couldn't convert to a value`
187-
188-
log.error(errMsg)
189-
return callback(errcode(new Error(errMsg), 'ERR_INVALID_IPNS_RECORD'))
195+
// unmarshal data
196+
this._unmarshalData(dsVal, callback)
190197
}
198+
})
199+
}
191200

192-
// unmarshal data
193-
try {
194-
result = ipns.unmarshal(dsVal)
195-
} catch (err) {
196-
const errMsg = `found ipns record that we couldn't convert to a value`
201+
_unmarshalData (data, callback) {
202+
let result
203+
try {
204+
result = ipns.unmarshal(data)
205+
} catch (err) {
206+
const errMsg = `found ipns record that we couldn't convert to a value`
197207

198-
log.error(errMsg)
199-
return callback(null, null)
200-
}
208+
log.error(errMsg)
209+
return callback(null, null)
210+
}
201211

202-
callback(null, result)
203-
})
212+
callback(null, result)
204213
}
205214

206215
_updateOrCreateRecord (privKey, value, validity, peerId, callback) {
@@ -212,7 +221,7 @@ class IpnsPublisher {
212221
}
213222

214223
const getPublishedOptions = {
215-
checkRouting: false // TODO ROUTING - change to true
224+
checkRouting: true
216225
}
217226

218227
this._getPublished(peerId, getPublishedOptions, (err, record) => {

src/core/ipns/resolver.js

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
'use strict'
22

33
const ipns = require('ipns')
4+
const crypto = require('libp2p-crypto')
45
const PeerId = require('peer-id')
56
const errcode = require('err-code')
7+
const parallel = require('async/parallel')
68

79
const debug = require('debug')
810
const log = debug('jsipfs:ipns:resolver')
@@ -96,13 +98,14 @@ class IpnsResolver {
9698
return callback(err)
9799
}
98100

99-
const { routingKey } = ipns.getIdKeys(peerId.toBytes())
101+
const { routingKey, routingPubKey } = ipns.getIdKeys(peerId.toBytes())
100102

101-
// TODO DHT - get public key from routing?
102-
// https://github.com/ipfs/go-ipfs/blob/master/namesys/routing.go#L70
103-
// https://github.com/libp2p/go-libp2p-routing/blob/master/routing.go#L99
104-
105-
this._routing.get(routingKey.toBuffer(), (err, res) => {
103+
parallel([
104+
// Name should be the hash of a public key retrievable from ipfs.
105+
// We retrieve public key to add it to the PeerId, as the IPNS record may not have it.
106+
(cb) => this._routing.get(routingPubKey.toBuffer(), cb),
107+
(cb) => this._routing.get(routingKey.toBuffer(), cb)
108+
], (err, res) => {
106109
if (err) {
107110
if (err.code !== 'ERR_NOT_FOUND') {
108111
const errMsg = `unexpected error getting the ipns record ${peerId.id}`
@@ -116,9 +119,21 @@ class IpnsResolver {
116119
return callback(errcode(new Error(errMsg), 'ERR_NO_RECORD_FOUND'))
117120
}
118121

122+
// Public key
123+
try {
124+
// Insert it into the peer id public key, to be validated by IPNS validator
125+
peerId.pubKey = crypto.keys.unmarshalPublicKey(res[0])
126+
} catch (err) {
127+
const errMsg = `found public key record that we couldn't convert to a value`
128+
129+
log.error(errMsg)
130+
return callback(errcode(new Error(errMsg), 'ERR_INVALID_PUB_KEY_RECEIVED'))
131+
}
132+
133+
// IPNS entry
119134
let ipnsEntry
120135
try {
121-
ipnsEntry = ipns.unmarshal(res)
136+
ipnsEntry = ipns.unmarshal(res[1])
122137
} catch (err) {
123138
const errMsg = `found ipns record that we couldn't convert to a value`
124139

src/core/ipns/routing/utils.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
'use strict'
22

33
const multibase = require('multibase')
4+
const ipns = require('ipns')
45

5-
module.exports.encodeBase32 = (buf) => {
6-
const m = multibase.encode('base32', buf).slice(1) // slice off multibase codec
6+
module.exports = {
7+
encodeBase32: (buf) => {
8+
const m = multibase.encode('base32', buf).slice(1) // slice off multibase codec
79

8-
return m.toString().toUpperCase() // should be uppercase for interop with go
10+
return m.toString().toUpperCase() // should be uppercase for interop with go
11+
},
12+
validator: {
13+
func: (key, record, cb) => ipns.validator.validate(record, key, cb)
14+
},
15+
selector: (k, records) => ipns.validator.select(records[0], records[1])
916
}

0 commit comments

Comments
 (0)