mirror of
https://github.com/Such-Software/smirk-backend-core.git
synced 2026-10-17 15:31:38 +02:00
Final wallet-handler slice. An async store-and-forward mailbox for interactive
Grin transfers between two registered Smirk users, over the grin_slatepacks db.
Reworked clean from the legacy relay — NOT a port.
Endpoints (under /api/v1/wallet/grin/relay, JWT-gated):
- POST create {recipient_user_id, slate_id, slatepack, amount_nanogrin}
- GET pending -> the caller's inbox
- POST get {slate_id} -> poll a relay's state (sender/recipient)
- POST respond {slate_id, response_slatepack} (recipient)
- POST finalize {slate_id, tx_hash} (sender; RECORDS the txid only)
- POST cancel {slate_id} (sender or recipient)
Non-custodial: stores only opaque (encrypted) slatepacks + lifecycle status;
never holds keys, finalizes, or broadcasts. The wallet broadcasts locally (via
/wallet/grin/broadcast) and reports the txid here — diverging from the legacy
server-side post_tx (see docs/private/OWN_BACKEND_CONVERGENCE.md).
Controls + hardening (incl. fixes from the adversarial review workflow):
- Per-action authz: caller must be the slate's sender/recipient; a non-party
gets 404 (no existence oracle).
- slate_id is UNIQUE (+ validated as a UUID) and mutations key on the
authorized row's PRIMARY KEY, not slate_id — so a duplicate slate_id can't
brick a relay (fetch_optional-on-2-rows 500) or let a slate_id-keyed UPDATE
touch another row. create maps the unique violation to 409.
- State transitions are ATOMIC: the status + expiry preconditions live in the
UPDATE WHERE clause (not a Rust read-check-write), so respond-twice and
cancel-races-finalize cannot occur; a non-qualifying transition -> 409.
- TTL enforced at the action boundary (respond/finalize require expires_at >
now) + a periodic expire_old_slatepacks sweep in main.rs (relay-enabled only).
- Recipient must be a registered user; slatepack sizes capped (64 KiB).
- Behind FEATURE_GRIN_RELAY (default on).
6 paths + schemas + grin_relay tag in openapi (drift-clean). 4 L1 integration
tests against live Postgres: full lifecycle, unregistered-recipient 400, auth
gate, non-party/non-recipient 404s.
139 lines
6.5 KiB
Rust
139 lines
6.5 KiB
Rust
//! OpenAPI specification assembly.
|
|
//!
|
|
//! `ApiDoc` aggregates the annotated handlers into the spec that is the single
|
|
//! source of truth for the API contract and the wallet's generated TypeScript
|
|
//! client. Add a path here once its handler carries `#[utoipa::path(...)]` and
|
|
//! its request/response types derive `utoipa::ToSchema`.
|
|
|
|
use utoipa::OpenApi;
|
|
|
|
#[derive(OpenApi)]
|
|
#[openapi(
|
|
info(
|
|
title = "smirk-backend-core",
|
|
version = "0.3.0",
|
|
description = "Open, self-hostable backend for the Smirk non-custodial multi-chain wallet."
|
|
),
|
|
paths(
|
|
crate::api::health::health,
|
|
crate::api::auth::pow_challenge,
|
|
crate::api::auth::extension_register,
|
|
crate::api::auth::check_restore,
|
|
crate::api::auth::refresh_token,
|
|
crate::api::auth::logout,
|
|
crate::api::auth::get_me,
|
|
crate::api::auth::nostr_login,
|
|
crate::api::auth::nostr_link,
|
|
crate::api::website::website_challenge,
|
|
crate::api::website::website_verify,
|
|
crate::api::users::set_username,
|
|
crate::api::users::get_my_username,
|
|
crate::api::users::lookup_username,
|
|
crate::api::users::register_key,
|
|
crate::api::users::get_user_keys,
|
|
crate::api::users::get_user_key_for_asset,
|
|
crate::api::wallet::btc_ltc::balance,
|
|
crate::api::wallet::btc_ltc::utxos,
|
|
crate::api::wallet::btc_ltc::history,
|
|
crate::api::wallet::btc_ltc::tip,
|
|
crate::api::wallet::btc_ltc::fee,
|
|
crate::api::wallet::btc_ltc::broadcast,
|
|
crate::api::wallet::xmr_wow::register,
|
|
crate::api::wallet::xmr_wow::balance,
|
|
crate::api::wallet::xmr_wow::history,
|
|
crate::api::wallet::xmr_wow::unspent_outs,
|
|
crate::api::wallet::xmr_wow::random_outs,
|
|
crate::api::wallet::xmr_wow::submit_tx,
|
|
crate::api::wallet::xmr_wow::height,
|
|
crate::api::wallet::xmr_wow::confirmations,
|
|
crate::api::wallet::grin::scan,
|
|
crate::api::wallet::grin::height,
|
|
crate::api::wallet::grin::broadcast,
|
|
crate::api::wallet::grin_relay::create,
|
|
crate::api::wallet::grin_relay::pending,
|
|
crate::api::wallet::grin_relay::get_relay,
|
|
crate::api::wallet::grin_relay::respond,
|
|
crate::api::wallet::grin_relay::finalize,
|
|
crate::api::wallet::grin_relay::cancel,
|
|
crate::api::nip05::well_known_nostr,
|
|
),
|
|
components(schemas(
|
|
crate::api::health::HealthResponse,
|
|
crate::api::auth::AuthResponse,
|
|
crate::api::auth::UserInfo,
|
|
crate::api::auth::AssetPublicKey,
|
|
crate::api::auth::ExtensionRegisterRequest,
|
|
crate::api::auth::CheckRestoreRequest,
|
|
crate::api::auth::CheckRestoreResponse,
|
|
crate::api::auth::RefreshTokenRequest,
|
|
crate::api::auth::LogoutRequest,
|
|
crate::api::auth::LogoutResponse,
|
|
crate::api::auth::NostrLinkRequest,
|
|
crate::api::auth::NostrLinkResponse,
|
|
crate::api::website::WebsiteChallengeRequest,
|
|
crate::api::website::WebsiteChallengeResponse,
|
|
crate::api::website::AssetSignature,
|
|
crate::api::website::WebsiteVerifyRequest,
|
|
crate::api::users::SetUsernameRequest,
|
|
crate::api::users::SetUsernameResponse,
|
|
crate::api::users::MyUsernameResponse,
|
|
crate::api::users::PublicKeysInfo,
|
|
crate::api::users::LookupUsernameResponse,
|
|
crate::api::users::RegisterKeyRequest,
|
|
crate::api::users::UserKeyInfo,
|
|
crate::api::users::UserKeysResponse,
|
|
crate::api::wallet::btc_ltc::AddressRequest,
|
|
crate::api::wallet::btc_ltc::BalanceResponse,
|
|
crate::api::wallet::btc_ltc::Utxo,
|
|
crate::api::wallet::btc_ltc::UtxosResponse,
|
|
crate::api::wallet::btc_ltc::HistoryEntry,
|
|
crate::api::wallet::btc_ltc::HistoryResponse,
|
|
crate::api::wallet::btc_ltc::AssetRequest,
|
|
crate::api::wallet::btc_ltc::TipResponse,
|
|
crate::api::wallet::btc_ltc::FeeRequest,
|
|
crate::api::wallet::btc_ltc::FeeResponse,
|
|
crate::api::wallet::btc_ltc::BroadcastRequest,
|
|
crate::api::wallet::btc_ltc::BroadcastResponse,
|
|
crate::api::wallet::xmr_wow::ViewRequest,
|
|
crate::api::wallet::xmr_wow::RegisterRequest,
|
|
crate::api::wallet::xmr_wow::RandomOutsRequest,
|
|
crate::api::wallet::xmr_wow::SubmitRequest,
|
|
crate::api::wallet::xmr_wow::LwsAssetRequest,
|
|
crate::api::wallet::xmr_wow::ConfirmationsRequest,
|
|
crate::api::wallet::xmr_wow::LwsBalanceResponse,
|
|
crate::api::wallet::xmr_wow::SpentOutputDto,
|
|
crate::api::wallet::xmr_wow::TxDto,
|
|
crate::api::wallet::xmr_wow::LwsHistoryResponse,
|
|
crate::api::wallet::xmr_wow::UnspentOutputDto,
|
|
crate::api::wallet::xmr_wow::UnspentOutsResponse,
|
|
crate::api::wallet::xmr_wow::RandomOutputDto,
|
|
crate::api::wallet::xmr_wow::AmountOutsDto,
|
|
crate::api::wallet::xmr_wow::RandomOutsResponse,
|
|
crate::api::wallet::xmr_wow::HeightResponse,
|
|
crate::api::wallet::xmr_wow::ConfirmationsResponse,
|
|
crate::api::wallet::xmr_wow::OkResponse,
|
|
crate::api::wallet::grin::GrinScanRequest,
|
|
crate::api::wallet::grin::GrinOutput,
|
|
crate::api::wallet::grin::GrinScanResponse,
|
|
crate::api::wallet::grin::GrinHeightResponse,
|
|
crate::api::wallet::grin::GrinBroadcastRequest,
|
|
crate::api::wallet::grin::GrinBroadcastResponse,
|
|
crate::api::wallet::grin_relay::CreateRelayRequest,
|
|
crate::api::wallet::grin_relay::RespondRelayRequest,
|
|
crate::api::wallet::grin_relay::FinalizeRelayRequest,
|
|
crate::api::wallet::grin_relay::SlateIdRequest,
|
|
crate::api::wallet::grin_relay::RelayEntry,
|
|
crate::api::wallet::grin_relay::PendingRelaysResponse,
|
|
crate::api::nip05::WellKnownResponse,
|
|
)),
|
|
tags(
|
|
(name = "system", description = "Service health and metadata."),
|
|
(name = "auth", description = "Wallet authentication: registration, restore, refresh, sessions, Nostr identity."),
|
|
(name = "btc_ltc", description = "Bitcoin/Litecoin chain access via Electrum (balance, UTXOs, history, tip, fee, broadcast)."),
|
|
(name = "xmr_wow", description = "Monero/Wownero chain access via LWS (register, balance, history, unspent/random outs, submit, height, confirmations)."),
|
|
(name = "grin", description = "Grin view-only chain access (rewind_hash scan, tip height, broadcast)."),
|
|
(name = "grin_relay", description = "Grin slatepack relay: async non-custodial mailbox for interactive Grin transfers (feature-flagged).")
|
|
)
|
|
)]
|
|
pub struct ApiDoc;
|