Fix socks5 GW probe regression (#6576)

* Restore tested gateway into topology

* Bump agent version

* Update .sqlx files

* Clean up code in probe test

* Probe error & logging improvements

* Fix clippy, improve log line

* Improve logging in ns agent

* Better tooling in NS API

* Bump agent

* Bump NS agent version
This commit is contained in:
dynco-nym
2026-03-20 10:36:32 +01:00
committed by GitHub
parent 4077717d3a
commit 180802feb8
71 changed files with 1074 additions and 54 deletions
Generated
+1 -1
View File
@@ -7468,7 +7468,7 @@ dependencies = [
[[package]]
name = "nym-node-status-agent"
version = "1.1.3"
version = "1.1.4"
dependencies = [
"anyhow",
"clap",
+3 -3
View File
@@ -76,9 +76,9 @@ pub async fn fetch_topology(
})
.unwrap_or_default();
if nym_api_urls.is_empty() {
let Some(nym_api_url) = nym_api_urls.first() else {
return Err(String::from("No nym-api URLs available to fetch topology"));
}
};
let topology_config = NymApiTopologyProviderConfig {
min_mixnode_performance: debug_config.topology.minimum_mixnode_performance,
@@ -87,7 +87,7 @@ pub async fn fetch_topology(
ignore_egress_epoch_role: debug_config.topology.ignore_egress_epoch_role,
};
let api_client = nym_http_api_client::Client::new_url(nym_api_urls[0].clone(), None)
let api_client = nym_http_api_client::Client::new_url(nym_api_url.clone(), None)
.map_err(|e| e.to_string())?;
let mut provider = NymApiTopologyProvider::new(topology_config, nym_api_urls, api_client);
+18 -13
View File
@@ -29,7 +29,7 @@ use nym_lp::peer::DHKeyPair;
use nym_registration_client::LpRegistrationClient;
use nym_sdk::NymNetworkDetails;
use nym_sdk::mixnet::{MixnetClient, MixnetClientBuilder, NodeIdentity, Recipient, Socks5};
use nym_topology::{HardcodedTopologyProvider, NymTopology};
use nym_topology::HardcodedTopologyProvider;
use rand09::SeedableRng;
use std::{
net::{IpAddr, Ipv4Addr, Ipv6Addr},
@@ -442,9 +442,7 @@ pub(crate) async fn do_socks5_connectivity_test(
nr_recipient: &Recipient,
entry_gateway_id: NodeIdentity,
network_details: NymNetworkDetails,
min_gw_performance: Option<u8>,
socks5_args: Socks5Args,
maybe_topology: Option<NymTopology>,
) -> anyhow::Result<Socks5ProbeResults> {
info!(
"Starting SOCKS5 test through Network Requester: {}",
@@ -463,23 +461,31 @@ pub(crate) async fn do_socks5_connectivity_test(
nr_recipient.identity().to_base58_string()
);
// since we define both entry & exit gateways to be the same tested GW,
// this shouldn't negatively affect mixnet layers but it will force route
// construction in case GW would get filtered out of topology
let min_gw_performance = Some(0);
// create ephemeral SOCKS5 client
let socks5_config = Socks5::new(nr_recipient.to_string());
// debug config similar to main probe
let debug_config = mixnet_debug_config(min_gw_performance, true);
let mut socks5_client_builder = MixnetClientBuilder::new_ephemeral()
// don't reuse topology: our gateway might be filtered out in it
info!("Fetching topology for SOCKS5 client...");
let topology_provider: Box<HardcodedTopologyProvider> =
match helpers::fetch_topology(&network_details, &debug_config).await {
Ok(topology) => Box::new(HardcodedTopologyProvider::new(topology)),
Err(e) => return Ok(Socks5ProbeResults::error_before_connecting(e)),
};
let socks5_client_builder = MixnetClientBuilder::new_ephemeral()
// Specify entry gateway explicitly
.request_gateway(entry_gateway_id.to_base58_string())
.socks5_config(socks5_config)
.network_details(network_details)
.debug_config(debug_config);
if let Some(topology) = maybe_topology {
socks5_client_builder = socks5_client_builder
.custom_topology_provider(Box::new(HardcodedTopologyProvider::new(topology)));
}
.debug_config(debug_config)
.custom_topology_provider(topology_provider);
let disconnected_socks5_client = socks5_client_builder.build()?;
@@ -489,9 +495,8 @@ pub(crate) async fn do_socks5_connectivity_test(
.await
{
Ok(client) => {
info!("🌐 Successfully connected to mixnet via SOCKS5 proxy");
info!(
"Connected via entry gateway: {}",
"🌐 Successfully connected to mixnet via SOCKS5 on entry gateway: {}",
client.nym_address().gateway().to_base58_string()
);
client
@@ -1,4 +1,4 @@
use anyhow::bail;
use anyhow::{Context, bail};
use rand::Rng;
use reqwest::Proxy;
use serde::{Deserialize, Serialize};
@@ -35,7 +35,7 @@ impl JsonRpcClient {
}
pub(super) async fn https_request_with_fallbacks(&self) -> SingleHttpsTestResult {
let mut error_msg = String::new();
let mut error_msg = Vec::new();
// endpoints are used as fallbacks: in case of success, return early
for endpoint in self.test_endpoints.iter() {
@@ -65,11 +65,11 @@ impl JsonRpcClient {
}
Ok((_, JsonRpcResponse::Err { error, .. })) => {
warn!("JSON-RPC error: {} (code: {})", error.message, error.code);
error_msg = format!("JSON-RPC error: {}", error.message);
error_msg.push(format!("JSON-RPC error: {}", error.message));
}
Err(e) => {
error_msg = e.to_string();
error!("{}", &error_msg);
error_msg.push(e.to_string());
error!("{}", &e);
}
}
}
@@ -78,8 +78,8 @@ impl JsonRpcClient {
success: false,
status_code: None,
latency_ms: None,
endpoint_used: self.test_endpoints.last().cloned(),
error: Some(error_msg),
endpoint_used: Some(self.test_endpoints.join(",")),
error: Some(error_msg.join(",")),
}
}
@@ -98,20 +98,32 @@ impl JsonRpcClient {
{
Ok(response) => {
let status = response.status();
let response_text = response
.text()
.await
.context("Failed to extract response text")
.unwrap_or_else(|e| e.to_string());
if status.is_success() {
// Deserialize body into JsonRpcResponse
response
.json::<JsonRpcResponse>()
.await
serde_json::from_str::<JsonRpcResponse>(&response_text)
.map(|res| (status, res))
.map_err(From::from)
} else {
bail!("HTTP error status: {}", status.as_u16());
bail!(
"HTTP error: {}\n{}",
status.as_u16(),
// truncate for logs in case response is too long
response_text.chars().take(200).collect::<String>()
);
}
}
Err(e) => {
let status = e
.status()
.map(|s| s.to_string())
.unwrap_or_else(|| "no HTTP status".to_string());
error!("HTTPS request failed: {}", e);
bail!("HTTPS request failed: {}", e);
bail!("HTTPS request failed: {} ({})", e, status);
}
}
}
+16 -11
View File
@@ -106,17 +106,26 @@ impl HttpsConnectivityResult {
fn from_results(results: Vec<SingleHttpsTestResult>) -> Self {
let (successes, errors): (Vec<SingleHttpsTestResult>, Vec<SingleHttpsTestResult>) =
results.into_iter().partition(|r| r.success);
let last_error_endpoint = errors
.iter()
.last()
.as_ref()
.and_then(|test| test.endpoint_used.clone());
let errors = errors
.into_iter()
.map(|r| r.error)
.collect::<Option<Vec<_>>>()
// partition above guarantees this vec is non-empty
.unwrap_or_default();
.collect::<Option<Vec<_>>>();
// use the last successful result for status_code and endpoint
// this works as an empty check as well: if there is no last success, array must be empty hence only errors are present
// this works as an empty check as well: if there is no last success,
// array must be empty hence only errors are present
let Some(last_success) = successes.last() else {
return Self::with_errors(errors);
return Self {
https_success: false,
https_status_code: None,
https_latency_ms: None,
endpoint_used: last_error_endpoint,
errors,
};
};
// average latency from successful runs
@@ -136,11 +145,7 @@ impl HttpsConnectivityResult {
https_latency_ms: Some(avg_latency),
endpoint_used: last_success.endpoint_used.clone(),
// even in case of success, some errors were possible
errors: if errors.is_empty() {
None
} else {
Some(errors)
},
errors,
}
}
-2
View File
@@ -407,9 +407,7 @@ impl Probe {
&network_requester,
self.entry_node.identity,
self.network.clone(),
self.config.min_gateway_mixnet_performance,
self.config.socks5_args,
self.topology,
)
.await
{
@@ -3,7 +3,7 @@
[package]
name = "nym-node-status-agent"
version = "1.1.3"
version = "1.1.4"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
@@ -25,7 +25,7 @@ SERVER="${NODE_STATUS_AGENT_SERVER_ADDRESS}|${NODE_STATUS_AGENT_SERVER_PORT}"
# hardcoded key used only for LOCAL TESTING
export NODE_STATUS_AGENT_AUTH_KEY=${NODE_STATUS_AGENT_AUTH_KEY_STAGING:-"BjyC9SsHAZUzPRkQR4sPTvVrp4GgaquTh5YfSJksvvWT"}
export NODE_STATUS_AGENT_PROBE_PATH="$crate_root/nym-gateway-probe"
export NODE_STATUS_AGENT_PROBE_EXTRA_ARGS="netstack-download-timeout-sec=30,netstack-num-ping=2,netstack-send-timeout-sec=1,netstack-recv-timeout-sec=1,socks5-json-rpc-url-list=https://cloudflare-eth.com;https://ethereum.publicnode.com"
export NODE_STATUS_AGENT_PROBE_EXTRA_ARGS="netstack-download-timeout-sec=30,netstack-num-ping=2,netstack-send-timeout-sec=1,netstack-recv-timeout-sec=1,socks5-json-rpc-url-list=https://cloudflare-eth.com;https://ethereum-rpc.publicnode.com"
workers=${1:-1}
echo "Running $workers workers in parallel"
@@ -1,3 +1,5 @@
use tracing::instrument;
use crate::cli::{GwProbe, ServerConfig};
pub(crate) async fn run_probe(
@@ -46,7 +48,7 @@ pub(crate) async fn run_probe(
let testrun_id = testrun.assignment.testrun_id;
let testrun_assigned_at = testrun.assignment.assigned_at_utc;
let gateway_identity_key = testrun.assignment.gateway_identity_key;
let gateway_identity_key = testrun.assignment.gateway_identity_key.clone();
tracing::info!("Received testrun {testrun_id} for gateway {gateway_identity_key} from primary",);
@@ -61,7 +63,7 @@ pub(crate) async fn run_probe(
// Extract JSON from log output (probe outputs logs followed by JSON)
let json_str = extract_json_from_log(&log);
if json_str.is_empty() {
tracing::error!("Failed to extract JSON from probe output");
tracing::warn!("Failed to extract JSON from probe output");
} else {
match serde_json::from_str::<serde_json::Value>(&json_str) {
Ok(json) => {
@@ -87,13 +89,32 @@ pub(crate) async fn run_probe(
}
}
// Submit to ALL servers in parallel
submit_results_to_servers(
servers,
testrun_id,
testrun_assigned_at,
&gateway_identity_key,
log,
)
.await;
Ok(())
}
#[instrument(level = "info", skip_all, fields(gateway_id = %gateway_identity_key, testrun = testrun_id))]
async fn submit_results_to_servers(
servers: &[ServerConfig],
testrun_id: i32,
testrun_assigned_at: i64,
gateway_identity_key: &str,
log: String,
) {
let handles = servers
.iter()
.enumerate()
.map(move |(idx, server)| {
.map(|(idx, server)| {
let log = log.clone();
let gateway_identity_key = gateway_identity_key.clone();
let gateway_identity_key = gateway_identity_key.to_string();
async move {
let auth_key = nym_crypto::asymmetric::ed25519::PrivateKey::from_bytes(
@@ -139,7 +160,7 @@ pub(crate) async fn run_probe(
match result {
Ok(()) => {
tracing::info!(
"✅ Successfully submitted {method} to server[{index}] {server_address}:{server_port}"
"✅ Successfully submitted {method} to server[{index}] {server_address}:{server_port}",
);
}
Err(e) => {
@@ -149,8 +170,6 @@ pub(crate) async fn run_probe(
}
}
}
Ok(())
}
/// Extract JSON from probe log output.
@@ -0,0 +1,32 @@
{
"db_name": "SQLite",
"query": "\n SELECT epoch_id as \"epoch_id: u32\", serialised_key, serialization_revision as \"serialization_revision: u8\"\n FROM master_verification_key WHERE epoch_id = ?\n ",
"describe": {
"columns": [
{
"name": "epoch_id: u32",
"ordinal": 0,
"type_info": "Integer"
},
{
"name": "serialised_key",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "serialization_revision: u8",
"ordinal": 2,
"type_info": "Integer"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false,
false
]
},
"hash": "0112296b190328a3856d1adf51aafa2525da6c0b871633aad80ad555db9cf47c"
}
@@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "SELECT EXISTS (SELECT 1 FROM registered_gateway WHERE gateway_id_bs58 = ?) AS 'exists'",
"describe": {
"columns": [
{
"name": "exists",
"ordinal": 0,
"type_info": "Integer"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false
]
},
"hash": "06e743d143fcc4be20ca2af5e99b19f15d22fff72490473587a14cdc046fda32"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n INSERT OR IGNORE INTO expiration_date_signatures(expiration_date, epoch_id, serialised_signatures, serialization_revision)\n VALUES (?, ?, ?, ?);\n UPDATE expiration_date_signatures\n SET\n serialised_signatures = ?,\n serialization_revision = ?\n WHERE expiration_date = ?\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 7
},
"nullable": []
},
"hash": "16d10f0ac0ed9ce4239937f46df3797a6a9ee7db2aab9f1b5e55f7c13c53bcc1"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n INSERT INTO remote_gateway_details(gateway_id_bs58, derived_aes256_gcm_siv_key, gateway_listener, fallback_listener, expiration_timestamp)\n VALUES (?, ?, ?, ?, ?)\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 5
},
"nullable": []
},
"hash": "1b43cc81ce6b6007ccc59172fc64c270fd5dd7f00eaab0fe82e6fe927e604294"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "DELETE FROM remote_gateway_details WHERE gateway_id_bs58 = ?",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "1da6904e72b5abb9abf75affb13af7974d7795b4cbdba234273345fe161df233"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "DELETE FROM reply_key;",
"describe": {
"columns": [],
"parameters": {
"Right": 0
},
"nullable": []
},
"hash": "1f4fde2cafa3b5fae95ab5fb653b5905d4dd6b7ac0e20f1cf100a51a1a40b35d"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE status SET previous_flush = ?",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "1fa61c23a1504de280d67c7943282b4dc00ba6a580de1faf83b137365f44b36d"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n INSERT INTO ecash_deposit_usage (deposit_id, ticketbooks_requested_on, client_pubkey, request_uuid)\n VALUES (?, ?, ?, ?)\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 4
},
"nullable": []
},
"hash": "1fc72f8ba24039548047e1766c9105614dea7fd301f0ec38bfe85bfe546dad40"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n UPDATE remote_gateway_details SET gateway_listener = ?, fallback_listener = ?, expiration_timestamp = ? WHERE gateway_id_bs58 = ?\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 4
},
"nullable": []
},
"hash": "20797a3e0bb951ed77216a906d7997139172411b10c7444dea74a1438de4f343"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n INSERT INTO reply_surb(reply_surb_sender_id, reply_surb, encoded_key_rotation) VALUES (?, ?, ?);\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 3
},
"nullable": []
},
"hash": "22bc9c7bcf96ec817c83c26104408cb6c3b99c6b808ba50c67b066fc3f36073b"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n INSERT INTO ecash_ticketbook\n (serialization_revision, ticketbook_data, expiration_date, ticketbook_type, epoch_id, total_tickets, used_tickets)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 7
},
"nullable": []
},
"hash": "284b3ceae42f9320c30323dde47765854899103fd3c0fa670eb6809492270e02"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n DELETE FROM blinded_shares WHERE created < ?\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "28681fcd8e2d4326f628681b8f2a317aabce063a650be362d3a8ed83cc7c3549"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n INSERT INTO global_expiration_date_signatures(expiration_date, epoch_id, serialised_signatures, serialization_revision)\n VALUES (?, ?, ?, ?)\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 4
},
"nullable": []
},
"hash": "2930ca6e3875c74acb7abb9ad889f166ad7f57681f76a1d0c7723d007c1f2c1e"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n INSERT INTO custom_gateway_details(gateway_id_bs58, data)\n VALUES (?, ?)\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
},
"hash": "2c113b37864f9fec7e64c0f8fdd38edcdf149acfd38c56a4db3bbf97bdb13210"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n DELETE FROM emergency_credential\n WHERE id = ?\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "2f3c12cb0c48084b569e12ecb0315212a6f526f78258e173c96ec177988696ef"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "DELETE FROM ecash_ticketbook WHERE expiration_date <= ?",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "37f82c9ec26b53d01601a2d6df82038a77ec37cca9f9aef18008dcd03030c2c4"
}
@@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "\n SELECT error_message\n FROM blinded_shares\n WHERE id = ?;\n ",
"describe": {
"columns": [
{
"name": "error_message",
"ordinal": 0,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true
]
},
"hash": "396f40c33f0f62796eb7449d640bd97845350f4fb9f806c60b93c7cebd5e410d"
}
@@ -0,0 +1,26 @@
{
"db_name": "SQLite",
"query": "\n SELECT serialised_signatures, serialization_revision as \"serialization_revision: u8\"\n FROM global_expiration_date_signatures\n WHERE expiration_date = ? AND epoch_id = ?\n ",
"describe": {
"columns": [
{
"name": "serialised_signatures",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "serialization_revision: u8",
"ordinal": 1,
"type_info": "Integer"
}
],
"parameters": {
"Right": 2
},
"nullable": [
false,
false
]
},
"hash": "3cc446220668fb3e02f0578104291d2a2af57656b405212af414d765b2263347"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE status SET client_in_use = ?",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "42dfc51aea793fc60484cff6c481bbace1bcf110e048e594e8bd03fa45290732"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n DELETE FROM emergency_credential\n WHERE type = ?\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "4a4f3b32b313f7fbc6eb579659e7cec1442967e53764b83ba0a66cd9a72494f9"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "DELETE FROM custom_gateway_details WHERE gateway_id_bs58 = ?",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "4f78619aca933484cd67cb89a376b2a5bec1c191993ff58f0c71c03e3ef6d92d"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n DELETE FROM partial_blinded_wallet_failure WHERE created < ?\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "52b378e282d93db941eff53b5b311e5732ece0bf84ea98f2328b20add8f2b5ef"
}
@@ -0,0 +1,26 @@
{
"db_name": "SQLite",
"query": "SELECT * FROM custom_gateway_details WHERE gateway_id_bs58 = ?",
"describe": {
"columns": [
{
"name": "gateway_id_bs58",
"ordinal": 0,
"type_info": "Text"
},
{
"name": "data",
"ordinal": 1,
"type_info": "Blob"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
true
]
},
"hash": "54f552a9dbe95236f946ac2b6615e03504afa58e345ae16a128629d8e76f0a11"
}
@@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "SELECT gateway_id_bs58 FROM registered_gateway",
"describe": {
"columns": [
{
"name": "gateway_id_bs58",
"ordinal": 0,
"type_info": "Text"
}
],
"parameters": {
"Right": 0
},
"nullable": [
false
]
},
"hash": "5661cf1ad8bd5ca062e855e1971a8787133ee41814bd3efdd501f9ee0c050f2b"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "DELETE FROM pending_issuance WHERE deposit_id = ?",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "5c5d4bfabf18bc6fa56e76a9b98e38b7f6ceb8e9191a7b9201922efcf6b07966"
}
@@ -0,0 +1,26 @@
{
"db_name": "SQLite",
"query": "\n SELECT serialised_signatures, serialization_revision as \"serialization_revision: u8\"\n FROM expiration_date_signatures\n WHERE expiration_date = ? AND epoch_id = ?\n ",
"describe": {
"columns": [
{
"name": "serialised_signatures",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "serialization_revision: u8",
"ordinal": 1,
"type_info": "Integer"
}
],
"parameters": {
"Right": 2
},
"nullable": [
false,
false
]
},
"hash": "5d3b8ad051ab6f46c702308c2fc751a5ca340ac9c6dd86da1a5e9a3e65ea589f"
}
@@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "SELECT name FROM sqlite_master WHERE type='table' AND name='status'",
"describe": {
"columns": [
{
"name": "name",
"ordinal": 0,
"type_info": "Text"
}
],
"parameters": {
"Right": 0
},
"nullable": [
true
]
},
"hash": "5e51396c534409a4b55c08170e00fd083e87cc9a18d798b2cf8d6774224aebed"
}
@@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "SELECT flush_in_progress FROM status;",
"describe": {
"columns": [
{
"name": "flush_in_progress",
"ordinal": 0,
"type_info": "Integer"
}
],
"parameters": {
"Right": 0
},
"nullable": [
false
]
},
"hash": "62f471858c831ac762a0ecf60876067dbf82d0dc376ad3f0df835b77dfcc3c37"
}
@@ -0,0 +1,26 @@
{
"db_name": "SQLite",
"query": "\n SELECT min_reply_surb_threshold as \"min_reply_surb_threshold: u32\", max_reply_surb_threshold as \"max_reply_surb_threshold: u32\" FROM reply_surb_storage_metadata;\n ",
"describe": {
"columns": [
{
"name": "min_reply_surb_threshold: u32",
"ordinal": 0,
"type_info": "Integer"
},
{
"name": "max_reply_surb_threshold: u32",
"ordinal": 1,
"type_info": "Integer"
}
],
"parameters": {
"Right": 0
},
"nullable": [
false,
false
]
},
"hash": "648fff18c4016fb356c164f813e0d5ebd37d62612b65b8ac4c9eb5d7b67a8884"
}
@@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "SELECT client_in_use FROM status;",
"describe": {
"columns": [
{
"name": "client_in_use",
"ordinal": 0,
"type_info": "Integer"
}
],
"parameters": {
"Right": 0
},
"nullable": [
false
]
},
"hash": "67db5e91bcdc831e0d1659eda358d18a0c6792d7a4244a65243d4b22d578a1e5"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "INSERT INTO master_verification_key(epoch_id, serialised_key, serialization_revision) VALUES (?, ?, ?)",
"describe": {
"columns": [],
"parameters": {
"Right": 3
},
"nullable": []
},
"hash": "70d8f240ad6edda6b8c7f2e800e7fca89d80869484f2f3c66cabb898f0298c62"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n INSERT INTO registered_gateway(gateway_id_bs58, registration_timestamp, gateway_type)\n VALUES (?, ?, ?)\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 3
},
"nullable": []
},
"hash": "727598e516090da6d26e36d09062b60ccb76d6468f359891428c0bfb96ddd7ef"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE status SET flush_in_progress = ?",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "74d77a3e15f49bde7b8b17dd638de04e5ece789fb0b0cd27ad09858fbf5c5e27"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE active_gateway SET active_gateway_id_bs58 = ?",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "80476cf2906eb0ecf7f66c16bc5682169b87f488b6927fa67fade6bf5abf7582"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n INSERT INTO emergency_credential\n (type, content, expiration)\n VALUES (?, ?, ?)\n ON CONFLICT(type, content) DO NOTHING;\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 3
},
"nullable": []
},
"hash": "805ad4f26e0234d7f482a263e186156311713d2e9f69d39c868cd16296b56326"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n INSERT INTO pending_issuance\n (deposit_id, serialization_revision, pending_ticketbook_data, expiration_date)\n VALUES (?, ?, ?, ?)\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 4
},
"nullable": []
},
"hash": "81a12a8a419c88b1c28a5533fde4d63462e9ea0049e2edafea1dc3f8476b33e4"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE ecash_ticketbook SET used_tickets = used_tickets + ? WHERE id = ?",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
},
"hash": "84cad8b1078a4000830835e6349de3eb76fed954b7336530401db72cd008aff3"
}
@@ -0,0 +1,32 @@
{
"db_name": "SQLite",
"query": "\n SELECT reply_surb_sender_id, reply_surb, encoded_key_rotation as \"encoded_key_rotation: u8\" FROM reply_surb\n WHERE reply_surb_sender_id = ?\n ",
"describe": {
"columns": [
{
"name": "reply_surb_sender_id",
"ordinal": 0,
"type_info": "Integer"
},
{
"name": "reply_surb",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "encoded_key_rotation: u8",
"ordinal": 2,
"type_info": "Integer"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false,
false
]
},
"hash": "85d542a8cb2e1fbba6142729056b57e6ea8685e9473a3a8bd635552810493a58"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n INSERT INTO partial_blinded_wallet_failure(corresponding_deposit, epoch_id, expiration_date, node_id, created, failure_message)\n VALUES (?, ?, ?, ?, ?, ?)\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 6
},
"nullable": []
},
"hash": "97d97ebb6bc8f4114fdea9ebc9f57f91a11f5057273cb70bd0e629712d17dd41"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "INSERT INTO status(flush_in_progress, previous_flush, client_in_use) VALUES (0, 0, 1)",
"describe": {
"columns": [],
"parameters": {
"Right": 0
},
"nullable": []
},
"hash": "a5a71e480118639397f7464abb09db66d1dc1307c0873627b39cb22a245b5a8c"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n INSERT OR IGNORE INTO master_verification_key(epoch_id, serialised_key, serialization_revision) VALUES (?, ?, ?);\n UPDATE master_verification_key\n SET\n serialised_key = ?,\n serialization_revision = ?\n WHERE epoch_id = ?\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 6
},
"nullable": []
},
"hash": "a5b18e66d77ff802e274623605e15dcfcffb502ba8398caefd56c481f44eb84e"
}
@@ -0,0 +1,32 @@
{
"db_name": "SQLite",
"query": "\n SELECT epoch_id as \"epoch_id: u32\", serialised_signatures, serialization_revision as \"serialization_revision: u8\"\n FROM global_coin_index_signatures WHERE epoch_id = ?\n ",
"describe": {
"columns": [
{
"name": "epoch_id: u32",
"ordinal": 0,
"type_info": "Integer"
},
{
"name": "serialised_signatures",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "serialization_revision: u8",
"ordinal": 2,
"type_info": "Integer"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false,
false
]
},
"hash": "a8b7ce0fe4755c28b96d1e503e313ab15fed747fb0cee1c9f949fb58461b3f79"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n INSERT INTO reply_surb_storage_metadata(min_reply_surb_threshold, max_reply_surb_threshold)\n VALUES (?, ?);\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
},
"hash": "b6ce77fcffda2ee24ba181213e04adec312abffa604fa805cc362a2c206107b4"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n DELETE FROM partial_blinded_wallet WHERE created < ?\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "b8257a0832d0124f0a8aaaf81dc6a811c593aea8febf1f891117e5e84213f147"
}
@@ -0,0 +1,32 @@
{
"db_name": "SQLite",
"query": "\n SELECT epoch_id as \"epoch_id: u32\", serialised_signatures, serialization_revision as \"serialization_revision: u8\"\n FROM coin_indices_signatures WHERE epoch_id = ?\n ",
"describe": {
"columns": [
{
"name": "epoch_id: u32",
"ordinal": 0,
"type_info": "Integer"
},
{
"name": "serialised_signatures",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "serialization_revision: u8",
"ordinal": 2,
"type_info": "Integer"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false,
false
]
},
"hash": "ba96344db31b0f2155e2af53eaaeafc9b5f64061b6c9a829e2912945b6cffc82"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n UPDATE ecash_ticketbook\n SET used_tickets = used_tickets - ?\n WHERE id = ?\n AND used_tickets = ?\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 3
},
"nullable": []
},
"hash": "bc823c54143e2dc590b91347cd089dde284b38a3a4960afed758206d03ca1cf4"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n INSERT OR IGNORE INTO coin_indices_signatures(epoch_id, serialised_signatures, serialization_revision) VALUES (?, ?, ?);\n UPDATE coin_indices_signatures\n SET\n serialised_signatures = ?,\n serialization_revision = ?\n WHERE epoch_id = ?\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 6
},
"nullable": []
},
"hash": "bd1973696121b6128bd75ae80fab253c071e04eb853d4b0f3b21782ea57c2f68"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n INSERT INTO reply_surb_sender(tag, last_sent) VALUES (?, ?);\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
},
"hash": "bdaf8e8dd711ff420807659456f5ebe0222a2a0ba96f28e3a3aa58fdc4b689db"
}
@@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "SELECT active_gateway_id_bs58 FROM active_gateway",
"describe": {
"columns": [
{
"name": "active_gateway_id_bs58",
"ordinal": 0,
"type_info": "Text"
}
],
"parameters": {
"Right": 0
},
"nullable": [
true
]
},
"hash": "bf249752f08c283bf5942b6ff48125c24750b523cfcad1e5e9069dbf7050e2a1"
}
@@ -0,0 +1,38 @@
{
"db_name": "SQLite",
"query": "\n SELECT\n t1.node_id as \"node_id!\",\n t1.blinded_signature as \"blinded_signature!\",\n t1.epoch_id as \"epoch_id!\",\n t1.expiration_date as \"expiration_date!: Date\"\n FROM partial_blinded_wallet as t1\n JOIN ecash_deposit_usage as t2\n on t1.corresponding_deposit = t2.deposit_id\n JOIN blinded_shares as t3\n ON t2.request_uuid = t3.request_uuid\n WHERE t3.device_id = ? AND t3.credential_id = ?;\n ",
"describe": {
"columns": [
{
"name": "node_id!",
"ordinal": 0,
"type_info": "Integer"
},
{
"name": "blinded_signature!",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "epoch_id!",
"ordinal": 2,
"type_info": "Integer"
},
{
"name": "expiration_date!: Date",
"ordinal": 3,
"type_info": "Date"
}
],
"parameters": {
"Right": 2
},
"nullable": [
true,
true,
true,
true
]
},
"hash": "c2b841762bdb963fff337ef5c8ec9f560017b4da6b0303ea0397d9568229e167"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "INSERT INTO global_coin_index_signatures(epoch_id, serialised_signatures, serialization_revision) VALUES (?, ?, ?)",
"describe": {
"columns": [],
"parameters": {
"Right": 3
},
"nullable": []
},
"hash": "d3510846941fa2525926b9bfbcdabd806877ce914b514d4f7cd6be318c4debe6"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "DELETE FROM reply_surb_sender;",
"describe": {
"columns": [],
"parameters": {
"Right": 0
},
"nullable": []
},
"hash": "daa9ffcbe13178d773e538e96971da2809ec4bd66ad49d2e3b8d67b741835475"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n INSERT INTO partial_blinded_wallet(corresponding_deposit, epoch_id, expiration_date, node_id, created, blinded_signature)\n VALUES (?, ?, ?, ?, ?, ?)\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 6
},
"nullable": []
},
"hash": "db176e98198fe594d88eb860d918f633a94d18a19b7f0f96935a62560def7d0f"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "DELETE FROM reply_surb;",
"describe": {
"columns": [],
"parameters": {
"Right": 0
},
"nullable": []
},
"hash": "e29e0c82d034626f9b369348b8e35677b9d9a1b29e4d89437134e92967fa98d1"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n UPDATE ecash_deposit_usage\n SET ticketbook_request_error = ?\n WHERE deposit_id = ?\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
},
"hash": "e584253e3856355899537eb8fc152f2bfed2d918b894ec0f588e38dd5e8ad726"
}
@@ -0,0 +1,38 @@
{
"db_name": "SQLite",
"query": "\n SELECT t1.node_id, t1.blinded_signature, t1.epoch_id, t1.expiration_date as \"expiration_date!: Date\"\n FROM partial_blinded_wallet as t1\n JOIN ecash_deposit_usage as t2\n on t1.corresponding_deposit = t2.deposit_id\n JOIN blinded_shares as t3\n ON t2.request_uuid = t3.request_uuid\n WHERE t3.id = ?;\n ",
"describe": {
"columns": [
{
"name": "node_id",
"ordinal": 0,
"type_info": "Integer"
},
{
"name": "blinded_signature",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "epoch_id",
"ordinal": 2,
"type_info": "Integer"
},
{
"name": "expiration_date!: Date",
"ordinal": 3,
"type_info": "Date"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false,
false,
false
]
},
"hash": "e77ffab19b099b84470fe5611716a2e314787586a46cffd074abb67f2f4d109e"
}
@@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "SELECT previous_flush AS \"previous_flush: OffsetDateTime\" FROM status",
"describe": {
"columns": [
{
"name": "previous_flush: OffsetDateTime",
"ordinal": 0,
"type_info": "Null"
}
],
"parameters": {
"Right": 0
},
"nullable": [
false
]
},
"hash": "e8717ef6523ef9bcf37a0e81570ac000904bb7fe3934786d652adc80fde78add"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n INSERT INTO reply_key(key_digest, reply_key, sent_at) VALUES (?, ?, ?);\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 3
},
"nullable": []
},
"hash": "eded014a5fffbd9c359f8557bcd3d41724666bf92357bf5faf42120a2aab131c"
}
@@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "\n SELECT error_message\n FROM blinded_shares\n WHERE device_id = ? AND credential_id = ?;\n ",
"describe": {
"columns": [
{
"name": "error_message",
"ordinal": 0,
"type_info": "Text"
}
],
"parameters": {
"Right": 2
},
"nullable": [
true
]
},
"hash": "ef60c2683211cc4ec2d3e46392518a1f62fa67dfe8f130deb876ebee11bf1602"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "DELETE FROM registered_gateway WHERE gateway_id_bs58 = ?",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "f3ebe259e26c05ecdd33bd9085dbb91cd5046a8c9d4434cf085a4fa2ebf03e93"
}
@@ -59,6 +59,7 @@ test-db-prepare: ## Run sqlx prepare for compile-time query verification
# --- Run Targets ---
.PHONY: run
run: ## Run the service (assumes database is already running)
@test -n "$$NYM_NODE_STATUS_API_MNEMONIC" || { echo "Error: NYM_NODE_STATUS_API_MNEMONIC is not set"; exit 1; }
@echo "Starting nym-node-status-api with $(ENVIRONMENT) environment..."
@set -a && source "$(MONOREPO_ROOT)/envs/$(ENVIRONMENT).env" && set +a && \
DATABASE_URL="$(TEST_DATABASE_URL)" \
@@ -1,6 +1,6 @@
services:
postgres-test:
image: postgres:16-alpine
image: postgres:17-alpine
container_name: nym_node_status_api_postgres_test
environment:
POSTGRES_DB: nym_node_status_api_test
@@ -18,4 +18,4 @@ services:
# - postgres_test_data:/var/lib/postgresql/data
# volumes:
# postgres_test_data:
# postgres_test_data: