-
Notifications
You must be signed in to change notification settings - Fork 35.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
wallet: Use CTxDestination in CRecipient instead of just scriptPubKey #28246
Conversation
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update. ConflictsReviewers, this pull request conflicts with the following ones:
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. |
12419d6
to
46ffe75
Compare
|
||
public: | ||
CNoDestination() = default; | ||
CNoDestination(const CScript& script) : m_script(script) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be explicit
? Otherwise passing a single CScript
in the "wrong" context could decay to CNoDestiantion
by accident?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #28728
public: | ||
friend bool operator==(const CNoDestination &a, const CNoDestination &b) { return true; } | ||
friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; } | ||
PubKeyDestination(const CPubKey& pubkey) : m_pubkey(pubkey) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it is fine to let this be implicit. There should be no risk.
CNoDestination() = default; | ||
CNoDestination(const CScript& script) : m_script(script) {} | ||
|
||
const CScript& GetScript() const { return m_script; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Could add LIFETIMEBOUND, if you re-touch?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #28728
Upstream bitcoin/bitcoin#28246 changed CRecipient to use a CTxDestination instead of a raw script; it supports raw scripts with CNoDestination as a fallbackup. This is how it currently continues to handle name scripts.
… of just scriptPubKey
Upstream changed CRecipient to hold a CTxDestination for the "recipient address" instead of a raw script, see bitcoin/bitcoin#28246. While this continues to "just work" for name operations since it still supports arbitrary scripts, this is of course not fully in line with the upstream refactor. In this commit, we change CRecipient to hold a name operation, if any, in a separate field that just contains the name script. The recipient address is kept in the CTxDestination. With this, name operations will more transparently blend in with the upstream code, and will more directly benefit from future things like silent payments.
Found another one post-merge (while testing things on master), although only when you Log output: https://gist.github.com/Sjors/22873167e460eb1265652e5dca973db2
|
That sounds like a bug in gcc, no? |
@MarcoFalke all I know is that |
Have you tried using 13.2 from the same distro? https://packages.ubuntu.com/mantic/g++ |
addressRet = CNoDestination(scriptPubKey); | ||
} else { | ||
addressRet = PubKeyDestination(pubKey); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change looks incorrect. The PUBKEY code path now always returns "false". Is this intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. See the first comment in this PR:
ExtractDestination
's behavior is slightly changed for the above. It now returnstrue
for those destinations that have addresses, so P2PK scripts now result infalse
. Even though it returns false forCNoDestination
, the script will now be included in thatCNoDestination
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But why is P2PK treated differently now?
Previously when the function returned "false", it meant the result is CNoDestination, but now that's ambiguous and could also be PubKeyDestination, and thus requires further checks.
This kind of makes the return value not very useful, IMHO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, looks like OutputTypeFromDestination needs to be updated to support PubKeyDestination?
I'm hitting an issue in my PR, with some wallet tests that use addresses from mined blocks (GetScriptForRawPubKey(coinbaseKey.GetPubKey())), and OutputTypeFromDestination is returning nullopt now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previously when the function returned "false", it meant the result is CNoDestination, but now that's ambiguous and could also be PubKeyDestination, and thus requires further checks.
This kind of makes the return value not very useful, IMHO.
As noted in the change to the documentation for this function, ExtractDestination
only returns true for things that have addresses. P2PK does not have an address. This is useful in other parts of the codebase and in future work such as silent payments. If you want to check if it is a CNoDestination
, then you can do that directly. However, it should generally be okay to not need to check for that. For the most part, checking for CNoDestination
is not all that useful and there's probably a better way to achieve what you want to do.
Hmm, looks like OutputTypeFromDestination needs to be updated to support PubKeyDestination?
I'm hitting an issue in my PR, with some wallet tests that use addresses from mined blocks (GetScriptForRawPubKey(coinbaseKey.GetPubKey())), and OutputTypeFromDestination is returning nullopt now.
No, it does not. OutputType
is also a synonym for address type in the context of giving an address to the user. P2PK does not have an address type and users cannot get an address for them. If you are using things that relied on the incorrect behavior of getting a PKHash
for a P2PK script, then I suggest that you don't do that. P2PK is not PKHash
and you shouldn't expect it to be giving you those scripts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I don't think I understand why it's considered to have no address ...
In the wallet test, I'm using GetScriptForRawPubKey(coinbaseKey.GetPubKey()) to mine a block (via the MineBlock function), and then create a wallet with that coinbase key and issue transactions with coins returned from AvailableCoins.
With the new code path, it seems the script coming from AvailableCoins gets classified as PubKeyDestination, which fails to get an OutputTypeFromDestination, however before this change, this code worked and generated successful transactions.
Is there another way to mine blocks and get non-P2PK script from the coinbase key?
For context, the code that used to work but is now failing is in the function CreateDeniabilizationTransaction, in spend.cpp in my PR (https://github.com/bitcoin/bitcoin/pull/27792/files#)
// validate that all UTXOs share the same address
std::optional<CTxDestination> op_shared_destination;
for (const auto& coin : preset_inputs.coins) {
CTxDestination destination = CNoDestination();
ExtractDestination(coin->txout.scriptPubKey, destination);
if (!std::get_if<CNoDestination>(&destination) && !op_shared_destination) {
op_shared_destination = destination;
}
if (!op_shared_destination || !(*op_shared_destination == destination)) {
return util::Error{_("Input addresses must all match.")};
}
}
And further down in the same function (with the workaround I just added to pass CI):
std::optional<OutputType> op_output_type;
if (std::get_if<PubKeyDestination>(&shared_destination)) {
op_output_type = OutputType::LEGACY;
} else {
op_output_type = OutputTypeFromDestination(shared_destination);
}
if (!op_output_type) {
op_output_type = wallet.m_default_change_type;
}
if (!op_output_type) {
return util::Error{_("Unable to determine output type.")};
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I don't think I understand why it's considered to have no address
They literally do not have addresses. There is no address type which maps to a P2PK script. That this and other software sometimes display an address for P2PK scripts is simply wrong. They are displaying P2PK addresses. If you send to those addresses, a P2PK script is not created.
In the wallet test, I'm using GetScriptForRawPubKey(coinbaseKey.GetPubKey()) to mine a block (via the MineBlock function), and then create a wallet with that coinbase key and issue transactions with coins returned from AvailableCoins.
With the new code path, it seems the address coming from AvailableCoins gets classified as PubKeyDestination, which fails to get an OutputTypeFromDestination, however before this change, this code worked and generated successful transactions.
AvailableCoins
does not operate on destinations, it operates on scripts. This is way more precise since there are more standard script types than there are standard addresses (e.g. P2PK, bare multisig). If you have modified it to operate on destinations, don't do that.
Please take this discussion to your PR as it would be more helpful to see what you've done rather than to continue discussing code proposed in another PR in this thread. Just leave a review comment on the relevant line in your PR and mention me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! It's clear I didn't have a good understanding of the relationship between scripts and addresses. I'll move the discussion to my PR.
src/addresstype.h
Outdated
@@ -115,8 +125,8 @@ bool IsValidDestination(const CTxDestination& dest); | |||
* is assigned to addressRet. | |||
* For all other scripts. addressRet is assigned as a CNoDestination containing the scriptPubKey. | |||
* | |||
* Returns true for standard destinations - P2PK, P2PKH, P2SH, P2WPKH, P2WSH, and P2TR scripts. | |||
* Returns false for non-standard destinations - P2PK with invalid pubkeys, P2W???, bare multisig, null data, and nonstandard scripts. | |||
* Returns true for standard destinations - P2PKH, P2SH, P2WPKH, P2WSH, and P2TR scripts. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From what I understand now, this refers to output scripts with address standards, but when I first saw it, I thought it was referring to outputs permitted in standard transactions.
* Returns true for standard destinations - P2PKH, P2SH, P2WPKH, P2WSH, and P2TR scripts. | |
* Returns true for destinations with address standards - P2PKH, P2SH, P2WPKH, P2WSH, and P2TR scripts. |
addressRet = CNoDestination(scriptPubKey); | ||
} else { | ||
addressRet = PubKeyDestination(pubKey); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. See the first comment in this PR:
ExtractDestination
's behavior is slightly changed for the above. It now returnstrue
for those destinations that have addresses, so P2PK scripts now result infalse
. Even though it returns false forCNoDestination
, the script will now be included in thatCNoDestination
.
@maflcko wrote:
That did the trick. Although for some reason it picks |
any_sh = true; | ||
} else if (type == TxoutType::PUBKEYHASH) { | ||
} else if (std::get_if<PKHash>(&recipient.dest)) { | ||
any_pkh = true; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the code, and the missing explicit
keyword, I presume this broke this feature in the GUI?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Confirmed locally that this breaks the feature for the gui. Can be fixed on current master by calling:
git revert ad0c469d98c51931b98b7fd937c6ac3eeaed024e
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the code, and the missing
explicit
keyword, I presume this broke this feature in the GUI?
Great catch. Can fix it with #28728 (comment).
This should fix the bug reported in bitcoin#28246 (comment), which caused the GUI to not detect the destination type of recipients, thus picking the wrong change destiantion type. Also, add missing lifetimebound attribute to a getter method.
This should fix the bug reported in bitcoin#28246 (comment), which caused the GUI to not detect the destination type of recipients, thus picking the wrong change destination type. Also, add missing lifetimebound attribute to a getter method.
This should fix the bug reported in bitcoin#28246 (comment), which caused the GUI to not detect the destination type of recipients, thus picking the wrong change destination type. Also, add missing lifetimebound attribute to a getter method. GitHub-Pull: bitcoin#28728 Rebased-From: 1111475
This should fix the bug reported in bitcoin#28246 (comment), which caused the GUI to not detect the destination type of recipients, thus picking the wrong change destination type. Also, add missing lifetimebound attribute to a getter method. GitHub-Pull: bitcoin#28728 Rebased-From: 1111475
This should fix the bug reported in bitcoin/bitcoin#28246 (comment), which caused the GUI to not detect the destination type of recipients, thus picking the wrong change destination type. Also, add missing lifetimebound attribute to a getter method.
For silent payments, we want to provide a
SilentPaymentsDestination
to be used as the recipient, which requiresCRecipient
to use something other than just thescriptPubKey
as we cannot know the output script for a silent payment prior to transaction creation.CTxDestination
seems like the obvious place to add aSilentPaymentsDestination
as it is our internal representation of an address.In order to still allow paying to arbitrary scriptPubKeys (e.g. for data carrier outputs, or the user hand crafted a raw transaction that they have given to
fundrawtransaction
),CNoDestination
is changed to contain raw scripts.Additionally, P2PK scripts are now interpreted as a new
PubKeyDestination
rather thanPKHash
. This results in some things that would have given an address for P2PK scripts to no longer do so. This is arguably more correct.ExtractDestination
's behavior is slightly changed for the above. It now returnstrue
for those destinations that have addresses, so P2PK scripts now result infalse
. Even though it returns false forCNoDestination
, the script will now be included in thatCNoDestination
.Builds on #28244