Conversation
CT blinding factor domain separation - derive_blinding_factor now uses domain tag "ct-blinding-v1" in its hash input, breaking the collision with derivation_to_scalar used by stealth addresses Removed duplicate declaration checkConfidentialTransaction Initialized TransactionPrefix::fee
Replace ringAmount: uint64 + ringOutputIndexes: vec<uint32> with ringMembers: vec<{amount: uint64, offset: uint32}>
allows to freely mix:
Transparent decoys of any amount with a CT real spend
CT decoys with a transparent real spend (pre-fork legacy)
Transparent + CT decoys all together
The validator now uses batched verify for every CT transaction, with the diagnostic per-output fallback only firing on rejection
Add a consensus-tracked confidential_supply field that records the total
visible value currently locked inside the ECC CT pool, alongside a
pq_plain_supply stub for the future PQ-plain output type. Both are stored
in DbBlockMeta and BlockEntry the same way already_generated_coins is, so
they survive reorgs, pop, and migration.
Per non-coinbase tx the delta is
delta = visible_plain_inputs - visible_plain_outputs - fee
which is 0 for plain->plain, positive when value enters the CT pool, and
negative when it exits to plain or PQ-plain. The invariant
visible_plain_supply + pq_plain_supply + confidential_supply == already_generated_coins
is preserved across all chain operations.
Underflow is rejected at three layers:
- mempool admission via checkConfidentialTransaction (single-tx outflow > tip pool)
- block template construction via tx_memory_pool::fill_block_template
(running pool simulation catches chained exits)
- block push (final authoritative accumulation)
Exposed via ICore::getConfidentialSupply / getPqPlainSupply and per-block
*AtBlock(hash, &out) accessors, surfaced in RPC /getinfo as
confidential_supply and pq_plain_supply (formatted, like
already_generated_coins) and in BlockchainExplorer block details.
Block v7 is reserved: BLOCK_MAJOR_VERSION_7, UPGRADE_HEIGHT_V7,
m_upgradeHeightV7 + builder, m_upgradeDetectorV7 plus its
init/checkAndRollback/blockPushed/blockPopped wiring, PoW switch and
serialization upper-bound bump. Not activated yet; intended for the
future PQ-plain output type.
…-out The v6+ "only CT transactions allowed" block-validation rule, the "legacy rejected in CT-only era" branch in Core::check_tx_syntax, and the strict version-equals-current check in TransactionPool::isTxVersionAllowedForHeight are all relaxed so that both legacy v1 plain and v2 CT transactions remain valid after the CT fork. Wallets default to CT, but v1 plain stays available for tooling, exchanges, and explicit opt-out users. Pre-fork unchanged: only v1 is accepted. Post-fork: both v1 and v2. TRANSACTION_VERSION_CT is still rejected before CT activation height, and unknown versions are still rejected outright. The transparent-output floor (>= MIN_CT_DENOMINATION post-fork) is unchanged; v1 senders must still use canonical decompositions. CT pool liability accounting is unaffected: v1 plain->plain has delta = 0 in the same formula used for CT-mixing cases. simplewallet gains a --legacy-tx startup flag that opts the wallet out of CT and sends v1 plain transactions even when the chain is post-fork. The flag flows through WalletLegacy::setForceLegacyTxs and is captured by WalletTransactionSender when it's constructed (each of the seven WalletLegacy construction sites in simplewallet now applies the flag, including the wallet-rpc-server entry point in main()).
WalletGreen gets setForceLegacyTxs(bool) / forceLegacyTxs() mirroring WalletLegacy. The single useCT decision in WalletGreen::makeTransaction is gated on !m_forceLegacyTxs so the wallet sends v1 plain transactions even when the chain is post-fork. GreenWallet exposes --legacy-tx via Config::legacyTx (Types.h), parseArguments wires it in, and ZedWallet applies it after constructing the WalletGreen instance. walletd exposes the same as a --legacy-tx boost program option in PaymentServiceConfiguration; PaymentGateService::runWalletService calls setForceLegacyTxs on the WalletGreen it constructs. Generation paths in PaymentGate/WalletService.cpp don't send transactions, so they're left untouched.
Block major v6 replaces the legacy dual height/timestamp interpretation of TransactionPrefix::unlockTime with a height-only rule capped at CRYPTONOTE_MAX_UNLOCK_HEIGHT_V6 (10M blocks, ~76 years at 240s/block): - New txs at v6+ tip are rejected if unlockTime exceeds the cap. CT already requires unlockTime == 0; this also constrains v1 plain txs that remain valid post-fork. - When a v6+ tx tries to spend an output whose source tx carries an unlockTime above the cap, the output is treated as unlocked. This recovers funds frozen by accidental Unix-timestamp values set under the legacy rule (no escrow/vesting usage exists in practice). - Pre-v6 spend evaluations keep the old behavior unchanged. TransfersContainer mirrors the consensus rule so wallets display recovered outputs as spendable. The two-arg Blockchain::is_tx_spendtime_unlocked(unlock_time, height) overload applies the v6 rule when height >= UPGRADE_HEIGHT_V6, so the spending-proof RPC path picks it up automatically.
- Allow v1 plain alongside v2 CT txs after the fork; --legacy-tx and pre-CT wallets keep working with full CN decomposition. - Allow KeyInput / ConfidentialInput mix in v2 (per-input dispatch, balance kernel handles both). Wallet routes transparent dust as KeyInput so the CT pool tracks shielding flows. - Triptych spend proofs replace MLSAG: GK-descended logarithmic linkable one-out-of-many, with a Schnorr branch at ring size 1 and batched verify via shared Pippenger MSM. - CTInputSignature gains n=0xFF empty-slot sentinel for v2 KeyInput slots; legacy ring sig lives in tx.signatures[i] for those slots. - Pre-CT wire compat for getrandom_outs via parallel outs / outs_extra arrays so old wallets keep deserializing the response. - Fix KV-binary RPC roundtrip for ConfidentialInput by wrapping ring members as objects (same fix in explorer details serializer). - Filter zero-bucket entries from CT mixing-decoy requests. - Built-in explorer: unified per-input Signatures section, input/output counts, "dust" wording, diagnostic queryBlocksLite log. - Round-trip self-tests for v2 mixed inputs (binary stream + KV binary).
# Conflicts: # src/Rpc/BuiltinExplorer.cpp # src/Rpc/BuiltinExplorer.h
isTransparentNonCanonicalCtAmount only flagged sub-cent dust
(amount % MIN_CT_DENOMINATION != 0). 0.01-aligned outputs that are
not a valid CN {1,2,5}*10^k denomination — most notably v5+
coinbase rewards (~2.92 KRB each, the emission decrement) — landed
in unusedUnmixable and were only spendable with mixin = 0
(dust_sweep). With Phase B those outputs are perfectly spendable as
v2 KeyInputs (coinbase uses ring size 1, no decoys needed), but the
selection bucket still gated them out at normal mixin.
Widen the predicate to also flag 0.01-aligned non-CN-canonical
outputs as Phase 2 candidates. They now flow through
nonCanonicalOutputs, chooseInputMixins routes coinbase to ring 1
via the existing carve-out, and a mining wallet can finally send
its accumulated rewards without --legacy-tx + mixin 0.
Same fix in both WalletLegacy and WalletGreen helpers.
Under Phase B every transparent output is spendable via v2 KeyInput
(coinbase uses ring size 1, others fall back gracefully). The
"unmixable" balance line and the "you have unmixable coins — set
mixin to zero" prompts were left over from the v1 ring-only era
and now lie to the user about what they can spend.
Drop user-facing references:
- simplewallet `balance` no longer lists unmixable.
- simplewallet `transfer` no longer prints the unmixable-coins
warning at send time.
- simplewallet wallet-open log line drops the unmixable field.
- greenwallet `transfer` --send-all path just sends
`balance - fee - nodeFee`; the dust carve-out and mixin-zero
confirmation prompt are gone.
- greenwallet `doWeHaveEnoughBalance` keeps the
"not enough funds" branch and drops the unmixable branch.
The underlying unmixableBalance() and getBalanceMinusDust() APIs
stay on IWalletLegacy / WalletGreen — no callers outside the UI
removed here, and they remain useful for diagnostics.
ctSignatures[i] with n=0 used to render as 'ring size 1'. n=0 is no
longer a valid proof shape after consensus dropped the Schnorr branch,
so the (n == 0) ? 1 : (1 << n) special case can collapse to plain
1 << n for the valid n in {2, 3, 4}. Empty-slot (n=0xFF) is still
handled separately as 'transparent shielding'.
Most v2 inputs in mining-heavy wallets are KeyInput shieldings of coinbase outputs (cheap legacy ring-1 sigs, microseconds to verify). Triptych ConfidentialInputs are single-digit ms even batched, so the worst-case all-ring-16 shape is still under ~2 s/tx. 128 was tight for batch coinbase consolidation; 512 lets a miner shield ~4x more mature blocks per transaction. CT_MAX_OUTPUTS stays at 64 — GK output proofs are the real asymmetric verifier cost.
Split the home-page header line in two. Row 1 keeps tip metrics (Height, Difficulty, Alt. blocks, Transactions) and gains a new Account numbers count. Row 2 collects supply metrics (Emission, Next reward) and adds Confidential supply (formatted via the existing amount formatter), so the CT pool is visible right next to total emission.
… to integration harness
(cherry picked from commit 96a800b8da7d94b5e91018464dff8165da7d9979)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Confidential Transactions for Karbo
This PR introduces an initial implementation of Confidential Transactions for Karbo using Pedersen commitments and GK-style denomination proofs.
KarboCT is deliberately a practical middle ground: better trading and transfer privacy than transparent CryptoNote amounts, with simpler math and implementation scope than arbitrary-amount Bulletproof-style CT.
Instead of using full range proofs such as Bulletproofs, Karbo CT uses GK-style denomination proofs over an allowed denomination set. Each output commits to one of 64 canonical denominations using a Pedersen commitment proved by a Groth-Kohlweiss one-out-of-many membership proof. The 64-entry denomination set is d × 10^k for d ∈ {1..9}, k ∈ {10..16}, plus the cap entry 10^17.
MIN_CT_DENOMINATION = 10^10 au = 0.01 KRB
Sub-floor pieces (less than 10^10 au) cannot become CT outputs and are absorbed into transaction fees at construction time.This makes confidential dust structurally impossible: any value below the floor either remains in atransparent pre-fork output (until consumed) or folds into a fee.
Coinbase remains transparent.
This PR follows paper where you can find more information.