Skip to content

Conversation

maflcko
Copy link
Member

@maflcko maflcko commented Dec 10, 2021

This function was added in commit 9594139.

However, now that taproot was added, I think it might be worth unconditionally trying bech32(m).

Reminder:

  • If the user picked the change type, this patch won't change anything.
  • If the user left all settings unchanged or explicitly set address type to bech32(m), the change will already be bech32(m) for all code paths.
  • If there is any bech32(m) output, the change will already be bech32(m)

This patch will only change behaviour if the user picked p2sh-witness as address type and there are no bech32(m) outputs. However, it seems inconsistent to special-case this one for a questionable privacy benefit, given that there are already potential privacy leaks with the other settings. Moreover, any potential privacy leak seems questionable, since p2sh-witness will later be revealed to be a wrapped witness spend on-chain. So I think we might as well use native witness to at least save the user some fees.

@maflcko
Copy link
Member Author

maflcko commented Dec 10, 2021

Obviously this will fail tests and need docs fixed up. I'll do that unless there are NACKs.

@maflcko
Copy link
Member Author

maflcko commented Dec 10, 2021

(un?) related: According to https://transactionfee.info/charts/inputs-types-by-count/ about 80% of spends today are using the witness stack.

@theStack
Copy link
Contributor

Light concept ACK

Talking about potential privacy loss, isn't the main concern here to consider how hard it is to deduce which one is the change output? (https://en.bitcoin.it/wiki/Privacy#Change_address_detection)
For the mentioned special scenario "p2sh-witness as address type and there are no bech32(m) outputs." and a simple spend to a P2SH(-wrapped) destination:

  • On master, the change address is also P2SH(-wrapped) -> Perfect, both outputs are equally likely to be change.
  • On the PR branch, the change address is bech32(m). Is this slightly worse? (Or even better, as analyzers assume the change address type usually matches the input type and would wrongly deduce that the bech32(m) output is actually the destination?)

Just thinking out loud, would be very interested to hear comments about this.

@maflcko
Copy link
Member Author

maflcko commented Dec 14, 2021

On master, the change address is also P2SH(-wrapped) -> Perfect, both outputs are equally likely to be change.

Not sure this is necessarily true. If the payment is unwrapped to be non-witness and our change is unwrapped to be witness (which is likely because ~80% of all spends are using the witness stack and also P2SH seems to be exclusively used for wrapping a witness program), then this can be combined with other leaks to determine the change address.

On the PR branch, the change address is bech32(m). Is this slightly worse? (Or even better, as analyzers assume the change address type usually matches the input type and would wrongly deduce that the bech32(m) output is actually the destination?)

As you say it might be slightly worse or slightly better, depending on the context. After all those are heuristics.

@maflcko
Copy link
Member Author

maflcko commented Dec 14, 2021

An alternative approach I could think of would be to treat a wallet that can provide bech32m addresses as a taproot wallet. A wallet that can provide bech32 as a wpkh wallet.

if tx_has_any_tr_output and wallet_has_tr:
 return bech32m
if tx_has_any_wpkh and wallet_has_wpkh:
 return bech32
if wallet_has_tr:
 return bech32m
if wallet_has_wpkh:
 return bech32

Obviously this will break again once we add output script descriptor support for v2 witness programs.

Happy to implement this nonetheless if reviewers think it is useful.

if (GetScriptPubKeyMan(OutputType::BECH32M, true)) {
return OutputType::BECH32M;
} else if (GetScriptPubKeyMan(OutputType::BECH32, true)) {
return OutputType::BECH32;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't this need

else {
  return m_default_address_type;
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, and it is already in the code after the comment // else use m_default_address_type for change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see, was outside of the preview of github

@achow101
Copy link
Member

Instead of always using bech32(m), I feel like we should just do the inverse of the current implementation - prefer bech32(m) and use p2sh-segwit if there is a p2sh-segwit output.

@maflcko
Copy link
Member Author

maflcko commented Dec 15, 2021

prefer bech32(m) and use p2sh-segwit if there is a p2sh-segwit output.

I don't think it is possible to see whether a p2sh is segwit or not. While it is likely that a p2sh is segwit, I don't see a benefit to pick p2sh-segwit change over native segwit change (potentially wpkh). (See OP for explanation).

@achow101
Copy link
Member

I don't think it is possible to see whether a p2sh is segwit or not.

p2sh-segwit if there are any p2sh outputs, rather.

While it is likely that a p2sh is segwit, I don't see a benefit to pick p2sh-segwit change over native segwit change (potentially wpkh). (See OP for explanation).

I disagree that there is little or no benefit. Given that the majority of p2sh inputs are nested segwit, it is likely that when those outputs are eventually spent, they will be revealed to be segwit as well, so there is less information to be able to determine which is change. Furthermore, the change output would be less likely to be identified before the outputs are spent.

@maflcko
Copy link
Member Author

maflcko commented Dec 15, 2021

Ok, will try to propose an alternative

@maflcko maflcko closed this Dec 15, 2021
@maflcko maflcko deleted the 2112-walletChangeChange branch December 15, 2021 19:29
@maflcko
Copy link
Member Author

maflcko commented Dec 15, 2021

Done in #23789

@bitcoin bitcoin locked and limited conversation to collaborators Dec 15, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants