Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7727c6747c | |||
| 64ba86ed08 | |||
| d4f1b59d2b | |||
| 35ea6fd179 | |||
| 2cba42411f |
@@ -26,7 +26,6 @@ on:
|
||||
- "nym-api/**"
|
||||
- "nym-node/**"
|
||||
- "nym-outfox/**"
|
||||
- 'nym-data-observatory/**'
|
||||
- "nym-validator-rewarder/**"
|
||||
- "sdk/rust/nym-sdk/**"
|
||||
- "service-providers/**"
|
||||
@@ -97,7 +96,6 @@ jobs:
|
||||
target/release/nym-socks5-client
|
||||
target/release/nym-api
|
||||
target/release/nym-network-requester
|
||||
target/release/nym-data-observatory
|
||||
target/release/nym-cli
|
||||
target/release/nymvisor
|
||||
target/release/nym-node
|
||||
@@ -115,7 +113,6 @@ jobs:
|
||||
cp target/release/nym-socks5-client $OUTPUT_DIR
|
||||
cp target/release/nym-api $OUTPUT_DIR
|
||||
cp target/release/nym-network-requester $OUTPUT_DIR
|
||||
cp target/release/nym-data-observatory $OUTPUT_DIR
|
||||
cp target/release/nymvisor $OUTPUT_DIR
|
||||
cp target/release/nym-node $OUTPUT_DIR
|
||||
cp target/release/nym-cli $OUTPUT_DIR
|
||||
|
||||
@@ -16,7 +16,6 @@ on:
|
||||
- 'nym-api/**'
|
||||
- 'nym-node/**'
|
||||
- 'nym-outfox/**'
|
||||
- 'nym-data-observatory/**'
|
||||
- 'nym-validator-rewarder/**'
|
||||
- 'tools/**'
|
||||
- 'wasm/**'
|
||||
|
||||
@@ -22,4 +22,4 @@ jobs:
|
||||
with:
|
||||
log-level: warn
|
||||
command: check ${{ matrix.checks }}
|
||||
arguments: --all-features
|
||||
argument: --all-features
|
||||
|
||||
@@ -4,82 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2024.11-wedel] (2024-09-23)
|
||||
|
||||
- Backport #4894 to fix ci ([#4899])
|
||||
- Bugfix/ticketbook false double spending ([#4892])
|
||||
- fix: allow updating globally stored signatures ([#4891])
|
||||
- [DOCs/operators]: Document changelog for patch/2024.10-caramello ([#4886])
|
||||
- [DOCs/operators]: Post release docs updates ([#4874])
|
||||
- Bump defguard to github latest version ([#4872])
|
||||
- chore: removed completed queued mixnet migration ([#4865])
|
||||
- Disable push trigger and add missing paths in ci-build ([#4864])
|
||||
- Fix linux conditional in ci-build.yml ([#4863])
|
||||
- Remove golang workaround in ci-sdk-wasm ([#4858])
|
||||
- Revert runner for ci-docs ([#4855])
|
||||
- Move credential verification into common crate ([#4853])
|
||||
- Fix test failure in ipr request size ([#4844])
|
||||
- Start switching over jobs to arc-ubuntu-20.04 ([#4843])
|
||||
- Use ecash credential type for bandwidth value ([#4840])
|
||||
- Create nym-repo-setup debian package and nym-vpn meta package ([#4837])
|
||||
- Remove serde_crate named import ([#4832])
|
||||
- Run cargo autoinherit following last weeks dependabot updates ([#4831])
|
||||
- revamped ticketbook serialisation and exposed additional cli methods ([#4827])
|
||||
- Expose wireguard details on self described endpoint ([#4825])
|
||||
- Remove unused wireguard flag from SDK ([#4823])
|
||||
- Add `axum` server to `nym-api` ([#4803])
|
||||
- Run cargo-autoinherit for a few new crates ([#4801])
|
||||
- Update dependabot ([#4796])
|
||||
- Fix clippy for unwrap_or_default ([#4783])
|
||||
- Enable dependabot version upgrades for root rust workspace ([#4778])
|
||||
- Persist used wireguard private IPs ([#4771])
|
||||
- Avoid race on ip and registration structures ([#4766])
|
||||
- docs/hotfix ([#4765])
|
||||
- chore: remove repetitive words ([#4763])
|
||||
- Make gateway latency check generic ([#4759])
|
||||
- Remove duplicate stat count for retransmissions ([#4756])
|
||||
- Update peer refresh value ([#4754])
|
||||
- Remove deprecated mark_as_success and use new disarm ([#4751])
|
||||
- Add get_mixnodes_described to validator_client ([#4725])
|
||||
- New Network Monitor ([#4610])
|
||||
|
||||
[#4899]: https://github.com/nymtech/nym/pull/4899
|
||||
[#4892]: https://github.com/nymtech/nym/pull/4892
|
||||
[#4891]: https://github.com/nymtech/nym/pull/4891
|
||||
[#4886]: https://github.com/nymtech/nym/pull/4886
|
||||
[#4874]: https://github.com/nymtech/nym/pull/4874
|
||||
[#4872]: https://github.com/nymtech/nym/pull/4872
|
||||
[#4865]: https://github.com/nymtech/nym/pull/4865
|
||||
[#4864]: https://github.com/nymtech/nym/pull/4864
|
||||
[#4863]: https://github.com/nymtech/nym/pull/4863
|
||||
[#4858]: https://github.com/nymtech/nym/pull/4858
|
||||
[#4855]: https://github.com/nymtech/nym/pull/4855
|
||||
[#4853]: https://github.com/nymtech/nym/pull/4853
|
||||
[#4844]: https://github.com/nymtech/nym/pull/4844
|
||||
[#4843]: https://github.com/nymtech/nym/pull/4843
|
||||
[#4840]: https://github.com/nymtech/nym/pull/4840
|
||||
[#4837]: https://github.com/nymtech/nym/pull/4837
|
||||
[#4832]: https://github.com/nymtech/nym/pull/4832
|
||||
[#4831]: https://github.com/nymtech/nym/pull/4831
|
||||
[#4827]: https://github.com/nymtech/nym/pull/4827
|
||||
[#4825]: https://github.com/nymtech/nym/pull/4825
|
||||
[#4823]: https://github.com/nymtech/nym/pull/4823
|
||||
[#4803]: https://github.com/nymtech/nym/pull/4803
|
||||
[#4801]: https://github.com/nymtech/nym/pull/4801
|
||||
[#4796]: https://github.com/nymtech/nym/pull/4796
|
||||
[#4783]: https://github.com/nymtech/nym/pull/4783
|
||||
[#4778]: https://github.com/nymtech/nym/pull/4778
|
||||
[#4771]: https://github.com/nymtech/nym/pull/4771
|
||||
[#4766]: https://github.com/nymtech/nym/pull/4766
|
||||
[#4765]: https://github.com/nymtech/nym/pull/4765
|
||||
[#4763]: https://github.com/nymtech/nym/pull/4763
|
||||
[#4759]: https://github.com/nymtech/nym/pull/4759
|
||||
[#4756]: https://github.com/nymtech/nym/pull/4756
|
||||
[#4754]: https://github.com/nymtech/nym/pull/4754
|
||||
[#4751]: https://github.com/nymtech/nym/pull/4751
|
||||
[#4725]: https://github.com/nymtech/nym/pull/4725
|
||||
[#4610]: https://github.com/nymtech/nym/pull/4610
|
||||
|
||||
## [2024.10-caramello] (2024-09-10)
|
||||
|
||||
- Backport 4844 and 4845 ([#4857])
|
||||
|
||||
Generated
+74
-536
File diff suppressed because it is too large
Load Diff
+9
-15
@@ -103,9 +103,6 @@ members = [
|
||||
"mixnode",
|
||||
"sdk/lib/socks5-listener",
|
||||
"sdk/rust/nym-sdk",
|
||||
"sdk/ffi/shared",
|
||||
"sdk/ffi/go",
|
||||
"sdk/ffi/cpp",
|
||||
"service-providers/authenticator",
|
||||
"service-providers/common",
|
||||
"service-providers/ip-packet-router",
|
||||
@@ -114,13 +111,11 @@ members = [
|
||||
"nym-api",
|
||||
"nym-browser-extension/storage",
|
||||
"nym-api/nym-api-requests",
|
||||
"nym-data-observatory",
|
||||
"nym-node",
|
||||
"nym-node/nym-node-http-api",
|
||||
"nym-node/nym-node-requests",
|
||||
"nym-outfox",
|
||||
"nym-validator-rewarder",
|
||||
"tools/echo-server",
|
||||
"tools/internal/ssl-inject",
|
||||
# "tools/internal/sdk-version-bump",
|
||||
"tools/internal/testnet-manager",
|
||||
@@ -135,9 +130,6 @@ members = [
|
||||
"wasm/mix-fetch",
|
||||
"wasm/node-tester",
|
||||
"wasm/zknym-lib",
|
||||
"tools/internal/testnet-manager",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract",
|
||||
"tools/echo-server",
|
||||
]
|
||||
|
||||
default-members = [
|
||||
@@ -147,7 +139,6 @@ default-members = [
|
||||
"gateway",
|
||||
"mixnode",
|
||||
"nym-api",
|
||||
"nym-data-observatory",
|
||||
"nym-node",
|
||||
"nym-validator-rewarder",
|
||||
"service-providers/authenticator",
|
||||
@@ -162,6 +153,7 @@ exclude = [
|
||||
"nym-wallet",
|
||||
"nym-vpn/ui/src-tauri",
|
||||
"cpu-cycles",
|
||||
"sdk/ffi/cpp",
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
@@ -221,8 +213,7 @@ ctr = "0.9.1"
|
||||
cupid = "0.6.1"
|
||||
curve25519-dalek = "4.1"
|
||||
dashmap = "5.5.3"
|
||||
# We want https://github.com/DefGuard/wireguard-rs/pull/64 , but there's no crates.io release being pushed out anymore
|
||||
defguard_wireguard_rs = { git = "https://github.com/DefGuard/wireguard-rs.git", rev = "v0.4.7" }
|
||||
defguard_wireguard_rs = "0.4.2"
|
||||
digest = "0.10.7"
|
||||
dirs = "5.0"
|
||||
doc-comment = "0.3"
|
||||
@@ -365,10 +356,13 @@ cw-controllers = { version = "=1.1.0" }
|
||||
# cosmrs-related
|
||||
bip32 = { version = "0.5.2", default-features = false }
|
||||
|
||||
cosmrs = { version = "0.21.1" }
|
||||
tendermint = "0.40.0"
|
||||
tendermint-rpc = "0.40.0"
|
||||
prost = { version = "0.13", default-features = false }
|
||||
# temporarily using a fork again (yay.) because we need staking and slashing support (which are already on main but not released)
|
||||
# plus response message parsing (which is, as of the time of writing this message, waiting to get merged)
|
||||
#cosmrs = { path = "../cosmos-rust-fork/cosmos-rust/cosmrs" }
|
||||
cosmrs = { git = "https://github.com/cosmos/cosmos-rust", rev = "4b1332e6d8258ac845cef71589c8d362a669675a" } # unfortuntely we need a fork by yours truly to get the staking support
|
||||
tendermint = "0.37.0" # same version as used by cosmrs
|
||||
tendermint-rpc = "0.37.0" # same version as used by cosmrs
|
||||
prost = { version = "0.12", default-features = false }
|
||||
|
||||
# wasm-related dependencies
|
||||
gloo-utils = "0.2.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.41"
|
||||
version = "1.1.40"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.41"
|
||||
version = "1.1.40"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||
edition = "2021"
|
||||
|
||||
@@ -6,7 +6,6 @@ pub mod v2;
|
||||
|
||||
mod error;
|
||||
|
||||
pub use error::Error;
|
||||
pub use v2 as latest;
|
||||
|
||||
pub const CURRENT_VERSION: u8 = 2;
|
||||
|
||||
@@ -10,4 +10,4 @@ pub use registration::{ClientMac, GatewayClient, InitMessage, Nonce};
|
||||
#[cfg(feature = "verify")]
|
||||
pub use registration::HmacSha256;
|
||||
|
||||
pub const VERSION: u8 = 1;
|
||||
const VERSION: u8 = 1;
|
||||
|
||||
@@ -26,7 +26,7 @@ pub type HmacSha256 = Hmac<Sha256>;
|
||||
pub type Nonce = u64;
|
||||
pub type Taken = Option<SystemTime>;
|
||||
|
||||
pub const BANDWIDTH_CAP_PER_DAY: i64 = 1024 * 1024 * 1024; // 1 GB
|
||||
pub const BANDWIDTH_CAP_PER_DAY: u64 = 1024 * 1024 * 1024; // 1 GB
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct InitMessage {
|
||||
|
||||
@@ -62,113 +62,8 @@ impl From<v1::registration::GatewayClient> for v2::registration::GatewayClient {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::registration::GatewayClient> for v1::registration::GatewayClient {
|
||||
fn from(gw_client: v2::registration::GatewayClient) -> Self {
|
||||
Self {
|
||||
pub_key: gw_client.pub_key,
|
||||
private_ip: gw_client.private_ip,
|
||||
mac: gw_client.mac.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v1::registration::ClientMac> for v2::registration::ClientMac {
|
||||
fn from(mac: v1::registration::ClientMac) -> Self {
|
||||
Self::new(mac.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::registration::ClientMac> for v1::registration::ClientMac {
|
||||
fn from(mac: v2::registration::ClientMac) -> Self {
|
||||
Self::new(mac.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::response::AuthenticatorResponse> for v1::response::AuthenticatorResponse {
|
||||
fn from(authenticator_response: v2::response::AuthenticatorResponse) -> Self {
|
||||
Self {
|
||||
version: authenticator_response.protocol.version,
|
||||
data: authenticator_response.data.into(),
|
||||
reply_to: authenticator_response.reply_to,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::response::AuthenticatorResponseData> for v1::response::AuthenticatorResponseData {
|
||||
fn from(authenticator_response_data: v2::response::AuthenticatorResponseData) -> Self {
|
||||
match authenticator_response_data {
|
||||
v2::response::AuthenticatorResponseData::PendingRegistration(
|
||||
pending_registration_response,
|
||||
) => v1::response::AuthenticatorResponseData::PendingRegistration(
|
||||
pending_registration_response.into(),
|
||||
),
|
||||
v2::response::AuthenticatorResponseData::Registered(registered_response) => {
|
||||
v1::response::AuthenticatorResponseData::Registered(registered_response.into())
|
||||
}
|
||||
v2::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
remaining_bandwidth_response,
|
||||
) => v1::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
remaining_bandwidth_response.into(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::response::PendingRegistrationResponse> for v1::response::PendingRegistrationResponse {
|
||||
fn from(value: v2::response::PendingRegistrationResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply_to: value.reply_to,
|
||||
reply: value.reply.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::response::RegisteredResponse> for v1::response::RegisteredResponse {
|
||||
fn from(value: v2::response::RegisteredResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply_to: value.reply_to,
|
||||
reply: value.reply.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::response::RemainingBandwidthResponse> for v1::response::RemainingBandwidthResponse {
|
||||
fn from(value: v2::response::RemainingBandwidthResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply_to: value.reply_to,
|
||||
reply: value.reply.map(Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::registration::RegistrationData> for v1::registration::RegistrationData {
|
||||
fn from(value: v2::registration::RegistrationData) -> Self {
|
||||
Self {
|
||||
nonce: value.nonce,
|
||||
gateway_data: value.gateway_data.into(),
|
||||
wg_port: value.wg_port,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::registration::RegistredData> for v1::registration::RegistredData {
|
||||
fn from(value: v2::registration::RegistredData) -> Self {
|
||||
Self {
|
||||
pub_key: value.pub_key,
|
||||
private_ip: value.private_ip,
|
||||
wg_port: value.wg_port,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::registration::RemainingBandwidthData> for v1::registration::RemainingBandwidthData {
|
||||
fn from(value: v2::registration::RemainingBandwidthData) -> Self {
|
||||
Self {
|
||||
available_bandwidth: value.available_bandwidth as u64,
|
||||
suspended: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,4 +6,4 @@ pub mod registration;
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
|
||||
pub const VERSION: u8 = 2;
|
||||
const VERSION: u8 = 2;
|
||||
|
||||
@@ -66,7 +66,7 @@ pub struct RegistredData {
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct RemainingBandwidthData {
|
||||
pub available_bandwidth: i64,
|
||||
pub available_bandwidth: u64,
|
||||
}
|
||||
|
||||
/// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret.
|
||||
|
||||
@@ -2,37 +2,21 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use si_scale::helpers::bibytes2;
|
||||
use std::sync::atomic::{AtomicBool, AtomicI64, Ordering};
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
pub(crate) struct BandwidthClaimGuard {
|
||||
inner: Arc<ClientBandwidthInner>,
|
||||
}
|
||||
|
||||
impl Drop for BandwidthClaimGuard {
|
||||
fn drop(&mut self) {
|
||||
let old = self.inner.claiming_more.swap(false, Ordering::SeqCst);
|
||||
assert!(
|
||||
old,
|
||||
"critical failure: there were multiple BandwidthClaimGuard existing"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Default)]
|
||||
pub struct ClientBandwidth {
|
||||
inner: Arc<ClientBandwidthInner>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ClientBandwidthInner {
|
||||
/// the actual bandwidth amount (in bytes) available
|
||||
available: AtomicI64,
|
||||
|
||||
/// flag to indicate whether this client is currently in the process of claiming additional bandwidth
|
||||
claiming_more: AtomicBool,
|
||||
|
||||
/// defines the timestamp when the bandwidth information has been logged to the logs stream
|
||||
last_logged_ts: AtomicI64,
|
||||
|
||||
@@ -45,28 +29,11 @@ impl ClientBandwidth {
|
||||
ClientBandwidth {
|
||||
inner: Arc::new(ClientBandwidthInner {
|
||||
available: AtomicI64::new(0),
|
||||
claiming_more: AtomicBool::new(false),
|
||||
last_logged_ts: AtomicI64::new(0),
|
||||
last_updated_ts: AtomicI64::new(0),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn begin_bandwidth_claim(&self) -> Option<BandwidthClaimGuard> {
|
||||
if self
|
||||
.inner
|
||||
.claiming_more
|
||||
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
|
||||
.is_ok()
|
||||
{
|
||||
Some(BandwidthClaimGuard {
|
||||
inner: self.inner.clone(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn remaining(&self) -> i64 {
|
||||
self.inner.available.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
@@ -724,11 +724,6 @@ impl<C, St> GatewayClient<C, St> {
|
||||
return Err(GatewayClientError::NoBandwidthControllerAvailable);
|
||||
}
|
||||
|
||||
let Some(_claim_guard) = self.bandwidth.begin_bandwidth_claim() else {
|
||||
debug!("there's already an existing bandwidth claim ongoing");
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
warn!("Not enough bandwidth. Trying to get more bandwidth, this might take a while");
|
||||
if !self.cfg.bandwidth.require_tickets {
|
||||
info!("The client is running in disabled credentials mode - attempting to claim bandwidth without a credential");
|
||||
|
||||
@@ -20,13 +20,11 @@ nym-coconut-bandwidth-contract-common = { path = "../../cosmwasm-smart-contracts
|
||||
nym-ecash-contract-common = { path = "../../cosmwasm-smart-contracts/ecash-contract" }
|
||||
nym-multisig-contract-common = { path = "../../cosmwasm-smart-contracts/multisig-contract" }
|
||||
nym-group-contract-common = { path = "../../cosmwasm-smart-contracts/group-contract" }
|
||||
nym-serde-helpers = { path = "../../serde-helpers", features = ["hex", "base64"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
nym-http-api-client = { path = "../../../common/http-api-client" }
|
||||
thiserror = { workspace = true }
|
||||
log = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
url = { workspace = true, features = ["serde"] }
|
||||
tokio = { workspace = true, features = ["sync", "time"] }
|
||||
time = { workspace = true, features = ["formatting"] }
|
||||
|
||||
+14
-78
@@ -5,7 +5,7 @@ use crate::nyxd;
|
||||
use crate::nyxd::coin::Coin;
|
||||
use crate::nyxd::cosmwasm_client::helpers::{create_pagination, next_page_key};
|
||||
use crate::nyxd::cosmwasm_client::types::{
|
||||
Account, CodeDetails, Contract, ContractCodeId, Model, SequenceResponse, SimulateResponse,
|
||||
Account, CodeDetails, Contract, ContractCodeId, SequenceResponse, SimulateResponse,
|
||||
};
|
||||
use crate::nyxd::error::NyxdError;
|
||||
use crate::nyxd::Query;
|
||||
@@ -21,14 +21,15 @@ use cosmrs::proto::cosmos::tx::v1beta1::{
|
||||
SimulateRequest, SimulateResponse as ProtoSimulateResponse,
|
||||
};
|
||||
use cosmrs::proto::cosmwasm::wasm::v1::{
|
||||
QueryAllContractStateRequest, QueryAllContractStateResponse, QueryCodeRequest,
|
||||
QueryCodeResponse, QueryCodesRequest, QueryCodesResponse, QueryContractHistoryRequest,
|
||||
QueryContractHistoryResponse, QueryContractInfoRequest, QueryContractInfoResponse,
|
||||
QueryContractsByCodeRequest, QueryContractsByCodeResponse, QueryRawContractStateRequest,
|
||||
QueryRawContractStateResponse, QuerySmartContractStateRequest, QuerySmartContractStateResponse,
|
||||
QueryCodeRequest, QueryCodeResponse, QueryCodesRequest, QueryCodesResponse,
|
||||
QueryContractHistoryRequest, QueryContractHistoryResponse, QueryContractInfoRequest,
|
||||
QueryContractInfoResponse, QueryContractsByCodeRequest, QueryContractsByCodeResponse,
|
||||
QueryRawContractStateRequest, QueryRawContractStateResponse, QuerySmartContractStateRequest,
|
||||
QuerySmartContractStateResponse,
|
||||
};
|
||||
use cosmrs::tendermint::{block, chain, Hash};
|
||||
use cosmrs::{AccountId, Coin as CosmosCoin, Tx};
|
||||
use log::trace;
|
||||
use prost::Message;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -67,7 +68,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
Res: Message + Default,
|
||||
{
|
||||
if let Some(ref abci_path) = path {
|
||||
tracing::trace!("performing query on abci path {abci_path}")
|
||||
trace!("performing query on abci path {abci_path}")
|
||||
}
|
||||
let mut buf = Vec::with_capacity(req.encoded_len());
|
||||
req.encode(&mut buf)?;
|
||||
@@ -153,20 +154,13 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
let req = QueryAllBalancesRequest {
|
||||
address: address.to_string(),
|
||||
pagination,
|
||||
resolve_denom: false,
|
||||
};
|
||||
|
||||
let mut res = self
|
||||
.make_abci_query::<_, QueryAllBalancesResponse>(path.clone(), req)
|
||||
.await?;
|
||||
|
||||
let early_break = res.balances.is_empty();
|
||||
raw_balances.append(&mut res.balances);
|
||||
|
||||
if early_break {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
@@ -194,13 +188,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
.make_abci_query::<_, QueryTotalSupplyResponse>(path.clone(), req)
|
||||
.await?;
|
||||
|
||||
let early_break = res.supply.is_empty();
|
||||
supply.append(&mut res.supply);
|
||||
|
||||
if early_break {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
@@ -230,19 +218,17 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
|
||||
loop {
|
||||
let mut res = self
|
||||
.tx_search(query.clone(), false, page, per_page, Order::Ascending)
|
||||
.tx_search(query.clone(), false, page, 100, Order::Ascending)
|
||||
.await?;
|
||||
|
||||
results.append(&mut res.txs);
|
||||
// sanity check for if tendermint's maximum per_page was modified -
|
||||
// we don't want to accidentally be stuck in an infinite loop
|
||||
let early_break = res.total_count == 0 || res.txs.is_empty();
|
||||
results.append(&mut res.txs);
|
||||
|
||||
if early_break {
|
||||
if res.total_count == 0 || res.txs.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
if res.total_count > results.len() as u32 {
|
||||
if res.total_count >= per_page {
|
||||
page += 1
|
||||
} else {
|
||||
break;
|
||||
@@ -309,7 +295,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
|
||||
let start = Instant::now();
|
||||
loop {
|
||||
tracing::debug!(
|
||||
log::debug!(
|
||||
"Polling for result of including {} in a block...",
|
||||
broadcasted.hash
|
||||
);
|
||||
@@ -341,13 +327,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
.make_abci_query::<_, QueryCodesResponse>(path.clone(), req)
|
||||
.await?;
|
||||
|
||||
let early_break = res.code_infos.is_empty();
|
||||
raw_codes.append(&mut res.code_infos);
|
||||
|
||||
if early_break {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
@@ -392,13 +372,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
.make_abci_query::<_, QueryContractsByCodeResponse>(path.clone(), req)
|
||||
.await?;
|
||||
|
||||
let early_break = res.contracts.is_empty();
|
||||
raw_contracts.append(&mut res.contracts);
|
||||
|
||||
if early_break {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
@@ -454,13 +428,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
.make_abci_query::<_, QueryContractHistoryResponse>(path.clone(), req)
|
||||
.await?;
|
||||
|
||||
let early_break = res.entries.is_empty();
|
||||
raw_entries.append(&mut res.entries);
|
||||
|
||||
if early_break {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
@@ -474,38 +442,6 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
.collect::<Result<_, _>>()?)
|
||||
}
|
||||
|
||||
async fn query_all_contract_state(&self, address: &AccountId) -> Result<Vec<Model>, NyxdError> {
|
||||
let path = Some("/cosmwasm.wasm.v1.Query/AllContractState".to_owned());
|
||||
|
||||
let mut models = Vec::new();
|
||||
let mut pagination = None;
|
||||
|
||||
loop {
|
||||
let req = QueryAllContractStateRequest {
|
||||
address: address.to_string(),
|
||||
pagination,
|
||||
};
|
||||
|
||||
let mut res = self
|
||||
.make_abci_query::<_, QueryAllContractStateResponse>(path.clone(), req)
|
||||
.await?;
|
||||
|
||||
let empty_response = res.models.is_empty();
|
||||
models.append(&mut res.models);
|
||||
|
||||
if empty_response {
|
||||
break;
|
||||
}
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(models.into_iter().map(Into::into).collect())
|
||||
}
|
||||
|
||||
async fn query_contract_raw(
|
||||
&self,
|
||||
address: &AccountId,
|
||||
@@ -552,7 +488,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
.make_abci_query::<_, QuerySmartContractStateResponse>(path, req)
|
||||
.await?;
|
||||
|
||||
tracing::trace!("raw query response: {}", String::from_utf8_lossy(&res.data));
|
||||
trace!("raw query response: {}", String::from_utf8_lossy(&res.data));
|
||||
Ok(serde_json::from_slice(&res.data)?)
|
||||
}
|
||||
|
||||
|
||||
@@ -27,34 +27,13 @@ use cosmrs::vesting::{
|
||||
};
|
||||
use cosmrs::{AccountId, Any, Coin as CosmosCoin};
|
||||
use prost::Message;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::Serialize;
|
||||
|
||||
pub use cosmrs::abci::GasInfo;
|
||||
pub use cosmrs::abci::MsgResponse;
|
||||
|
||||
pub type ContractCodeId = u64;
|
||||
|
||||
// yet another thing to put in cosmrs
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Model {
|
||||
#[serde(with = "nym_serde_helpers::hex")]
|
||||
pub key: Vec<u8>,
|
||||
|
||||
#[serde(with = "nym_serde_helpers::base64")]
|
||||
pub value: Vec<u8>,
|
||||
}
|
||||
|
||||
// follow the cosmwasm serialisation format, i.e. hex for key and base64 for value
|
||||
|
||||
impl From<cosmrs::proto::cosmwasm::wasm::v1::Model> for Model {
|
||||
fn from(model: cosmrs::proto::cosmwasm::wasm::v1::Model) -> Self {
|
||||
Model {
|
||||
key: model.key,
|
||||
value: model.value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct EmptyMsg {}
|
||||
|
||||
|
||||
@@ -4,11 +4,9 @@
|
||||
use crate::rpc::TendermintRpcClient;
|
||||
use async_trait::async_trait;
|
||||
use base64::Engine;
|
||||
use cosmrs::tendermint;
|
||||
use cosmrs::tendermint::{block::Height, evidence::Evidence, Hash};
|
||||
use reqwest::header::HeaderMap;
|
||||
use reqwest::{header, RequestBuilder};
|
||||
use tendermint_rpc::dialect::{v0_34, v0_37, v0_38, LatestDialect};
|
||||
use tendermint_rpc::{
|
||||
client::CompatMode,
|
||||
dialect::{self, Dialect},
|
||||
@@ -23,21 +21,8 @@ macro_rules! perform_with_compat {
|
||||
($self:expr, $request:expr) => {{
|
||||
let request = $request;
|
||||
match $self.compat {
|
||||
CompatMode::V0_38 => {
|
||||
$self
|
||||
.perform_request_with_dialect(request, dialect::v0_38::Dialect)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => {
|
||||
$self
|
||||
.perform_request_with_dialect(request, dialect::v0_37::Dialect)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_34 => {
|
||||
$self
|
||||
.perform_request_with_dialect(request, dialect::v0_34::Dialect)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => $self.perform_v0_37(request).await,
|
||||
CompatMode::V0_34 => $self.perform_v0_34(request).await,
|
||||
}
|
||||
}};
|
||||
}
|
||||
@@ -85,11 +70,7 @@ impl ReqwestRpcClient {
|
||||
.headers(headers)
|
||||
}
|
||||
|
||||
async fn perform_request_with_dialect<R, S>(
|
||||
&self,
|
||||
request: R,
|
||||
_dialect: S,
|
||||
) -> Result<R::Output, Error>
|
||||
async fn perform_request<R, S>(&self, request: R) -> Result<R::Output, Error>
|
||||
where
|
||||
R: SimpleRequest<S>,
|
||||
S: Dialect,
|
||||
@@ -100,25 +81,26 @@ impl ReqwestRpcClient {
|
||||
.send()
|
||||
.await
|
||||
.map_err(TendermintRpcErrorMap::into_rpc_err)?;
|
||||
let response_status = response.status();
|
||||
let bytes = response
|
||||
.bytes()
|
||||
.await
|
||||
.map_err(TendermintRpcErrorMap::into_rpc_err)?;
|
||||
|
||||
// Successful JSON-RPC requests are expected to return a 200 OK HTTP status.
|
||||
// Otherwise, this means that the HTTP request failed as a whole,
|
||||
// as opposed to the JSON-RPC request returning an error,
|
||||
// and we cannot expect the response body to be a valid JSON-RPC response.
|
||||
if response_status != reqwest::StatusCode::OK {
|
||||
// hehe, that's so nasty but we have to somehow convert between different versions of the same lib
|
||||
return Err(Error::http_request_failed(
|
||||
response_status.as_u16().try_into().unwrap(),
|
||||
));
|
||||
}
|
||||
|
||||
R::Response::from_string(bytes).map(Into::into)
|
||||
}
|
||||
|
||||
async fn perform_v0_34<R>(&self, request: R) -> Result<R::Output, Error>
|
||||
where
|
||||
R: SimpleRequest<dialect::v0_34::Dialect>,
|
||||
{
|
||||
self.perform_request(request).await
|
||||
}
|
||||
|
||||
async fn perform_v0_37<R>(&self, request: R) -> Result<R::Output, Error>
|
||||
where
|
||||
R: SimpleRequest<dialect::v0_37::Dialect>,
|
||||
{
|
||||
self.perform_request(request).await
|
||||
}
|
||||
}
|
||||
|
||||
trait TendermintRpcErrorMap {
|
||||
@@ -138,50 +120,18 @@ impl TendermintRpcClient for ReqwestRpcClient {
|
||||
where
|
||||
R: SimpleRequest,
|
||||
{
|
||||
self.perform_request_with_dialect(request, LatestDialect)
|
||||
.await
|
||||
self.perform_request(request).await
|
||||
}
|
||||
|
||||
async fn block<H>(&self, height: H) -> Result<endpoint::block::Response, Error>
|
||||
async fn block_results<H>(&self, height: H) -> Result<block_results::Response, Error>
|
||||
where
|
||||
H: Into<Height> + Send,
|
||||
{
|
||||
perform_with_compat!(self, endpoint::block::Request::new(height.into()))
|
||||
perform_with_compat!(self, block_results::Request::new(height.into()))
|
||||
}
|
||||
|
||||
async fn block_by_hash(
|
||||
&self,
|
||||
hash: tendermint::Hash,
|
||||
) -> Result<endpoint::block_by_hash::Response, Error> {
|
||||
perform_with_compat!(self, endpoint::block_by_hash::Request::new(hash))
|
||||
}
|
||||
|
||||
async fn latest_block(&self) -> Result<endpoint::block::Response, Error> {
|
||||
perform_with_compat!(self, endpoint::block::Request::default())
|
||||
}
|
||||
|
||||
async fn block_results<H>(&self, height: H) -> Result<endpoint::block_results::Response, Error>
|
||||
where
|
||||
H: Into<Height> + Send,
|
||||
{
|
||||
perform_with_compat!(self, endpoint::block_results::Request::new(height.into()))
|
||||
}
|
||||
|
||||
async fn latest_block_results(&self) -> Result<endpoint::block_results::Response, Error> {
|
||||
perform_with_compat!(self, endpoint::block_results::Request::default())
|
||||
}
|
||||
|
||||
async fn block_search(
|
||||
&self,
|
||||
query: Query,
|
||||
page: u32,
|
||||
per_page: u8,
|
||||
order: Order,
|
||||
) -> Result<endpoint::block_search::Response, Error> {
|
||||
perform_with_compat!(
|
||||
self,
|
||||
endpoint::block_search::Request::new(query, page, per_page, order)
|
||||
)
|
||||
async fn latest_block_results(&self) -> Result<block_results::Response, Error> {
|
||||
perform_with_compat!(self, block_results::Request::default())
|
||||
}
|
||||
|
||||
async fn header<H>(&self, height: H) -> Result<endpoint::header::Response, Error>
|
||||
@@ -190,26 +140,11 @@ impl TendermintRpcClient for ReqwestRpcClient {
|
||||
{
|
||||
let height = height.into();
|
||||
match self.compat {
|
||||
CompatMode::V0_38 => {
|
||||
self.perform_request_with_dialect(
|
||||
endpoint::header::Request::new(height),
|
||||
v0_38::Dialect,
|
||||
)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => {
|
||||
self.perform_request_with_dialect(
|
||||
endpoint::header::Request::new(height),
|
||||
v0_37::Dialect,
|
||||
)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => self.perform(endpoint::header::Request::new(height)).await,
|
||||
CompatMode::V0_34 => {
|
||||
// Back-fill with a request to /block endpoint and
|
||||
// taking just the header from the response.
|
||||
let resp = self
|
||||
.perform_request_with_dialect(block::Request::new(height), v0_34::Dialect)
|
||||
.await?;
|
||||
let resp = self.perform_v0_34(block::Request::new(height)).await?;
|
||||
Ok(resp.into())
|
||||
}
|
||||
}
|
||||
@@ -217,25 +152,12 @@ impl TendermintRpcClient for ReqwestRpcClient {
|
||||
|
||||
async fn header_by_hash(&self, hash: Hash) -> Result<header_by_hash::Response, Error> {
|
||||
match self.compat {
|
||||
CompatMode::V0_38 => {
|
||||
self.perform_request_with_dialect(
|
||||
header_by_hash::Request::new(hash),
|
||||
v0_38::Dialect,
|
||||
)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => {
|
||||
self.perform_request_with_dialect(
|
||||
header_by_hash::Request::new(hash),
|
||||
v0_37::Dialect,
|
||||
)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => self.perform(header_by_hash::Request::new(hash)).await,
|
||||
CompatMode::V0_34 => {
|
||||
// Back-fill with a request to /block_by_hash endpoint and
|
||||
// taking just the header from the response.
|
||||
let resp = self
|
||||
.perform_request_with_dialect(block_by_hash::Request::new(hash), v0_34::Dialect)
|
||||
.perform_v0_34(block_by_hash::Request::new(hash))
|
||||
.await?;
|
||||
Ok(resp.into())
|
||||
}
|
||||
@@ -245,18 +167,8 @@ impl TendermintRpcClient for ReqwestRpcClient {
|
||||
/// `/broadcast_evidence`: broadcast an evidence.
|
||||
async fn broadcast_evidence(&self, e: Evidence) -> Result<evidence::Response, Error> {
|
||||
match self.compat {
|
||||
CompatMode::V0_38 => {
|
||||
self.perform_request_with_dialect(evidence::Request::new(e), v0_38::Dialect)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => {
|
||||
self.perform_request_with_dialect(evidence::Request::new(e), v0_37::Dialect)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_34 => {
|
||||
self.perform_request_with_dialect(evidence::Request::new(e), v0_34::Dialect)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => self.perform(evidence::Request::new(e)).await,
|
||||
CompatMode::V0_34 => self.perform_v0_34(evidence::Request::new(e)).await,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -171,20 +171,10 @@ impl SqliteEcashTicketbookManager {
|
||||
data: &[u8],
|
||||
) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
INSERT OR IGNORE INTO master_verification_key(epoch_id, serialised_key, serialization_revision) VALUES (?, ?, ?);
|
||||
UPDATE master_verification_key
|
||||
SET
|
||||
serialised_key = ?,
|
||||
serialization_revision = ?
|
||||
WHERE epoch_id = ?
|
||||
"#,
|
||||
"INSERT INTO master_verification_key(epoch_id, serialised_key, serialization_revision) VALUES (?, ?, ?)",
|
||||
epoch_id,
|
||||
data,
|
||||
serialisation_revision,
|
||||
data,
|
||||
serialisation_revision,
|
||||
epoch_id
|
||||
serialisation_revision
|
||||
)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
@@ -214,20 +204,10 @@ impl SqliteEcashTicketbookManager {
|
||||
data: &[u8],
|
||||
) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
INSERT OR IGNORE INTO coin_indices_signatures(epoch_id, serialised_signatures, serialization_revision) VALUES (?, ?, ?);
|
||||
UPDATE coin_indices_signatures
|
||||
SET
|
||||
serialised_signatures = ?,
|
||||
serialization_revision = ?
|
||||
WHERE epoch_id = ?
|
||||
"#,
|
||||
"INSERT INTO coin_indices_signatures(epoch_id, serialised_signatures, serialization_revision) VALUES (?, ?, ?)",
|
||||
epoch_id,
|
||||
data,
|
||||
serialisation_revision,
|
||||
data,
|
||||
serialisation_revision,
|
||||
epoch_id,
|
||||
serialisation_revision
|
||||
)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
@@ -260,21 +240,13 @@ impl SqliteEcashTicketbookManager {
|
||||
) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
INSERT OR IGNORE INTO expiration_date_signatures(expiration_date, epoch_id, serialised_signatures, serialization_revision)
|
||||
VALUES (?, ?, ?, ?);
|
||||
UPDATE expiration_date_signatures
|
||||
SET
|
||||
serialised_signatures = ?,
|
||||
serialization_revision = ?
|
||||
WHERE expiration_date = ?
|
||||
INSERT INTO expiration_date_signatures(expiration_date, epoch_id, serialised_signatures, serialization_revision)
|
||||
VALUES (?, ?, ?, ?)
|
||||
"#,
|
||||
expiration_date,
|
||||
epoch_id,
|
||||
data,
|
||||
serialisation_revision,
|
||||
data,
|
||||
serialisation_revision,
|
||||
expiration_date
|
||||
serialisation_revision
|
||||
)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::*;
|
||||
use crate::BandwidthFlushingBehaviourConfig;
|
||||
use crate::ClientBandwidth;
|
||||
use nym_credentials::ecash::utils::ecash_today;
|
||||
use nym_credentials_interface::Bandwidth;
|
||||
use nym_credentials_interface::{AvailableBandwidth, Bandwidth};
|
||||
use nym_gateway_requests::ServerResponse;
|
||||
use nym_gateway_storage::Storage;
|
||||
use si_scale::helpers::bibytes2;
|
||||
use time::OffsetDateTime;
|
||||
use tracing::*;
|
||||
|
||||
use crate::error::*;
|
||||
use crate::BandwidthFlushingBehaviourConfig;
|
||||
use crate::ClientBandwidth;
|
||||
|
||||
const FREE_TESTNET_BANDWIDTH_VALUE: Bandwidth = Bandwidth::new_unchecked(64 * 1024 * 1024 * 1024); // 64GB
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -40,13 +41,13 @@ impl<S: Storage + Clone + 'static> BandwidthStorageManager<S> {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn available_bandwidth(&self) -> i64 {
|
||||
self.client_bandwidth.available().await
|
||||
pub fn available_bandwidth(&self) -> AvailableBandwidth {
|
||||
self.client_bandwidth.bandwidth
|
||||
}
|
||||
|
||||
async fn sync_expiration(&mut self) -> Result<()> {
|
||||
self.storage
|
||||
.set_expiration(self.client_id, self.client_bandwidth.expiration().await)
|
||||
.set_expiration(self.client_id, self.client_bandwidth.bandwidth.expiration)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -60,17 +61,17 @@ impl<S: Storage + Clone + 'static> BandwidthStorageManager<S> {
|
||||
|
||||
self.increase_bandwidth(FREE_TESTNET_BANDWIDTH_VALUE, ecash_today())
|
||||
.await?;
|
||||
let available_total = self.client_bandwidth.available().await;
|
||||
let available_total = self.client_bandwidth.bandwidth.bytes;
|
||||
|
||||
Ok(ServerResponse::Bandwidth { available_total })
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn try_use_bandwidth(&mut self, required_bandwidth: i64) -> Result<i64> {
|
||||
if self.client_bandwidth.expired().await {
|
||||
if self.client_bandwidth.bandwidth.expired() {
|
||||
self.expire_bandwidth().await?;
|
||||
}
|
||||
let available_bandwidth = self.client_bandwidth.available().await;
|
||||
let available_bandwidth = self.client_bandwidth.bandwidth.bytes;
|
||||
|
||||
if available_bandwidth < required_bandwidth {
|
||||
return Err(Error::OutOfBandwidth {
|
||||
@@ -89,7 +90,8 @@ impl<S: Storage + Clone + 'static> BandwidthStorageManager<S> {
|
||||
|
||||
async fn expire_bandwidth(&mut self) -> Result<()> {
|
||||
self.storage.reset_bandwidth(self.client_id).await?;
|
||||
self.client_bandwidth.expire_bandwidth().await;
|
||||
self.client_bandwidth.bandwidth = Default::default();
|
||||
self.client_bandwidth.update_sync_data();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -99,31 +101,31 @@ impl<S: Storage + Clone + 'static> BandwidthStorageManager<S> {
|
||||
///
|
||||
/// * `amount`: amount to decrease the available bandwidth by.
|
||||
async fn consume_bandwidth(&mut self, amount: i64) -> Result<()> {
|
||||
self.client_bandwidth.decrease_bandwidth(amount).await;
|
||||
self.client_bandwidth.bandwidth.bytes -= amount;
|
||||
self.client_bandwidth.bytes_delta_since_sync -= amount;
|
||||
|
||||
// since we're going to be operating on a fair use policy anyway, even if we crash and let extra few packets
|
||||
// through, that's completely fine
|
||||
if self.client_bandwidth.should_sync(self.bandwidth_cfg).await {
|
||||
self.sync_storage_bandwidth().await?;
|
||||
if self.client_bandwidth.should_sync(self.bandwidth_cfg) {
|
||||
self.sync_bandwidth().await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
async fn sync_storage_bandwidth(&mut self) -> Result<()> {
|
||||
async fn sync_bandwidth(&mut self) -> Result<()> {
|
||||
trace!("syncing client bandwidth with the underlying storage");
|
||||
let updated = self
|
||||
.storage
|
||||
.increase_bandwidth(
|
||||
self.client_id,
|
||||
self.client_bandwidth.delta_since_sync().await,
|
||||
)
|
||||
.increase_bandwidth(self.client_id, self.client_bandwidth.bytes_delta_since_sync)
|
||||
.await?;
|
||||
|
||||
self.client_bandwidth
|
||||
.resync_bandwidth_with_storage(updated)
|
||||
.await;
|
||||
trace!(updated);
|
||||
|
||||
self.client_bandwidth.bandwidth.bytes = updated;
|
||||
|
||||
self.client_bandwidth.update_sync_data();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -138,14 +140,13 @@ impl<S: Storage + Clone + 'static> BandwidthStorageManager<S> {
|
||||
bandwidth: Bandwidth,
|
||||
expiration: OffsetDateTime,
|
||||
) -> Result<()> {
|
||||
self.client_bandwidth
|
||||
.increase_bandwidth(bandwidth.value() as i64, expiration)
|
||||
.await;
|
||||
self.client_bandwidth.bandwidth.bytes += bandwidth.value() as i64;
|
||||
self.client_bandwidth.bytes_delta_since_sync += bandwidth.value() as i64;
|
||||
self.client_bandwidth.bandwidth.expiration = expiration;
|
||||
|
||||
// any increases to bandwidth should get flushed immediately
|
||||
// (we don't want to accidentally miss somebody claiming a gigabyte voucher)
|
||||
self.sync_expiration().await?;
|
||||
self.sync_storage_bandwidth().await?;
|
||||
Ok(())
|
||||
self.sync_bandwidth().await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_credentials::ecash::utils::ecash_today;
|
||||
use nym_credentials_interface::AvailableBandwidth;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use nym_credentials_interface::AvailableBandwidth;
|
||||
use time::OffsetDateTime;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
const DEFAULT_CLIENT_BANDWIDTH_MAX_FLUSHING_RATE: Duration = Duration::from_millis(5);
|
||||
const DEFAULT_CLIENT_BANDWIDTH_MAX_DELTA_FLUSHING_AMOUNT: i64 = 512 * 1024; // 512kB
|
||||
@@ -30,15 +28,10 @@ impl Default for BandwidthFlushingBehaviourConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ClientBandwidth {
|
||||
inner: Arc<RwLock<ClientBandwidthInner>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ClientBandwidthInner {
|
||||
pub(crate) bandwidth: AvailableBandwidth,
|
||||
pub(crate) last_synced: OffsetDateTime,
|
||||
pub(crate) last_flushed: OffsetDateTime,
|
||||
|
||||
/// the number of bytes the client had during the last sync.
|
||||
/// it is used to determine whether the current value should be synced with the storage
|
||||
@@ -50,74 +43,28 @@ struct ClientBandwidthInner {
|
||||
impl ClientBandwidth {
|
||||
pub fn new(bandwidth: AvailableBandwidth) -> ClientBandwidth {
|
||||
ClientBandwidth {
|
||||
inner: Arc::new(RwLock::new(ClientBandwidthInner {
|
||||
bandwidth,
|
||||
last_synced: OffsetDateTime::now_utc(),
|
||||
bytes_at_last_sync: bandwidth.bytes,
|
||||
bytes_delta_since_sync: 0,
|
||||
})),
|
||||
bandwidth,
|
||||
last_flushed: OffsetDateTime::now_utc(),
|
||||
bytes_at_last_sync: bandwidth.bytes,
|
||||
bytes_delta_since_sync: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn should_sync(&self, cfg: BandwidthFlushingBehaviourConfig) -> bool {
|
||||
let guard = self.inner.read().await;
|
||||
|
||||
if guard.bytes_delta_since_sync.abs() >= cfg.client_bandwidth_max_delta_flushing_amount {
|
||||
pub(crate) fn should_sync(&self, cfg: BandwidthFlushingBehaviourConfig) -> bool {
|
||||
if self.bytes_delta_since_sync.abs() >= cfg.client_bandwidth_max_delta_flushing_amount {
|
||||
return true;
|
||||
}
|
||||
|
||||
if guard.last_synced + cfg.client_bandwidth_max_flushing_rate < OffsetDateTime::now_utc() {
|
||||
if self.last_flushed + cfg.client_bandwidth_max_flushing_rate < OffsetDateTime::now_utc() {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub(crate) async fn available(&self) -> i64 {
|
||||
self.inner.read().await.bandwidth.bytes
|
||||
}
|
||||
|
||||
pub(crate) async fn delta_since_sync(&self) -> i64 {
|
||||
self.inner.read().await.bytes_delta_since_sync
|
||||
}
|
||||
pub(crate) async fn expiration(&self) -> OffsetDateTime {
|
||||
self.inner.read().await.bandwidth.expiration
|
||||
}
|
||||
|
||||
pub(crate) async fn expired(&self) -> bool {
|
||||
self.expiration().await < ecash_today()
|
||||
}
|
||||
|
||||
pub(crate) async fn decrease_bandwidth(&self, decrease: i64) {
|
||||
let mut guard = self.inner.write().await;
|
||||
|
||||
guard.bandwidth.bytes -= decrease;
|
||||
guard.bytes_delta_since_sync -= decrease;
|
||||
}
|
||||
|
||||
pub(crate) async fn increase_bandwidth(&self, increase: i64, new_expiration: OffsetDateTime) {
|
||||
let mut guard = self.inner.write().await;
|
||||
|
||||
guard.bandwidth.bytes += increase;
|
||||
guard.bandwidth.expiration = new_expiration;
|
||||
guard.bytes_delta_since_sync += increase;
|
||||
}
|
||||
|
||||
pub(crate) async fn expire_bandwidth(&self) {
|
||||
let mut guard = self.inner.write().await;
|
||||
|
||||
guard.bandwidth = AvailableBandwidth::default();
|
||||
guard.last_synced = OffsetDateTime::now_utc();
|
||||
guard.bytes_at_last_sync = 0;
|
||||
guard.bytes_delta_since_sync = 0;
|
||||
}
|
||||
|
||||
pub(crate) async fn resync_bandwidth_with_storage(&self, stored: i64) {
|
||||
let mut guard = self.inner.write().await;
|
||||
|
||||
guard.bandwidth.bytes = stored;
|
||||
guard.bytes_at_last_sync = stored;
|
||||
guard.bytes_delta_since_sync = 0;
|
||||
guard.last_synced = OffsetDateTime::now_utc();
|
||||
pub(crate) fn update_sync_data(&mut self) {
|
||||
self.last_flushed = OffsetDateTime::now_utc();
|
||||
self.bytes_at_last_sync = self.bandwidth.bytes;
|
||||
self.bytes_delta_since_sync = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ impl<S: Storage + Clone + 'static> CredentialVerifier<S> {
|
||||
Ok(self
|
||||
.bandwidth_storage_manager
|
||||
.client_bandwidth
|
||||
.available()
|
||||
.await)
|
||||
.bandwidth
|
||||
.bytes)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,3 +67,19 @@ impl From<v7::response::InfoLevel> for v6::response::InfoLevel {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v7::response::UnrequestedDisconnectReason> for v6::response::UnrequestedDisconnectReason {
|
||||
fn from(reason: v7::response::UnrequestedDisconnectReason) -> Self {
|
||||
match reason {
|
||||
v7::response::UnrequestedDisconnectReason::ClientMixnetTrafficTimeout => {
|
||||
v6::response::UnrequestedDisconnectReason::ClientMixnetTrafficTimeout
|
||||
}
|
||||
v7::response::UnrequestedDisconnectReason::ClientTunTrafficTimeout => {
|
||||
v6::response::UnrequestedDisconnectReason::ClientTunTrafficTimeout
|
||||
}
|
||||
v7::response::UnrequestedDisconnectReason::Other(reason) => {
|
||||
v6::response::UnrequestedDisconnectReason::Other(reason)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,4 +2,4 @@ pub mod conversion;
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
|
||||
pub const VERSION: u8 = 6;
|
||||
const VERSION: u8 = 6;
|
||||
|
||||
@@ -3,4 +3,4 @@ pub mod request;
|
||||
pub mod response;
|
||||
pub mod signature;
|
||||
|
||||
pub const VERSION: u8 = 7;
|
||||
const VERSION: u8 = 7;
|
||||
|
||||
@@ -32,7 +32,7 @@ pub const NYXD_URL: &str = "https://rpc.nymtech.net";
|
||||
pub const NYM_API: &str = "https://validator.nymtech.net/api/";
|
||||
pub const NYXD_WS: &str = "wss://rpc.nymtech.net/websocket";
|
||||
pub const EXPLORER_API: &str = "https://explorer.nymtech.net/api/";
|
||||
pub const NYM_VPN_API: &str = "https://nymvpn.com/api/";
|
||||
pub const NYM_VPN_API: &str = "https://nymvpn.net/api/";
|
||||
|
||||
// I'm making clippy mad on purpose, because that url HAS TO be updated and deployed before merging
|
||||
pub const EXIT_POLICY_URL: &str =
|
||||
|
||||
@@ -10,7 +10,6 @@ use crate::rpc_client::RpcClient;
|
||||
use crate::storage::{persist_block, ScraperStorage};
|
||||
use crate::PruningOptions;
|
||||
use futures::StreamExt;
|
||||
use std::cmp::max;
|
||||
use std::collections::{BTreeMap, HashSet, VecDeque};
|
||||
use std::ops::{Add, Range};
|
||||
use std::sync::Arc;
|
||||
@@ -100,15 +99,7 @@ impl BlockProcessor {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_pruning(mut self, pruning_options: PruningOptions) -> Self {
|
||||
self.pruning_options = pruning_options;
|
||||
self
|
||||
}
|
||||
|
||||
pub(super) async fn process_block(
|
||||
&mut self,
|
||||
block: BlockToProcess,
|
||||
) -> Result<(), ScraperError> {
|
||||
async fn process_block(&mut self, block: BlockToProcess) -> Result<(), ScraperError> {
|
||||
info!("processing block at height {}", block.height);
|
||||
|
||||
let full_info = self.rpc_client.try_get_full_details(block).await?;
|
||||
@@ -178,10 +169,6 @@ impl BlockProcessor {
|
||||
self.msg_modules = modules;
|
||||
}
|
||||
|
||||
pub(super) fn last_process_height(&self) -> u32 {
|
||||
self.last_processed_height
|
||||
}
|
||||
|
||||
async fn maybe_request_missing_blocks(&mut self) -> Result<(), ScraperError> {
|
||||
// we're still processing, so we're good
|
||||
if self.last_processed_at.elapsed() < MAX_MISSING_BLOCKS_DELAY {
|
||||
@@ -267,7 +254,6 @@ impl BlockProcessor {
|
||||
}
|
||||
|
||||
if to_prune == 0 {
|
||||
self.last_pruned_height = self.last_processed_height;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -367,14 +353,7 @@ impl BlockProcessor {
|
||||
self.maybe_prune_storage().await?;
|
||||
|
||||
let latest_block = self.rpc_client.current_block_height().await? as u32;
|
||||
|
||||
if latest_block > self.last_processed_height && self.last_processed_height != 0 {
|
||||
// in case we were offline for a while,
|
||||
// make sure we don't request blocks we'd have to prune anyway
|
||||
let keep_recent = self.pruning_options.strategy_keep_recent();
|
||||
let last_to_keep = latest_block - keep_recent;
|
||||
self.last_processed_height = max(self.last_processed_height, last_to_keep);
|
||||
|
||||
let request_range = self.last_processed_height + 1..latest_block + 1;
|
||||
info!("we need to request {request_range:?} to resync");
|
||||
self.request_missing_blocks(request_range).await?;
|
||||
|
||||
@@ -84,7 +84,13 @@ impl TryFrom<Event> for BlockToProcess {
|
||||
|
||||
// TODO: we're losing `result_begin_block` and `result_end_block` here but maybe that's fine?
|
||||
let maybe_block = match event.data {
|
||||
EventData::NewBlock { block, .. } => block,
|
||||
// we don't care about `NewBlock` until CometBFT 0.38, i.e. until we upgrade to wasmd 0.50
|
||||
EventData::NewBlock { .. } => {
|
||||
return Err(ScraperError::InvalidSubscriptionEvent {
|
||||
query,
|
||||
kind: "NewBlock".to_string(),
|
||||
})
|
||||
}
|
||||
EventData::LegacyNewBlock { block, .. } => block,
|
||||
EventData::Tx { .. } => {
|
||||
return Err(ScraperError::InvalidSubscriptionEvent {
|
||||
|
||||
@@ -16,7 +16,7 @@ pub enum ScraperError {
|
||||
#[error("failed to perform startup SQL migration: {0}")]
|
||||
StartupMigrationFailure(#[from] sqlx::migrate::MigrateError),
|
||||
|
||||
#[error("the block scraper is already running")]
|
||||
#[error("can't add any modules to the scraper as it's already running")]
|
||||
ScraperAlreadyRunning,
|
||||
|
||||
#[error("failed to establish websocket connection to {url}: {source}")]
|
||||
|
||||
@@ -1,25 +1,21 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::block_processor::types::BlockToProcess;
|
||||
use crate::block_processor::BlockProcessor;
|
||||
use crate::block_requester::{BlockRequest, BlockRequester};
|
||||
use crate::block_requester::BlockRequester;
|
||||
use crate::error::ScraperError;
|
||||
use crate::modules::{BlockModule, MsgModule, TxModule};
|
||||
use crate::rpc_client::RpcClient;
|
||||
use crate::scraper::subscriber::ChainSubscriber;
|
||||
use crate::storage::ScraperStorage;
|
||||
use crate::PruningOptions;
|
||||
use futures::future::join_all;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc::{
|
||||
channel, unbounded_channel, Receiver, Sender, UnboundedReceiver, UnboundedSender,
|
||||
};
|
||||
use tokio::sync::mpsc::{channel, unbounded_channel};
|
||||
use tokio::sync::Notify;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tokio_util::task::TaskTracker;
|
||||
use tracing::{error, info};
|
||||
use tracing::info;
|
||||
use url::Url;
|
||||
|
||||
mod subscriber;
|
||||
@@ -119,7 +115,6 @@ pub struct NyxdScraper {
|
||||
cancel_token: CancellationToken,
|
||||
startup_sync: Arc<Notify>,
|
||||
pub storage: ScraperStorage,
|
||||
rpc_client: RpcClient,
|
||||
}
|
||||
|
||||
impl NyxdScraper {
|
||||
@@ -130,7 +125,6 @@ impl NyxdScraper {
|
||||
pub async fn new(config: Config) -> Result<Self, ScraperError> {
|
||||
config.pruning_options.validate()?;
|
||||
let storage = ScraperStorage::init(&config.database_path).await?;
|
||||
let rpc_client = RpcClient::new(&config.rpc_url)?;
|
||||
|
||||
Ok(NyxdScraper {
|
||||
config,
|
||||
@@ -138,7 +132,6 @@ impl NyxdScraper {
|
||||
cancel_token: CancellationToken::new(),
|
||||
startup_sync: Arc::new(Default::default()),
|
||||
storage,
|
||||
rpc_client,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -158,156 +151,36 @@ impl NyxdScraper {
|
||||
self.task_tracker.close();
|
||||
}
|
||||
|
||||
pub async fn process_single_block(&self, height: u32) -> Result<(), ScraperError> {
|
||||
info!(height = height, "attempting to process a single block");
|
||||
if !self.task_tracker.is_empty() {
|
||||
return Err(ScraperError::ScraperAlreadyRunning);
|
||||
}
|
||||
pub async fn start(&self) -> Result<(), ScraperError> {
|
||||
let (processing_tx, processing_rx) = unbounded_channel();
|
||||
let (req_tx, req_rx) = channel(5);
|
||||
|
||||
let (_, processing_rx) = unbounded_channel();
|
||||
let (req_tx, _) = channel(5);
|
||||
let rpc_client = RpcClient::new(&self.config.rpc_url)?;
|
||||
|
||||
let mut block_processor = self
|
||||
.new_block_processor(req_tx.clone(), processing_rx)
|
||||
.await?
|
||||
.with_pruning(PruningOptions::nothing());
|
||||
|
||||
let block = self.rpc_client.get_basic_block_details(height).await?;
|
||||
|
||||
block_processor.process_block(block.into()).await
|
||||
}
|
||||
|
||||
pub async fn process_block_range(
|
||||
&self,
|
||||
starting_height: Option<u32>,
|
||||
end_height: Option<u32>,
|
||||
) -> Result<(), ScraperError> {
|
||||
if !self.task_tracker.is_empty() {
|
||||
return Err(ScraperError::ScraperAlreadyRunning);
|
||||
}
|
||||
|
||||
let (_, processing_rx) = unbounded_channel();
|
||||
let (req_tx, _) = channel(5);
|
||||
|
||||
let mut block_processor = self
|
||||
.new_block_processor(req_tx.clone(), processing_rx)
|
||||
.await?
|
||||
.with_pruning(PruningOptions::nothing());
|
||||
|
||||
let current_height = self.rpc_client.current_block_height().await? as u32;
|
||||
let last_processed = block_processor.last_process_height();
|
||||
|
||||
let starting_height = match starting_height {
|
||||
// always attempt to use whatever the user has provided
|
||||
Some(explicit) => explicit,
|
||||
None => {
|
||||
// otherwise, attempt to resume where we last stopped
|
||||
// and if we haven't processed anything, start from the current height
|
||||
if last_processed != 0 {
|
||||
last_processed
|
||||
} else {
|
||||
current_height
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let end_height = match end_height {
|
||||
// always attempt to use whatever the user has provided
|
||||
Some(explicit) => explicit,
|
||||
None => {
|
||||
// otherwise, attempt to either go from the start height to the height right
|
||||
// before the final processed block held in the storage (in case there are gaps)
|
||||
// or finally, just go to the current block height
|
||||
if last_processed > starting_height {
|
||||
last_processed - 1
|
||||
} else {
|
||||
current_height
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
info!(
|
||||
starting_height = starting_height,
|
||||
end_height = end_height,
|
||||
"attempting to process block range"
|
||||
);
|
||||
|
||||
let range = (starting_height..=end_height).collect::<Vec<_>>();
|
||||
|
||||
// the most likely bottleneck here are going to be the chain queries,
|
||||
// so batch multiple requests
|
||||
for batch in range.chunks(4) {
|
||||
let batch_result = join_all(
|
||||
batch
|
||||
.iter()
|
||||
.map(|height| self.rpc_client.get_basic_block_details(*height)),
|
||||
)
|
||||
.await;
|
||||
for result in batch_result {
|
||||
match result {
|
||||
Ok(block) => block_processor.process_block(block.into()).await?,
|
||||
Err(err) => {
|
||||
error!("failed to retrieve the block: {err}. stopping...");
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn new_block_requester(
|
||||
&self,
|
||||
req_rx: Receiver<BlockRequest>,
|
||||
processing_tx: UnboundedSender<BlockToProcess>,
|
||||
) -> BlockRequester {
|
||||
BlockRequester::new(
|
||||
// create the tasks
|
||||
let block_requester = BlockRequester::new(
|
||||
self.cancel_token.clone(),
|
||||
self.rpc_client.clone(),
|
||||
rpc_client.clone(),
|
||||
req_rx,
|
||||
processing_tx.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
async fn new_block_processor(
|
||||
&self,
|
||||
req_tx: Sender<BlockRequest>,
|
||||
processing_rx: UnboundedReceiver<BlockToProcess>,
|
||||
) -> Result<BlockProcessor, ScraperError> {
|
||||
BlockProcessor::new(
|
||||
);
|
||||
let block_processor = BlockProcessor::new(
|
||||
self.config.pruning_options,
|
||||
self.cancel_token.clone(),
|
||||
self.startup_sync.clone(),
|
||||
processing_rx,
|
||||
req_tx,
|
||||
self.storage.clone(),
|
||||
self.rpc_client.clone(),
|
||||
rpc_client,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn new_chain_subscriber(
|
||||
&self,
|
||||
processing_tx: UnboundedSender<BlockToProcess>,
|
||||
) -> Result<ChainSubscriber, ScraperError> {
|
||||
ChainSubscriber::new(
|
||||
.await?;
|
||||
let chain_subscriber = ChainSubscriber::new(
|
||||
&self.config.websocket_url,
|
||||
self.cancel_token.clone(),
|
||||
self.task_tracker.clone(),
|
||||
processing_tx,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn start(&self) -> Result<(), ScraperError> {
|
||||
let (processing_tx, processing_rx) = unbounded_channel();
|
||||
let (req_tx, req_rx) = channel(5);
|
||||
|
||||
// create the tasks
|
||||
let block_requester = self.new_block_requester(req_rx, processing_tx.clone());
|
||||
let block_processor = self.new_block_processor(req_tx, processing_rx).await?;
|
||||
let chain_subscriber = self.new_chain_subscriber(processing_tx).await?;
|
||||
.await?;
|
||||
|
||||
// spawn them
|
||||
self.start_tasks(block_requester, block_processor, chain_subscriber);
|
||||
|
||||
@@ -16,7 +16,7 @@ use url::Url;
|
||||
|
||||
const MAX_FAILURES: usize = 10;
|
||||
const MAX_RECONNECTION_ATTEMPTS: usize = 8;
|
||||
const SOCKET_FAILURE_RESET: Duration = Duration::minutes(15);
|
||||
const SOCKET_FAILURE_RESET: Duration = Duration::hours(2);
|
||||
|
||||
pub struct ChainSubscriber {
|
||||
cancel: CancellationToken,
|
||||
|
||||
@@ -435,12 +435,9 @@ where
|
||||
trace!("update_last_processed");
|
||||
let start = Instant::now();
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE metadata SET last_processed_height = MAX(last_processed_height, ?)",
|
||||
height
|
||||
)
|
||||
.execute(executor)
|
||||
.await?;
|
||||
sqlx::query!("UPDATE metadata SET last_processed_height = ?", height)
|
||||
.execute(executor)
|
||||
.await?;
|
||||
log_db_operation_time("update_last_processed", start);
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -306,13 +306,7 @@ async fn persist_commits(
|
||||
} => (validator_address, timestamp, signature),
|
||||
};
|
||||
|
||||
let validator = match crate::helpers::validator_info(*validator_id, validators) {
|
||||
Ok(validator_info) => validator_info,
|
||||
Err(err) => {
|
||||
error!("{err}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let validator = crate::helpers::validator_info(*validator_id, validators)?;
|
||||
let validator_address = crate::helpers::validator_consensus_address(*validator_id)?;
|
||||
|
||||
if signature.is_none() {
|
||||
|
||||
@@ -13,13 +13,11 @@ license.workspace = true
|
||||
[dependencies]
|
||||
|
||||
serde = { workspace = true }
|
||||
hex = { workspace = true, optional = true }
|
||||
bs58 = { workspace = true, optional = true }
|
||||
base64 = { workspace = true, optional = true }
|
||||
time = { workspace = true, features = ["formatting", "parsing"], optional = true }
|
||||
|
||||
[features]
|
||||
hex = ["dep:hex"]
|
||||
bs58 = ["dep:bs58"]
|
||||
base64 = ["dep:base64"]
|
||||
date = ["time"]
|
||||
@@ -32,20 +32,6 @@ pub mod bs58 {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "hex")]
|
||||
pub mod hex {
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
|
||||
pub fn serialize<S: Serializer>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error> {
|
||||
serializer.serialize_str(&::hex::encode(bytes))
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<u8>, D::Error> {
|
||||
let s = String::deserialize(deserializer)?;
|
||||
::hex::decode(&s).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "date")]
|
||||
pub mod date {
|
||||
use serde::ser::Error;
|
||||
|
||||
@@ -114,7 +114,6 @@ pub async fn start_wireguard<St: nym_gateway_storage::Storage + Clone + 'static>
|
||||
address: wireguard_data.inner.config().private_ip.to_string(),
|
||||
port: wireguard_data.inner.config().announced_port as u32,
|
||||
peers,
|
||||
mtu: None,
|
||||
};
|
||||
wg_api.configure_interface(&interface_config)?;
|
||||
|
||||
|
||||
@@ -226,7 +226,7 @@ impl<St: Storage + Clone + 'static> PeerController<St> {
|
||||
.read()
|
||||
.await
|
||||
.available_bandwidth()
|
||||
.await
|
||||
.bytes as u64
|
||||
} else {
|
||||
let peer = self
|
||||
.host_information
|
||||
@@ -236,7 +236,7 @@ impl<St: Storage + Clone + 'static> PeerController<St> {
|
||||
.get(key)
|
||||
.ok_or(Error::PeerMismatch)?
|
||||
.clone();
|
||||
BANDWIDTH_CAP_PER_DAY.saturating_sub((peer.rx_bytes + peer.tx_bytes) as i64)
|
||||
BANDWIDTH_CAP_PER_DAY.saturating_sub(peer.rx_bytes + peer.tx_bytes)
|
||||
};
|
||||
|
||||
Ok(Some(RemainingBandwidthData {
|
||||
|
||||
@@ -538,13 +538,15 @@ pub fn query(
|
||||
|
||||
#[entry_point]
|
||||
pub fn migrate(
|
||||
deps: DepsMut<'_>,
|
||||
mut deps: DepsMut<'_>,
|
||||
_env: Env,
|
||||
msg: MigrateMsg,
|
||||
) -> Result<Response, MixnetContractError> {
|
||||
set_build_information!(deps.storage)?;
|
||||
cw2::ensure_from_older_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
|
||||
|
||||
crate::queued_migrations::explicit_contract_admin(deps.branch())?;
|
||||
|
||||
// due to circular dependency on contract addresses (i.e. mixnet contract requiring vesting contract address
|
||||
// and vesting contract requiring the mixnet contract address), if we ever want to deploy any new fresh
|
||||
// environment, one of the contracts will HAVE TO go through a migration
|
||||
|
||||
@@ -1,2 +1,20 @@
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
|
||||
use cosmwasm_std::DepsMut;
|
||||
use mixnet_contract_common::error::MixnetContractError;
|
||||
|
||||
pub(crate) fn explicit_contract_admin(deps: DepsMut) -> Result<(), MixnetContractError> {
|
||||
// we need to read the deprecated field to migrate it over
|
||||
#[allow(deprecated)]
|
||||
// SAFETY: this value should ALWAYS exist on the first execution of this migration;
|
||||
// as a matter of fact, it should ALWAYS continue existing until another migration
|
||||
#[allow(clippy::expect_used)]
|
||||
let existing_admin = mixnet_params_storage::CONTRACT_STATE
|
||||
.load(deps.storage)?
|
||||
.owner
|
||||
.expect("the contract state is corrupt - there's no admin set");
|
||||
mixnet_params_storage::ADMIN.set(deps, Some(existing_admin))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2,242 +2,6 @@
|
||||
|
||||
This page displays a full list of all the changes during our release cycle from [`v2024.3-eclipse`](https://github.com/nymtech/nym/blob/nym-binaries-v2024.3-eclipse/CHANGELOG.md) onwards. Operators can find here the newest updates together with links to relevant documentation. The list is sorted so that the newest changes appear first.
|
||||
|
||||
## `v2024.11-wedel`
|
||||
|
||||
- [Release binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2024.11-wedel)
|
||||
- [Release CHANGELOG.md](https://github.com/nymtech/nym/blob/nym-binaries-v2024.11-wedel/CHANGELOG.md)
|
||||
- [`nym-node`](nodes/nym-node.md) version `1.1.8`
|
||||
|
||||
```sh
|
||||
Binary Name: nym-node
|
||||
Build Timestamp: 2024-09-27T11:02:37.073944654Z
|
||||
Build Version: 1.1.8
|
||||
Commit SHA: c3ec970a377adb25d57be5428551fada2ec55128
|
||||
Commit Date: 2024-09-26T08:24:53.000000000+02:00
|
||||
Commit Branch: master
|
||||
rustc Version: 1.80.1
|
||||
rustc Channel: stable
|
||||
cargo Profile: release
|
||||
```
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
- [New Network Monitor](https://github.com/nymtech/nym/pull/4610): Monitors the Nym network by sending itself packages across the mixnet. Network monitor is running two tokio tasks, one manages mixnet clients and another manages monitoring itself. Monitor is designed to be driven externally, via an `HTTP api`. This means that it does not do any monitoring unless driven by something like [`locust`](https://locust.io/). This allows us to tailor the load externally, potentially distributing it across multiple monitors. Includes a dockerised setup for automatically spinning up monitor and driving it with locust.
|
||||
- *Note: NNM is not deployed on mainnet yet!*
|
||||
|
||||
- [Add get_mixnodes_described to validator_client](https://github.com/nymtech/nym/pull/4725)
|
||||
|
||||
- [Remove deprecated mark_as_success and use new disarm](https://github.com/nymtech/nym/pull/4751): Update function name to keep terminology consistent with tokio `CancellationToken DropGuard`.
|
||||
|
||||
- [Update peer refresh value](https://github.com/nymtech/nym/pull/4754): `lso` expose the value by moving it to wireguard types, and separate the refresh time to the database sync time, so that more probable and needed actions happen faster (refresh) and more improbable ones don't overload the system (peer suspended or stale)
|
||||
~~~admonish example collapsible=true title='Testing steps performed'
|
||||
- **Noted** that the constants `DEFAULT_PEER_TIMEOUT` and `DEFAULT_PEER_TIMEOUT_CHECK` have been moved to `common/wireguard-types/src/lib.rs` and are now being used across modules for consistency
|
||||
- **Observed** that the `peer_controller.rs` now separates the in-memory updates from the storage sync operations to reduce system load
|
||||
- **Identified** that in-memory updates of peer bandwidth usage happen every `DEFAULT_PEER_TIMEOUT_CHECK` (every 5 seconds), while storage updates occur every 5 * `DEFAULT_PEER_TIMEOUT_CHECK` (every 25 seconds)
|
||||
|
||||
**Checked System Load and Performance:**
|
||||
|
||||
- **Monitored** system resource usage (CPU, memory, I/O) during the test to assess the impact of the changes
|
||||
- **Confirmed** that the separation of in-memory updates and storage syncs resulted in reduced system load, particularly I/O operations, compared to previous versions where storage updates occurred more frequently
|
||||
- **Ensured** that the system remained responsive and no performance bottlenecks were introduced
|
||||
|
||||
- **Efficiency Improvement:** The separation of in-memory updates and storage syncs effectively reduced unnecessary database writes, improving system efficiency without compromising data accuracy
|
||||
~~~
|
||||
|
||||
- [Remove duplicate stat count for retransmissions](https://github.com/nymtech/nym/pull/4756)
|
||||
|
||||
- [Make gateway latency check generic](https://github.com/nymtech/nym/pull/4759): Replace concrete gateway type with trait in latency check, so we can make use of it in the vpn client.
|
||||
~~~admonish example collapsible=true title='Testing steps performed'
|
||||
- Initialised new `nym-client` with the `--latency-based-selection` flag and ensured it still works as normal.
|
||||
~~~
|
||||
|
||||
- [chore: remove repetitive words](https://github.com/nymtech/nym/pull/4763)
|
||||
|
||||
- [Avoid race on ip and registration structures](https://github.com/nymtech/nym/pull/4766): To avoid a state where the ip is being cleared out before the registration is also cleared out, couple the two structures under the same lock, since they are anyway very inter-dependent.
|
||||
~~~admonish example collapsible=true title='Testing steps performed'
|
||||
1. - **Checked out** the release/2024.10-wedel branch containing the fix for the race condition on IP and registration structures
|
||||
- **Deployed** the on a controlled test environment to prevent interference
|
||||
|
||||
2. **Monitored Logs:**
|
||||
|
||||
- **Enabled** debug logging to capture all events
|
||||
- **Monitored** logs in real-time to observe the handling of concurrent registration requests
|
||||
- **Checked** for any error messages, warnings, or indications of race conditions
|
||||
|
||||
3. **Verified Client Responses:**
|
||||
|
||||
- Ensured that all clients received appropriate responses:
|
||||
- Successful registration with assigned IP and registration data
|
||||
- Appropriate error messages if no IPs were available or if other issues occurred
|
||||
- Confirmed that no clients were left in an inconsistent state (e.g., assigned an IP but not fully registered)
|
||||
|
||||
4. **Validated Normal Operation:**
|
||||
- **Conducted standard registration processes** with individual clients to confirm that regular functionality is unaffected via `nym-vpn-cli`
|
||||
- Ensured that authenticated clients could communicate over the network as expected
|
||||
~~~
|
||||
|
||||
- [Persist used wireguard private IPs](https://github.com/nymtech/nym/pull/4771)
|
||||
|
||||
- [Enable dependabot version upgrades for root rust workspace](https://github.com/nymtech/nym/pull/4778)
|
||||
|
||||
- [Fix clippy for `unwrap_or_default`](https://github.com/nymtech/nym/pull/4783): Fix nightly build for [beta toolchain](https://github.com/nymtech/nym/actions/runs/10552082396/job/29230401668)
|
||||
|
||||
- [Update dependabot](https://github.com/nymtech/nym/pull/4796): Bump max number of dependabot rust PRs to 10. Add readme entry to workspace package.
|
||||
|
||||
- [Run `cargo-autoinherit` for a few new crates](https://github.com/nymtech/nym/pull/4801): Run cargo-autoinherit for a few new crates - Sort crates list.
|
||||
|
||||
- [Add `axum` server to `nym-api`](https://github.com/nymtech/nym/pull/4803): Summary PR to add axum functionality behind a feature flag `axum`, alongside rocket.
|
||||
|
||||
- [Remove unused wireguard flag from SDK](https://github.com/nymtech/nym/pull/4823)
|
||||
|
||||
- [Expose wireguard details on self described endpoint](https://github.com/nymtech/nym/pull/4825)
|
||||
~~~admonish example collapsible=true title='Testing steps performed'
|
||||
Wireguard details are now visible at the nym-node endpoint `/api/v1/gateway/client-interfaces` as well as on the nym-api self-described endpoint `/api/v1/gateways/described`, above the existing data displaying mixnet_websocket information.
|
||||
|
||||
An example of what will be shown is:
|
||||
```json
|
||||
"wireguard": {
|
||||
"port": 51822,
|
||||
"public_key": "<some public key here>"
|
||||
}
|
||||
```
|
||||
~~~
|
||||
|
||||
- [Revamped ticketbook serialisation and exposed additional cli methods](https://github.com/nymtech/nym/pull/4827): `wip` branch that includes changes needed for `vpn-api` alongside additional `ecash utils`
|
||||
~~~admonish example collapsible=true title='Testing steps performed'
|
||||
Checked the following commands:
|
||||
```sh
|
||||
show-ticket-books # which displays the information about all ticketbooks associated to the client
|
||||
import-ticket-book # which imports a normal ticketbook to the client alongside `--full` flag
|
||||
```
|
||||
|
||||
On the cli, the following were added: `import-coin-index-signatures`, `import-expiration-date-signatures` and `import-master-verification-key`.
|
||||
~~~
|
||||
|
||||
- [Run cargo autoinherit following last weeks dependabot updates](https://github.com/nymtech/nym/pull/4831)
|
||||
|
||||
- [Remove serde_crate named import](https://github.com/nymtech/nym/pull/4832)
|
||||
|
||||
- [Create nym-repo-setup debian package and nym-vpn meta package](https://github.com/nymtech/nym/pull/4837): Create nym-repo-setup debian package that sets up the nymtech debian repo on the system it's installed on. It does 2 things:
|
||||
|
||||
1. Copy the keyring to `/usr/share/keyrings/nymtech.gpg`
|
||||
2. Copy the repo spec to `/etc/apt/sources.list.d/nymtech.list`
|
||||
- Also create a meta package `nym-vpn` which only purpose is to depend on the daemon and UI.
|
||||
|
||||
~~~admonish example collapsible=true title='Usage'
|
||||
1. Install with
|
||||
```sh
|
||||
sudo dpkg -i ./nym-repo-setup.deb
|
||||
```
|
||||
2. Once it's installed, it should be possible to install the vpn client with
|
||||
```sh
|
||||
sudo apt install nym-vpnc
|
||||
```
|
||||
3. To reemove the repo, use
|
||||
```sh
|
||||
sudo apt remove nym-repo-setup
|
||||
```
|
||||
|
||||
NOTE: removing the repo will not remove any installed nym-vpn packages
|
||||
~~~
|
||||
|
||||
~~~admonish example collapsible=true title='Testing steps performed'
|
||||
|
||||
1. **Downloaded** the `nym-repo-setup.deb` package to a Debian-based test system
|
||||
|
||||
2. **Installed** the repository setup package using the command:
|
||||
```bash
|
||||
sudo dpkg -i ./nym-repo-setup.deb
|
||||
```
|
||||
|
||||
3. **Verified** that the GPG keyring was copied to `/usr/share/keyrings/nymtech.gpg`:
|
||||
```bash
|
||||
ls -l /usr/share/keyrings/nymtech.gpg
|
||||
```
|
||||
|
||||
4. **Checked** that the repository specification was added to `/etc/apt/sources.list.d/nymtech.list`:
|
||||
```bash
|
||||
cat /etc/apt/sources.list.d/nymtech.list
|
||||
```
|
||||
|
||||
5. **Updated** the package list:
|
||||
```bash
|
||||
sudo apt update
|
||||
```
|
||||
|
||||
6. **Installed** the VPN client meta-package:
|
||||
```bash
|
||||
sudo apt install nym-vpnc
|
||||
```
|
||||
|
||||
7. **Confirmed** that the `nym-vpnc` package and its dependencies (daemon and UI) were installed successfully
|
||||
|
||||
8. **Tested** the VPN client to ensure it operates as expected
|
||||
|
||||
9. **Removed** the repository setup package:
|
||||
```bash
|
||||
sudo apt remove nym-repo-setup
|
||||
```
|
||||
|
||||
10. **Verified** that the repository specification file `/etc/apt/sources.list.d/nymtech.list` was removed
|
||||
|
||||
11. **Ensured** that the installed `nym-vpnc` packages remained installed and functional after removing the repo setup package
|
||||
~~~
|
||||
|
||||
- [Use ecash credential type for bandwidth value](https://github.com/nymtech/nym/pull/4840)
|
||||
|
||||
- [Start switching over jobs to arc-ubuntu-20.04](https://github.com/nymtech/nym/pull/4843)
|
||||
|
||||
~~~admonish example collapsible=true title='`ci-binary-config-checker`'
|
||||
```
|
||||
- ci-build-upload-binaries
|
||||
- ci-build
|
||||
- ci-cargo-deny
|
||||
- ci-contracts-schema
|
||||
- ci-contracts-upload-binaries
|
||||
- ci-contracts
|
||||
- ci-docs
|
||||
- ci-nym-wallet-rust
|
||||
- ci-sdk-wasm
|
||||
```
|
||||
~~~
|
||||
|
||||
- [Move credential verification into common crate](https://github.com/nymtech/nym/pull/4853)
|
||||
|
||||
- [Revert runner for `ci-docs`](https://github.com/nymtech/nym/pull/4855)
|
||||
|
||||
- [Remove `golang` workaround in `ci-sdk-wasm`](https://github.com/nymtech/nym/pull/4858)
|
||||
|
||||
- [Fix linux conditional in `ci-build.yml`](https://github.com/nymtech/nym/pull/4863)
|
||||
|
||||
- [Disable push trigger and add missing paths in `ci-build`](https://github.com/nymtech/nym/pull/4864)
|
||||
|
||||
- [chore: removed completed queued mixnet migration](https://github.com/nymtech/nym/pull/4865)
|
||||
|
||||
- [Bump defguard to github latest version](https://github.com/nymtech/nym/pull/4872)
|
||||
|
||||
- [Backport #4894 to fix ci](https://github.com/nymtech/nym/pull/4899)
|
||||
|
||||
### Bugfix
|
||||
|
||||
- [Fix test failure in ipr request size](https://github.com/nymtech/nym/pull/4844): Nightly build started failing due to a unit test using `now()`, changing the serialized size. Fixed to use a fixed date.
|
||||
|
||||
- [Fix clippy for nym-wallet and latest rustc](https://github.com/nymtech/nym/pull/4845)
|
||||
|
||||
- [Allow updating globally stored signatures](https://github.com/nymtech/nym/pull/4891)
|
||||
|
||||
- [Bugfix/ticketbook false double spending](https://github.com/nymtech/nym/pull/4892)
|
||||
~~~admonish example collapsible=true title='Testing steps performed'
|
||||
Tested running a client in mixnet mode, with a standard ticketbook, as well as a client using an imported ticketbook. The double spending bug is no longer an issue, bandwidth is consumed properly, and upon consumption of one ticket another ticket is properly obtained.
|
||||
~~~
|
||||
|
||||
### Operators Guide, Tooling & Updates
|
||||
|
||||
- [WSS setup guide updates](https://github.com/nymtech/nym/commit/05d6652177fb77324f8c38b3d8a547d07e729fec): Operators setting up WSS and reverse proxy on Gateways have now cleaner and simpler guide to configure their VPS.
|
||||
|
||||
- [Updat hostname instruction for WSS](https://github.com/nymtech/nym/commit/7146c4c012ba7012dc74edc8510bbf377dc32fba): Adding a hostname instruction for clarity
|
||||
|
||||
## `nym-node` patch from `release/2024.10-caramello`
|
||||
|
||||
- [Patch release binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2024.10-caramello-patch)
|
||||
|
||||
+7
-4
@@ -11,13 +11,16 @@ STAKE_DENOM_DISPLAY=nyx
|
||||
DENOMS_EXPONENT=6
|
||||
|
||||
MIXNET_CONTRACT_ADDRESS=n1hm4y6fzgxgu688jgf7ek66px6xkrtmn3gyk8fax3eawhp68c2d5qujz296
|
||||
ECASH_CONTRACT_ADDRESS=n13xspq62y9gq6nueqmywxcdv2yep4p6nzv98w2889k25v3nhdy2dq2rkrk7
|
||||
GROUP_CONTRACT_ADDRESS=n13l7rwuwktklrwskc7m6lv70zws07en85uma28j7dxwsz9y5hvvhspl7a2t
|
||||
MULTISIG_CONTRACT_ADDRESS=n138c9pyf7f3hyx0j3t6vmsz7ultnw2wj0lu6hzndep9z5grgq9haqlc25k0
|
||||
COCONUT_DKG_CONTRACT_ADDRESS=n1pk8jgr6y4c5k93gz7qf3xc0hvygmp7csk88c2tf8l39tkq6834wq2a6dtr
|
||||
ECASH_CONTRACT_ADDRESS=n14y2x8a60knc5jjfeztt84kw8x8l5pwdgnqg256v0p9v4p7t2q6eswxyusw
|
||||
GROUP_CONTRACT_ADDRESS=n1qp35fcj0v9u3trhaps5v9q0lc42t4m6aty2wryss75ee8zuqnsqqdcreyq
|
||||
MULTISIG_CONTRACT_ADDRESS=n1qa4hswlcjmttulj0q9qa46jf64f93pecl6tydcsjldfe0hy5ju0sdmwzya
|
||||
COCONUT_DKG_CONTRACT_ADDRESS=n1ayrk6wp6w5lf6njtnfjwljmtcc9vevv5sxwkz7uq24rp2pw67t0qhmmxdd
|
||||
VESTING_CONTRACT_ADDRESS=n1jlzdxnyces4hrhqz68dqk28mrw5jgwtcfq0c2funcwrmw0dx9l9s8nnnvj
|
||||
REWARDING_VALIDATOR_ADDRESS=n1rfvpsynktze6wvn6ldskj8xgwfzzk5v6pnff39
|
||||
|
||||
EXPLORER_API=https://qa-network-explorer.qa.nymte.ch/api
|
||||
NYXD="https://qa-validator.qa.nymte.ch"
|
||||
NYM_API="https://qa-nym-api.qa.nymte.ch/api"
|
||||
|
||||
DKG_TIME_CONFIGURATION="600,300,300,60,60,1209600"
|
||||
EXIT_POLICY="https://nymtech.net/.wellknown/network-requester/exit-policy.txt"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "explorer-api"
|
||||
version = "1.1.40"
|
||||
version = "1.1.39"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ use nym_gateway_storage::{error::StorageError, Storage};
|
||||
use nym_sphinx::forwarding::packet::MixPacket;
|
||||
use nym_task::TaskClient;
|
||||
use nym_validator_client::coconut::EcashApiError;
|
||||
use rand::{random, CryptoRng, Rng};
|
||||
use rand::{CryptoRng, Rng};
|
||||
use std::{process, time::Duration};
|
||||
use thiserror::Error;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
@@ -236,7 +236,11 @@ where
|
||||
enc_credential: Vec<u8>,
|
||||
iv: Vec<u8>,
|
||||
) -> Result<ServerResponse, RequestHandlingError> {
|
||||
debug!("handling e-cash bandwidth request");
|
||||
// TODO: change it into a span field instead once we move to tracing
|
||||
debug!(
|
||||
"handling e-cash bandwidth request from {}",
|
||||
self.client.address
|
||||
);
|
||||
|
||||
let credential = ClientControlRequest::try_from_enc_ecash_credential(
|
||||
enc_credential,
|
||||
@@ -249,11 +253,7 @@ where
|
||||
self.bandwidth_storage_manager.clone(),
|
||||
);
|
||||
|
||||
let available_total = verifier
|
||||
.verify()
|
||||
.await
|
||||
.inspect_err(|verification_failure| debug!("{verification_failure}"))?;
|
||||
trace!("available total bandwidth: {available_total}");
|
||||
let available_total = verifier.verify().await?;
|
||||
|
||||
Ok(ServerResponse::Bandwidth { available_total })
|
||||
}
|
||||
@@ -340,17 +340,20 @@ where
|
||||
&mut self,
|
||||
ciphertext: Vec<u8>,
|
||||
nonce: Vec<u8>,
|
||||
) -> Result<ServerResponse, RequestHandlingError> {
|
||||
) -> Message {
|
||||
let Ok(req) = ClientRequest::decrypt(&ciphertext, &nonce, &self.client.shared_keys) else {
|
||||
return Err(RequestHandlingError::InvalidEncryptedTextRequest);
|
||||
return RequestHandlingError::InvalidEncryptedTextRequest.into_error_message();
|
||||
};
|
||||
|
||||
match req {
|
||||
ClientRequest::UpgradeKey {
|
||||
hkdf_salt,
|
||||
derived_key_digest,
|
||||
} => self.handle_key_upgrade(hkdf_salt, derived_key_digest).await,
|
||||
_ => Err(RequestHandlingError::UnknownEncryptedTextRequest),
|
||||
} => self
|
||||
.handle_key_upgrade(hkdf_salt, derived_key_digest)
|
||||
.await
|
||||
.into_ws_message(),
|
||||
_ => RequestHandlingError::UnknownEncryptedTextRequest.into_error_message(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,64 +366,59 @@ where
|
||||
/// * `raw_request`: raw message to handle.
|
||||
async fn handle_text(&mut self, raw_request: String) -> Message {
|
||||
trace!("text request");
|
||||
|
||||
let request = match ClientControlRequest::try_from(raw_request) {
|
||||
Ok(req) => {
|
||||
debug!("received request of type {}", req.name());
|
||||
req
|
||||
}
|
||||
Err(err) => {
|
||||
debug!("request was malformed: {err}");
|
||||
return RequestHandlingError::InvalidTextRequest(err).into_error_message();
|
||||
}
|
||||
};
|
||||
|
||||
match request {
|
||||
ClientControlRequest::EncryptedRequest { ciphertext, nonce } => {
|
||||
self.handle_encrypted_text_request(ciphertext, nonce).await
|
||||
}
|
||||
ClientControlRequest::EcashCredential { enc_credential, iv } => {
|
||||
self.handle_ecash_bandwidth(enc_credential, iv).await
|
||||
}
|
||||
ClientControlRequest::BandwidthCredential { .. } => {
|
||||
Err(RequestHandlingError::IllegalRequest {
|
||||
additional_context: "coconut credential are not longer supported".into(),
|
||||
})
|
||||
}
|
||||
ClientControlRequest::BandwidthCredentialV2 { .. } => {
|
||||
Err(RequestHandlingError::IllegalRequest {
|
||||
additional_context: "coconut credential are not longer supported".into(),
|
||||
})
|
||||
}
|
||||
ClientControlRequest::ClaimFreeTestnetBandwidth => self
|
||||
.bandwidth_storage_manager
|
||||
.handle_claim_testnet_bandwidth()
|
||||
.await
|
||||
.map_err(|e| e.into()),
|
||||
ClientControlRequest::SupportedProtocol { .. } => {
|
||||
Ok(self.inner.handle_supported_protocol_request())
|
||||
}
|
||||
other @ ClientControlRequest::Authenticate { .. } => {
|
||||
Err(RequestHandlingError::IllegalRequest {
|
||||
additional_context: format!(
|
||||
"received illegal message of type {} in an authenticated client",
|
||||
other.name()
|
||||
),
|
||||
})
|
||||
}
|
||||
other @ ClientControlRequest::RegisterHandshakeInitRequest { .. } => {
|
||||
Err(RequestHandlingError::IllegalRequest {
|
||||
additional_context: format!(
|
||||
"received illegal message of type {} in an authenticated client",
|
||||
other.name()
|
||||
),
|
||||
})
|
||||
}
|
||||
_ => Err(RequestHandlingError::UnknownTextRequest),
|
||||
match ClientControlRequest::try_from(raw_request) {
|
||||
Err(e) => RequestHandlingError::InvalidTextRequest(e).into_error_message(),
|
||||
Ok(request) => match request {
|
||||
ClientControlRequest::EncryptedRequest { ciphertext, nonce } => {
|
||||
self.handle_encrypted_text_request(ciphertext, nonce).await
|
||||
}
|
||||
ClientControlRequest::EcashCredential { enc_credential, iv } => self
|
||||
.handle_ecash_bandwidth(enc_credential, iv)
|
||||
.await
|
||||
.into_ws_message(),
|
||||
ClientControlRequest::BandwidthCredential { .. } => {
|
||||
RequestHandlingError::IllegalRequest {
|
||||
additional_context: "coconut credential are not longer supported".into(),
|
||||
}
|
||||
.into_error_message()
|
||||
}
|
||||
ClientControlRequest::BandwidthCredentialV2 { .. } => {
|
||||
RequestHandlingError::IllegalRequest {
|
||||
additional_context: "coconut credential are not longer supported".into(),
|
||||
}
|
||||
.into_error_message()
|
||||
}
|
||||
ClientControlRequest::ClaimFreeTestnetBandwidth => self
|
||||
.bandwidth_storage_manager
|
||||
.handle_claim_testnet_bandwidth()
|
||||
.await
|
||||
.map_err(|e| e.into())
|
||||
.into_ws_message(),
|
||||
ClientControlRequest::SupportedProtocol { .. } => self
|
||||
.inner
|
||||
.handle_supported_protocol_request()
|
||||
.into_ws_message(),
|
||||
other @ ClientControlRequest::Authenticate { .. } => {
|
||||
RequestHandlingError::IllegalRequest {
|
||||
additional_context: format!(
|
||||
"received illegal message of type {} in an authenticated client",
|
||||
other.name()
|
||||
),
|
||||
}
|
||||
.into_error_message()
|
||||
}
|
||||
other @ ClientControlRequest::RegisterHandshakeInitRequest { .. } => {
|
||||
RequestHandlingError::IllegalRequest {
|
||||
additional_context: format!(
|
||||
"received illegal message of type {} in an authenticated client",
|
||||
other.name()
|
||||
),
|
||||
}
|
||||
.into_error_message()
|
||||
}
|
||||
_ => RequestHandlingError::UnknownTextRequest.into_error_message(),
|
||||
},
|
||||
}
|
||||
.inspect(|res| debug!(response = ?res, "success"))
|
||||
.inspect_err(|err| debug!(error = %err, "failure"))
|
||||
.into_ws_message()
|
||||
}
|
||||
|
||||
/// Handles pong message received from the client.
|
||||
@@ -454,13 +452,12 @@ where
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `raw_request`: raw received websocket message.
|
||||
#[instrument(level = "debug", skip_all,
|
||||
fields(
|
||||
client = %self.client.address.as_base58_string()
|
||||
)
|
||||
)]
|
||||
async fn handle_request(&mut self, raw_request: Message) -> Option<Message> {
|
||||
trace!("new request");
|
||||
// TODO: this should be added via tracing
|
||||
debug!(
|
||||
"handling request from {}",
|
||||
self.client.address.as_base58_string()
|
||||
);
|
||||
|
||||
// apparently tungstenite auto-handles ping/pong/close messages so for now let's ignore
|
||||
// them and let's test that claim. If that's not the case, just copy code from
|
||||
@@ -481,8 +478,8 @@ where
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
let tag: u64 = random();
|
||||
debug!("got request to ping our connection: {tag}");
|
||||
let tag: u64 = rand::thread_rng().gen();
|
||||
debug!("Got request to ping our connection: {}", tag);
|
||||
self.inner
|
||||
.send_websocket_message(Message::Ping(tag.to_be_bytes().to_vec()))
|
||||
.await?;
|
||||
|
||||
@@ -420,7 +420,7 @@ where
|
||||
// we can't handle clients with higher protocol than ours
|
||||
// (perhaps we could try to negotiate downgrade on our end? sounds like a nice future improvement)
|
||||
if client_protocol_version <= CURRENT_PROTOCOL_VERSION {
|
||||
debug!("the client is using exactly the same (or older) protocol version as we are. We're good to continue!");
|
||||
info!("the client is using exactly the same (or older) protocol version as we are. We're good to continue!");
|
||||
Ok(CURRENT_PROTOCOL_VERSION)
|
||||
} else {
|
||||
let err = InitialAuthenticationError::IncompatibleProtocol {
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
[package]
|
||||
name = "nym-api"
|
||||
license = "GPL-3.0"
|
||||
version = "1.1.44"
|
||||
version = "1.1.43"
|
||||
authors = [
|
||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
# Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
[package]
|
||||
name = "nym-data-observatory"
|
||||
version = "0.1.0"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
axum = { workspace = true, features = ["tokio"] }
|
||||
chrono = { workspace = true }
|
||||
nym-bin-common = { path = "../common/bin-common" }
|
||||
nym-network-defaults = { path = "../common/network-defaults" }
|
||||
nym-task = { path = "../common/task" }
|
||||
nym-node-requests = { path = "../nym-node/nym-node-requests", features = ["openapi"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "postgres", "offline"] }
|
||||
tokio = { workspace = true, features = ["process"] }
|
||||
tokio-util = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tracing-subscriber = { workspace = true, features = ["env-filter"] }
|
||||
tower-http = { workspace = true, features = ["cors", "trace"] }
|
||||
utoipa = { workspace = true, features = ["axum_extras", "time"] }
|
||||
utoipa-swagger-ui = { workspace = true, features = ["axum"] }
|
||||
utoipauto = { workspace = true }
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = { workspace = true }
|
||||
tokio = { workspace = true, features = ["macros" ] }
|
||||
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "postgres"] }
|
||||
@@ -1,93 +0,0 @@
|
||||
# Making `sqlx` work
|
||||
|
||||
Some of the errors encountered and possible solutions.
|
||||
|
||||
## `The cargo feature offline has to be enabled to use SQLX_OFFLINE`
|
||||
|
||||
Did you enable `offline` **cargo feature** of `sqlx` dependency in your
|
||||
`Cargo.toml`?
|
||||
|
||||
Also, it may happen if you have a version mismatch beetween
|
||||
|
||||
- `sqlx` cargo dependency in `Cargo.toml`
|
||||
- `sqlx-cli` installed by cargo
|
||||
|
||||
To ensure correct version, do
|
||||
|
||||
```
|
||||
cargo uninstall sqlx-cli
|
||||
cargo install --version <version> sqlx-cli
|
||||
```
|
||||
|
||||
where `<version>` matches the version of sqlx in your `Cargo.toml`.
|
||||
|
||||
## `Error: failed to connect to database: password authentication failed`
|
||||
|
||||
If it's in-code, make sure you don't "double authenticate", i.e.
|
||||
|
||||
- if username and password are already specified in `DATABASE_URL`
|
||||
- then, you don't have to use
|
||||
|
||||
```rust
|
||||
ConnectOptions::from_str(&database_Url)
|
||||
.username() // unnecessary
|
||||
.password() // unnecessary
|
||||
```
|
||||
|
||||
If it's outside of code (i.e. when running `cargo check`)
|
||||
|
||||
- make sure password doesn't have any special characters that could be
|
||||
interpreted by the command line/shell weirdly, like `$#\` etc.
|
||||
|
||||
## Cannot generate `sqlx-data.json`
|
||||
|
||||
In order for `sqlx` to generate schema for "offline" work (without DB
|
||||
connection), as of `v0.6.3` you first **need an active DB connection**.
|
||||
|
||||
So make sure
|
||||
|
||||
- DB is running
|
||||
- `DATABASE_URL` is set correctly
|
||||
- `SQLX_OFFLINE` isn't exported to true
|
||||
|
||||
Then run `cargo sqlx prepare`
|
||||
|
||||
After you have the file, you can ignore `DATABASE_URL` and terminate the DB
|
||||
instance. This file represents the DB schema, so when your migrations change,
|
||||
you'll need to re-generate it
|
||||
|
||||
Make sure to commit the file to VCS if you want to avoid re-doing this again on
|
||||
each machine (e.g. other developers, CI).
|
||||
|
||||
## Generated `sqlx-data.json` looks like this
|
||||
|
||||
```json
|
||||
{
|
||||
"db": "PostgreSQL"
|
||||
}
|
||||
```
|
||||
|
||||
after running `cargo sqlx prepare`
|
||||
|
||||
### Similar to:
|
||||
|
||||
```
|
||||
warning: no queries found; do you have the `offline` feature enabled
|
||||
```
|
||||
|
||||
### Possible solutions
|
||||
|
||||
- does your `sqlx-cli` version match `sqlx` version from `Cargo.toml`?
|
||||
- do you have `offline` cargo feature enabled?
|
||||
- make sure to `cargo clean` after these updates
|
||||
|
||||
## Any many, many more
|
||||
|
||||
- `EOF while parsing a value at line`
|
||||
- `failed to find data for query`
|
||||
|
||||
### Possible solutions
|
||||
|
||||
- Usually a DB connection issue
|
||||
- Retry everything
|
||||
- Throw in a `cargo clean -p <your package>` for good measure
|
||||
@@ -1,58 +0,0 @@
|
||||
use anyhow::Result;
|
||||
use sqlx::{Connection, PgConnection};
|
||||
use std::io::Write;
|
||||
use std::{collections::HashMap, fs::File};
|
||||
|
||||
const POSTGRES_USER: &str = "nym";
|
||||
const POSTGRES_PASSWORD: &str = "password123";
|
||||
const POSTGRES_DB: &str = "data_obs_db";
|
||||
|
||||
/// if schema changes, rerun `cargo sqlx prepare` with a running DB
|
||||
/// https://github.com/launchbadge/sqlx/blob/main/sqlx-cli/README.md#enable-building-in-offline-mode-with-query
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let db_url =
|
||||
format!("postgresql://{POSTGRES_USER}:{POSTGRES_PASSWORD}@localhost:5432/{POSTGRES_DB}");
|
||||
|
||||
// if a live DB is reachable, use that
|
||||
if PgConnection::connect(&db_url).await.is_ok() {
|
||||
export_db_variables(&db_url)?;
|
||||
println!("cargo::rustc-env=SQLX_OFFLINE=false");
|
||||
run_migrations(&db_url).await?;
|
||||
} else {
|
||||
// by default, run in offline mode
|
||||
println!("cargo::rustc-env=SQLX_OFFLINE=true");
|
||||
}
|
||||
|
||||
rerun_if_changed();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn export_db_variables(db_url: &str) -> Result<()> {
|
||||
let mut map = HashMap::new();
|
||||
map.insert("POSTGRES_USER", POSTGRES_USER);
|
||||
map.insert("POSTGRES_PASSWORD", POSTGRES_PASSWORD);
|
||||
map.insert("POSTGRES_DB", POSTGRES_DB);
|
||||
map.insert("DATABASE_URL", db_url);
|
||||
|
||||
let mut file = File::create(".env")?;
|
||||
for (var, value) in map.iter() {
|
||||
println!("cargo::rustc-env={}={}", var, value);
|
||||
writeln!(file, "{}={}", var, value).expect("Failed to write to dotenv file");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn run_migrations(db_url: &str) -> Result<()> {
|
||||
let mut conn = PgConnection::connect(db_url).await?;
|
||||
sqlx::migrate!("./migrations").run(&mut conn).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn rerun_if_changed() {
|
||||
println!("cargo::rerun-if-changed=migrations");
|
||||
println!("cargo::rerun-if-changed=src/db/queries");
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:13
|
||||
container_name: nym-data-observatory-pg
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
@@ -1,6 +0,0 @@
|
||||
CREATE TABLE responses (
|
||||
id SERIAL PRIMARY KEY,
|
||||
joke_id VARCHAR NOT NULL UNIQUE,
|
||||
joke TEXT NOT NULL,
|
||||
date_created INTEGER NOT NULL
|
||||
);
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
source .env
|
||||
|
||||
# Launching a container in such a way that it's destroyed after you detach from the terminal:
|
||||
docker compose up
|
||||
|
||||
# docker exec -it nym-data-observatory-pg /bin/bash
|
||||
# psql -U youruser -d yourdb
|
||||
|
||||
echo "Tearing down containers to have a clean slate"
|
||||
docker compose down -v
|
||||
@@ -1,79 +0,0 @@
|
||||
{
|
||||
"db": "PostgreSQL",
|
||||
"249faa11b88b749f50342bb5c9cc41d20896db543eed74a6f320c041bcbb723d": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Int4"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "INSERT INTO responses\n (joke_id, joke, date_created)\n VALUES\n ($1, $2, $3)\n ON CONFLICT(joke_id) DO UPDATE SET\n joke=excluded.joke,\n date_created=excluded.date_created;"
|
||||
},
|
||||
"aff7fbd06728004d2f2226d20c32f1482df00de2dc1d2b4debbb2e12553d997b": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "joke_id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "joke",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "date_created",
|
||||
"ordinal": 2,
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "SELECT joke_id, joke, date_created FROM responses WHERE joke_id = $1"
|
||||
},
|
||||
"e53f479f8cead3dc8aa1875e5d450ad69686cf6a109e37d6c3f0623c3e9f91d0": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "joke_id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "joke",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "date_created",
|
||||
"ordinal": 2,
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
}
|
||||
},
|
||||
"query": "SELECT joke_id, joke, date_created FROM responses"
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
use core::str;
|
||||
use serde::Deserialize;
|
||||
use tokio::process::Command;
|
||||
use tokio::task::JoinHandle;
|
||||
use tokio::time::Duration;
|
||||
|
||||
use crate::db::{self, DbPool};
|
||||
|
||||
const REFRESH_DELAY: Duration = Duration::from_secs(15);
|
||||
const FAILURE_RETRY_DELAY: Duration = Duration::from_secs(60 * 2);
|
||||
|
||||
pub(crate) async fn spawn_in_background(db_pool: DbPool) -> JoinHandle<()> {
|
||||
loop {
|
||||
tracing::info!("Running in a loop 🏃");
|
||||
|
||||
if let Err(e) = some_network_action(&db_pool).await {
|
||||
tracing::error!(
|
||||
"❌ Run failed: {e}, retrying in {}s...",
|
||||
FAILURE_RETRY_DELAY.as_secs()
|
||||
);
|
||||
tokio::time::sleep(FAILURE_RETRY_DELAY).await;
|
||||
} else {
|
||||
tracing::info!(
|
||||
"✅ Run successful, sleeping for {}s...",
|
||||
REFRESH_DELAY.as_secs()
|
||||
);
|
||||
tokio::time::sleep(REFRESH_DELAY).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub(crate) struct Response {
|
||||
#[serde(rename(deserialize = "id"))]
|
||||
pub(crate) joke_id: String,
|
||||
pub(crate) joke: String,
|
||||
#[serde(rename(deserialize = "status"))]
|
||||
pub(crate) _status: u16,
|
||||
}
|
||||
|
||||
async fn some_network_action(pool: &DbPool) -> anyhow::Result<()> {
|
||||
// for demonstration purposes only. You should use reqwest if you need it
|
||||
let output = Command::new("curl")
|
||||
.arg("-H")
|
||||
.arg("Accept: application/json")
|
||||
.arg("https://icanhazdadjoke.com/")
|
||||
.output()
|
||||
.await?;
|
||||
|
||||
if !output.status.success() {
|
||||
anyhow::bail!("Curl command failed with status: {}", output.status);
|
||||
}
|
||||
|
||||
let response_str = str::from_utf8(&output.stdout)?;
|
||||
let joke_response: Response = serde_json::from_str(response_str)?;
|
||||
|
||||
tracing::info!("{:?}", joke_response.joke);
|
||||
db::queries::insert_joke(pool, joke_response.into()).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use sqlx::{migrate::Migrator, postgres::PgConnectOptions, ConnectOptions, PgPool};
|
||||
use std::str::FromStr;
|
||||
|
||||
pub(crate) mod models;
|
||||
pub(crate) mod queries;
|
||||
|
||||
pub(crate) const DATABASE_URL_ENV_VAR: &str = "DATABASE_URL";
|
||||
|
||||
static MIGRATOR: Migrator = sqlx::migrate!("./migrations");
|
||||
|
||||
pub(crate) type DbPool = PgPool;
|
||||
|
||||
pub(crate) struct Storage {
|
||||
pool: DbPool,
|
||||
}
|
||||
|
||||
impl Storage {
|
||||
pub async fn init() -> Result<Self> {
|
||||
let connection_url = std::env::var(DATABASE_URL_ENV_VAR).map_err(anyhow::Error::from)?;
|
||||
let connect_options = {
|
||||
let mut connect_options = PgConnectOptions::from_str(&connection_url)?;
|
||||
let connect_options = connect_options.disable_statement_logging();
|
||||
(*connect_options).clone()
|
||||
};
|
||||
|
||||
let pool = DbPool::connect_with(connect_options)
|
||||
.await
|
||||
.map_err(|err| anyhow!("Failed to connect to {}: {}", &connection_url, err))?;
|
||||
|
||||
MIGRATOR.run(&pool).await?;
|
||||
|
||||
Ok(Storage { pool })
|
||||
}
|
||||
|
||||
/// Cloning pool is cheap, it's the same underlying set of connections
|
||||
pub async fn pool_owned(&self) -> DbPool {
|
||||
self.pool.clone()
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::background_task::Response;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, ToSchema)]
|
||||
pub(crate) struct JokeDto {
|
||||
pub(crate) joke_id: String,
|
||||
pub(crate) joke: String,
|
||||
pub(crate) date_created: i32,
|
||||
}
|
||||
|
||||
impl From<Response> for JokeDto {
|
||||
fn from(value: Response) -> Self {
|
||||
Self {
|
||||
joke_id: value.joke_id,
|
||||
joke: value.joke,
|
||||
// casting not smart, can implicitly panic, don't do this in prod
|
||||
date_created: chrono::offset::Utc::now().timestamp() as i32,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
use crate::db::{models::JokeDto, DbPool};
|
||||
|
||||
pub(crate) async fn insert_joke(pool: &DbPool, joke: JokeDto) -> anyhow::Result<()> {
|
||||
let mut conn = pool.acquire().await?;
|
||||
sqlx::query!(
|
||||
"INSERT INTO responses
|
||||
(joke_id, joke, date_created)
|
||||
VALUES
|
||||
($1, $2, $3)
|
||||
ON CONFLICT(joke_id) DO UPDATE SET
|
||||
joke=excluded.joke,
|
||||
date_created=excluded.date_created;",
|
||||
joke.joke_id,
|
||||
joke.joke,
|
||||
joke.date_created as i32,
|
||||
)
|
||||
.execute(&mut *conn)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn select_joke_by_id(pool: &DbPool, joke_id: &str) -> anyhow::Result<JokeDto> {
|
||||
sqlx::query_as!(
|
||||
JokeDto,
|
||||
"SELECT joke_id, joke, date_created FROM responses WHERE joke_id = $1",
|
||||
joke_id
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
.map_err(anyhow::Error::from)
|
||||
}
|
||||
|
||||
pub(crate) async fn select_all(pool: &DbPool) -> anyhow::Result<Vec<JokeDto>> {
|
||||
sqlx::query_as!(JokeDto, "SELECT joke_id, joke, date_created FROM responses",)
|
||||
.fetch_all(pool)
|
||||
.await
|
||||
.map_err(anyhow::Error::from)
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
// group queries in files by theme
|
||||
mod joke;
|
||||
|
||||
// re-exporting allows us to access all queries via `queries::bla``
|
||||
pub(crate) use joke::{insert_joke, select_all, select_joke_by_id};
|
||||
@@ -1,78 +0,0 @@
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
Json, Router,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use utoipa::IntoParams;
|
||||
|
||||
use crate::{
|
||||
db::{
|
||||
models::JokeDto,
|
||||
queries::{self, select_joke_by_id},
|
||||
},
|
||||
http::{
|
||||
error::{Error, HttpResult},
|
||||
state::AppState,
|
||||
},
|
||||
};
|
||||
|
||||
pub(crate) fn routes() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route("/", axum::routing::get(jokes))
|
||||
.route("/:joke_id", axum::routing::get(joke_by_id))
|
||||
.route("/fetch_another", axum::routing::get(fetch_another))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
tag = "Dad Jokes",
|
||||
get,
|
||||
path = "/v1/jokes",
|
||||
responses(
|
||||
(status = 200, body = Vec<JokeDto>)
|
||||
)
|
||||
)]
|
||||
async fn jokes(State(state): State<AppState>) -> HttpResult<Json<Vec<JokeDto>>> {
|
||||
queries::select_all(state.db_pool())
|
||||
.await
|
||||
.map(Json::from)
|
||||
.map_err(|_| Error::internal())
|
||||
}
|
||||
|
||||
#[derive(Deserialize, IntoParams)]
|
||||
#[into_params(parameter_in = Path)]
|
||||
struct JokeIdParam {
|
||||
joke_id: String,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
tag = "Dad Jokes",
|
||||
get,
|
||||
params(
|
||||
JokeIdParam
|
||||
),
|
||||
path = "/v1/jokes/{joke_id}",
|
||||
responses(
|
||||
(status = 200, body = JokeDto)
|
||||
)
|
||||
)]
|
||||
async fn joke_by_id(
|
||||
Path(JokeIdParam { joke_id }): Path<JokeIdParam>,
|
||||
State(state): State<AppState>,
|
||||
) -> HttpResult<Json<JokeDto>> {
|
||||
select_joke_by_id(state.db_pool(), &joke_id)
|
||||
.await
|
||||
.map(Json::from)
|
||||
.map_err(|_| Error::not_found(joke_id))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
tag = "Dad Jokes",
|
||||
get,
|
||||
path = "/v1/jokes/fetch_another",
|
||||
responses(
|
||||
(status = 200, body = String)
|
||||
)
|
||||
)]
|
||||
async fn fetch_another(State(_state): State<AppState>) -> HttpResult<Json<String>> {
|
||||
Ok(Json(String::from("Done boss, check the DB")))
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
use axum::{extract::State, Json, Router};
|
||||
|
||||
use crate::http::{error::HttpResult, state::AppState};
|
||||
|
||||
pub(crate) fn routes() -> Router<AppState> {
|
||||
Router::new().route("/", axum::routing::get(mixnodes))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
tag = "Mixnodes",
|
||||
get,
|
||||
path = "/v1/mixnodes",
|
||||
responses(
|
||||
(status = 200, body = String)
|
||||
)
|
||||
)]
|
||||
async fn mixnodes(State(_state): State<AppState>) -> HttpResult<Json<serde_json::Value>> {
|
||||
Ok(Json(
|
||||
serde_json::json!({"message": "😎 Nothing to see here, move along 😎"}),
|
||||
))
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
use anyhow::anyhow;
|
||||
use axum::{response::Redirect, Router};
|
||||
use tokio::net::ToSocketAddrs;
|
||||
use tower_http::{cors::CorsLayer, trace::TraceLayer};
|
||||
use utoipa::OpenApi;
|
||||
use utoipa_swagger_ui::SwaggerUi;
|
||||
|
||||
use crate::http::{api_docs, server::HttpServer, state::AppState};
|
||||
|
||||
pub(crate) mod jokes;
|
||||
pub(crate) mod mixnodes;
|
||||
|
||||
pub(crate) struct RouterBuilder {
|
||||
unfinished_router: Router<AppState>,
|
||||
}
|
||||
|
||||
impl RouterBuilder {
|
||||
pub(crate) fn with_default_routes() -> Self {
|
||||
let router = Router::new()
|
||||
.merge(
|
||||
SwaggerUi::new("/swagger")
|
||||
.url("/api-docs/openapi.json", api_docs::ApiDoc::openapi()),
|
||||
)
|
||||
.route(
|
||||
"/",
|
||||
axum::routing::get(|| async { Redirect::permanent("/swagger") }),
|
||||
)
|
||||
.nest(
|
||||
"/v1",
|
||||
Router::new()
|
||||
.nest("/jokes", jokes::routes())
|
||||
.nest("/mixnodes", mixnodes::routes()),
|
||||
);
|
||||
|
||||
Self {
|
||||
unfinished_router: router,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn with_state(self, state: AppState) -> RouterWithState {
|
||||
RouterWithState {
|
||||
router: self.finalize_routes().with_state(state),
|
||||
}
|
||||
}
|
||||
|
||||
fn finalize_routes(self) -> Router<AppState> {
|
||||
// layers added later wrap earlier layers
|
||||
self.unfinished_router
|
||||
// CORS layer needs to wrap other API layers
|
||||
.layer(setup_cors())
|
||||
// logger should be outermost layer
|
||||
.layer(TraceLayer::new_for_http())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RouterWithState {
|
||||
router: Router,
|
||||
}
|
||||
|
||||
impl RouterWithState {
|
||||
pub(crate) async fn build_server<A: ToSocketAddrs>(
|
||||
self,
|
||||
bind_address: A,
|
||||
) -> anyhow::Result<HttpServer> {
|
||||
tokio::net::TcpListener::bind(bind_address)
|
||||
.await
|
||||
.map(|listener| HttpServer::new(self.router, listener))
|
||||
.map_err(|err| anyhow!("Couldn't bind to address due to {}", err))
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_cors() -> CorsLayer {
|
||||
use axum::http::Method;
|
||||
CorsLayer::new()
|
||||
.allow_origin(tower_http::cors::Any)
|
||||
.allow_methods([Method::POST, Method::GET, Method::PATCH, Method::OPTIONS])
|
||||
.allow_headers(tower_http::cors::Any)
|
||||
.allow_credentials(false)
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
use axum::Router;
|
||||
use core::net::SocketAddr;
|
||||
use tokio::{net::TcpListener, task::JoinHandle};
|
||||
use tokio_util::sync::{CancellationToken, WaitForCancellationFutureOwned};
|
||||
|
||||
use crate::{
|
||||
db::DbPool,
|
||||
http::{api::RouterBuilder, state::AppState},
|
||||
};
|
||||
|
||||
/// Return handles that allow for graceful shutdown of server + awaiting its
|
||||
/// background tokio task
|
||||
pub(crate) async fn start_http_api(
|
||||
db_pool: DbPool,
|
||||
http_port: u16,
|
||||
nym_http_cache_ttl: u64,
|
||||
) -> anyhow::Result<ShutdownHandles> {
|
||||
let router_builder = RouterBuilder::with_default_routes();
|
||||
|
||||
let state = AppState::new(db_pool, nym_http_cache_ttl);
|
||||
let router = router_builder.with_state(state);
|
||||
|
||||
let bind_addr = format!("0.0.0.0:{}", http_port);
|
||||
let server = router.build_server(bind_addr).await?;
|
||||
|
||||
Ok(start_server(server))
|
||||
}
|
||||
|
||||
fn start_server(server: HttpServer) -> ShutdownHandles {
|
||||
// one copy is stored to trigger a graceful shutdown later
|
||||
let shutdown_button = CancellationToken::new();
|
||||
// other copy is given to server to listen for a shutdown
|
||||
let shutdown_receiver = shutdown_button.clone();
|
||||
let shutdown_receiver = shutdown_receiver.cancelled_owned();
|
||||
|
||||
let server_handle = tokio::spawn(async move { server.run(shutdown_receiver).await });
|
||||
|
||||
ShutdownHandles {
|
||||
server_handle,
|
||||
shutdown_button,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ShutdownHandles {
|
||||
server_handle: JoinHandle<std::io::Result<()>>,
|
||||
shutdown_button: CancellationToken,
|
||||
}
|
||||
|
||||
impl ShutdownHandles {
|
||||
/// Send graceful shutdown signal to server and wait for server task to complete
|
||||
pub(crate) async fn shutdown(self) -> anyhow::Result<()> {
|
||||
self.shutdown_button.cancel();
|
||||
|
||||
match self.server_handle.await {
|
||||
Ok(Ok(_)) => {
|
||||
tracing::info!("HTTP server shut down without errors");
|
||||
}
|
||||
Ok(Err(err)) => {
|
||||
tracing::error!("HTTP server terminated with: {err}");
|
||||
anyhow::bail!(err)
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!("Server task panicked: {err}");
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct HttpServer {
|
||||
router: Router,
|
||||
listener: TcpListener,
|
||||
}
|
||||
|
||||
impl HttpServer {
|
||||
pub(crate) fn new(router: Router, listener: TcpListener) -> Self {
|
||||
Self { router, listener }
|
||||
}
|
||||
|
||||
pub(crate) async fn run(self, receiver: WaitForCancellationFutureOwned) -> std::io::Result<()> {
|
||||
// into_make_service_with_connect_info allows us to see client ip address
|
||||
axum::serve(
|
||||
self.listener,
|
||||
self.router
|
||||
.into_make_service_with_connect_info::<SocketAddr>(),
|
||||
)
|
||||
.with_graceful_shutdown(receiver)
|
||||
.await
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
use utoipa::OpenApi;
|
||||
use utoipauto::utoipauto;
|
||||
|
||||
// manually import external structs which are behind feature flags because they
|
||||
// can't be automatically discovered
|
||||
// https://github.com/ProbablyClem/utoipauto/issues/13#issuecomment-1974911829
|
||||
#[utoipauto(paths = "./nym-data-observatory/src")]
|
||||
#[derive(OpenApi)]
|
||||
#[openapi(info(title = "Nym API"), tags(), components(schemas()))]
|
||||
pub(super) struct ApiDoc;
|
||||
@@ -1,23 +0,0 @@
|
||||
use crate::read_env_var;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Config {
|
||||
http_port: u16,
|
||||
}
|
||||
|
||||
const HTTP_PORT_DEFAULT: u16 = 8000;
|
||||
|
||||
impl Config {
|
||||
pub(crate) fn from_env() -> Self {
|
||||
Self {
|
||||
http_port: read_env_var("HTTP_PORT")
|
||||
.unwrap_or(HTTP_PORT_DEFAULT.to_string())
|
||||
.parse()
|
||||
.unwrap_or(HTTP_PORT_DEFAULT),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn http_port(&self) -> u16 {
|
||||
self.http_port
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
pub(crate) type HttpResult<T> = Result<T, Error>;
|
||||
|
||||
pub(crate) struct Error {
|
||||
message: String,
|
||||
status: axum::http::StatusCode,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub(crate) fn not_found(message: String) -> Self {
|
||||
Self {
|
||||
message,
|
||||
status: axum::http::StatusCode::NOT_FOUND,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn internal() -> Self {
|
||||
Self {
|
||||
message: String::from("Internal server error"),
|
||||
status: axum::http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl axum::response::IntoResponse for Error {
|
||||
fn into_response(self) -> axum::response::Response {
|
||||
(self.status, self.message).into_response()
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
pub(crate) mod api;
|
||||
pub(crate) mod api_docs;
|
||||
pub(crate) mod config;
|
||||
pub(crate) mod error;
|
||||
pub(crate) mod server;
|
||||
pub(crate) mod state;
|
||||
@@ -1,91 +0,0 @@
|
||||
use axum::Router;
|
||||
use core::net::SocketAddr;
|
||||
use tokio::{net::TcpListener, task::JoinHandle};
|
||||
use tokio_util::sync::{CancellationToken, WaitForCancellationFutureOwned};
|
||||
|
||||
use crate::{
|
||||
db::DbPool,
|
||||
http::{api::RouterBuilder, state::AppState},
|
||||
};
|
||||
|
||||
/// Return handles that allow for graceful shutdown of server + awaiting its
|
||||
/// background tokio task
|
||||
pub(crate) async fn start_http_api(
|
||||
db_pool: DbPool,
|
||||
http_port: u16,
|
||||
) -> anyhow::Result<ShutdownHandles> {
|
||||
let router_builder = RouterBuilder::with_default_routes();
|
||||
|
||||
let state = AppState::new(db_pool);
|
||||
let router = router_builder.with_state(state);
|
||||
|
||||
let bind_addr = format!("0.0.0.0:{}", http_port);
|
||||
let server = router.build_server(bind_addr).await?;
|
||||
|
||||
Ok(start_server(server))
|
||||
}
|
||||
|
||||
fn start_server(server: HttpServer) -> ShutdownHandles {
|
||||
// one copy is stored to trigger a graceful shutdown later
|
||||
let shutdown_button = CancellationToken::new();
|
||||
// other copy is given to server to listen for a shutdown
|
||||
let shutdown_receiver = shutdown_button.clone();
|
||||
let shutdown_receiver = shutdown_receiver.cancelled_owned();
|
||||
|
||||
let server_handle = tokio::spawn(async move { server.run(shutdown_receiver).await });
|
||||
|
||||
ShutdownHandles {
|
||||
server_handle,
|
||||
shutdown_button,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ShutdownHandles {
|
||||
server_handle: JoinHandle<std::io::Result<()>>,
|
||||
shutdown_button: CancellationToken,
|
||||
}
|
||||
|
||||
impl ShutdownHandles {
|
||||
/// Send graceful shutdown signal to server and wait for server task to complete
|
||||
pub(crate) async fn shutdown(self) -> anyhow::Result<()> {
|
||||
self.shutdown_button.cancel();
|
||||
|
||||
match self.server_handle.await {
|
||||
Ok(Ok(_)) => {
|
||||
tracing::info!("HTTP server shut down without errors");
|
||||
}
|
||||
Ok(Err(err)) => {
|
||||
tracing::error!("HTTP server terminated with: {err}");
|
||||
anyhow::bail!(err)
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!("Server task panicked: {err}");
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct HttpServer {
|
||||
router: Router,
|
||||
listener: TcpListener,
|
||||
}
|
||||
|
||||
impl HttpServer {
|
||||
pub(crate) fn new(router: Router, listener: TcpListener) -> Self {
|
||||
Self { router, listener }
|
||||
}
|
||||
|
||||
pub(crate) async fn run(self, receiver: WaitForCancellationFutureOwned) -> std::io::Result<()> {
|
||||
// into_make_service_with_connect_info allows us to see client ip address
|
||||
// in middleware, for logging, TLS, routing etc.
|
||||
axum::serve(
|
||||
self.listener,
|
||||
self.router
|
||||
.into_make_service_with_connect_info::<SocketAddr>(),
|
||||
)
|
||||
.with_graceful_shutdown(receiver)
|
||||
.await
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
use crate::db::DbPool;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct AppState {
|
||||
db_pool: DbPool,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
pub(crate) fn new(db_pool: DbPool) -> Self {
|
||||
Self { db_pool }
|
||||
}
|
||||
|
||||
pub(crate) fn db_pool(&self) -> &DbPool {
|
||||
&self.db_pool
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
use tracing::level_filters::LevelFilter;
|
||||
use tracing_subscriber::{filter::Directive, EnvFilter};
|
||||
|
||||
pub(crate) fn setup_tracing_logger() {
|
||||
fn directive_checked(directive: String) -> Directive {
|
||||
directive.parse().expect("Failed to parse log directive")
|
||||
}
|
||||
|
||||
let log_builder = tracing_subscriber::fmt()
|
||||
// Use a more compact, abbreviated log format
|
||||
.compact()
|
||||
// Display source code file paths
|
||||
.with_file(true)
|
||||
// Display source code line numbers
|
||||
.with_line_number(true)
|
||||
// Don't display the event's target (module path)
|
||||
.with_target(false);
|
||||
|
||||
let mut filter = EnvFilter::builder()
|
||||
// if RUST_LOG isn't set, set default level
|
||||
.with_default_directive(LevelFilter::INFO.into())
|
||||
.from_env_lossy();
|
||||
// these crates are more granularly filtered
|
||||
let filter_crates = [
|
||||
"nym_bin_common",
|
||||
"nym_explorer_client",
|
||||
"nym_network_defaults",
|
||||
"nym_validator_client",
|
||||
"reqwest",
|
||||
"rustls",
|
||||
"hyper",
|
||||
"sqlx",
|
||||
"h2",
|
||||
"tendermint_rpc",
|
||||
"tower_http",
|
||||
"axum",
|
||||
];
|
||||
for crate_name in filter_crates {
|
||||
filter = filter.add_directive(directive_checked(format!("{}=warn", crate_name)));
|
||||
}
|
||||
|
||||
log_builder.with_env_filter(filter).init();
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
use nym_network_defaults::setup_env;
|
||||
use nym_task::signal::wait_for_signal;
|
||||
|
||||
use crate::http::config;
|
||||
|
||||
mod background_task;
|
||||
mod db;
|
||||
mod http;
|
||||
mod logging;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
logging::setup_tracing_logger();
|
||||
|
||||
// if dotenv file is present, load its values
|
||||
// otherwise, default to mainnet
|
||||
setup_env(Some(".env"));
|
||||
|
||||
let conf = config::Config::from_env();
|
||||
tracing::debug!("Using config:\n{:?}", conf);
|
||||
|
||||
let storage = db::Storage::init().await?;
|
||||
let db_pool = storage.pool_owned().await;
|
||||
tokio::spawn(async move {
|
||||
background_task::spawn_in_background(db_pool).await;
|
||||
tracing::info!("Started task");
|
||||
});
|
||||
|
||||
let shutdown_handles =
|
||||
http::server::start_http_api(storage.pool_owned().await, conf.http_port())
|
||||
.await
|
||||
.expect("Failed to start server");
|
||||
tracing::info!("Started HTTP server on port {}", conf.http_port());
|
||||
|
||||
wait_for_signal().await;
|
||||
|
||||
if let Err(err) = shutdown_handles.shutdown().await {
|
||||
tracing::error!("{err}");
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO dz move this to common
|
||||
fn read_env_var(env_var: &str) -> anyhow::Result<String> {
|
||||
std::env::var(env_var)
|
||||
.map_err(|_| anyhow::anyhow!("You need to set {}", env_var))
|
||||
.map(|value| {
|
||||
tracing::trace!("{}={}", env_var, value);
|
||||
value
|
||||
})
|
||||
}
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-node"
|
||||
version = "1.1.8"
|
||||
version = "1.1.7"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
|
||||
@@ -13,8 +13,6 @@ use url::Url;
|
||||
|
||||
pub mod build_info;
|
||||
pub mod init;
|
||||
pub mod process_block;
|
||||
pub mod process_until;
|
||||
pub mod run;
|
||||
pub mod upgrade_helpers;
|
||||
|
||||
@@ -44,8 +42,6 @@ impl Cli {
|
||||
match self.command {
|
||||
Commands::Init(args) => init::execute(args),
|
||||
Commands::Run(args) => run::execute(args).await,
|
||||
Commands::ProcessBlock(args) => process_block::execute(args).await,
|
||||
Commands::ProcessUntil(args) => process_until::execute(args).await,
|
||||
Commands::BuildInfo(args) => build_info::execute(args),
|
||||
}
|
||||
}
|
||||
@@ -101,12 +97,6 @@ pub(crate) enum Commands {
|
||||
/// Run the validator rewarder with the preconfigured settings.
|
||||
Run(run::Args),
|
||||
|
||||
/// Attempt to process a single block.
|
||||
ProcessBlock(process_block::Args),
|
||||
|
||||
/// Attempt to process multiple blocks until the provided height.
|
||||
ProcessUntil(process_until::Args),
|
||||
|
||||
/// Show build information of this binary
|
||||
BuildInfo(build_info::Args),
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::cli::{try_load_current_config, ConfigOverridableArgs};
|
||||
use crate::error::NymRewarderError;
|
||||
use nyxd_scraper::NyxdScraper;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, clap::Args)]
|
||||
pub struct Args {
|
||||
#[command(flatten)]
|
||||
config_override: ConfigOverridableArgs,
|
||||
|
||||
/// Height of the block we want to process
|
||||
#[clap(long)]
|
||||
height: u32,
|
||||
|
||||
/// Specifies custom location for the configuration file of nym validators rewarder.
|
||||
#[clap(long)]
|
||||
custom_config_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: Args) -> Result<(), NymRewarderError> {
|
||||
let config =
|
||||
try_load_current_config(&args.custom_config_path)?.with_override(args.config_override);
|
||||
|
||||
NyxdScraper::new(config.scraper_config())
|
||||
.await?
|
||||
.process_single_block(args.height)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::cli::{try_load_current_config, ConfigOverridableArgs};
|
||||
use crate::error::NymRewarderError;
|
||||
use nyxd_scraper::NyxdScraper;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, clap::Args)]
|
||||
pub struct Args {
|
||||
#[command(flatten)]
|
||||
config_override: ConfigOverridableArgs,
|
||||
|
||||
/// Optional starting height for processing the blocks.
|
||||
/// If none is provided, the default behaviour will be applied.
|
||||
#[clap(long)]
|
||||
start_height: Option<u32>,
|
||||
|
||||
/// Height of until we want to be processing the blocks.
|
||||
/// If none is provided, the currrent block height will be used
|
||||
#[clap(long)]
|
||||
stop_height: Option<u32>,
|
||||
|
||||
/// Specifies custom location for the configuration file of nym validators rewarder.
|
||||
#[clap(long)]
|
||||
custom_config_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: Args) -> Result<(), NymRewarderError> {
|
||||
if let (Some(start), Some(end)) = (args.start_height, args.stop_height) {
|
||||
if start > end {
|
||||
eprintln!("the start height can't be larger than the stop height!");
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let config =
|
||||
try_load_current_config(&args.custom_config_path)?.with_override(args.config_override);
|
||||
|
||||
NyxdScraper::new(config.scraper_config())
|
||||
.await?
|
||||
.process_block_range(args.start_height, args.stop_height)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::config::RewardingRatios;
|
||||
use crate::rewarder::Epoch;
|
||||
use nym_compact_ecash::error::CompactEcashError;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_validator_client::nym_api::error::NymAPIError;
|
||||
@@ -179,9 +178,6 @@ pub enum NymRewarderError {
|
||||
|
||||
#[error("pruning.keep_recent must not be smaller than {min_to_keep}. got: {keep_recent}")]
|
||||
TooSmallKeepRecent { min_to_keep: u32, keep_recent: u32 },
|
||||
|
||||
#[error("there were no blocks processed within the epoch {epoch}")]
|
||||
NoBlocksProcessedInEpoch { epoch: Epoch },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -14,7 +14,7 @@ use nym_network_defaults::setup_env;
|
||||
pub mod cli;
|
||||
pub mod config;
|
||||
pub mod error;
|
||||
pub mod rewarder;
|
||||
mod rewarder;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
|
||||
@@ -49,12 +49,7 @@ impl EpochSigning {
|
||||
) -> Result<Vec<staking::Validator>, NymRewarderError> {
|
||||
// first attempt to get it via the historical info.
|
||||
// if that fails, attempt to use current block information to at least get **something**
|
||||
if let Ok(Some(validators)) = self
|
||||
.nyxd_client
|
||||
.historical_info(height)
|
||||
.await
|
||||
.map(|v| v.hist)
|
||||
{
|
||||
if let Some(validators) = self.nyxd_client.historical_info(height).await?.hist {
|
||||
Ok(validators.valset)
|
||||
} else {
|
||||
let mut page_request = None;
|
||||
@@ -68,10 +63,6 @@ impl EpochSigning {
|
||||
break;
|
||||
};
|
||||
|
||||
if pagination.next_key.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
page_request = Some(PageRequest {
|
||||
key: pagination.next_key,
|
||||
offset: 0,
|
||||
@@ -101,28 +92,18 @@ impl EpochSigning {
|
||||
|
||||
let epoch_start = current_epoch.start_time;
|
||||
let epoch_end = current_epoch.end_time;
|
||||
|
||||
let Some(first_block) = self
|
||||
let first_block = self
|
||||
.nyxd_scraper
|
||||
.storage
|
||||
.get_first_block_height_after(epoch_start)
|
||||
.await?
|
||||
else {
|
||||
return Err(NymRewarderError::NoBlocksProcessedInEpoch {
|
||||
epoch: current_epoch,
|
||||
});
|
||||
};
|
||||
|
||||
let Some(last_block) = self
|
||||
.unwrap_or_default();
|
||||
let last_block = self
|
||||
.nyxd_scraper
|
||||
.storage
|
||||
.get_last_block_height_before(epoch_end)
|
||||
.await?
|
||||
else {
|
||||
return Err(NymRewarderError::NoBlocksProcessedInEpoch {
|
||||
epoch: current_epoch,
|
||||
});
|
||||
};
|
||||
.unwrap_or_default();
|
||||
|
||||
// each validator MUST be online at some point during the first 20 blocks, otherwise they're not getting anything.
|
||||
let vp_range_end = min(first_block + 20, last_block);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
use crate::error::NymRewarderError;
|
||||
use sqlx::FromRow;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::ops::Add;
|
||||
use std::time::Duration;
|
||||
use time::format_description::well_known::Rfc3339;
|
||||
@@ -35,11 +34,6 @@ impl Epoch {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn has_finished(&self) -> bool {
|
||||
let now = OffsetDateTime::now_utc();
|
||||
self.end_time < now
|
||||
}
|
||||
|
||||
pub fn until_end(&self) -> Duration {
|
||||
let now = OffsetDateTime::now_utc();
|
||||
(self.end_time - now).try_into().unwrap_or_default()
|
||||
@@ -66,15 +60,3 @@ impl Epoch {
|
||||
self.end_time.format(&Rfc3339).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Epoch {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}: {} - {}",
|
||||
self.id,
|
||||
self.start_rfc3339(),
|
||||
self.end_rfc3339()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::rewarder::block_signing::types::EpochSigningResults;
|
||||
use crate::rewarder::block_signing::EpochSigning;
|
||||
use crate::rewarder::credential_issuance::types::CredentialIssuanceResults;
|
||||
use crate::rewarder::credential_issuance::CredentialIssuance;
|
||||
use crate::rewarder::epoch::Epoch;
|
||||
use crate::rewarder::nyxd_client::NyxdClient;
|
||||
use crate::rewarder::storage::RewarderStorage;
|
||||
use futures::future::{FusedFuture, OptionFuture};
|
||||
@@ -27,8 +28,6 @@ mod nyxd_client;
|
||||
mod storage;
|
||||
mod tasks;
|
||||
|
||||
pub(crate) use crate::rewarder::epoch::Epoch;
|
||||
|
||||
pub struct RewardingResult {
|
||||
pub total_spent: Coin,
|
||||
pub rewarding_tx: Hash,
|
||||
@@ -48,30 +47,22 @@ impl EpochRewards {
|
||||
pub fn amounts(&self) -> Result<Vec<(AccountId, Vec<Coin>)>, NymRewarderError> {
|
||||
let mut amounts = Vec::new();
|
||||
|
||||
match &self.signing {
|
||||
Ok(Some(signing)) => {
|
||||
for (account, signing_amount) in signing.rewarding_amounts(&self.signing_budget) {
|
||||
if signing_amount[0].amount != 0 {
|
||||
amounts.push((account, signing_amount))
|
||||
}
|
||||
if let Ok(Some(signing)) = &self.signing {
|
||||
for (account, signing_amount) in signing.rewarding_amounts(&self.signing_budget) {
|
||||
if signing_amount[0].amount != 0 {
|
||||
amounts.push((account, signing_amount))
|
||||
}
|
||||
}
|
||||
Err(err) => error!("failed to determine rewards for block signing: {err}"),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
match &self.credentials {
|
||||
Ok(Some(credentials)) => {
|
||||
for (account, credential_amount) in
|
||||
credentials.rewarding_amounts(&self.credentials_budget)
|
||||
{
|
||||
if credential_amount[0].amount != 0 {
|
||||
amounts.push((account, credential_amount))
|
||||
}
|
||||
if let Ok(Some(credentials)) = &self.credentials {
|
||||
for (account, credential_amount) in
|
||||
credentials.rewarding_amounts(&self.credentials_budget)
|
||||
{
|
||||
if credential_amount[0].amount != 0 {
|
||||
amounts.push((account, credential_amount))
|
||||
}
|
||||
}
|
||||
Err(err) => error!("failed to determine rewards for credential issuance: {err}"),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
Ok(amounts)
|
||||
@@ -288,58 +279,6 @@ impl Rewarder {
|
||||
self.current_epoch = self.current_epoch.next();
|
||||
}
|
||||
|
||||
async fn ensure_has_epoch_blocks(&self) -> Result<(), NymRewarderError> {
|
||||
// make sure we at least have a single block processed within the epoch
|
||||
let epoch_start = self.current_epoch.start_time;
|
||||
let epoch_end = self.current_epoch.end_time;
|
||||
|
||||
if let Some(epoch_signing) = &self.epoch_signing {
|
||||
if epoch_signing
|
||||
.nyxd_scraper
|
||||
.storage
|
||||
.get_first_block_height_after(epoch_start)
|
||||
.await?
|
||||
.is_none()
|
||||
{
|
||||
return Err(NymRewarderError::NoBlocksProcessedInEpoch {
|
||||
epoch: self.current_epoch,
|
||||
});
|
||||
}
|
||||
|
||||
if epoch_signing
|
||||
.nyxd_scraper
|
||||
.storage
|
||||
.get_last_block_height_before(epoch_end)
|
||||
.await?
|
||||
.is_none()
|
||||
{
|
||||
return Err(NymRewarderError::NoBlocksProcessedInEpoch {
|
||||
epoch: self.current_epoch,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn startup_resync(&mut self) -> Result<(), NymRewarderError> {
|
||||
// no sync required
|
||||
if !self.current_epoch.has_finished() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
info!("attempting to distribute missed rewards");
|
||||
while self.current_epoch.has_finished() {
|
||||
info!("processing epoch {}", self.current_epoch);
|
||||
self.ensure_has_epoch_blocks().await?;
|
||||
|
||||
// we need to perform rewarding from the 'current' epoch until the actual current epoch
|
||||
self.handle_epoch_end().await
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn run(mut self) -> Result<(), NymRewarderError> {
|
||||
info!("Starting nym validators rewarder");
|
||||
|
||||
@@ -367,20 +306,6 @@ impl Rewarder {
|
||||
|
||||
let until_end = self.current_epoch.until_end();
|
||||
|
||||
if let Err(err) = self.startup_resync().await {
|
||||
error!("failed to perform startup sync: {err}");
|
||||
error!("if the failure was due to insufficient number of blocks, your course of action is as follows:");
|
||||
error!("(ideally it would have been automatically resolved in this very method, but that'd require some serious refactoring)");
|
||||
error!(
|
||||
"1. determine height of the first block of the epoch (doesn't have to be exact)"
|
||||
);
|
||||
error!("2. run the following subcommand of the rewarder: `nym-validator-rewarder process-until --start-height=$STARTING_BLOCK");
|
||||
error!("3. !!IMPORTANT!! go to config.toml and temporarily disable block pruning, i.e. `pruning.strategy=nothing`");
|
||||
error!("4. restart nym-validator-rewarder as normal until it sends missing rewards");
|
||||
error!("5. re-enable pruning and restart the nym-validator rewarder");
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
info!(
|
||||
"the initial epoch (id: {}) will finish in {} secs",
|
||||
self.current_epoch.id,
|
||||
|
||||
Generated
+55
-43
@@ -538,7 +538,7 @@ checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"cargo-platform",
|
||||
"semver",
|
||||
"semver 1.0.22",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
@@ -1141,7 +1141,7 @@ dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw2",
|
||||
"schemars",
|
||||
"semver",
|
||||
"semver 1.0.22",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
@@ -1156,7 +1156,7 @@ dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"schemars",
|
||||
"semver",
|
||||
"semver 1.0.22",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
@@ -2158,7 +2158,7 @@ dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"http 1.1.0",
|
||||
"indexmap 2.5.0",
|
||||
"indexmap 2.0.0",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
@@ -2190,9 +2190,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
@@ -2559,12 +2559,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.5.0"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
|
||||
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.5",
|
||||
"hashbrown 0.14.0",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@@ -3115,7 +3115,7 @@ dependencies = [
|
||||
"log",
|
||||
"pretty_env_logger",
|
||||
"schemars",
|
||||
"semver",
|
||||
"semver 0.11.0",
|
||||
"serde",
|
||||
"utoipa",
|
||||
"vergen",
|
||||
@@ -3173,7 +3173,7 @@ dependencies = [
|
||||
"log",
|
||||
"nym-network-defaults",
|
||||
"serde",
|
||||
"toml 0.8.19",
|
||||
"toml 0.7.6",
|
||||
"url",
|
||||
]
|
||||
|
||||
@@ -3517,9 +3517,12 @@ dependencies = [
|
||||
"base64 0.22.1",
|
||||
"log",
|
||||
"nym-config",
|
||||
"nym-crypto",
|
||||
"nym-network-defaults",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"utoipa",
|
||||
"x25519-dalek",
|
||||
]
|
||||
|
||||
@@ -4552,7 +4555,7 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
dependencies = [
|
||||
"semver",
|
||||
"semver 1.0.22",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4795,13 +4798,31 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.23"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
||||
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
|
||||
dependencies = [
|
||||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
|
||||
dependencies = [
|
||||
"pest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.210"
|
||||
@@ -4876,9 +4897,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.8"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
|
||||
checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -5429,7 +5450,7 @@ dependencies = [
|
||||
"raw-window-handle",
|
||||
"regex",
|
||||
"rfd",
|
||||
"semver",
|
||||
"semver 1.0.22",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
@@ -5462,7 +5483,7 @@ dependencies = [
|
||||
"cargo_toml",
|
||||
"heck 0.4.1",
|
||||
"json-patch",
|
||||
"semver",
|
||||
"semver 1.0.22",
|
||||
"serde_json",
|
||||
"tauri-utils",
|
||||
"winres",
|
||||
@@ -5483,7 +5504,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"semver",
|
||||
"semver 1.0.22",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.8",
|
||||
@@ -5566,7 +5587,7 @@ dependencies = [
|
||||
"phf 0.10.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"semver",
|
||||
"semver 1.0.22",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
@@ -5661,7 +5682,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tendermint 0.37.0",
|
||||
"toml 0.8.19",
|
||||
"toml 0.8.2",
|
||||
"url",
|
||||
]
|
||||
|
||||
@@ -5714,7 +5735,7 @@ dependencies = [
|
||||
"pin-project",
|
||||
"rand 0.8.5",
|
||||
"reqwest 0.11.22",
|
||||
"semver",
|
||||
"semver 1.0.22",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"serde_json",
|
||||
@@ -5933,21 +5954,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.19"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
|
||||
checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit 0.22.22",
|
||||
"toml_edit 0.20.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.8"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -5958,24 +5979,24 @@ version = "0.19.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a"
|
||||
dependencies = [
|
||||
"indexmap 2.5.0",
|
||||
"indexmap 2.0.0",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow 0.5.10",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.22"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
|
||||
checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
|
||||
dependencies = [
|
||||
"indexmap 2.5.0",
|
||||
"indexmap 2.0.0",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow 0.6.20",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6192,7 +6213,7 @@ version = "4.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5afb1a60e207dca502682537fefcfd9921e71d0b83e9576060f09abc6efab23"
|
||||
dependencies = [
|
||||
"indexmap 2.5.0",
|
||||
"indexmap 2.0.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"utoipa-gen",
|
||||
@@ -6849,15 +6870,6 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.50.0"
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
[package]
|
||||
name = "nym-cpp-ffi"
|
||||
version = "0.1.2"
|
||||
version = "0.1.1"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "nym_cpp_ffi"
|
||||
@@ -12,12 +11,13 @@ crate-type = ["cdylib"]
|
||||
# Async runtime
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
# Nym clients, addressing, packet format, common tools (logging), ffi shared
|
||||
nym-sdk = { path = "../../rust/nym-sdk/" }
|
||||
nym-bin-common = { path = "../../../common/bin-common" }
|
||||
nym-sphinx-anonymous-replies = { path = "../../../common/nymsphinx/anonymous-replies" }
|
||||
nym-sdk = { git = "https://github.com/nymtech/nym", branch = "master" }
|
||||
nym-bin-common = { git = "https://github.com/nymtech/nym", branch = "master" }
|
||||
nym-sphinx-anonymous-replies = { git = "https://github.com/nymtech/nym", branch = "master" }
|
||||
nym-ffi-shared = { path = "../shared" }
|
||||
lazy_static = "1.4.0"
|
||||
# error handling
|
||||
anyhow = "1.0.75"
|
||||
# base58 en/decoding
|
||||
bs58 = "0.5.0"
|
||||
|
||||
|
||||
+23
-23
@@ -1,46 +1,46 @@
|
||||
# C++ FFI
|
||||
> ⚠️ This is an initial version of this library in order to give developers something to experiment with. If you use this code to begin testing out Mixnet integration and run into issues, errors, or have feedback, please feel free to open an issue; feedback from developers trying to use it will help us improve it. If you have questions feel free to reach out via our [Matrix channel](https://matrix.to/#/#dev:nymtech.chat).
|
||||
# C++ FFI
|
||||
> ⚠️ This is an initial version of this library in order to give developers something to experiment with. If you use this code to begin testing out Mixnet integration and run into issues, errors, or have feedback, please feel free to open an issue; feedback from developers trying to use it will help us improve it. If you have questions feel free to reach out via our [Matrix channel](https://matrix.to/#/#dev:nymtech.chat).
|
||||
|
||||
This repo contains:
|
||||
* `lib.rs`: an initial version of bindings for interacting with the Mixnet via the Rust SDK from C++. These are essentially match statements wrapping imported functions from the `nym-ffi-shared` lib allowing for nicer [error handling](#error-handling-).
|
||||
* `main.cpp`: an example of using this library, relying on `Boost` for threads.
|
||||
* `lib.rs`: an initial version of bindings for interacting with the Mixnet via the Rust SDK from C++. These are essentially match statements wrapping imported functions from the `nym-ffi-shared` lib allowing for nicer [error handling](#error-handling-).
|
||||
* `main.cpp`: an example of using this library, relying on `Boost` for threads.
|
||||
|
||||
The example `.cpp` file is a simple example flow of:
|
||||
* setting up Nym client logging
|
||||
The example `.cpp` file is a simple example flow of:
|
||||
* setting up Nym client logging
|
||||
* creating an ephemeral Nym client (no key storage / persistent address - this will come in a future iteration)
|
||||
* getting its [Nym address](https://nymtech.net/docs/clients/addressing-system.html)
|
||||
* using that address to send a message to yourself via the Mixnet
|
||||
* using that address to send a message to yourself via the Mixnet
|
||||
* listen for and parse the incoming message for the `sender_tag` used for [anonymous replies with SURBs](https://nymtech.net/docs/architecture/traffic-flow.html#private-replies-using-surbs)
|
||||
* send a reply to yourself using SURBs
|
||||
|
||||
> Unlike the Go FFI code, this code does not yet have bindings for the TcpProxyClient/Server. This will happen in the future.
|
||||
|
||||
## Installation
|
||||
Prerequisites:
|
||||
## Installation
|
||||
Prerequisites:
|
||||
* Rust
|
||||
* C++
|
||||
* C++
|
||||
* [Boost](https://www.boost.org/) which can be installed with:
|
||||
```
|
||||
# Arch / Manjaro
|
||||
yay -S boost boost-libs
|
||||
# Arch / Manjaro
|
||||
yay -S boost boost-libs
|
||||
|
||||
# Debian / Ubuntu
|
||||
# Debian / Ubuntu
|
||||
sudo apt install libboost-all-dev
|
||||
```
|
||||
|
||||
## Usage
|
||||
The `build.sh` script in the root of the repository speeds up the task of building and linking the Rust and C++ code.
|
||||
* if want to quickly recompile your code run it as-is with `./build.sh`
|
||||
* if you want to clean build both the Rust and C++ code after removing existing compiled binaries run it with the optional `clean` argument: `./build.sh clean`.
|
||||
The `build.sh` script in the root of the repository speeds up the task of building and linking the Rust and C++ code.
|
||||
* if want to quickly recompile your code run it as-is with `./build.sh`
|
||||
* if you want to clean build both the Rust and C++ code after removing existing compiled binaries run it with the optional `clean` argument: `./build.sh clean`.
|
||||
|
||||
> Make sure to run the script from the root of the project directory.
|
||||
|
||||
> Make sure to run the script from the root of the project directory.
|
||||
|
||||
This script will:
|
||||
This script will:
|
||||
* (optionally if called with `clean` argument) remove existing Rust and C++ artifacts
|
||||
* build `lib.rs` with the `--release` flag
|
||||
* compile `main.cpp`, linking `lib.rs`
|
||||
* compile `main.cpp`, linking `lib.rs`
|
||||
* set value of `LD_LIBRARY_PATH` to the Rust code in `target/release/`
|
||||
* run the compiled `main`
|
||||
|
||||
## Error Handling
|
||||
## Error Handling
|
||||
When calling a function across the FFI boundary (e.g.) `reply`, the Rust code is matching the output of an `_internal` function - `Res` or `Err` - to a member of the `StatusCode` enum. This allows for both Rust-style error handling and the ease of returning a `c_int` across the FFI boundary, which can be used by C++ for its own error handling / conditional logic.
|
||||
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ build_artifacts_and_link() {
|
||||
cargo build --release &&
|
||||
cd src/ &&
|
||||
printf "compiling cpp \n"
|
||||
g++ -std=c++11 -o main main.cpp -ldl -lpthread -L../../../../target/release -lnym_cpp_ffi -lboost_thread &&
|
||||
export LD_LIBRARY_PATH=../../../../target/release:$LD_LIBRARY_PATH
|
||||
g++ -std=c++11 -o main main.cpp -ldl -lpthread -L../target/release -lnym_cpp_ffi -lboost_thread &&
|
||||
export LD_LIBRARY_PATH=../target/release:$LD_LIBRARY_PATH
|
||||
# check output for name of rust lib - can be helpful if you've changed e.g. the name of a file and the compilation is failing
|
||||
# printf "ldd main: \n"
|
||||
# ldd main
|
||||
@@ -48,3 +48,4 @@ else
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// TODO REMOVE when you're working on new CPP branch
|
||||
#![allow(clippy::all)]
|
||||
// use nym_ffi_shared;
|
||||
|
||||
use nym_ffi_shared;
|
||||
use std::ffi::{c_char, c_int, CStr, CString};
|
||||
|
||||
use nym_sphinx_anonymous_replies::requests::AnonymousSenderTag;
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// TODO REMOVE when you're working on new CPP branch
|
||||
#![allow(clippy::all)]
|
||||
pub mod types {
|
||||
|
||||
use std::ffi::c_char;
|
||||
|
||||
// TODO change all the numbers / replace -2 with prxy?
|
||||
#[derive(Debug)]
|
||||
pub enum StatusCode {
|
||||
NoError = 0,
|
||||
ClientInitError = -1,
|
||||
// ClientUninitialisedError = -2,
|
||||
ClientUninitialisedError = -2,
|
||||
SelfAddrError = -3,
|
||||
SendMsgError = -4,
|
||||
ReplyError = -5,
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
[package]
|
||||
name = "nym-go-ffi" #"goffitest"
|
||||
version = "0.2.0"
|
||||
name = "nym-go-ffi" #"goffitest"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
name = "nym_go_ffi" #"go_ffi"
|
||||
name = "nym_go_ffi" #"go_ffi"
|
||||
|
||||
[dependencies]
|
||||
# Bindgen
|
||||
uniffi = { version = "0.25.2", features = ["cli"] }
|
||||
# Nym clients, addressing, packet format, common tools (logging), ffi shared
|
||||
nym-sdk = { path = "../../rust/nym-sdk/" }
|
||||
nym-bin-common = { path = "../../../common/bin-common" }
|
||||
nym-sphinx-anonymous-replies = { path = "../../../common/nymsphinx/anonymous-replies" }
|
||||
nym-bin-common = { git = "https://github.com/nymtech/nym", branch = "master" }
|
||||
nym-sdk = { git = "https://github.com/nymtech/nym", branch = "master" }
|
||||
nym-sphinx-anonymous-replies = { git = "https://github.com/nymtech/nym", branch = "master" }
|
||||
nym-ffi-shared = { path = "../shared" }
|
||||
# Async runtime
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
@@ -24,8 +23,8 @@ anyhow = "1.0.79"
|
||||
thiserror = "1.0.56"
|
||||
|
||||
[build-dependencies]
|
||||
uniffi = { version = "0.25.2", features = ["build"] }
|
||||
uniffi_build = { version = "0.25.2", features = ["builtin-bindgen"] }
|
||||
uniffi = { version = "0.25.2", features = ["build" ] }
|
||||
uniffi_build = { version = "0.25.2", features=["builtin-bindgen"] }
|
||||
|
||||
[[bin]]
|
||||
name = "uniffi-bindgen"
|
||||
|
||||
+24
-16
@@ -1,20 +1,27 @@
|
||||
# Go FFI
|
||||
> ⚠️ This is an initial version of this library in order to give developers something to experiment with. If you use this code to begin testing out Mixnet integration and run into issues, errors, or have feedback, please feel free to open an issue; feedback from developers trying to use it will help us improve it. If you have questions feel free to reach out via our [Matrix channel](https://matrix.to/#/#dev:nymtech.chat).
|
||||
|
||||
This repo contains:
|
||||
* `lib.rs`: an initial version of bindings for interacting with the Mixnet via the Rust SDK from Go. These are essentially match statemtns wrapping imported functions from the `nym-ffi-shared` lib.
|
||||
* `ffi/`: a directory containing:
|
||||
This repo contains:
|
||||
* `lib.rs`: an initial version of bindings for interacting with the Mixnet via the Rust SDK from Go. These are essentially match statemtns wrapping imported functions from the `nym-ffi-shared` lib.
|
||||
* `ffi/`: a directory containing:
|
||||
* the `bindings/` files generated using [`uniffi-bindgen-go`](https://github.com/NordSecurity/uniffi-bindgen-go)
|
||||
* [`example.go`](./example.go): an example of using the mixnet client functionality.
|
||||
* [`proxy_example.go`](./proxy_example.go): an example of using the TcpProxy functionality.
|
||||
* [`example.go`](./example.go): an example of using this library.
|
||||
|
||||
## Useage - Consuming the Library
|
||||
You can import the bindings as normal and interact with them as shown in the example files. These files import the bindings from this repository (hence the `go.mod` and `go.sum` in the crate root) but you can import them remotely as usual.
|
||||
The `example.go` file is an example flow of:
|
||||
* setting up Nym client logging
|
||||
* creating an ephemeral Nym client (no key storage / persistent address - this will come in a future iteration)
|
||||
* getting its [Nym address](https://nymtech.net/docs/clients/addressing-system.html)
|
||||
* using that address to send a message to yourself via the Mixnet
|
||||
* listen for and parse the incoming message for the `sender_tag` used for [anonymous replies with SURBs](https://nymtech.net/docs/architecture/traffic-flow.html#private-replies-using-surbs)
|
||||
* send a reply to yourself using SURBs
|
||||
|
||||
## Useage - Developing on the Library
|
||||
If you want to fork and add new features/functions to this library use the following instructions to rebuild the Go bindings.
|
||||
## Useage - Consuming the Library
|
||||
You can import the bindings as normal and interact with them as shown in the [example file](./example.go). This example imports the bindings from the this repository (hence the `go.mod` and `go.sum` in the crate root) but you can import them remotely as usual.
|
||||
|
||||
Rust functions exposed to the Go binding library are in `./src/lib.rs`.
|
||||
## Useage - Developing on the Library
|
||||
If you want to fork and add new features/functions to this library use the following instructions to rebuild the Go bindings.
|
||||
|
||||
Rust functions exposed to the Go binding library are in `./src/lib.rs`.
|
||||
|
||||
The `build.sh` script in the root of the repository speeds up the task of building and linking the Rust and Go code.
|
||||
* if want to quickly recompile your code run it as-is with `./build.sh`
|
||||
@@ -22,18 +29,19 @@ The `build.sh` script in the root of the repository speeds up the task of buildi
|
||||
|
||||
> Make sure to run the script from the root of the project directory, and that your LD PATH is set first!
|
||||
> ```
|
||||
> RUST_BINARIES=../../../target/release
|
||||
> echo 'export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:'${RUST_BINARIES} >> ~/.zshrc
|
||||
> source ~/.zshrc
|
||||
> RUST_BINARIES=target/release
|
||||
> echo 'export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:'${RUST_BINARIES} >> ~/.zshrc
|
||||
> source ~/.zshrc
|
||||
> ```
|
||||
|
||||
This script will:
|
||||
* (optionally if called with `clean` argument) remove existing Rust and Go artifacts
|
||||
* build `lib.rs` with the `--release` flag
|
||||
* compile the Go bindings
|
||||
* compile the Go bindings
|
||||
|
||||
**WIP** you need to manually add the following `cgo` flags to the generated bindings immediately underneath LN3 (`// #include <bindings.h`). In the future this will be automated in `build.sh`:
|
||||
**WIP** you need to manually add the following `cgo` flags to the generated bindings immediately underneath LN3 (`// #include <bindings.h`). In the future this will be automated in `build.sh`:
|
||||
|
||||
```
|
||||
// #cgo LDFLAGS: -L../../../../../target/release -lnym_go_ffi
|
||||
// #cgo LDFLAGS: -L../../target/release -lnym_go_ffi
|
||||
```
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
fn main() {
|
||||
uniffi::generate_scaffolding("src/bindings.udl").unwrap();
|
||||
}
|
||||
|
||||
|
||||
+8
-1
@@ -15,7 +15,14 @@ build_artifacts() {
|
||||
printf "building go bindings \n"
|
||||
uniffi-bindgen-go $UDL_PATH --out-dir $GO_DIR
|
||||
printf "bindings built \n\n"
|
||||
# TODO pull in auto binding from https://github.com/NordSecurity/uniffi-bindgen-go/blob/main/test_bindings.sh (removes need for manual addition of cgo flags)
|
||||
|
||||
# something not right with these - having to add it manually to bindings.go for the moment
|
||||
# pushd $GO_DIR/bindings
|
||||
# echo $(pwd)
|
||||
# LD_LIBRARY_PATH="${LD_LIBRARY_PATH:-}:../../target/release" \
|
||||
# CGO_LDFLAGS="-L../target/release -lnym_go_ffi -lm -ldl" \
|
||||
# CGO_ENABLED=1 \
|
||||
# go run ../main.go
|
||||
}
|
||||
|
||||
clean_artifacts() {
|
||||
|
||||
@@ -6,15 +6,6 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
Flow showing:
|
||||
- setting up Nym client logging
|
||||
- creating an ephemeral Nym client (no key storage / persistent address - this will come in a future iteration)
|
||||
- getting its [Nym address](https://nymtech.net/docs/clients/addressing-system.html)
|
||||
- using that address to send a message to yourself via the Mixnet
|
||||
- listen for and parse the incoming message for the `sender_tag` used for [anonymous replies with SURBs] (https://nymtech.net/docs/architecture/traffic-flow.html#private-replies-using-surbs)
|
||||
- send a reply to yourself using SURBs
|
||||
*/
|
||||
func main() {
|
||||
|
||||
// initialise Nym client logging - this is quite verbose but very informative
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package bindings
|
||||
|
||||
// #include <bindings.h>
|
||||
// #cgo LDFLAGS: -L../../../../../target/release -lnym_go_ffi
|
||||
// #cgo LDFLAGS: -L../../target/release -lnym_go_ffi
|
||||
import "C"
|
||||
|
||||
import (
|
||||
@@ -379,42 +379,6 @@ func uniffiCheckChecksums() {
|
||||
panic("bindings: uniffi_nym_go_ffi_checksum_func_listen_for_incoming: UniFFI API checksum mismatch")
|
||||
}
|
||||
}
|
||||
{
|
||||
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
|
||||
return C.uniffi_nym_go_ffi_checksum_func_new_proxy_client(uniffiStatus)
|
||||
})
|
||||
if checksum != 14386 {
|
||||
// If this happens try cleaning and rebuilding your project
|
||||
panic("bindings: uniffi_nym_go_ffi_checksum_func_new_proxy_client: UniFFI API checksum mismatch")
|
||||
}
|
||||
}
|
||||
{
|
||||
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
|
||||
return C.uniffi_nym_go_ffi_checksum_func_new_proxy_client_default(uniffiStatus)
|
||||
})
|
||||
if checksum != 23215 {
|
||||
// If this happens try cleaning and rebuilding your project
|
||||
panic("bindings: uniffi_nym_go_ffi_checksum_func_new_proxy_client_default: UniFFI API checksum mismatch")
|
||||
}
|
||||
}
|
||||
{
|
||||
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
|
||||
return C.uniffi_nym_go_ffi_checksum_func_new_proxy_server(uniffiStatus)
|
||||
})
|
||||
if checksum != 40789 {
|
||||
// If this happens try cleaning and rebuilding your project
|
||||
panic("bindings: uniffi_nym_go_ffi_checksum_func_new_proxy_server: UniFFI API checksum mismatch")
|
||||
}
|
||||
}
|
||||
{
|
||||
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
|
||||
return C.uniffi_nym_go_ffi_checksum_func_proxy_server_address(uniffiStatus)
|
||||
})
|
||||
if checksum != 1079 {
|
||||
// If this happens try cleaning and rebuilding your project
|
||||
panic("bindings: uniffi_nym_go_ffi_checksum_func_proxy_server_address: UniFFI API checksum mismatch")
|
||||
}
|
||||
}
|
||||
{
|
||||
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
|
||||
return C.uniffi_nym_go_ffi_checksum_func_reply(uniffiStatus)
|
||||
@@ -424,24 +388,6 @@ func uniffiCheckChecksums() {
|
||||
panic("bindings: uniffi_nym_go_ffi_checksum_func_reply: UniFFI API checksum mismatch")
|
||||
}
|
||||
}
|
||||
{
|
||||
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
|
||||
return C.uniffi_nym_go_ffi_checksum_func_run_proxy_client(uniffiStatus)
|
||||
})
|
||||
if checksum != 45441 {
|
||||
// If this happens try cleaning and rebuilding your project
|
||||
panic("bindings: uniffi_nym_go_ffi_checksum_func_run_proxy_client: UniFFI API checksum mismatch")
|
||||
}
|
||||
}
|
||||
{
|
||||
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
|
||||
return C.uniffi_nym_go_ffi_checksum_func_run_proxy_server(uniffiStatus)
|
||||
})
|
||||
if checksum != 57536 {
|
||||
// If this happens try cleaning and rebuilding your project
|
||||
panic("bindings: uniffi_nym_go_ffi_checksum_func_run_proxy_server: UniFFI API checksum mismatch")
|
||||
}
|
||||
}
|
||||
{
|
||||
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
|
||||
return C.uniffi_nym_go_ffi_checksum_func_send_message(uniffiStatus)
|
||||
@@ -453,30 +399,6 @@ func uniffiCheckChecksums() {
|
||||
}
|
||||
}
|
||||
|
||||
type FfiConverterUint64 struct{}
|
||||
|
||||
var FfiConverterUint64INSTANCE = FfiConverterUint64{}
|
||||
|
||||
func (FfiConverterUint64) Lower(value uint64) C.uint64_t {
|
||||
return C.uint64_t(value)
|
||||
}
|
||||
|
||||
func (FfiConverterUint64) Write(writer io.Writer, value uint64) {
|
||||
writeUint64(writer, value)
|
||||
}
|
||||
|
||||
func (FfiConverterUint64) Lift(value C.uint64_t) uint64 {
|
||||
return uint64(value)
|
||||
}
|
||||
|
||||
func (FfiConverterUint64) Read(reader io.Reader) uint64 {
|
||||
return readUint64(reader)
|
||||
}
|
||||
|
||||
type FfiDestroyerUint64 struct{}
|
||||
|
||||
func (FfiDestroyerUint64) Destroy(_ uint64) {}
|
||||
|
||||
type FfiConverterString struct{}
|
||||
|
||||
var FfiConverterStringINSTANCE = FfiConverterString{}
|
||||
@@ -625,15 +547,11 @@ func (err GoWrapError) Unwrap() error {
|
||||
|
||||
// Err* are used for checking error type with `errors.Is`
|
||||
var ErrGoWrapErrorClientInitError = fmt.Errorf("GoWrapErrorClientInitError")
|
||||
var ErrGoWrapErrorClientUninitialisedError = fmt.Errorf("GoWrapErrorClientUninitialisedError")
|
||||
var ErrGoWrapErrorSelfAddrError = fmt.Errorf("GoWrapErrorSelfAddrError")
|
||||
var ErrGoWrapErrorSendMsgError = fmt.Errorf("GoWrapErrorSendMsgError")
|
||||
var ErrGoWrapErrorReplyError = fmt.Errorf("GoWrapErrorReplyError")
|
||||
var ErrGoWrapErrorListenError = fmt.Errorf("GoWrapErrorListenError")
|
||||
var ErrGoWrapErrorProxyInitError = fmt.Errorf("GoWrapErrorProxyInitError")
|
||||
var ErrGoWrapErrorProxyRunError = fmt.Errorf("GoWrapErrorProxyRunError")
|
||||
var ErrGoWrapErrorServerInitError = fmt.Errorf("GoWrapErrorServerInitError")
|
||||
var ErrGoWrapErrorAddressGetterError = fmt.Errorf("GoWrapErrorAddressGetterError")
|
||||
var ErrGoWrapErrorServerRunError = fmt.Errorf("GoWrapErrorServerRunError")
|
||||
|
||||
// Variant structs
|
||||
type GoWrapErrorClientInitError struct {
|
||||
@@ -654,6 +572,24 @@ func (self GoWrapErrorClientInitError) Is(target error) bool {
|
||||
return target == ErrGoWrapErrorClientInitError
|
||||
}
|
||||
|
||||
type GoWrapErrorClientUninitialisedError struct {
|
||||
message string
|
||||
}
|
||||
|
||||
func NewGoWrapErrorClientUninitialisedError() *GoWrapError {
|
||||
return &GoWrapError{
|
||||
err: &GoWrapErrorClientUninitialisedError{},
|
||||
}
|
||||
}
|
||||
|
||||
func (err GoWrapErrorClientUninitialisedError) Error() string {
|
||||
return fmt.Sprintf("ClientUninitialisedError: %s", err.message)
|
||||
}
|
||||
|
||||
func (self GoWrapErrorClientUninitialisedError) Is(target error) bool {
|
||||
return target == ErrGoWrapErrorClientUninitialisedError
|
||||
}
|
||||
|
||||
type GoWrapErrorSelfAddrError struct {
|
||||
message string
|
||||
}
|
||||
@@ -726,96 +662,6 @@ func (self GoWrapErrorListenError) Is(target error) bool {
|
||||
return target == ErrGoWrapErrorListenError
|
||||
}
|
||||
|
||||
type GoWrapErrorProxyInitError struct {
|
||||
message string
|
||||
}
|
||||
|
||||
func NewGoWrapErrorProxyInitError() *GoWrapError {
|
||||
return &GoWrapError{
|
||||
err: &GoWrapErrorProxyInitError{},
|
||||
}
|
||||
}
|
||||
|
||||
func (err GoWrapErrorProxyInitError) Error() string {
|
||||
return fmt.Sprintf("ProxyInitError: %s", err.message)
|
||||
}
|
||||
|
||||
func (self GoWrapErrorProxyInitError) Is(target error) bool {
|
||||
return target == ErrGoWrapErrorProxyInitError
|
||||
}
|
||||
|
||||
type GoWrapErrorProxyRunError struct {
|
||||
message string
|
||||
}
|
||||
|
||||
func NewGoWrapErrorProxyRunError() *GoWrapError {
|
||||
return &GoWrapError{
|
||||
err: &GoWrapErrorProxyRunError{},
|
||||
}
|
||||
}
|
||||
|
||||
func (err GoWrapErrorProxyRunError) Error() string {
|
||||
return fmt.Sprintf("ProxyRunError: %s", err.message)
|
||||
}
|
||||
|
||||
func (self GoWrapErrorProxyRunError) Is(target error) bool {
|
||||
return target == ErrGoWrapErrorProxyRunError
|
||||
}
|
||||
|
||||
type GoWrapErrorServerInitError struct {
|
||||
message string
|
||||
}
|
||||
|
||||
func NewGoWrapErrorServerInitError() *GoWrapError {
|
||||
return &GoWrapError{
|
||||
err: &GoWrapErrorServerInitError{},
|
||||
}
|
||||
}
|
||||
|
||||
func (err GoWrapErrorServerInitError) Error() string {
|
||||
return fmt.Sprintf("ServerInitError: %s", err.message)
|
||||
}
|
||||
|
||||
func (self GoWrapErrorServerInitError) Is(target error) bool {
|
||||
return target == ErrGoWrapErrorServerInitError
|
||||
}
|
||||
|
||||
type GoWrapErrorAddressGetterError struct {
|
||||
message string
|
||||
}
|
||||
|
||||
func NewGoWrapErrorAddressGetterError() *GoWrapError {
|
||||
return &GoWrapError{
|
||||
err: &GoWrapErrorAddressGetterError{},
|
||||
}
|
||||
}
|
||||
|
||||
func (err GoWrapErrorAddressGetterError) Error() string {
|
||||
return fmt.Sprintf("AddressGetterError: %s", err.message)
|
||||
}
|
||||
|
||||
func (self GoWrapErrorAddressGetterError) Is(target error) bool {
|
||||
return target == ErrGoWrapErrorAddressGetterError
|
||||
}
|
||||
|
||||
type GoWrapErrorServerRunError struct {
|
||||
message string
|
||||
}
|
||||
|
||||
func NewGoWrapErrorServerRunError() *GoWrapError {
|
||||
return &GoWrapError{
|
||||
err: &GoWrapErrorServerRunError{},
|
||||
}
|
||||
}
|
||||
|
||||
func (err GoWrapErrorServerRunError) Error() string {
|
||||
return fmt.Sprintf("ServerRunError: %s", err.message)
|
||||
}
|
||||
|
||||
func (self GoWrapErrorServerRunError) Is(target error) bool {
|
||||
return target == ErrGoWrapErrorServerRunError
|
||||
}
|
||||
|
||||
type FfiConverterTypeGoWrapError struct{}
|
||||
|
||||
var FfiConverterTypeGoWrapErrorINSTANCE = FfiConverterTypeGoWrapError{}
|
||||
@@ -836,23 +682,15 @@ func (c FfiConverterTypeGoWrapError) Read(reader io.Reader) error {
|
||||
case 1:
|
||||
return &GoWrapError{&GoWrapErrorClientInitError{message}}
|
||||
case 2:
|
||||
return &GoWrapError{&GoWrapErrorSelfAddrError{message}}
|
||||
return &GoWrapError{&GoWrapErrorClientUninitialisedError{message}}
|
||||
case 3:
|
||||
return &GoWrapError{&GoWrapErrorSendMsgError{message}}
|
||||
return &GoWrapError{&GoWrapErrorSelfAddrError{message}}
|
||||
case 4:
|
||||
return &GoWrapError{&GoWrapErrorReplyError{message}}
|
||||
return &GoWrapError{&GoWrapErrorSendMsgError{message}}
|
||||
case 5:
|
||||
return &GoWrapError{&GoWrapErrorListenError{message}}
|
||||
return &GoWrapError{&GoWrapErrorReplyError{message}}
|
||||
case 6:
|
||||
return &GoWrapError{&GoWrapErrorProxyInitError{message}}
|
||||
case 7:
|
||||
return &GoWrapError{&GoWrapErrorProxyRunError{message}}
|
||||
case 8:
|
||||
return &GoWrapError{&GoWrapErrorServerInitError{message}}
|
||||
case 9:
|
||||
return &GoWrapError{&GoWrapErrorAddressGetterError{message}}
|
||||
case 10:
|
||||
return &GoWrapError{&GoWrapErrorServerRunError{message}}
|
||||
return &GoWrapError{&GoWrapErrorListenError{message}}
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown error code %d in FfiConverterTypeGoWrapError.Read()", errorID))
|
||||
}
|
||||
@@ -863,67 +701,22 @@ func (c FfiConverterTypeGoWrapError) Write(writer io.Writer, value *GoWrapError)
|
||||
switch variantValue := value.err.(type) {
|
||||
case *GoWrapErrorClientInitError:
|
||||
writeInt32(writer, 1)
|
||||
case *GoWrapErrorSelfAddrError:
|
||||
case *GoWrapErrorClientUninitialisedError:
|
||||
writeInt32(writer, 2)
|
||||
case *GoWrapErrorSendMsgError:
|
||||
case *GoWrapErrorSelfAddrError:
|
||||
writeInt32(writer, 3)
|
||||
case *GoWrapErrorReplyError:
|
||||
case *GoWrapErrorSendMsgError:
|
||||
writeInt32(writer, 4)
|
||||
case *GoWrapErrorListenError:
|
||||
case *GoWrapErrorReplyError:
|
||||
writeInt32(writer, 5)
|
||||
case *GoWrapErrorProxyInitError:
|
||||
case *GoWrapErrorListenError:
|
||||
writeInt32(writer, 6)
|
||||
case *GoWrapErrorProxyRunError:
|
||||
writeInt32(writer, 7)
|
||||
case *GoWrapErrorServerInitError:
|
||||
writeInt32(writer, 8)
|
||||
case *GoWrapErrorAddressGetterError:
|
||||
writeInt32(writer, 9)
|
||||
case *GoWrapErrorServerRunError:
|
||||
writeInt32(writer, 10)
|
||||
default:
|
||||
_ = variantValue
|
||||
panic(fmt.Sprintf("invalid error value `%v` in FfiConverterTypeGoWrapError.Write", value))
|
||||
}
|
||||
}
|
||||
|
||||
type FfiConverterOptionalString struct{}
|
||||
|
||||
var FfiConverterOptionalStringINSTANCE = FfiConverterOptionalString{}
|
||||
|
||||
func (c FfiConverterOptionalString) Lift(rb RustBufferI) *string {
|
||||
return LiftFromRustBuffer[*string](c, rb)
|
||||
}
|
||||
|
||||
func (_ FfiConverterOptionalString) Read(reader io.Reader) *string {
|
||||
if readInt8(reader) == 0 {
|
||||
return nil
|
||||
}
|
||||
temp := FfiConverterStringINSTANCE.Read(reader)
|
||||
return &temp
|
||||
}
|
||||
|
||||
func (c FfiConverterOptionalString) Lower(value *string) RustBuffer {
|
||||
return LowerIntoRustBuffer[*string](c, value)
|
||||
}
|
||||
|
||||
func (_ FfiConverterOptionalString) Write(writer io.Writer, value *string) {
|
||||
if value == nil {
|
||||
writeInt8(writer, 0)
|
||||
} else {
|
||||
writeInt8(writer, 1)
|
||||
FfiConverterStringINSTANCE.Write(writer, *value)
|
||||
}
|
||||
}
|
||||
|
||||
type FfiDestroyerOptionalString struct{}
|
||||
|
||||
func (_ FfiDestroyerOptionalString) Destroy(value *string) {
|
||||
if value != nil {
|
||||
FfiDestroyerString{}.Destroy(*value)
|
||||
}
|
||||
}
|
||||
|
||||
func GetSelfAddress() (string, error) {
|
||||
_uniffiRV, _uniffiErr := rustCallWithError(FfiConverterTypeGoWrapError{}, func(_uniffiStatus *C.RustCallStatus) RustBufferI {
|
||||
return C.uniffi_nym_go_ffi_fn_func_get_self_address(_uniffiStatus)
|
||||
@@ -963,42 +756,6 @@ func ListenForIncoming() (IncomingMessage, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func NewProxyClient(serverAddress string, listenAddress string, listenPort string, closeTimeout uint64, env *string) error {
|
||||
_, _uniffiErr := rustCallWithError(FfiConverterTypeGoWrapError{}, func(_uniffiStatus *C.RustCallStatus) bool {
|
||||
C.uniffi_nym_go_ffi_fn_func_new_proxy_client(FfiConverterStringINSTANCE.Lower(serverAddress), FfiConverterStringINSTANCE.Lower(listenAddress), FfiConverterStringINSTANCE.Lower(listenPort), FfiConverterUint64INSTANCE.Lower(closeTimeout), FfiConverterOptionalStringINSTANCE.Lower(env), _uniffiStatus)
|
||||
return false
|
||||
})
|
||||
return _uniffiErr
|
||||
}
|
||||
|
||||
func NewProxyClientDefault(serverAddress string, env *string) error {
|
||||
_, _uniffiErr := rustCallWithError(FfiConverterTypeGoWrapError{}, func(_uniffiStatus *C.RustCallStatus) bool {
|
||||
C.uniffi_nym_go_ffi_fn_func_new_proxy_client_default(FfiConverterStringINSTANCE.Lower(serverAddress), FfiConverterOptionalStringINSTANCE.Lower(env), _uniffiStatus)
|
||||
return false
|
||||
})
|
||||
return _uniffiErr
|
||||
}
|
||||
|
||||
func NewProxyServer(upstreamAddress string, configDir string, env *string) error {
|
||||
_, _uniffiErr := rustCallWithError(FfiConverterTypeGoWrapError{}, func(_uniffiStatus *C.RustCallStatus) bool {
|
||||
C.uniffi_nym_go_ffi_fn_func_new_proxy_server(FfiConverterStringINSTANCE.Lower(upstreamAddress), FfiConverterStringINSTANCE.Lower(configDir), FfiConverterOptionalStringINSTANCE.Lower(env), _uniffiStatus)
|
||||
return false
|
||||
})
|
||||
return _uniffiErr
|
||||
}
|
||||
|
||||
func ProxyServerAddress() (string, error) {
|
||||
_uniffiRV, _uniffiErr := rustCallWithError(FfiConverterTypeGoWrapError{}, func(_uniffiStatus *C.RustCallStatus) RustBufferI {
|
||||
return C.uniffi_nym_go_ffi_fn_func_proxy_server_address(_uniffiStatus)
|
||||
})
|
||||
if _uniffiErr != nil {
|
||||
var _uniffiDefaultValue string
|
||||
return _uniffiDefaultValue, _uniffiErr
|
||||
} else {
|
||||
return FfiConverterStringINSTANCE.Lift(_uniffiRV), _uniffiErr
|
||||
}
|
||||
}
|
||||
|
||||
func Reply(recipient []byte, message string) error {
|
||||
_, _uniffiErr := rustCallWithError(FfiConverterTypeGoWrapError{}, func(_uniffiStatus *C.RustCallStatus) bool {
|
||||
C.uniffi_nym_go_ffi_fn_func_reply(FfiConverterBytesINSTANCE.Lower(recipient), FfiConverterStringINSTANCE.Lower(message), _uniffiStatus)
|
||||
@@ -1007,22 +764,6 @@ func Reply(recipient []byte, message string) error {
|
||||
return _uniffiErr
|
||||
}
|
||||
|
||||
func RunProxyClient() error {
|
||||
_, _uniffiErr := rustCallWithError(FfiConverterTypeGoWrapError{}, func(_uniffiStatus *C.RustCallStatus) bool {
|
||||
C.uniffi_nym_go_ffi_fn_func_run_proxy_client(_uniffiStatus)
|
||||
return false
|
||||
})
|
||||
return _uniffiErr
|
||||
}
|
||||
|
||||
func RunProxyServer() error {
|
||||
_, _uniffiErr := rustCallWithError(FfiConverterTypeGoWrapError{}, func(_uniffiStatus *C.RustCallStatus) bool {
|
||||
C.uniffi_nym_go_ffi_fn_func_run_proxy_server(_uniffiStatus)
|
||||
return false
|
||||
})
|
||||
return _uniffiErr
|
||||
}
|
||||
|
||||
func SendMessage(recipient string, message string) error {
|
||||
_, _uniffiErr := rustCallWithError(FfiConverterTypeGoWrapError{}, func(_uniffiStatus *C.RustCallStatus) bool {
|
||||
C.uniffi_nym_go_ffi_fn_func_send_message(FfiConverterStringINSTANCE.Lower(recipient), FfiConverterStringINSTANCE.Lower(message), _uniffiStatus)
|
||||
|
||||
@@ -84,46 +84,12 @@ RustBuffer uniffi_nym_go_ffi_fn_func_listen_for_incoming(
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void uniffi_nym_go_ffi_fn_func_new_proxy_client(
|
||||
RustBuffer server_address,
|
||||
RustBuffer listen_address,
|
||||
RustBuffer listen_port,
|
||||
uint64_t close_timeout,
|
||||
RustBuffer env,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void uniffi_nym_go_ffi_fn_func_new_proxy_client_default(
|
||||
RustBuffer server_address,
|
||||
RustBuffer env,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void uniffi_nym_go_ffi_fn_func_new_proxy_server(
|
||||
RustBuffer upstream_address,
|
||||
RustBuffer config_dir,
|
||||
RustBuffer env,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
RustBuffer uniffi_nym_go_ffi_fn_func_proxy_server_address(
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void uniffi_nym_go_ffi_fn_func_reply(
|
||||
RustBuffer recipient,
|
||||
RustBuffer message,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void uniffi_nym_go_ffi_fn_func_run_proxy_client(
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void uniffi_nym_go_ffi_fn_func_run_proxy_server(
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void uniffi_nym_go_ffi_fn_func_send_message(
|
||||
RustBuffer recipient,
|
||||
RustBuffer message,
|
||||
@@ -445,34 +411,10 @@ uint16_t uniffi_nym_go_ffi_checksum_func_listen_for_incoming(
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
uint16_t uniffi_nym_go_ffi_checksum_func_new_proxy_client(
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
uint16_t uniffi_nym_go_ffi_checksum_func_new_proxy_client_default(
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
uint16_t uniffi_nym_go_ffi_checksum_func_new_proxy_server(
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
uint16_t uniffi_nym_go_ffi_checksum_func_proxy_server_address(
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
uint16_t uniffi_nym_go_ffi_checksum_func_reply(
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
uint16_t uniffi_nym_go_ffi_checksum_func_run_proxy_client(
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
uint16_t uniffi_nym_go_ffi_checksum_func_run_proxy_server(
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
uint16_t uniffi_nym_go_ffi_checksum_func_send_message(
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net"
|
||||
"nymffi/go-nym/bindings"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
func runProxyClient() {
|
||||
run_err := bindings.RunProxyClient()
|
||||
if run_err != nil {
|
||||
fmt.Println(run_err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func runProxyServer() {
|
||||
run_err := bindings.RunProxyServer()
|
||||
if run_err != nil {
|
||||
fmt.Println(run_err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// connects to the proxy server and listens out for incoming as you would with a normal tcp connection
|
||||
func startTcpListener() {
|
||||
ln, err := net.Listen("tcp", ":9000")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
go handleConnection(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func handleConnection(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
_, err := conn.Read(buf)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Server-side tcp received: %s", buf)
|
||||
|
||||
_, err = conn.Write(buf)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Flow showing:
|
||||
- setting up Nym client logging
|
||||
- creating instances of both the NymProxyClient and NymProxyServer
|
||||
- running both in goroutines to kick off the process
|
||||
- starting a vanilla tcp listener/echo in a goroutine connected to the ProxyServer instance
|
||||
- pinging that via a tcp client and waiting for the reply: this is (under the hood) sent anonymously via SURBs - the ProxyServer and 'server-side' tcp listener never know the Nym address or IP of the ProxyClient/'client-side' tcp client.
|
||||
*/
|
||||
func main() {
|
||||
|
||||
// our mixnet client config file defining which network to use
|
||||
var env_path = "../../../envs/canary.env"
|
||||
// the tcp socket our server communicates with - the remote host your client is trying to hit
|
||||
var upstreamAddress = "127.0.0.1:9000"
|
||||
// where the keys and persistent storage for SURBs is located (this path will be prepended with the value of $HOME in the rust lib)
|
||||
var configDir = "/tmp/go-proxy-server-example"
|
||||
// tcp socket port our proxy client communicates with
|
||||
var clientPort = "8080"
|
||||
// timeout for ephemeral client to shutdown connection after sending Close message enum once it has sent all of the other messages (in seconds): this is used by the ProxyServer for session management
|
||||
var clientTimeout uint64 = 60
|
||||
|
||||
bindings.InitLogging()
|
||||
|
||||
// checking loading proper env
|
||||
file, err := os.Open(env_path)
|
||||
if err != nil {
|
||||
fmt.Println("Error opening file:", err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
fmt.Println(scanner.Text())
|
||||
}
|
||||
|
||||
// init a proxy server
|
||||
build_serv_err := bindings.NewProxyServer(upstreamAddress, configDir, &env_path)
|
||||
if build_serv_err != nil {
|
||||
fmt.Println(build_serv_err)
|
||||
return
|
||||
}
|
||||
|
||||
// get proxy addr
|
||||
proxyAddr, get_addr_err := bindings.ProxyServerAddress()
|
||||
if get_addr_err != nil {
|
||||
fmt.Println("(Go) Error:", get_addr_err)
|
||||
return
|
||||
}
|
||||
fmt.Println("(Go) server address:")
|
||||
fmt.Println(proxyAddr)
|
||||
|
||||
// run it in a goroutine
|
||||
go runProxyServer()
|
||||
|
||||
// initialise a proxy client
|
||||
build_err := bindings.NewProxyClient(proxyAddr, "127.0.0.1", clientPort, clientTimeout, &env_path)
|
||||
if build_err != nil {
|
||||
fmt.Println(build_err)
|
||||
return
|
||||
}
|
||||
|
||||
// run it in a goroutine
|
||||
go runProxyClient()
|
||||
|
||||
// connect 'server-side' tcp socket to ProxyServer
|
||||
go startTcpListener()
|
||||
|
||||
// send a oneshot message, wait for the echo, and close. you will see the session uuid info and the fact that the proxy_client logs it will be closing the session in <clientTimeout>.
|
||||
conn, err := net.Dial("tcp", "localhost:8080")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
_, err = conn.Write([]byte("Hello, server: oneshot ping\n"))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
_, read_err := conn.Read(buf)
|
||||
if read_err != nil {
|
||||
fmt.Println(read_err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("Client-side tcp received: %s", buf)
|
||||
conn.Close()
|
||||
|
||||
// sleep so that the nym client processes can catch up - in reality you'd have another process
|
||||
// running to keep logging going, so this is only necessary for this reference
|
||||
time.Sleep(60 * time.Second)
|
||||
fmt.Println("(Go) end go example")
|
||||
}
|
||||
@@ -1,15 +1,11 @@
|
||||
[Error]
|
||||
enum GoWrapError {
|
||||
"ClientInitError",
|
||||
"ClientUninitialisedError",
|
||||
"SelfAddrError",
|
||||
"SendMsgError",
|
||||
"ReplyError",
|
||||
"ListenError",
|
||||
"ProxyInitError",
|
||||
"ProxyRunError",
|
||||
"ServerInitError",
|
||||
"AddressGetterError",
|
||||
"ServerRunError"
|
||||
"ListenError"
|
||||
};
|
||||
|
||||
dictionary IncomingMessage {
|
||||
@@ -29,16 +25,4 @@ namespace bindings {
|
||||
void reply(bytes recipient, string message);
|
||||
[Throws=GoWrapError]
|
||||
IncomingMessage listen_for_incoming();
|
||||
[Throws=GoWrapError]
|
||||
void new_proxy_client(string server_address, string listen_address, string listen_port, u64 close_timeout, string? env);
|
||||
[Throws=GoWrapError]
|
||||
void new_proxy_client_default(string server_address, string? env);
|
||||
[Throws=GoWrapError]
|
||||
void run_proxy_client();
|
||||
[Throws=GoWrapError]
|
||||
void new_proxy_server(string upstream_address, string config_dir, string? env);
|
||||
[Throws=GoWrapError]
|
||||
string proxy_server_address();
|
||||
[Throws=GoWrapError]
|
||||
void run_proxy_server();
|
||||
};
|
||||
|
||||
+5
-81
@@ -5,11 +5,12 @@ use nym_sdk::mixnet::Recipient;
|
||||
use nym_sphinx_anonymous_replies::requests::AnonymousSenderTag;
|
||||
uniffi::include_scaffolding!("bindings");
|
||||
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
enum GoWrapError {
|
||||
#[error("Couldn't init client")]
|
||||
ClientInitError {},
|
||||
#[error("Client is uninitialised: init client first")]
|
||||
ClientUninitialisedError {},
|
||||
#[error("Error getting self address")]
|
||||
SelfAddrError {},
|
||||
#[error("Error sending message")]
|
||||
@@ -18,16 +19,6 @@ enum GoWrapError {
|
||||
ReplyError {},
|
||||
#[error("Could not start listening")]
|
||||
ListenError {},
|
||||
#[error("Couldn't init proxy client")]
|
||||
ProxyInitError {},
|
||||
#[error("Couldn't run proxy client")]
|
||||
ProxyRunError {},
|
||||
#[error("Couldn't init proxy server")]
|
||||
ServerInitError {},
|
||||
#[error("Couldn't get proxy server address")]
|
||||
AddressGetterError {},
|
||||
#[error("Couldn't run proxy server")]
|
||||
ServerRunError {},
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -53,8 +44,7 @@ fn get_self_address() -> Result<String, GoWrapError> {
|
||||
|
||||
#[no_mangle]
|
||||
fn send_message(recipient: String, message: String) -> Result<(), GoWrapError> {
|
||||
let nym_recipient_type =
|
||||
Recipient::try_from_base58_string(recipient).expect("couldn't create Recipient");
|
||||
let nym_recipient_type = Recipient::try_from_base58_string(recipient).unwrap();
|
||||
match nym_ffi_shared::send_message_internal(nym_recipient_type, &message) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err(GoWrapError::SendMsgError {}),
|
||||
@@ -82,77 +72,11 @@ fn listen_for_incoming() -> Result<IncomingMessage, GoWrapError> {
|
||||
match nym_ffi_shared::listen_for_incoming_internal() {
|
||||
Ok(received) => {
|
||||
let message = String::from_utf8_lossy(&received.message).to_string();
|
||||
let sender = received.sender_tag.unwrap().to_bytes().to_vec();
|
||||
// maybe change this to raw bytes to send over TODO
|
||||
let sender = received.sender_tag.unwrap().to_bytes().to_vec(); //.to_base58_string();
|
||||
let incoming = IncomingMessage { message, sender };
|
||||
Ok(incoming)
|
||||
}
|
||||
Err(_) => Err(GoWrapError::ListenError {}),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn new_proxy_client(
|
||||
server_address: String,
|
||||
listen_address: String,
|
||||
listen_port: String,
|
||||
close_timeout: u64,
|
||||
env: Option<String>,
|
||||
) -> Result<(), GoWrapError> {
|
||||
let server_nym_addr =
|
||||
Recipient::try_from_base58_string(server_address).expect("couldn't create Recipient");
|
||||
match nym_ffi_shared::proxy_client_new_internal(
|
||||
server_nym_addr,
|
||||
&listen_address,
|
||||
&listen_port,
|
||||
close_timeout,
|
||||
env,
|
||||
) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err(GoWrapError::ProxyInitError {}),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn new_proxy_client_default(
|
||||
server_address: String,
|
||||
env: Option<String>,
|
||||
) -> Result<(), GoWrapError> {
|
||||
let server_nym_addr =
|
||||
Recipient::try_from_base58_string(server_address).expect("couldn't create Recipient");
|
||||
match nym_ffi_shared::proxy_client_new_defaults_internal(server_nym_addr, env) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err(GoWrapError::ProxyInitError {}),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_proxy_client() -> Result<(), GoWrapError> {
|
||||
match nym_ffi_shared::proxy_client_run_internal() {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err(GoWrapError::ProxyRunError {}),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_proxy_server(
|
||||
upstream_address: String,
|
||||
config_dir: String,
|
||||
env: Option<String>,
|
||||
) -> Result<(), GoWrapError> {
|
||||
match nym_ffi_shared::proxy_server_new_internal(&upstream_address, &config_dir, env) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err(GoWrapError::ServerInitError {}),
|
||||
}
|
||||
}
|
||||
|
||||
fn proxy_server_address() -> Result<String, GoWrapError> {
|
||||
match nym_ffi_shared::proxy_server_address_internal() {
|
||||
Ok(address) => Ok(address.to_string()),
|
||||
Err(_) => Err(GoWrapError::AddressGetterError {}),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_proxy_server() -> Result<(), GoWrapError> {
|
||||
match nym_ffi_shared::proxy_server_run_internal() {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err(GoWrapError::ServerRunError {}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
fn main() {
|
||||
uniffi::uniffi_bindgen_main()
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
[package]
|
||||
name = "nym-ffi-shared"
|
||||
version = "0.2.0"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
# TODO change to load relative + remove this from the workspace exclude list
|
||||
[dependencies]
|
||||
# Async runtime
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
# Nym clients, addressing, packet format, common tools (logging)
|
||||
nym-sdk = { path = "../../rust/nym-sdk/" }
|
||||
nym-bin-common = { path = "../../../common/bin-common" }
|
||||
nym-sphinx-anonymous-replies = { path = "../../../common/nymsphinx/anonymous-replies" }
|
||||
nym-sdk = { git = "https://github.com/nymtech/nym", branch = "master" }
|
||||
nym-bin-common = { git = "https://github.com/nymtech/nym", branch = "master" }
|
||||
nym-sphinx-anonymous-replies = { git = "https://github.com/nymtech/nym", branch = "master" }
|
||||
# static var macro
|
||||
lazy_static = "1.4.0"
|
||||
# error handling
|
||||
@@ -21,5 +21,7 @@ bs58 = "0.5.0"
|
||||
uniffi = { version = "0.25.2", features = ["cli"] }
|
||||
|
||||
[build-dependencies]
|
||||
uniffi = { version = "0.25.2", features = ["build"] }
|
||||
uniffi_build = { version = "0.25.2", features = ["builtin-bindgen"] }
|
||||
uniffi = { version = "0.25.2", features = ["build" ] }
|
||||
uniffi_build = { version = "0.25.2", features=["builtin-bindgen"] }
|
||||
|
||||
|
||||
|
||||
+7
-162
@@ -3,24 +3,19 @@
|
||||
|
||||
use anyhow::{anyhow, bail};
|
||||
use lazy_static::lazy_static;
|
||||
use nym_sdk::mixnet::{
|
||||
MixnetClient, MixnetClientBuilder, MixnetMessageSender, Recipient, ReconstructedMessage,
|
||||
StoragePaths,
|
||||
};
|
||||
use nym_sdk::tcp_proxy::{NymProxyClient, NymProxyServer};
|
||||
use nym_sdk::mixnet::{MixnetClient, MixnetMessageSender, ReconstructedMessage, Recipient};
|
||||
use nym_sphinx_anonymous_replies::requests::AnonymousSenderTag;
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
// NYM_CLIENT/PROXIES: Static reference (only init-ed once) to:
|
||||
// NYM_CLIENT: Static reference (only init-ed once) to:
|
||||
// - Arc: share ownership
|
||||
// - Mutex: thread-safe way to share data between threads
|
||||
// - Option: init-ed or not
|
||||
// RUNTIME: Tokio runtime: no need to pass back to C and deal with raw pointers as it was previously
|
||||
lazy_static! {
|
||||
static ref NYM_PROXY_CLIENT: Arc<Mutex<Option<NymProxyClient>>> = Arc::new(Mutex::new(None));
|
||||
static ref NYM_PROXY_SERVER: Arc<Mutex<Option<NymProxyServer>>> = Arc::new(Mutex::new(None));
|
||||
static ref NYM_CLIENT: Arc<Mutex<Option<MixnetClient>>> = Arc::new(Mutex::new(None));
|
||||
static ref RUNTIME: Runtime = Runtime::new().unwrap();
|
||||
}
|
||||
@@ -35,30 +30,7 @@ pub fn init_ephemeral_internal() -> anyhow::Result<(), anyhow::Error> {
|
||||
if let Ok(ref mut client) = client {
|
||||
**client = Some(init_client);
|
||||
} else {
|
||||
return Err(anyhow!("couldnt lock ephemeral NYM_CLIENT"));
|
||||
}
|
||||
Ok::<(), anyhow::Error>(())
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn init_default_storage_internal(config_dir: PathBuf) -> anyhow::Result<(), anyhow::Error> {
|
||||
if NYM_CLIENT.lock().unwrap().as_ref().is_some() {
|
||||
bail!("client already exists");
|
||||
} else {
|
||||
RUNTIME.block_on(async move {
|
||||
let storage_paths = StoragePaths::new_from_dir(&config_dir).unwrap();
|
||||
let init_client = MixnetClientBuilder::new_with_default_storage(storage_paths)
|
||||
.await?
|
||||
.build()?
|
||||
.connect_to_mixnet()
|
||||
.await?;
|
||||
let mut client = NYM_CLIENT.try_lock();
|
||||
if let Ok(ref mut client) = client {
|
||||
**client = Some(init_client);
|
||||
} else {
|
||||
return Err(anyhow!("couldnt lock NYM_CLIENT"));
|
||||
anyhow!("couldnt lock NYM_CLIENT");
|
||||
}
|
||||
Ok::<(), anyhow::Error>(())
|
||||
})?;
|
||||
@@ -78,8 +50,6 @@ pub fn get_self_address_internal() -> anyhow::Result<String, anyhow::Error> {
|
||||
Ok(nym_client.nym_address().to_string())
|
||||
}
|
||||
|
||||
// TODO split sender
|
||||
|
||||
pub fn send_message_internal(
|
||||
recipient: Recipient,
|
||||
message: &str,
|
||||
@@ -92,6 +62,7 @@ pub fn send_message_internal(
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow!("could not get client as_ref()"))?;
|
||||
|
||||
// send message
|
||||
RUNTIME.block_on(async move {
|
||||
nym_client.send_plain_message(recipient, message).await?;
|
||||
Ok::<(), anyhow::Error>(())
|
||||
@@ -99,8 +70,6 @@ pub fn send_message_internal(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO send_raw_message_internal
|
||||
|
||||
pub fn reply_internal(
|
||||
recipient: AnonymousSenderTag,
|
||||
message: &str,
|
||||
@@ -131,10 +100,7 @@ pub fn listen_for_incoming_internal() -> anyhow::Result<ReconstructedMessage, an
|
||||
|
||||
let message = RUNTIME.block_on(async move {
|
||||
let received = wait_for_non_empty_message(client).await?;
|
||||
Ok::<ReconstructedMessage, anyhow::Error>(ReconstructedMessage {
|
||||
message: received.message,
|
||||
sender_tag: received.sender_tag,
|
||||
})
|
||||
Ok::<ReconstructedMessage, anyhow::Error>(ReconstructedMessage {message: received.message, sender_tag: received.sender_tag})
|
||||
})?;
|
||||
|
||||
Ok(message)
|
||||
@@ -152,124 +118,3 @@ pub async fn wait_for_non_empty_message(
|
||||
}
|
||||
bail!("(Rust) did not receive any non-empty message")
|
||||
}
|
||||
|
||||
pub fn proxy_client_new_internal(
|
||||
server_address: Recipient,
|
||||
listen_address: &str,
|
||||
listen_port: &str,
|
||||
close_timeout: u64,
|
||||
env: Option<String>,
|
||||
) -> anyhow::Result<(), anyhow::Error> {
|
||||
if NYM_PROXY_CLIENT.lock().unwrap().as_ref().is_some() {
|
||||
bail!("proxy client already exists");
|
||||
} else {
|
||||
RUNTIME.block_on(async move {
|
||||
let init_proxy_client = NymProxyClient::new(
|
||||
server_address,
|
||||
listen_address,
|
||||
listen_port,
|
||||
close_timeout,
|
||||
env,
|
||||
)
|
||||
.await?;
|
||||
let mut client = NYM_PROXY_CLIENT.try_lock();
|
||||
if let Ok(ref mut client) = client {
|
||||
**client = Some(init_proxy_client);
|
||||
} else {
|
||||
return Err(anyhow!("couldnt lock NYM_PROXY_CLIENT"));
|
||||
}
|
||||
Ok::<(), anyhow::Error>(())
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn proxy_client_new_defaults_internal(
|
||||
server_address: Recipient,
|
||||
env: Option<String>,
|
||||
) -> anyhow::Result<(), anyhow::Error> {
|
||||
if NYM_PROXY_CLIENT.lock().unwrap().as_ref().is_some() {
|
||||
bail!("proxy client already exists");
|
||||
} else {
|
||||
RUNTIME.block_on(async move {
|
||||
let init_proxy_client = NymProxyClient::new_with_defaults(server_address, env).await?;
|
||||
let mut client = NYM_PROXY_CLIENT.try_lock();
|
||||
if let Ok(ref mut client) = client {
|
||||
**client = Some(init_proxy_client);
|
||||
} else {
|
||||
return Err(anyhow!("couldn't lock PROXY_CLIENT"));
|
||||
}
|
||||
Ok::<(), anyhow::Error>(())
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn proxy_client_run_internal() -> anyhow::Result<(), anyhow::Error> {
|
||||
let proxy_client = NYM_PROXY_CLIENT
|
||||
.lock()
|
||||
.expect("could not lock NYM_PROXY_CLIENT");
|
||||
if proxy_client.is_none() {
|
||||
bail!("Client is not yet initialised");
|
||||
}
|
||||
let proxy = proxy_client
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow!("could not get proxy_client as_ref()"))?;
|
||||
RUNTIME.block_on(async move {
|
||||
proxy.run().await?;
|
||||
Ok::<(), anyhow::Error>(())
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn proxy_server_new_internal(
|
||||
upstream_address: &str,
|
||||
config_dir: &str,
|
||||
env: Option<String>,
|
||||
) -> anyhow::Result<(), anyhow::Error> {
|
||||
if NYM_PROXY_SERVER.lock().unwrap().as_ref().is_some() {
|
||||
bail!("proxy client already exists");
|
||||
} else {
|
||||
RUNTIME.block_on(async move {
|
||||
let init_proxy_server = NymProxyServer::new(upstream_address, config_dir, env).await?;
|
||||
let mut client = NYM_PROXY_SERVER.try_lock();
|
||||
if let Ok(ref mut client) = client {
|
||||
**client = Some(init_proxy_server);
|
||||
} else {
|
||||
return Err(anyhow!("couldn't lock PROXY_SERVER"));
|
||||
}
|
||||
Ok::<(), anyhow::Error>(())
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn proxy_server_run_internal() -> anyhow::Result<(), anyhow::Error> {
|
||||
let mut proxy_server = NYM_PROXY_SERVER
|
||||
.lock()
|
||||
.expect("could not lock NYM_PROXY_CLIENT");
|
||||
if proxy_server.is_none() {
|
||||
bail!("Server is not yet initialised");
|
||||
}
|
||||
let proxy = proxy_server
|
||||
.as_mut()
|
||||
.ok_or_else(|| anyhow!("could not get proxy_client as_ref()"))?;
|
||||
RUNTIME.block_on(async move {
|
||||
proxy.run_with_shutdown().await?;
|
||||
Ok::<(), anyhow::Error>(())
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn proxy_server_address_internal() -> anyhow::Result<Recipient, anyhow::Error> {
|
||||
let mut proxy_server = NYM_PROXY_SERVER
|
||||
.lock()
|
||||
.expect("could not lock NYM_PROXY_CLIENT");
|
||||
if proxy_server.is_none() {
|
||||
bail!("Server is not yet initialised");
|
||||
}
|
||||
let proxy = proxy_server
|
||||
.as_mut()
|
||||
.ok_or_else(|| anyhow!("could not get proxy_client as_ref()"))?;
|
||||
Ok(proxy.nym_address().to_owned())
|
||||
}
|
||||
|
||||
@@ -40,25 +40,12 @@ zeroize = { workspace = true }
|
||||
|
||||
futures = { workspace = true }
|
||||
log = { workspace = true }
|
||||
rand = { workspace = true, features = ["small_rng"] }
|
||||
rand = { workspace = true }
|
||||
tap = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
url = { workspace = true }
|
||||
toml = { workspace = true }
|
||||
|
||||
# tcpproxy dependencies
|
||||
anyhow.workspace = true
|
||||
dashmap.workspace = true
|
||||
tokio.workspace = true
|
||||
tokio-stream.workspace = true
|
||||
tokio-util.workspace = true
|
||||
uuid = { version = "1", features = ["v4", "serde"] }
|
||||
bincode = "1.0"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
tracing.workspace = true
|
||||
tracing-subscriber = "0.3"
|
||||
dirs.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = { workspace = true }
|
||||
dotenvy = { workspace = true }
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user