- Schnorr signatures (CryptoNote-style) for authenticating KEX messages
- SignedKexMessage wrapper with ephemeral keypair matching C++ multisig_kex_msg
- Binary wire format: magic + base58(Epee) for KEX, raw Epee for TX sets
- TCP transport coordinator with star topology and length-delimited framing
- 32 new tests across all four modules
- New mainnet_validation.rs: 9 tests that sample blocks/TXs from every
hard fork era (HF1–HF10) against public mainnet daemons, covering
block parsing, header validation, tree hash, TX parsing, serialize
roundtrip, CLSAG/TCLSAG signature verification, tamper detection,
and BP+ structure checks.
- Extract shared verification helpers (fetch_and_parse_tx, fetch_mix_ring,
prepare_verification_data, etc.) into tests/common/mod.rs to avoid
duplication between testnet and mainnet test files.
- Refactor rct_verify_testnet.rs to use mod common.
- All tests are #[ignore] — run with:
cargo test -p salvium-tx --test mainnet_validation -- --ignored --nocapture
- Replace simplified challenge computation in partial_sign() with proper
CLSAG ring traversal via new ClsagContext (mirrors C++ CLSAG_context_t)
- Add MultisigTxBuilder for constructing PendingMultisigTx with signing
contexts (pseudo-outputs, BP+ proof, signing message, fake responses)
- Enhance PendingMultisigTx with per-input signing contexts and ring data
so other signers can reconstruct ClsagContext for partial signing
- Update wallet sign_multisig_tx() to use proper partial_sign() with ring
traversal instead of dummy sc_mul_sub
- Expose inv_eight() from salvium-crypto for commitment image computation
- Fix iOS build: set CROSS_TOP/CROSS_SDK for openssl-src and per-target
CC/CFLAGS/AR/RANLIB for cc crate (SQLCipher) cross-compilation
- Fix cargo fmt across workspace
macOS GitHub Actions runners ship bash 3.2, which does not support
declare -A (requires bash 4.0+). Rewrote build-ios.sh and
build-android.sh to use parallel indexed arrays instead.
Add salvium_txs table to track change output Kos from CONVERT/STAKE/AUDIT
TXs. During sync, populate this map and use it to perform two-step key
derivation for pre-CARROT PROTOCOL return outputs (C++ m_salvium_txs
equivalent). This fixes scanning of blocks between HF6-HF10 where
returned staked/audited/converted funds were invisible or had wrong key
images.
Replace difficulty_to_target() stub with bit-by-bit long division of
(2^256-1)/difficulty. Add 5 unit tests including round-trip verification
against check_hash().
Consolidate the overlapping release.yml and build-release.yml into one
workflow. Adds pre-release test gate (tests + clippy), miner binary
builds, SHA-256 checksums, and creates releases as drafts.
This covers the 15 clippy fixes (redundant closures, unnecessary casts, identity map_err), 16 rustdoc fixes (unresolved bracket links, bare URLs), and formatting across 11 files in salvium-crypto, salvium-wallet,
salvium-tx, salvium-ffi, and salvium-multisig.
- Version all crates at 1.0.7-r001 (tracking Salvium C++ 1.0.7)
- Source-available license: free for author/designees and non-commercial
use; annual commercial license required for any revenue-generating use
- Build scripts for Android/iOS/Linux/macOS/Windows producing both
libsalvium_crypto and libsalvium_ffi
- CI workflow publishes platform archives to salvium-rs-releases
- 23 new FFI functions: storage ops, address book, tx notes, attributes,
staked balance, key derivation, mnemonic
- Fix stake detection: count owned outputs regardless of spent state
- Auto-resume testnet integration tests from current chain height
Add 6 enhancements: sync parse-error tracking via SyncEvent channel,
balance assertions after each fork phase, TX-ID stake return verification
at HF6+, view-only wallet balance matching, sync idempotency check, and
subaddress (0,1) receive test at HF10.
y
Implement stratum v1 protocol client for Bitcoin-style pool mining
(mining.subscribe/authorize/notify/submit). Add salvium-miner-gr crate
with GhostRider PoW (15 SPH-512 + 3 CryptoNight rounds), verified
against XMRig test vectors. Extend MiningJob with optional nonce_offset
and target fields for backward-compatible stratum support.
Audited all validation rules against blockchain.cpp and
tx_verification_utils.cpp. Fixed every divergence found:
- Asset type HF segregation: enforce SAL before HF6, SAL1 from HF6+
(was ignoring hf_version parameter entirely)
- Output key sorting: use <= (strictly increasing, no duplicates)
to match C++ !(pubkey > last) comparison
- Output sorting boundary: enforce at HF > CARROT || is_carrot
(was HF >= CARROT for all TXs)
- Ring size unmixable exception: allow < 16 for unmixable non-RCT
outputs with ≤1 mixable input, gated behind HF >= 2
The CI workflow uses RUSTFLAGS="-Dwarnings" which promotes all warnings
to errors. This resolves every warning across the workspace so both
cargo build, cargo test, and cargo clippy pass cleanly.
Clippy fixes (~100 warnings across 9 crates):
- Replace manual assign ops with compound assignment (+=, *=) in
bulletproofs_plus Scalar arithmetic
- Use iterator patterns instead of index-based loops (enumerate, zip,
iter_mut) in elligator2, bulletproofs_plus, miner, mnemonic
- Replace manual div_ceil with .div_ceil() in consensus difficulty/fee
- Replace map_or(true, ..) with is_none_or(..) in stake filtering
- Replace inherent to_string() with Display impl in multisig types
- Collapse nested if statements in validation.rs
- Remove redundant closures (.map(hex::encode), .and_then(hex_to_32))
- Remove unnecessary .to_vec(), borrows, and return statements
- Use RangeInclusive::contains for range checks
- Derive Default instead of manual impl for BlockExtendedInfo
- Use unwrap_or_default/unwrap_or for simplified match expressions
- Use std::ptr::slice_from_raw_parts_mut in FFI free function
- Add #[allow(clippy::too_many_arguments)] where function signatures
match the C++ reference implementation (crypto scan, TCLSAG, RCT
verify, miner, RPC, stake lifecycle)
- Add #[allow(clippy::type_complexity)] for complex tuple return types
- Convert match-with-single-pattern to if-let in CLI commands
- Rename AssetType::from_str to ::parse to avoid std trait confusion
- Fix literal-in-format-string warnings in CLI table headers
Rustc warning fixes (from prior session, included for completeness):
- Remove unused constants, imports, and struct fields
- Fix deprecated Nonce::from_slice calls in AES-256-GCM functions
- Add missing protocol_tx_data field to all TxPrefix constructors
- Prefix unused variables with underscore
Port test/legacy-js/full-testnet.js to Rust: a single #[ignore] integration
test that mines from genesis through all 10 hard forks, testing transfers,
stakes, burns, and sweeps at each era boundary. Suppress unused-code warnings
in the miner crate (deserialized API fields, FFI stubs, stratum placeholder).
- Add method/url/endpoint context to every RpcError variant
- Default retries 0→2 with exponential backoff (500ms initial)
- HTTP status handling (401/4xx/5xx) with body capture
- Transient error classification for retry decisions
- Connection pooling (pool_max_idle_per_host=4)
- All NoResult errors now identify the failing call
CI now runs fmt, clippy, compile (debug+release), WASM target check,
per-crate test jobs (types+consensus, crypto, wallet+tx, miner+multisig+rpc),
doc tests, and a final ci-pass gate. Release workflow gates on tests,
fixes workspace target paths, and adds scaffolding for publishing
binaries to an external releases repo.
- Fix balance computation: staked amounts now included in total balance
to match C++ wallet behavior (balance = unspent + staked, locked =
immature + staked). Affects getBalance, getStorageBalance, and
integration sync test reporting.
- Add stake lifecycle methods to Rust storage (putStake, getStake,
getStakes, getStakeByOutputKey, markStakeReturned, deleteStakesAbove)
with FFI bindings and JS FfiStorage wrappers.
- Fix BigInt serialization in FfiStorage.putStake (JSON.stringify crash).
- Add balance diagnostic script (test/diagnose-balance.js) for verifying
key image spent status against the blockchain.
- Integration sync test: clear stale DB on fresh sync, add per-txType
and per-format unspent breakdowns, show staked balance in report.
The Monero/Salvium binary format serializes both L and R vectors with
their own varint length prefix, but the parser skipped R's varint and
reused Lcount. This 1-byte offset shift corrupted all subsequent
prunable data (TCLSAG sigs, pseudoOuts), causing RCT signature
verification to fail on every daemon-fetched transaction.
Crypto Backend Refactoring
──────────────────────────
- Delete src/ed25519.js — all scalar/point operations now route through
the crypto provider (WASM/FFI/JSI backends only)
- Remove duplicate JS BigInt implementations of scReduce32, scReduce64,
scalarAdd, scalarMul from scanning.js, carrot.js, and carrot-scanning.js;
delegate to Rust backend via crypto/index.js
- Remove debug/test exports from index.js (randomPoint, testDouble,
getBasePoint, isOnCurve, etc.) that were only used during development
- Rebuild WASM binary (476KB → 487KB) with updated Rust crate
Consolidated CryptoNote Scanner (cn_scan)
─────────────────────────────────────────
- New Rust module crates/salvium-crypto/src/cn_scan.rs replaces 5-12
individual FFI round-trips per output with a single native call:
view tag check → derive subaddress pubkey → subaddress map lookup →
amount decryption → commitment mask → key image generation
- FFI wrapper salvium_cn_scan_output in ffi.rs + C header declaration
- FFI backend scanCnOutput() with full subaddress map marshaling
(32-byte key + u32 major/minor LE per entry) and JSON result parsing
- JSI backend delegation via this.native.cnScanOutput()
- wallet-sync.js _scanCNOutput() tries native path first when available
(FFI/JSI), falls through to existing JS pipeline for WASM/JS backends
- Change pub(crate) visibility on subaddress.rs cn_subaddress_secret_key
- 8 Rust unit tests covering view tag, amount, commitment mask,
subaddress matching, and key image generation
- Verified identical results: WASM (JS fallback) and FFI (native cn_scan)
produce same 964 outputs at same chain height; FFI is 3x faster sync
(0.8s vs 2.5s) with 12x less heap (12MB vs 150MB)
RCT Batch Signature Verification
─────────────────────────────────
- New Rust module crates/salvium-crypto/src/rct_verify.rs — single-call
verification of all ring signatures in a transaction (CLSAG + TCLSAG),
avoiding N individual JS↔Rust boundary crossings
- Computes pre-MLSAG message hash matching C++ get_pre_mlsag_hash
- FFI export salvium_verify_rct_signatures with flat byte array interface
- FFI backend verifyRctSignatures() method
- JS backend stub returns null (validation.js handles JS fallback)
- validation.js: 200+ lines of RCT verification logic including
flattenKeyImages, packTclsagSigsFlat, packClsagSigsFlat helpers
Transaction Expansion
─────────────────────
- transaction.js: add expandTransaction() matching C++ expand_transaction_2
(copies key images from prefix inputs into TCLSAG/CLSAG signature structs)
- New test/expand-transaction.test.js (634 lines)
- New test/rct-verify-testnet.test.js (430 lines)
Mining Resilience
─────────────────
- salvium-miner main.rs: retry get_info and get_block_template up to 5
times with 2s delay for transient daemon errors
- full-testnet.js mineTo(): retry miner up to 3 times with 3s delay,
check for partial progress between attempts
Testnet Tooling
───────────────
- sync-only.js: CRYPTO_BACKEND env var for A/B testing (wasm vs ffi)
- full-testnet.js: daemon URL update (node12.whiskymine.io)
- Debug scripts for cn_scan development (debug-cn-scan/marshal/match/wasm)
- Android .gitignore and build-bundle.sh for mobile builds
The return pubkey is explicit in the tx prefix for all STAKE types:
CARROT (v>=4) stores it in protocol_tx_data.return_pubkey, pre-CARROT
stores it as a top-level prefix field. Use it as the primary matching
key instead of relying solely on the CARROT _returnOutputMap path.
- Extract return_pubkey from prefix when recording StakeRecords
- Match PROTOCOL outputs by publicKey against stored return_pubkey
as fallback when CARROT returnOriginKey isn't set
- Pass return_address/return_pubkey/protocol_tx_data through
_convertJsonToTx so JSON fallback path has the same fields
Thread isReturn/returnOriginKey from CARROT scan pipeline into
WalletOutput storage, then use it to link incoming PROTOCOL payouts
back to their originating STAKE transactions.
- Add StakeRecord class (stakeTxHash → return info lifecycle)
- Add stake storage to MemoryStorage + IndexedDBStorage (v2 schema)
- Create StakeRecords on outgoing STAKE txs, match returns on
PROTOCOL txs via CARROT _returnOutputMap → changeOutputKey lookup
- stakedBalance now reflects only currently locked stakes (not
total ever staked) in getBalance() and getStorageBalance()
- getStakeHistory() returns full lifecycle with status, returnAmount,
yieldEarned
- Handle reorg rollback for stakes (deleteStakesAbove)
- Fix stake-tracker.js tx hash display, add --tx lookup mode
Stakes (and burns, converts, returns) were stored as TRANSFER (type 3)
due to a field name mismatch: parseTransaction() writes prefix.txType
but _getTxType() read prefix.type. After rescan, all TX types are now
correctly identified.
Also adds amount_burnt propagation through the sync pipeline into a new
WalletTransaction.amountBurnt field, enabling getBalance() to return a
separate stakedBalance and a new getStakeHistory() API for wallet apps.
- Mark HF5 as paused in testnet fork table — SHUTDOWN_USER_TXS
disables all user transactions at this fork level, causing the
burn-in to fail with a silent daemon rejection
- Increase transfer rejection error detail from 200→800 chars so
daemon errors are no longer hidden behind "(response too large)"
- Bump wallet sync MAX_BATCH_SIZE 500→1000 for faster testnet syncs
- Add tools/block-privacy-extract.js — era-aware extractor that
shows real encrypted data from Salvium blocks (stealth addresses,
ring signatures, Bulletproof+ proofs, Pedersen commitments) with
correct annotations per fork level (1-byte vs 3-byte view tags,
CLSAG vs TCLSAG, BulletproofPlus vs SalviumOne RCT types)