Skip to content

Auto-convert basecoin and CT to RingCT on block confirmation#1055

Open
ohcee wants to merge 10 commits into
Veil-Project:masterfrom
ohcee:feature/auto-ct-to-ringct
Open

Auto-convert basecoin and CT to RingCT on block confirmation#1055
ohcee wants to merge 10 commits into
Veil-Project:masterfrom
ohcee:feature/auto-ct-to-ringct

Conversation

@ohcee

@ohcee ohcee commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements native automatic conversion of basecoin and CT balances to RingCT, addressing #1033 and #1054.

A working proof of concept was built as a Python ZMQ script and tested on mainnet. This PR is the native C++ implementation.

How It Works

Hooks into BlockConnected in wallet.cpp. On each new block:

  1. Checks for spendable basecoin balance → converts to CT via AddStandardInputs
  2. Checks for spendable CT balance → converts to RingCT via AddBlindedInputs
  3. Both conversions are broadcast immediately and appear in the transaction list

The two-hop flow:
Basecoin → CT
CT → RingCT

Configuration

Disabled by default (opt-in). To enable, add to veil.conf:
autoconvert=1

-autoconvert is registered as a wallet startup option and shows up in veild -help.

Safety

  • Skips during initial block download (IsInitialBlockDownload())
  • Skips if wallet is locked or staking-only unlock
  • Minimum conversion amount: 1 VEIL to keep fee overhead reasonable
  • Graceful error logging on failure, never crashes
  • Wallet must be fully unlocked for conversions to occur. Staking-only unlock (walletpassphrase "passphrase" timeout true) is not sufficient. If the wallet is locked or staking-only unlocked, AutoConvert will silently skip until the wallet is fully unlocked.

Implementation Notes

  • GetBlindBalance() is used instead of GetAvailableBlindBalance() for the CT balance check. GetAvailableBlindBalance() internally calls AvailableBlindedCoins() which requires AssertLockHeld and performs UTXO set checks via view.HaveCoin() that do not work correctly in this context. GetBlindBalance() iterates mapRecords directly and correctly returns the spendable CT balance.

  • Balance checks are performed inside a scoped LOCK2(cs_main, cs_wallet) block that is released before the transaction is built and committed, avoiding deadlocks.

  • Zerocoin balances are completely untouched. Only basecoin and CT are converted.

  • BlockConnected fires for all block types in Veil — PoS, ProgPow, RandomX, and SHA256d — so AutoConvert works correctly regardless of which algorithm produces the next block.

Known Limitations

  • CT amounts below the 1 VEIL minimum floor will remain as CT indefinitely and will not be converted. This is intentional to avoid conversion transactions where the fee exceeds a significant percentage of the amount being converted.

  • The conversion fee is approximately 0.015 VEIL per transaction at current fee rates, representing roughly 1.5% overhead at the 1 VEIL minimum.

  • Wallet lock behavior has not been explicitly tested. The implementation skips conversion if pAnonWalletMain->IsLocked() returns true, which covers both fully locked and staking-only unlock states. Users with encrypted wallets should verify behavior independently.

Testing

All tests performed on mainnet on M4 Mac Mini running macOS.

Test Result
CT → RingCT auto conversion
Basecoin → CT → RingCT full two hop
Multiple CT UTXOs swept in single transaction
Amounts below 1 VEIL floor skipped
IBD protection — no spam during sync
autoconvert=0 opt-out
Re-enabling autoconvert converts on next block
Restart persistence

Closes

#1033
#1054

@ohcee

ohcee commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator Author

✅ Additional Testing — June 10, 2026

Completed full end-to-end test on fresh wallet using latest binary (commit f456afcd5).

Transaction Flow

Step Action Result
1 Received 10 VEIL basecoin from zerocoin spend ✅ No crash
2 Auto-converted basecoin → CT on confirmation ✅ Broadcast
3 Received 1 VEIL CT (stealth to stealth) ✅ No crash
4 Auto-converted all CT → RingCT on confirmation ✅ Broadcast
5 Sent RingCT → RingCT manually ✅ Clean send

Debug Log

2026-06-10T18:35:22Z AutoConvert: converting 10.00 basecoin -> CT
2026-06-10T18:35:22Z [default wallet] Relaying wtx edcf03d4e843f961c69570a9b7bc564e93d8864d2797c2d66aaeb93e45a396a9
2026-06-10T18:39:08Z AutoConvert: converting 10.998902 CT -> RingCT
2026-06-10T18:39:08Z [default wallet] Relaying wtx 7f256fa6fa2108f6e26f8876cc453534c635f3541a086c27d6b7dd5455606082
2026-06-10T19:16:26Z [default wallet] Relaying wtx 8fe2010f92183ddd8a98f0f73f5c90e8d451bd5001d3d58e1039796d60f273b7

On-Chain Verification

  • Basecoin→CT: edcf03d4e843f961c69570a9b7bc564e93d8864d2797c2d66aaeb93e45a396a9
  • CT→RingCT: 7f256fa6fa2108f6e26f8876cc453534c635f3541a086c27d6b7dd5455606082
  • RingCT→RingCT send: 8fe2010f92183ddd8a98f0f73f5c90e8d451bd5001d3d58e1039796d60f273b7

@ohcee
ohcee force-pushed the feature/auto-ct-to-ringct branch from f456afc to e847010 Compare June 11, 2026 18:24
@seanPhill seanPhill added the Issue Type: Feature Request Additional feature that would make Veil better. label Jun 20, 2026
@ohcee

ohcee commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator Author

This PR auto-converts basecoin and CT to RingCT on every block, closing #1033 and #1054. Tested on mainnet, conversions confirm correctly each block, and autoconvert=0 is available for anyone who needs funds to stay at the base layer instead.

@seanPhill — given your comment confirmed this solves the issues you raised, do we want more testing? Let me know if there's additional testing you'd want to see, or any case you think isn't covered yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Issue Type: Feature Request Additional feature that would make Veil better.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants