2021-09-08 00:36:52 +10:00
use crate ::signed_envelope ::SignedEnvelope ;
2023-03-13 01:46:58 +11:00
use crate ::{ proto , signed_envelope , DecodeError , Multiaddr } ;
2021-10-30 12:41:30 +02:00
use instant ::SystemTime ;
2023-03-13 01:46:58 +11:00
use libp2p_identity ::Keypair ;
use libp2p_identity ::PeerId ;
use libp2p_identity ::SigningError ;
2023-03-02 05:45:07 -05:00
use quick_protobuf ::{ BytesReader , Writer } ;
2021-09-08 00:36:52 +10:00
use std ::convert ::TryInto ;
const PAYLOAD_TYPE : & str = " /libp2p/routing-state-record " ;
const DOMAIN_SEP : & str = " libp2p-routing-state " ;
/// Represents a peer routing record.
///
/// Peer records are designed to be distributable and carry a signature by being wrapped in a signed envelope.
/// For more information see RFC0003 of the libp2p specifications: <https://github.com/libp2p/specs/blob/master/RFC/0003-routing-records.md>
2022-08-14 04:03:04 +02:00
#[ derive(Debug, PartialEq, Eq, Clone) ]
2021-09-08 00:36:52 +10:00
pub struct PeerRecord {
peer_id : PeerId ,
seq : u64 ,
addresses : Vec < Multiaddr > ,
/// A signed envelope representing this [`PeerRecord`].
///
/// If this [`PeerRecord`] was constructed from a [`SignedEnvelope`], this is the original instance.
envelope : SignedEnvelope ,
}
impl PeerRecord {
/// Attempt to re-construct a [`PeerRecord`] from a [`SignedEnvelope`].
///
/// If this function succeeds, the [`SignedEnvelope`] contained a peer record with a valid signature and can hence be considered authenticated.
pub fn from_signed_envelope ( envelope : SignedEnvelope ) -> Result < Self , FromEnvelopeError > {
2023-03-02 05:45:07 -05:00
use quick_protobuf ::MessageRead ;
2021-09-08 00:36:52 +10:00
2022-02-21 01:45:57 -08:00
let ( payload , signing_key ) =
envelope . payload_and_signing_key ( String ::from ( DOMAIN_SEP ) , PAYLOAD_TYPE . as_bytes ( ) ) ? ;
2023-03-02 05:45:07 -05:00
let mut reader = BytesReader ::from_bytes ( payload ) ;
2023-05-12 04:26:01 +02:00
let record = proto ::PeerRecord ::from_reader ( & mut reader , payload ) . map_err ( DecodeError ) ? ;
2021-09-08 00:36:52 +10:00
let peer_id = PeerId ::from_bytes ( & record . peer_id ) ? ;
2022-02-09 16:39:02 +01:00
2022-02-21 01:45:57 -08:00
if peer_id ! = signing_key . to_peer_id ( ) {
2022-02-09 16:39:02 +01:00
return Err ( FromEnvelopeError ::MismatchedSignature ) ;
}
2021-09-08 00:36:52 +10:00
let seq = record . seq ;
let addresses = record
. addresses
. into_iter ( )
2023-03-02 05:45:07 -05:00
. map ( | a | a . multiaddr . to_vec ( ) . try_into ( ) )
2021-09-08 00:36:52 +10:00
. collect ::< Result < Vec < _ > , _ > > ( ) ? ;
Ok ( Self {
peer_id ,
seq ,
addresses ,
envelope ,
} )
}
/// Construct a new [`PeerRecord`] by authenticating the provided addresses with the given key.
///
/// This is the same key that is used for authenticating every libp2p connection of your application, i.e. what you use when setting up your [`crate::transport::Transport`].
2022-02-15 23:47:32 +01:00
pub fn new ( key : & Keypair , addresses : Vec < Multiaddr > ) -> Result < Self , SigningError > {
2023-03-02 05:45:07 -05:00
use quick_protobuf ::MessageWrite ;
2021-09-08 00:36:52 +10:00
let seq = SystemTime ::now ( )
. duration_since ( SystemTime ::UNIX_EPOCH )
. expect ( " now() is never before UNIX_EPOCH " )
. as_secs ( ) ;
let peer_id = key . public ( ) . to_peer_id ( ) ;
let payload = {
2023-03-02 05:45:07 -05:00
let record = proto ::PeerRecord {
2021-09-08 00:36:52 +10:00
peer_id : peer_id . to_bytes ( ) ,
seq ,
addresses : addresses
. iter ( )
2023-03-02 05:45:07 -05:00
. map ( | m | proto ::AddressInfo {
2021-09-08 00:36:52 +10:00
multiaddr : m . to_vec ( ) ,
} )
. collect ( ) ,
} ;
2023-03-02 05:45:07 -05:00
let mut buf = Vec ::with_capacity ( record . get_size ( ) ) ;
let mut writer = Writer ::new ( & mut buf ) ;
2021-09-08 00:36:52 +10:00
record
2023-03-02 05:45:07 -05:00
. write_message ( & mut writer )
. expect ( " Encoding to succeed " ) ;
2021-09-08 00:36:52 +10:00
buf
} ;
let envelope = SignedEnvelope ::new (
2022-04-19 12:13:45 +02:00
key ,
2021-09-08 00:36:52 +10:00
String ::from ( DOMAIN_SEP ) ,
PAYLOAD_TYPE . as_bytes ( ) . to_vec ( ) ,
payload ,
) ? ;
Ok ( Self {
peer_id ,
seq ,
addresses ,
envelope ,
} )
}
pub fn to_signed_envelope ( & self ) -> SignedEnvelope {
self . envelope . clone ( )
}
pub fn into_signed_envelope ( self ) -> SignedEnvelope {
self . envelope
}
pub fn peer_id ( & self ) -> PeerId {
self . peer_id
}
pub fn seq ( & self ) -> u64 {
self . seq
}
pub fn addresses ( & self ) -> & [ Multiaddr ] {
self . addresses . as_slice ( )
}
}
2022-11-02 23:02:21 +11:00
#[ derive(thiserror::Error, Debug) ]
2021-09-08 00:36:52 +10:00
pub enum FromEnvelopeError {
/// Failed to extract the payload from the envelope.
2022-11-02 23:02:21 +11:00
#[ error( " Failed to extract payload from envelope " ) ]
BadPayload ( #[ from ] signed_envelope ::ReadPayloadError ) ,
2021-09-08 00:36:52 +10:00
/// Failed to decode the provided bytes as a [`PeerRecord`].
2022-11-02 23:02:21 +11:00
#[ error( " Failed to decode bytes as PeerRecord " ) ]
InvalidPeerRecord ( #[ from ] DecodeError ) ,
2021-09-08 00:36:52 +10:00
/// Failed to decode the peer ID.
2022-11-02 23:02:21 +11:00
#[ error( " Failed to decode bytes as PeerId " ) ]
InvalidPeerId ( #[ from ] multihash ::Error ) ,
2022-02-09 16:39:02 +01:00
/// The signer of the envelope is different than the peer id in the record.
2022-11-02 23:02:21 +11:00
#[ error( " The signer of the envelope is different than the peer id in the record " ) ]
2022-02-09 16:39:02 +01:00
MismatchedSignature ,
2021-09-08 00:36:52 +10:00
/// Failed to decode a multi-address.
2022-11-02 23:02:21 +11:00
#[ error( " Failed to decode bytes as MultiAddress " ) ]
InvalidMultiaddr ( #[ from ] multiaddr ::Error ) ,
2021-09-08 00:36:52 +10:00
}
#[ cfg(test) ]
mod tests {
use super ::* ;
const HOME : & str = " /ip4/127.0.0.1/tcp/1337 " ;
#[ test ]
fn roundtrip_envelope ( ) {
2022-02-15 23:47:32 +01:00
let key = Keypair ::generate_ed25519 ( ) ;
let record = PeerRecord ::new ( & key , vec! [ HOME . parse ( ) . unwrap ( ) ] ) . unwrap ( ) ;
2021-09-08 00:36:52 +10:00
let envelope = record . to_signed_envelope ( ) ;
let reconstructed = PeerRecord ::from_signed_envelope ( envelope ) . unwrap ( ) ;
assert_eq! ( reconstructed , record )
}
2022-02-09 16:39:02 +01:00
#[ test ]
fn mismatched_signature ( ) {
2023-03-02 05:45:07 -05:00
use quick_protobuf ::MessageWrite ;
2022-02-09 16:39:02 +01:00
let addr : Multiaddr = HOME . parse ( ) . unwrap ( ) ;
let envelope = {
let identity_a = Keypair ::generate_ed25519 ( ) ;
let identity_b = Keypair ::generate_ed25519 ( ) ;
let payload = {
2023-03-02 05:45:07 -05:00
let record = proto ::PeerRecord {
2022-02-09 16:39:02 +01:00
peer_id : identity_a . public ( ) . to_peer_id ( ) . to_bytes ( ) ,
seq : 0 ,
2023-03-02 05:45:07 -05:00
addresses : vec ! [ proto ::AddressInfo {
2022-02-09 16:39:02 +01:00
multiaddr : addr . to_vec ( ) ,
} ] ,
} ;
2023-03-02 05:45:07 -05:00
let mut buf = Vec ::with_capacity ( record . get_size ( ) ) ;
let mut writer = Writer ::new ( & mut buf ) ;
2022-02-09 16:39:02 +01:00
record
2023-03-02 05:45:07 -05:00
. write_message ( & mut writer )
. expect ( " Encoding to succeed " ) ;
2022-02-09 16:39:02 +01:00
buf
} ;
SignedEnvelope ::new (
2022-02-15 23:47:32 +01:00
& identity_b ,
2022-02-09 16:39:02 +01:00
String ::from ( DOMAIN_SEP ) ,
PAYLOAD_TYPE . as_bytes ( ) . to_vec ( ) ,
payload ,
)
. unwrap ( )
} ;
assert! ( matches! (
PeerRecord ::from_signed_envelope ( envelope ) ,
Err ( FromEnvelopeError ::MismatchedSignature )
) ) ;
}
2021-09-08 00:36:52 +10:00
}