ZOMBIIIIIII 53635806d6 swaggerready
2026-05-14 01:11:20 +03:00
2026-05-14 01:11:20 +03:00
2026-05-13 00:17:32 +03:00
2026-05-14 01:11:20 +03:00
2026-05-13 00:17:32 +03:00
2026-05-13 12:35:05 +03:00
2026-05-13 12:07:48 +03:00
2026-05-13 00:17:32 +03:00
2026-05-13 23:59:32 +03:00
2026-04-14 20:22:51 +03:00
2026-05-13 12:35:05 +03:00
2026-05-13 00:17:32 +03:00

CryptoWallet API — Deployment Bundle

Multi-chain custodial wallet API (ETH / BSC / BTC / TRX / SOL).

  • Сервер генерит mnemonic, хранит зашифрованной (AES-256-GCM, master-key из HashiCorp Vault)
  • Сервер сам подписывает tx по запросу юзера (юзер на клиенте жмёт "подтвердить")

Auth — JWT, выданный сервисом bitok (внешний). Секреты — HashiCorp Vault (AppRole).

Pre-deploy setup (один раз)

# 1. Master-key в Vault
vault kv put dev-secrets/crypto/master key=$(openssl rand -hex 32)

# 2. CSRF secret в Vault
vault kv put dev-secrets/csrf secret_key=$(openssl rand -hex 32) salt=csrf-salt digest=sha256

# 3. DB schema — APPEND-ONLY / NON-DESTRUCTIVE
#    Безопасно прогонять на existing БД. См. ниже "Schema is non-destructive".
psql -h <db-host> -U postgres_user -d postgres -f cryptowallet-schema.sql

# 4. bitok public key в Vault (для kid из JWT header)
vault kv put dev-secrets/jwt/kid active=<kid-from-bitok>
vault kv put dev-secrets/jwt/kids/<kid-from-bitok> \
  algorithm=RS256 \
  public_key="$(cat /path/to/bitok-public.pem)"

⚠️ Master-key менять нельзя — все existing encrypted_mnemonic станут нерасшифровываемыми. API на старте делает self-test: пытается декриптить любой существующий mnemonic и фейлится если ключ не подошёл.

Deploy

# Залить bundle на сервер
scp -P 2222 -r deployserver/ server@<host>:~/cryptowallet/

# На сервере: заполнить .env, поднять
ssh server@<host> -p 2222
cd ~/cryptowallet
cp .env.example .env
chmod 600 .env
nano .env              # заполни VAULT_*, JWT_*, CORS_ORIGINS
./start.sh

В .env обязательны: VAULT_ADDR, VAULT_ROLE_ID, VAULT_SECRET_ID, JWT_ISSUER=bitok, JWT_AUDIENCE, CORS_ORIGINS.

Update / Rebuild

scp -P 2222 -r deployserver/apps server@<host>:~/cryptowallet/
ssh server@<host> -p 2222 'cd cryptowallet && docker compose up -d --build'

Endpoints

Method Path Описание
GET /api/health Liveness (public)
GET /api/docs Swagger UI
GET /api/docs/swagger.json OpenAPI JSON
POST /api/wallets/create Сервер создаёт коша (no body, returns 5 addresses)
GET /api/wallets Список адресов юзера
POST /api/wallets/mnemonic/reveal Reveal seed (body confirm + 5/час rate-limit)
GET /api/wallets/{chain}/balance Баланс (native + все известные токены чейна)
GET /api/wallets/{chain}/transactions История tx
POST /api/wallets/{chain}/send Сервер подписывает + broadcast. Body: {to, amount, token?, feeTier?}
GET /api/wallets/{chain}/gas-suggestions Slow/normal/fast tiers (ETH/BSC, parsed из eth_feeHistory)
POST /api/wallets/{chain}/sign-raw-evm-tx Подписать произвольную EVM tx (для Relay/Swap execute responses)
/api/btc/* /api/tron/* /api/sol/* /api/bsc/* /api/relay/* Proxy endpoints (swap quote/build, bridge quote/execute/status)

Security highlights

  • AES-256-GCM для encrypted_mnemonic (12-byte random IV, 16-byte auth tag, fail-secure)
  • Master-key set-once (rotation запрещена в коде)
  • Crypto self-test на старте — fail-fast если master-key не декриптит existing mnemonics
  • Race-safe createWalletdb.transaction + UPDATE WHERE encrypted_mnemonic IS NULL (set-once primitive)
  • Atomic erc20 update — ETH-адрес кладётся в users.erc20 внутри той же транзакции
  • TRX MITM defense — local recompute txID + 4-layer raw_data verification перед подписью
  • EVM gas cap 500 gwei (применён к tx, не только check)
  • EVM gas oracle через eth_feeHistory p25/p50/p75 — minimum-but-works fees (BSC floor 0.05, ETH 0.5 gwei)
  • BTC fee tier-based (slow=144 blocks, normal=6, fast=1) + floor 2 sat/vB
  • TRX fee_limit cap 30 TRX (раньше 100, излишне)
  • Address checksum validation (BTC bitcoinjs-lib, TRX bs58check, SOL PublicKey, EVM EIP-55)
  • assertAddressMatch — derived(mnemonic, path) === DB.address перед подписью
  • SOL confirmTransaction — ждём подтверждения сети
  • BTC P2WPKH bech32, dust 294, broadcast 20s timeout
  • POST mnemonic/reveal + CSRF + body confirm token + 5/час rate-limit + audit-log
  • Logger sanitization — password/token/mnemonic/hex64/BIP39-phrase patterns маскируются
  • Audit log в stdout (структурированный JSON с "level":"audit") — wallet.create / wallet.send / mnemonic.reveal / wallet.sign_raw_evm
  • Hourly key rotation — JWT keys + CSRF secret из Vault (master-key НЕ ротируется)
  • Fail-fast — сервис не стартует без master-key, JWT_ISSUER, JWT_AUDIENCE
  • Container hardening — read-only fs, cap_drop ALL, no-new-privileges, pids/mem/cpu limits, non-root uid 1001, loopback-only port
  • Relay proxy whitelist method+path — /quote (POST), /intents/status/v3 (GET), /execute/{swap|bridge} (POST). Никаких freeform action'ов
  • Bridge UI dropdown ETH/BSC/SOL only (TRX через Relay убран — плохая ликвидность)

Schema is non-destructive

cryptowallet-schema.sql append-only. Re-run на боксе с уже настроенной БД = zero DDL changes. Если оператор добавил кастомные таблицы / индексы / constraints вручную — они никогда не будут перезаписаны или удалены.

Что делает script:

  • CREATE TABLE IF NOT EXISTS users / wallets
  • ALTER TABLE users ADD COLUMN <X> (только если колонки нет — encrypted_mnemonic, erc20, passport_data)
  • CREATE UNIQUE INDEX users_email_lower_unique (если индекса нет)
  • CREATE INDEX idx_users_active / idx_wallets_* (если индексов нет)
  • ADD CONSTRAINT × 4 (только если данного constraint name нет)

Что script НЕ делает:

  • Никогда не DROP TABLE
  • Никогда не DROP CONSTRAINT
  • Никогда не DROP COLUMN
  • Никогда не перезаписывает существующие constraints / indexes

Legacy cleanup (audit_log, idempotency_keys, sessions от старых версий) — manual one-time операторская задача, не часть этого script'а:

psql ... -c "DROP TABLE IF EXISTS audit_log CASCADE;"
psql ... -c "DROP TABLE IF EXISTS idempotency_keys CASCADE;"
psql ... -c "DROP TABLE IF EXISTS sessions CASCADE;"

Logs

Файловых логов нет. Весь код пишет в process.stdout (см. apps/api/src/lib/logger.ts и lib/audit-log.ts). Docker подбирает stdout через json-file driver и показывает через docker compose logs:

docker compose logs -f api                          # все логи (structured JSON)
docker compose logs api | grep '"level":"audit"'    # только audit events
docker compose logs api | grep '"level":"ERROR"'    # только ошибки

Production hardening checklist (опционально)

  • Vault server-mode (raft/file backend) с unseal flow
  • TLS termination на reverse-proxy (Caddy / Nginx) перед 127.0.0.1:3001
  • Swagger UI скрыть за basic-auth (endpoints всё ещё доступны через /api/docs/swagger.json)
  • Postgres backups (pg_dump → S3 по cron)
  • Vault root token ротация
  • Mnemonic-reveal endpoint — 2FA / time-based confirmation tokens
  • Rate-limit tune под реальный трафик
Description
No description provided
Readme 2.3 MiB
Languages
TypeScript 98.8%
Dockerfile 0.8%
Shell 0.4%