Skip to content

Commit

Permalink
feat: exchange signed peer records in identify
Browse files Browse the repository at this point in the history
  • Loading branch information
vasco-santos committed Jun 22, 2020
1 parent 7c29e72 commit fd20b89
Show file tree
Hide file tree
Showing 9 changed files with 367 additions and 33 deletions.
6 changes: 5 additions & 1 deletion src/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
exports.messages = {
NOT_STARTED_YET: 'The libp2p node is not started yet',
DHT_DISABLED: 'DHT is not available',
CONN_ENCRYPTION_REQUIRED: 'At least one connection encryption module is required'
CONN_ENCRYPTION_REQUIRED: 'At least one connection encryption module is required',
ERR_INVALID_ENVELOPE: 'Invalid envelope received',
ERR_INVALID_PEER_RECORD: 'Invalid peer record received'
}

exports.codes = {
Expand All @@ -20,6 +22,8 @@ exports.codes = {
ERR_DUPLICATE_TRANSPORT: 'ERR_DUPLICATE_TRANSPORT',
ERR_ENCRYPTION_FAILED: 'ERR_ENCRYPTION_FAILED',
ERR_HOP_REQUEST_FAILED: 'ERR_HOP_REQUEST_FAILED',
ERR_INVALID_ENVELOPE: 'ERR_INVALID_ENVELOPE',
ERR_INVALID_PEER_RECORD: 'ERR_INVALID_PEER_RECORD',
ERR_INVALID_KEY: 'ERR_INVALID_KEY',
ERR_INVALID_MESSAGE: 'ERR_INVALID_MESSAGE',
ERR_INVALID_PARAMETERS: 'ERR_INVALID_PARAMETERS',
Expand Down
8 changes: 6 additions & 2 deletions src/identify/consts.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,9 @@

module.exports.PROTOCOL_VERSION = 'ipfs/0.1.0'
module.exports.AGENT_VERSION = 'js-libp2p/0.1.0'
module.exports.MULTICODEC_IDENTIFY = '/ipfs/id/1.0.0'
module.exports.MULTICODEC_IDENTIFY_PUSH = '/ipfs/id/push/1.0.0'
module.exports.MULTICODEC_IDENTIFY = '/p2p/id/1.1.0'
module.exports.MULTICODEC_IDENTIFY_PUSH = '/p2p/id/push/1.1.0'

// Legacy
module.exports.MULTICODEC_IDENTIFY_LEGACY = '/ipfs/id/1.0.0'
module.exports.MULTICODEC_IDENTIFY_PUSH_LEGACY = '/ipfs/id/push/1.0.0'
168 changes: 159 additions & 9 deletions src/identify/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@ log.error = debug('libp2p:identify:error')

const {
MULTICODEC_IDENTIFY,
MULTICODEC_IDENTIFY_LEGACY,
MULTICODEC_IDENTIFY_PUSH,
MULTICODEC_IDENTIFY_PUSH_LEGACY,
AGENT_VERSION,
PROTOCOL_VERSION
} = require('./consts')

const errCode = require('err-code')
const { codes } = require('../errors')
const { messages, codes } = require('../errors')
const Envelope = require('../record-manager/envelope')
const PeerRecord = require('../record-manager/peer-record')

class IdentifyService {
/**
Expand Down Expand Up @@ -89,11 +93,27 @@ class IdentifyService {
push (connections) {
const pushes = connections.map(async connection => {
try {
const { stream } = await connection.newStream(MULTICODEC_IDENTIFY_PUSH)
const { protocol, stream } = await connection.newStream([MULTICODEC_IDENTIFY_PUSH, MULTICODEC_IDENTIFY_PUSH_LEGACY])

// Handle Legacy
if (protocol === MULTICODEC_IDENTIFY_PUSH_LEGACY) {
return pipe(
[{
listenAddrs: this._libp2p.multiaddrs.map((ma) => ma.buffer),
protocols: Array.from(this._protocols.keys())
}],
pb.encode(Message),
stream,
consume
)
}

const envelope = this._libp2p.recordManager.getPeerRecord()
const signedPeerRecord = envelope.marshal()

await pipe(
[{
listenAddrs: this._libp2p.multiaddrs.map((ma) => ma.buffer),
signedPeerRecord,
protocols: Array.from(this._protocols.keys())
}],
pb.encode(Message),
Expand Down Expand Up @@ -135,7 +155,7 @@ class IdentifyService {
* @returns {Promise<void>}
*/
async identify (connection) {
const { stream } = await connection.newStream(MULTICODEC_IDENTIFY)
const { protocol, stream } = await connection.newStream([MULTICODEC_IDENTIFY, MULTICODEC_IDENTIFY_LEGACY])
const [data] = await pipe(
[],
stream,
Expand All @@ -160,7 +180,8 @@ class IdentifyService {
publicKey,
listenAddrs,
protocols,
observedAddr
observedAddr,
signedPeerRecord
} = message

const id = await PeerId.createFromPubKey(publicKey)
Expand All @@ -172,8 +193,40 @@ class IdentifyService {
// Get the observedAddr if there is one
observedAddr = IdentifyService.getCleanMultiaddr(observedAddr)

// LEGACY: differentiate message with SignedPeerRecord
if (protocol === MULTICODEC_IDENTIFY_LEGACY) {
// Update peers data in PeerStore
this.peerStore.addressBook.set(id, listenAddrs.map((addr) => multiaddr(addr)))
this.peerStore.protoBook.set(id, protocols)

// TODO: Track our observed address so that we can score it
log('received observed address of %s', observedAddr)

return
}

// Open envelope and verify if is authenticated
let envelope
try {
envelope = await Envelope.openAndCertify(signedPeerRecord, PeerRecord.DOMAIN)
} catch (err) {
log('received invalid envelope, discard it')
throw errCode(new Error(messages.ERR_INVALID_ENVELOPE), codes.ERR_INVALID_ENVELOPE)
}

// Decode peer record
let peerRecord
try {
peerRecord = await PeerRecord.createFromProtobuf(envelope.payload)
} catch (err) {
log('received invalid peer record, discard it')
throw errCode(new Error(messages.ERR_INVALID_PEER_RECORD), codes.ERR_INVALID_PEER_RECORD)
}

// TODO: Store as certified record

// Update peers data in PeerStore
this.peerStore.addressBook.set(id, listenAddrs.map((addr) => multiaddr(addr)))
this.peerStore.addressBook.set(id, peerRecord.multiaddrs.map((addr) => multiaddr(addr)))
this.peerStore.protoBook.set(id, protocols)

// TODO: Track our observed address so that we can score it
Expand All @@ -193,16 +246,20 @@ class IdentifyService {
switch (protocol) {
case MULTICODEC_IDENTIFY:
return this._handleIdentify({ connection, stream })
case MULTICODEC_IDENTIFY_LEGACY:
return this._handleIdentifyLegacy({ connection, stream })
case MULTICODEC_IDENTIFY_PUSH:
return this._handlePush({ connection, stream })
case MULTICODEC_IDENTIFY_PUSH_LEGACY:
return this._handlePushLegacy({ connection, stream })
default:
log.error('cannot handle unknown protocol %s', protocol)
}
}

/**
* Sends the `Identify` response to the requesting peer over the
* given `connection`
* Sends the `Identify` response with the Signed Peer Record
* to the requesting peer over the given `connection`
* @private
* @param {object} options
* @param {*} options.stream
Expand All @@ -214,6 +271,40 @@ class IdentifyService {
publicKey = this.peerId.pubKey.bytes
}

const envelope = this._libp2p.recordManager.getPeerRecord()
const signedPeerRecord = envelope.marshal()

const message = Message.encode({
protocolVersion: PROTOCOL_VERSION,
agentVersion: AGENT_VERSION,
publicKey,
signedPeerRecord,
observedAddr: connection.remoteAddr.buffer,
protocols: Array.from(this._protocols.keys())
})

pipe(
[message],
lp.encode(),
stream,
consume
)
}

/**
* Sends the `Identify` response with listen addresses (LEGACY)
* to the requesting peer over the given `connection`
* @private
* @param {object} options
* @param {*} options.stream
* @param {Connection} options.connection
*/
_handleIdentifyLegacy ({ connection, stream }) {
let publicKey = Buffer.alloc(0)
if (this.peerId.pubKey) {
publicKey = this.peerId.pubKey.bytes
}

const message = Message.encode({
protocolVersion: PROTOCOL_VERSION,
agentVersion: AGENT_VERSION,
Expand Down Expand Up @@ -255,6 +346,63 @@ class IdentifyService {
return log.error('received invalid message', err)
}

// Open envelope and verify if is authenticated
let envelope
try {
envelope = await Envelope.openAndCertify(message.signedPeerRecord, PeerRecord.DOMAIN)
} catch (err) {
log('received invalid envelope, discard it')
throw errCode(new Error(messages.ERR_INVALID_ENVELOPE), codes.ERR_INVALID_ENVELOPE)
}

// Decode peer record
let peerRecord
try {
peerRecord = await PeerRecord.createFromProtobuf(envelope.payload)
} catch (err) {
log('received invalid peer record, discard it')
throw errCode(new Error(messages.ERR_INVALID_PEER_RECORD), codes.ERR_INVALID_PEER_RECORD)
}

// Update peers data in PeerStore
const id = connection.remotePeer
try {
// TODO: Store as certified record

this.peerStore.addressBook.set(id, peerRecord.multiaddrs.map((addr) => multiaddr(addr)))
} catch (err) {
return log.error('received invalid listen addrs', err)
}

// Update the protocols
this.peerStore.protoBook.set(id, message.protocols)
}

/**
* Reads the Identify Push message from the given `connection`
* with listen addresses (LEGACY)
* @private
* @param {object} options
* @param {*} options.stream
* @param {Connection} options.connection
*/
async _handlePushLegacy ({ connection, stream }) {
const [data] = await pipe(
[],
stream,
lp.decode(),
take(1),
toBuffer,
collect
)

let message
try {
message = Message.decode(data)
} catch (err) {
return log.error('received invalid message', err)
}

// Update peers data in PeerStore
const id = connection.remotePeer
try {
Expand All @@ -275,6 +423,8 @@ module.exports.IdentifyService = IdentifyService
*/
module.exports.multicodecs = {
IDENTIFY: MULTICODEC_IDENTIFY,
IDENTIFY_PUSH: MULTICODEC_IDENTIFY_PUSH
IDENTIFY_LEGACY: MULTICODEC_IDENTIFY_LEGACY,
IDENTIFY_PUSH: MULTICODEC_IDENTIFY_PUSH,
IDENTIFY_PUSH_LEGACY: MULTICODEC_IDENTIFY_PUSH_LEGACY
}
module.exports.Message = Message
5 changes: 5 additions & 0 deletions src/identify/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ message Identify {
optional bytes observedAddr = 4;
repeated string protocols = 3;
// signedPeerRecord contains a serialized SignedEnvelope containing a PeerRecord,
// signed by the sending node. It contains the same addresses as the listenAddrs field, but
// in a form that lets us share authenticated addrs with other peers.
optional bytes signedPeerRecord = 8;
}
`

Expand Down
5 changes: 4 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class Libp2p extends EventEmitter {
this.addressManager = new AddressManager(this._options.addresses)

// Records
this.RecordManager = new RecordManager(this)
this.recordManager = new RecordManager(this)

this._modules = this._options.modules
this._config = this._options.config
Expand Down Expand Up @@ -439,6 +439,9 @@ class Libp2p extends EventEmitter {
// Listen on the provided transports
await this.transportManager.listen()

// Start record Manager
await this.recordManager.start()

// Start PeerStore
await this.peerStore.start()

Expand Down
2 changes: 1 addition & 1 deletion src/record-manager/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class RecordManager {
* Get signed peer record envelope.
* @return {Envelope}
*/
getPeerRecordEnvelope () {
getPeerRecord () {
// TODO: create here if not existing?
return this._signedPeerRecord
}
Expand Down
2 changes: 2 additions & 0 deletions src/record-manager/peer-record/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,5 @@ exports.createFromProtobuf = (buf) => {

return new PeerRecord({ peerId, multiaddrs, seqNumber })
}

exports.DOMAIN = ENVELOPE_DOMAIN_PEER_RECORD
Loading

0 comments on commit fd20b89

Please sign in to comment.