feat: v1.2.0-alpha — E2E encrypted mesh relay, steganography, relay status polling
Phase 5 mesh networking: - E2E encrypted TX relay (X25519 + ChaCha20-Poly1305) — non-Archy nodes relay encrypted blobs transparently via Meshcore native routing - Steganographic encoding modes (WeatherStation, SensorNetwork) — traffic looks like sensor data on the wire, 0xAA marker, configurable per-node - Pre-flight Bitcoin Core health check on relay node — specific error codes (bitcoin_unreachable, bitcoin_syncing, tx_rejected) instead of generic fails - mesh.relay-status RPC endpoint — frontend polls for relay result every 3s - On-Chain / Lightning tabs in Off-Grid Bitcoin panel - Archy Peers vs Mesh Broadcast relay mode selector - Mesh view fills viewport (no page scroll), internal panel scrolling - Version bump to 1.2.0-alpha Also includes: deploy hardening, container fixes, IndeedHub updates, boot screen, dashboard improvements, MASTER_PLAN task tracking Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
d1ac098edb
commit
f273816405
@@ -93,6 +93,8 @@ pub struct RelayTracker {
|
||||
tx_requests: RwLock<HashMap<u64, PendingRelay>>,
|
||||
/// Pending Lightning relay requests.
|
||||
lightning_requests: RwLock<HashMap<u64, PendingRelay>>,
|
||||
/// Completed relay results (kept for 5 minutes for frontend polling).
|
||||
completed_results: RwLock<Vec<RelayResult>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -101,11 +103,22 @@ struct PendingRelay {
|
||||
created_at: String,
|
||||
}
|
||||
|
||||
/// Result of a completed relay attempt, stored for frontend polling.
|
||||
#[derive(Debug, Clone, serde::Serialize)]
|
||||
pub struct RelayResult {
|
||||
pub request_id: u64,
|
||||
pub txid: Option<String>,
|
||||
pub error: Option<String>,
|
||||
pub error_code: Option<String>,
|
||||
pub completed_at: String,
|
||||
}
|
||||
|
||||
impl RelayTracker {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
tx_requests: RwLock::new(HashMap::new()),
|
||||
lightning_requests: RwLock::new(HashMap::new()),
|
||||
completed_results: RwLock::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,6 +168,31 @@ impl RelayTracker {
|
||||
let ln = self.lightning_requests.read().await.len();
|
||||
(tx, ln)
|
||||
}
|
||||
|
||||
/// Store a completed relay result for frontend polling.
|
||||
pub async fn store_result(&self, result: RelayResult) {
|
||||
let mut results = self.completed_results.write().await;
|
||||
// Evict results older than 5 minutes
|
||||
let cutoff = chrono::Utc::now() - chrono::Duration::minutes(5);
|
||||
let cutoff_str = cutoff.to_rfc3339();
|
||||
results.retain(|r| r.completed_at > cutoff_str);
|
||||
results.push(result);
|
||||
}
|
||||
|
||||
/// Get relay result by request_id (returns None if not yet completed or expired).
|
||||
pub async fn get_result(&self, request_id: u64) -> Option<RelayResult> {
|
||||
self.completed_results
|
||||
.read()
|
||||
.await
|
||||
.iter()
|
||||
.find(|r| r.request_id == request_id)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
/// Check if a TX relay request is still pending.
|
||||
pub async fn is_pending(&self, request_id: u64) -> bool {
|
||||
self.tx_requests.read().await.contains_key(&request_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for RelayTracker {
|
||||
@@ -220,11 +258,13 @@ pub fn build_tx_relay_response(
|
||||
request_id: u64,
|
||||
txid: Option<&str>,
|
||||
error: Option<&str>,
|
||||
error_code: Option<&str>,
|
||||
) -> Result<Vec<u8>> {
|
||||
let payload = message_types::encode_payload(&TxRelayResponsePayload {
|
||||
request_id,
|
||||
txid: txid.map(|s| s.to_string()),
|
||||
error: error.map(|s| s.to_string()),
|
||||
error_code: error_code.map(|s| s.to_string()),
|
||||
})?;
|
||||
let envelope = TypedEnvelope::new(MeshMessageType::TxRelayResponse, payload);
|
||||
envelope.to_wire()
|
||||
|
||||
Reference in New Issue
Block a user