mirror of
https://github.com/Such-Software/smirk-backend-core.git
synced 2026-10-17 07:21:32 +02:00
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.
105 lines
3.1 KiB
Rust
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");
|
|
}
|