Commit Graph

183 Commits

Author SHA1 Message Date
Matt Hess 1b9e8b6e64 Fix JSI backend missing x25519ScalarMult — broke all CARROT scanning
The JSI (React Native) crypto backend was missing the x25519ScalarMult
  method. The native function existed on __SalviumCrypto (registered in
  js_engine.dart) but JsiCryptoBackend never wrapped it.

  Impact: carrotEcdhKeyExchange() calls x25519ScalarMult via the crypto
  provider. On JSI, this called undefined(), silently failing the ECDH
  key exchange. With a bad shared secret, every CARROT output failed the
  view tag check and was rejected — resulting in zero balance for any
  wallet syncing CARROT-era blocks through the Flutter/QuickJS path.

  Also fix integration-sync.test.js unlocked balance calculation:
  the manual balance path checked txType === 'miner' (string) but
  wallet-sync stores TX_TYPE.MINER = 1 (number), so coinbase outputs
  were never identified as coinbase — they used the 10-block unlock
  window instead of the correct 60-block coinbase maturity. Now
  delegates to WalletOutput.isUnlocked() which handles both forms.
2026-02-13 14:51:22 +00:00
Matt Hess 56fbea363c Crypto layer overhaul 14:12:05 [18/529]
─────────────────────
  - Rebuild WASM binary via wasm-pack — adds batch exports that were missing
    from stale binary (cn_subaddress_map_batch, carrot_subaddress_map_batch,
    derive_carrot_keys_batch, compute_carrot_view_tag, decrypt_carrot_amount,
    parse_extra, serialize_tx_extra, compute_tx_prefix_hash, and 8 more)
  - Deprecate JS backend scalar/point ops — JS backend now provides hashing
    only (keccak256, blake2b, sha256); all EC math requires WASM or FFI
  - Add initCrypto() entry point that loads WASM automatically at startup
  - Wire batch subaddress, CARROT key derivation, CARROT helpers, and
    tx_extra parsing/serialization through all four backends (WASM/FFI/JSI/JS)
  - Extend crypto provider with 15 new delegating functions for the
    unified backend interface

  FFI use-after-free fix (critical)
  ─────────────────────────────────
  - Fix Bun segfault / SIGILL crash when using CRYPTO_BACKEND=ffi:
    toArrayBuffer() from bun:ffi returns a zero-copy VIEW of Rust-owned
    heap memory. Calling salvium_storage_free_buf() afterwards left the
    Uint8Array pointing to freed memory — classic use-after-free.
    With small buffers the freed memory wasn't reused (masking the bug);
    at 50×200 subaddress scale (~400KB) the allocator reused immediately,
    corrupting data and crashing with SIGILL at 1.11GB RSS.
  - Fix: .slice() after toArrayBuffer() to copy into JS-owned memory
    before freeing the Rust buffer. Applied to all 4 affected call sites:
    cnSubaddressMapBatch, carrotSubaddressMapBatch, parseExtra,
    serializeTxExtra.

  Subaddress batch fallback hardening
  ────────────────────────────────────
  - Log warnings (not silent catch) when batch WASM/FFI calls fail,
    so the fallback to per-item JS loop is visible in console output

  Rust crate (salvium-crypto)
  ───────────────────────────
  - Add crates/salvium-crypto/src/subaddress.rs — batch CN + CARROT
    subaddress map generation with #[wasm_bindgen] + #[no_mangle] FFI
  - Add crates/salvium-crypto/src/carrot_keys.rs — batch CARROT key
    derivation (full + view-only) from master secret
  - Add crates/salvium-crypto/src/tx_format.rs — tx_extra parse/serialize
    and tx prefix hash computation
  - Extend ffi.rs with 15 new extern "C" entry points for the above
  - Extend lib.rs with matching #[wasm_bindgen] entry points

  Test suite updates
  ──────────────────
  - Add initCrypto() to all test files that perform key derivation or
    EC math (wallet-class, persistent-wallet, integration-sync, keys,
    keyimage, subaddress, scanning, address, transaction, etc.)
  - Fix integration-sync.test.js: always init WASM unless FFI explicitly
    requested (JS backend can't do scalar/point ops post-deprecation)
  - Rewrite test/full-testnet.js: replace hardcoded phase functions with
    data-driven FORKS[] table covering all 10 hard forks (HF1–HF10),
    WASM miner probes at every fork boundary, per-fork TX tests with
    era-appropriate asset types and address formats, --resume-from and
    --skip-mining CLI flags

  README
  ──────
  - Update JS backend description: "Hashing only (keccak, blake2b)"
  - Add initCrypto() requirement to Quick Start and all code examples
  - Add full-testnet commands to Testing section
2026-02-13 14:16:33 +00:00
Matt Hess e96904e3bb Match C++ tag 0x01 vs 0x04 selection in serializeTxExtra
The C++ store_carrot_ephemeral_pubkeys_to_extra() omits tag 0x01 and
  writes only tag 0x04 when outputs have distinct per-output D_e values.
  The JS serializer unconditionally wrote both tags, producing non-standard
  extra fields that masked the multi-output scan regression on testnet.
2026-02-13 00:07:43 +00:00
Matt Hess 070901f2dd Fix CARROT sync regression: early view tag rejection for multi-output txs
Multi-output CARROT transactions store per-output D_e in additional_pubkeys
  (tag 0x04) with no tx_pubkey (tag 0x01), so the single-D_e pre-computation
  path was skipped entirely — every output hit the full _scanCarrotOutput path
  (~40ms each via FFI). Added per-output X25519 + 3-byte view tag rejection
  that restores post-fork sync from ~10 blk/s back to ~900+ blk/s.

  Also fixed parseExtra to handle 0xDE (minergate tag) and gracefully skip
  unknown tags via varint-length instead of aborting all remaining extra bytes.
2026-02-13 00:01:18 +00:00
Matt Hess ddeb249b09 Fix stale balance cache surviving wallet rescan
WalletSync.rescan() cleared storage but never notified PersistentWallet,
   so _balanceCache kept returning pre-rescan balances until new outputs
   arrived. Emit storageCleared event immediately after clear() and flush
   the cache on receipt.
2026-02-12 21:42:13 +00:00
Matt Hess bcfe173261 Add FFI CARROT scanner integration reference for Dart/Flutter consumers 2026-02-12 21:29:26 +00:00
Matt Hess 947f64eba9 Move CARROT output scanning to Rust FFI to eliminate JS↔Rust hairpinning
Route x25519ScalarMult through the crypto provider backend instead of
  using a pure-JS BigInt implementation inline in carrot-scanning.js.
  Add a full 7-step CARROT scanner in Rust (carrot_scan.rs) with two FFI
  entry points (standard X25519 ECDH and self-send paths), so wallet-sync
  can execute the entire scan in a single native call when the FFI backend
  is active, falling back to the existing JS pipeline otherwise.
2026-02-12 19:24:18 +00:00
Matt Hess 54a3847308 ● Fix variable shadowing in burn-in test after CARROT address fix
The previous commit introduced `const h` for CARROT height detection in
  phaseCN, shadowing the existing `let h` used throughout the function.
  Changed to `let` so subsequent reassignments compile.
2026-02-12 18:47:25 +00:00
Matt Hess 102f65b180 Reject legacy addresses at CARROT heights, fix wallet B sync bug
After HF10 legacy CryptoNote addresses cannot be used — CARROT outputs built
   with legacy pubkeys are undetectable by the receiver's CARROT scanner because
   the CN view key differs from the CARROT viewIncomingKey. transfer() and sweep()
   now fail fast with a clear error when a SaLv... address is used post-HF10,
   using the network-aware hard fork table (not hardcoded heights).
2026-02-12 17:49:04 +00:00
Matt Hess e1e656bd33 Pass explicit assetType to all wallet API calls in integration tests
After removing SAL defaults from the wallet API, four test files were
  broken. Add era-aware assetType (SAL pre-HF6, SAL1 post-HF6) to every
  transfer, stake, burn, sweep, and getStorageBalance call. Update fmt()
  helper to accept an asset label parameter. Add wallet-b.json fallback
  in bidirectional-test.js.
2026-02-12 00:18:42 +00:00
Matt Hess 58751e6271 Add SQLCipher storage backend via Rust FFI for mobile wallet sync performance 2026-02-11 21:57:48 +00:00
Matt Hess 7cc64501c4 Add AES-256-GCM
encrypt/decrypt to Rust FFI for Dart cache pipeline

   Exposes salvium_aes256gcm_encrypt and salvium_aes256gcm_decrypt via the
   native shared library. Encrypt generates a random 12-byte nonce
   internally; output is nonce||ciphertext||tag (input_len + 28 bytes).
   Dart side: gzip → FFI encrypt → disk, disk → FFI decrypt → gunzip.
2026-02-11 19:18:12 +00:00
Matt Hess 45f82112dc Fix 7 blocking wallet API issues from audit
Unify the two incompatible sync systems so getBalance/getUTXOs read from
   _storage (syncWithDaemon path) when available, falling back to legacy
   _utxos. Fix Account class calling non-existent wallet methods, add
   getTransactions() API, implement CARROT subaddress spending, generate
   _dataKey for restored wallets, and bridge WalletSync events to
   WalletListener.
2026-02-11 17:36:28 +00:00
Matt Hess eae8555364 Fix invalid point panic spam, add Bun FFI crypto backend
Replace .expect() with match on all point decompression in lib.rs —
  returns empty Vec instead of panicking on invalid Ed25519 points.
  During sync, generate_key_derivation is called for every tx on chain
  and most have invalid pubkeys; the old code threw hundreds of thousands
  of WASM exceptions (potential QuickJS crash vector). Now returns null
  with zero exceptions in the hot path.

  Also adds native Rust FFI backend (bun:ffi) as a third crypto code
  path for debugging sync issues alongside WASM and JS backends.
2026-02-11 05:14:12 +00:00
Matt Hess 0c5edadf79 updated gitignore 2026-02-11 04:35:34 +00:00
Matt Hess 1bee9a1391 updated gitignore 2026-02-11 04:25:26 +00:00
Matt Hess 8d297db5cf Fix coinbase unlock: miner/protocol outputs require 60 confirmations
Salvium coinbase outputs set unlock_time=60 (bare constant), not
   height+60. WalletOutput.isUnlocked() was treating this as "unlock at
   block 60" which is always true, causing ~12 SAL overcounting vs C++
   wallet. Now checks txType for miner/protocol and applies the 60-block
   confirmation window from blockHeight.
2026-02-11 04:22:12 +00:00
Matt Hess 01e07c2222 Add Argon2id to Rust crypto crate, fix CARROT-era mining address
- Add X25519 Montgomery ladder to Rust crate with Salvium's non-standard
     clamping (only clears bit 255, unlike RFC 7748). Fix ref10/RFC formula
     mismatch (BB not AA with a24=121666). WASM + FFI + C header wired.
   - Add internal CARROT scanning path (scanCarrotInternalOutput) using
     s_view_balance as shared secret for detecting self-send/change outputs.
   - Add return output map for STAKE: pre-compute K_r from self-send outputs,
     detect matching protocol_tx return outputs.
   - Fix coinbase unlock_time: Salvium sets bare constant 60, not height+60.
     Apply CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW as relative confirmation count.
2026-02-11 04:17:27 +00:00
Matt Hess 3a90f78ab9 Fix CARROT main address outputs silently rejected during sync
recoveredSpendPubkeyHex was block-scoped inside the subaddress else-if
  branch but referenced in the return statement, causing a ReferenceError
  for outputs sent to the main CARROT address (K_s). The exception was
  silently caught, making all main-address mining rewards invisible.
2026-02-10 23:35:17 +00:00
Matt Hess 6904ee60a4 fixed gitignore issue 2026-02-10 22:40:13 +00:00
Matt Hess 9d3e93ebe3 Fix BP+ proof generation with WASM backend, fix 7 stale test expectations
ensureBytes() silently converted BigInt mask scalars to all-zeros via
  Uint8Array.set(BigInt), causing Bulletproofs+ proofs to use wrong
  commitments. The daemon correctly rejected these transactions. Also parse
  WASM proof results into full field objects and fix test expectations for
  upper-median, RCT type constants, and Pedersen commitment properties.
2026-02-10 22:32:06 +00:00
Matt Hess 530b3ad3c9 Wire Argon2id through crypto provider, rebuild WASM with Rust binding
wallet-encryption.js was importing argon2id directly from @noble/hashes,
  bypassing the crypto provider entirely and always using the slow pure-JS
  implementation. Now routes through the provider so WASM (Rust) and JSI
  (native) backends are used when available, with Noble as a last-resort
  JS fallback that emits a console.warn.
2026-02-10 19:28:12 +00:00
Matt Hess 293e6f6b96 Fix wallet sync performance cliff at ~9000 blocks, fix node switching
Yield to event loop every 5 blocks (was 20) to prevent UI freeze on
  mobile during batch processing.

  Preserve WalletSync instance across setDaemon() calls — update daemon
  reference instead of destroying sync state, listeners, and progress.
  Stop in-progress sync cleanly before switching.

  Replace bytesToHex Array.from().map().join() with pre-computed lookup
  table across all hot paths (address.js, backend-wasm.js, mining.js,
  subaddress.js). Eliminates ~400k intermediate allocations per full sync.

  Add binary Uint8Array comparison for main address (0,0) in both CN and
  CARROT scanning — skips hex conversion for 99%+ of non-owned outputs.

  Add duplicate listener check in WalletSync.on() to prevent callback
  accumulation on sync restart.
2026-02-10 18:48:13 +00:00
Matt Hess 83f56bf269 Fix 14 audit bugs, verify CARROT D_e format, update README
CRITICAL: Fix RCT type constants (SalviumZero/One/FullProofs) and
  checkHash PoW byte order (LE words matching C++ difficulty.cpp).

  HIGH: Fix TXOUT_TYPE wire format (0x02/0x03/0x04), replace Math.random()
  with CSPRNG in decoy selection and BP+ batch verification.

  MEDIUM: Fix CryptoNote median, LWMA difficulty BigInt arithmetic,
  transaction hash (3-part H(prefix||rctBase||rctPrunable)), and
  fee estimation for Salvium TX types.

  LOW: Fix error message in getNetworkConfig, remove dead yMN in BP+.

  Verify CARROT D_e is X25519 Montgomery u-coordinate per C++ source
  (carrot_enote_types.h, format_utils.cpp) — no conversion needed.

  Update README with crypto backends, WASM performance, 25 test suites.
2026-02-10 18:23:36 +00:00
Matt Hess 137751f6d8 quick fix for global -> globalThis 2026-02-10 16:32:56 +00:00
Matt Hess 382f1f3cd4 Add CLSAG, TCLSAG, Bulletproofs+ to Rust crate with WASM/FFI bindings
Implement the three expensive cryptographic protocols in Rust for
  15-26x speedup over pure JS (Noble curves BigInt arithmetic):

  - CLSAG sign/verify: 286ms -> 15ms (18x, ring=16)
  - TCLSAG sign/verify: 257ms -> 10ms (26x, ring=11)
  - BP+ prove/verify: 2315ms -> 150ms (15x, 2 outputs)

  Rust crate (crates/salvium-crypto):
  - clsag.rs: CLSAG ring signatures with curve25519-dalek
  - tclsag.rs: Twin CLSAG with dual keys (x,y) and generator T
  - bulletproofs_plus.rs: Range proofs with optimized MSM
  - FFI wrappers + C header for JSI/NAPI consumers
  - WASM bindings via wasm-bindgen (326KB binary)
  - 46/46 Rust tests pass (13 new)

  JS layer:
  - All three backends (WASM, JSI, JS) wired for new operations
  - transaction.js delegates clsag/tclsagSign/Verify to backend
  - bulletproofs_plus.js delegates prove/verify to backend
  - JS backend returns null to signal fallback to pure JS
  - initCrypto() tries WASM first, falls back to JS

  Cross-backend tests confirm binary compatibility:
  sign with WASM, verify with JS (and vice versa) — 20/20 pass.
  All 25 existing test suites pass unchanged.
2026-02-10 16:23:16 +00:00
Matt Hess c931290f10 Add Argon2id to Rust crypto crate, fix CARROT-era mining address
- Add argon2 = "0.5" dependency and salvium_argon2id FFI function for
    native key derivation (wallet PIN → encryption key). Eliminates the
    pure-JS Argon2id bottleneck on QuickJS/mobile (~minutes → ~1-2s).
  - C header and JSI backend wired for Flutter FFI integration.
  - 33/33 Rust tests pass (2 new: RFC-params test + bad-params test).
  - Fix testnet suite: switch from legacy to CARROT address at HF10
    boundary (height 1100) for mining. Phase 6 CARROT TX tests passing
    (3 transfers, B→A, stake, burn all succeeded on-chain).
2026-02-10 06:31:28 +00:00
Matt Hess 4eb3062d9c Wire oracle.js to crypto provider, add QuickJS compatibility
- Oracle: replace Node.js crypto (createHash/createVerify) with crypto
    provider's verifySignature, eliminating the 'crypto' module dependency.
    verifyPricingRecordSignature and validatePricingRecord are now async.

  - Rust crate: add SHA-256 (all targets) and verify_signature with ECDSA
    P-256 + DSA support (native-only) to salvium-crypto. FFI bindings and
    C header updated. All 31 Rust tests pass.

  - Crypto backends: add sha256/verifySignature to JS (noble + WebCrypto
    fallback), WASM (noble + WebCrypto fallback), and JSI (native FFI).

  - QuickJS compat (6 issues):
    · catch {} → catch (_e) {} across 10 files
    · Buffer → bytesToHex/hexToBytes in consensus.js and validation.js
    · AbortController → Promise.race timeout in rpc/client, connection-
      manager, and oracle
    · URLSearchParams → manual query string in rpc/client and oracle
    · Promise.allSettled → Promise.all wrapper in connection-manager
    · btoa → _toBase64 with manual fallback in rpc/client
    · pemToDer base64 decoder with 3-tier fallback (Buffer/atob/manual)

  - Fix GammaPicker exclusion zone (60 → 10) matching C++ gamma_picker
  - Fix testnet TARGET heights for coinbase maturity
2026-02-10 05:42:11 +00:00
Matt Hess 01bcb742ae ● Restructure native module for Expo auto-discovery, fix 3 test failures
Move native code from native/ to Expo-compatible layout with android/
  and ios/ at package root. Add Android JNI bridge (OnLoad.cpp,
  ExpoSalviumCryptoModule.java, ExpoSalviumCryptoPackage.java).
  Prebuilt binaries now go in prebuilt/ instead of native/lib/.

  Test fixes:
  - wallet-sync: update stale DEFAULT_BATCH_SIZE assertion (10 -> 100)
  - bulletproofs+: fix hashToPoint -> hashToPointMonero rename in test,
    fix serializeProof to include V array matching parseProof format
  - transaction-builder: expect 2 outputs for exact-amount tx (zero-change
    output is always added for privacy)
2026-02-09 22:50:15 +00:00
Matt Hess 23afd269e1 Add React Native JSI crypto backend scaffold
Native Rust crypto via JSI for 100-200x faster mobile wallet scanning
  on Hermes (which doesn't support WASM). Adds:

  - Rust FFI layer: 27 extern "C" functions in ffi.rs wrapping
    curve25519-dalek ops, with staticlib crate-type for native linking
  - JS backend: JsiCryptoBackend class calling global.__SalviumCrypto
    (installed by native module), with 'jsi' case in setCryptoBackend()
  - C++ JSI module: HostObject scaffold extracting Uint8Array args,
    calling Rust FFI, returning Uint8Array results
  - Build scripts: iOS (lipo universal) and Android (NDK cross-compile)
  - Expo module scaffold: podspec + gradle + CMakeLists.txt
  - 27 Rust tests verifying FFI output matches direct Rust functions,
    including keypair derivation consistency check
2026-02-09 21:31:55 +00:00
Matt Hess 027226ab81 Fix binary sync path producing zero outputs
Use raw byte offsets for tx hash computation instead of broken
   re-serialization, and fix subaddress map hex key mismatch.
2026-02-09 21:03:31 +00:00
Matt Hess 32b60e09f6 Add live sync tests, fix MockDaemon for new batch engine
- Add sync-live.test.js: real daemon sync test with stats reporting
    (binary path, adaptive batching, throughput, tx counts)
  - Add sync-live-fallback.test.js: binary vs JSON fallback comparison
  - Fix MockDaemon getBlock() to return json field (needed by _syncBatch)
  - Fix DEFAULT_BATCH_SIZE assertion (100 → 10)
2026-02-09 15:44:55 +00:00
Matt Hess 99b7239d9d Optimize wallet sync: binary bulk fetch, parallel fallback, adaptive batching
Replace serial per-block RPC calls with binary bulk fetch via
  getBlocksByHeight (2 RPCs per batch regardless of size). Falls
  back to parallel JSON fetch with bulk tx retrieval when binary
  endpoint unavailable. Adaptive batch sizing tracks ms/block
  trend across batches — doubles when fast, halves when slow,
  nudges up when stable. Starts at 10 blocks, ramps to 500.
2026-02-09 15:36:17 +00:00
Matt Hess ba691182a0 ● Remove Hermes-incompatible WASM imports from crypto module
Drop static re-export of WasmCryptoBackend from crypto/index.js
  (import.meta breaks React Native/Hermes) and replace dynamic
  import() in setCryptoBackend('wasm') with a thrown error pointing
  to direct import for Node/Bun environments.
2026-02-09 06:27:06 +00:00
Matt Hess 7bb66ac1ac Shared cache WASM mining, delete broken JS VM files, fix browser field
- Share one 256MB RandomX cache across all worker threads via
     SharedArrayBuffer (correct light mode — was N×256MB per worker)
   - Workers receive shared WebAssembly.Memory + Modules via workerData,
     each creates its own VM with private scratchpad
   - Delete 11 broken pure JS RandomX VM files (vm.js, vm-jit.js,
     vm-wasm.js, aes.js, randomx-worker-js.js, debug/benchmark scripts)
   - Remove stale browser field from package.json (pointed to nonexistent file)
   - Tested: 3/3 blocks accepted at difficulty 2000 (~5.2 H/s, 2 threads)

   Note: WASM-JIT multi-thread scaling is limited by WebAssembly.Module
   compilation serialization in V8/JSC (~10 H/s single-thread ceiling).
   Use Rust miner for multi-threaded performance.
2026-02-09 06:10:31 +00:00
Matt Hess be714b4b72 Purge broken pure JS RandomX VM, optimize WASM miner, fix stale message race
Remove hand-written JS RandomX VM (vm.js, vm-jit.js, vm-wasm.js, aes.js)
  which never produced correct hashes — the vendored l1mey112/randomx.js
  WASM-JIT was always the working implementation. Simplifies mining to
  two backends: WASM-JIT and Rust.

  - Delete 11 files: broken JS VM, its worker, debug/benchmark scripts
  - Fix stale message race: add jobId filtering + handler lifecycle cleanup
    so late worker responses don't corrupt the next block's submission
  - Optimize WASM worker: precomputed target byte-array comparison instead
    of per-hash BigInt multiplication, increase chunk size to 500
  - Remove require('crypto') Node.js fallback from carrot.js, ed25519.js,
    crypto/provider.js (fixes Metro bundler issues)
  - Update README and index.js exports
2026-02-09 05:33:59 +00:00
Matt Hess 2916487aa9 Fix RandomX mining: byte-order check, worker job switching, multi-backend solo miner
- Fix little-endian difficulty check in randomx-worker.js (was big-endian,
    causing all mined blocks to be rejected by daemon)
  - Convert synchronous mine loop to async chunked with generation-based
    cancellation (eliminates stale nonce race condition between blocks)
  - Fix Rust miner light mode: workers now check for new jobs via try_recv
    instead of looping through entire u32 nonce space before accepting next job
  - Rewrite test/solo-miner.js with js/wasm/rust backends, light/full modes,
    proper worker pool management, and Rust child process integration
  - Add src/randomx/randomx-worker-js.js for multi-threaded pure JS mining
2026-02-09 04:20:02 +00:00
Matt Hess 4a67452239 Fix SAL/SAL1 asset type index mismatch, correct zeroCommit across all layers, add encrypted sync cache
- Fix transfer/sweep/stake/burn/convert rejecting with invalid_input: outputs
    were selected from SAL index space but TX declared SAL1, causing daemon to
    resolve ring members in wrong LMDB table (16/16 mismatch → 0/16 match)
  - Filter UTXO selection to exact asset type in all 5 spending functions
  - Fix zeroCommit to use blinding factor=1 (matching C++ rct::zeroCommit) in
    Rust WASM, JS backend, and serialization layer; rebuild WASM binary
  - Override CARROT coinbase mask to scalar 1 so ring signatures use correct
    blinding factor for mining outputs
  - Add wallet dataKey with AES-256-GCM encrypted sync cache at rest
  - Add areAssetTypesEquivalent/getSpendableAssetTypes consensus helpers
2026-02-08 23:56:30 +00:00
Matt Hess 82fbd6964a Fix stale sync cache, add wallet password change, add test runner scripts
- wallet-store.js: Reconcile _spentKeyImages with output isSpent flags
    on load() to prevent stale caches from causing double-spend rejections
  - wallet-encryption.js: Add reEncryptWalletJSON() for password changes
    with fresh salts and KEM keypair
  - wallet.js: Add Wallet.changePassword() static method, import update
  - index.js: Export reEncryptWalletJSON
  - test-helpers.js: loadWalletFromFile() auto-detects encrypted wallets
    and reads sibling .pin file for decryption
  - wallet-encryption.test.js: Add password change tests
  - test/run-unit.sh, run-integration.sh, run-all.sh: Shell wrappers
    for running unit, integration, and full test suites
2026-02-08 20:14:54 +00:00
Matt Hess 60cf578eb2 Refactor tests to use Wallet class, add PQ encryption, fix sweep spentKeyImages
- Refactor all test files to use Wallet.fromJSON/setDaemon/syncWithDaemon/transfer/sweep
     instead of manual MemoryStorage/createWalletSync/markOutputSpent wiring
   - Extract shared test helpers (getHeight, waitForHeight, fmt, short, loadWalletFromFile)
     into test/test-helpers.js
   - Add ML-KEM-768 + Argon2id + AES-256-GCM hybrid post-quantum wallet encryption
     (src/wallet-encryption.js, 47 unit tests)
   - Fix sweep() not returning spentKeyImages, causing wallet._markSpent() to silently
     skip marking spent outputs after sweeps (led to double_spend errors on next sweep)
   - Fix megaSweep test helper to always wait for maturity between rounds
   - Burn-in tested: 1295 TXs across CN and CARROT eras (1000 CARROT micro transfers,
     stakes, burns, sweeps) with 98% success rate (failures all sweep-related pre-fix)
2026-02-08 01:09:02 +00:00
Matt Hess c159b04e35 Fix sweep TX rejection, CARROT scanning, CLSAG verification, and add burn-in test suite 2026-02-07 19:32:23 +00:00
Matt Hess ccfe5252f6 Fix CARROT protocol roundtrip, verify all TX types on testnet, clean up debug logging
- Add CARROT output creation to buildTransaction for rctType >= 9 (X25519 ECDH,
    viewTag, encryptedJanusAnchor)
  - Fix scanner to use txPubKey from tx_extra as D_e instead of p_r (mask commitment)
  - Fix scReduce32 → scReduce64 in carrot-output.js (WASM sc_reduce32 silently
    truncated 64-byte blake2b outputs, producing wrong scalars)
  - Thread viewSecretKey through buildConvertTransaction
  - Remove 31 debug console.log statements across transaction.js, transfer.js,
    wallet-sync.js, and carrot-scanning.js
  - Verified on testnet: 16 transfers, stake, burn, and 36-input sweep all accepted
    by daemon with correct TCLSAG signatures and RCT sum checks
2026-02-05 20:13:30 +00:00
Matt Hess 8dd13b6f10 Fix transaction signing, wallet sync, and stake balance bugs
- CLSAG signing mask: scSub(inputMask, pseudoMask) not reverse
  - Wallet-sync: convert spendSecretKey to bytes for key image calc
  - Stake: subtract amountBurnt from change amount
  - Sweep: limit to 60 inputs to stay under tx weight limit
  - Add spentKeyImages tracking to stake function
2026-02-03 18:45:56 +00:00
Matt Hess 949719c2e7 Add transfer/sweep/stake API, integrate BP+ proofs, dual CN+CARROT wallet keys
- Wire up full transaction lifecycle: UTXO selection → input prep → build → serialize → broadcast
   - Integrate bulletproofPlusProve() into buildTransaction() (replaces placeholder)
   - Fix BP+ serialization to match Salvium binary format (varint-prefixed vectors)
   - Fix serializeTransaction() ordering to match Salvium consensus (prefix → RCT base → prunable)
   - Create src/wallet/transfer.js with transfer(), sweep(), stake() high-level functions
   - Fix getOutputIndexes to use portable storage binary format
   - Fix prepareInputs to use getOutputDistribution for decoy selection
   - Handle coinbase outputs with null mask (identity scalar + zeroCommit)
   - Refactor Wallet class to always derive BOTH legacy CN and CARROT keys from seed
     (matching Salvium C++ carrot_and_legacy_account::generate behavior)
   - Add getLegacyAddress(), getCarrotAddress(), height-based auto-format in getAddress()
   - Bump wallet JSON to v3 with both addresses and full CARROT key set
   - Backward-compatible: old wallet files with seed upgrade seamlessly
2026-02-02 01:00:11 +00:00
Matt Hess fe0e8af07d Add connection manager with latency-based server selection and failover
- Race multiple RPC servers on connect, pick fastest responder
  - Automatic failover to next-fastest on network errors
  - Re-race when latency degrades beyond 2x baseline
  - Ranked failover order maintained after each race
  - Hardcoded Salvium seed nodes (seed01-03.salvium.io) per network
  - Supports single URL (no overhead), multiple URLs, or network name
  - Comma-separated DAEMON_URL env var for multi-server configs
  - Backward compatible: existing single-URL usage unchanged
2026-02-01 20:49:09 +00:00
Matt Hess 6cb5f55d4c Add cross-platform build system and CI/CD release pipeline
- Add setup.sh (Unix) and setup.bat (Windows) build scripts that detect
    platform, check dependencies, and build all components (JS, WASM, miner)
  - Add GitHub Actions CI workflow (test on push/PR)
  - Add GitHub Actions release workflow that builds static miner binaries
    for Linux x86_64/aarch64, macOS Intel/Apple Silicon, and Windows
  - Switch miner from OpenSSL to rustls for zero runtime dependencies
  - Fix wallet-sync batch fetching (remove broken .bin binary endpoint,
    use sequential JSON-RPC get_block calls)
  - Add portable storage binary format serializer/deserializer for future
    batch RPC support
  - Add postBinary() to RPC client for binary endpoint communication
  - Update package.json: bun-only runtime, remove node/npm references
2026-02-01 20:05:24 +00:00
Matt Hess 244bfb78a2 ● Add native Rust RandomX miner with IPC mode and JS mining scripts
Native miner achieves ~360 H/s/thread (vs ~10 H/s in WASM) using
  hardware AES-NI/AVX2 via direct RandomX C FFI. Includes multi-threaded
  mining with shared 2GB dataset, JSON-lines IPC protocol for parent
  process control, and JS-based mining scripts for testnet.
2026-02-01 17:50:07 +00:00
Matt Hess 3690068cad Added RUSTFLAGS="-Ctarget-feature=+simd128" flag to the build:wasm-crypto script 2026-02-01 04:27:05 +00:00
Matt Hess f026e4c824 Fix 4 pre-existing test bugs in transaction, parser, and wallet-sync tests
- transaction.test: getFeeMultiplier(0) expects 5n (priority 2/Normal)
    matching C++ wallet2.cpp fee algorithm >= 2, not 1n (priority 1)
  - transaction-parser.test: Add missing txType and rct_type bytes to
    minimal tx test data (Salvium prefix is longer than Monero's)
  - transaction-parser.test: Use txnFee (parser's field name) not fee
    in summarizeTransaction mock data
  - wallet-sync.test: Add getBlocksByHeight stub to MockDaemon so the
    existing individual-fetch fallback path is exercised
2026-02-01 04:20:08 +00:00
Matt Hess a8185b6e31 Remove last direct crypto imports from consumer files
Rewire signature.js to use provider functions (scCheck, scSub, scReduce32,
  doubleScalarMultBase with compressed bytes) instead of ed25519.js internals.
  Remove unused pointFromBytes/pointToBytes import from carrot-scanning.js.
  No consumer files now import directly from ed25519/keyimage/keccak/blake2b
2026-02-01 03:45:50 +00:00