merge develop
This commit is contained in:
@@ -3,11 +3,12 @@ name: ci-build-ts
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'ts-packages/**'
|
||||
- "ts-packages/**"
|
||||
- "sdk/typescript/**"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: custom-runner-linux
|
||||
runs-on: ubuntu-20.04-16-core
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install rsync
|
||||
@@ -20,7 +21,7 @@ jobs:
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
- name: Build
|
||||
run: yarn && yarn build && yarn build:ci
|
||||
run: yarn && yarn build && yarn build:ci:storybook
|
||||
- name: Deploy branch to CI www (storybook)
|
||||
continue-on-error: true
|
||||
uses: easingthemes/ssh-deploy@main
|
||||
|
||||
@@ -28,11 +28,11 @@ jobs:
|
||||
command: build
|
||||
args: --workspace --release --all
|
||||
- name: Install mdbook
|
||||
run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4.33" mdbook)
|
||||
run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4.35" mdbook)
|
||||
- name: Install mdbook plugins
|
||||
run: |
|
||||
cargo install --vers "=0.2.2" mdbook-variables && cargo install \
|
||||
--vers "^1.8.0" mdbook-admonish && cargo install --vers \
|
||||
--vers "^1.8.0" mdbook-admonish install && cargo install --vers \
|
||||
"^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" mdbook-theme \
|
||||
&& cargo install --vers "^0.7.7" mdbook-linkcheck
|
||||
- name: Build all projects in documentation/ & move to ~/dist/docs/
|
||||
|
||||
@@ -22,7 +22,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: custom-runner-linux
|
||||
runs-on: ubuntu-20.04-16-core
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
@@ -39,6 +39,8 @@ jobs:
|
||||
toolchain: stable
|
||||
- name: Install wasm-pack
|
||||
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
- name: Install wasm-opt
|
||||
run: cargo install wasm-opt
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
@@ -49,7 +51,7 @@ jobs:
|
||||
run: yarn
|
||||
|
||||
- name: Build packages
|
||||
run: yarn build:ci:sdk
|
||||
run: yarn build:ci
|
||||
|
||||
- name: Lint
|
||||
run: yarn lint
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [macos-latest]
|
||||
platform: [macos-12-large]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
outputs:
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [macos-latest]
|
||||
platform: [macos-12-large]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
outputs:
|
||||
|
||||
@@ -4,7 +4,7 @@ on:
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: [custom-ubuntu-20.04]
|
||||
runs-on: ubuntu-20.04-16-core
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
@@ -25,6 +25,9 @@ jobs:
|
||||
- name: Install wasm-pack
|
||||
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
|
||||
- name: Install wasm-opt
|
||||
run: cargo install wasm-opt
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
|
||||
Generated
+632
-589
File diff suppressed because it is too large
Load Diff
@@ -74,7 +74,7 @@ impl PartiallyDelegated {
|
||||
|
||||
fn route_socket_messages(
|
||||
ws_msgs: Vec<Message>,
|
||||
packet_router: &mut PacketRouter,
|
||||
packet_router: &PacketRouter,
|
||||
shared_key: &SharedKeys,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
let plaintexts = Self::recover_received_plaintexts(ws_msgs, shared_key);
|
||||
@@ -97,7 +97,6 @@ impl PartiallyDelegated {
|
||||
let mixnet_receiver_future = async move {
|
||||
let mut notify_receiver = notify_receiver;
|
||||
let mut chunk_stream = (&mut stream).ready_chunks(8);
|
||||
let mut packet_router = packet_router;
|
||||
|
||||
let ret_err = loop {
|
||||
tokio::select! {
|
||||
@@ -115,7 +114,7 @@ impl PartiallyDelegated {
|
||||
Ok(msgs) => msgs
|
||||
};
|
||||
|
||||
if let Err(err) = Self::route_socket_messages(ws_msgs, &mut packet_router, shared_key.as_ref()) {
|
||||
if let Err(err) = Self::route_socket_messages(ws_msgs, &packet_router, shared_key.as_ref()) {
|
||||
log::warn!("Route socket messages failed: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,9 @@ pub trait GatewayPacketRouter {
|
||||
}
|
||||
|
||||
n if n
|
||||
== PacketSize::OutfoxRegularPacket.plaintext_size() - outfox_ack_overhead =>
|
||||
== PacketSize::OutfoxRegularPacket
|
||||
.plaintext_size()
|
||||
.saturating_sub(outfox_ack_overhead) =>
|
||||
{
|
||||
trace!("received regular outfox packet");
|
||||
received_messages.push(received_packet);
|
||||
|
||||
@@ -86,4 +86,5 @@ required-features = ["http-client"]
|
||||
default = ["http-client"]
|
||||
http-client = ["cosmrs/rpc", "openssl"]
|
||||
generate-ts = []
|
||||
contract-testing = ["nym-mixnet-contract-common/contract-testing"]
|
||||
|
||||
|
||||
+5
-4
@@ -683,13 +683,14 @@ pub trait MixnetSigningClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(feature = "nym_mixnet_contract_common/contract-testing")]
|
||||
#[cfg(feature = "contract-testing")]
|
||||
async fn testing_resolve_all_pending_events(
|
||||
&self,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::TestingResolveAllPendingEvents {},
|
||||
MixnetExecuteMsg::TestingResolveAllPendingEvents { limit: None },
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
@@ -928,8 +929,8 @@ mod tests {
|
||||
.withdraw_delegator_reward_on_behalf(owner.parse().unwrap(), mix_id, None)
|
||||
.ignore(),
|
||||
|
||||
#[cfg(feature = "nym_mixnet_contract_common/contract-testing")]
|
||||
MixnetExecuteMsg::TestingResolveAllPendingEvents {} => {
|
||||
#[cfg(feature = "contract-testing")]
|
||||
MixnetExecuteMsg::TestingResolveAllPendingEvents { .. } => {
|
||||
client.testing_resolve_all_pending_events(None).ignore()
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,25 +3,31 @@ use std::fmt::{Display, Formatter};
|
||||
use bytes::Bytes;
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub enum Event {
|
||||
/// IP packet received from the WireGuard tunnel that should be passed through to the corresponding virtual device/internet.
|
||||
/// Original implementation also has protocol here since it understands it, but we'll have to infer it downstream
|
||||
WgPacket(Bytes),
|
||||
/// IP packet received from the WireGuard tunnel that should be passed through to the
|
||||
/// corresponding virtual device/internet.
|
||||
Wg(Bytes),
|
||||
/// IP packet received from the WireGuard tunnel that was verified as part of the handshake.
|
||||
WgVerified(Bytes),
|
||||
/// IP packet to be sent through the WireGuard tunnel as crafted by the virtual device.
|
||||
IpPacket(Bytes),
|
||||
Ip(Bytes),
|
||||
}
|
||||
|
||||
impl Display for Event {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Event::WgPacket(data) => {
|
||||
Event::Wg(data) => {
|
||||
let size = data.len();
|
||||
write!(f, "WgPacket{{ size={size} }}")
|
||||
write!(f, "Wg{{ size={size} }}")
|
||||
}
|
||||
Event::IpPacket(data) => {
|
||||
Event::WgVerified(data) => {
|
||||
let size = data.len();
|
||||
write!(f, "IpPacket{{ size={size} }}")
|
||||
write!(f, "WgVerified{{ size={size} }}")
|
||||
}
|
||||
Event::Ip(data) => {
|
||||
let size = data.len();
|
||||
write!(f, "Ip{{ size={size} }}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
#![cfg_attr(not(target_os = "linux"), allow(dead_code))]
|
||||
// #![warn(clippy::pedantic)]
|
||||
// #![warn(clippy::expect_used)]
|
||||
// #![warn(clippy::unwrap_used)]
|
||||
|
||||
mod error;
|
||||
mod event;
|
||||
mod network_table;
|
||||
mod platform;
|
||||
mod registered_peers;
|
||||
mod setup;
|
||||
mod udp_listener;
|
||||
mod wg_tunnel;
|
||||
@@ -21,21 +25,24 @@ impl TunTaskTx {
|
||||
}
|
||||
}
|
||||
|
||||
/// Start wireguard UDP listener and TUN device
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if either the UDP listener of the TUN device fails to start.
|
||||
#[cfg(target_os = "linux")]
|
||||
pub async fn start_wireguard(
|
||||
task_client: nym_task::TaskClient,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
use std::sync::Arc;
|
||||
|
||||
// The set of active tunnels indexed by the peer's address
|
||||
let active_peers = Arc::new(udp_listener::ActivePeers::new());
|
||||
let peers_by_ip = Arc::new(std::sync::Mutex::new(network_table::NetworkTable::new()));
|
||||
|
||||
// Start the tun device that is used to relay traffic outbound
|
||||
let tun_task_tx = tun_device::start_tun_device(peers_by_ip.clone());
|
||||
|
||||
// Start the UDP listener that clients connect to
|
||||
udp_listener::start_udp_listener(tun_task_tx, active_peers, peers_by_ip, task_client).await?;
|
||||
udp_listener::start_udp_listener(tun_task_tx, peers_by_ip, task_client).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ fn setup_tokio_tun_device(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> t
|
||||
|
||||
pub(crate) fn start_tun_device(peers_by_ip: Arc<std::sync::Mutex<PeersByIp>>) -> TunTaskTx {
|
||||
let tun = setup_tokio_tun_device(
|
||||
format!("{}%d", TUN_BASE_NAME).as_str(),
|
||||
format!("{TUN_BASE_NAME}%d").as_str(),
|
||||
TUN_DEVICE_ADDRESS.parse().unwrap(),
|
||||
TUN_DEVICE_NETMASK.parse().unwrap(),
|
||||
);
|
||||
@@ -63,7 +63,7 @@ pub(crate) fn start_tun_device(peers_by_ip: Arc<std::sync::Mutex<PeersByIp>>) ->
|
||||
if let Some(peer_tx) = peers_by_ip.lock().unwrap().longest_match(dst_addr).map(|(_, tx)| tx) {
|
||||
log::info!("Forward packet to wg tunnel");
|
||||
peer_tx
|
||||
.send(Event::IpPacket(packet.to_vec().into()))
|
||||
.send(Event::Ip(packet.to_vec().into()))
|
||||
.tap_err(|err| log::error!("{err}"))
|
||||
.unwrap();
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use boringtun::x25519;
|
||||
use ip_network::IpNetwork;
|
||||
|
||||
pub(crate) type PeerIdx = u32;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct RegisteredPeer {
|
||||
pub(crate) public_key: x25519::PublicKey,
|
||||
pub(crate) index: PeerIdx,
|
||||
pub(crate) allowed_ips: IpNetwork,
|
||||
// endpoint: SocketAddr,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct RegisteredPeers {
|
||||
peers: HashMap<x25519::PublicKey, Arc<tokio::sync::Mutex<RegisteredPeer>>>,
|
||||
peers_by_idx: HashMap<PeerIdx, Arc<tokio::sync::Mutex<RegisteredPeer>>>,
|
||||
}
|
||||
|
||||
impl RegisteredPeers {
|
||||
pub(crate) async fn insert(
|
||||
&mut self,
|
||||
public_key: x25519::PublicKey,
|
||||
peer: Arc<tokio::sync::Mutex<RegisteredPeer>>,
|
||||
) {
|
||||
let peer_idx = { peer.lock().await.index };
|
||||
self.peers.insert(public_key, Arc::clone(&peer));
|
||||
self.peers_by_idx.insert(peer_idx, peer);
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) async fn remove(&mut self, public_key: &x25519::PublicKey) {
|
||||
if let Some(peer) = self.peers.remove(public_key) {
|
||||
let peer_idx = peer.lock().await.index;
|
||||
if self.peers_by_idx.remove(&peer_idx).is_none() {
|
||||
log::error!("Removed registered peer but no registered index was found");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_by_key(
|
||||
&self,
|
||||
public_key: &x25519::PublicKey,
|
||||
) -> Option<&Arc<tokio::sync::Mutex<RegisteredPeer>>> {
|
||||
self.peers.get(public_key)
|
||||
}
|
||||
|
||||
pub(crate) fn get_by_idx(
|
||||
&self,
|
||||
peer_idx: PeerIdx,
|
||||
) -> Option<&Arc<tokio::sync::Mutex<RegisteredPeer>>> {
|
||||
self.peers_by_idx.get(&peer_idx)
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ pub fn peer_static_public_key() -> x25519::PublicKey {
|
||||
let peer_static_public_bytes: [u8; 32] = decode_base64_key(PEER);
|
||||
let peer_static_public = x25519::PublicKey::try_from(peer_static_public_bytes).unwrap();
|
||||
info!(
|
||||
"peer public key: {}",
|
||||
"Adding wg peer public key: {}",
|
||||
general_purpose::STANDARD.encode(peer_static_public)
|
||||
);
|
||||
peer_static_public
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
use std::{net::SocketAddr, sync::Arc, time::Duration};
|
||||
|
||||
use boringtun::{
|
||||
noise::{self, handshake::parse_handshake_anon, rate_limiter::RateLimiter, TunnResult},
|
||||
x25519,
|
||||
};
|
||||
use dashmap::DashMap;
|
||||
use futures::StreamExt;
|
||||
use log::error;
|
||||
@@ -13,49 +17,80 @@ use tokio::{
|
||||
use crate::{
|
||||
event::Event,
|
||||
network_table::NetworkTable,
|
||||
registered_peers::{RegisteredPeer, RegisteredPeers},
|
||||
setup::{self, WG_ADDRESS, WG_PORT},
|
||||
TunTaskTx,
|
||||
};
|
||||
|
||||
const MAX_PACKET: usize = 65535;
|
||||
|
||||
pub(crate) type ActivePeers = DashMap<SocketAddr, mpsc::UnboundedSender<Event>>;
|
||||
// Registered peers
|
||||
pub(crate) type PeersByIp = NetworkTable<mpsc::UnboundedSender<Event>>;
|
||||
|
||||
// Active peers
|
||||
pub(crate) type ActivePeers = DashMap<x25519::PublicKey, mpsc::UnboundedSender<Event>>;
|
||||
pub(crate) type PeersByAddr = DashMap<SocketAddr, mpsc::UnboundedSender<Event>>;
|
||||
|
||||
async fn add_test_peer(registered_peers: &mut RegisteredPeers) {
|
||||
let peer_static_public = setup::peer_static_public_key();
|
||||
let peer_index = 0;
|
||||
let peer_allowed_ips = setup::peer_allowed_ips();
|
||||
let test_peer = Arc::new(tokio::sync::Mutex::new(RegisteredPeer {
|
||||
public_key: peer_static_public,
|
||||
index: peer_index,
|
||||
allowed_ips: peer_allowed_ips,
|
||||
}));
|
||||
registered_peers.insert(peer_static_public, test_peer).await;
|
||||
}
|
||||
|
||||
pub(crate) async fn start_udp_listener(
|
||||
tun_task_tx: TunTaskTx,
|
||||
active_peers: Arc<ActivePeers>,
|
||||
peers_by_ip: Arc<std::sync::Mutex<PeersByIp>>,
|
||||
mut task_client: TaskClient,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
let wg_address = SocketAddr::new(WG_ADDRESS.parse().unwrap(), WG_PORT);
|
||||
log::info!("Starting wireguard UDP listener on {wg_address}");
|
||||
let udp_socket = Arc::new(UdpSocket::bind(wg_address).await?);
|
||||
let udp = Arc::new(UdpSocket::bind(wg_address).await?);
|
||||
|
||||
// Setup some static keys for development
|
||||
// Setup our own keys
|
||||
let static_private = setup::server_static_private_key();
|
||||
let peer_static_public = setup::peer_static_public_key();
|
||||
let peer_allowed_ips = setup::peer_allowed_ips();
|
||||
let static_public = x25519::PublicKey::from(&static_private);
|
||||
let handshake_max_rate = 100u64;
|
||||
let rate_limiter = RateLimiter::new(&static_public, handshake_max_rate);
|
||||
|
||||
// Create a test peer for dev
|
||||
let mut registered_peers = RegisteredPeers::default();
|
||||
add_test_peer(&mut registered_peers).await;
|
||||
|
||||
tokio::spawn(async move {
|
||||
// The set of active tunnels indexed by the peer's address
|
||||
let active_peers = Arc::new(ActivePeers::new());
|
||||
let active_peers_by_addr = PeersByAddr::new();
|
||||
// Each tunnel is run in its own task, and the task handle is stored here so we can remove
|
||||
// it from `active_peers` when the tunnel is closed
|
||||
let mut active_peers_task_handles = futures::stream::FuturesUnordered::new();
|
||||
|
||||
let mut buf = [0u8; MAX_PACKET];
|
||||
let mut dst_buf = [0u8; MAX_PACKET];
|
||||
|
||||
while !task_client.is_shutdown() {
|
||||
tokio::select! {
|
||||
_ = task_client.recv() => {
|
||||
() = task_client.recv() => {
|
||||
log::trace!("WireGuard UDP listener: received shutdown");
|
||||
break;
|
||||
}
|
||||
// Reset the rate limiter every 1 sec
|
||||
() = tokio::time::sleep(Duration::from_secs(1)) => {
|
||||
rate_limiter.reset_count();
|
||||
},
|
||||
// Handle tunnel closing
|
||||
Some(addr) = active_peers_task_handles.next() => {
|
||||
match addr {
|
||||
Ok(addr) => {
|
||||
log::info!("Removing peer: {addr:?}");
|
||||
active_peers.remove(&addr);
|
||||
// TODO: remove from peers_by_ip
|
||||
Some(public_key) = active_peers_task_handles.next() => {
|
||||
match public_key {
|
||||
Ok(public_key) => {
|
||||
log::info!("Removing peer: {public_key:?}");
|
||||
active_peers.remove(&public_key);
|
||||
log::warn!("TODO: remove from peers_by_ip?");
|
||||
log::warn!("TODO: remove from peers_by_addr");
|
||||
}
|
||||
Err(err) => {
|
||||
error!("WireGuard UDP listener: error receiving shutdown from peer: {err}");
|
||||
@@ -63,40 +98,100 @@ pub(crate) async fn start_udp_listener(
|
||||
}
|
||||
},
|
||||
// Handle incoming packets
|
||||
Ok((len, addr)) = udp_socket.recv_from(&mut buf) => {
|
||||
Ok((len, addr)) = udp.recv_from(&mut buf) => {
|
||||
log::trace!("udp: received {} bytes from {}", len, addr);
|
||||
|
||||
if let Some(peer_tx) = active_peers.get_mut(&addr) {
|
||||
// If this addr has already been encountered, send directly to tunnel
|
||||
// TODO: optimization opportunity to instead create a connected UDP socket
|
||||
// inside the wg tunnel, where you can recv the data directly.
|
||||
if let Some(peer_tx) = active_peers_by_addr.get(&addr) {
|
||||
log::info!("udp: received {len} bytes from {addr} from known peer");
|
||||
peer_tx.send(Event::WgPacket(buf[..len].to_vec().into()))
|
||||
peer_tx
|
||||
.send(Event::Wg(buf[..len].to_vec().into()))
|
||||
.tap_err(|e| log::error!("{e}"))
|
||||
.ok();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Verify the incoming packet
|
||||
let verified_packet = match rate_limiter.verify_packet(Some(addr.ip()), &buf[..len], &mut dst_buf) {
|
||||
Ok(packet) => packet,
|
||||
Err(TunnResult::WriteToNetwork(cookie)) => {
|
||||
log::info!("Send back cookie to: {addr}");
|
||||
udp.send_to(cookie, addr).await.tap_err(|e| log::error!("{e}")).ok();
|
||||
continue;
|
||||
}
|
||||
Err(err) => {
|
||||
log::warn!("{err:?}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// Check if this is a registered peer, if not, just skip
|
||||
let registered_peer = {
|
||||
let reg_peer = match verified_packet {
|
||||
noise::Packet::HandshakeInit(ref packet) => {
|
||||
let Ok(handshake) = parse_handshake_anon(&static_private, &static_public, packet) else {
|
||||
log::warn!("Handshake failed: {addr}");
|
||||
continue;
|
||||
};
|
||||
registered_peers.get_by_key(&x25519::PublicKey::from(handshake.peer_static_public))
|
||||
},
|
||||
noise::Packet::HandshakeResponse(packet) => {
|
||||
let peer_idx = packet.receiver_idx >> 8;
|
||||
registered_peers.get_by_idx(peer_idx)
|
||||
},
|
||||
noise::Packet::PacketCookieReply(packet) => {
|
||||
let peer_idx = packet.receiver_idx >> 8;
|
||||
registered_peers.get_by_idx(peer_idx)
|
||||
},
|
||||
noise::Packet::PacketData(packet) => {
|
||||
let peer_idx = packet.receiver_idx >> 8;
|
||||
registered_peers.get_by_idx(peer_idx)
|
||||
},
|
||||
};
|
||||
|
||||
match reg_peer {
|
||||
Some(reg_peer) => reg_peer.lock().await,
|
||||
None => {
|
||||
log::warn!("Peer not registered: {addr}");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Look up if the peer is already connected
|
||||
if let Some(peer_tx) = active_peers.get_mut(®istered_peer.public_key) {
|
||||
// We found the peer as connected, even though the addr was not known
|
||||
log::info!("udp: received {len} bytes from {addr} which is a known peer with unknown addr");
|
||||
peer_tx.send(Event::WgVerified(buf[..len].to_vec().into()))
|
||||
.tap_err(|err| log::error!("{err}"))
|
||||
.unwrap();
|
||||
.ok();
|
||||
} else {
|
||||
// If it isn't, start a new tunnel
|
||||
log::info!("udp: received {len} bytes from {addr} from unknown peer, starting tunnel");
|
||||
// TODO: this is a temporary solution for development since this
|
||||
// assumes we know the peer_static_public this corresponds to.
|
||||
// TODO: rework this before production! This is likely not secure!
|
||||
log::warn!("Assuming peer_static_public is known");
|
||||
log::warn!("SECURITY: Rework me to do proper handshake before creating the tunnel!");
|
||||
// NOTE: we are NOT passing in the existing rate_limiter. Re-visit this
|
||||
// choice later.
|
||||
log::warn!("Creating new rate limiter, consider re-using?");
|
||||
let (join_handle, peer_tx) = crate::wg_tunnel::start_wg_tunnel(
|
||||
addr,
|
||||
udp_socket.clone(),
|
||||
udp.clone(),
|
||||
static_private.clone(),
|
||||
peer_static_public,
|
||||
peer_allowed_ips,
|
||||
registered_peer.public_key,
|
||||
registered_peer.index,
|
||||
registered_peer.allowed_ips,
|
||||
tun_task_tx.clone(),
|
||||
);
|
||||
|
||||
peers_by_ip.lock().unwrap().insert(peer_allowed_ips, peer_tx.clone());
|
||||
peers_by_ip.lock().unwrap().insert(registered_peer.allowed_ips, peer_tx.clone());
|
||||
active_peers_by_addr.insert(addr, peer_tx.clone());
|
||||
|
||||
peer_tx.send(Event::WgPacket(buf[..len].to_vec().into()))
|
||||
.tap_err(|err| log::error!("{err}"))
|
||||
.unwrap();
|
||||
peer_tx.send(Event::Wg(buf[..len].to_vec().into()))
|
||||
.tap_err(|e| log::error!("{e}"))
|
||||
.ok();
|
||||
|
||||
// WIP(JON): active peers should probably be keyed by peer_static_public
|
||||
// instead. Does this current setup lead to any issues?
|
||||
log::info!("Adding peer: {addr}");
|
||||
active_peers.insert(addr, peer_tx);
|
||||
active_peers.insert(registered_peer.public_key, peer_tx);
|
||||
active_peers_task_handles.push(join_handle);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::{net::SocketAddr, sync::Arc, time::Duration};
|
||||
|
||||
use async_recursion::async_recursion;
|
||||
use boringtun::{
|
||||
noise::{errors::WireGuardError, Tunn, TunnResult},
|
||||
noise::{errors::WireGuardError, rate_limiter::RateLimiter, Tunn, TunnResult},
|
||||
x25519,
|
||||
};
|
||||
use bytes::Bytes;
|
||||
@@ -14,7 +14,11 @@ use tokio::{
|
||||
time::timeout,
|
||||
};
|
||||
|
||||
use crate::{error::WgError, event::Event, network_table::NetworkTable, TunTaskTx};
|
||||
use crate::{
|
||||
error::WgError, event::Event, network_table::NetworkTable, registered_peers::PeerIdx, TunTaskTx,
|
||||
};
|
||||
|
||||
const HANDSHAKE_MAX_RATE: u64 = 10;
|
||||
|
||||
const MAX_PACKET: usize = 65535;
|
||||
|
||||
@@ -55,7 +59,9 @@ impl WireGuardTunnel {
|
||||
endpoint: SocketAddr,
|
||||
static_private: x25519::StaticSecret,
|
||||
peer_static_public: x25519::PublicKey,
|
||||
index: PeerIdx,
|
||||
peer_allowed_ips: ip_network::IpNetwork,
|
||||
// rate_limiter: Option<RateLimiter>,
|
||||
tunnel_tx: TunTaskTx,
|
||||
) -> (Self, mpsc::UnboundedSender<Event>) {
|
||||
let local_addr = udp.local_addr().unwrap();
|
||||
@@ -64,8 +70,12 @@ impl WireGuardTunnel {
|
||||
|
||||
let preshared_key = None;
|
||||
let persistent_keepalive = None;
|
||||
let index = 0;
|
||||
let rate_limiter = None;
|
||||
|
||||
let static_public = x25519::PublicKey::from(&static_private);
|
||||
let rate_limiter = Some(Arc::new(RateLimiter::new(
|
||||
&static_public,
|
||||
HANDSHAKE_MAX_RATE,
|
||||
)));
|
||||
|
||||
let wg_tunnel = Arc::new(tokio::sync::Mutex::new(
|
||||
Tunn::new(
|
||||
@@ -117,12 +127,17 @@ impl WireGuardTunnel {
|
||||
Some(packet) => {
|
||||
info!("event loop: {packet}");
|
||||
match packet {
|
||||
Event::WgPacket(data) => {
|
||||
Event::Wg(data) => {
|
||||
let _ = self.consume_wg(&data)
|
||||
.await
|
||||
.tap_err(|err| error!("WireGuard tunnel: consume_wg error: {err}"));
|
||||
},
|
||||
Event::IpPacket(data) => self.consume_eth(&data).await,
|
||||
Event::WgVerified(data) => {
|
||||
let _ = self.consume_verified_wg(&data)
|
||||
.await
|
||||
.tap_err(|err| error!("WireGuard tunnel: consume_verified_wg error: {err}"));
|
||||
}
|
||||
Event::Ip(data) => self.consume_eth(&data).await,
|
||||
}
|
||||
},
|
||||
None => {
|
||||
@@ -130,7 +145,7 @@ impl WireGuardTunnel {
|
||||
break;
|
||||
},
|
||||
},
|
||||
_ = tokio::time::sleep(Duration::from_millis(250)) => {
|
||||
() = tokio::time::sleep(Duration::from_millis(250)) => {
|
||||
let _ = self.update_wg_timers()
|
||||
.await
|
||||
.map_err(|err| error!("WireGuard tunnel: update_wg_timers error: {err}"));
|
||||
@@ -182,8 +197,6 @@ impl WireGuardTunnel {
|
||||
}
|
||||
}
|
||||
TunnResult::WriteToTunnelV4(packet, addr) => {
|
||||
// TODO: once the flow is redone, we should add updating the endpoint dynamically
|
||||
// self.set_endpoint(addr);
|
||||
if self.allowed_ips.longest_match(addr).is_some() {
|
||||
self.tun_task_tx.send(packet.to_vec()).unwrap();
|
||||
} else {
|
||||
@@ -191,8 +204,6 @@ impl WireGuardTunnel {
|
||||
}
|
||||
}
|
||||
TunnResult::WriteToTunnelV6(packet, addr) => {
|
||||
// TODO: once the flow is redone, we should add updating the endpoint dynamically
|
||||
// self.set_endpoint(addr);
|
||||
if self.allowed_ips.longest_match(addr).is_some() {
|
||||
self.tun_task_tx.send(packet.to_vec()).unwrap();
|
||||
} else {
|
||||
@@ -209,6 +220,13 @@ impl WireGuardTunnel {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn consume_verified_wg(&mut self, data: &[u8]) -> Result<(), WgError> {
|
||||
// Potentially we could take some shortcuts here in the name of performance, but currently
|
||||
// I don't see that the needed functions in boringtun is exposed in the public API.
|
||||
// TODO: make sure we don't put double pressure on the rate limiter!
|
||||
self.consume_wg(data).await
|
||||
}
|
||||
|
||||
async fn consume_eth(&self, data: &Bytes) {
|
||||
info!("consume_eth: raw packet size: {}", data.len());
|
||||
let encapsulated_packet = self.encapsulate_packet(data).await;
|
||||
@@ -269,7 +287,7 @@ impl WireGuardTunnel {
|
||||
return;
|
||||
};
|
||||
peer.format_handshake_initiation(&mut buf[..], false);
|
||||
self.handle_routine_tun_result(result).await
|
||||
self.handle_routine_tun_result(result).await;
|
||||
}
|
||||
TunnResult::Err(err) => {
|
||||
error!("Failed to prepare routine packet for WireGuard endpoint: {err:?}");
|
||||
@@ -287,10 +305,11 @@ pub(crate) fn start_wg_tunnel(
|
||||
udp: Arc<UdpSocket>,
|
||||
static_private: x25519::StaticSecret,
|
||||
peer_static_public: x25519::PublicKey,
|
||||
peer_index: PeerIdx,
|
||||
peer_allowed_ips: ip_network::IpNetwork,
|
||||
tunnel_tx: TunTaskTx,
|
||||
) -> (
|
||||
tokio::task::JoinHandle<SocketAddr>,
|
||||
tokio::task::JoinHandle<x25519::PublicKey>,
|
||||
mpsc::UnboundedSender<Event>,
|
||||
) {
|
||||
let (mut tunnel, peer_tx) = WireGuardTunnel::new(
|
||||
@@ -298,12 +317,13 @@ pub(crate) fn start_wg_tunnel(
|
||||
endpoint,
|
||||
static_private,
|
||||
peer_static_public,
|
||||
peer_index,
|
||||
peer_allowed_ips,
|
||||
tunnel_tx,
|
||||
);
|
||||
let join_handle = tokio::spawn(async move {
|
||||
tunnel.spin_off().await;
|
||||
endpoint
|
||||
peer_static_public
|
||||
});
|
||||
(join_handle, peer_tx)
|
||||
}
|
||||
|
||||
@@ -17,8 +17,7 @@ This is a *reference page*, to see the entire presentation join Max's talk at [H
|
||||
## SDKs
|
||||
|
||||
* [Rust SDK](https://nymtech.net/docs/sdk/rust.html)
|
||||
* [Typescript SDK](https://nymtech.net/docs/sdk/typescript.html)
|
||||
* [Interactive Typescript SDK docs](https://sdk.nymtech.net)
|
||||
* [Typescript SDK](https://sdk.nymtech.net/)
|
||||
|
||||
### Rust examples
|
||||
|
||||
|
||||
@@ -2,4 +2,8 @@
|
||||
|
||||
Welcome to the Nym Developer Portal, containing quickstart resources, user manuals, integration information, and tutorials outlining to start building privacy enhanced apps.
|
||||
|
||||
For more in-depth information about nodes, network traffic flows, clients, coconut etc check out the [docs](https://nymtech.net/docs). If you are looking for information and setup guides for the various pieces of Nym mixnet infrastructure (mix nodes, gateways and network requesters) and Nyx blockchain validators see the **new [Operators Guides](https://nymtech.net/operators)** book.
|
||||
For more in-depth information about nodes, network traffic flows, clients, coconut etc check out the [docs](https://nymtech.net/docs).
|
||||
|
||||
If you are looking for information and setup guides for the various pieces of Nym mixnet infrastructure (mix nodes, gateways and network requesters) and Nyx blockchain validators see the **new [Operators Guides](https://nymtech.net/operators)** book.
|
||||
|
||||
If you're looking for TypeScript/JavaScript related information such as SDKs to build your own tools, step-by-step tutorials, live playgrounds and more, make sure to check out the **new [TS SDK Handbook](https://sdk.nymtech.net/)** !
|
||||
@@ -1,3 +1,3 @@
|
||||
# Typescript
|
||||
|
||||
Tutorial code in this section is built to interact with a standalone Nym client. You can read about interacting with standalone clients [here](https://nymtech.net/docs/clients/websocket-client.html#connecting-to-the-local-websocket), although it is usually preferable to use the [Typescript SDK](https://nymtech.net/docs/sdk/typescript.html).
|
||||
Tutorial code in this section is built to interact with a standalone Nym client. You can read about interacting with standalone clients [here](https://nymtech.net/docs/clients/websocket-client.html#connecting-to-the-local-websocket), although it is usually preferable to use the [Typescript SDK](https://sdk.nymtech.net/).
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
This is Nym's technical documentation, containing information and setup guides about the various pieces of Nym software such as different mixnet infrastructure nodes, application clients, and existing applications like the desktop wallet and mixnet explorer.
|
||||
|
||||
If you are new to Nym and want to learn about the mixnet, explore kickstart options and demos, learn how to integrate with the network, and follow developer tutorials check out the [Developer Portal](https://nymtech.net/developers/) where you can find also our [FAQ section](https://nymtech.net/developers/faq/general-faq.md).
|
||||
|
||||
If you are looking for information and setup guides for the various pieces of Nym mixnet infrastructure (mix nodes, gateways and network requesters) and Nyx blockchain validators see the **new [Operators Guides](https://nymtech.net/operators)** book.
|
||||
|
||||
If you are new to Nym and want to learn about the mixnet, explore kickstart options and demos, learn how to integrate with the network, and follow developer tutorials check out the [Developer Portal](https://nymtech.net/developers/) where you can find also our [FAQ section](https://nymtech.net/developers/faq/general-faq.md).
|
||||
If you're specically looking for TypeScript/JavaScript related information such as SDKs to build your own tools, step-by-step tutorials, live playgrounds and more - make sure to check out the **new [TS SDK Handbook](https://sdk.nymtech.net/)** !
|
||||
|
||||
## Popular pages
|
||||
**Network Architecture:**
|
||||
@@ -12,7 +14,7 @@ If you are new to Nym and want to learn about the mixnet, explore kickstart opti
|
||||
* [Mixnet Traffic Flow](./architecture/traffic-flow.md)
|
||||
|
||||
**SDK examples:**
|
||||
* [Typescript SDK](./sdk/typescript.md)
|
||||
* [Typescript SDK](https://sdk.nymtech.net/)
|
||||
* [Rust SDK](./sdk/rust.md)
|
||||
|
||||
**Nyx**
|
||||
|
||||
@@ -1,66 +1,4 @@
|
||||
# Typescript SDK
|
||||
The Typescript SDK allows developers to start building browser-based mixnet applications quickly, by simply importing the SDK into their code via NPM as they would any other Typescript library.
|
||||
|
||||
You can find the source code [here](https://github.com/nymtech/nym/tree/master/sdk) and the library on NPM [here](https://www.npmjs.com/package/@nymproject/sdk).
|
||||
|
||||
Currently developers can use the SDK to do the following **entirely in the browser**:
|
||||
* Create a client
|
||||
* Listen for incoming messages and reply to them
|
||||
* Encrypt text and binary-encoded messages as Sphinx packets and send these through the mixnet
|
||||
|
||||
> We will be fleshing out further mixnet-related features in the coming weeks with functionality such as importing/exporting keypairs for developing apps with a retained identity over time.
|
||||
|
||||
In the future the SDK will be made up of several components, each of which will allow developers to interact with different parts of Nym's infrastructure.
|
||||
|
||||
| Component | Functionality | Released |
|
||||
| --------- | ------------------------------------------------------------------------------ | -------- |
|
||||
| Mixnet | Create clients & keypairs, subscribe to Mixnet events, send & receive messages | ✔️ |
|
||||
| Coconut | Create & verify Coconut credentials | ❌ |
|
||||
| Validator | Sign & broadcast Nyx blockchain transactions, query the blockchain | ❌ |
|
||||
|
||||
### How it works
|
||||
The SDK can be thought of as a 'wrapper' around the compiled [WebAssembly client](https://github.com/nymtech/nym/tree/master/clients/webassembly) code: it runs the client (a Wasm blob) in a web worker. This allows us to keep the work done by the client - such as the heavy lifting of creating and multiply-encrypting Sphinx packets - in a seperate thread from our UI, enabling you to build reactive frontends without worrying about the work done under the hood by the client eating your processing power.
|
||||
|
||||
The SDK exposes an interface that allows developers to interact with the Wasm blob inside the webworker from frontend code.
|
||||
|
||||
### Framework Support
|
||||
Currently, the SDK **only** works with frameworks that use either `Webpack` or `Parcel` as bundlers. If you want to use the SDK with a framework that isn't on this list, such as Angular, or NodeJS, **here be dragons!** These frameworks will probably use a different bundler and you will probably run into problems.
|
||||
|
||||
| Bundler | Supported |
|
||||
| ------- | --------- |
|
||||
| Webpack | ✔️ |
|
||||
| Packer | ✔️ |
|
||||
|
||||
Support for environments with different bundlers will be added in subsequent releases.
|
||||
|
||||
| Environment | Supported |
|
||||
| ---------------- | --------- |
|
||||
| Browser | ✔️ |
|
||||
| Headless NodeJS | ❌ |
|
||||
| Electron Desktop | ❌ |
|
||||
|
||||
|
||||
### Using the SDK
|
||||
There are multiple example projects in [`nym/sdk/typescript/examples/`](https://github.com/nymtech/nym/tree/master/sdk/typescript/examples/), each for a different frontend framework.
|
||||
|
||||
#### Vanilla HTML
|
||||
The best place to start if you just want to quickly get a basic frontend up and running with which to experiment is `examples/plain-html`:
|
||||
|
||||
```typescript
|
||||
{{#include ../../../../sdk/typescript/examples/shared/index.ts}}
|
||||
```
|
||||
|
||||
As you can see, all that is required to create an ephemeral keypair and connect to the mixnet is creating a client and then subscribing to the mixnet events coming down the websocket, and adding logic to deal with them.
|
||||
|
||||
#### Parcel
|
||||
If you don't want to use `Webpack` as your app bundler, we have an example with `Parcel` located at [`examples/parcel/`](https://github.com/nymtech/nym/tree/master/sdk/typescript/examples/chat-app/parcel).
|
||||
|
||||
#### Create React App
|
||||
For React developers we have an example which is a basic React app scaffold with the additional logic for creating a client and subscribing to mixnet events in [`examples/react-webpack-with-theme-example/`](https://github.com/nymtech/nym/tree/master/sdk/typescript/examples/chat-app/react-webpack-with-theme-example).
|
||||
|
||||
### Developers: think about what you're sending (and importing)!
|
||||
Think about what information your app sends. That goes for whatever you put into your Sphinx packet messages as well as what your app's environment may leak.
|
||||
|
||||
Whenever you write client PEApps using HTML/JavaScript, we recommend that you **do not load external resources from CDNs**. Webapp developers do this all the time, to save load time for common resources, or just for convenience. But when you're writing privacy apps it's better not to make these kinds of requests. **Pack everything locally**.
|
||||
|
||||
If you use only local resources within your Electron app or your browser extensions, explicitly encoding request data in a Sphinx packet does protect you from the normal leakage that gets sent in a browser HTTP request. [There's a lot of stuff that leaks when you make an HTTP request from a browser window](https://panopticlick.eff.org/). Luckily, all that metadata and request leakage doesn't happen in Nym, because you're choosing very explicitly what to encode into Sphinx packets, instead of sending a whole browser environment by default.
|
||||
> If you'd like to learn more, build apps or integrate Nym components using the TS SDK, please visit the **dedicated [TS SDK Handbook](https://sdk.nymtech.net/)** !
|
||||
@@ -42,7 +42,7 @@ turn-off = true
|
||||
|
||||
[preprocessor.admonish]
|
||||
command = "mdbook-admonish"
|
||||
assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install`
|
||||
assets_version = "3.0.0" # do not edit: managed by `mdbook-admonish install`
|
||||
|
||||
# variables preprocessor: import variables into files
|
||||
# https://gitlab.com/tglman/mdbook-variables/
|
||||
@@ -83,7 +83,7 @@ curly-quotes = true
|
||||
# mathjax-support = false # useful if we want to pull equations in
|
||||
copy-fonts = true
|
||||
no-section-label = false
|
||||
additional-css = ["theme/pagetoc.css", "././mdbook-admonish.css","./custom.css"]
|
||||
additional-css = ["theme/pagetoc.css", "././mdbook-admonish.css","./custom.css", "./mdbook-admonish.css"]
|
||||
additional-js = ["theme/pagetoc.js"]
|
||||
git-repository-url = "https://github.com/nymtech/nym"
|
||||
git-repository-icon = "fa-github"
|
||||
|
||||
@@ -1,31 +1,18 @@
|
||||
@charset "UTF-8";
|
||||
:root {
|
||||
--md-admonition-icon--note:
|
||||
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20.71 7.04c.39-.39.39-1.04 0-1.41l-2.34-2.34c-.37-.39-1.02-.39-1.41 0l-1.84 1.83 3.75 3.75M3 17.25V21h3.75L17.81 9.93l-3.75-3.75L3 17.25z'/></svg>");
|
||||
--md-admonition-icon--abstract:
|
||||
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17 9H7V7h10m0 6H7v-2h10m-3 6H7v-2h7M12 3a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1m7 0h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z'/></svg>");
|
||||
--md-admonition-icon--info:
|
||||
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 9h-2V7h2m0 10h-2v-6h2m-1-9A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10A10 10 0 0 0 12 2z'/></svg>");
|
||||
--md-admonition-icon--tip:
|
||||
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17.66 11.2c-.23-.3-.51-.56-.77-.82-.67-.6-1.43-1.03-2.07-1.66C13.33 7.26 13 4.85 13.95 3c-.95.23-1.78.75-2.49 1.32-2.59 2.08-3.61 5.75-2.39 8.9.04.1.08.2.08.33 0 .22-.15.42-.35.5-.23.1-.47.04-.66-.12a.58.58 0 0 1-.14-.17c-1.13-1.43-1.31-3.48-.55-5.12C5.78 10 4.87 12.3 5 14.47c.06.5.12 1 .29 1.5.14.6.41 1.2.71 1.73 1.08 1.73 2.95 2.97 4.96 3.22 2.14.27 4.43-.12 6.07-1.6 1.83-1.66 2.47-4.32 1.53-6.6l-.13-.26c-.21-.46-.77-1.26-.77-1.26m-3.16 6.3c-.28.24-.74.5-1.1.6-1.12.4-2.24-.16-2.9-.82 1.19-.28 1.9-1.16 2.11-2.05.17-.8-.15-1.46-.28-2.23-.12-.74-.1-1.37.17-2.06.19.38.39.76.63 1.06.77 1 1.98 1.44 2.24 2.8.04.14.06.28.06.43.03.82-.33 1.72-.93 2.27z'/></svg>");
|
||||
--md-admonition-icon--success:
|
||||
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m9 20.42-6.21-6.21 2.83-2.83L9 14.77l9.88-9.89 2.83 2.83L9 20.42z'/></svg>");
|
||||
--md-admonition-icon--question:
|
||||
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m15.07 11.25-.9.92C13.45 12.89 13 13.5 13 15h-2v-.5c0-1.11.45-2.11 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8a4 4 0 0 1 4-4 4 4 0 0 1 4 4 3.2 3.2 0 0 1-.93 2.25M13 19h-2v-2h2M12 2A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10c0-5.53-4.5-10-10-10z'/></svg>");
|
||||
--md-admonition-icon--warning:
|
||||
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 14h-2V9h2m0 9h-2v-2h2M1 21h22L12 2 1 21z'/></svg>");
|
||||
--md-admonition-icon--failure:
|
||||
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20 6.91 17.09 4 12 9.09 6.91 4 4 6.91 9.09 12 4 17.09 6.91 20 12 14.91 17.09 20 20 17.09 14.91 12 20 6.91z'/></svg>");
|
||||
--md-admonition-icon--danger:
|
||||
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M11 15H6l7-14v8h5l-7 14v-8z'/></svg>");
|
||||
--md-admonition-icon--bug:
|
||||
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 12h-4v-2h4m0 6h-4v-2h4m6-6h-2.81a5.985 5.985 0 0 0-1.82-1.96L17 4.41 15.59 3l-2.17 2.17a6.002 6.002 0 0 0-2.83 0L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8z'/></svg>");
|
||||
--md-admonition-icon--example:
|
||||
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M7 13v-2h14v2H7m0 6v-2h14v2H7M7 7V5h14v2H7M3 8V5H2V4h2v4H3m-1 9v-1h3v4H2v-1h2v-.5H3v-1h1V17H2m2.25-7a.75.75 0 0 1 .75.75c0 .2-.08.39-.21.52L3.12 13H5v1H2v-.92L4 11H2v-1h2.25z'/></svg>");
|
||||
--md-admonition-icon--quote:
|
||||
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 17h3l2-4V7h-6v6h3M6 17h3l2-4V7H5v6h3l-2 4z'/></svg>");
|
||||
--md-details-icon:
|
||||
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M8.59 16.58 13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.42Z'/></svg>");
|
||||
--md-admonition-icon--admonish-note: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20.71 7.04c.39-.39.39-1.04 0-1.41l-2.34-2.34c-.37-.39-1.02-.39-1.41 0l-1.84 1.83 3.75 3.75M3 17.25V21h3.75L17.81 9.93l-3.75-3.75L3 17.25z'/></svg>");
|
||||
--md-admonition-icon--admonish-abstract: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17 9H7V7h10m0 6H7v-2h10m-3 6H7v-2h7M12 3a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1m7 0h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z'/></svg>");
|
||||
--md-admonition-icon--admonish-info: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 9h-2V7h2m0 10h-2v-6h2m-1-9A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10A10 10 0 0 0 12 2z'/></svg>");
|
||||
--md-admonition-icon--admonish-tip: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17.66 11.2c-.23-.3-.51-.56-.77-.82-.67-.6-1.43-1.03-2.07-1.66C13.33 7.26 13 4.85 13.95 3c-.95.23-1.78.75-2.49 1.32-2.59 2.08-3.61 5.75-2.39 8.9.04.1.08.2.08.33 0 .22-.15.42-.35.5-.23.1-.47.04-.66-.12a.58.58 0 0 1-.14-.17c-1.13-1.43-1.31-3.48-.55-5.12C5.78 10 4.87 12.3 5 14.47c.06.5.12 1 .29 1.5.14.6.41 1.2.71 1.73 1.08 1.73 2.95 2.97 4.96 3.22 2.14.27 4.43-.12 6.07-1.6 1.83-1.66 2.47-4.32 1.53-6.6l-.13-.26c-.21-.46-.77-1.26-.77-1.26m-3.16 6.3c-.28.24-.74.5-1.1.6-1.12.4-2.24-.16-2.9-.82 1.19-.28 1.9-1.16 2.11-2.05.17-.8-.15-1.46-.28-2.23-.12-.74-.1-1.37.17-2.06.19.38.39.76.63 1.06.77 1 1.98 1.44 2.24 2.8.04.14.06.28.06.43.03.82-.33 1.72-.93 2.27z'/></svg>");
|
||||
--md-admonition-icon--admonish-success: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m9 20.42-6.21-6.21 2.83-2.83L9 14.77l9.88-9.89 2.83 2.83L9 20.42z'/></svg>");
|
||||
--md-admonition-icon--admonish-question: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m15.07 11.25-.9.92C13.45 12.89 13 13.5 13 15h-2v-.5c0-1.11.45-2.11 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8a4 4 0 0 1 4-4 4 4 0 0 1 4 4 3.2 3.2 0 0 1-.93 2.25M13 19h-2v-2h2M12 2A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10c0-5.53-4.5-10-10-10z'/></svg>");
|
||||
--md-admonition-icon--admonish-warning: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 14h-2V9h2m0 9h-2v-2h2M1 21h22L12 2 1 21z'/></svg>");
|
||||
--md-admonition-icon--admonish-failure: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20 6.91 17.09 4 12 9.09 6.91 4 4 6.91 9.09 12 4 17.09 6.91 20 12 14.91 17.09 20 20 17.09 14.91 12 20 6.91z'/></svg>");
|
||||
--md-admonition-icon--admonish-danger: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M11 15H6l7-14v8h5l-7 14v-8z'/></svg>");
|
||||
--md-admonition-icon--admonish-bug: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 12h-4v-2h4m0 6h-4v-2h4m6-6h-2.81a5.985 5.985 0 0 0-1.82-1.96L17 4.41 15.59 3l-2.17 2.17a6.002 6.002 0 0 0-2.83 0L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8z'/></svg>");
|
||||
--md-admonition-icon--admonish-example: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M7 13v-2h14v2H7m0 6v-2h14v2H7M7 7V5h14v2H7M3 8V5H2V4h2v4H3m-1 9v-1h3v4H2v-1h2v-.5H3v-1h1V17H2m2.25-7a.75.75 0 0 1 .75.75c0 .2-.08.39-.21.52L3.12 13H5v1H2v-.92L4 11H2v-1h2.25z'/></svg>");
|
||||
--md-admonition-icon--admonish-quote: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 17h3l2-4V7h-6v6h3M6 17h3l2-4V7H5v6h3l-2 4z'/></svg>");
|
||||
--md-details-icon: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M8.59 16.58 13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.42Z'/></svg>");
|
||||
}
|
||||
|
||||
:is(.admonition) {
|
||||
@@ -75,8 +62,9 @@ a.admonition-anchor-link::before {
|
||||
content: "§";
|
||||
}
|
||||
|
||||
:is(.admonition-title, summary) {
|
||||
:is(.admonition-title, summary.admonition-title) {
|
||||
position: relative;
|
||||
min-height: 4rem;
|
||||
margin-block: 0;
|
||||
margin-inline: -1.6rem -1.2rem;
|
||||
padding-block: 0.8rem;
|
||||
@@ -85,13 +73,13 @@ a.admonition-anchor-link::before {
|
||||
background-color: rgba(68, 138, 255, 0.1);
|
||||
display: flex;
|
||||
}
|
||||
:is(.admonition-title, summary) p {
|
||||
:is(.admonition-title, summary.admonition-title) p {
|
||||
margin: 0;
|
||||
}
|
||||
html :is(.admonition-title, summary):last-child {
|
||||
html :is(.admonition-title, summary.admonition-title):last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
:is(.admonition-title, summary)::before {
|
||||
:is(.admonition-title, summary.admonition-title)::before {
|
||||
position: absolute;
|
||||
top: 0.625em;
|
||||
inset-inline-start: 1.6rem;
|
||||
@@ -106,7 +94,7 @@ html :is(.admonition-title, summary):last-child {
|
||||
-webkit-mask-size: contain;
|
||||
content: "";
|
||||
}
|
||||
:is(.admonition-title, summary):hover a.admonition-anchor-link {
|
||||
:is(.admonition-title, summary.admonition-title):hover a.admonition-anchor-link {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
@@ -131,204 +119,204 @@ details[open].admonition > summary.admonition-title::after {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
:is(.admonition):is(.note) {
|
||||
:is(.admonition):is(.admonish-note) {
|
||||
border-color: #448aff;
|
||||
}
|
||||
|
||||
:is(.note) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-note) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(68, 138, 255, 0.1);
|
||||
}
|
||||
:is(.note) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-note) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #448aff;
|
||||
mask-image: var(--md-admonition-icon--note);
|
||||
-webkit-mask-image: var(--md-admonition-icon--note);
|
||||
mask-image: var(--md-admonition-icon--admonish-note);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-note);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.abstract, .summary, .tldr) {
|
||||
:is(.admonition):is(.admonish-abstract, .admonish-summary, .admonish-tldr) {
|
||||
border-color: #00b0ff;
|
||||
}
|
||||
|
||||
:is(.abstract, .summary, .tldr) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-abstract, .admonish-summary, .admonish-tldr) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(0, 176, 255, 0.1);
|
||||
}
|
||||
:is(.abstract, .summary, .tldr) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-abstract, .admonish-summary, .admonish-tldr) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #00b0ff;
|
||||
mask-image: var(--md-admonition-icon--abstract);
|
||||
-webkit-mask-image: var(--md-admonition-icon--abstract);
|
||||
mask-image: var(--md-admonition-icon--admonish-abstract);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-abstract);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.info, .todo) {
|
||||
:is(.admonition):is(.admonish-info, .admonish-todo) {
|
||||
border-color: #00b8d4;
|
||||
}
|
||||
|
||||
:is(.info, .todo) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-info, .admonish-todo) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(0, 184, 212, 0.1);
|
||||
}
|
||||
:is(.info, .todo) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-info, .admonish-todo) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #00b8d4;
|
||||
mask-image: var(--md-admonition-icon--info);
|
||||
-webkit-mask-image: var(--md-admonition-icon--info);
|
||||
mask-image: var(--md-admonition-icon--admonish-info);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-info);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.tip, .hint, .important) {
|
||||
:is(.admonition):is(.admonish-tip, .admonish-hint, .admonish-important) {
|
||||
border-color: #00bfa5;
|
||||
}
|
||||
|
||||
:is(.tip, .hint, .important) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-tip, .admonish-hint, .admonish-important) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(0, 191, 165, 0.1);
|
||||
}
|
||||
:is(.tip, .hint, .important) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-tip, .admonish-hint, .admonish-important) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #00bfa5;
|
||||
mask-image: var(--md-admonition-icon--tip);
|
||||
-webkit-mask-image: var(--md-admonition-icon--tip);
|
||||
mask-image: var(--md-admonition-icon--admonish-tip);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-tip);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.success, .check, .done) {
|
||||
:is(.admonition):is(.admonish-success, .admonish-check, .admonish-done) {
|
||||
border-color: #00c853;
|
||||
}
|
||||
|
||||
:is(.success, .check, .done) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-success, .admonish-check, .admonish-done) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(0, 200, 83, 0.1);
|
||||
}
|
||||
:is(.success, .check, .done) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-success, .admonish-check, .admonish-done) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #00c853;
|
||||
mask-image: var(--md-admonition-icon--success);
|
||||
-webkit-mask-image: var(--md-admonition-icon--success);
|
||||
mask-image: var(--md-admonition-icon--admonish-success);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-success);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.question, .help, .faq) {
|
||||
:is(.admonition):is(.admonish-question, .admonish-help, .admonish-faq) {
|
||||
border-color: #64dd17;
|
||||
}
|
||||
|
||||
:is(.question, .help, .faq) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-question, .admonish-help, .admonish-faq) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(100, 221, 23, 0.1);
|
||||
}
|
||||
:is(.question, .help, .faq) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-question, .admonish-help, .admonish-faq) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #64dd17;
|
||||
mask-image: var(--md-admonition-icon--question);
|
||||
-webkit-mask-image: var(--md-admonition-icon--question);
|
||||
mask-image: var(--md-admonition-icon--admonish-question);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-question);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.warning, .caution, .attention) {
|
||||
:is(.admonition):is(.admonish-warning, .admonish-caution, .admonish-attention) {
|
||||
border-color: #ff9100;
|
||||
}
|
||||
|
||||
:is(.warning, .caution, .attention) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-warning, .admonish-caution, .admonish-attention) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(255, 145, 0, 0.1);
|
||||
}
|
||||
:is(.warning, .caution, .attention) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-warning, .admonish-caution, .admonish-attention) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #ff9100;
|
||||
mask-image: var(--md-admonition-icon--warning);
|
||||
-webkit-mask-image: var(--md-admonition-icon--warning);
|
||||
mask-image: var(--md-admonition-icon--admonish-warning);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-warning);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.failure, .fail, .missing) {
|
||||
:is(.admonition):is(.admonish-failure, .admonish-fail, .admonish-missing) {
|
||||
border-color: #ff5252;
|
||||
}
|
||||
|
||||
:is(.failure, .fail, .missing) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-failure, .admonish-fail, .admonish-missing) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(255, 82, 82, 0.1);
|
||||
}
|
||||
:is(.failure, .fail, .missing) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-failure, .admonish-fail, .admonish-missing) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #ff5252;
|
||||
mask-image: var(--md-admonition-icon--failure);
|
||||
-webkit-mask-image: var(--md-admonition-icon--failure);
|
||||
mask-image: var(--md-admonition-icon--admonish-failure);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-failure);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.danger, .error) {
|
||||
:is(.admonition):is(.admonish-danger, .admonish-error) {
|
||||
border-color: #ff1744;
|
||||
}
|
||||
|
||||
:is(.danger, .error) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-danger, .admonish-error) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(255, 23, 68, 0.1);
|
||||
}
|
||||
:is(.danger, .error) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-danger, .admonish-error) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #ff1744;
|
||||
mask-image: var(--md-admonition-icon--danger);
|
||||
-webkit-mask-image: var(--md-admonition-icon--danger);
|
||||
mask-image: var(--md-admonition-icon--admonish-danger);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-danger);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.bug) {
|
||||
:is(.admonition):is(.admonish-bug) {
|
||||
border-color: #f50057;
|
||||
}
|
||||
|
||||
:is(.bug) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-bug) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(245, 0, 87, 0.1);
|
||||
}
|
||||
:is(.bug) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-bug) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #f50057;
|
||||
mask-image: var(--md-admonition-icon--bug);
|
||||
-webkit-mask-image: var(--md-admonition-icon--bug);
|
||||
mask-image: var(--md-admonition-icon--admonish-bug);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-bug);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.example) {
|
||||
:is(.admonition):is(.admonish-example) {
|
||||
border-color: #7c4dff;
|
||||
}
|
||||
|
||||
:is(.example) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-example) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(124, 77, 255, 0.1);
|
||||
}
|
||||
:is(.example) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-example) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #7c4dff;
|
||||
mask-image: var(--md-admonition-icon--example);
|
||||
-webkit-mask-image: var(--md-admonition-icon--example);
|
||||
mask-image: var(--md-admonition-icon--admonish-example);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-example);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.quote, .cite) {
|
||||
:is(.admonition):is(.admonish-quote, .admonish-cite) {
|
||||
border-color: #9e9e9e;
|
||||
}
|
||||
|
||||
:is(.quote, .cite) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-quote, .admonish-cite) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(158, 158, 158, 0.1);
|
||||
}
|
||||
:is(.quote, .cite) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-quote, .admonish-cite) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #9e9e9e;
|
||||
mask-image: var(--md-admonition-icon--quote);
|
||||
-webkit-mask-image: var(--md-admonition-icon--quote);
|
||||
mask-image: var(--md-admonition-icon--admonish-quote);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-quote);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
@@ -339,7 +327,8 @@ details[open].admonition > summary.admonition-title::after {
|
||||
background-color: var(--sidebar-bg);
|
||||
}
|
||||
|
||||
.ayu :is(.admonition), .coal :is(.admonition) {
|
||||
.ayu :is(.admonition),
|
||||
.coal :is(.admonition) {
|
||||
background-color: var(--theme-hover);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
> We aim on purpose to make minimal changes to reward scheme and software. We're just 'smooshing' together stuff we already debugged and know works.
|
||||
> -- Harry Halpin, Nym CEO
|
||||
<p>
|
||||
<p></p>
|
||||
|
||||
This page refer to the changes which are planned to take place over Q3 and Q4 2023. As this is a transition period in the beginning (Q3 2023) the [Mix Nodes FAQ page](./mixnodes-faq.md) holds more answers to the current setup as project Smoosh refers to the eventual setup. As project Smoosh gets progressively implemented the answers on this page will become to be more relevant to the current state and eventually this FAQ page will be merged with the still relevant parts of the main Mix Nodes FAQ page.
|
||||
</p>
|
||||
|
||||
If any questions are not answered or it's not clear for you in which stage project Smoosh is right now, please reach out in Node Operators [Matrix room](https://matrix.to/#/#operators:nymtech.chat).
|
||||
|
||||
## Overview
|
||||
@@ -40,7 +41,7 @@ Yes, to run a mix node only is an option. However it will be less rewarded as no
|
||||
|
||||
### What are the incentives for the node operator?
|
||||
|
||||
In the original setup there were no incentives to run a `network-requester`. After the transition all the users will buy multiple tickets of zkNyms credentials and use those as [anonymous e-cash](https://arxiv.org/abs/2303.08221) to pay for their data traffic (`[Nym API](https://github.com/nymtech/nym/tree/master/nym-api)` will do the do cryptographical checks to prevent double-spending). All collected fees get distributed to all active nodes proportionally to their work by the end of each epoch.
|
||||
In the original setup there were no incentives to run a `network-requester`. After the transition all the users will buy multiple tickets of zkNyms credentials and use those as [anonymous e-cash](https://arxiv.org/abs/2303.08221) to pay for their data traffic ([`Nym API`](https://github.com/nymtech/nym/tree/master/nym-api) will do the do cryptographical checks to prevent double-spending). All collected fees get distributed to all active nodes proportionally to their work by the end of each epoch.
|
||||
|
||||
### How does this change the token economics?
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ impl ApiCmdProcessor {
|
||||
}
|
||||
|
||||
fn ephemera_config<A: Application>(
|
||||
ephemera: &mut Ephemera<A>,
|
||||
ephemera: &Ephemera<A>,
|
||||
reply: Sender<api::Result<ApiEphemeraConfig>>,
|
||||
) {
|
||||
let node_info = ephemera.node_info.clone();
|
||||
|
||||
@@ -191,10 +191,10 @@ impl<A: Application> EphemeraStarterWithApplication<A> {
|
||||
|
||||
let block_manager = self.init_block_manager(&mut storage)?;
|
||||
|
||||
let (mut shutdown_manager, shutdown_handle) = ShutdownManager::init();
|
||||
let (shutdown_manager, shutdown_handle) = ShutdownManager::init();
|
||||
|
||||
let mut service_data = ServiceInfo::default();
|
||||
let services = self.init_services(&mut service_data, &mut shutdown_manager, provider)?;
|
||||
let services = self.init_services(&mut service_data, &shutdown_manager, provider)?;
|
||||
|
||||
Ok(EphemeraStarterWithProvider {
|
||||
with_application: self,
|
||||
@@ -237,7 +237,7 @@ impl<A: Application> EphemeraStarterWithApplication<A> {
|
||||
>(
|
||||
&mut self,
|
||||
service_data: &mut ServiceInfo,
|
||||
shutdown_manager: &mut ShutdownManager,
|
||||
shutdown_manager: &ShutdownManager,
|
||||
provider: P,
|
||||
) -> anyhow::Result<Vec<BoxFuture<'static, anyhow::Result<()>>>> {
|
||||
let services = vec![
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { GatewayResponse, GatewayBond, GatewayReportResponse } from '../typeDefs/explorer-api';
|
||||
import { toPercentIntegerString } from '../utils';
|
||||
import { toPercentInteger } from '../utils';
|
||||
|
||||
export type GatewayRowType = {
|
||||
id: string;
|
||||
@@ -9,7 +9,7 @@ export type GatewayRowType = {
|
||||
host: string;
|
||||
location: string;
|
||||
version: string;
|
||||
node_performance: string;
|
||||
node_performance: number;
|
||||
};
|
||||
|
||||
export type GatewayEnrichedRowType = GatewayRowType & {
|
||||
@@ -30,7 +30,7 @@ export function gatewayToGridRow(arrayOfGateways: GatewayResponse): GatewayRowTy
|
||||
bond: gw.pledge_amount.amount || 0,
|
||||
host: gw.gateway.host || '',
|
||||
version: gw.gateway.version || '',
|
||||
node_performance: toPercentIntegerString(gw.node_performance.last_24h),
|
||||
node_performance: toPercentInteger(gw.node_performance.last_24h),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -47,6 +47,6 @@ export function gatewayEnrichedToGridRow(gateway: GatewayBond, report: GatewayRe
|
||||
mixPort: gateway.gateway.mix_port || 0,
|
||||
routingScore: `${report.most_recent}%`,
|
||||
avgUptime: `${report.last_day || report.last_hour}%`,
|
||||
node_performance: toPercentIntegerString(gateway.node_performance.most_recent),
|
||||
node_performance: toPercentInteger(gateway.node_performance.most_recent),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable camelcase */
|
||||
import { MixNodeResponse, MixNodeResponseItem, MixnodeStatus, NodePerformance } from '../../typeDefs/explorer-api';
|
||||
import { toPercentIntegerString } from '../../utils';
|
||||
import { MixNodeResponse, MixNodeResponseItem, MixnodeStatus } from '../../typeDefs/explorer-api';
|
||||
import { toPercentInteger, toPercentIntegerString } from '../../utils';
|
||||
import { unymToNym } from '../../utils/currency';
|
||||
|
||||
export type MixnodeRowType = {
|
||||
@@ -15,11 +15,11 @@ export type MixnodeRowType = {
|
||||
pledge_amount: number;
|
||||
host: string;
|
||||
layer: string;
|
||||
profit_percentage: string;
|
||||
profit_percentage: number;
|
||||
avg_uptime: string;
|
||||
stake_saturation: React.ReactNode;
|
||||
operating_cost: string;
|
||||
node_performance: NodePerformance['most_recent'];
|
||||
operating_cost: number;
|
||||
node_performance: number;
|
||||
blacklisted: boolean;
|
||||
};
|
||||
|
||||
@@ -32,7 +32,7 @@ export function mixNodeResponseItemToMixnodeRowType(item: MixNodeResponseItem):
|
||||
const delegations = Number(item.total_delegation.amount) || 0;
|
||||
const totalBond = pledge + delegations;
|
||||
const selfPercentage = ((pledge * 100) / totalBond).toFixed(2);
|
||||
const profitPercentage = toPercentIntegerString(item.profit_margin_percent) || 0;
|
||||
const profitPercentage = toPercentInteger(item.profit_margin_percent) || 0;
|
||||
const uncappedSaturation = typeof item.uncapped_saturation === 'number' ? item.uncapped_saturation * 100 : 0;
|
||||
|
||||
return {
|
||||
@@ -47,11 +47,11 @@ export function mixNodeResponseItemToMixnodeRowType(item: MixNodeResponseItem):
|
||||
pledge_amount: pledge,
|
||||
host: item?.mix_node?.host || '',
|
||||
layer: item?.layer || '',
|
||||
profit_percentage: `${profitPercentage}%`,
|
||||
profit_percentage: profitPercentage,
|
||||
avg_uptime: `${toPercentIntegerString(item.node_performance.last_24h)}%`,
|
||||
stake_saturation: Number(uncappedSaturation.toFixed(2)),
|
||||
operating_cost: `${unymToNym(item.operating_cost?.amount, 6)} NYM`,
|
||||
node_performance: `${toPercentIntegerString(item.node_performance.most_recent)}%`,
|
||||
operating_cost: Number(unymToNym(item.operating_cost?.amount, 6)) || 0,
|
||||
node_performance: toPercentInteger(item.node_performance.most_recent),
|
||||
blacklisted: item.blacklisted,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ export const PageMixnodes: FCWithChildren = () => {
|
||||
component={RRDLink}
|
||||
to={`/network-components/mixnode/${params.row.mix_id}`}
|
||||
>
|
||||
{params.value}
|
||||
{params.value}%
|
||||
</MuiLink>
|
||||
),
|
||||
},
|
||||
@@ -233,7 +233,7 @@ export const PageMixnodes: FCWithChildren = () => {
|
||||
component={RRDLink}
|
||||
to={`/network-components/mixnode/${params.row.mix_id}`}
|
||||
>
|
||||
{params.value}
|
||||
{params.value} NYM
|
||||
</MuiLink>
|
||||
),
|
||||
},
|
||||
@@ -256,7 +256,7 @@ export const PageMixnodes: FCWithChildren = () => {
|
||||
component={RRDLink}
|
||||
to={`/network-components/mixnode/${params.row.mix_id}`}
|
||||
>
|
||||
{params.value}
|
||||
{params.value}%
|
||||
</MuiLink>
|
||||
),
|
||||
},
|
||||
|
||||
@@ -55,6 +55,7 @@ export const splice = (start: number, deleteCount: number, address?: string): st
|
||||
* @returns A stringified integer
|
||||
*/
|
||||
export const toPercentIntegerString = (value: string) => Math.round(Number(value) * 100).toString();
|
||||
export const toPercentInteger = (value: string) => Math.round(Number(value) * 100);
|
||||
|
||||
export const textColour = (value: EconomicsRowsType, field: string, theme: Theme) => {
|
||||
const progressBarValue = value?.progressBarValue || 0;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
net::SocketAddr,
|
||||
@@ -8,6 +7,7 @@ use std::{
|
||||
};
|
||||
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use dashmap::DashMap;
|
||||
use hmac::{Hmac, Mac};
|
||||
use nym_crypto::asymmetric::encryption::PrivateKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -178,4 +178,4 @@ impl<'de> Deserialize<'de> for ClientPublicKey {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type ClientRegistry = HashMap<SocketAddr, Client>;
|
||||
pub(crate) type ClientRegistry = DashMap<SocketAddr, Client>;
|
||||
|
||||
@@ -14,8 +14,7 @@ use crate::node::http::ApiState;
|
||||
|
||||
async fn process_final_message(client: Client, state: Arc<ApiState>) -> StatusCode {
|
||||
let preshared_nonce = {
|
||||
let in_progress_ro = state.registration_in_progress.read().await;
|
||||
if let Some(nonce) = in_progress_ro.get(client.pub_key()) {
|
||||
if let Some(nonce) = state.registration_in_progress.get(client.pub_key()) {
|
||||
*nonce
|
||||
} else {
|
||||
return StatusCode::BAD_REQUEST;
|
||||
@@ -27,12 +26,10 @@ async fn process_final_message(client: Client, state: Arc<ApiState>) -> StatusCo
|
||||
.is_ok()
|
||||
{
|
||||
{
|
||||
let mut in_progress_rw = state.registration_in_progress.write().await;
|
||||
in_progress_rw.remove(client.pub_key());
|
||||
state.registration_in_progress.remove(client.pub_key());
|
||||
}
|
||||
{
|
||||
let mut registry_rw = state.client_registry.write().await;
|
||||
registry_rw.insert(client.socket(), client);
|
||||
state.client_registry.insert(client.socket(), client);
|
||||
}
|
||||
return StatusCode::OK;
|
||||
}
|
||||
@@ -42,8 +39,9 @@ async fn process_final_message(client: Client, state: Arc<ApiState>) -> StatusCo
|
||||
|
||||
async fn process_init_message(init_message: InitMessage, state: Arc<ApiState>) -> u64 {
|
||||
let nonce: u64 = fastrand::u64(..);
|
||||
let mut registry_rw = state.registration_in_progress.write().await;
|
||||
registry_rw.insert(init_message.pub_key().clone(), nonce);
|
||||
state
|
||||
.registration_in_progress
|
||||
.insert(init_message.pub_key().clone(), nonce);
|
||||
nonce
|
||||
}
|
||||
|
||||
@@ -67,12 +65,12 @@ pub(crate) async fn register_client(
|
||||
pub(crate) async fn get_all_clients(
|
||||
State(state): State<Arc<ApiState>>,
|
||||
) -> (StatusCode, Json<Vec<ClientPublicKey>>) {
|
||||
let registry_ro = state.client_registry.read().await;
|
||||
(
|
||||
StatusCode::OK,
|
||||
Json(
|
||||
registry_ro
|
||||
.values()
|
||||
state
|
||||
.client_registry
|
||||
.iter()
|
||||
.map(|c| c.pub_key().clone())
|
||||
.collect::<Vec<ClientPublicKey>>(),
|
||||
),
|
||||
@@ -87,12 +85,13 @@ pub(crate) async fn get_client(
|
||||
Ok(pub_key) => pub_key,
|
||||
Err(_) => return (StatusCode::BAD_REQUEST, Json(vec![])),
|
||||
};
|
||||
let registry_ro = state.client_registry.read().await;
|
||||
let clients = registry_ro
|
||||
let clients = state
|
||||
.client_registry
|
||||
.iter()
|
||||
.filter_map(|(_, c)| {
|
||||
if c.pub_key() == &pub_key {
|
||||
Some(c.clone())
|
||||
.filter_map(|r| {
|
||||
let client = r.value();
|
||||
if client.pub_key() == &pub_key {
|
||||
Some(client.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::{
|
||||
routing::{get, post},
|
||||
Router,
|
||||
};
|
||||
use dashmap::DashMap;
|
||||
use log::info;
|
||||
use nym_crypto::asymmetric::encryption;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
mod api;
|
||||
use api::v1::client_registry::*;
|
||||
@@ -16,9 +16,9 @@ use super::client_handling::client_registration::{ClientPublicKey, ClientRegistr
|
||||
const ROUTE_PREFIX: &str = "/api/v1/gateway/client-interfaces/wireguard";
|
||||
|
||||
pub struct ApiState {
|
||||
client_registry: Arc<RwLock<ClientRegistry>>,
|
||||
client_registry: Arc<ClientRegistry>,
|
||||
sphinx_key_pair: Arc<encryption::KeyPair>,
|
||||
registration_in_progress: Arc<RwLock<HashMap<ClientPublicKey, u64>>>,
|
||||
registration_in_progress: Arc<DashMap<ClientPublicKey, u64>>,
|
||||
}
|
||||
|
||||
fn router_with_state(state: Arc<ApiState>) -> Router {
|
||||
@@ -33,7 +33,7 @@ fn router_with_state(state: Arc<ApiState>) -> Router {
|
||||
}
|
||||
|
||||
pub(crate) async fn start_http_api(
|
||||
client_registry: Arc<RwLock<ClientRegistry>>,
|
||||
client_registry: Arc<ClientRegistry>,
|
||||
sphinx_key_pair: Arc<encryption::KeyPair>,
|
||||
) {
|
||||
// Port should be 80 post smoosh
|
||||
@@ -46,7 +46,7 @@ pub(crate) async fn start_http_api(
|
||||
let state = Arc::new(ApiState {
|
||||
client_registry,
|
||||
sphinx_key_pair,
|
||||
registration_in_progress: Arc::new(RwLock::new(HashMap::new())),
|
||||
registration_in_progress: Arc::new(DashMap::new()),
|
||||
});
|
||||
|
||||
let routes = router_with_state(state);
|
||||
@@ -62,17 +62,17 @@ pub(crate) async fn start_http_api(
|
||||
mod test {
|
||||
use std::net::SocketAddr;
|
||||
use std::str::FromStr;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::body::Body;
|
||||
use axum::http::Request;
|
||||
use axum::http::StatusCode;
|
||||
use dashmap::DashMap;
|
||||
use hmac::Mac;
|
||||
use tower::Service;
|
||||
use tower::ServiceExt;
|
||||
|
||||
use nym_crypto::asymmetric::encryption;
|
||||
use tokio::sync::RwLock;
|
||||
use x25519_dalek::{PublicKey, StaticSecret};
|
||||
|
||||
use crate::node::client_handling::client_registration::{
|
||||
@@ -105,8 +105,8 @@ mod test {
|
||||
|
||||
let client_dh = client_static_private.diffie_hellman(&gateway_static_public);
|
||||
|
||||
let registration_in_progress = Arc::new(RwLock::new(HashMap::new()));
|
||||
let client_registry = Arc::new(RwLock::new(HashMap::new()));
|
||||
let registration_in_progress = Arc::new(DashMap::new());
|
||||
let client_registry = Arc::new(DashMap::new());
|
||||
|
||||
let state = Arc::new(ApiState {
|
||||
client_registry: Arc::clone(&client_registry),
|
||||
@@ -136,7 +136,7 @@ mod test {
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert!(!registration_in_progress.read().await.is_empty());
|
||||
assert!(!registration_in_progress.is_empty());
|
||||
|
||||
let nonce: Option<u64> =
|
||||
serde_json::from_slice(&hyper::body::to_bytes(response.into_body()).await.unwrap())
|
||||
@@ -171,7 +171,7 @@ mod test {
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert!(!client_registry.read().await.is_empty());
|
||||
assert!(!client_registry.is_empty());
|
||||
|
||||
let clients_request = Request::builder()
|
||||
.method("GET")
|
||||
@@ -194,11 +194,10 @@ mod test {
|
||||
|
||||
assert!(!clients.is_empty());
|
||||
|
||||
let ro_clients = client_registry.read().await.clone();
|
||||
assert_eq!(
|
||||
ro_clients
|
||||
.values()
|
||||
.map(|c| c.pub_key().clone())
|
||||
client_registry
|
||||
.iter()
|
||||
.map(|c| c.value().pub_key().clone())
|
||||
.collect::<Vec<ClientPublicKey>>(),
|
||||
clients
|
||||
)
|
||||
|
||||
@@ -18,6 +18,7 @@ use crate::node::mixnet_handling::receiver::connection_handler::ConnectionHandle
|
||||
use crate::node::statistics::collector::GatewayStatisticsCollector;
|
||||
use crate::node::storage::Storage;
|
||||
use anyhow::bail;
|
||||
use dashmap::DashMap;
|
||||
use futures::channel::{mpsc, oneshot};
|
||||
use log::*;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
@@ -29,12 +30,10 @@ use nym_task::{TaskClient, TaskManager};
|
||||
use nym_validator_client::{nyxd, DirectSigningHttpRpcNyxdClient};
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::net::SocketAddr;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
pub(crate) mod client_handling;
|
||||
pub(crate) mod helpers;
|
||||
@@ -91,7 +90,7 @@ pub(crate) struct Gateway<St = PersistentStorage> {
|
||||
sphinx_keypair: Arc<encryption::KeyPair>,
|
||||
storage: St,
|
||||
|
||||
client_registry: Arc<RwLock<ClientRegistry>>,
|
||||
client_registry: Arc<ClientRegistry>,
|
||||
}
|
||||
|
||||
impl<St> Gateway<St> {
|
||||
@@ -107,7 +106,7 @@ impl<St> Gateway<St> {
|
||||
sphinx_keypair: Arc::new(helpers::load_sphinx_keys(&config)?),
|
||||
config,
|
||||
network_requester_opts,
|
||||
client_registry: Arc::new(RwLock::new(HashMap::new())),
|
||||
client_registry: Arc::new(DashMap::new()),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -125,7 +124,7 @@ impl<St> Gateway<St> {
|
||||
identity_keypair: Arc::new(identity_keypair),
|
||||
sphinx_keypair: Arc::new(sphinx_keypair),
|
||||
storage,
|
||||
client_registry: Arc::new(RwLock::new(HashMap::new())),
|
||||
client_registry: Arc::new(DashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "extension-storage"
|
||||
version = "1.2.0-rc.10"
|
||||
version = "1.2.0"
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/nymtech/nym"
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v1.2.9] (2023-10-10)
|
||||
|
||||
- Wallet: Introduce edit account name ([#3895])
|
||||
|
||||
[#3895]: https://github.com/nymtech/nym/pull/3895
|
||||
|
||||
## [v1.2.8] (2023-08-23)
|
||||
|
||||
- [hotfix]: don't assign invalid fields when crossing the JS boundary ([#3805])
|
||||
|
||||
Generated
+1
-1
@@ -3512,7 +3512,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym_wallet"
|
||||
version = "1.2.8"
|
||||
version = "1.2.9"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.13.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nymproject/nym-wallet-app",
|
||||
"version": "1.2.8",
|
||||
"version": "1.2.9",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
@@ -123,4 +123,4 @@
|
||||
"webpack-favicons": "^1.3.8",
|
||||
"webpack-merge": "^5.8.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym_wallet"
|
||||
version = "1.2.8"
|
||||
version = "1.2.9"
|
||||
description = "Nym Native Wallet"
|
||||
authors = ["Nym Technologies SA"]
|
||||
license = ""
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"package": {
|
||||
"productName": "nym-wallet",
|
||||
"version": "1.2.8"
|
||||
"version": "1.2.9"
|
||||
},
|
||||
"build": {
|
||||
"distDir": "../dist",
|
||||
|
||||
@@ -125,7 +125,6 @@ export type TBondingContext = {
|
||||
updateBondAmount: (data: TUpdateBondArgs, tokenPool: TokenPool) => Promise<TransactionExecuteResult | undefined>;
|
||||
redeemRewards: (fee?: FeeDetails) => Promise<TransactionExecuteResult | undefined>;
|
||||
updateMixnode: (pm: string, fee?: FeeDetails) => Promise<TransactionExecuteResult | undefined>;
|
||||
checkOwnership: () => Promise<void>;
|
||||
generateMixnodeMsgPayload: (data: TBondMixnodeSignatureArgs) => Promise<string | undefined>;
|
||||
generateGatewayMsgPayload: (data: TBondGatewaySignatureArgs) => Promise<string | undefined>;
|
||||
isVestingAccount: boolean;
|
||||
@@ -152,9 +151,6 @@ export const BondingContext = createContext<TBondingContext>({
|
||||
updateMixnode: async () => {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
checkOwnership(): Promise<void> {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
generateMixnodeMsgPayload: async () => {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
@@ -171,7 +167,7 @@ export const BondingContextProvider: FCWithChildren = ({ children }): JSX.Elemen
|
||||
const [isVestingAccount, setIsVestingAccount] = useState(false);
|
||||
|
||||
const { userBalance, clientDetails } = useContext(AppContext);
|
||||
const { ownership, isLoading: isOwnershipLoading, checkOwnership } = useCheckOwnership();
|
||||
const { ownership, isLoading: isOwnershipLoading } = useCheckOwnership();
|
||||
|
||||
useEffect(() => {
|
||||
userBalance.fetchBalance();
|
||||
@@ -342,8 +338,6 @@ export const BondingContextProvider: FCWithChildren = ({ children }): JSX.Elemen
|
||||
setIsLoading(true);
|
||||
setError(undefined);
|
||||
|
||||
await checkOwnership();
|
||||
|
||||
if (ownership.hasOwnership && ownership.nodeType === EnumNodeType.mixnode && clientDetails) {
|
||||
try {
|
||||
const data = await getMixnodeBondDetails();
|
||||
@@ -617,7 +611,6 @@ export const BondingContextProvider: FCWithChildren = ({ children }): JSX.Elemen
|
||||
refresh,
|
||||
redeemRewards,
|
||||
updateBondAmount,
|
||||
checkOwnership,
|
||||
generateMixnodeMsgPayload,
|
||||
generateGatewayMsgPayload,
|
||||
isVestingAccount,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { AppContext } from '../context/main';
|
||||
import { checkGatewayOwnership, checkMixnodeOwnership, getVestingPledgeInfo } from '../requests';
|
||||
import { EnumNodeType, TNodeOwnership } from '../types';
|
||||
|
||||
const initial = {
|
||||
const initial: TNodeOwnership = {
|
||||
hasOwnership: false,
|
||||
nodeType: undefined,
|
||||
vestingPledge: undefined,
|
||||
@@ -18,8 +18,7 @@ export const useCheckOwnership = () => {
|
||||
const [error, setError] = useState<string>();
|
||||
|
||||
const checkOwnership = useCallback(async () => {
|
||||
const status = initial as TNodeOwnership;
|
||||
|
||||
const status = { ...initial };
|
||||
try {
|
||||
const [ownsMixnode, ownsGateway] = await Promise.all([checkMixnodeOwnership(), checkGatewayOwnership()]);
|
||||
|
||||
|
||||
@@ -34,17 +34,8 @@ const Bonding = () => {
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const {
|
||||
bondedNode,
|
||||
bondMixnode,
|
||||
bondGateway,
|
||||
redeemRewards,
|
||||
isLoading,
|
||||
checkOwnership,
|
||||
updateBondAmount,
|
||||
error,
|
||||
refresh,
|
||||
} = useBondingContext();
|
||||
const { bondedNode, bondMixnode, bondGateway, redeemRewards, isLoading, updateBondAmount, error, refresh } =
|
||||
useBondingContext();
|
||||
|
||||
useEffect(() => {
|
||||
if (bondedNode && isMixnode(bondedNode) && bondedNode.uncappedStakeSaturation) {
|
||||
@@ -54,7 +45,7 @@ const Bonding = () => {
|
||||
|
||||
const handleCloseModal = async () => {
|
||||
setShowModal(undefined);
|
||||
await checkOwnership();
|
||||
refresh();
|
||||
};
|
||||
|
||||
const handleError = (err: string) => {
|
||||
|
||||
+15
-10
@@ -6,7 +6,10 @@
|
||||
"workspaces": [
|
||||
"dist/wasm/**",
|
||||
"dist/node/**",
|
||||
"sdk/typescript/packages/**",
|
||||
"dist/ts/**",
|
||||
"sdk/typescript/packages/mui-theme",
|
||||
"sdk/typescript/packages/react-components",
|
||||
"sdk/typescript/packages/validator-client",
|
||||
"ts-packages/*",
|
||||
"nym-wallet",
|
||||
"nym-connect/**",
|
||||
@@ -16,22 +19,22 @@
|
||||
],
|
||||
"scripts": {
|
||||
"nuke": "npx rimraf **/node_modules node_modules",
|
||||
"scrub": "npx rimraf **/dist dist",
|
||||
"clean": "lerna run clean",
|
||||
|
||||
"build:ci:sdk": "run-s build:types build:packages build:wasm build:sdk:ci",
|
||||
"build:sdk:ci": "lerna run --scope '{@nymproject/sdk,@nymproject/node-tester,@nymproject/sdk-react,@nymproject/mix-fetch}' build:dev --stream",
|
||||
|
||||
"build": "run-s build:types build:packages",
|
||||
"build:wasm": "make sdk-wasm-build",
|
||||
"build:sdk": "make sdk-typescript-build",
|
||||
"build:types": "lerna run --scope @nymproject/types build --stream",
|
||||
"build:packages": "run-s build:packages:theme build:packages:react",
|
||||
"build:packages:theme": "lerna run --scope @nymproject/mui-theme build",
|
||||
"build:packages:react": "lerna run --scope @nymproject/react build",
|
||||
"build:react-example": "lerna run --scope @nymproject/react-webpack-with-theme-example build --stream",
|
||||
"build:playground": "lerna run --scope @nymproject/react storybook:build --stream",
|
||||
"build:ci": "yarn build && run-p build:react-example build:playground && yarn build:ci:collect-artifacts",
|
||||
"build:ci:collect-artifacts": "mkdir -p ts-packages/dist && mv ts-packages/react-components/storybook-static ts-packages/dist/storybook && mv ts-packages/react-webpack-with-theme-example/dist ts-packages/dist/example",
|
||||
"build:ci:storybook": "yarn build && yarn dev:on && run-p build:react-example build:playground && yarn build:ci:storybook:collect-artifacts",
|
||||
"build:ci:storybook:collect-artifacts": "mkdir -p ts-packages/dist && mv sdk/typescript/packages/react-components/storybook-static ts-packages/dist/storybook && mv sdk/typescript/examples/react/mui-theme/dist ts-packages/dist/example",
|
||||
"prebuild:ci": "yarn dev:on && yarn",
|
||||
"build:ci": "run-s build:types build:packages build:wasm build:ci:sdk",
|
||||
"postbuild:ci": "yarn dev:off",
|
||||
"build:ci:sdk": "lerna run --scope '{@nymproject/sdk,@nymproject/node-tester,@nymproject/sdk-react,@nymproject/mix-fetch}' build:dev --stream",
|
||||
"docs:prod:build": "run-s docs:prod:build:ws",
|
||||
"docs:prod:build:ws": "lerna run docs:prod:build --stream",
|
||||
"sdk:build": "./sdk/typescript/scripts/build-prod-sdk.sh",
|
||||
@@ -40,7 +43,9 @@
|
||||
"lint:fix": "lerna run lint:fix --stream",
|
||||
"tsc": "lerna run tsc --stream",
|
||||
"types:lint:fix": "lerna run lint:fix --scope @nymproject/types --scope @nymproject/nym-wallet-app",
|
||||
"audit:fix": "npm_config_yes=true npx yarn-audit-fix -- --dry-run"
|
||||
"audit:fix": "npm_config_yes=true npx yarn-audit-fix -- --dry-run",
|
||||
"dev:on": "node sdk/typescript/scripts/dev-mode-add.mjs",
|
||||
"dev:off": "node sdk/typescript/scripts/dev-mode-remove.mjs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"lerna": "^7.3.0",
|
||||
@@ -48,4 +53,4 @@
|
||||
"@npmcli/node-gyp": "^3.0.0",
|
||||
"node-gyp": "^9.3.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,6 @@ use libp2p::ping::Success;
|
||||
use libp2p::swarm::{keep_alive, NetworkBehaviour, SwarmEvent};
|
||||
use libp2p::{identity, ping, Multiaddr, PeerId};
|
||||
use log::{debug, info, LevelFilter};
|
||||
use nym_sdk::mixnet::MixnetClient;
|
||||
use std::error::Error;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -70,7 +69,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
use libp2p::swarm::SwarmBuilder;
|
||||
use rust_libp2p_nym::transport::NymTransport;
|
||||
|
||||
let client = MixnetClient::connect_new().await.unwrap();
|
||||
let client = nym_sdk::mixnet::MixnetClient::connect_new().await.unwrap();
|
||||
|
||||
let transport = NymTransport::new(client, local_key.clone()).await?;
|
||||
SwarmBuilder::with_tokio_executor(
|
||||
|
||||
@@ -80,6 +80,7 @@ pub struct NymTransport {
|
||||
|
||||
impl NymTransport {
|
||||
/// New transport.
|
||||
#[allow(unused)]
|
||||
pub async fn new(client: MixnetClient, keypair: Keypair) -> Result<Self, Error> {
|
||||
Self::new_maybe_with_notify_inbound(client, keypair, None, None).await
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nymproject/contract-clients",
|
||||
"version": "1.2.0-rc.10",
|
||||
"version": "1.2.0",
|
||||
"description": "A client for all Nym smart contracts",
|
||||
"license": "Apache-2.0",
|
||||
"author": "Nym Technologies SA",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nymproject/ts-sdk-docs",
|
||||
"version": "1.2.0-rc.10",
|
||||
"version": "1.2.0",
|
||||
"description": "Nym Typescript SDK Docs",
|
||||
"license": "Apache-2.0",
|
||||
"author": "Nym Technologies SA",
|
||||
@@ -28,10 +28,10 @@
|
||||
"@mui/icons-material": "^5.14.9",
|
||||
"@mui/lab": "^5.0.0-alpha.145",
|
||||
"@mui/material": "^5.14.8",
|
||||
"@nymproject/contract-clients": "^1.2.0-rc.9",
|
||||
"@nymproject/mix-fetch": "^1.2.0-rc.9",
|
||||
"@nymproject/mix-fetch-full-fat": "^1.2.0-rc.9",
|
||||
"@nymproject/sdk-full-fat": "^1.2.0-rc.9",
|
||||
"@nymproject/contract-clients": ">=1.2.0-rc.10 || ^1",
|
||||
"@nymproject/mix-fetch": ">=1.2.0-rc.10 || ^1",
|
||||
"@nymproject/mix-fetch-full-fat": ">=1.2.0-rc.10 || ^1",
|
||||
"@nymproject/sdk-full-fat": ">=1.2.0-rc.10 || ^1",
|
||||
"chain-registry": "^1.19.0",
|
||||
"cosmjs-types": "^0.8.0",
|
||||
"next": "^13.4.19",
|
||||
@@ -51,4 +51,4 @@
|
||||
"typescript": "^4.9.3"
|
||||
},
|
||||
"private": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"source": "src/index.html",
|
||||
"browserslist": "> 0.5%, last 2 versions, not dead",
|
||||
"dependencies": {
|
||||
"@nymproject/sdk": "1.2.0-rc.1"
|
||||
"@nymproject/sdk": ">=1.2.0-rc.10 || ^1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^27.0.1",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"version": "1.0.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@nymproject/sdk": "1.2.0-rc.1"
|
||||
"@nymproject/sdk": ">=1.2.0-rc.10 || ^1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.15.0",
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"@mui/material": "^5.0.1",
|
||||
"@mui/styles": "^5.0.1",
|
||||
"react-mui-dropzone": "^4.0.6",
|
||||
"@nymproject/sdk": "1.2.0-rc.1",
|
||||
"@nymproject/sdk": ">=1.2.0-rc.10 || ^1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-dropzone": "^14.2.3",
|
||||
|
||||
@@ -15,6 +15,6 @@
|
||||
"webpack-cli": "^5.1.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nymproject/sdk": "1.2.0-rc.1"
|
||||
"@nymproject/sdk": ">=1.2.0-rc.10 || ^1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,6 @@
|
||||
"build": "yarn webpack"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nymproject/sdk": "1.2.0-rc.1"
|
||||
"@nymproject/sdk": ">=1.2.0-rc.10 || ^1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"source": "src/index.html",
|
||||
"dependencies": {
|
||||
"parcel": "^2.9.3",
|
||||
"@nymproject/mix-fetch": ">=1.2.0-rc.7 || ^1"
|
||||
"@nymproject/mix-fetch": ">=1.2.0-rc.10 || ^1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "parcel --no-cache",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"source": "src/index.html",
|
||||
"browserslist": "> 0.5%, last 2 versions, not dead",
|
||||
"dependencies": {
|
||||
"@nymproject/sdk": "1.2.0-rc.1"
|
||||
"@nymproject/sdk": ">=1.2.0-rc.10 || ^1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^27.0.1",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"version": "1.0.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@nymproject/sdk": "1.2.0-rc.1"
|
||||
"@nymproject/sdk": ">=1.2.0-rc.10 || ^1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.15.0",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@mui/icons-material": "^5.14.0",
|
||||
"@mui/material": "^5.14.0",
|
||||
"@nymproject/sdk": "1.2.0-rc.1",
|
||||
"@nymproject/sdk": ">=1.2.0-rc.10 || ^1",
|
||||
"parcel": "^2.9.3",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"outDir": "./dist"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"outDir": "./dist",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"version": "1.0.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@nymproject/mix-fetch": ">=1.2.0-rc.7 || ^1"
|
||||
"@nymproject/mix-fetch": ">=1.2.0-rc.10 || ^1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.22.10",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"source": "../src/index.html",
|
||||
"dependencies": {
|
||||
"@nymproject/mix-fetch": ">=1.2.0-rc.7 || ^1"
|
||||
"@nymproject/mix-fetch": ">=1.2.0-rc.10 || ^1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "npx parcel --no-cache",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nymproject/mix-fetch",
|
||||
"version": "1.2.0-rc.10",
|
||||
"version": "1.2.0",
|
||||
"description": "This package is a drop-in replacement for `fetch` to send HTTP requests over the Nym Mixnet.",
|
||||
"license": "Apache-2.0",
|
||||
"author": "Nym Technologies SA",
|
||||
@@ -33,7 +33,7 @@
|
||||
"tsc": "tsc --noEmit true"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nymproject/mix-fetch-wasm": ">=1.2.0-rc.7 || 1",
|
||||
"@nymproject/mix-fetch-wasm": ">=1.2.0-rc.10 || ^1",
|
||||
"comlink": "^4.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -81,4 +81,4 @@
|
||||
"private": false,
|
||||
"type": "module",
|
||||
"types": "./dist/esm/index.d.ts"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nymproject/node-tester",
|
||||
"version": "1.2.0-rc.10",
|
||||
"version": "1.2.0",
|
||||
"description": "This package provides a tester that can send test packets to mixnode that is part of the Nym Mixnet.",
|
||||
"license": "Apache-2.0",
|
||||
"author": "Nym Technologies SA",
|
||||
@@ -25,7 +25,7 @@
|
||||
"tsc": "tsc --noEmit true"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nymproject/nym-node-tester-wasm": ">=1.2.0-rc.7 || ^1",
|
||||
"@nymproject/nym-node-tester-wasm": ">=1.2.0-rc.10 || ^1",
|
||||
"comlink": "^4.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -71,4 +71,4 @@
|
||||
"private": false,
|
||||
"type": "module",
|
||||
"types": "./dist/esm/index.d.ts"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nymproject/sdk-react",
|
||||
"version": "1.2.0-rc.10",
|
||||
"version": "1.2.0",
|
||||
"license": "Apache-2.0",
|
||||
"author": "Nym Technologies SA",
|
||||
"files": [
|
||||
@@ -20,7 +20,7 @@
|
||||
"tsc": "tsc --noEmit true"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nymproject/sdk": ">=1.2.0-rc.7 || ^1"
|
||||
"@nymproject/sdk": ">=1.2.0-rc.10 || ^1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.17.5",
|
||||
@@ -67,4 +67,4 @@
|
||||
"private": false,
|
||||
"type": "module",
|
||||
"types": "./dist/index.d.ts"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nymproject/sdk",
|
||||
"version": "1.2.0-rc.10",
|
||||
"version": "1.2.0",
|
||||
"license": "Apache-2.0",
|
||||
"author": "Nym Technologies SA",
|
||||
"files": [
|
||||
@@ -30,7 +30,7 @@
|
||||
"tsc": "tsc --noEmit true"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nymproject/nym-client-wasm": ">=1.2.0-rc.7 || ^1",
|
||||
"@nymproject/nym-client-wasm": ">=1.2.0-rc.10 || ^1",
|
||||
"comlink": "^4.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -80,4 +80,4 @@
|
||||
"private": false,
|
||||
"type": "module",
|
||||
"types": "./dist/esm/index.d.ts"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nymproject/nym-validator-client",
|
||||
"version": "1.2.0-rc.10",
|
||||
"version": "1.2.0",
|
||||
"description": "A TypeScript client for interacting with smart contracts in Nym validators",
|
||||
"license": "Apache-2.0",
|
||||
"author": "Nym Technologies SA (https://nymtech.net)",
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import fs from 'fs';
|
||||
|
||||
const packageJson = JSON.parse(fs.readFileSync('package.json').toString());
|
||||
|
||||
const devWorkspace = ['sdk/typescript/packages/**', 'sdk/typescript/examples/**'];
|
||||
if (!packageJson.workspaces.includes(devWorkspace)) {
|
||||
// add
|
||||
packageJson.workspaces.push(...devWorkspace);
|
||||
|
||||
// write out modified file
|
||||
fs.writeFileSync('package.json', JSON.stringify(packageJson, null, 2));
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import fs from 'fs';
|
||||
|
||||
const packageJson = JSON.parse(fs.readFileSync('package.json').toString());
|
||||
|
||||
const devWorkspace = ['sdk/typescript/packages/**', 'sdk/typescript/examples/**'];
|
||||
|
||||
// remove
|
||||
packageJson.workspaces = packageJson.workspaces.filter((w) => !devWorkspace.includes(w));
|
||||
|
||||
// write out modified file
|
||||
fs.writeFileSync('package.json', JSON.stringify(packageJson, null, 2));
|
||||
@@ -34,7 +34,7 @@ use nym_socks5_requests::{
|
||||
};
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||
use nym_sphinx::params::PacketSize;
|
||||
use nym_sphinx::params::{PacketSize, PacketType};
|
||||
use nym_sphinx::receiver::ReconstructedMessage;
|
||||
use nym_statistics_common::collector::StatisticsSender;
|
||||
use nym_task::connections::LaneQueueLengths;
|
||||
@@ -321,6 +321,7 @@ impl NRServiceProviderBuilder {
|
||||
let stats_collector_clone = stats_collector.clone();
|
||||
let mixnet_client_sender = mixnet_client.split_sender();
|
||||
let self_address = *mixnet_client.nym_address();
|
||||
let packet_type = self.config.base.debug.traffic.packet_type;
|
||||
|
||||
// start the listener for mix messages
|
||||
tokio::spawn(async move {
|
||||
@@ -328,6 +329,7 @@ impl NRServiceProviderBuilder {
|
||||
mixnet_client_sender,
|
||||
mix_input_receiver,
|
||||
stats_collector_clone,
|
||||
packet_type,
|
||||
)
|
||||
.await;
|
||||
});
|
||||
@@ -420,6 +422,7 @@ impl NRServiceProvider {
|
||||
mixnet_client_sender: nym_sdk::mixnet::MixnetClientSender,
|
||||
mut mix_input_reader: MixProxyReader<MixnetMessage>,
|
||||
stats_collector: Option<ServiceStatisticsCollector>,
|
||||
packet_type: PacketType,
|
||||
) {
|
||||
loop {
|
||||
tokio::select! {
|
||||
@@ -440,7 +443,7 @@ impl NRServiceProvider {
|
||||
}
|
||||
}
|
||||
|
||||
let response_message = msg.into_input_message();
|
||||
let response_message = msg.into_input_message(packet_type);
|
||||
mixnet_client_sender.send(response_message).await.unwrap();
|
||||
} else {
|
||||
log::error!("Exiting: channel closed!");
|
||||
|
||||
@@ -11,6 +11,7 @@ use nym_socks5_requests::{
|
||||
};
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||
use nym_sphinx::params::PacketType;
|
||||
use nym_task::connections::TransmissionLane;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
|
||||
@@ -148,8 +149,9 @@ impl MixnetMessage {
|
||||
self.data.len()
|
||||
}
|
||||
|
||||
pub(crate) fn into_input_message(self) -> InputMessage {
|
||||
self.address.send_back_to(self.data, self.connection_id)
|
||||
pub(crate) fn into_input_message(self, packet_type: PacketType) -> InputMessage {
|
||||
self.address
|
||||
.send_back_to(self.data, self.connection_id, packet_type)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,17 +177,28 @@ impl MixnetAddress {
|
||||
None
|
||||
}
|
||||
|
||||
pub(super) fn send_back_to(self, message: Vec<u8>, connection_id: u64) -> InputMessage {
|
||||
pub(super) fn send_back_to(
|
||||
self,
|
||||
message: Vec<u8>,
|
||||
connection_id: u64,
|
||||
packet_type: PacketType,
|
||||
) -> InputMessage {
|
||||
match self {
|
||||
MixnetAddress::Known(recipient) => InputMessage::Regular {
|
||||
recipient: *recipient,
|
||||
data: message,
|
||||
lane: TransmissionLane::ConnectionId(connection_id),
|
||||
MixnetAddress::Known(recipient) => InputMessage::MessageWrapper {
|
||||
message: Box::new(InputMessage::Regular {
|
||||
recipient: *recipient,
|
||||
data: message,
|
||||
lane: TransmissionLane::ConnectionId(connection_id),
|
||||
}),
|
||||
packet_type,
|
||||
},
|
||||
MixnetAddress::Anonymous(sender_tag) => InputMessage::Reply {
|
||||
recipient_tag: sender_tag,
|
||||
data: message,
|
||||
lane: TransmissionLane::ConnectionId(connection_id),
|
||||
MixnetAddress::Anonymous(sender_tag) => InputMessage::MessageWrapper {
|
||||
message: Box::new(InputMessage::Reply {
|
||||
recipient_tag: sender_tag,
|
||||
data: message,
|
||||
lane: TransmissionLane::ConnectionId(connection_id),
|
||||
}),
|
||||
packet_type,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,15 +174,24 @@ fn initialise_internal_packages<P: AsRef<Path>>(root: P) -> InternalPackages {
|
||||
packages.register_cargo("nym-browser-extension/storage");
|
||||
|
||||
// js packages that will have their package.json modified
|
||||
packages.register_json("nym-wallet");
|
||||
packages.register_json("sdk/typescript/docs");
|
||||
packages.register_json("sdk/typescript/examples/chat-app/parcel");
|
||||
packages.register_json("sdk/typescript/examples/chat-app/plain-html");
|
||||
packages.register_json("sdk/typescript/examples/chat-app/react-webpack-with-theme-example");
|
||||
packages.register_json("sdk/typescript/examples/chrome-extension");
|
||||
packages.register_json("sdk/typescript/examples/firefox-extension");
|
||||
packages.register_json("sdk/typescript/examples/mix-fetch/browser");
|
||||
packages.register_json("sdk/typescript/examples/node-tester/parcel");
|
||||
packages.register_json("sdk/typescript/examples/node-tester/plain-html");
|
||||
packages.register_json("sdk/typescript/examples/node-tester/react");
|
||||
packages.register_json("sdk/typescript/packages/mix-fetch");
|
||||
packages.register_json("sdk/typescript/packages/mui-theme");
|
||||
packages.register_json("sdk/typescript/packages/mix-fetch/internal-dev");
|
||||
packages.register_json("sdk/typescript/packages/mix-fetch/internal-dev/parcel");
|
||||
packages.register_json("sdk/typescript/packages/node-tester");
|
||||
packages.register_json("sdk/typescript/packages/react-components");
|
||||
packages.register_json("sdk/typescript/packages/nodejs-client");
|
||||
packages.register_json("sdk/typescript/packages/sdk");
|
||||
packages.register_json("sdk/typescript/packages/sdk-react");
|
||||
packages.register_json("sdk/typescript/packages/validator-client");
|
||||
packages.register_json("sdk/typescript/codegen/contract-clients");
|
||||
packages.register_json("sdk/typescript/docs");
|
||||
|
||||
// dependencies that will have their versions adjusted in the above packages
|
||||
packages.register_known_js_dependency("@nymproject/mix-fetch");
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "nym-client-wasm"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
|
||||
version = "1.2.0-rc.10"
|
||||
version = "1.2.0"
|
||||
edition = "2021"
|
||||
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
|
||||
license = "Apache-2.0"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "nym-wasm-sdk"
|
||||
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
|
||||
version = "1.2.0-rc.10"
|
||||
version = "1.2.0"
|
||||
edition = "2021"
|
||||
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy"]
|
||||
license = "Apache-2.0"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "mix-fetch-wasm"
|
||||
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
|
||||
version = "1.2.0-rc.10"
|
||||
version = "1.2.0"
|
||||
edition = "2021"
|
||||
keywords = ["nym", "fetch", "wasm", "webassembly", "privacy"]
|
||||
license = "Apache-2.0"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "nym-node-tester-wasm"
|
||||
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
|
||||
version = "1.2.0-rc.10"
|
||||
version = "1.2.0"
|
||||
edition = "2021"
|
||||
keywords = ["nym", "sphinx", "webassembly", "privacy", "tester"]
|
||||
license = "Apache-2.0"
|
||||
|
||||
Reference in New Issue
Block a user