ee5b55fab6
* Feature/ephemera compile (#3437) * Include ephemera node code in repo * Upgrade deps * Bump minor version of cosmwasm-std * Include ephemera in nym-api dep and downgrade rusqlite * Fix clippy and ephemera docs code * More clippy on ephemera --------- Co-authored-by: Andrus Salumets <andrus@nymtech.net> * Start ephemera components in nym-api (#3475) * Start ephemera components in nym-api * Pass nyxd client and use common metric structures * Swap url endpoint with contract for sending rewarding messages * Fix build after rebase * Perform ephemera rewards computation before normal nym-api ones * Remove contract mock from ephemera * Take raw rewards from network monitor * Remove ephemera old reward version * Use nym shutdown procedure in ephemera * Temporary fix for some warnings * Umock contract membership of ephemera (#3574) * Pass nyxd client to members provider * Basic ephemera contract * Add register peer tx * Add query all peers * Nyxd ephemera client * Add registration of ephemera peer * Replace epoch http api with actual contract * Merge ephemera config into nym-api config * Load cluster from contract * Guard nym-outfox out of cosmwasm builds (#3650) * Feature/fixes while testing (#3668) * Commit local peer before querying contract * Default to anyonline * Remove string from template * Fix avg computing * Use updated qa env * Fix clippy * Add unit tests for ephemera contract * Upload ephemera contract in CI * Add group check for peer signup * Peer registration unit test * Start ephemera only on monitoring * Remove old MixnodeToReward struct * Move all ephemera config to its file * Skip with serde ephemera config * Fix default value in args * Feature/add ephemera flag (#3727) * Replace unwrap with error handling * Add ephemera enable flag * Fix template * Add json schema to ephemera contract (#3735) * Update lock files * Update changelog --------- Co-authored-by: Andrus Salumets <andrus@nymtech.net>
238 lines
6.4 KiB
Rust
238 lines
6.4 KiB
Rust
use std::fmt::Display;
|
|
use std::net::IpAddr;
|
|
use std::str::FromStr;
|
|
|
|
use ::libp2p::{multiaddr::Protocol, Multiaddr};
|
|
use libp2p_identity::PeerId as Libp2pPeerId;
|
|
use log::info;
|
|
use serde::{Deserialize, Serialize};
|
|
use thiserror::Error;
|
|
|
|
use crate::crypto::PublicKey;
|
|
|
|
pub(crate) mod libp2p;
|
|
pub(crate) mod members;
|
|
|
|
pub(crate) type PeerIdType = Libp2pPeerId;
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum PeerIdError {
|
|
#[error("Invalid peer ID: {0}")]
|
|
InvalidPeerId(String),
|
|
}
|
|
|
|
/// Unique identifier of a peer.
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
pub struct PeerId(pub(crate) PeerIdType);
|
|
|
|
impl PeerId {
|
|
#[must_use]
|
|
pub fn random() -> Self {
|
|
Self(PeerIdType::random())
|
|
}
|
|
|
|
/// Returns the internal representation of the peer ID.
|
|
pub(crate) fn inner(&self) -> &PeerIdType {
|
|
&self.0
|
|
}
|
|
|
|
/// Returns a raw representation of the peer ID.
|
|
#[must_use]
|
|
pub fn to_bytes(&self) -> Vec<u8> {
|
|
self.0.to_bytes()
|
|
}
|
|
|
|
/// Returns a peer ID from a raw representation.
|
|
///
|
|
/// # Returns
|
|
/// A `PeerId` if the bytes are valid.
|
|
///
|
|
/// # Errors
|
|
/// An error if input has wrong format. This function is reverse to [`PeerId::to_bytes`].
|
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self, PeerIdError> {
|
|
Ok(Self(PeerIdType::from_bytes(bytes).map_err(|e| {
|
|
PeerIdError::InvalidPeerId(format!("Invalid peer ID: {e}"))
|
|
})?))
|
|
}
|
|
|
|
/// Builds a `PeerId` from a public key.
|
|
#[must_use]
|
|
pub fn from_public_key(public_key: &PublicKey) -> Self {
|
|
Self(PeerIdType::from_public_key(public_key.inner()))
|
|
}
|
|
}
|
|
|
|
impl From<PeerId> for libp2p_identity::PeerId {
|
|
fn from(peer_id: PeerId) -> Self {
|
|
peer_id.0
|
|
}
|
|
}
|
|
|
|
impl From<libp2p_identity::PeerId> for PeerId {
|
|
fn from(peer_id: libp2p_identity::PeerId) -> Self {
|
|
Self(peer_id)
|
|
}
|
|
}
|
|
|
|
impl Display for PeerId {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}", self.0)
|
|
}
|
|
}
|
|
|
|
pub trait ToPeerId {
|
|
fn peer_id(&self) -> PeerId;
|
|
}
|
|
|
|
/// A peer of the network.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
pub(crate) struct Peer {
|
|
/// The peer's ID. It identifies the peer uniquely and is derived from its public key.
|
|
///
|
|
/// # Deriving PeerId from PublicKey example
|
|
///
|
|
/// ```
|
|
/// use ephemera::crypto::{EphemeraKeypair, Keypair, PublicKey};
|
|
/// use ephemera::peer::{PeerId, ToPeerId};
|
|
///
|
|
/// let public_key = Keypair::generate(None).public_key();
|
|
///
|
|
/// let peer_id = PeerId::from_public_key(&public_key);
|
|
///
|
|
/// assert_eq!(peer_id, public_key.peer_id());
|
|
///
|
|
/// ```
|
|
pub peer_id: PeerId,
|
|
/// The peer's public key. It matches PeerId.
|
|
pub public_key: PublicKey,
|
|
/// The peer's address.
|
|
pub address: Address,
|
|
/// The cosmos address of the peer, used in interacting with the chain.
|
|
pub cosmos_address: String,
|
|
}
|
|
|
|
#[derive(Error, Debug)]
|
|
pub enum AddressError {
|
|
#[error("Failed to parse address: {0}")]
|
|
ParsingError(String),
|
|
}
|
|
|
|
/// Ephemera node address.
|
|
///
|
|
/// Supported formats:
|
|
/// 1. `<IP>:<PORT>`
|
|
/// 2. `/ip4/<IP>/tcp/<PORT>` - this is format used by libp2p multiaddr.
|
|
/// 3. `/dns4/<NAME>/tcp/<PORT>` - this is format used by libp2p multiaddr.
|
|
/// See [libp2p/multiaddress](https://github.com/libp2p/specs/blob/master/addressing/README.md) for more details.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
pub struct Address(pub Multiaddr);
|
|
|
|
impl Address {
|
|
pub fn inner(&self) -> &Multiaddr {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl From<Multiaddr> for Address {
|
|
fn from(multiaddr: Multiaddr) -> Self {
|
|
Self(multiaddr)
|
|
}
|
|
}
|
|
|
|
impl FromStr for Address {
|
|
type Err = AddressError;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
let address: Option<Multiaddr> = match Multiaddr::from_str(s) {
|
|
Ok(multiaddr) => Some(multiaddr),
|
|
Err(err) => {
|
|
info!("Failed to parse multiaddr: {}", err);
|
|
None
|
|
}
|
|
};
|
|
|
|
let multi_address = address.or_else(|| match std::net::SocketAddr::from_str(s) {
|
|
Ok(sa) => {
|
|
let mut multiaddr = Multiaddr::empty();
|
|
match sa {
|
|
std::net::SocketAddr::V4(v4) => {
|
|
multiaddr.push(Protocol::Ip4(*v4.ip()));
|
|
multiaddr.push(Protocol::Tcp(v4.port()));
|
|
}
|
|
std::net::SocketAddr::V6(v6) => {
|
|
multiaddr.push(Protocol::Ip6(*v6.ip()));
|
|
multiaddr.push(Protocol::Tcp(v6.port()));
|
|
}
|
|
}
|
|
|
|
Some(multiaddr)
|
|
}
|
|
Err(err) => {
|
|
info!("Failed to parse socket addr: {err}");
|
|
None
|
|
}
|
|
});
|
|
|
|
match multi_address {
|
|
Some(multi_address) => Ok(Self(multi_address)),
|
|
None => Err(AddressError::ParsingError(s.to_string())),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TryFrom<Address> for (IpAddr, u16) {
|
|
type Error = std::io::Error;
|
|
|
|
fn try_from(addr: Address) -> Result<Self, Self::Error> {
|
|
let mut multiaddr = addr.0;
|
|
if let Some(Protocol::Tcp(port)) = multiaddr.pop() {
|
|
if let Some(Protocol::Ip4(ip)) = multiaddr.pop() {
|
|
return Ok((IpAddr::V4(ip), port));
|
|
}
|
|
}
|
|
Err(std::io::Error::new(
|
|
std::io::ErrorKind::Other,
|
|
"invalid address",
|
|
))
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_parse_multiaddr() {
|
|
"/ip4/127.0.0.1/tcp/1234".parse::<Address>().unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_ip_port() {
|
|
"127.0.0.1:1234".parse::<Address>().unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn test_fail_parse_multiaddr_without_port() {
|
|
let result = "/ip4/127.0.0.1/tcp/".parse::<Address>();
|
|
assert!(matches!(result, Err(AddressError::ParsingError(_))));
|
|
}
|
|
|
|
#[test]
|
|
fn test_fail_parse_multiaddr_without_ip() {
|
|
let result = "/ip4//tcp/1234".parse::<Address>();
|
|
assert!(matches!(result, Err(AddressError::ParsingError(_))));
|
|
}
|
|
|
|
#[test]
|
|
fn test_fail_parse_ip_port_without_port() {
|
|
let result = "127.0.0.1".parse::<Address>();
|
|
assert!(matches!(result, Err(AddressError::ParsingError(_))));
|
|
}
|
|
|
|
#[test]
|
|
fn test_fail_parse_ip_port_without_ip() {
|
|
let result = "1234".parse::<Address>();
|
|
assert!(matches!(result, Err(AddressError::ParsingError(_))));
|
|
}
|
|
}
|