Files
smirk-backend-core/tests/wallet_xmr_wow.rs
T
jwinterm 10b508fa8e feat: XMR/WOW wallet handlers (LWS proxy, stateless-forward)
Second wallet-handler slice. Authenticated, non-custodial, stateless-forward
proxies over the LwsClient: the wallet sends its private view key per request,
the backend forwards it (never stores it; the LWS holds it for scanning), and
the wallet constructs + signs spends locally.

Endpoints (POST, under /api/v1/wallet/lws, asset = xmr|wow in body):
- register     -> add/import account (view key) for LWS scanning (+start_height)
- balance      -> total_received / unlocked / locked + scan state
- history      -> txs incl. mempool pending + candidate spent_outputs
- unspent_outs -> outputs + fee params for client-side spend construction
- random_outs  -> ring decoys (count clamped + fan-out capped in the client)
- submit_tx    -> broadcast a finalized signed tx
- height       -> daemon chain height
- confirmations-> tx confirmation count

- JWT-gated; the view key is skipped from every tracing span (never logged) and
  never stored. Feature-gated dispatch; unknown/disabled asset -> 400.
- Boundary validation: view_key (64 hex), address (bounded), txid (64 hex),
  tx_hex (even hex, <=2 MiB) — all before any LWS call.
- Shared validators in wallet/mod.rs (validate_hex/view_key/cn_address).
- Colliding DTO names disambiguated (Lws-prefixed) so the openapi schema
  registry stays correct; 8 paths + schemas + xmr_wow tag registered; spec
  regenerated, drift-clean.

6 L1 integration tests (auth gate on balance/register; unknown-asset,
bad-view-key, malformed-tx-hex, malformed-txid 400s) + validator unit tests.
2026-06-23 14:32:38 -04:00

105 lines
3.1 KiB
Rust

//! L1 integration: Monero/Wownero wallet endpoints through the real router.
//!
//! Deterministic, no-network paths only: the JWT gate, asset validation, and
//! view-key / tx-hex / txid validation — all resolved before any LWS call. The
//! funded/networked happy paths live in the separate gitignored L3 harness.
mod common;
use axum::http::StatusCode;
use serde_json::json;
#[tokio::test]
async fn balance_requires_a_token() {
let app = require_app!();
let (status, _) = app
.request(
"POST",
"/api/v1/wallet/lws/balance",
None,
Some(json!({ "asset": "xmr", "address": "4Addr", "view_key": "00" })),
)
.await;
assert_eq!(status, StatusCode::UNAUTHORIZED);
}
#[tokio::test]
async fn balance_rejects_unknown_asset() {
let app = require_app!();
let (_uid, access, _r) = app.mint_session().await;
let (status, body) = app
.request(
"POST",
"/api/v1/wallet/lws/balance",
Some(&access),
Some(json!({ "asset": "doge", "address": "x", "view_key": "00" })),
)
.await;
assert_eq!(status, StatusCode::BAD_REQUEST);
assert_eq!(body["code"], "VALIDATION_ERROR");
}
#[tokio::test]
async fn balance_rejects_malformed_view_key() {
let app = require_app!();
let (_uid, access, _r) = app.mint_session().await;
// Authed + valid asset + address, but the view key is not 64 hex -> 400
// before any LWS call.
let (status, body) = app
.request(
"POST",
"/api/v1/wallet/lws/balance",
Some(&access),
Some(json!({ "asset": "xmr", "address": "4SomeAddress", "view_key": "tooshort" })),
)
.await;
assert_eq!(status, StatusCode::BAD_REQUEST);
assert_eq!(body["code"], "VALIDATION_ERROR");
}
#[tokio::test]
async fn register_requires_a_token() {
let app = require_app!();
let (status, _) = app
.request(
"POST",
"/api/v1/wallet/lws/register",
None,
Some(json!({ "asset": "xmr", "address": "4Addr", "view_key": "00" })),
)
.await;
assert_eq!(status, StatusCode::UNAUTHORIZED);
}
#[tokio::test]
async fn submit_rejects_malformed_tx_hex() {
let app = require_app!();
let (_uid, access, _r) = app.mint_session().await;
let (status, body) = app
.request(
"POST",
"/api/v1/wallet/lws/submit_tx",
Some(&access),
Some(json!({ "asset": "xmr", "tx_hex": "nothex!!" })),
)
.await;
assert_eq!(status, StatusCode::BAD_REQUEST);
assert_eq!(body["code"], "VALIDATION_ERROR");
}
#[tokio::test]
async fn confirmations_rejects_malformed_txid() {
let app = require_app!();
let (_uid, access, _r) = app.mint_session().await;
let (status, body) = app
.request(
"POST",
"/api/v1/wallet/lws/confirmations",
Some(&access),
Some(json!({ "asset": "xmr", "txid": "not-a-txid" })),
)
.await;
assert_eq!(status, StatusCode::BAD_REQUEST);
assert_eq!(body["code"], "VALIDATION_ERROR");
}