Commit Graph
10 Commits
Author SHA1 Message Date
Satoshi Qazi Muhammed 96840fd17e Add bounded automation-key auto-pay: per-tx cap, daily velocity cap, circuit breaker
Small/mid-size swaps no longer have to wait on 3-of-5 for every single
transaction. usdt-bridge can now execute Assets.transfer_approved directly via
the automation key's existing delegated allowance when a deposit/withdrawal
passes all three independent guards:

- max_single_tx (50,000 USDT): any single transaction above this always queues
  for manual multisig review, no exceptions.
- auto_pay_daily_cap (200,000 USDT): a rolling 24h budget tracked separately
  per leg (wUSDT payouts vs USDT payouts draw on separate on-chain allowances),
  not a per-tx ceiling - lets many legitimate sub-cap swaps clear same-day.
- circuit breaker: if >=50% of the daily cap is consumed within 30 minutes,
  auto-pay halts entirely until a human runs reset-circuit-breaker - a burst
  that fast reads as a compromised key or a bug, not organic demand.

The automation key can only ever move funds via its pre-approved allowance
(never mint/burn/set_team/transfer_ownership - those stay multisig-only), so
its worst case is bounded by whatever allowance + daily cap remain, not
unbounded custody. Withdrawal auto-pay releases real USDT immediately but
defers the corresponding wUSDT burn (Owner/Admin-only, no delegation exists
for it) to a periodic manual multisig batch - tracked via a distinct
auto_paid_burn_pending status so it can't be silently forgotten.

Auto-pay is opt-in at the infra level: AUTOMATION_KEY_MNEMONIC unset means
100% manual behavior, unchanged from before. Bumped pezbridge-bot's
renewal_threshold/topup_amount so the next 3-of-5-approved allowance renewal
scales to the new daily cap.
2026-07-14 15:28:20 -07:00
Satoshi Qazi Muhammed a1555ce952 Fix 3 real bugs in usdt-bridge's listener before trusting it at custody scale
- show_status/show_balances/show_reserves checked the wrong account:
  load_wallet_addresses(&config.seed_path), the old, explicitly-retired
  single-operator key, never the multisig custody addresses. These
  commands were actively misleading, not just unfinished.
- listen_deposits/listen_withdrawals ran as concurrent tasks sharing one
  BridgeDatabase JSON file with no locking and a non-atomic write - two
  events landing close together could produce a lost update, and a
  mid-write crash left a truncated file that hard-crashed the next
  start. Added a process-wide async mutex around every load-mutate-save
  critical section, and made save() write-temp-then-rename so a crash
  mid-write can never corrupt the file in place. load() now refuses to
  silently treat a corrupt file as empty (would erase replay-protection
  history) - only a genuinely missing file gets a fresh database.
- The reserve-shortfall circuit breaker - the single most safety-critical
  signal this tool can produce - only called a local error!() log macro.
  Nobody was notified. Added real Telegram alerting (reusing
  PezbridgeBot's exact chats/send pattern rather than a second channel)
  for reserve shortfalls, max-single-tx-cap deposits/withdrawals needing
  manual review, and every new pending_multisig_approval row.

Also removed ProcessDeposit/MintWusdt/TransferUsdt - dead-but-dangerous
CLI subcommands that still loaded the retired operator key and attempted
direct mint/transfer/burn calls that only happened to fail today because
that key lost asset authority. A latent trap, not a safe no-op by
design; production flow is listen-all -> pending_multisig_approval ->
pwap-web signing, never a single-key path.

cargo check: 0 errors, 0 warnings (both binaries).
2026-07-14 14:08:38 -07:00
Satoshi Qazi Muhammed eb821b2ff5 Rewrite pezbridge-bot as notify-only, drop Noter key custody entirely
Renewing the automation key's spending allowance always needs 3-of-5
multisig approval regardless, so having this bot hold one signer's
(Noter's) private key on a VPS to auto-submit one of those three
signatures was never actually saving a real step - the other two
signers still had to sign manually every time. Once pwap-web shipped
a real multisig operations UI (and now wallet-android's Bridge screen
too), the bot's only remaining job is to detect a low allowance and
notify all 5 signers via Telegram - they each sign with their own
wallet through one of pwap-web, pezbridge-sign.pex.mom, or the wallet
app directly. Noter's mnemonic was removed from the VPS entirely
(shred -u) after this rewrite was deployed and verified.

Also adds gen_automation_key.rs (one-off key generation helper) and
the updated systemd unit with LoadCredential removed.
2026-07-14 07:38:24 -07:00
Satoshi Qazi Muhammed 042e1df314 fix(usdt-bridge): replace fragile Debug-string event matching with real byte decoding
listen_deposits/listen_withdrawals matched the bridge address by
checking whether a Debug-formatted string of the decoded dynamic Value
contained a 10-char slice of the address's SS58 text - flagged earlier
this session as unverified and provably broken (that same Debug string
was also fed into convert_ss58_prefix, which only parses real SS58
text and could never have succeeded on it).

Added account_id_from_dynamic(), which decodes an AccountId32's actual
32 bytes out of the event field (peeling the single-item composite
wrapper subxt's dynamic API adds, same nesting seen in Tiki storage
this session) and account_bytes_to_ss58() to re-encode them properly.
Both listeners now compare raw bytes against the target address
instead of matching strings, and derive the sender's real SS58 address
for logging and convert_ss58_prefix() instead of a Debug string.

Verified against a live event, not just read: sent a real 1 wUSDT
transfer from Treasury_1 to the multisig and confirmed the decoder
correctly extracted and matched the multisig's AccountId32 bytes from
the resulting Assets.Transferred event.
2026-07-13 17:43:16 -07:00
Satoshi Qazi Muhammed 45e4b26068 feat(usdt-bridge): migrate custody from single hot key to 3-of-5 multisig
The single-key custodial model (see prior commit's own doc comment:
"Custodial: A single keypair controls both sides") was replaced with
a 3-of-5 multisig spanning the Serok/SerokiMeclise/Xezinedar/Berdevk/
Noter tiki role-holders (full record in res/validators-tiki.md,
outside this repo). Discovered along the way: wUSDT's ~100M supply
had zero real USDT backing on Polkadot Asset Hub - Treasury_1 (the
actual asset owner, not the seed_path wallet this tool assumed) held
0 USDT/DOT there, with ~99M of the 100M supply sitting undistributed
in Treasury_1's own account. Migrated Pezkuwi Asset Hub custody in one
atomic batch (transfer 1M wUSDT to the multisig, set_team, then
transfer_ownership last) signed by Treasury_1 - tx
0xd5dc550c5cf04f323e5c1d9c13f2056acafc9f56404c657b9743c15ff3a3aacb,
verified on-chain.

listen_deposits/listen_withdrawals no longer sign and submit
mint/burn/transfer - no key this process holds has authority over the
multisig account. They now only detect on-chain events and record
status=pending_multisig_approval with the exact call parameters an
operator needs; actual approval happens through pwap-web's existing
multisig UI (shared/lib/multisig.ts, shared/lib/usdt.ts), not this
relayer. BridgeConfig gained custody_multisig_polkadot/_pezkuwi
fields.

Added pezkuwi-subxt/pezkuwi-subxt-signer alongside the existing
vanilla subxt dependency: vanilla subxt's codegen hardcodes the
`sp_runtime` module path and its default signed-extension handling
produced "bad signature" on submission against this chain's actual
metadata - pezkuwi-subxt (following vendor/pezkuwi-subxt's own
grant_noter_tiki.rs example: PezkuwiConfig, XCM v3, sudo_unchecked_weight)
is what actually signs and submits successfully. Also used to grant
the Xezinedar and Berdevk tiki roles (previously missing signatories
for the multisig) to two validators via a relay-chain sudo XCM Transact,
since the People chain has no local Sudo pallet.
2026-07-13 16:59:20 -07:00
Satoshi Qazi Muhammed cc246664ee fix(usdt-bridge): replay protection, real recipients, working withdraw path, reserve check
The withdrawal direction (burn wUSDT -> release real USDT) had no
listener at all - nothing watched Pezkuwi for redemption deposits, and
the manual ProcessWithdrawals command was an empty stub. The deposit
listener also had two real bugs: it minted to the bridge's own address
instead of the sender's (a leftover "for demo" placeholder), and it
never checked whether a deposit had already been processed before
minting again.

- Add is_deposit_processed/is_withdrawal_processed idempotency checks,
  enforced before every mint/burn/release, not just recorded after.
- Fix listen_deposits() to mint to the sender's actual Pezkuwi address
  (same AccountId32, re-encoded under Pezkuwi's SS58 prefix - both
  chains share one derivation, so no memo/registration is needed) and
  to persist processed deposits to the database, which it never did.
- Add listen_withdrawals(), mirroring the deposit listener: detects
  wUSDT sent to bridge custody, burns it, verifies the Polkadot
  custody wallet actually holds enough real USDT before releasing
  (circuit breaker - an under-collateralized peg must halt loudly,
  not silently short-pay), then releases to the sender's Polkadot
  address.
- Add max_single_tx cap so one bad/anomalous event can't move an
  unbounded amount automatically.
- Add a Reserves command reporting real custody balance vs circulating
  wUSDT supply - the exact invariant that, when broken, is what the
  wallet's "waiting for liquidity" banner was masking.
- load_keypair() now prefers BRIDGE_SEED_MNEMONIC (meant to be
  Vault-injected, matching how the exchange's hot wallets are already
  provisioned) over the plaintext seed_path file, which remains only
  as a local/dev fallback with a loud warning.

Flagged, not fixed here: the event-matching in both listeners
(Debug-formatted Value string containing a slice of the bridge's SS58
text) is unverified against a live node in this environment and should
be replaced with subxt's typed/codegen API before this handles real
value. Custody is still single-keypair (no multisig/MPC threshold
signing) - that is an infrastructure/key-ceremony decision, not a code
change, and is the highest-priority follow-up.
2026-07-13 13:03:16 -07:00
Kurdistan Tech Ministry 0e809c3a74 sec: remove hardcoded mnemonics, add mainnet tools and subxt examples
- Replace all hardcoded wallet mnemonics with env variable reads
- Add comprehensive e2e test suite (tools/e2e-test/)
- Add zagros validator management tools
- Add subxt examples for mainnet operations
- Update CRITICAL_STATE with zagros testnet and mainnet status
- Fix people chain spec ID and chainspec build script
2026-02-16 08:18:26 +03:00
Kurdistan Tech Ministry f249190090 feat: add mainnet chain spec builder script
Generates relay + teyrchain chain specs with embedded genesis data.
2026-02-15 01:12:21 +03:00
Kurdistan Tech Ministry fdf72b4055 feat: add mainnet deployment script with session key injection
Automates: stop nodes, archive old chain data, deploy binaries,
inject session keys from MAINNET_WALLETS.json, start nodes sequentially.
2026-02-14 23:43:57 +03:00
Kurdistan Tech Ministry 23d52f2f98 feat: add chain-spec-tool and usdt-bridge utilities
chain-spec-tool:
- CLI utility for chain spec manipulation and validation
- Used for mainnet chain spec generation and verification

usdt-bridge:
- Custodial bridge for wUSDT token operations
- Handles USDT wrapping/unwrapping between external chains and Asset Hub
- Configuration in bridge_config.json
2026-01-25 19:43:33 +03:00