Files
Jędrzej Stuczyński b429c64168 [demo] libp2p example with nym-sdk (#3763)
* imported libp2p to monorepo

* fixed vanilla ping example

* added libp2p client to workspace

* naively replaced dockerised client with the sdk

* moved libp2p code to sdk examples

* reduced number of dependencies required for libp2p example

* updated readmes

* added protobuf compiler to build dependencies

* added protoc dependency to readme for chat and ping examples

---------

Co-authored-by: mfahampshire <maxhampshire@pm.me>
2023-08-22 13:12:13 +02:00

336 lines
10 KiB
Rust

use libp2p::core::PeerId;
use nym_sphinx::addressing::clients::Recipient;
use rand::rngs::OsRng;
use rand::RngCore;
use std::fmt::{Debug, Formatter};
use super::error::Error;
const RECIPIENT_LENGTH: usize = Recipient::LEN;
const CONNECTION_ID_LENGTH: usize = 32;
const SUBSTREAM_ID_LENGTH: usize = 32;
const NONCE_BYTES_LEN: usize = 8; // length of u64
const MIN_CONNECTION_MESSAGE_LEN: usize = CONNECTION_ID_LENGTH + NONCE_BYTES_LEN;
/// ConnectionId is a unique, randomly-generated per-connection ID that's used to
/// identify which connection a message belongs to.
#[derive(Clone, Default, Eq, Hash, PartialEq)]
pub(crate) struct ConnectionId([u8; 32]);
impl ConnectionId {
pub(crate) fn generate() -> Self {
let mut bytes = [0u8; 32];
OsRng.fill_bytes(&mut bytes);
ConnectionId(bytes)
}
fn from_bytes(bytes: &[u8]) -> Self {
let mut id = [0u8; 32];
id[..].copy_from_slice(&bytes[0..CONNECTION_ID_LENGTH]);
ConnectionId(id)
}
}
impl Debug for ConnectionId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", hex::encode(self.0))
}
}
/// SubstreamId is a unique, randomly-generated per-substream ID that's used to
/// identify which substream a message belongs to.
#[derive(Clone, Default, Eq, Hash, PartialEq)]
pub struct SubstreamId(pub(crate) [u8; 32]);
impl SubstreamId {
pub(crate) fn generate() -> Self {
let mut bytes = [0u8; 32];
OsRng.fill_bytes(&mut bytes);
SubstreamId(bytes)
}
fn from_bytes(bytes: &[u8]) -> Self {
let mut id = [0u8; 32];
id[..].copy_from_slice(&bytes[0..SUBSTREAM_ID_LENGTH]);
SubstreamId(id)
}
}
impl Debug for SubstreamId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", hex::encode(self.0))
}
}
#[derive(Debug)]
#[allow(clippy::enum_variant_names)]
pub(crate) enum Message {
ConnectionRequest(ConnectionMessage),
ConnectionResponse(ConnectionMessage),
TransportMessage(TransportMessage),
}
/// ConnectionMessage is exchanged to open a new connection.
#[derive(Debug)]
pub(crate) struct ConnectionMessage {
pub(crate) peer_id: PeerId,
pub(crate) id: ConnectionId,
/// recipient is the sender's Nym address.
/// only required if this is a ConnectionRequest.
pub(crate) recipient: Option<Recipient>,
}
/// TransportMessage is sent over a connection after establishment.
#[derive(Debug, Clone)]
pub(crate) struct TransportMessage {
/// increments by 1 for every TransportMessage sent over a connection.
/// required for ordering, since Nym does not guarantee ordering.
/// ConnectionMessages do not need nonces, as we know that they will
/// be the first messages sent over a connection.
/// the first TransportMessage sent over a connection will have nonce 1.
pub(crate) nonce: u64,
pub(crate) message: SubstreamMessage,
pub(crate) id: ConnectionId,
}
impl Message {
fn try_from_bytes(bytes: Vec<u8>) -> Result<Self, Error> {
if bytes.len() < 2 {
return Err(Error::InvalidMessageBytes);
}
Ok(match bytes[0] {
0 => Message::ConnectionRequest(ConnectionMessage::try_from_bytes(&bytes[1..])?),
1 => Message::ConnectionResponse(ConnectionMessage::try_from_bytes(&bytes[1..])?),
2 => Message::TransportMessage(TransportMessage::try_from_bytes(&bytes[1..])?),
_ => return Err(Error::InvalidMessageBytes),
})
}
}
impl ConnectionMessage {
fn to_bytes(&self) -> Vec<u8> {
let mut bytes = self.id.0.to_vec();
match self.recipient {
Some(recipient) => {
bytes.push(1u8);
bytes.append(&mut recipient.to_bytes().to_vec());
}
None => bytes.push(0u8),
}
bytes.append(&mut self.peer_id.to_bytes());
bytes
}
fn try_from_bytes(bytes: &[u8]) -> Result<Self, Error> {
if bytes.len() < CONNECTION_ID_LENGTH + 1 {
return Err(Error::ConnectionMessageBytesTooShort);
}
let id = ConnectionId::from_bytes(&bytes[0..CONNECTION_ID_LENGTH]);
let recipient = match bytes[CONNECTION_ID_LENGTH] {
0u8 => None,
1u8 => {
if bytes.len() < CONNECTION_ID_LENGTH + 1 + RECIPIENT_LENGTH {
return Err(Error::ConnectionMessageBytesNoRecipient);
}
let mut recipient_bytes = [0u8; RECIPIENT_LENGTH];
recipient_bytes[..].copy_from_slice(
&bytes[CONNECTION_ID_LENGTH + 1..CONNECTION_ID_LENGTH + 1 + RECIPIENT_LENGTH],
);
Some(
Recipient::try_from_bytes(recipient_bytes)
.map_err(Error::InvalidRecipientBytes)?,
)
}
_ => {
return Err(Error::InvalidRecipientPrefixByte);
}
};
let peer_id = match recipient {
Some(_) => {
if bytes.len() < CONNECTION_ID_LENGTH + RECIPIENT_LENGTH + 2 {
return Err(Error::ConnectionMessageBytesNoPeerId);
}
PeerId::from_bytes(&bytes[CONNECTION_ID_LENGTH + 1 + RECIPIENT_LENGTH..])
.map_err(|_| Error::InvalidPeerIdBytes)?
}
None => {
if bytes.len() < CONNECTION_ID_LENGTH + 2 {
return Err(Error::ConnectionMessageBytesNoPeerId);
}
PeerId::from_bytes(&bytes[CONNECTION_ID_LENGTH + 1..])
.map_err(|_| Error::InvalidPeerIdBytes)?
}
};
Ok(ConnectionMessage {
peer_id,
recipient,
id,
})
}
}
impl TransportMessage {
fn to_bytes(&self) -> Vec<u8> {
let mut bytes = self.nonce.to_be_bytes().to_vec();
bytes.extend_from_slice(self.id.0.as_ref());
bytes.extend_from_slice(&self.message.to_bytes());
bytes
}
fn try_from_bytes(bytes: &[u8]) -> Result<Self, Error> {
if bytes.len() < MIN_CONNECTION_MESSAGE_LEN + 1 {
return Err(Error::TransportMessageBytesTooShort);
}
let nonce = u64::from_be_bytes(
bytes[0..NONCE_BYTES_LEN]
.to_vec()
.try_into()
.map_err(|_| Error::InvalidNonce)?,
);
let id = ConnectionId::from_bytes(&bytes[NONCE_BYTES_LEN..MIN_CONNECTION_MESSAGE_LEN]);
let message = SubstreamMessage::try_from_bytes(&bytes[MIN_CONNECTION_MESSAGE_LEN..])?;
Ok(TransportMessage { nonce, message, id })
}
}
impl Ord for TransportMessage {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.nonce.cmp(&other.nonce)
}
}
impl std::cmp::PartialOrd for TransportMessage {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl std::cmp::Eq for TransportMessage {}
impl std::cmp::PartialEq for TransportMessage {
fn eq(&self, other: &Self) -> bool {
self.nonce == other.nonce
}
}
#[derive(Debug, Clone)]
pub(crate) enum SubstreamMessageType {
OpenRequest,
OpenResponse,
Close,
Data(Vec<u8>),
}
impl SubstreamMessageType {
fn to_u8(&self) -> u8 {
match self {
SubstreamMessageType::OpenRequest => 0,
SubstreamMessageType::OpenResponse => 1,
SubstreamMessageType::Close => 2,
SubstreamMessageType::Data(_) => 3,
}
}
}
/// SubstreamMessage is a message sent over a substream.
#[derive(Debug, Clone)]
pub(crate) struct SubstreamMessage {
pub(crate) substream_id: SubstreamId,
pub(crate) message_type: SubstreamMessageType,
}
impl SubstreamMessage {
pub(crate) fn new_with_data(substream_id: SubstreamId, message: Vec<u8>) -> Self {
SubstreamMessage {
substream_id,
message_type: SubstreamMessageType::Data(message),
}
}
pub(crate) fn new_close(substream_id: SubstreamId) -> Self {
SubstreamMessage {
substream_id,
message_type: SubstreamMessageType::Close,
}
}
pub(crate) fn to_bytes(&self) -> Vec<u8> {
let mut bytes = self.substream_id.0.clone().to_vec();
bytes.push(self.message_type.to_u8());
if let SubstreamMessageType::Data(message) = &self.message_type {
bytes.extend_from_slice(message);
}
bytes
}
pub(crate) fn try_from_bytes(bytes: &[u8]) -> Result<Self, Error> {
if bytes.len() < SUBSTREAM_ID_LENGTH + 1 {
return Err(Error::InvalidSubstreamMessageBytes);
}
let substream_id = SubstreamId::from_bytes(&bytes[0..SUBSTREAM_ID_LENGTH]);
let message_type = match bytes[SUBSTREAM_ID_LENGTH] {
0 => SubstreamMessageType::OpenRequest,
1 => SubstreamMessageType::OpenResponse,
2 => SubstreamMessageType::Close,
3 => {
if bytes.len() < SUBSTREAM_ID_LENGTH + 2 {
return Err(Error::InvalidSubstreamMessageBytes);
}
SubstreamMessageType::Data(bytes[SUBSTREAM_ID_LENGTH + 1..].to_vec())
}
_ => return Err(Error::InvalidSubstreamMessageType),
};
Ok(SubstreamMessage {
substream_id,
message_type,
})
}
}
impl Message {
pub(crate) fn to_bytes(&self) -> Vec<u8> {
match self {
Message::ConnectionRequest(msg) => {
let mut bytes = 0_u8.to_be_bytes().to_vec();
bytes.append(&mut msg.to_bytes());
bytes
}
Message::ConnectionResponse(msg) => {
let mut bytes = 1_u8.to_be_bytes().to_vec();
bytes.append(&mut msg.to_bytes());
bytes
}
Message::TransportMessage(msg) => {
let mut bytes = 2_u8.to_be_bytes().to_vec();
bytes.append(&mut msg.to_bytes());
bytes
}
}
}
}
/// InboundMessage represents an inbound mixnet message.
pub(crate) struct InboundMessage(pub(crate) Message);
/// OutboundMessage represents an outbound mixnet message.
#[derive(Debug)]
pub(crate) struct OutboundMessage {
pub(crate) message: Message,
pub(crate) recipient: Recipient,
}
pub(crate) fn parse_message_data(data: &[u8]) -> Result<InboundMessage, Error> {
if data.len() < 2 {
return Err(Error::InvalidMessageBytes);
}
let msg = Message::try_from_bytes(data.to_vec())?;
Ok(InboundMessage(msg))
}