Memperkeras jalur uang: keamanan transaksi di settlement KVR

Tiga kelas kerentanan nyata — replay tanda tangan pembayaran, pencetakan imbalan tanpa autentikasi, dan double-spend karena race — ditemukan, dieksploitasi dalam tes, dan ditutup di layanan settlement gateway.

Di sebuah DePIN, jalur uang sama bermusuhannya dengan jalur komputasi: setiap endpoint yang mengkredit KVR cepat atau lambat akan dijajal oleh seseorang yang ingin KVR tanpa bekerja. Pemeriksaan keamanan atas layanan settlement gateway — proses yang memverifikasi pembayaran on-chain dan mengkredit stake, imbalan node, serta biaya inferensi — menemukan dan menutup tiga kelas kerentanan nyata. Masing-masing didemonstrasikan dengan tes bergaya exploit sebelum perbaikan dan diverifikasi ulang sesudahnya.

Model kepercayaan

Verifikasi fakta on-chain, bukan klaim klien

Model kustodi Kvasir menaruh kunci pada pengguna: dompet menandatangani transaksi, Solana mencatatnya, dan tugas layanan settlement adalah memverifikasi apa yang benar-benar terjadi di chain sebelum menyentuh saldo. Pembayaran mengikuti *quote → payment → inference*, dengan setiap tanda tangan transaksi yang terpakai dicatat di registri sekali-pakai usedSignatures sehingga tak pernah bisa diajukan dua kali. Itu menjadikan layanan settlement titik sempit — dan aturan yang tak boleh dilanggarnya: hanya kreditkan yang dibuktikan chain, jangan pernah yang diklaim klien. Layanan ini memang memegang dana di devnet: KVR yang di-stake dan kredit prabayar berada di treasury-nya dan dicatat di buku besarnya hingga program staking on-chain dirilis.

A settlement vault guarded by three locks: sender binding, trusted reporter, and a serialization gate
Perbaikan #1 · pengikatan pengirim

Ikat pembayaran ke pembayarnya

Tanda tangan Solana bersifat publik. Jalur verifikasi stake hanya memeriksa bahwa vault *menerima* KVR yang diharapkan — tak pernah *siapa yang mengirimnya*. Penyerang bisa mengamati transfer KVR→vault milik korban di devnet, lalu mengajukan {owner: penyerang, signature: milik korban}: pemeriksaan penerimaan vault lolos, pokok dikreditkan ke penyerang, dan satu unstake kemudian dananya jadi milik penyerang. Pencurian langsung, hanya bermodal block explorer.

Perbaikannya: KVR harus didebit dari akun token yang dimiliki owner yang dikreditkan.

verifyStakeTransfer(signature, owner, amount):
  delta(vault)  >= amount            # vault actually received it (old check)
  Σ debits from token accounts
    whose owner == credited owner    # NEW — sender binding
                >= amount            # summed across that owner's accounts
  # inference path (no owner): bound by private requestId
  # + one-shot usedSignatures instead
Perbaikan #2 · pelapor tepercaya

Imbalan hanya dari sumber terautentikasi

Endpoint imbalan node mencetak KVR yang dapat diklaim dari masukan yang dilaporkan sendiri: POST /api/node/contribution mengkredit units apa pun yang diklaim klien — units: 1e9 plus satu panggilan claim bisa menguras vault — dan register/heartbeat mempercayai peran hub/gateway yang diaku sendiri (imbalan infra per jam) serta skor performa (pengali imbalan). Perbaikannya menempatkan setiap pernyataan yang memengaruhi imbalan di balik pelapor tepercaya: hanya token layanan M2M yang dipakai poll kontribusi hub, atau admin terautentikasi, yang boleh menyatakan units, peran infra, atau tingkat performa — ditegakkan bahkan dalam mode LAN terbuka, karena ini mencetak KVR. Perbandingan token berjalan waktu-konstan, dan penautan dompet↔node tetap bebas; ia hanya tak bisa lagi menyatakan imbalannya sendiri.

Perbaikan #3 · serialisasi settlement

Satu penulis untuk tiap saldo

Status settlement adalah read-modify-write tanpa kunci, dan setiap operasi uang *await* pembayaran atau verifikasi on-chain di tengah jalan — menyerahkan event loop dengan saldo basi di tangan. Dua claim serentak bisa membaca saldo tertunda 100 KVR yang sama dan sama-sama membayarkannya. Bukan teori: tes exploit menunjukkan tiga claim serentak membayar 300 untuk saldo 100.

Serialisasi asinkron per kunci: operasi uang dengan kunci sama berjalan ketat berurutan.

withLock(key, fn)         # per-key promise chain, self-cleaning map
  stake / unstake / claim  → keyed by owner
  inference settlement     → keyed by requestId
inside the lock:
  usedSignatures check + credit   # no same-signature double-credit
  pay out FIRST, then debit       # failed payout leaves balance intact
Pertahanan berlapis

Posisi tiap lapisan sekarang

LapisanMekanisme
IdentitasLogin tanda tangan dompet SIWS atas nonce server + 2FA TOTP + kode cadangan sekali-pakai
TransportNode token turunan dompet pada unduhan shard; kesepakatan sidik jari build di relay
PembayaranPengikatan pengirim pada transfer stake; usedSignatures sekali-pakai; requestId privat pada inferensi
SettlementKunci per-kunci di setiap penulisan saldo; bayar dulu baru debit; pengajuan ulang idempoten
PelaporanFakta yang memengaruhi imbalan hanya dari token layanan M2M atau admin, dibandingkan waktu-konstan
KustodiDompet non-kustodial — layanan hanya bisa memindahkan isi vault, tak pernah kunci pengguna
Diukur, bukan diasumsikan

Setiap perbaikan membawa tes exploit-nya sendiri

  • Memutar ulang tanda tangan transfer korban di bawah dompet penyerang kini ditolak ("not sent by owner"); stake sah, klaim-berlebih, dan pembayaran inferensi berperilaku tak berubah.
  • Tiga claim serentak terhadap satu saldo membayar tepat sekali; mengajukan ulang inferensi yang sudah dibayar mengembalikan hasil sama secara idempoten.
  • units, peran hub/gateway, dan tingkat performa yang diaku sendiri oleh klien tanpa autentikasi tak lagi menggerakkan satu lamport imbalan pun.

Benang merah ketiga perbaikan adalah satu prinsip yang diterapkan tiga cara: chain adalah sumber kebenaran, layanan adalah verifikator, dan tiap saldo punya tepat satu penulis. Layanan settlement masih berjalan di Solana devnet — persis tempat Anda ingin menemukan, mengeksploitasi, dan memperbaiki kelas-kelas ini sebelum mainnet menaikkan taruhannya.