Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions common/lthash/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,19 @@ publish = true
# pin (<1.8.4 keeps blake3 on digest 0.10).
blake3 = { version = ">=1.7, <1.8.4", default-features = false, features = ["traits-preview"] }
digest = { workspace = true }
# Not `workspace = true`, same reason as blake3 above (default-features = false for
# no_std). Optional and off by default so the wasm contract build (which never
# serializes an `LtHash`) does not pay for it; native consumers opt in via the `serde`
# feature.
serde = { version = "1.0.219", default-features = false, optional = true }
serde_bytes = { version = "0.11.17", default-features = false, optional = true }

[dev-dependencies]
sha3 = { workspace = true }
bincode = { workspace = true }

[features]
serde = ["dep:serde", "dep:serde_bytes"]

[lints]
workspace = true
34 changes: 34 additions & 0 deletions common/lthash/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
//! * <https://eprint.iacr.org/2019/227>: Securing Update Propagation with Homomorphic Hashing
//! * <https://github.com/facebook/folly/blob/main/folly/crypto/LtHash.cpp>: C++ implementation of the LtHash used at Facebook.

use core::hash::{Hash, Hasher};
use core::marker::PhantomData;
use digest::{Digest, ExtendableOutput, Output, Update, XofReader};

Expand Down Expand Up @@ -67,6 +68,21 @@ impl<H> LtHash<H> {
}
}

#[cfg(feature = "serde")]
impl<H> serde::Serialize for LtHash<H> {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serde_bytes::serialize(&self.to_bytes(), serializer)
}
}

#[cfg(feature = "serde")]
impl<'de, H> serde::Deserialize<'de> for LtHash<H> {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let bytes: [u8; DIGEST_LEN] = serde_bytes::deserialize(deserializer)?;
Ok(LtHash::from_bytes(&bytes))
}
}

impl<H: Default + Update + ExtendableOutput> LtHash<H> {
fn expand(element: &[u8]) -> [u16; ELEMENTS] {
let mut hasher = H::default();
Expand Down Expand Up @@ -135,6 +151,12 @@ impl<H> PartialEq for LtHash<H> {
}
}

impl<T> Hash for LtHash<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.state.hash(state);
}
}

impl<H> Eq for LtHash<H> {}

impl<H> core::fmt::Debug for LtHash<H> {
Expand Down Expand Up @@ -213,6 +235,18 @@ mod tests {
assert_ne!(only_a, LtHash::<H>::new());
}

#[cfg(feature = "serde")]
#[test]
fn serde_round_trips_through_bincode() {
let mut lt = LtHash16::new();
lt.add(b"alice");
lt.add(b"bob");

let bytes = bincode::serialize(&lt).unwrap();
let restored: LtHash16 = bincode::deserialize(&bytes).unwrap();
assert_eq!(lt, restored);
}

#[test]
fn invariants_blake3() {
check_invariants::<blake3::Hasher>();
Expand Down
47 changes: 34 additions & 13 deletions common/network-defaults/src/mainnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

#[cfg(feature = "network")]
use crate::{ApiUrlConst, DenomDetails, ValidatorDetails};
use crate::{ApiUrlConst, DenomDetails, DirectoryAttestationSourceConst, ValidatorDetails};

pub const NETWORK_NAME: &str = "mainnet";

Expand Down Expand Up @@ -73,6 +73,25 @@ pub const UPGRADE_MODE_ATTESTATION_URL: &str =
pub const UPGRADE_MODE_ATTESTER_ED25519_BS58_PUBKEY: &str =
"3bgffBYcfFkTTXc2npNNn9MkddFZ3H2LrPjXDmnJzrqd";

#[cfg(feature = "network")]
pub const DIRECTORY_ATTESTATION_SOURCES: &[DirectoryAttestationSourceConst] = &[
DirectoryAttestationSourceConst {
api_url: "https://nym-api-signer-1.nymtech.net/api",
identity_ed25519_bs58: "5dRq2oCSD6GUozZH3Qq5p1hPuztT6sFvpxtSbK311tDp",
},
DirectoryAttestationSourceConst {
api_url: "https://nym-api-signer-2.nymtech.net/api",
identity_ed25519_bs58: "2dJHDhUr5bRWxELMzZBZGVAW8cUHEJC1mGQYniKscQMo",
},
// TODO: we need unconditional key exposure for this to work
// (currently https://validator.nymtech.net/api even though has an ed25519 key it does NOT expose
// it anywhere)
// DirectoryAttestationSourceConst {
// api_url: "https://validator.nymtech.net/api",
// identity_ed25519_bs58: "<UNKNOWN>",
// },
];

#[cfg(feature = "network")]
pub const NYM_VPN_APIS: &[ApiUrlConst] = &[
ApiUrlConst {
Expand All @@ -85,14 +104,6 @@ pub const NYM_VPN_APIS: &[ApiUrlConst] = &[
},
];

#[cfg(feature = "env")]
fn serialize_api_urls(urls: &[ApiUrlConst]) -> String {
serde_json::to_string(urls)
.inspect_err(|e| tracing::warn!("failed to serialize nym_api_urls for env: {e}"))
.unwrap_or_default()
}

// I'm making clippy mad on purpose, because that url HAS TO be updated and deployed before merging
pub const EXIT_POLICY_URL: &str =
"https://nymtech.net/.wellknown/network-requester/exit-policy.txt";

Expand Down Expand Up @@ -153,6 +164,7 @@ pub fn read_parsed_var<T: std::str::FromStr>(var: &str) -> Result<T, T::Err> {

#[cfg(all(feature = "env", feature = "network"))]
pub fn export_to_env() {
use crate::network::json_serialise;
use crate::var_names;

set_var_to_default(var_names::CONFIGURED, "true");
Expand Down Expand Up @@ -203,11 +215,11 @@ pub fn export_to_env() {
);
set_var_to_default(var_names::NYXD, NYXD_URL);
set_var_to_default(var_names::NYM_API, NYM_API);
set_var_to_default(var_names::NYM_APIS, &serialize_api_urls(NYM_APIS));
set_var_to_default(var_names::NYM_APIS, &json_serialise(NYM_APIS));
set_var_to_default(var_names::NYXD_WEBSOCKET, NYXD_WS);
set_var_to_default(var_names::EXIT_POLICY_URL, EXIT_POLICY_URL);
set_var_to_default(var_names::NYM_VPN_API, NYM_VPN_API);
set_var_to_default(var_names::NYM_VPN_APIS, &serialize_api_urls(NYM_VPN_APIS));
set_var_to_default(var_names::NYM_VPN_APIS, &json_serialise(NYM_VPN_APIS));
set_var_to_default(
var_names::UPGRADE_MODE_ATTESTATION_URL,
UPGRADE_MODE_ATTESTATION_URL,
Expand All @@ -218,10 +230,15 @@ pub fn export_to_env() {
);
set_var_to_default(var_names::NYXD_QUERY_LITE, NYXD_QUERY_LITE);
set_var_to_default(var_names::NYXD_WS_LITE, NYXD_WS_LITE);
set_var_to_default(
var_names::DIRECTORY_ATTESTATION_SOURCES,
&json_serialise(DIRECTORY_ATTESTATION_SOURCES),
);
}

#[cfg(all(feature = "env", feature = "network"))]
pub fn export_to_env_if_not_set() {
use crate::network::json_serialise;
use crate::var_names;

set_var_conditionally_to_default(var_names::CONFIGURED, "true");
Expand Down Expand Up @@ -264,9 +281,9 @@ pub fn export_to_env_if_not_set() {
);
set_var_conditionally_to_default(var_names::NYXD, NYXD_URL);
set_var_conditionally_to_default(var_names::NYM_API, NYM_API);
set_var_conditionally_to_default(var_names::NYM_APIS, &serialize_api_urls(NYM_APIS));
set_var_conditionally_to_default(var_names::NYM_APIS, &json_serialise(NYM_APIS));
set_var_conditionally_to_default(var_names::NYM_VPN_API, NYM_VPN_API);
set_var_conditionally_to_default(var_names::NYM_VPN_APIS, &serialize_api_urls(NYM_VPN_APIS));
set_var_conditionally_to_default(var_names::NYM_VPN_APIS, &json_serialise(NYM_VPN_APIS));
set_var_conditionally_to_default(var_names::NYXD_WEBSOCKET, NYXD_WS);
set_var_conditionally_to_default(var_names::EXIT_POLICY_URL, EXIT_POLICY_URL);
set_var_conditionally_to_default(
Expand All @@ -279,4 +296,8 @@ pub fn export_to_env_if_not_set() {
);
set_var_conditionally_to_default(var_names::NYXD_QUERY_LITE, NYXD_QUERY_LITE);
set_var_conditionally_to_default(var_names::NYXD_WS_LITE, NYXD_WS_LITE);
set_var_conditionally_to_default(
var_names::DIRECTORY_ATTESTATION_SOURCES,
&json_serialise(DIRECTORY_ATTESTATION_SOURCES),
);
}
59 changes: 55 additions & 4 deletions common/network-defaults/src/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,44 @@ pub struct ApiUrlConst<'a> {
pub front_hosts: Option<&'a [&'a str]>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DirectoryAttestationSource {
pub api_url: String,
pub identity_ed25519_bs58: String,
}

/// A nym-api capable of producing a signed directory-digest-snapshot attestation (see
/// `nym-directory-client`'s `AttestedTrustAnchor`), paired with the ed25519 identity
/// key it is expected to sign with.
#[derive(Copy, Clone, Debug, Serialize)]
pub struct DirectoryAttestationSourceConst<'a> {
pub api_url: &'a str,
pub identity_ed25519_bs58: &'a str,
}

impl From<DirectoryAttestationSourceConst<'_>> for DirectoryAttestationSource {
fn from(value: DirectoryAttestationSourceConst) -> Self {
DirectoryAttestationSource {
api_url: value.api_url.into(),
identity_ed25519_bs58: value.identity_ed25519_bs58.into(),
}
}
}

pub fn default_directory_attestation_sources() -> Vec<DirectoryAttestationSource> {
#[cfg(feature = "env")]
{
if crate::env_configured() {
return json_deserialise_env(var_names::DIRECTORY_ATTESTATION_SOURCES)
.unwrap_or_default();
}
}
mainnet::DIRECTORY_ATTESTATION_SOURCES
.iter()
.map(|i| (*i).into())
.collect()
}

impl From<ApiUrlConst<'_>> for ApiUrl {
fn from(value: ApiUrlConst) -> Self {
ApiUrl {
Expand Down Expand Up @@ -153,11 +191,11 @@ impl NymNetworkDetails {
}

let nym_api = var(var_names::NYM_API).expect("nym api not set");
let nym_api_urls = try_parse_api_urls(var_names::NYM_APIS).unwrap_or(vec![ApiUrl {
let nym_api_urls = json_deserialise_env(var_names::NYM_APIS).unwrap_or(vec![ApiUrl {
url: nym_api.clone(),
front_hosts: None,
}]);
let nym_vpn_api_urls = try_parse_api_urls(var_names::NYM_VPN_APIS);
let nym_vpn_api_urls = json_deserialise_env(var_names::NYM_VPN_APIS);

NymNetworkDetails::new_empty()
.with_network_name(var(var_names::NETWORK_NAME).expect("network name not set"))
Expand Down Expand Up @@ -465,10 +503,23 @@ impl NymNetworkDetails {
}

#[cfg(feature = "env")]
fn try_parse_api_urls(k: impl AsRef<OsStr>) -> Option<Vec<ApiUrl>> {
pub(crate) fn json_serialise<T>(data: &T) -> String
where
T: ?Sized + serde::Serialize,
{
serde_json::to_string(data)
.inspect_err(|e| tracing::warn!("failed to serialise data for env: {e}"))
.unwrap_or_default()
}

#[cfg(feature = "env")]
pub(crate) fn json_deserialise_env<T>(k: impl AsRef<OsStr>) -> Option<T>
where
T: serde::de::DeserializeOwned,
{
let raw = var(k).ok()?;
serde_json::from_str(&raw)
.inspect_err(|e| tracing::warn!("failed to parse api urls from env \"{raw:?}\": {e}"))
.inspect_err(|e| tracing::warn!("failed to parse data from env \"{raw:?}\": {e}"))
.ok()
}

Expand Down
1 change: 1 addition & 0 deletions common/network-defaults/src/var_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub const NYM_VPN_APIS: &str = "NYM_VPN_APIS";
pub const CLIENT_STATS_COLLECTION_PROVIDER: &str = "CLIENT_STATS_COLLECTION_PROVIDER";
pub const UPGRADE_MODE_ATTESTATION_URL: &str = "UPGRADE_MODE_ATTESTATION_URL";
pub const UPGRADE_MODE_ATTESTER_ED25519_BS58_PUBKEY: &str = "UPGRADE_MODE_ATTESTER_ED25519_PUBKEY";
pub const DIRECTORY_ATTESTATION_SOURCES: &str = "DIRECTORY_ATTESTATION_SOURCES";

pub const DKG_TIME_CONFIGURATION: &str = "DKG_TIME_CONFIGURATION";

Expand Down
8 changes: 6 additions & 2 deletions common/nym-directory-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,20 @@ publish = true
[dependencies]
thiserror = { workspace = true }
async-trait = { workspace = true }
blake3 = { workspace = true }
futures = { workspace = true }
ics23 = { workspace = true, features = ["host-functions"] }
prost = { workspace = true }
cosmrs = { workspace = true }
serde = { workspace = true }
rand = { workspace = true }
serde = { workspace = true, features = ["derive"] }
tracing = { workspace = true }
tokio = { workspace = true, features = ["sync"] }
tendermint-light-client = { workspace = true, optional = true }

nym-lthash = { workspace = true }
nym-lthash = { workspace = true, features = ["serde"] }
nym-crypto = { workspace = true, features = ["asymmetric"] }
nym-network-defaults = { workspace = true }
nym-validator-client = { workspace = true, features = ["http-client"] }
nym-directory-contract-common = { workspace = true }
nym-mixnet-contract-common = { workspace = true }
Expand Down
Loading
Loading