Deniability - a tool to automatically improve coin ownership privacy #733

pull denavila wants to merge 2 commits into bitcoin-core:master from denavila:deniability changing 23 files +3012 −7
  1. denavila commented at 6:32 am on May 21, 2023: none

    This new feature is an implementation of the ideas in Paul Sztorc’s blog post “Deniability - Unilateral Transaction Meta-Privacy”(https://www.truthcoin.info/blog/deniability/).

    In short, the idea is to periodically split coins and send them to yourself, making it look like common “spend” transactions, such that blockchain ownership analysis becomes more difficult, and thus improving the user’s privacy.

    This is the GUI portion of the PR (bitcoin-core/gui). The core functionality PR ( https://github.com/bitcoin/bitcoin/pull/27792 ) is in the main repo (bitcoin/bitcoin).

    This PR implements an additional “Deniability” wallet view. The majority of the GUI code is in a new deniabilitydialog.cpp/h source files containing a new DeniabilityDialog class, hooked up via the WalletView class. 

    On startup and on notable events (new blocks, new transactions, etc), we evaluate the privacy of all coins in the wallet, and we build a “deniabilization” candidate list. UTXOs that share the same destination address are grouped together into a single candidate (see DeniabilityDialog::updateCoins and DeniabilityDialog::updateCoinTable).

    We inspect the blockchain data to find out if we have performed “deniabilization” transactions already, and we count how many “cycles” (self-sends) have been performed for each coin (see DeniabilityDialog::calculateDeniabilizationStats). Since we infer the state entirely from the blockchain data, even if the wallet is restored from a backup, the state would not be lost. This also means that if the user has performed manual self-sends that have improved the ownership privacy, they will be counted too.

    The user has a couple of controls for the “deniabillization” process - a Frequency selector and a Budget spinner, which respectively determine how often to perform a cycle (see below) and how much total BTC to spend on transaction fees.

    The user can initiate the “deniabillization” process by pressing the Start button (DeniabilityDialog::startDeniabilization). The first time the button is pressed, we offer an information / confirmation box to explain to the user how Deniability works.

    The process periodically perform a “deniabilization” cycle (DeniabilityDialog::deniabilizeProc). In each such cycle we do the following: A coin is selected form the candidate list. The more a coin is “deniabilized”, the less likely it is to be selected. Smaller coins are also less likely to be selected. If a coin is selected, we prepare and broadcast a transaction, which splits the coin into a pair of new wallet addresses (DeniabilityDialog::deniabilizeCoin). 

    If Bitcoin Core is left running continuously, the cycles would be performed at the selected frequency (with some randomization). If Bitcoin Core is shutdown, the “deniabilization” process will resume at the next restart, and if more time has elapsed than the selected frequency, it will perform a single cycle. We deliberately don’t “catch up” all missed cycles, since that would expose the process to blockchain analysis. The state is saved and restored via QSettings (DeniabilityDialog::loadSettings and DeniabilityDialog::saveSettings).

    We monitor each broadcasted transaction and we automatically attempt a fee bump if the transaction is still in the memory pool since the previous cycle (DeniabilityDialog::bumpDeniabilizationTx). We don’t issue any other deniabilization transactions until the previous transaction is confirmed (or abandoned/dropped).

    The process ends when the budget is exhausted or if there’s no candidates left. The user can also stop the process manually by pressing a Stop button (DeniabilityDialog::stopDeniabilization).

    External signers are supported in a “best effort” way - since the user needs to manually sign, we postpone the deniabilization processing till the external signer is connected and use some additional UI to get the user’s attention to sign (see the codepath predicated on hasExternalSigner). This is not ideal, so I’m looking for ideas if we can improve this in some way.

    Watch-only wallets are partially supported, where we display the candidate list, but we don’t allow any processing (since we don’t have the private keys to issue transactions).

    I’ve tested all this functionality on regtest, testnet, signet and mainnet. I’ve also added some unit tests (under WalletTests) to exercise the main functions.

    This is my first change and PR for Bitcoin Core, and I did my best to validate everything against the guidelines and documentation and to follow the patterns in the existing code, but I’m sure there may be things I’ve missed, so I’m looking forward to your feedback. In particular one thing I’m not particularly happy with - the save/restore of state via QSettings makes me a bit nervous as we store wallet specific data keyed on wallet name, however wallets can be erased and new ones created with the same name, so this is not ideal. I made an effort to validate all data on load, but it would be great if we could improve this somehow - either by storing the settings based on some wallet identity signature, or by storing the state in the wallet database (however that doesn’t seem accessible via the interfaces::Wallet API). Please let me know your thoughts and suggestions.

    Thank you.

  2. DrahtBot commented at 6:32 am on May 21, 2023: contributor

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    Concept ACK katesalazar

    If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #815 (Bugfix on TransactionsView - Disable if privacy mode is set during wallet selection by pablomartin4btc)
    • #700 (Dialog for allowing the user to choose the change output when bumping a tx by achow101)
    • #bitcoin/bitcoin/28710 (Remove the legacy wallet and BDB dependency by achow101)
    • #bitcoin/bitcoin/27865 (wallet: Track no-longer-spendable TXOs separately by achow101)
    • #bitcoin/bitcoin/27286 (wallet: Keep track of the wallet’s own transaction outputs in memory by achow101)
    • #bitcoin/bitcoin/24963 (RPC/Wallet: Convert walletprocesspsbt to use options parameter by luke-jr)
    • #bitcoin/bitcoin/21283 (Implement BIP 370 PSBTv2 by achow101)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

  3. DrahtBot added the label CI failed on May 21, 2023
  4. hebasto added the label Feature on May 21, 2023
  5. hebasto added the label Wallet on May 21, 2023
  6. hebasto commented at 8:01 am on May 21, 2023: member

    This PR is an implementation of the ideas in Paul Sztorc’s blog post “Deniability - Unilateral Transaction Meta-Privacy”(truthcoin.info/blog/deniability).

    Has that idea been discussed somewhere already?

  7. denavila commented at 7:17 am on May 23, 2023: none

    Has that idea been discussed somewhere already?

    The idea was published quite a while ago, but I learned about it from some Bitcoiner friends just recently. We looked around to see if there were existing discussions or implementations, but we didn’t find anything on bitcoin-dev or the repo here. There’s an old implementation in a Bitcoin fork (Drivechain) which I used as a starting point, but ended up with a pretty different approach and implementation. We were thinking of perhaps writing down the spec into a Process BIP, if this would make it easier to review on a conceptual level?

  8. VladdyC commented at 12:33 pm on May 24, 2023: none

    This is really cool and I’d love to have it as an option in Bitcoin Core. However, most CoinJoin implementations are more advanced in this regard – for example, WabiSabi performs both divisions and consolidations. And it makes me wonder to which extent it’s possible to decentralize coordination to the point where clients randomly take turns in doing it.

    Anyway, this is just my curiosity/wish list. I believe that anything is better than having no privacy options at all, so I’d be happy with some more basic deniability too :)

  9. achow101 commented at 3:59 pm on May 24, 2023: member
    It seems like a feature like this would be better to integrate into the wallet directly rather than something that is done through and managed by the GUI.
  10. denavila commented at 5:31 pm on May 24, 2023: none

    This is really cool and I’d love to have it as an option in Bitcoin Core. However, most CoinJoin implementations are more advanced in this regard – for example, WabiSabi performs both divisions and consolidations. And it makes me wonder to which extent it’s possible to decentralize coordination to the point where clients randomly take turns in doing it.

    Yes, CoinJoin is definitely more powerful, however it has some drawbacks. In particular, if it’s possible to detect that an UTXO has come from a coinjoin, it may be considered “tainted” by CEXs and they could refuse processing it (eg demand source of funds etc).

    That’s the “meta-privacy” that Paul talks about in his blog post. The idea that the knowledge of a secret has to also be kept a secret.

    Here’s another one of Paul’s blog posts where he talks about drawbacks of other privacy methods: https://www.truthcoin.info/blog/expensive-privacy/

    At any rate, I’m definitely open to suggestions or ideas we can borrow from CoinJoin. In particular if there’s a way we can re-merge UTXOs in a privacy-preserving way. Or at bare minimum, use “deniability-aware” logic in the automatic coin selection during user-initiated spends to minimize the privacy reduction.

    Anyway, this is just my curiosity/wish list. I believe that anything is better than having no privacy options at all, so I’d be happy with some more basic deniability too :)

    Thank you. I believe so too.

  11. denavila commented at 5:39 pm on May 24, 2023: none

    It seems like a feature like this would be better to integrate into the wallet directly rather than something that is done through and managed by the GUI.

    Do you mean moving some of the core logic inside the interfaces::Wallet API, or the CWallet implementation?

    Yes, certainly, that could have benefits. I had to workaround certain things that weren’t in the Wallet API (namely address reservation, bump fee handling etc). So that would clean up the wallet side for sure.

    Since this was my first Bitcoin Core change I decided to be more conservative and avoid API changes. Of course, if this PR is accepted, I’d be happy to move functionality into the API.

  12. achow101 commented at 6:08 pm on May 24, 2023: member

    Do you mean moving some of the core logic inside the interfaces::Wallet API, or the CWallet implementation?

    Inside of CWallet.

    Since this was my first Bitcoin Core change I decided to be more conservative and avoid API changes. Of course, if this PR is accepted, I’d be happy to move functionality into the API.

    The API isn’t public, so changing it isn’t a big concern. Really the only consumer of it is the GUI.

    Having it inside of CWallet will probably make this feature more likely to be accepted. It will be easier to write tests (adding an RPC allows for functional tests, and implementing in CWallet allows unit tests to better access the data) and it can be split up into at least 2 chunks - the CWallet and RPC only implementation, and then the GUI and interface changes. This makes it more likely to be accepted.

  13. denavila commented at 6:15 pm on May 24, 2023: none

    The API isn’t public, so changing it isn’t a big concern. Really the only consumer of it is the GUI.

    Having it inside of CWallet will probably make this feature more likely to be accepted. It will be easier to write tests (adding an RPC allows for functional tests, and implementing in CWallet allows unit tests to better access the data) and it can be split up into at least 2 chunks - the CWallet and RPC only implementation, and then the GUI and interface changes. This makes it more likely to be accepted.

    Ah, I see. I had assumed the opposite - that not touching the API would be easier to accept. Ok, I can look into refactoring the PR and moving the core functionality into CWallet.

    I’d need to open a PR against the main repo in that case, right? Should I keep this PR and leave the GUI bits here, and open another PR in the main repo with just the CWallet changes? Or is it better to close this PR and open a new one in the main repo with all the changes?

  14. achow101 commented at 6:31 pm on May 24, 2023: member

    I’d need to open a PR against the main repo in that case, right?

    Yes

    Should I keep this PR and leave the GUI bits here, and open another PR in the main repo with just the CWallet changes? Or is it better to close this PR and open a new one in the main repo with all the changes?

    You can leave this open and rebase it onto the main repo PR, just mention it in the description.

  15. denavila commented at 4:05 am on May 25, 2023: none

    You can leave this open and rebase it onto the main repo PR, just mention it in the description.

    All right, I’ll start working on this refactor. Btw, do we have some docs on how to setup git locally so I can have dependent changes across the “bitcoin” and the “gui” repos? So far I was moving changes via patches but that doesn’t seem too convenient.

  16. denavila referenced this in commit a035b42fd6 on May 26, 2023
  17. hebasto removed the label CI failed on May 26, 2023
  18. denavila force-pushed on May 30, 2023
  19. denavila commented at 4:31 pm on May 30, 2023: none

    I refactored the code and updated the PR. The new code is split between two branches:

    1. deniability-api - new API and implementation of core “deniabilization” functions. I’ll open a PR in the main repo once I add RPC and unit tests.
    2. deniability - the GUI portion of the Deniability dialog which uses the new deniability-api functions.
  20. denavila force-pushed on May 30, 2023
  21. DrahtBot added the label CI failed on May 30, 2023
  22. denavila force-pushed on May 30, 2023
  23. denavila force-pushed on May 30, 2023
  24. denavila force-pushed on May 30, 2023
  25. denavila force-pushed on May 31, 2023
  26. denavila force-pushed on May 31, 2023
  27. denavila referenced this in commit 246589f3f7 on May 31, 2023
  28. denavila force-pushed on May 31, 2023
  29. denavila force-pushed on May 31, 2023
  30. DrahtBot removed the label CI failed on May 31, 2023
  31. maflcko commented at 9:17 pm on June 13, 2023: contributor
    Maybe mark as draft as long as it depends on the upstream changes?
  32. denavila marked this as a draft on Jun 13, 2023
  33. denavila commented at 10:00 pm on June 13, 2023: none

    Maybe mark as draft as long as it depends on the upstream changes?

    I converted the PR to a draft. Thanks!

  34. DrahtBot added the label Needs rebase on Jul 19, 2023
  35. denavila referenced this in commit 1925738074 on Sep 2, 2023
  36. denavila referenced this in commit 379b724eb5 on Sep 2, 2023
  37. denavila force-pushed on Sep 2, 2023
  38. DrahtBot removed the label Needs rebase on Sep 2, 2023
  39. DrahtBot added the label CI failed on Sep 3, 2023
  40. DrahtBot removed the label CI failed on Sep 4, 2023
  41. DrahtBot added the label CI failed on Sep 13, 2023
  42. DrahtBot removed the label CI failed on Sep 18, 2023
  43. DrahtBot added the label CI failed on Sep 20, 2023
  44. denavila referenced this in commit 403dba36a5 on Oct 6, 2023
  45. denavila force-pushed on Oct 6, 2023
  46. denavila force-pushed on Oct 8, 2023
  47. denavila force-pushed on Oct 8, 2023
  48. DrahtBot removed the label CI failed on Oct 8, 2023
  49. denavila force-pushed on Oct 10, 2023
  50. denavila referenced this in commit ed75f632a9 on Oct 20, 2023
  51. denavila force-pushed on Oct 21, 2023
  52. DrahtBot added the label CI failed on Oct 21, 2023
  53. denavila referenced this in commit 4d1a6d147e on Oct 21, 2023
  54. denavila force-pushed on Oct 21, 2023
  55. denavila force-pushed on Oct 23, 2023
  56. denavila force-pushed on Oct 23, 2023
  57. denavila force-pushed on Oct 25, 2023
  58. DrahtBot removed the label CI failed on Oct 25, 2023
  59. denavila force-pushed on Oct 25, 2023
  60. DrahtBot added the label CI failed on Oct 25, 2023
  61. denavila force-pushed on Oct 25, 2023
  62. DrahtBot removed the label CI failed on Oct 26, 2023
  63. denavila force-pushed on Oct 26, 2023
  64. DrahtBot added the label CI failed on Oct 26, 2023
  65. in src/qt/test/wallettests.cpp:548 in 16d50b3699 outdated
    541+
    542+    // mine a block to confirm the transaction
    543+    MineBlock(test.m_node, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
    544+    {
    545+        LOCK(wallet->cs_wallet);
    546+        wallet->SetLastBlockProcessed(106, WITH_LOCK(node.context()->chainman->GetMutex(), return node.context()->chainman->ActiveChain().Tip()->GetBlockHash()));
    


    katesalazar commented at 6:30 pm on October 26, 2023:

    Crosses even column 160.

    0        wallet->SetLastBlockProcessed(106, WITH_LOCK(
    1                node.context()->chainman->GetMutex(),
    2                return node.context()->chainman->ActiveChain().Tip()->GetBlockHash()));
    

    Wouldn’t reach even column 100.


    denavila commented at 8:52 pm on October 28, 2023:
    There was a bug introduced in the fee bumping after the last rebase. Interestingly it didn’t get flagged by the compiler … even though it was an obviously wrong type being assigned to CRecipient struct … :-O At any rate, I fixed it in the latest commit. Please let me know if you’re still seeing an issue running the tests. Thanks!
  66. in src/wallet/feebumper.cpp:390 in 16d50b3699 outdated
    387@@ -388,5 +388,102 @@ Result CommitTransaction(CWallet& wallet, const uint256& txid, CMutableTransacti
    388     return Result::OK;
    389 }
    390 
    391+Result CreateRateBumpDeniabilizationTransaction(CWallet& wallet, const uint256& txid, unsigned int confirm_target, bool sign, bilingual_str& error, CAmount& old_fee, CAmount& new_fee, CTransactionRef& new_tx)
    


    katesalazar commented at 6:32 pm on October 26, 2023:
    0Result CreateRateBumpDeniabilizationTransaction(
    1        CWallet& wallet, const uint256& txid, unsigned int confirm_target,
    2        bool sign, bilingual_str& error, CAmount& old_fee, CAmount& new_fee,
    3        CTransactionRef& new_tx)
    

    so many params, did you think of removing some?


    denavila commented at 8:49 pm on October 28, 2023:
    CreateRateBumpDeniabilizationTransaction essentially does the job of CreateRateBumpTransaction and SignTransaction, but specialized for bumping deniabilization transactions. This functionality is responsible for the presence of the “txid”, “old_fee”, “new_fee” and “sign” parameters. “confirm_target” takes the place of the “coin_control” parameter (since that’s all we need), and the “error” and “new_tx” are needed to communicate the result/error with the caller. The reason we need a new API function here is that we need to take the new bump fee from one of the recipients of the old transaction (fSubtractFeeFromAmount), and there didn’t seem to be a way to do that via CreateRateBumpTransaction. And generally CreateRateBumpTransaction just does too many other things that we don’t need once we know we’re bumping a deniabilization transaction.
  67. in src/wallet/spend.cpp:1737 in 16d50b3699 outdated
    1618+    } else {
    1619+        // By default "Core" doesn't perform BIP69 ordering
    1620+    }
    1621+}
    1622+
    1623+util::Result<CreatedTransactionResult> CreateDeniabilizationTransaction(CWallet& wallet, const std::set<COutPoint>& inputs, const std::optional<OutputType>& opt_output_type, unsigned int confirm_target, unsigned int deniabilization_cycles, bool sign, bool& insufficient_amount)
    


    katesalazar commented at 6:34 pm on October 26, 2023:
    same

    denavila commented at 8:32 pm on October 28, 2023:
    The “inputs”, “out_output_type” and “confirm_target” are usually passed in as part of CCoinControl. However for deniabilization we create a specifically built CCoinControl, so I didn’t want the API user to have to fill one out, just to have the majority of the fields essentially ignored, so that’s why I decided to list these individually. I could make a new class to pass them together though, eg CDeniabilizationControl? Does that sound reasonable?

    katesalazar commented at 2:39 pm on October 29, 2023:

    just by thinking of the possibility seems reasonable enough for me at this point

    for now make as you feel it’s best made

  68. in src/wallet/spend.cpp:1736 in 16d50b3699 outdated
    1731+            // we don't expect to get change, but we provide the address to prevent CreateTransactionInternal from generating a change address
    1732+            coin_control.destChange = dest;
    1733+        }
    1734+    }
    1735+
    1736+    CAmount recipient_amount = std::accumulate(recipients.cbegin(), recipients.cend(), CAmount{0}, [](CAmount sum, const CRecipient& recipient) { return sum + recipient.nAmount; });
    


    katesalazar commented at 6:35 pm on October 26, 2023:
    fold to several lines?

    denavila commented at 9:03 pm on October 28, 2023:
    Yes, indeed. I split the code into several lines in the latest commit. Thanks!

    katesalazar commented at 2:50 pm on October 29, 2023:
    I hate that indent standard though, and yet I think now it’s a bit better than before. Thanks
  69. katesalazar commented at 6:36 pm on October 26, 2023: contributor

    what a long shot

    Concept ACK

    keep up the good works

  70. denavila force-pushed on Oct 28, 2023
  71. denavila force-pushed on Oct 28, 2023
  72. DrahtBot removed the label CI failed on Oct 28, 2023
  73. in src/wallet/spend.cpp:1474 in c781cd8863 outdated
    1391@@ -1392,4 +1392,384 @@ bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet,
    1392 
    1393     return true;
    1394 }
    1395+
    1396+constexpr int NUM_DENIABILIZATION_OUTPUTS = 2;
    


    katesalazar commented at 2:42 pm on October 29, 2023:
    why is this hardwired to 2?

    denavila commented at 1:02 am on October 30, 2023:
    2 outputs is a common spend transaction and we’re trying to mimic that. Another concern with a larger output count is that the number of deniabilization chunks grows much larger with each “cycle”, and thus becomes more expensive to perform further cycles. In the end 2 looked like the sweet spot, but I made the constant just in case we want to play with other output counts in the future. I’ll add comments in the code to that effect so that it’s more obvious. Thanks!

    katesalazar commented at 4:49 pm on October 30, 2023:
    Maybe commenting the constant helps, maybe best commented in some function, at the extremely minimum least I think such intrincacies should be commented in the commit message introducing either the constant or whatever function
  74. in src/wallet/spend.cpp:1412 in c781cd8863 outdated
    1407+    int witnessversion = 0;
    1408+    std::vector<unsigned char> witnessprogram;
    1409+    if (script.IsWitnessProgram(witnessversion, witnessprogram)) {
    1410+        txSize += (unsigned int)(numTxIn * 67.75f + 0.5f);
    1411+    } else {
    1412+        txSize += numTxIn * 148;
    


    katesalazar commented at 2:45 pm on October 29, 2023:
    why this magic number? is this the number of legacy and p2sh bytes per txin? can’t name some constant with that?

    denavila commented at 1:08 am on October 30, 2023:
    Yes, these are legacy vs Segwit tx size calculation, I think I found the code in GetDustThreshold. I’ll see if we have named constants for some of those.

    katesalazar commented at 4:46 pm on October 30, 2023:
    and/or simple one line comments could help clarify, I feel like named constant for magic number is a better strategy than commenting, but maintainer should take final choice :thinking:
  75. in src/wallet/spend.cpp:1474 in c781cd8863 outdated
    1469+
    1470+    // this cycle will use all the UTXOs, while following cycles will have just one UTXO
    1471+    CAmount deniabilizationFee = CalculateDeniabilizationTxFee(shared_script, total_value, num_utxos, fee_rate);
    1472+
    1473+    // calculate the fees from future deniabilization cycles
    1474+    CAmount futureDeniabilizationFee = CalculateDeniabilizationFeeEstimate(shared_script, total_value / NUM_DENIABILIZATION_OUTPUTS, 1, deniabilization_cycles + 1, fee_rate) * 2;
    


    katesalazar commented at 2:45 pm on October 29, 2023:
    why compute fee estimate and then multiply by a hardwired 2?

    denavila commented at 1:11 am on October 30, 2023:
    Oh, that 2 probably needs to be NUM_DENIABILIZATION_OUTPUTS. Basically we calculate the future cycle cost for each of the chunks we split into. Each chunk should cost the same if the number of splits is the same, so we can calculate one chunk and multiply by the number of splits. Thanks for spotting that. I’ll fix it.

    katesalazar commented at 4:42 pm on October 30, 2023:
    happpy to help
  76. denavila force-pushed on Nov 9, 2023
  77. denavila force-pushed on Nov 16, 2023
  78. DrahtBot added the label CI failed on Nov 18, 2023
  79. denavila referenced this in commit 842cb26c18 on Nov 19, 2023
  80. denavila force-pushed on Nov 19, 2023
  81. DrahtBot removed the label CI failed on Nov 19, 2023
  82. denavila referenced this in commit 21e39b96bc on Nov 22, 2023
  83. denavila force-pushed on Nov 22, 2023
  84. DrahtBot added the label CI failed on Nov 27, 2023
  85. denavila referenced this in commit 37722d6f27 on Nov 29, 2023
  86. denavila force-pushed on Nov 29, 2023
  87. DrahtBot removed the label CI failed on Nov 29, 2023
  88. DrahtBot added the label Needs rebase on Dec 11, 2023
  89. denavila referenced this in commit 9e6fdce439 on Dec 11, 2023
  90. denavila referenced this in commit 737fbc4643 on Dec 12, 2023
  91. denavila force-pushed on Dec 12, 2023
  92. DrahtBot removed the label Needs rebase on Dec 12, 2023
  93. DrahtBot added the label CI failed on Jan 14, 2024
  94. DrahtBot added the label Needs rebase on Jan 23, 2024
  95. denavila referenced this in commit 95e53e7766 on Jan 26, 2024
  96. denavila force-pushed on Jan 26, 2024
  97. DrahtBot removed the label CI failed on Jan 26, 2024
  98. DrahtBot removed the label Needs rebase on Jan 26, 2024
  99. DrahtBot commented at 5:19 am on February 29, 2024: contributor

    🚧 At least one of the CI tasks failed. Make sure to run all tests locally, according to the documentation.

    Possibly this is due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    Leave a comment here, if you need help tracking down a confusing failure.

    Debug: https://github.com/bitcoin-core/gui/runs/20890537182

  100. DrahtBot added the label CI failed on Feb 29, 2024
  101. denavila referenced this in commit 4e836f674e on Feb 29, 2024
  102. denavila force-pushed on Feb 29, 2024
  103. DrahtBot removed the label CI failed on Feb 29, 2024
  104. jess2505 approved
  105. 1440000bytes commented at 4:19 pm on April 28, 2024: none

    I tested it and this is the first transaction after starting hourly deniability: https://mempool.space/signet/tx/9acd8fd572d4c245c7e09c454cc721caa2ce4dc91d970400ba228edc81e15d16

    Some feedback based on testing:

    1. ‘Budget’ should be called ‘Fee Budget’.
    2. There should be an option to select “random” frequency.
    3. There should be an option to select minimum amount for resulting UTXOs so that user doesn’t end up with dust outputs that need to be used together.
    4. Post deniability UTXOs have to be carefully spent but there is nothing to differentiate them in the labels and all are labelled as “deniability”. Users will need to do manual analysis before spending which is not good UX.
    5. I don’t understand why the next cycle starts after 1 hr 34 minutes when frequency is 1 hour: https://i.imgur.com/fxE0AvG.png
  106. denavila commented at 4:39 pm on April 29, 2024: none

    @1440000bytes, Thank you so much for testing the PR and for the feedback. That’s very helpful.

    (1) Yes, that’s a good idea, I’ll rename it. (2) and (5) are related. The selectable frequencies are actually also randomized, within the selected frequency range - for example hourly is random from 1 to 2 hours, daily is random from 1 to 2 days etc (see line 934 in deniabilitydialog.cpp). Perhaps we need to rename the frequency UI selection to something that better describes this? (3) Great idea as well - especially since we’re getting into long stretches of overloaded mempool with fees sky rocketing. I’m actually even more concerned that any number the user picks, may not be good enough long term, as smaller UTXOs may effectively become dust if fees rise and stay elevated (and there’s a good chance that’s going to happen). I’m not sure we can solve that just on the transaction end though … A minimum UTXO amount does sound like a good option regardless, so I’ll implement that. (4) Indeed - I’ll have to think how to solve this. Perhaps we need to add some “deniability” awareness to the regular UTXO selection logic. I’m not entirely clear what that logic would look like though. Let me know if you have ideas - eg, what kind of rules for UTXO selection should we add etc.

    I’m a bit busy with my main job at the moment, but when I get a chance I’ll work on this as time allows. Thanks!

  107. DrahtBot commented at 6:56 am on May 8, 2024: contributor

    🚧 At least one of the CI tasks failed. Make sure to run all tests locally, according to the documentation.

    Possibly this is due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    Leave a comment here, if you need help tracking down a confusing failure.

    Debug: https://github.com/bitcoin-core/gui/runs/22114359812

  108. DrahtBot added the label CI failed on May 8, 2024
  109. denavila referenced this in commit 4fd852bb3d on May 18, 2024
  110. denavila force-pushed on May 18, 2024
  111. 1Helpmeout3 approved
  112. 1Helpmeout3 approved
  113. 1Helpmeout3 approved
  114. denavila referenced this in commit 0b66676c2f on Jul 30, 2024
  115. denavila referenced this in commit c2c12c0a94 on Aug 1, 2024
  116. denavila force-pushed on Aug 1, 2024
  117. DrahtBot added the label Needs rebase on Aug 28, 2024
  118. jonatack commented at 7:29 pm on August 28, 2024: member

    Didn’t conceptually review yet. While rebasing, the clang-tidy CI highlighted these issues to be addressed:

     0wallet/spend.cpp:1539:9: error: function 'CalculateDeniabilizationFeeEstimate' is within a recursive call chain [misc-no-recursion,-warnings-as-errors]
     1 1539 | CAmount CalculateDeniabilizationFeeEstimate(const CScript& shared_script, CAmount total_value, unsigned int num_utxos, unsigned int deniabilization_cycles, const CFeeRate& fee_rate)
     2      |         ^
     3wallet/spend.cpp:1539:9: note: example recursive call chain, starting from function 'CalculateDeniabilizationFeeEstimate'
     4
     5wallet/spend.cpp:1552:40: note: Frame [#1](/bitcoin-core-gui/1/): function 'CalculateDeniabilizationFeeEstimate' calls function 'CalculateDeniabilizationFeeEstimate' here:
     6 1552 |     CAmount futureDeniabilizationFee = CalculateDeniabilizationFeeEstimate(shared_script, total_value / NUM_DENIABILIZATION_OUTPUTS, 1, deniabilization_cycles + 1, fee_rate) * NUM_DENIABILIZATION_OUTPUTS;
     7      |                                        ^
     8wallet/spend.cpp:1552:40: note: ... which was the starting point of the recursive call chain; there may be other cycles
     9
    10wallet/spend.cpp:1561:31: error: function 'CalculateDeniabilizationCycles' is within a recursive call chain [misc-no-recursion,-warnings-as-errors]
    11 1561 | std::pair<unsigned int, bool> CalculateDeniabilizationCycles(CWallet& wallet, const COutPoint& outpoint)
    12      |                               ^
    13wallet/spend.cpp:1561:31: note: example recursive call chain, starting from function 'CalculateDeniabilizationCycles'
    14
    15wallet/spend.cpp:1634:23: note: Frame [#1](/bitcoin-core-gui/1/): function 'CalculateDeniabilizationCycles' calls function 'CalculateDeniabilizationCycles' here:
    16 1634 |     auto inputStats = CalculateDeniabilizationCycles(wallet, txIn.prevout);
    
  119. denavila commented at 10:35 pm on September 3, 2024: none
    Yes, it looks like recursive functions were disallowed recently. I’ll rework the code to not use recursion.
  120. denavila referenced this in commit a869bd1740 on Sep 4, 2024
  121. denavila referenced this in commit a24133189d on Sep 4, 2024
  122. denavila force-pushed on Sep 4, 2024
  123. denavila referenced this in commit 5f4549cf3a on Sep 4, 2024
  124. denavila force-pushed on Sep 4, 2024
  125. denavila referenced this in commit 38119b91f9 on Sep 4, 2024
  126. denavila force-pushed on Sep 4, 2024
  127. DrahtBot removed the label Needs rebase on Sep 4, 2024
  128. DrahtBot removed the label CI failed on Sep 4, 2024
  129. DrahtBot commented at 7:27 pm on September 12, 2024: contributor

    🚧 At least one of the CI tasks failed. Debug: https://github.com/bitcoin-core/gui/runs/29695577162

    Make sure to run all tests locally, according to the documentation.

    The failure may happen due to a number of reasons, for example:

    • Possibly due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    • A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.

    • An intermittent issue.

    Leave a comment here, if you need help tracking down a confusing failure.

  130. DrahtBot added the label CI failed on Sep 12, 2024
  131. denavila force-pushed on Sep 18, 2024
  132. denavila force-pushed on Sep 18, 2024
  133. DrahtBot removed the label CI failed on Sep 19, 2024
  134. DrahtBot added the label CI failed on Oct 10, 2024
  135. DrahtBot commented at 8:18 pm on October 10, 2024: contributor

    🚧 At least one of the CI tasks failed. Debug: https://github.com/bitcoin-core/gui/runs/30344213036

    Try to run the tests locally, according to the documentation. However, a CI failure may still happen due to a number of reasons, for example:

    • Possibly due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    • A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.

    • An intermittent issue.

    Leave a comment here, if you need help tracking down a confusing failure.

  136. Deniability API
    This PR is the wallet API and implementation portion of the GUI PR ( https://github.com/bitcoin-core/gui/pull/733 ) which is an implementation of the ideas in Paul Sztorc's blog post "Deniability - Unilateral Transaction Meta-Privacy"(https://www.truthcoin.info/blog/deniability/).
    
    The GUI PR has all the details and screenshots of the GUI additions. Here I'll just copy the relevant context for the wallet API changes:
    
    "
    In short, Paul's idea is to periodically split coins and send them to yourself, making it look like common "spend" transactions, such that blockchain ownership analysis becomes more difficult, and thus improving the user's privacy.
    I've implemented this as an additional "Deniability" wallet view. The majority of the code is in a new deniabilitydialog.cpp/h source files containing a new DeniabilityDialog class, hooked up to the WalletView class. 
    "
    
    While the Deniability dialog can be implemented entirely with the existing API, adding the core "deniabilization" functions to the CWallet and interfaces::Wallet API allows us to implement the GUI portion with much less code, and more importantly allows us to add RPC support and more thorough unit tests.
    
    -----
    Implemented basic deniability unit tests to wallet_tests
    
    -----
    Implemented a new 'walletdeniabilizecoin' RPC.
    
    -----
    Implemented fingerprint spoofing for deniabilization (and fee bump) transactions.
    Currently spoofing with data for 6 different wallet implementations, with 4 specific fingerprint-able behaviors (version, anti-fee-sniping, bip69 ordering, no-rbf).
    
    -----
    Implemented CalculateDeniabilizationFeeEstimate and CalculateDeniabilizationCycles as non-recursive functions.
    c5ea2c6fdb
  137. denavila force-pushed on Oct 14, 2024
  138. 1440000bytes commented at 9:40 pm on October 14, 2024: none
    @denavila any update on 3 and 4 from #733 (comment)?
  139. Deniability - a tool to automatically improve coin ownership privacy
    This new feature is an implementation of the ideas in Paul Sztorc's blog post "Deniability - Unilateral Transaction Meta-Privacy"(https://www.truthcoin.info/blog/deniability/).
    
    In short, the idea is to periodically split coins and send them to yourself, making it look like a common "spend" transaction, such that blockchain ownership analysis becomes more difficult, and thus improving the user's privacy.
    
    This is the GUI portion of the PR (bitcoin-core/gui). The core functionality PR is in the main repo (bitcoin/bitcoin).
    
    This PR implements an additional "Deniability" wallet view. The majority of the GUI code is in a new deniabilitydialog.cpp/h source files containing a new DeniabilityDialog class, hooked up via the WalletView class. 
    
    On startup and on notable events (new blocks, new transactions, etc), we evaluate the privacy of all coins in the wallet, and we build a "deniabilization" candidate list. UTXOs that share the same destination address are grouped together into a single candidate (see DeniabilityDialog::updateCoins and DeniabilityDialog::updateCoinTable).
    
    We inspect the blockchain data to find out if we have performed "deniabilization" transactions already, and we count how many "cycles" (self-sends) have been performed for each coin (see DeniabilityDialog::calculateDeniabilizationStats).
    Since we infer the state entirely from the blockchain data, even if the wallet is restored from a seed phrase, the state would not be lost. This also means that if the user has performed manual self-sends that have improved the ownership privacy, they will be counted too.
    
    The user can initiate the "deniabillization" process by pressing a Start button (DeniabilityDialog::startDeniabilization). The process periodically perform a "deniabilization" cycle (DeniabilityDialog::deniabilizeProc). Each such cycle goes as follows:
    A coin is selected form the candidate list. The more a coin is "deniabilized", the less likely it is to be selected. Smaller coins are also less likely to be selected.
    If a coin is selected, we prepare and broadcast a transaction, which splits the coin into a pair of new wallet addresses (DeniabilityDialog::deniabilizeCoin). 
    
    The user can control this process via a Frequency selector and a Budget spinner, which respectively determine how often to perform the cycle and how much total BTC to spend on transaction fees.
    
    If Bitcoin Core is left running continuously, the cycles would be performed at the selected frequency (with some randomization). If Bitcoin Core is shutdown, the "deniabilization" process will resume at the next restart, and if more time has elapsed than the selected frequency, it will perform a single cycle. We deliberately don't "catch up" all missed cycles, since that would expose the process to blockchain analysis.
    The state is saved and restored via QSettings (DeniabilityDialog::loadSettings and DeniabilityDialog::saveSettings).
    
    We monitor each broadcasted transaction and we automatically attempt a fee bump if the transaction is still in the memory pool since the previous cycle (DeniabilityDialog::bumpDeniabilizationTx).
    We don't issue any other deniabilization transactions until the previous transaction is confirmed (or abandoned/dropped).
    
    The process ends when the budget is exhausted or if there's no candidates left. The user can also stop the process manually by pressing a Stop button (DeniabilityDialog::stopDeniabilization).
    
    External signers are supported in a "best effort" way - since the user needs to manually sign, we postpone the processing till the external signer is connected and use some additional UI to get the user's attention to sign (see the codepath predicated on hasExternalSigner). This is not ideal, so I'm looking for some ideas if we can improve this in some way.
    
    Watch-only wallets are partially supported, where we display the candidate list, but we don't allow any processing (since we don't have the private keys to issue transactions).
    
    I've tested all this functionality on regtest, testnet, signet and mainnet. I've also added some unit tests (under WalletTests) to exercise the main GUI functionality.
    
    This is my first change and PR for Bitcoin Core, and I tried as much as possible to validate everything against the guidelines and documentation and to follow the patterns in the existing code, but I'm sure there are things I missed, so I'm looking forward to your feedback.
    In particular things I'm not very sure about - the save/restore of state via QSettings makes me a bit nervous as we store some wallet specific data there which I put some effort to validate on load, however keying the settings based on wallet name is not ideal, so I'd like to improve this somehow - either by storing the settings based on some wallet identity signature, or by storing the state in the wallet database (however that doesn't seem accessible via the interfaces::Wallet API).
    Please let me know your thoughts and suggestions.
    
    -----
    Refactored the coin update to explicitly match utxos by scriptPubKey and not rely on the ListCoin destination grouping.
    
    -----
    Fixed linter error about bitcoin-config.h not being included.
    -----
    Post-rebase fixes for cmake build.
    -----
    Refactored deprecated hex from string conversions.
    b48a95a79f
  140. denavila force-pushed on Oct 15, 2024
  141. DrahtBot removed the label CI failed on Oct 15, 2024
  142. achow101 commented at 2:32 pm on October 15, 2024: member

    This PR does not seem to have attracted much attention from reviewers. As such, it does not seem important enough right now to keep it sitting idle in the list of open PRs.

    Closing due to lack of interest.

  143. achow101 closed this on Oct 15, 2024


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin-core/gui. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-11-21 12:20 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me