Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2cc99b486e | |||
| 556d980c01 | |||
| b86ae7520e | |||
| 43da0e3aa7 |
@@ -43,7 +43,7 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --release --features wireguard
|
||||
args: --workspace --release
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --release --features wireguard
|
||||
args: --workspace --release
|
||||
|
||||
- name: Prepare build output
|
||||
shell: bash
|
||||
|
||||
Generated
-1
@@ -7572,7 +7572,6 @@ dependencies = [
|
||||
"nym-network-defaults",
|
||||
"nym-task",
|
||||
"nym-wireguard-types",
|
||||
"serde",
|
||||
"tokio",
|
||||
"x25519-dalek 2.0.0",
|
||||
]
|
||||
|
||||
@@ -275,10 +275,6 @@ impl NymApiClient {
|
||||
Ok(self.nym_api.get_gateways().await?)
|
||||
}
|
||||
|
||||
pub async fn get_all_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorClientError> {
|
||||
Ok(self.nym_api.get_all_gateways().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_described_gateways(
|
||||
&self,
|
||||
) -> Result<Vec<DescribedGateway>, ValidatorClientError> {
|
||||
|
||||
@@ -79,11 +79,6 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_all_gateways(&self) -> Result<Vec<GatewayBond>, NymAPIError> {
|
||||
self.get_json(&[routes::API_VERSION, routes::GATEWAYS_ALL], NO_PARAMS)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_gateways_described(&self) -> Result<Vec<DescribedGateway>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[routes::API_VERSION, routes::GATEWAYS, routes::DESCRIBED],
|
||||
|
||||
@@ -6,7 +6,6 @@ use nym_network_defaults::NYM_API_VERSION;
|
||||
pub const API_VERSION: &str = NYM_API_VERSION;
|
||||
pub const MIXNODES: &str = "mixnodes";
|
||||
pub const GATEWAYS: &str = "gateways";
|
||||
pub const GATEWAYS_ALL: &str = "gateways/all";
|
||||
pub const DESCRIBED: &str = "described";
|
||||
|
||||
pub const DETAILED: &str = "detailed";
|
||||
|
||||
@@ -81,15 +81,6 @@ ExitPolicy accept6 *6:119
|
||||
ExitPolicy accept *4:120
|
||||
ExitPolicy reject6 [FC00::]/7:*
|
||||
|
||||
# Portless
|
||||
ExitPolicy accept *:0
|
||||
ExitPolicy accept *4:0
|
||||
ExitPolicy accept *6:0
|
||||
|
||||
ExitPolicy reject *:0
|
||||
ExitPolicy reject *4:0
|
||||
ExitPolicy reject *6:0
|
||||
|
||||
#ExitPolicy accept *:8080 #and another comment here
|
||||
|
||||
ExitPolicy reject FE80:0000:0000:0000:0202:B3FF:FE1E:8329:*
|
||||
@@ -193,60 +184,6 @@ ExitPolicy reject *:*
|
||||
},
|
||||
);
|
||||
|
||||
// ExitPolicy accept *:0
|
||||
expected.push(
|
||||
Accept,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::Star,
|
||||
ports: PortRange::new_zero(),
|
||||
},
|
||||
);
|
||||
|
||||
// ExitPolicy accept *4:0
|
||||
expected.push(
|
||||
Accept,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::V4Star,
|
||||
ports: PortRange::new_zero(),
|
||||
},
|
||||
);
|
||||
|
||||
// ExitPolicy accept *6:0
|
||||
expected.push(
|
||||
Accept,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::V6Star,
|
||||
ports: PortRange::new_zero(),
|
||||
},
|
||||
);
|
||||
|
||||
// ExitPolicy reject *:0
|
||||
expected.push(
|
||||
Reject,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::Star,
|
||||
ports: PortRange::new_zero(),
|
||||
},
|
||||
);
|
||||
|
||||
// ExitPolicy reject *4:0
|
||||
expected.push(
|
||||
Reject,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::V4Star,
|
||||
ports: PortRange::new_zero(),
|
||||
},
|
||||
);
|
||||
|
||||
// ExitPolicy reject *6:0
|
||||
expected.push(
|
||||
Reject,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::V6Star,
|
||||
ports: PortRange::new_zero(),
|
||||
},
|
||||
);
|
||||
|
||||
// ExitPolicy FE80:0000:0000:0000:0202:B3FF:FE1E:8329:*
|
||||
expected.push(
|
||||
Reject,
|
||||
|
||||
@@ -264,13 +264,7 @@ mod stringified_ip_pattern {
|
||||
impl AddressPortPattern {
|
||||
/// Return true iff this pattern matches a given address and port.
|
||||
pub fn matches(&self, addr: &IpAddr, port: u16) -> bool {
|
||||
// For backward compatibility, we treat port 0 as a wildcard until all gateways have
|
||||
// upgraded, at which point we can add *:0 to the policy list.
|
||||
if port == 0 {
|
||||
self.ip_pattern.matches(addr)
|
||||
} else {
|
||||
self.ip_pattern.matches(addr) && self.ports.contains(port)
|
||||
}
|
||||
self.ip_pattern.matches(addr) && self.ports.contains(port)
|
||||
}
|
||||
|
||||
/// As matches, but accept a SocketAddr.
|
||||
@@ -401,9 +395,19 @@ fn parse_addr(s: &str) -> Result<IpAddr, PolicyError> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Helper: try to parse a port making sure it's non-zero
|
||||
fn parse_port(s: &str) -> Result<u16, PolicyError> {
|
||||
s.parse::<u16>()
|
||||
.map_err(|_| PolicyError::InvalidPort { raw: s.to_string() })
|
||||
let port = s
|
||||
.parse::<u16>()
|
||||
.map_err(|_| PolicyError::InvalidPort { raw: s.to_string() })?;
|
||||
|
||||
if port == 0 {
|
||||
Err(PolicyError::InvalidPort {
|
||||
raw: port.to_string(),
|
||||
})
|
||||
} else {
|
||||
Ok(port)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for IpPattern {
|
||||
@@ -490,10 +494,6 @@ impl PortRange {
|
||||
PortRange::new_unchecked(1, 65535)
|
||||
}
|
||||
|
||||
pub fn new_zero() -> Self {
|
||||
PortRange { start: 0, end: 0 }
|
||||
}
|
||||
|
||||
/// Create a new PortRange.
|
||||
///
|
||||
/// The Portrange contains all ports between `start` and `end` inclusive.
|
||||
@@ -574,7 +574,6 @@ mod test {
|
||||
|
||||
check("marzipan:80");
|
||||
check("1.2.3.4:90-80");
|
||||
check("1.2.3.4:0-80");
|
||||
check("1.2.3.4/100:8888");
|
||||
check("[1.2.3.4]/16:80");
|
||||
check("[::1]/130:8888");
|
||||
@@ -613,22 +612,6 @@ mod test {
|
||||
|
||||
check("0.0.0.0/0:*", &["127.0.0.1:80"], &["[f00b::]:80"]);
|
||||
check("[::]/0:*", &["[f00b::]:80"], &["127.0.0.1:80"]);
|
||||
|
||||
check(
|
||||
"*:0",
|
||||
&["1.2.3.4:0", "[::1]:0", "9.0.0.0:0"],
|
||||
&["1.2.3.4:443", "[::1]:500", "9.0.0.0:80", "[::1]:80"],
|
||||
);
|
||||
check(
|
||||
"*4:0",
|
||||
&["1.2.3.4:0", "9.0.0.0:0"],
|
||||
&["1.2.3.4:443", "9.0.0.0:80", "[::1]:0", "[::1]:80"],
|
||||
);
|
||||
check(
|
||||
"*6:0",
|
||||
&["[::1]:0"],
|
||||
&["[::1]:80", "1.2.3.4:0", "1.2.3.4:443"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -637,7 +620,6 @@ mod test {
|
||||
policy.push(AddressPolicyAction::Accept, "*:443".parse()?);
|
||||
policy.push(AddressPolicyAction::Accept, "[::1]:80".parse()?);
|
||||
policy.push(AddressPolicyAction::Reject, "*:80".parse()?);
|
||||
policy.push(AddressPolicyAction::Accept, "*:0".parse()?);
|
||||
|
||||
let policy = policy; // drop mut
|
||||
assert!(policy
|
||||
@@ -658,9 +640,6 @@ mod test {
|
||||
assert!(policy
|
||||
.allows_sockaddr(&"127.0.0.1:66".parse().unwrap())
|
||||
.is_none());
|
||||
assert!(policy
|
||||
.allows_sockaddr(&"127.0.0.1:0".parse().unwrap())
|
||||
.unwrap());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -693,6 +672,7 @@ mod test {
|
||||
assert_eq!("*".parse::<PortRange>().unwrap(), PortRange::new_all());
|
||||
|
||||
assert!("hello".parse::<PortRange>().is_err());
|
||||
assert!("0".parse::<PortRange>().is_err());
|
||||
assert!("65536".parse::<PortRange>().is_err());
|
||||
assert!("65537".parse::<PortRange>().is_err());
|
||||
assert!("1-2-3".parse::<PortRange>().is_err());
|
||||
@@ -700,9 +680,6 @@ mod test {
|
||||
assert!("1-".parse::<PortRange>().is_err());
|
||||
assert!("-2".parse::<PortRange>().is_err());
|
||||
assert!("-".parse::<PortRange>().is_err());
|
||||
|
||||
assert_eq!("0".parse::<PortRange>().unwrap(), PortRange::new_zero(),);
|
||||
assert!("0-1".parse::<PortRange>().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -22,5 +22,4 @@ log.workspace = true
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
nym-task = { path = "../task" }
|
||||
nym-wireguard-types = { path = "../wireguard-types" }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "net", "io-util"] }
|
||||
|
||||
+11
-10
@@ -9,12 +9,13 @@ use nym_wireguard_types::registration::GatewayClientRegistry;
|
||||
use std::sync::Arc;
|
||||
|
||||
// Currently the module related to setting up the virtual network device is platform specific.
|
||||
use crate::setup::PeerPair;
|
||||
#[cfg(target_os = "linux")]
|
||||
use crate::setup::{peer_static_pairs, PRIVATE_KEY};
|
||||
use crate::setup::{peer_allowed_ips, peer_static_public_key, PRIVATE_KEY};
|
||||
use defguard_wireguard_rs::WGApi;
|
||||
#[cfg(target_os = "linux")]
|
||||
use defguard_wireguard_rs::{InterfaceConfiguration, WireguardInterfaceApi};
|
||||
use defguard_wireguard_rs::{
|
||||
host::Peer, key::Key, net::IpAddrMask, InterfaceConfiguration, WireguardInterfaceApi,
|
||||
};
|
||||
#[cfg(target_os = "linux")]
|
||||
use nym_network_defaults::{WG_PORT, WG_TUN_DEVICE_ADDRESS};
|
||||
|
||||
@@ -23,7 +24,6 @@ use nym_network_defaults::{WG_PORT, WG_TUN_DEVICE_ADDRESS};
|
||||
pub async fn start_wireguard(
|
||||
mut task_client: nym_task::TaskClient,
|
||||
_gateway_client_registry: Arc<GatewayClientRegistry>,
|
||||
peer_pairs: Vec<PeerPair>,
|
||||
) -> Result<WGApi, Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
let ifname = String::from("wg0");
|
||||
let wgapi = WGApi::new(ifname.clone(), false)?;
|
||||
@@ -36,11 +36,13 @@ pub async fn start_wireguard(
|
||||
peers: vec![],
|
||||
};
|
||||
wgapi.configure_interface(&interface_config)?;
|
||||
let peers = peer_static_pairs(peer_pairs);
|
||||
for peer in peers.iter() {
|
||||
wgapi.configure_peer(peer)?;
|
||||
}
|
||||
wgapi.configure_peer_routing(&peers)?;
|
||||
let peer = peer_static_public_key();
|
||||
let mut peer = Peer::new(Key::new(peer.to_bytes()));
|
||||
let peer_ip = peer_allowed_ips();
|
||||
let peer_ip_mask = IpAddrMask::new(peer_ip.network_address(), peer_ip.netmask());
|
||||
peer.set_allowed_ips(vec![peer_ip_mask]);
|
||||
wgapi.configure_peer(&peer)?;
|
||||
wgapi.configure_peer_routing(&[peer.clone()])?;
|
||||
|
||||
tokio::spawn(async move { task_client.recv().await });
|
||||
|
||||
@@ -50,7 +52,6 @@ pub async fn start_wireguard(
|
||||
pub async fn start_wireguard(
|
||||
_task_client: nym_task::TaskClient,
|
||||
_gateway_client_registry: Arc<GatewayClientRegistry>,
|
||||
_peer_pairs: Vec<PeerPair>,
|
||||
) -> Result<WGApi, Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
todo!("WireGuard is currently only supported on Linux")
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
use std::net::IpAddr;
|
||||
|
||||
use base64::{engine::general_purpose, Engine as _};
|
||||
use defguard_wireguard_rs::host::Peer;
|
||||
use defguard_wireguard_rs::key::Key;
|
||||
use defguard_wireguard_rs::net::IpAddrMask;
|
||||
use log::{info, warn};
|
||||
use serde::Deserialize;
|
||||
use log::info;
|
||||
|
||||
// The wireguard UDP listener
|
||||
pub const WG_ADDRESS: &str = "0.0.0.0";
|
||||
@@ -14,23 +10,21 @@ pub const WG_ADDRESS: &str = "0.0.0.0";
|
||||
// Corresponding public key: "WM8s8bYegwMa0TJ+xIwhk+dImk2IpDUKslDBCZPizlE="
|
||||
pub(crate) const PRIVATE_KEY: &str = "AEqXrLFT4qjYq3wmX0456iv94uM6nDj5ugp6Jedcflg=";
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct PeerPair {
|
||||
pub addr: String,
|
||||
pub public_key: String,
|
||||
}
|
||||
// The AllowedIPs for the connected peer, which is one a single IP and the same as the IP that the
|
||||
// peer has configured on their side.
|
||||
const ALLOWED_IPS: &str = "10.1.0.2";
|
||||
|
||||
fn decode_base64_key(base64_key: &str) -> Result<[u8; 32], String> {
|
||||
fn decode_base64_key(base64_key: &str) -> [u8; 32] {
|
||||
general_purpose::STANDARD
|
||||
.decode(base64_key)
|
||||
.map_err(|_| String::from("Could not decode"))?
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.map_err(|_| String::from("Not enough bytes"))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn server_static_private_key() -> x25519_dalek::StaticSecret {
|
||||
// TODO: this is a temporary solution for development
|
||||
let static_private_bytes: [u8; 32] = decode_base64_key(PRIVATE_KEY).unwrap();
|
||||
let static_private_bytes: [u8; 32] = decode_base64_key(PRIVATE_KEY);
|
||||
let static_private = x25519_dalek::StaticSecret::from(static_private_bytes);
|
||||
let static_public = x25519_dalek::PublicKey::from(&static_private);
|
||||
info!(
|
||||
@@ -40,29 +34,23 @@ pub fn server_static_private_key() -> x25519_dalek::StaticSecret {
|
||||
static_private
|
||||
}
|
||||
|
||||
pub fn peer_static_pairs(raw_pairs: Vec<PeerPair>) -> Vec<Peer> {
|
||||
raw_pairs
|
||||
.into_iter()
|
||||
.filter_map(|pair| {
|
||||
if let Ok(peer_static_public_bytes) = decode_base64_key(&pair.public_key) {
|
||||
let peer_static_public = x25519_dalek::PublicKey::from(peer_static_public_bytes);
|
||||
let mut peer = Peer::new(Key::new(peer_static_public.to_bytes()));
|
||||
pub fn peer_static_public_key() -> x25519_dalek::PublicKey {
|
||||
// A single static public key is used during development
|
||||
|
||||
if let Ok(key) = pair.addr.parse::<IpAddr>() {
|
||||
let peer_ip = ip_network::IpNetwork::new_truncate(key, 32u8)
|
||||
.expect("Netmask should be correct");
|
||||
let peer_ip_mask =
|
||||
IpAddrMask::new(peer_ip.network_address(), peer_ip.netmask());
|
||||
peer.set_allowed_ips(vec![peer_ip_mask]);
|
||||
Some(peer)
|
||||
} else {
|
||||
warn!("Not adding {:?} as IP doesn't parse", pair);
|
||||
None
|
||||
}
|
||||
} else {
|
||||
warn!("Not adding {:?} as public key doesn't decode", pair);
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
// Read from NYM_PEER_PUBLIC_KEY env variable
|
||||
let peer = std::env::var("NYM_PEER_PUBLIC_KEY").expect("NYM_PEER_PUBLIC_KEY must be set");
|
||||
|
||||
let peer_static_public_bytes: [u8; 32] = decode_base64_key(&peer);
|
||||
let peer_static_public = x25519_dalek::PublicKey::from(peer_static_public_bytes);
|
||||
info!(
|
||||
"Adding wg peer public key: {}",
|
||||
general_purpose::STANDARD.encode(peer_static_public)
|
||||
);
|
||||
peer_static_public
|
||||
}
|
||||
|
||||
pub fn peer_allowed_ips() -> ip_network::IpNetwork {
|
||||
let key: IpAddr = ALLOWED_IPS.parse().unwrap();
|
||||
let cidr = 32u8;
|
||||
ip_network::IpNetwork::new_truncate(key, cidr).unwrap()
|
||||
}
|
||||
|
||||
@@ -10,19 +10,22 @@ use crate::dealings::transactions::try_commit_dealings;
|
||||
use crate::epoch_state::queries::{
|
||||
query_current_epoch, query_current_epoch_threshold, query_initial_dealers,
|
||||
};
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH;
|
||||
use crate::epoch_state::storage::{CURRENT_EPOCH, THRESHOLD};
|
||||
use crate::epoch_state::transactions::{advance_epoch_state, try_surpassed_threshold};
|
||||
use crate::error::ContractError;
|
||||
use crate::state::{State, MULTISIG, STATE};
|
||||
use crate::verification_key_shares::queries::query_vk_shares_paged;
|
||||
use crate::verification_key_shares::storage::vk_shares;
|
||||
use crate::verification_key_shares::transactions::try_commit_verification_key_share;
|
||||
use crate::verification_key_shares::transactions::try_verify_verification_key_share;
|
||||
use cosmwasm_std::{
|
||||
entry_point, to_binary, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response,
|
||||
entry_point, to_binary, Addr, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response,
|
||||
Timestamp,
|
||||
};
|
||||
use cw4::Cw4Contract;
|
||||
use nym_coconut_dkg_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
|
||||
use nym_coconut_dkg_common::types::{Epoch, EpochState};
|
||||
use nym_coconut_dkg_common::types::{Epoch, EpochId, EpochState, TimeConfiguration};
|
||||
use nym_coconut_dkg_common::verification_key::ContractVKShare;
|
||||
|
||||
/// Instantiate the contract.
|
||||
///
|
||||
@@ -127,7 +130,85 @@ pub fn query(deps: Deps<'_>, _env: Env, msg: QueryMsg) -> Result<QueryResponse,
|
||||
}
|
||||
|
||||
#[entry_point]
|
||||
pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
|
||||
pub fn migrate(deps: DepsMut<'_>, env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
|
||||
CURRENT_EPOCH.save(
|
||||
deps.storage,
|
||||
&Epoch {
|
||||
state: EpochState::InProgress,
|
||||
epoch_id: 0,
|
||||
time_configuration: TimeConfiguration {
|
||||
public_key_submission_time_secs: 999999,
|
||||
dealing_exchange_time_secs: 999999,
|
||||
verification_key_submission_time_secs: 999999,
|
||||
verification_key_validation_time_secs: 999999,
|
||||
verification_key_finalization_time_secs: 999999,
|
||||
in_progress_time_secs: 999999,
|
||||
},
|
||||
finish_timestamp: env.block.time.plus_days(10000),
|
||||
},
|
||||
)?;
|
||||
|
||||
let apis = [
|
||||
"https://qa-nym-api-coconut1.qa.nymte.ch/api",
|
||||
"https://qa-nym-api-coconut2.qa.nymte.ch/api",
|
||||
"https://qa-nym-api-coconut3.qa.nymte.ch/api",
|
||||
"https://qa-nym-api-coconut-6.qa.nymte.ch/api",
|
||||
"https://qa-nym-api-coconut-7.qa.nymte.ch/api",
|
||||
"https://qa-nym-api-coconut-8.qa.nymte.ch/api",
|
||||
"https://qa-nym-api-coconut-9.qa.nymte.ch/api",
|
||||
"https://qa-nym-api-coconut-10.qa.nymte.ch/api",
|
||||
"https://qa-nym-api-coconut-11.qa.nymte.ch/api",
|
||||
"https://qa-nym-api-coconut-12.qa.nymte.ch/api",
|
||||
];
|
||||
|
||||
let addresses = [
|
||||
Addr::unchecked("n1e6wkf0x2l4qt45uwkft9t7fs3ka02rsmft5jjn"),
|
||||
Addr::unchecked("n190f6rwtcpfgx3y84af6eapg54suehd4nzq4sh3"),
|
||||
Addr::unchecked("n1c7nu7wncuru09eg2m8429d5ff6umytet993xmf"),
|
||||
Addr::unchecked("n144fypmxc9jrdk28qrjlptpqn7h077f30vvvdjf"),
|
||||
Addr::unchecked("n15rmhp3psrnlcgp8tsa9y528ppwt0vvwuazfa4a"),
|
||||
Addr::unchecked("n18dy35dtsg7ycyp7g9pvlj29ksmjpu9h6tznxdl"),
|
||||
Addr::unchecked("n17usrtqe6ypvn3r4v05sspu3ufs30uykc7euwne"),
|
||||
Addr::unchecked("n1efpvtu5x7x4v293pc42a2fwkf265lnh05r023z"),
|
||||
Addr::unchecked("n15k2zadxm7m3wyfyx7g2uk2tctmmp43syxuqmdf"),
|
||||
Addr::unchecked("n1k4w8pp62htn0tdmrsv3gh9kzrap4tpalw8eqd7"),
|
||||
];
|
||||
|
||||
let keys = [
|
||||
"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",
|
||||
];
|
||||
|
||||
for (i, ((address, api), key)) in addresses
|
||||
.iter()
|
||||
.zip(apis.iter())
|
||||
.zip(keys.iter())
|
||||
.enumerate()
|
||||
{
|
||||
let share = ContractVKShare {
|
||||
share: key.to_string(),
|
||||
announce_address: api.to_string(),
|
||||
node_index: (i + 1) as u64,
|
||||
owner: address.clone(),
|
||||
epoch_id: 0,
|
||||
verified: true,
|
||||
};
|
||||
|
||||
vk_shares().save(deps.storage, (address, 0), &share)?;
|
||||
}
|
||||
|
||||
THRESHOLD.save(deps.storage, &7)?;
|
||||
|
||||
Ok(Default::default())
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
- [Querying the Chain](tutorials/cosmos-service/querying.md)
|
||||
|
||||
- [Typescript](tutorials/typescript.md)
|
||||
- [Simple Service Provider](tutorials/simple-service-provider/simple-service-provider.md)
|
||||
- [[DEPRECATED] Simple Service Provider](tutorials/simple-service-provider/simple-service-provider.md)
|
||||
- [Tutorial Overview](tutorials/simple-service-provider/overview.md)
|
||||
- [Preparing Your User Client Environment](tutorials/simple-service-provider/preparating-env.md)
|
||||
- [Building Your User Client](tutorials/simple-service-provider/user-client.md)
|
||||
@@ -59,12 +59,11 @@
|
||||
- [Building Your Service Provider](tutorials/simple-service-provider/service-provider.md)
|
||||
- [Sending a Message Through the Mixnet](tutorials/simple-service-provider/sending-message.md)
|
||||
|
||||
[//]: # (TODO make generic )
|
||||
[//]: # (# Shipyard Builders Hackathon 2023 )
|
||||
[//]: # (- [General Info & Resources](shipyard/general.md))
|
||||
[//]: # (- [Hackathon Challenges](shipyard/challenges-overview.md))
|
||||
[//]: # (- [A Note on Infrastructure](shipyard/infra.md))
|
||||
[//]: # (- [Submission Guidelines](shipyard/guidelines.md))
|
||||
# Shipyard Builders Hackathon 2023
|
||||
- [General Info & Resources](shipyard/general.md)
|
||||
- [Hackathon Challenges](shipyard/challenges-overview.md)
|
||||
- [A Note on Infrastructure](shipyard/infra.md)
|
||||
- [Submission Guidelines](shipyard/guidelines.md)
|
||||
|
||||
# Events
|
||||
|
||||
|
||||
@@ -61,8 +61,7 @@ Quite a bit of stuff gets built. The key working parts are:
|
||||
* [network requester](../nodes/network-requester.md): `nym-network-requester`
|
||||
* [nym-cli tool](../tools/nym-cli.md): `nym-cli`
|
||||
* [nym-api](https://nymtech.net/operators/nodes/nym-api.html): `nym-api`
|
||||
|
||||
[//]: # (* [nymvisor](https://nymtech.net/operators/nodes/nymvisor-upgrade.html): `nymvisor`)
|
||||
* [nymvisor](https://nymtech.net/operators/nodes/nymvisor-upgrade.html): `nymvisor`
|
||||
|
||||
The repository also contains Typescript applications which aren't built in this process. These can be built by following the instructions on their respective docs pages.
|
||||
* [Nym Wallet](../wallet/desktop-wallet.md)
|
||||
|
||||
@@ -4,9 +4,9 @@ This is Nym's technical documentation, containing information and setup guides a
|
||||
|
||||
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.html).
|
||||
|
||||
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 [Operators Guides](https://nymtech.net/operators) book.
|
||||
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 specifically looking for TypeScript/JavaScript related information such as SDKs to build your own tools, step-by-step tutorials, live playgrounds and more - check out the [TS SDK Handbook](https://sdk.nymtech.net/).
|
||||
If you're specifically 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:**
|
||||
|
||||
@@ -20,8 +20,7 @@
|
||||
- [Nym API Setup](nodes/nym-api.md)
|
||||
- [Maintenance](nodes/maintenance.md)
|
||||
- [Manual Node Upgrade](nodes/manual-upgrade.md)
|
||||
|
||||
[//]: # ( - [Automatic Node Upgrade: Nymvisor Setup and Usage](nodes/nymvisor-upgrade.md))
|
||||
- [Automatic Node Upgrade: Nymvisor Setup and Usage](nodes/nymvisor-upgrade.md)
|
||||
- [Troubleshooting](nodes/troubleshooting.md)
|
||||
|
||||
# FAQ
|
||||
|
||||
@@ -62,8 +62,7 @@ Quite a bit of stuff gets built. The key working parts are:
|
||||
* [network requester](../nodes/network-requester-setup.md): `nym-network-requester`
|
||||
* [nym-cli tool](https://nymtech.net/docs/tools/nym-cli.html): `nym-cli`
|
||||
* [nym-api](../nodes/nym-api.md): `nym-api`
|
||||
|
||||
[//]: # (* [nymvisor](../nodes/nymvisor-upgrade.md): `nymvisor`)
|
||||
* [nymvisor](../nodes/nymvisor-upgrade.md): `nymvisor`
|
||||
|
||||
The repository also contains Typescript applications which aren't built in this process. These can be built by following the instructions on their respective docs pages.
|
||||
* [Nym Wallet](https://nymtech.net/docs/wallet/desktop-wallet.html)
|
||||
|
||||
@@ -242,11 +242,6 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_client_keys_path(mut self, client_keys: PathBuf) -> Self {
|
||||
self.wireguard.storage_paths.client_keys = client_keys;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_listening_address(mut self, listening_address: IpAddr) -> Self {
|
||||
self.gateway.listening_address = listening_address;
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ impl From<ConfigV1_1_31> for Config {
|
||||
announced_port: value.wireguard.announced_port,
|
||||
private_network_prefix: Default::default(),
|
||||
storage_paths: nym_node::config::persistence::WireguardPaths {
|
||||
client_keys: Default::default(),
|
||||
// no fields (yet)
|
||||
},
|
||||
},
|
||||
storage_paths: GatewayPaths {
|
||||
|
||||
@@ -207,10 +207,7 @@ impl<St> Gateway<St> {
|
||||
&self,
|
||||
shutdown: TaskClient,
|
||||
) -> Result<WGApi, Box<dyn Error + Send + Sync>> {
|
||||
let file = std::fs::File::open(&self.config.wireguard.storage_paths.client_keys)?;
|
||||
let reader = std::io::BufReader::new(file);
|
||||
let peers = serde_json::from_reader(reader)?;
|
||||
nym_wireguard::start_wireguard(shutdown, Arc::clone(&self.client_registry), peers).await
|
||||
nym_wireguard::start_wireguard(shutdown, Arc::clone(&self.client_registry)).await
|
||||
}
|
||||
|
||||
fn start_client_websocket_listener(
|
||||
|
||||
@@ -57,14 +57,14 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
|
||||
&config.storage_paths.decryption_key_path,
|
||||
&config.storage_paths.public_key_with_proof_path,
|
||||
))?;
|
||||
if let Ok(coconut_keypair_value) =
|
||||
nym_pemstore::load_keypair(&nym_pemstore::KeyPairPath::new(
|
||||
&config.storage_paths.secret_key_path,
|
||||
&config.storage_paths.verification_key_path,
|
||||
))
|
||||
{
|
||||
coconut_keypair.set(Some(coconut_keypair_value)).await;
|
||||
}
|
||||
// if let Ok(coconut_keypair_value) =
|
||||
// nym_pemstore::load_keypair(&nym_pemstore::KeyPairPath::new(
|
||||
// &config.storage_paths.secret_key_path,
|
||||
// &config.storage_paths.verification_key_path,
|
||||
// ))
|
||||
// {
|
||||
// coconut_keypair.set(Some(coconut_keypair_value)).await;
|
||||
// }
|
||||
let persistent_state =
|
||||
PersistentState::load_from_file(&config.storage_paths.dkg_persistent_state_path)
|
||||
.unwrap_or_default();
|
||||
@@ -186,15 +186,17 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
|
||||
}
|
||||
|
||||
pub(crate) async fn run(mut self, mut shutdown: TaskClient) {
|
||||
let mut interval = interval(self.polling_rate);
|
||||
while !shutdown.is_shutdown() {
|
||||
tokio::select! {
|
||||
_ = interval.tick() => self.handle_epoch_state().await,
|
||||
_ = shutdown.recv() => {
|
||||
trace!("DkgController: Received shutdown");
|
||||
}
|
||||
}
|
||||
}
|
||||
warn!("ignoring the dkg epochs!");
|
||||
shutdown.mark_as_success();
|
||||
// let mut interval = interval(self.polling_rate);
|
||||
// while !shutdown.is_shutdown() {
|
||||
// tokio::select! {
|
||||
// _ = interval.tick() => self.handle_epoch_state().await,
|
||||
// _ = shutdown.recv() => {
|
||||
// trace!("DkgController: Received shutdown");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// TODO: can we make it non-async? it seems we'd have to modify `coconut_keypair.set(coconut_keypair_value)` in new
|
||||
|
||||
@@ -80,6 +80,14 @@ async fn start_nym_api_tasks(
|
||||
let network_details = NetworkDetails::new(connected_nyxd.to_string(), nym_network_details);
|
||||
|
||||
let coconut_keypair = coconut::keypair::KeyPair::new();
|
||||
let keys = nym_pemstore::load_keypair(&nym_pemstore::KeyPairPath::new(
|
||||
&config.coconut_signer.storage_paths.secret_key_path,
|
||||
&config.coconut_signer.storage_paths.verification_key_path,
|
||||
))
|
||||
.expect("failed to load coconut keys");
|
||||
|
||||
coconut_keypair.set(Some(keys)).await;
|
||||
info!("set coconut keys");
|
||||
|
||||
// let's build our rocket!
|
||||
let rocket = http::setup_rocket(
|
||||
|
||||
@@ -146,7 +146,7 @@ impl PacketPreparer {
|
||||
let initialisation_backoff = Duration::from_secs(30);
|
||||
loop {
|
||||
let gateways = self.validator_cache.gateways_all().await;
|
||||
let mixnodes = self.validator_cache.mixnodes_all_basic().await;
|
||||
let mixnodes = self.validator_cache.mixnodes_basic().await;
|
||||
|
||||
if gateways.len() < minimum_full_routes {
|
||||
self.topology_wait_backoff(initialisation_backoff).await;
|
||||
@@ -179,21 +179,12 @@ impl PacketPreparer {
|
||||
async fn all_mixnodes_and_gateways(&self) -> (Vec<MixNodeBond>, Vec<GatewayBond>) {
|
||||
info!("Obtaining network topology...");
|
||||
|
||||
let mixnodes = self.validator_cache.mixnodes_all_basic().await;
|
||||
let mixnodes = self.validator_cache.mixnodes_basic().await;
|
||||
let gateways = self.validator_cache.gateways_all().await;
|
||||
|
||||
(mixnodes, gateways)
|
||||
}
|
||||
|
||||
async fn filtered_mixnodes_and_gateways(&self) -> (Vec<MixNodeBond>, Vec<GatewayBond>) {
|
||||
info!("Obtaining network topology...");
|
||||
|
||||
let mixnodes = self.validator_cache.mixnodes_filtered_basic().await;
|
||||
let gateways = self.validator_cache.gateways_filtered().await;
|
||||
|
||||
(mixnodes, gateways)
|
||||
}
|
||||
|
||||
pub(crate) fn try_parse_mix_bond(&self, mix: &MixNodeBond) -> Result<mix::Node, String> {
|
||||
let identity = mix.mix_node.identity_key.clone();
|
||||
mix.try_into().map_err(|_| identity)
|
||||
@@ -217,7 +208,7 @@ impl PacketPreparer {
|
||||
n: usize,
|
||||
blacklist: &mut HashSet<String>,
|
||||
) -> Option<Vec<TestRoute>> {
|
||||
let (mixnodes, gateways) = self.filtered_mixnodes_and_gateways().await;
|
||||
let (mixnodes, gateways) = self.all_mixnodes_and_gateways().await;
|
||||
// separate mixes into layers for easier selection
|
||||
let mut layered_mixes = HashMap::new();
|
||||
for mix in mixnodes {
|
||||
|
||||
+13
-14
@@ -182,20 +182,19 @@ impl NymContractCache {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn mixnodes_filtered_basic(&self) -> Vec<MixNodeBond> {
|
||||
self.mixnodes_filtered()
|
||||
.await
|
||||
.into_iter()
|
||||
.map(|bond| bond.bond_information)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub async fn mixnodes_all_basic(&self) -> Vec<MixNodeBond> {
|
||||
self.mixnodes_all()
|
||||
.await
|
||||
.into_iter()
|
||||
.map(|bond| bond.bond_information)
|
||||
.collect()
|
||||
pub async fn mixnodes_basic(&self) -> Vec<MixNodeBond> {
|
||||
match time::timeout(Duration::from_millis(100), self.inner.read()).await {
|
||||
Ok(cache) => cache
|
||||
.mixnodes
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|bond| bond.bond_information)
|
||||
.collect(),
|
||||
Err(err) => {
|
||||
error!("{err}");
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn gateways_filtered(&self) -> Vec<GatewayBond> {
|
||||
|
||||
@@ -19,7 +19,6 @@ pub(crate) fn nym_contract_cache_routes(settings: &OpenApiSettings) -> (Vec<Rout
|
||||
settings: routes::get_mixnodes,
|
||||
routes::get_mixnodes_detailed,
|
||||
routes::get_gateways,
|
||||
routes::get_all_gateways,
|
||||
routes::get_active_set,
|
||||
routes::get_active_set_detailed,
|
||||
routes::get_rewarded_set,
|
||||
|
||||
@@ -114,12 +114,6 @@ pub async fn get_blacklisted_gateways(
|
||||
}
|
||||
}
|
||||
|
||||
#[openapi(tag = "contract-cache")]
|
||||
#[get("/gateways/all")]
|
||||
pub async fn get_all_gateways(cache: &State<NymContractCache>) -> Json<Vec<GatewayBond>> {
|
||||
Json(cache.gateways_all().await.clone())
|
||||
}
|
||||
|
||||
#[openapi(tag = "contract-cache")]
|
||||
#[get("/epoch/reward_params")]
|
||||
pub async fn get_interval_reward_params(
|
||||
|
||||
@@ -119,7 +119,7 @@ impl StorageManager {
|
||||
r#"
|
||||
SELECT
|
||||
d.identity as "identity: String",
|
||||
CASE WHEN count(*) > 3 THEN AVG(reliability) ELSE 100 END as "value: f32"
|
||||
AVG(reliability) as "value: f32"
|
||||
FROM
|
||||
gateway_details d
|
||||
JOIN
|
||||
|
||||
@@ -93,9 +93,7 @@ impl Default for Wireguard {
|
||||
),
|
||||
announced_port: DEFAULT_WIREGUARD_PORT,
|
||||
private_network_prefix: DEFAULT_WIREGUARD_PREFIX,
|
||||
storage_paths: persistence::WireguardPaths {
|
||||
client_keys: PathBuf::from("/root/keys_pub.json"),
|
||||
},
|
||||
storage_paths: persistence::WireguardPaths {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct WireguardPaths {
|
||||
pub client_keys: PathBuf,
|
||||
// pub keys:
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ impl MyTopologyProvider {
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let gateways = self.validator_client.get_all_gateways().await.unwrap();
|
||||
let gateways = self.validator_client.get_cached_gateways().await.unwrap();
|
||||
|
||||
nym_topology_from_detailed(filtered_mixnodes, gateways)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::net::SocketAddr;
|
||||
|
||||
pub use nym_client_core::error::ClientCoreError;
|
||||
use nym_exit_policy::PolicyError;
|
||||
@@ -57,15 +57,9 @@ pub enum IpPacketRouterError {
|
||||
#[error("the provided socket address, '{addr}' is not covered by the exit policy!")]
|
||||
AddressNotCoveredByExitPolicy { addr: SocketAddr },
|
||||
|
||||
#[error("the provided ip address, '{ip}' is not covered by the exit policy!")]
|
||||
IpNotCoveredByExitPolicy { ip: IpAddr },
|
||||
|
||||
#[error("failed filter check: '{addr}'")]
|
||||
AddressFailedFilterCheck { addr: SocketAddr },
|
||||
|
||||
#[error("failed filter check: '{ip}'")]
|
||||
IpFailedFilterCheck { ip: IpAddr },
|
||||
|
||||
#[error("failed to apply the exit policy: {source}")]
|
||||
ExitPolicyFailure {
|
||||
#[from]
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
net::{IpAddr, SocketAddr},
|
||||
};
|
||||
use std::{collections::HashMap, net::IpAddr};
|
||||
|
||||
use futures::StreamExt;
|
||||
use nym_ip_packet_requests::{
|
||||
@@ -323,11 +320,15 @@ impl MixnetListener {
|
||||
}
|
||||
|
||||
// Filter check
|
||||
let dst = dst.unwrap_or_else(|| SocketAddr::new(dst_addr, 0));
|
||||
if !self.request_filter.check_address(&dst).await {
|
||||
log::info!("Denied filter check: {dst}");
|
||||
// TODO: we could consider sending back a response here
|
||||
return Err(IpPacketRouterError::AddressFailedFilterCheck { addr: dst });
|
||||
if let Some(dst) = dst {
|
||||
if !self.request_filter.check_address(&dst).await {
|
||||
log::warn!("Failed filter check: {dst}");
|
||||
// TODO: we could consider sending back a response here
|
||||
return Err(IpPacketRouterError::AddressFailedFilterCheck { addr: dst });
|
||||
}
|
||||
} else {
|
||||
// TODO: we should also filter packets without port number
|
||||
log::warn!("Ignoring filter check for packet without port number! TODO!");
|
||||
}
|
||||
|
||||
// TODO: consider changing from Vec<u8> to bytes::Bytes?
|
||||
|
||||
Reference in New Issue
Block a user