Merge bitcoin/bitcoin#25504: RPC: allow to track coins by parent descriptors

a6b0c1fcc0 doc: add releases notes for 25504 (listsinceblock updates) (Antoine Poinsot)
0fd2d14454 rpc: add an include_change parameter to listsinceblock (Antoine Poinsot)
55f98d087e rpc: output parent wallet descriptors for coins in listunspent (Antoine Poinsot)
b724476158 rpc: output wallet descriptors for received entries in listsinceblock (Antoine Poinsot)
55a82eaf91 wallet: allow to fetch the wallet descriptors for a given Script (Antoine Poinsot)

Pull request description:

  Wallet descriptors are useful for applications using the Bitcoin Core wallet as a backend for tracking coins, as they allow to track coins for multiple descriptors in a single wallet. However there is no information currently given for such applications to link a coin with an imported descriptor, severely limiting the possibilities for such applications of using multiple descriptors in a single wallet. This PR outputs the matching imported descriptor(s) for a given received coin in `listsinceblock` (and friends).

  It comes from a need for an application i'm working on, but i think it's something any software using `bitcoind` to track multiple descriptors in a single wallet would have eventually. For instance i'm thinking about the BDK project. Currently, the way to achieve this is to import raw addresses with labels and to have your application be responsible for wallet things like the gap limit.

  I'll add this to the output of `listunspent` too if this gets a few Concept ACKs.

ACKs for top commit:
  instagibbs:
    ACK a6b0c1fcc0
  achow101:
    re-ACK a6b0c1fcc0

Tree-SHA512: 7a5850e8de98b439ddede2cb72de0208944f8cda67272e8b8037678738d55b7a5272375be808b0f7d15def4904430e089dafdcc037436858ff3292c5f8b75e37
This commit is contained in:
Andrew Chow
2022-08-16 12:55:17 -04:00
12 changed files with 158 additions and 9 deletions

View File

@@ -7,6 +7,7 @@ from decimal import Decimal
from itertools import product
from test_framework.blocktools import COINBASE_MATURITY
from test_framework.descriptors import descsum_create
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_array_result,
@@ -700,6 +701,38 @@ class WalletTest(BitcoinTestFramework):
txid_feeReason_four = self.nodes[2].sendmany(dummy='', amounts={address: 5}, verbose=False)
assert_equal(self.nodes[2].gettransaction(txid_feeReason_four)['txid'], txid_feeReason_four)
self.log.info("Testing 'listunspent' outputs the parent descriptor(s) of coins")
# Create two multisig descriptors, and send a UTxO each.
multi_a = descsum_create("wsh(multi(1,tpubD6NzVbkrYhZ4YBNjUo96Jxd1u4XKWgnoc7LsA1jz3Yc2NiDbhtfBhaBtemB73n9V5vtJHwU6FVXwggTbeoJWQ1rzdz8ysDuQkpnaHyvnvzR/*,tpubD6NzVbkrYhZ4YHdDGMAYGaWxMSC1B6tPRTHuU5t3BcfcS3nrF523iFm5waFd1pP3ZvJt4Jr8XmCmsTBNx5suhcSgtzpGjGMASR3tau1hJz4/*))")
multi_b = descsum_create("wsh(multi(1,tpubD6NzVbkrYhZ4YHdDGMAYGaWxMSC1B6tPRTHuU5t3BcfcS3nrF523iFm5waFd1pP3ZvJt4Jr8XmCmsTBNx5suhcSgtzpGjGMASR3tau1hJz4/*,tpubD6NzVbkrYhZ4Y2RLiuEzNQkntjmsLpPYDm3LTRBYynUQtDtpzeUKAcb9sYthSFL3YR74cdFgF5mW8yKxv2W2CWuZDFR2dUpE5PF9kbrVXNZ/*))")
addr_a = self.nodes[0].deriveaddresses(multi_a, 0)[0]
addr_b = self.nodes[0].deriveaddresses(multi_b, 0)[0]
txid_a = self.nodes[0].sendtoaddress(addr_a, 0.01)
txid_b = self.nodes[0].sendtoaddress(addr_b, 0.01)
self.generate(self.nodes[0], 1, sync_fun=self.no_op)
# Now import the descriptors, make sure we can identify on which descriptor each coin was received.
self.nodes[0].createwallet(wallet_name="wo", descriptors=True, disable_private_keys=True)
wo_wallet = self.nodes[0].get_wallet_rpc("wo")
wo_wallet.importdescriptors([
{
"desc": multi_a,
"active": False,
"timestamp": "now",
},
{
"desc": multi_b,
"active": False,
"timestamp": "now",
},
])
coins = wo_wallet.listunspent(minconf=0)
assert_equal(len(coins), 2)
coin_a = next(c for c in coins if c["txid"] == txid_a)
assert_equal(coin_a["parent_descs"][0], multi_a)
coin_b = next(c for c in coins if c["txid"] == txid_b)
assert_equal(coin_b["parent_descs"][0], multi_b)
self.nodes[0].unloadwallet("wo")
if __name__ == '__main__':
WalletTest().main()