Used to transfer signed peer data across the network
Libp2p nodes need to store data in a public location (e.g. a DHT), or rely on potentially untrustworthy intermediaries to relay information over its lifetime. Accordingly, libp2p nodes need to be able to verify that the data came from a specific peer and that it hasn't been tampered with.
Libp2p provides an all-purpose data container called envelope. It was created to enable the distribution of verifiable records, which we can prove originated from the addressed peer itself. The envelope includes a signature of the data, so that its authenticity is verified.
This envelope stores a marshaled record implementing the interface-record. These Records are designed to be serialized to bytes and placed inside of the envelopes before being shared with other peers.
You can read further about the envelope in RFC 0002 - Signed Envelopes. For the original discussion about it you can look at the PR that was used to create it: libp2p/specs#217.
Create an envelope with an instance of an interface-record implementation and prepare it for being exchanged:
import { PeerRecord, RecordEnvelope } from '@libp2p/peer-record'
import { generateKeyPair } from '@libp2p/crypto/keys'
import { peerIdFromPrivateKey } from '@libp2p/peer-id'
const privateKey = await generateKeyPair('Ed25519')
const peerId = peerIdFromPrivateKey(privateKey)
const record = new PeerRecord({
peerId,
// ...other data
})
const envelope = await RecordEnvelope.seal(record, privateKey)
const wireData = envelope.marshal()
Consume a received envelope wireData
and transform it back to a record:
import { PeerRecord, RecordEnvelope } from '@libp2p/peer-record'
const wireData = Uint8Array.from([0, 1, 2, 3, 4])
const envelope = await RecordEnvelope.openAndCertify(wireData, PeerRecord.DOMAIN)
const record = PeerRecord.createFromProtobuf(envelope.payload)
All libp2p nodes keep a PeerStore
, that among other information stores a set of known addresses for each peer, which can come from a variety of sources.
Libp2p peer records were created to enable the distribution of verifiable address records, which we can prove originated from the addressed peer itself. With such guarantees, libp2p is able to prioritize addresses based on their authenticity, with the most strict strategy being to only dial certified addresses (no strategies have been implemented at the time of writing).
A peer record contains the peers' publicly reachable listen addresses, and may be extended in the future to contain additional metadata relevant to routing. It also contains a seqNumber
field, a timestamp per the spec, so that we can verify the most recent record.
You can read further about the Peer Record in RFC 0003 - Peer Routing Records. For the original discussion about it you can view the PR that created the RFC: libp2p/specs#217.
Create a new Peer Record
import { PeerRecord } from '@libp2p/peer-record'
import { peerIdFromPrivateKey } from '@libp2p/peer-id'
import { generateKeyPair } from '@libp2p/crypto/keys'
import { multiaddr } from '@multiformats/multiaddr'
const peerId = peerIdFromPrivateKey(await generateKeyPair('Ed25519'))
const record = new PeerRecord({
peerId: peerId,
multiaddrs: [
multiaddr('/ip4/...'),
multiaddr('/ip4/...')
]
})
Create a Peer Record from a protobuf
import { PeerRecord } from '@libp2p/peer-record'
const data = Uint8Array.from([0, 1, 2, 3, 4])
const record = PeerRecord.createFromProtobuf(data)
Once a libp2p node has started and is listening on a set of multiaddrs, its own peer record can be created.
The identify service is responsible for creating the self record when the identify protocol kicks in for the first time. This record will be stored for future needs of the identify protocol when connecting with other peers.
While creating peer records is fairly trivial, addresses are not static and might be modified at arbitrary times. This can happen via an Address Manager API, or even through AutoRelay/AutoNAT.
When a libp2p node changes its listen addresses, the identify service will be informed. Once that happens, the identify service creates a new self record and stores it. With the new record, the identify push/delta protocol will be used to communicate this change to the connected peers.
Considering that a node can discover other peers' addresses from a variety of sources, Libp2p Peerstore can differentiate the addresses that were obtained through a signed peer record.
Once a record is received and its signature properly validated, its envelope is stored in the AddressBook in its byte representation. The seqNumber
remains unmarshalled so that we can quickly compare it against incoming records to determine the most recent record.
The AddressBook Addresses will be updated with the content of the envelope with a certified property. This allows other subsystems to identify the known certified addresses of a peer.
Libp2p subsystems that exchange other peers information will provide the envelope that they received by those peers. As a result, other peers can verify if the envelope was really created by the addressed peer.
When a subsystem wants to provide a record, it will get it from the AddressBook, if it exists. Other subsystems are also able to provide the self record, since it is also stored in the AddressBook.
$ npm i @libp2p/peer-record
<script>
tagLoading this module through a script tag will make it's exports available as Libp2pPeerRecord
in the global namespace.
<script src="https://unpkg.com/@libp2p/peer-record/dist/index.min.js"></script>
Licensed under either of
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Libp2p nodes need to store data in a public location (e.g. a DHT), or rely on potentially untrustworthy intermediaries to relay information over its lifetime. Accordingly, libp2p nodes need to be able to verify that the data came from a specific peer and that it hasn't been tampered with.
Envelope
Libp2p provides an all-purpose data container called envelope. It was created to enable the distribution of verifiable records, which we can prove originated from the addressed peer itself. The envelope includes a signature of the data, so that its authenticity is verified.
This envelope stores a marshaled record implementing the interface-record. These Records are designed to be serialized to bytes and placed inside of the envelopes before being shared with other peers.
You can read further about the envelope in RFC 0002 - Signed Envelopes. For the original discussion about it you can look at the PR that was used to create it: libp2p/specs#217.
Example: Creating a peer record
Create an envelope with an instance of an interface-record implementation and prepare it for being exchanged:
Example: Consuming a peer record
Consume a received envelope
wireData
and transform it back to a record:Peer Record
All libp2p nodes keep a
PeerStore
, that among other information stores a set of known addresses for each peer, which can come from a variety of sources.Libp2p peer records were created to enable the distribution of verifiable address records, which we can prove originated from the addressed peer itself. With such guarantees, libp2p is able to prioritize addresses based on their authenticity, with the most strict strategy being to only dial certified addresses (no strategies have been implemented at the time of writing).
A peer record contains the peers' publicly reachable listen addresses, and may be extended in the future to contain additional metadata relevant to routing. It also contains a
seqNumber
field, a timestamp per the spec, so that we can verify the most recent record.You can read further about the Peer Record in RFC 0003 - Peer Routing Records. For the original discussion about it you can view the PR that created the RFC: libp2p/specs#217.
Example
Create a new Peer Record
Example
Create a Peer Record from a protobuf
Libp2p Flows
Self Record
Once a libp2p node has started and is listening on a set of multiaddrs, its own peer record can be created.
The identify service is responsible for creating the self record when the identify protocol kicks in for the first time. This record will be stored for future needs of the identify protocol when connecting with other peers.
Self record Updates
While creating peer records is fairly trivial, addresses are not static and might be modified at arbitrary times. This can happen via an Address Manager API, or even through AutoRelay/AutoNAT.
When a libp2p node changes its listen addresses, the identify service will be informed. Once that happens, the identify service creates a new self record and stores it. With the new record, the identify push/delta protocol will be used to communicate this change to the connected peers.
Subsystem receiving a record
Considering that a node can discover other peers' addresses from a variety of sources, Libp2p Peerstore can differentiate the addresses that were obtained through a signed peer record.
Once a record is received and its signature properly validated, its envelope is stored in the AddressBook in its byte representation. The
seqNumber
remains unmarshalled so that we can quickly compare it against incoming records to determine the most recent record.The AddressBook Addresses will be updated with the content of the envelope with a certified property. This allows other subsystems to identify the known certified addresses of a peer.
Subsystem providing a record
Libp2p subsystems that exchange other peers information will provide the envelope that they received by those peers. As a result, other peers can verify if the envelope was really created by the addressed peer.
When a subsystem wants to provide a record, it will get it from the AddressBook, if it exists. Other subsystems are also able to provide the self record, since it is also stored in the AddressBook.
Future Work