Bitcoin Forum
June 14, 2025, 02:20:47 AM *
News: Latest Bitcoin Core release: 29.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: [1]
  Print  
Author Topic: Question about Change & Receiving addresses  (Read 170 times)
apogio (OP)
Hero Member
*****
Offline Offline

Activity: 812
Merit: 1740


Duelbits.com - Rewarding, beyond limits.


View Profile WWW
May 07, 2025, 05:02:06 PM
Merited by ABCbits (1), Cricktor (1)
 #1

Disclaimer:

I was doing an experiment and I have a question that I wasn't able to answer myself.

I will post some real data here (but not private keys etc). I will definitely not be using the wallet in the future.

The experiment:

I have a fully airgaped computer, where I installed Bitcoin Core and I created a wallet.

I got an address from there bc1qfrmthac4es7fpw5yhl7cu9kvedsgj7levwxv5g and sent some sats there.

I then stored the encrypted wallet.dat in another usb stick for backup.

I also got the descriptors of the wallet using the command listdescriptors:

Code:
> listdescriptors

{
   ...
   "desc": "wpkh([6ac77e84/84h/0h/0h]xpub6D9yC3ibKEDbLjV8fcF1AmQyBXuKErT13KeW4cVTqS7e6XmaoEMzJeU1gyHUUHwMx5F6utw4VkSB416rXrPCgRi1tLWxSHbZoWjv281oEKc/0/*)#hrntfr65"
   "internal": false
   ...
},
{
   ...
   "desc": "wpkh([6ac77e84/84h/0h/0h]xpub6D9yC3ibKEDbLjV8fcF1AmQyBXuKErT13KeW4cVTqS7e6XmaoEMzJeU1gyHUUHwMx5F6utw4VkSB416rXrPCgRi1tLWxSHbZoWjv281oEKc/1/*)#xhk25k2v"
   "internal": true
   ...
}

Then, I decided, to import the descriptors on Sparrow wallet to see if I would be able to create a simple watch-only wallet.

Sparrow didn't let me import two descriptors on the same wallet, so I had to google it and someone suggested to import the descriptor like this:

Code:
wpkh([6ac77e84/84h/0h/0h]xpub6D9yC3ibKEDbLjV8fcF1AmQyBXuKErT13KeW4cVTqS7e6XmaoEMzJeU1gyHUUHwMx5F6utw4VkSB416rXrPCgRi1tLWxSHbZoWjv281oEKc/<0;1>/*)

Notice the <0;1> in the path, which I suspect is to get both receiving and change addresses.

To my surprise, it looked like it worked perfectly, but...

I decided to import the descriptors on my full node (bitcoin core) to create a watch-only wallet there too, so I did:

Code:
> importdescriptors "[{\"desc\": \"wpkh([6ac77e84/84h/0h/0h]xpub6D9yC3ibKEDbLjV8fcF1AmQyBXuKErT13KeW4cVTqS7e6XmaoEMzJeU1gyHUUHwMx5F6utw4VkSB416rXrPCgRi1tLWxSHbZoWjv281oEKc/0/*)#hrntfr65\", \"range\": [0, 999], \"timestamp\": 1746528820, \"internal\": false, \"watchonly\": true, \"active\": true}]"

> importdescriptors "[{\"desc\": \"wpkh([6ac77e84/84h/0h/0h]xpub6D9yC3ibKEDbLjV8fcF1AmQyBXuKErT13KeW4cVTqS7e6XmaoEMzJeU1gyHUUHwMx5F6utw4VkSB416rXrPCgRi1tLWxSHbZoWjv281oEKc/1/*)#xhk25k2v\", \"range\": [0, 999], \"timestamp\": 1746528820, \"internal\": true, \"watchonly\": true, \"active\": true}]"

The "strange" result:

In Bitcoin Core, it looks like my addresses (when I click to generate a new address) are correct, but it generates the Receive addresses that Sparrow considers to be the Change addresses.

In simpler terms, it looks like:

Bitcoin Core -> Receive Address = Sparrow -> Change Address

My question:

Even though the answer may be the simplest one, like that I have confused the descriptors when importing them, or that Sparrow doesn't work well with the <0;1>, I would like to ask for a clear justification.

Mia Chloe
Hero Member
*****
Offline Offline

Activity: 742
Merit: 1232


Contact me for your designs...


View Profile
May 07, 2025, 07:37:20 PM
Merited by ABCbits (1), apogio (1)
 #2

I think  the whole thing actually comes down to how different wallets interpret the instructions for generating addresses. Bitcoin Core actually separates receiving and change addresses based on specific paths. However, when using a combined instruction (<0;1>) in another wallet like Sparrow it may not make consider same distinctions.

Think of it kinda like giving directions. Bitcoin Core follows the directions exactly, while Sparrow might take a slightly different route even with the same map. This discrepancy can actually lead to differences in how addresses are generated and displayed. Don't forget the wallet considers which would be displayed as change and which as receiving.

Forsyth Jones
Legendary
*
Offline Offline

Activity: 1568
Merit: 1442


I love Bitcoin!


View Profile WWW
May 07, 2025, 08:45:07 PM
Merited by ABCbits (2), apogio (2), Cricktor (1)
 #3

Try importing only the receiving descriptor in Sparrow Wallet, like this:

Code:
wpkh([6ac77e84/84h/0h/0h]xpub6D9yC3ibKEDbLjV8fcF1AmQyBXuKErT13KeW4cVTqS7e6XmaoEMzJeU1gyHUUHwMx5F6utw4VkSB416rXrPCgRi1tLWxSHbZoWjv281oEKc/0/*)#hrntfr65

You don't need to import the change descriptor, Sparrow wallet will automatically calculate the change path using the extended public key.

See, in my case (using another descriptor I just generated for testing) Sparrow correctly imported both receiving and change addresses from the descriptor containing the xpub:



Edit: Descriptor I used for this test for anyone who wants to compare the addresses:

Code:
wpkh([b80b046c/84h/0h/0h]xpub6D3qx2CTZBJViXQ6VESUixBqVM6YJ6oTU4khL9yNgzqrtNvLGLmpJtba5Zyw71mKHE8mL8mz63YBmvTD8HZXxJUgazTwrYViTMoWepjtQg2/0/*)#j4u6p44e

Code:
deriveaddresses "wpkh([b80b046c/84h/0h/0h]xpub6D3qx2CTZBJViXQ6VESUixBqVM6YJ6oTU4khL9yNgzqrtNvLGLmpJtba5Zyw71mKHE8mL8mz63YBmvTD8HZXxJUgazTwrYViTMoWepjtQg2/0/*)#j4u6p44e" "[0,20]"

[
  "bc1qmtqyucnay9h9e96rszy3lmmde3ysn9lntg5rt6",
  "bc1qhjht5z29c56ntsrcjzwrlzr9nrfrgens5g0f2z",
  "bc1q2zq5wwcvqzaazym6evjmexzmch4fhsa4l2fkz8",
  "bc1qwheceqql8kxygt9wvvdlnm658dy68lhmm5pzyf",
  "bc1qcjy76f9rntlg734mes39cxc79uzt6spqldwzkf",
  "bc1q3uenn9fm9yzcfrchksm0hk2r2da9z2948cuvzj",
  "bc1qt9speug5zqtc49y6dlgyyt6gp4hnrqy7alnj2n",
  "bc1qe4fcmtk9rpv078xd0wr40a0w395yum5nku3z5r",
  "bc1qt24r65fn3h0ht5qa9rc6xgdvcstegxdz9cz4wt",
  "bc1q06jwpm6amwl4xagf96nxuyq3yu6aa9j4svaf05",
  "bc1qdepnjxkqr3qwap9nrw7v6g8rtzagvnsqht8zvg",
  "bc1qfl05l3unz605fxatt92nzueh3d2m0m40gwcmla",
  "bc1qgkn9ngdffekm597lmetjgr96m9m8zw87s6efy6",
  "bc1qsyhrfqg6sx0yhmpd33rzc3whe6257ha0we823g",
  "bc1q3p3070nu8pkm8uhulaxpphk2jk3pv098q268u7",
  "bc1qw2u5czl3n0lwmawluwkdr66g4j3vjdn8vapwfx",
  "bc1q6225td877x5m58t9hkfy9k3z9lp689efdtdgu8",
  "bc1qhpj5k5d3seuvwneefnwymqcszlhpqjzp85pcmf",
  "bc1qxmf79d9h6j57q0qdv3qe2pkmgpy09pckepzf64",
  "bc1qfl66ezxeh9qxess66mc7cgwdpsttfw9l9g6w8q",
  "bc1qjnxjkmzjycscf5m0fwyfxdk00f9p3xhmkv6mtj"
]

deriveaddresses "wpkh([b80b046c/84h/0h/0h]xpub6D3qx2CTZBJViXQ6VESUixBqVM6YJ6oTU4khL9yNgzqrtNvLGLmpJtba5Zyw71mKHE8mL8mz63YBmvTD8HZXxJUgazTwrYViTMoWepjtQg2/1/*)#rpemuq9p" "[0,19]"

[
  "bc1q0m3lv7xejcalg8l8g8y07csz29d8ws2chpgv28",
  "bc1qevxuvu82cuxtaqreys5hug7fucs7cvluzyk7c3",
  "bc1qvnpqu5nmcgur8yj2480ey4e50tczxggdlt7pzk",
  "bc1qjphupx5jp77udw7u47ukcsj4rw75j25jmqu9ua",
  "bc1qn3z9glfkzuu9rvmzj6npjyjjmjl3uy35xx76s6",
  "bc1qel6gtgc4a7zcxhk8jrw66eael4a39wus4dl276",
  "bc1qjmd3ptm7sd0h90w5669at468m2pv63t4acmqea",
  "bc1qkcwpfelhpwm6js2z6eea5f42qt8ug5t9a5pe2h",
  "bc1qzpj33mzwneftg6a2nv00zmgy520w3c2fezndkj",
  "bc1qkhcmvdecv0c7hpmktkdmq8he0vajzsecwxh27f",
  "bc1qale0mmkxmu697lj5586sd8sumkrn6w2t2c02sc",
  "bc1q4wttw4lpky5698k0kt0h8drhke8wezvypu006a",
  "bc1qn5a4x7u8xl6kec3mf9lckvvg0uf94m8jug6te4",
  "bc1qeyyfj5vwq8jveq4z0mggjt2urwv0l3j29gvefa",
  "bc1qf8kfl0vehrzqg44rym394mer855twmlp7xuuup",
  "bc1qpct0lepxahcxj5fusn88hzqd3j0z79fxet40d9",
  "bc1qps0ree7qvdcn3le7lukaxzcfvhe4xfg6g7t2hn",
  "bc1q6myn66mnvx8kwshn38pczamq7zyrn5n6l7pe33",
  "bc1qpyueytk809jj4wcn9tl6mq3h479hcunt245je0",
  "bc1q5d2jlqkaw0p4yuty86e28ptwtzj047wtkm6pzr"
]

apogio (OP)
Hero Member
*****
Offline Offline

Activity: 812
Merit: 1740


Duelbits.com - Rewarding, beyond limits.


View Profile WWW
May 08, 2025, 04:37:33 AM
 #4

Thank you both for the answers, it's true that it worked when I changed my descriptor to one that didn't include <0;1> at the end.

I am very happy the issue is resolved, and it looks like the post I randomly found on Google was wrong. I am trying to find it for reference, but I can't...

Anyway, again, thank you both.

I will lock the topic soon.

nc50lc
Legendary
*
Offline Offline

Activity: 2814
Merit: 7285


Self-proclaimed Genius


View Profile
May 08, 2025, 06:06:58 AM
 #5

-snip- I am very happy the issue is resolved, and it looks like the post I randomly found on Google was wrong. I am trying to find it for reference, but I can't...
I think it's correct but a bug is somehow causing the internal and external chain reversed in some scenarios.

It's actually inconsistent;
I can reproduce it in some "MultiPath descriptor", some descriptors work as intended, some result with the same behavior.
I used the latest release version v2.1.3.

So, you may have found an issue with Sparrow Wallet's implementation of BIP389.
It's worth reporting as a new issue if it's not posted already: github.com/sparrowwallet/sparrow/issues

apogio (OP)
Hero Member
*****
Offline Offline

Activity: 812
Merit: 1740


Duelbits.com - Rewarding, beyond limits.


View Profile WWW
May 08, 2025, 06:30:23 AM
 #6

I think it's correct but a bug is somehow causing the internal and external chain reversed in some scenarios.

It's actually inconsistent;
I can reproduce it in some "MultiPath descriptor", some descriptors work as intended, some result with the same behavior.
I used the latest release version v2.1.3.

So, you may have found an issue with Sparrow Wallet's implementation of BIP389.
It's worth reporting as a new issue if it's not posted already: github.com/sparrowwallet/sparrow/issues

I definitely will, but let's just be sure first. Because it's possible that I may have screwed up...

Let me give some additional context, because there must be an issue with my Core watch-only wallet (because of my mistake) and not with my Sparrow watch-only wallet.

So, I am on my watch-only wallet (on Core) and I am running > listdescriptors

It produces this:

Code:
{
  "wallet_name": "test",
  "descriptors": [
    {
      "desc": "wpkh([6ac77e84/84h/0h/0h]xpub6D9yC3ibKEDbLjV8fcF1AmQyBXuKErT13KeW4cVTqS7e6XmaoEMzJeU1gyHUUHwMx5F6utw4VkSB416rXrPCgRi1tLWxSHbZoWjv281oEKc/0/*)#hrntfr65",
      "timestamp": 1746517954,
      "active": false,
      "range": [
        0,
        1000
      ],
      "next": 1,
      "next_index": 1
    },
    {
      "desc": "wpkh([6ac77e84/84h/0h/0h]xpub6D9yC3ibKEDbLjV8fcF1AmQyBXuKErT13KeW4cVTqS7e6XmaoEMzJeU1gyHUUHwMx5F6utw4VkSB416rXrPCgRi1tLWxSHbZoWjv281oEKc/1/*)#xhk25k2v",
      "timestamp": 1746517954,
      "active": true,
      "internal": false,
      "range": [
        0,
        1003
      ],
      "next": 4,
      "next_index": 4
    }
  ]
}


It confuses me that:
(a) it says "active": false on the first descriptor
(b) I was expecting that the first descriptor (/0/*) should be saying "internal": false and the second one (/1/*) should be saying "internal": true

It looks like I may have messed up when creating the watch-only wallet...

Edit: The commands I 've written in my OP may be correct but when I actually submitted the commands and spotted the issue I may have made a mistake. This inconsistency from my part, could be happening because I didn't copy-paste the exact commands I used, but instead I re-wrote them (probably correctly the second time). So I may have written them wrong at first when I created the core watch-only wallet, but correctly the second time when I created the OP.

nc50lc
Legendary
*
Offline Offline

Activity: 2814
Merit: 7285


Self-proclaimed Genius


View Profile
May 08, 2025, 11:20:37 AM
Merited by LoyceV (12), ABCbits (4), Cricktor (2), apogio (1)
 #7

It confuses me that:
(a) it says "active": false on the first descriptor
This happens if you've imported a same-script descriptor with the same "internal" flag that has its "active" flag set to true.
The older descriptor will then be marked false.

Quote from: apogio
(b) I was expecting that the first descriptor (/0/*) should be saying "internal": false and the second one (/1/*) should be saying "internal": true
Can you try it again in another watch-only wallet?
Perhaps it's the case above where you forgot to include the "internal" flag.


Anyways, here's an example in Sparrow where it successfully imported a "MultiPath descriptor":

Receiving Addresses:
deriveaddresses "wpkh([17cedf16/84h/0h/0h]xpub6CpCrfxag5Dg1mnpf8eZrMgfY8igb9PZrPgEcAKqdAn486pbAYStBsYz3xgPDc35VJVG45Ntjwr jk3rj7deonTzq1broC77ipsVWKVhsCi5/0/*)#395jh6ks" "[0,4]"
Code:
[
  "bc1qhxk4hx2sxnx2u0mcux8wzermrxh2lruwsxa8rh",
  "bc1qwfewh8l35emuml9jr6fmvw90734gwc4pkddvyw",
  "bc1qez7gymgf0g9ra87w7ed0gmxaf45zakj329s8ce",
  "bc1q5khvsht8846r2wvd4qn2fhe377qtkuzs6d8ndr",
  "bc1qmpm5et3gq9xqjhkqrpl38vg2jnrz0fy8zs80xv"
]

Change Address Index 0:
".../1/*)#q33n20xg" "[0,0]"
Code:
[
  "bc1qaxtkyzgs94xhaqshyavu5ua5qmvzzs6na7furc"
]

Import to Sparrow:



You can see that the instructions you found on Google works.



Then, I've just tested the descriptor that you've provided in the OP and it seem to be working normally.
The instance where I reproduced the reversed internal flag in Sparrow may have been a misunderstanding on Bitcoin Core's getaddressinfo "ischange: true" flag of a non-active descriptor's receiving address.

Sparrow's BIP389 import seem to be working as intended.

apogio (OP)
Hero Member
*****
Offline Offline

Activity: 812
Merit: 1740


Duelbits.com - Rewarding, beyond limits.


View Profile WWW
May 08, 2025, 12:20:28 PM
 #8

-snip-

Sorry for the small merit amount, I am out of sMerit. Your answer is very helpful.

Indeed, I tried it and it works perfectly. I have messed up with the descriptors. I have set the internal flag wrong. Now, I 've tried it in another wallet and it works like a charm.

I think it's time for me to lock the thread. Again, thank you!

Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!