Silent Payments: Sending (take 2) #35302

pull Eunovo wants to merge 18 commits into bitcoin:master from Eunovo:implement-bip352-sending changing 47 files +8745 −114
  1. Eunovo commented at 4:16 AM on May 16, 2026: contributor

    This PR is part of integrating silent payments into Bitcoin Core. It is the second iteration of #28201. Status and tracking for the project is managed in #28536

    Prerequisite PRs:

    Sending

    Silent Payments logic The main focus of this PR is:

    • Applying the Taptweak to a taproot internal private key (this is a copy-paste of the code for applying the taptweak in the signing process)
    • Getting a private key from a given scriptPubKey
    • Creating silent payment outputs
    • Applying the created scriptPubKeys back to the vector of CRecipients
    • The functions are then used together to create silent payment outputs during CreateTransactionInternal.
    • The change output type is correctly chosen when paying to a silent payment address
    • RBF for silent payments transactions are handled correctly
    • Functional tests
  2. DrahtBot commented at 4:16 AM on May 16, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

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

    <!--006a51241073e994b41acfe9ec718e94-->

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/35302.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline and AI policy for information on the review process.

    Type Reviewers
    Concept ACK rkrux

    If your review is incorrectly listed, please copy-paste <code>&lt;!--meta-tag:bot-skip--&gt;</code> into the comment that the bot should ignore.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #bitcoin-core/gui/954 (Add dialog to select change output when bumping fee by pablomartin4btc)
    • #35861 (Testnet 5 (BIP95) by fjahr)
    • #35819 (test: add coverage for untested descriptor parse error paths by azuchi)
    • #35793 (Implement BIP 54 (Consensus Cleanup) without mainnet activation by darosior)
    • #35716 (wallet: Replace mapWallet and wtxOrdered with a boost::multi_index by achow101)
    • #35569 (Encapsulation for CTransaction by purpleKarrot)
    • #35501 (wallet: store all witness variants of a transaction by achow101)
    • #35433 (wallet: deprecate replaceable argument from transaction (and psbt) creation (and modification) RPCs by rkrux)
    • #35317 (wallet: fix ignored subtract_fee_from_outputs option by stutxo)
    • #35301 (Silent Payments: Implement bip352 (take 2) by Eunovo)
    • #34405 (wallet: skip APS when no partial spend exists by 8144225309)
    • #33034 (wallet: Store transactions in a separate sqlite table by achow101)
    • #27865 (wallet: Track no-longer-spendable TXOs separately by achow101)
    • #27260 (Enhanced error messages for invalid network prefix during address parsing. by portlandhodl)

    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.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

    LLM Linter (✨ experimental)

    Possible typos and grammar issues:

    • desitnation -> destination [misspelled in src/addresstype.h; impacts readability]
    • Payements -> Payments [misspelled in src/wallet/spend.cpp comment]
    • reedeemScript -> redeemScript [misspelled in src/wallet/spend.cpp comment]
    • know way -> no way [typo in src/wallet/spend.cpp comment; intended meaning is clear but spelling is wrong]

    Possible places where named args for integral literals may be used (e.g. func(x, /*named_arg=*/0) in C++, and func(x, named_arg=0) in Python):

    • output.Set(hex_data.begin(), hex_data.end(), true) in src/test/bip352_tests.cpp
    • wallet.walletcreatefundedpsbt([], [{self.sp_addr_1: 5}], 0) in test/functional/wallet_silentpayments_sending.py

    <sup>2026-07-24 15:18:39</sup>

  3. rkrux commented at 9:11 AM on May 20, 2026: contributor

    Concept ACK 03c835d

  4. in src/wallet/scriptpubkeyman.cpp:1484 in f9a10e9ea9
    1476 | @@ -1477,6 +1477,30 @@ bool DescriptorScriptPubKeyMan::AddCryptedKey(const CKeyID& key_id, const CPubKe
    1477 |      return true;
    1478 |  }
    1479 |  
    1480 | +Key DescriptorScriptPubKeyMan::GetPrivKeyForSilentPayment(const CScript& scriptPubKey) const
    1481 | +{
    1482 | +    std::vector<std::vector<unsigned char>> solutions;
    1483 | +    TxoutType whichType = Solver(scriptPubKey, solutions);
    1484 | +    if (whichType == TxoutType::NONSTANDARD || whichType == TxoutType::MULTISIG || whichType == TxoutType::WITNESS_UNKNOWN ) return {};
    


    theStack commented at 1:21 AM on May 23, 2026:

    in f9a10e9ea9b57f6b21a6dbfd1239c3ba60f721f8: I think it would be more robust to express this condition using the explicit list of relevant output script types as specified in the BIP, i.e. return early if whichType is not any of P2TR, P2WPKH, P2SH-P2WPKH or P2PKH. Otherwise, the condition has to be extended whenever the TxoutType enum class gets extended (e.g. P2A has been added since and is not there in the condition).


    Eunovo commented at 11:07 AM on June 7, 2026:

    Done.

  5. in src/wallet/scriptpubkeyman.cpp:1488 in f9a10e9ea9
    1483 | +    TxoutType whichType = Solver(scriptPubKey, solutions);
    1484 | +    if (whichType == TxoutType::NONSTANDARD || whichType == TxoutType::MULTISIG || whichType == TxoutType::WITNESS_UNKNOWN ) return {};
    1485 | +    std::unique_ptr<FlatSigningProvider> coin_keys = GetSigningProvider(scriptPubKey, true);
    1486 | +    if (!coin_keys || coin_keys->keys.size() != 1) return {};
    1487 | +    const auto& [_, key] = *coin_keys->keys.begin();
    1488 | +    (void) _;
    


    theStack commented at 1:34 AM on May 23, 2026:

    f9a10e9ea9b57f6b21a6dbfd1239c3ba60f721f8: nit: is this line needed? Not sure what the exact rules in C++ are about the single underscore identifier, but I haven't seen this fake-use pattern (probably with the intention to avoid warnings) in other similar places where we use it.


    Eunovo commented at 11:07 AM on June 7, 2026:

    Removed.

  6. in src/wallet/rpc/spend.cpp:436 in e3ef349ad5
     432 | @@ -420,6 +433,11 @@ RPCMethod sendmany()
     433 |              ParseOutputs(sendTo),
     434 |              InterpretSubtractFeeFromOutputInstructions(request.params[4], sendTo.getKeys())
     435 |      );
     436 | +    auto it = std::find_if(recipients.begin(), recipients.end(), [](const auto& r) { return std::holds_alternative<V0SilentPaymentDestination>(r.dest); });
    


    theStack commented at 1:41 AM on May 23, 2026:

    in e3ef349ad5ad2bc283644b5faaa1a6fa234e16bd: could slightly simplify by using std::any_of instead (returns a bool instead of an iterator)


    Eunovo commented at 11:08 AM on June 7, 2026:

    Done.

  7. in src/wallet/rpc/spend.cpp:1280 in e3ef349ad5
    1294 |              outputs = NormalizeOutputs(request.params[0]);
    1295 |              std::vector<CRecipient> recipients = CreateRecipients(
    1296 |                      ParseOutputs(outputs),
    1297 |                      InterpretSubtractFeeFromOutputInstructions(options["subtract_fee_from_outputs"], outputs.getKeys())
    1298 |              );
    1299 | -            CCoinControl coin_control;
    


    theStack commented at 1:42 AM on May 23, 2026:

    in e3ef349ad5ad2bc283644b5faaa1a6fa234e16bd: any reason why the coin_control instance got moved up?


    Eunovo commented at 11:08 AM on June 7, 2026:

    Reverted.

  8. in src/wallet/rpc/spend.cpp:171 in e3ef349ad5
     167 | @@ -168,6 +168,14 @@ static void PreventOutdatedOptions(const UniValue& options)
     168 |      }
     169 |  }
     170 |  
     171 | +static void IsSilentPaymentsEnabled(const CWallet &pwallet)
    


    theStack commented at 1:45 AM on May 23, 2026:

    in e3ef349ad5ad2bc283644b5faaa1a6fa234e16bd: for functions starting with Is, I'd usually expect to return a boolean value. maybe rename with using Ensure as a prefix instead?


    Eunovo commented at 11:08 AM on June 7, 2026:

    Done.

  9. Eunovo commented at 11:45 AM on May 25, 2026: contributor

    @theStack I'll attend to your reviews when I rebase on https://github.com/bitcoin/bitcoin/pull/35301

  10. DrahtBot added the label Needs rebase on May 28, 2026
  11. Eunovo force-pushed on Jun 7, 2026
  12. Eunovo force-pushed on Jun 7, 2026
  13. DrahtBot added the label CI failed on Jun 7, 2026
  14. DrahtBot commented at 11:15 AM on June 7, 2026: contributor

    <!--85328a0da195eb286784d51f73fa0af9-->

    🚧 At least one of the CI tasks failed. <sub>Task lint: https://github.com/bitcoin/bitcoin/actions/runs/27090770600/job/79953877713</sub> <sub>LLM reason (✨ experimental): (empty)</sub>

    <details><summary>Hints</summary>

    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.

    </details>

  15. DrahtBot removed the label Needs rebase on Jun 7, 2026
  16. Eunovo force-pushed on Jun 7, 2026
  17. Eunovo force-pushed on Jun 8, 2026
  18. Eunovo force-pushed on Jun 8, 2026
  19. DrahtBot removed the label CI failed on Jun 8, 2026
  20. DrahtBot added the label Needs rebase on Jun 12, 2026
  21. Eunovo force-pushed on Jun 23, 2026
  22. DrahtBot added the label CI failed on Jun 23, 2026
  23. DrahtBot removed the label Needs rebase on Jun 23, 2026
  24. Eunovo force-pushed on Jun 24, 2026
  25. in test/functional/wallet_silentpayments_sending.py:323 in 989d3548c6
     318 | +            expected_error_fund=(-4, "No silent payment eligible inputs were found."),
     319 | +        )
     320 | +
     321 | +    def test_address_reuse(self):
     322 | +        self.log.info("Testing that two sends to the same SP address produce distinct outputs")
     323 | +        self.nodes[0].createwallet(wallet_name="address_reuse_sender", descriptors=True)
    


    rkrux commented at 1:41 PM on June 24, 2026:

    In 989d3548c6933a4bc31b82a6f950029440e1d232 "tests: add sending functional tests"

    descriptors=True

    Please remove all occurrences of this createwallet argument, it's not required post disallowing loading legacy wallets.

    I see a new SP specific wallet flag is introduced in the SP receiving PR #32966. I have not checked the implementation yet but from a pure functionality POV, the addition of that new wallet flag should not necessitate the passing of the descriptors flag now (which I assume, in all likelihood, is not happening and thus this flag should be removed from the tests).


    Eunovo commented at 9:38 AM on June 26, 2026:

    Done

  26. in test/functional/wallet_silentpayments_sending.py:403 in 989d3548c6
     398 | +            assert txid not in node.getrawmempool()
     399 | +            self.generate(node, 1)
     400 | +            assert wallet.gettransaction(new_txid)["confirmations"] == 1
     401 | +
     402 | +        def send_psbt(w):
     403 | +            psbt = w.walletcreatefundedpsbt([], [{SP_ADDR_1: 5}], 0, {"replaceable": True})["psbt"]
    


    rkrux commented at 1:44 PM on June 24, 2026:

    In 989d354 "tests: add sending functional tests"

    "replaceable": True

    Please remove all occurrences of this in transaction/psbt creation/fund/send RPCs that are being used in this test. PR #35433 seeks to deprecate it (hopefully in the upcoming release) because of prevalence of fullrbf in the network and we shouldn't encourage the use of this argument in the functional tests.


    Eunovo commented at 9:38 AM on June 26, 2026:

    Done

  27. in test/functional/wallet_silentpayments_sending.py:426 in 989d3548c6 outdated
     421 | +        self.generate(node, 1)  # rbf_sendall funding UTXO now has 2 confs; extra UTXO has 1 conf
     422 | +
     423 | +        txid = wallet.sendall(recipients=[SP_ADDR_1], options={"replaceable": True, "maxconf": 1})["txid"]
     424 | +        assert txid in node.getrawmempool()
     425 | +        original_input_count = len(node.getrawtransaction(txid, True)["vin"])
     426 | +        result = wallet.bumpfee(txid, fee_rate=500)
    


    rkrux commented at 1:58 PM on June 24, 2026:

    In 989d354 "tests: add sending functional tests"

    https://github.com/bitcoin/bitcoin/blob/d84fc352cbc1363df5cd6024a22e73fc63283e4f/src/wallet/rpc/spend.cpp#L1003-L1006

    1. There's an outputs argument in the (psbt)bumpfee RPCs that allows to add new outputs in the replacing transaction. Please add a test case covering the behaviour when this argument is passed while replacing a silent payment transaction.
    2. Please also add a test case covering the scenario where the user is able to rbf-cancel the SP transaction by sending the funds to themselves.

    Eunovo commented at 9:38 AM on June 26, 2026:

    Added new tests for bumpfee with the outputs replaced.

  28. rkrux commented at 2:09 PM on June 24, 2026: contributor

    Reviewed only the last testing commit partially 989d354 "tests: add sending functional tests".

  29. DrahtBot removed the label CI failed on Jun 24, 2026
  30. Eunovo force-pushed on Jun 26, 2026
  31. DrahtBot added the label CI failed on Jun 26, 2026
  32. DrahtBot commented at 9:37 AM on June 26, 2026: contributor

    <!--85328a0da195eb286784d51f73fa0af9-->

    🚧 At least one of the CI tasks failed. <sub>Task Windows native, fuzz, VS: https://github.com/bitcoin/bitcoin/actions/runs/28226175984/job/83618564020</sub> <sub>LLM reason (✨ experimental): CI failed because the fuzz RPC target errored out: scantxforsilentpayments RPC command is missing from rpc.cpp’s RPC_COMMANDS_SAFE_FOR_FUZZING/RPC_COMMANDS_NOT_SAFE_FOR_FUZZING.</sub>

    <details><summary>Hints</summary>

    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.

    </details>

  33. Eunovo force-pushed on Jun 26, 2026
  34. Eunovo force-pushed on Jun 26, 2026
  35. Eunovo force-pushed on Jun 26, 2026
  36. DrahtBot removed the label CI failed on Jun 26, 2026
  37. in test/functional/wallet_silentpayments_sending.py:10 in 22e42cb9b5 outdated
       5 | +from test_framework.segwit_addr import bech32_encode, convertbits, Encoding
       6 | +from test_framework.test_framework import BitcoinTestFramework
       7 | +from test_framework.util import assert_equal, assert_raises_rpc_error
       8 | +
       9 | +XPRV = "tprv8ZgxMBicQKsPevADjDCWsa6DfhkVXicu8NQUzfibwX2MexVwW4tCec5mXdCW8kJwkzBRRmAay1KZya4WsehVvjTGVW6JLqiqd8DdZ4xSg52"
      10 | +XPUB = "tpubD6NzVbkrYhZ4YNXVQbNhMK1WqguFsUXceaVJKbmno2aZ3B6QfbMeraaYvnBSGpV3vxLyTTK9DYT1yoEck4XUScMzXoQ2U2oSmE2JyMedq3H"
    


    rkrux commented at 12:48 PM on June 27, 2026:

    In 22e42cb9b58a3406ab1870be71aa38f861fbba4d "tests: add sending functional tests"

    After PR #35543 is merged, we don't need to (manually find and) hardcode xprvs and xpubs in the functional tests anymore. Something like the following can be done, similar to how ECKey.generate() is used:

    from test_framework.extendedkey import ExtendedPrivateKey
    ...
    extended_key = ExtendedPrivateKey.generate()
    xprv, xpub = extended_key.to_string(), extended_key.pubkey().to_string()
    

    Eunovo commented at 9:04 AM on June 29, 2026:

    Will fix when I rebase.


    Eunovo commented at 3:25 PM on July 24, 2026:

    Fixed.

  38. in test/functional/wallet_silentpayments_sending.py:50 in 22e42cb9b5
      45 | +
      46 | +    def skip_test_if_missing_module(self):
      47 | +        self.skip_if_no_wallet()
      48 | +
      49 | +    def init_wallet(self, *, node):
      50 | +        pass
    


    rkrux commented at 12:49 PM on June 27, 2026:

    In 22e42cb9b58a3406ab1870be71aa38f861fbba4d "tests: add sending functional tests"

    I assume it's taken from wallet_taproot.py? It's dead code and should be removed.


    Eunovo commented at 9:22 AM on June 29, 2026:

    Done.

  39. in test/functional/wallet_silentpayments_sending.py:112 in 22e42cb9b5
     107 | +            return {}
     108 | +
     109 | +        if single:
     110 | +            txid = wallet.sendtoaddress(addr0, amt0)
     111 | +            assert txid in node.getrawmempool()
     112 | +            assert wallet.gettransaction(txid)["confirmations"] == 0
    


    rkrux commented at 12:52 PM on June 27, 2026:

    In 22e42cb9b58a3406ab1870be71aa38f861fbba4d "tests: add sending functional tests"

    There are many asserts in the file of the form assert X == Y. Not sure why the Drahtbot has not mentioned in this PR (because it has commented on several PRs before), but the preference is to use the assert_equal(X, Y) utility function from the testing framework.


    Eunovo commented at 9:22 AM on June 29, 2026:

    Done.

  40. in test/functional/wallet_silentpayments_sending.py:538 in 22e42cb9b5
     533 | +        for _ in range(3):
     534 | +            self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
     535 | +        self.nodes[0].syncwithvalidationinterfacequeue()
     536 | +
     537 | +        for txid in txids:
     538 | +            assert wallet.gettransaction(txid)["confirmations"] <= 0
    


    rkrux commented at 12:56 PM on June 27, 2026:

    In 22e42cb "tests: add sending functional tests"

    Similar to the equality: s/assert X <= Y/assert_greater_than(Y, X)


    Eunovo commented at 9:22 AM on June 29, 2026:

    Done.

  41. in test/functional/wallet_silentpayments_sending.py:327 in 22e42cb9b5 outdated
     322 | +        assert finalized["complete"]
     323 | +        txid = self.nodes[0].sendrawtransaction(finalized["hex"])
     324 | +        verify_inputs(txid)
     325 | +        self.generate(self.nodes[0], 1)
     326 | +        self.assert_sp_outputs(txid, (self.scan_key_1, self.spend_key_1, 1))
     327 | +        cleanup()
    


    rkrux commented at 1:04 PM on June 27, 2026:

    In 22e42cb "tests: add sending functional tests"

    For all these cases where the testing functionality is preceded by setup() and succeeded by cleanup(), a local context manager can be used for readability. Refer how a custom WalletUnlock context manager is used.

    https://github.com/bitcoin/bitcoin/blob/7a74f6529357cf78ee74f0316b9502664f56889e/test/functional/test_framework/wallet_util.py#L95-L112


    Eunovo commented at 8:55 AM on June 29, 2026:

    It seems a bit overkill to create a Context Manager class for this one test function. I think the current setup-cleanup code is better for this case


    rkrux commented at 9:50 AM on June 30, 2026:

    There is a precedent (or soon going to be) of using context managers in a single test file: https://github.com/bitcoin/bitcoin/pull/35003/changes#diff-815ba9df1be928d33d72629e1cdd6208a68493b24a4cdee0c823fb0375acd44aR34-R47

    I think it seems reasonable to create and use a context manager if it improves readability. I also feel it's a nice-to-have and not a must-have.


    Eunovo commented at 6:40 AM on July 1, 2026:

    Changed the code to use the generator context manager pattern.

  42. rkrux commented at 1:14 PM on June 27, 2026: contributor

    Thanks for addressing the previous suggestions in both the source code and the tests - git range-diff 989d354...22e42cb

    Partial review of the test commit 22e42cb "tests: add sending functional tests".

  43. Eunovo force-pushed on Jun 29, 2026
  44. Eunovo force-pushed on Jul 1, 2026
  45. Eunovo force-pushed on Jul 7, 2026
  46. DrahtBot added the label CI failed on Jul 7, 2026
  47. Eunovo commented at 3:09 PM on July 7, 2026: contributor

    CI failure seems unrelated.

  48. DrahtBot added the label Needs rebase on Jul 13, 2026
  49. crypto: add read-only method to KeyPair
    Add a method for passing a KeyPair object to secp256k1 functions expecting a secp256k1_keypair.
    This allows for passing a KeyPair directly to a secp256k1 function without needing to create a
    temporary secp256k1_keypair object.
    15e82f6698
  50. Add "sp" HRP 10240e6092
  51. Add Silent Payments address types
    Add V0SilentPaymentsDestination and UnknownSilentPaymentsDestination
    for v1 to v30; Bip352 reserves v31 for a backwards incompatible change.
    v1 to v30 addresses are handled as specified in Bip352. The extra_data
    will be ignored during sending.
    
    Valid v0 and v1 addresses were added to key_io_valid.json for testing,
    and invalid v0 and a v31 address were added to key_io_invalid.json.
    
    V0SilentPaymentsDestination must not be formed from invalid public keys;
    the operations that must be performed to send to a V0SilentPaymentsDestination,
    require valid public keys. Hence, the V0SilentPaymentsDestination struct takes
    the following precautions to ensure that such invalid states are not possible:
    - The constructor will throw when provided with invalid public keys
    - The public keys are private and const references can be retrieved using member functions.
    75950c7c4b
  52. common: add bip352.{h,cpp} secp256k1 module
    Wrap the silentpayments module from libsecp256k1. This is placed in
    common as it is intended to be used by:
    
      * RPCs: for parsing addresses
      * Wallet: for sending, receiving, spending silent payments outputs
      * Node: for creating silent payments indexes for light clients
    d7f1998fa5
  53. wallet: disable sending to silent payments address
    Have `IsValidDestination` return false for silent payments destinations
    and set an error string when decoding a silent payments address.
    
    This prevents anyone from sending to a silent payments address before
    sending is implemented in the wallet, but also allows the functions to
    be used in the unit testing famework.
    b2ffab8f62
  54. tests: add BIP352 test vectors as unit tests
    Use the test vectors to test sending and receiving. A few cases are not
    covered here, namely anything that requires testing specific to the
    wallet. For example:
    
    * Taproot script path spending is not tested, as that is better tested in
      a wallets coin selection / signing logic
    * Re-computing outputs during RBF is not tested, as that is better
      tested in a wallets RBF logic
    
    The unit tests are written in such a way that adding new test cases is
    as easy as updating the JSON file
    90c2898fe2
  55. wallet: get serialized size for `V0SilentPayments`
    BIP352 v0 specifies that a silent payment output is a taproot output.
    Taproot scriptPubKeys are a fixed size, so when calculating the
    serialized size for a CRecipient with a V0SilentPayments destination,
    use WitnessV1Taproot for the serialized txout size.
    3892ec5ce0
  56. wallet: add method for retreiving a private key
    Add a method for retreiving a private key for a given scriptPubKey.
    If the scriptPubKey is a taproot output, tweak the private key with the
    merkle root or hash of the public key, if applicable.
    0500c3134f
  57. wallet: flag silent payments transactions
    Add a flag to the `CoinControl` object if silent payments destinations
    are provided. Before adding the flag, call a function which checks if:
    
    * The wallet has private keys
    * The wallet is unlocked
    
    Without both of the above being true, we cannot send to a silent payment
    address.
    
    During coin selection, if this flag is set, skip taproot inputs when
    script spend data is available. This is based on the assumption that if
    a user provides script spend data, they don't have access to the key
    path spend. As future improvement, we could instead check to see if we
    have access to the key path spend, and only exclude the output when we
    don't regardless of whether or not the user provides script spend data.
    
    Also skip UTXOs of type `WITNESS_UNKNOWN`, although it is very unlikely
    our wallet would ever try to spend a witness unknown output.
    0dcfc851ea
  58. wallet: add `IsInputForSharedSecretDerivation` function dccc255471
  59. wallet: make coin selection silent payments aware
    The coin selection algorithm wasn't guaranteed
    to return a set of coins that are eligible for silent
    payments transactions even when eligible coins
    were available.
    
    This commit forces coin selection to first select
    eligible coins before attempting to complete
    the result set with ineligible coins.
    
    If the transaction cannot be fulfilled with only
    eligible coins, coin selection will "preset" eligible
    outputs in descending order of `effective_value`
    and attempt to construct a selection from the
    remaining available coins.
    ac2d30fd71
  60. wallet: add `CreateSilentPaymentsOutputs` function
    `CreateSilentPaymentsOutputs` gets the correct private keys, adds them
    together, groups the silent payments destinations and then generates the
    taproot script pubkeys. These are then passed back to
    CreateTransactionInternal, which uses these scriptPubKeys to update
    vecSend before adding them to the transaction outputs.
    2f28820cab
  61. wallet: update wallet change type selection logic for sp dest
    If sending to a silent payments dest, the change type should be taproot
    because silent payments dests appear as taproot outputs on the blockchain.
    46005f1f90
  62. wallet: enable sending to silent payments address
    Treat silent payments addresses as valid destination. Also disable using
    silent payments addresses with the `addr()` descriptor, as this
    descriptor expects an encoding of a scriptPubKey, whereas a silent
    payment address consists of instructions on how to generate a
    scriptPubKey for the recipient.
    
    Co-authored-by: Novo <eunovo9@gmail.com>
    2717f420e8
  63. wallet/rpc: add sp addr support to sendall 99e0447190
  64. wallet/rbf: support addition of new sp outputs
    Handle sp addresses that may be supplied in the `outputs` param of `bumpfee` and `psbtbumpfee`.
    
    The outputs must be passed as CTxDestinations rather than scriptPubKeys to `CreateRateBumpTransaction`,
    so `CreateTransaction` can properly transform the provided sp addresses to taproot scriptPubKeys.
    3192061b2c
  65. rpc/util: add scantxforsilentpayments
    Useful in sp sending functional tests to check that the resulting taproot outputs are correct.
    39a705eae1
  66. tests: add sending functional tests 4ad3cbf575
  67. Eunovo force-pushed on Jul 24, 2026
  68. DrahtBot removed the label CI failed on Jul 24, 2026
  69. DrahtBot removed the label Needs rebase on Jul 24, 2026
  70. DrahtBot added the label Needs rebase on Aug 4, 2026
  71. DrahtBot commented at 10:56 PM on August 4, 2026: contributor

    <!--cf906140f33d8803c4a75a2196329ecb-->

    🐙 This pull request conflicts with the target branch and needs rebase.


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-08-11 03:51 UTC

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