-
Notifications
You must be signed in to change notification settings - Fork 489
feat: certified addressbook #683
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
305a9ed
e4645ea
b5c92f5
c34881b
864d153
5019a60
84c4f96
fe5be1a
49c0726
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,10 +9,12 @@ const multiaddr = require('multiaddr') | |
const PeerId = require('peer-id') | ||
|
||
const Book = require('./book') | ||
const PeerRecord = require('../record/peer-record') | ||
|
||
const { | ||
codes: { ERR_INVALID_PARAMETERS } | ||
} = require('../errors') | ||
const Envelope = require('../record/envelope') | ||
|
||
/** | ||
* The AddressBook is responsible for keeping the known multiaddrs | ||
|
@@ -23,8 +25,23 @@ class AddressBook extends Book { | |
* Address object | ||
* @typedef {Object} Address | ||
* @property {Multiaddr} multiaddr peer multiaddr. | ||
* @property {boolean} isCertified obtained from a signed peer record. | ||
*/ | ||
|
||
/** | ||
* CertifiedRecord object | ||
* @typedef {Object} CertifiedRecord | ||
* @property {Buffer} raw raw envelope. | ||
* @property {number} seqNumber seq counter. | ||
*/ | ||
|
||
/** | ||
* Entry object for the addressBook | ||
* @typedef {Object} Entry | ||
* @property {Array<Address>} addresses peer Addresses. | ||
* @property {CertifiedRecord} record certified peer record. | ||
*/ | ||
|
||
/** | ||
* @constructor | ||
* @param {PeerStore} peerStore | ||
|
@@ -39,16 +56,104 @@ class AddressBook extends Book { | |
peerStore, | ||
eventName: 'change:multiaddrs', | ||
eventProperty: 'multiaddrs', | ||
eventTransformer: (data) => data.map((address) => address.multiaddr) | ||
eventTransformer: (data) => { | ||
if (!data.addresses) { | ||
return [] | ||
} | ||
return data.addresses.map((address) => address.multiaddr) | ||
} | ||
}) | ||
|
||
/** | ||
* Map known peers to their known Addresses. | ||
* @type {Map<string, Array<Address>>} | ||
* Map known peers to their known Address Entries. | ||
* @type {Map<string, Array<Entry>>} | ||
*/ | ||
this.data = new Map() | ||
} | ||
|
||
/** | ||
* ConsumePeerRecord adds addresses from a signed peer record contained in a record envelope. | ||
* This will return a boolean that indicates if the record was successfully processed and added | ||
* into the AddressBook. | ||
* @param {Envelope} envelope | ||
* @return {boolean} | ||
*/ | ||
consumePeerRecord (envelope) { | ||
let peerRecord | ||
try { | ||
peerRecord = PeerRecord.createFromProtobuf(envelope.payload) | ||
} catch (err) { | ||
log.error('invalid peer record received') | ||
return false | ||
} | ||
|
||
// Verify peerId | ||
if (peerRecord.peerId.toB58String() !== envelope.peerId.toB58String()) { | ||
log('signing key does not match PeerId in the PeerRecord') | ||
return false | ||
} | ||
|
||
// ensure the record has multiaddrs | ||
if (!peerRecord.multiaddrs || !peerRecord.multiaddrs.length) { | ||
return false | ||
} | ||
|
||
const peerId = peerRecord.peerId | ||
const id = peerId.toB58String() | ||
const entry = this.data.get(id) || {} | ||
const storedRecord = entry.record | ||
|
||
// ensure seq is greater than, or equal to, the last received | ||
if (storedRecord && storedRecord.seqNumber >= peerRecord.seqNumber) { | ||
return false | ||
} | ||
|
||
const addresses = this._toAddresses(peerRecord.multiaddrs, true) | ||
|
||
// Replace unsigned addresses by the new ones from the record | ||
// TODO: Once we have ttls for the addresses, we should merge these in. | ||
this._setData(peerId, { | ||
addresses, | ||
record: { | ||
raw: envelope.marshal(), | ||
seqNumber: peerRecord.seqNumber | ||
} | ||
}) | ||
log(`stored provided peer record for ${id}`) | ||
|
||
return true | ||
} | ||
|
||
/** | ||
* Get a peer raw envelope. | ||
* @param {PeerId} peerId | ||
* @return {Buffer} | ||
vasco-santos marked this conversation as resolved.
Show resolved
Hide resolved
|
||
*/ | ||
getRawEnvelope (peerId) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we add a convenience method for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the need here? We should already have easy access to libp2p, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is not needed, but could potentially help. I did not have access to the self peerId in the rendezvous server (in the previous PR state), and I needed to change everything to provide libp2p because of that. But yeah, it was mostly a question, we don't need it, it could just help in some cases |
||
const entry = this.data.get(peerId.toB58String()) | ||
|
||
if (!entry || !entry.record || !entry.record.raw) { | ||
return undefined | ||
} | ||
|
||
return entry.record.raw | ||
} | ||
|
||
/** | ||
* Get an Envelope containing a PeerRecord for the given peer. | ||
* @param {PeerId} peerId | ||
* @return {Promise<Envelope>} | ||
vasco-santos marked this conversation as resolved.
Show resolved
Hide resolved
|
||
*/ | ||
getPeerRecord (peerId) { | ||
const raw = this.getRawEnvelope(peerId) | ||
|
||
if (!raw) { | ||
return undefined | ||
} | ||
|
||
return Envelope.createFromProtobuf(raw) | ||
} | ||
|
||
/** | ||
* Set known multiaddrs of a provided peer. | ||
* @override | ||
|
@@ -64,7 +169,8 @@ class AddressBook extends Book { | |
|
||
const addresses = this._toAddresses(multiaddrs) | ||
const id = peerId.toB58String() | ||
const rec = this.data.get(id) | ||
const entry = this.data.get(id) || {} | ||
const rec = entry.addresses | ||
|
||
// Not replace multiaddrs | ||
if (!addresses.length) { | ||
|
@@ -73,7 +179,7 @@ class AddressBook extends Book { | |
|
||
// Already knows the peer | ||
if (rec && rec.length === addresses.length) { | ||
const intersection = rec.filter((mi) => addresses.some((newMi) => mi.multiaddr.equals(newMi.multiaddr))) | ||
const intersection = rec.filter((addr) => addresses.some((newAddr) => addr.multiaddr.equals(newAddr.multiaddr))) | ||
|
||
// Are new addresses equal to the old ones? | ||
// If yes, no changes needed! | ||
|
@@ -83,7 +189,10 @@ class AddressBook extends Book { | |
} | ||
} | ||
|
||
this._setData(peerId, addresses) | ||
this._setData(peerId, { | ||
addresses, | ||
record: entry.record | ||
}) | ||
jacobheun marked this conversation as resolved.
Show resolved
Hide resolved
|
||
log(`stored provided multiaddrs for ${id}`) | ||
|
||
// Notify the existance of a new peer | ||
|
@@ -109,12 +218,14 @@ class AddressBook extends Book { | |
|
||
const addresses = this._toAddresses(multiaddrs) | ||
const id = peerId.toB58String() | ||
const rec = this.data.get(id) | ||
|
||
const entry = this.data.get(id) || {} | ||
const rec = entry.addresses | ||
vasco-santos marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// Add recorded uniquely to the new array (Union) | ||
rec && rec.forEach((mi) => { | ||
if (!addresses.find(r => r.multiaddr.equals(mi.multiaddr))) { | ||
addresses.push(mi) | ||
rec && rec.forEach((addr) => { | ||
vasco-santos marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (!addresses.find(r => r.multiaddr.equals(addr.multiaddr))) { | ||
addresses.push(addr) | ||
} | ||
}) | ||
|
||
|
@@ -125,7 +236,10 @@ class AddressBook extends Book { | |
return this | ||
} | ||
|
||
this._setData(peerId, addresses) | ||
this._setData(peerId, { | ||
addresses, | ||
record: entry.record | ||
}) | ||
|
||
log(`added provided multiaddrs for ${id}`) | ||
|
||
|
@@ -137,13 +251,30 @@ class AddressBook extends Book { | |
return this | ||
} | ||
|
||
/** | ||
* Get the known data of a provided peer. | ||
* @override | ||
* @param {PeerId} peerId | ||
* @returns {Array<data>} | ||
*/ | ||
get (peerId) { | ||
if (!PeerId.isPeerId(peerId)) { | ||
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS) | ||
} | ||
|
||
const entry = this.data.get(peerId.toB58String()) | ||
|
||
return entry && entry.addresses ? [...entry.addresses] : undefined | ||
} | ||
|
||
/** | ||
* Transforms received multiaddrs into Address. | ||
* @private | ||
* @param {Array<Multiaddr>} multiaddrs | ||
* @param {boolean} [isCertified] | ||
* @returns {Array<Address>} | ||
*/ | ||
_toAddresses (multiaddrs) { | ||
_toAddresses (multiaddrs, isCertified = false) { | ||
if (!multiaddrs) { | ||
log.error('multiaddrs must be provided to store data') | ||
throw errcode(new Error('multiaddrs must be provided'), ERR_INVALID_PARAMETERS) | ||
|
@@ -158,7 +289,8 @@ class AddressBook extends Book { | |
} | ||
|
||
addresses.push({ | ||
multiaddr: addr | ||
multiaddr: addr, | ||
isCertified | ||
}) | ||
}) | ||
|
||
|
@@ -176,13 +308,13 @@ class AddressBook extends Book { | |
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS) | ||
} | ||
|
||
const record = this.data.get(peerId.toB58String()) | ||
const entry = this.data.get(peerId.toB58String()) | ||
|
||
if (!record) { | ||
if (!entry || !entry.addresses) { | ||
return undefined | ||
} | ||
|
||
return record.map((address) => { | ||
return entry.addresses.map((address) => { | ||
const multiaddr = address.multiaddr | ||
|
||
const idString = multiaddr.getPeerId() | ||
|
Uh oh!
There was an error while loading. Please reload this page.