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
Allow fundrawtransaction and walletcreatefundedpsbt to take external inputs #17211
Conversation
6f632fb
to
fe1c549
Compare
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. 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. |
5deea6c
to
9e10531
Compare
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 needs tests for the scripts, descriptors options in RPC.
Also a simple check that wrong pubkeys don't also cause things to seem complete.
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.
RPC help also needs to be updated to reflect the new functionality. For example: https://github.com/bitcoin/bitcoin/pull/17211/files#diff-df7d84ff2f53fcb2a0dc15a3a51e55ceR3167
7af1fcc
to
c63d41d
Compare
Updated the help |
c63d41d
to
e90de8e
Compare
can you also reflect the updated help into walletcreatefundedpsbt verbatim? It's helpful for usage. |
e96d6e5
to
a7e26d4
Compare
Also updated the help of |
not compiling |
a7e26d4
to
7f9aef6
Compare
code review ACK 7f9aef6 |
src/wallet/rpcwallet.cpp
Outdated
@@ -3157,6 +3206,25 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request) | |||
"This boolean should reflect whether the transaction has inputs\n" | |||
"(e.g. fully valid, or on-chain transactions), if known by the caller." | |||
}, | |||
{"solving_data", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "Keys and scripts needed for producing a final transaction with a dummy signature. Used for fee estimation during coin selection.\n", |
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 seems like an advanced feature, so maybe move to "options"?
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 don't think this really belongs as part of options. It's not like a setting, just additional data needed.
src/wallet/wallet.cpp
Outdated
@@ -1332,14 +1332,12 @@ int64_t CWalletTx::GetTxTime() const | |||
|
|||
// Helper for producing a max-sized low-S low-R signature (eg 71 bytes) | |||
// or a max-sized low-S signature (e.g. 72 bytes) if use_max_sig is true | |||
bool CWallet::DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig) const | |||
static bool DummySignInput(const SigningProvider* provider, CTxIn &tx_in, const CTxOut &txout, bool use_max_sig) |
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.
Why not a reference instead of a pointer?
Is there any reason not to move this to SigningProvider
as a method?
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 changed this to a reference.
I don't think this really makes sense as part of SigningProvider. SigningProvider is to provide data for signing and is agnostic of what is being signed and how it is being signed. To add this to SigningProvider would violate that. Also, that would imply adding every other signing function to SigningProvider as well, and I don't think we should do that.
7f9aef6
to
1b189f8
Compare
1b189f8
to
0073107
Compare
0073107
to
fd6ce28
Compare
Done |
0b6b098
to
928af61
Compare
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.
Re-utACK 928af61
Only changes are those suggested above.
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.
crACK 928af61
|
||
CInputCoin coin(outpoint, txout, input_bytes); | ||
nValueFromPresetInputs += coin.txout.nValue; | ||
if (coin.m_input_bytes <= 0) { |
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: we should probably keep error checking conditions the same, input_bytes == -1
as per above
void Select(const COutPoint& output) | ||
{ | ||
setSelected.insert(output); | ||
} | ||
|
||
void Select(const COutPoint& outpoint, const CTxOut& txout) |
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.
Would rather it be named the more obvious SelectExternal
for grepping
reACK 928af61 nit: agree with #17211 (comment) |
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.
utACK 928af61.
What's crACK btw? (#17211 (review))
@@ -3289,6 +3291,54 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out, | |||
coinControl.fAllowWatchOnly = ParseIncludeWatchonly(NullUniValue, wallet); | |||
} | |||
|
|||
if (options.exists("solving_data")) { | |||
UniValue solving_data = options["solving_data"].get_obj(); |
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.
Any reason for this not to be const
?
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("'%s' is not hex", pk_str)); | ||
} | ||
const std::vector<unsigned char> data(ParseHex(pk_str)); | ||
CPubKey pubkey(data.begin(), data.end()); |
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.
Ditto.
@@ -788,10 +811,10 @@ static bool CreateTransactionInternal( | |||
} | |||
|
|||
// Calculate the transaction fee | |||
TxSize tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, coin_control.fAllowWatchOnly); | |||
TxSize tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, &coin_control); | |||
int nBytes = tx_sizes.vsize; | |||
if (nBytes < 0) { |
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: Shouldn't that likewise be -1
, then?
raw_tx = wallet.createrawtransaction([ext_utxo], {self.nodes[0].getnewaddress(): 15}) | ||
assert_raises_rpc_error(-4, "Insufficient funds", wallet.fundrawtransaction, raw_tx) | ||
|
||
# Error conditions |
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 think you forgot "TX must have at least one output"?
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.
doesn't seem related to the feature?
Remaining review comments are non-blocking and can be left for a followup. |
Thank you for merging this!! |
Thanks for the quick reviews and integration guys! I'm trying to call Also, for my understanding, how does the size evaluation work at a high-level for p2wsh inputs? |
@t-bast This feature currently only works for scripts that Bitcoin Core would be able to sign too, so you are limited to the set of standard scripts. You also need to provide pubkeys. We would need to be able to understand arbitrary scripts in order for this to work with any script. In order for that to happen, I think we will need Miniscript to be implemented. I suppose an alternative would be to allow the user to supply their own input size? |
Damn, ok, thanks for the clarification, that makes sense.
That would be perfect for apps that rely on more complex scripts (LN / vaults). |
assert_raises_rpc_error(-8, "Unable to parse descriptor 'not a descriptor'", wallet.fundrawtransaction, raw_tx, {"solving_data": {"descriptors":["not a descriptor"]}}) | ||
|
||
# But funding should work when the solving data is provided | ||
funded_tx = wallet.fundrawtransaction(raw_tx, {"solving_data": {"pubkeys": [addr_info['pubkey']], "scripts": [addr_info["embedded"]["scriptPubKey"]]}}) |
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 sometimes fails with "insufficient funds". I wonder if the following diff fixes it:
diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py
index f1215915a6..e92d5c8886 100755
--- a/test/functional/rpc_fundrawtransaction.py
+++ b/test/functional/rpc_fundrawtransaction.py
@@ -1009,13 +1009,13 @@ class RawTransactionsTest(BitcoinTestFramework):
addr = self.nodes[0].deriveaddresses(desc)[0]
addr_info = self.nodes[0].getaddressinfo(addr)
- self.nodes[0].sendtoaddress(addr, 10)
- self.nodes[0].sendtoaddress(wallet.getnewaddress(), 10)
+ self.nodes[0].sendtoaddress(addr, .10)
+ self.nodes[0].sendtoaddress(wallet.getnewaddress(), .10)
self.nodes[0].generate(6)
ext_utxo = self.nodes[0].listunspent(addresses=[addr])[0]
# An external input without solving data should result in an error
- raw_tx = wallet.createrawtransaction([ext_utxo], {self.nodes[0].getnewaddress(): 15})
+ raw_tx = wallet.createrawtransaction([ext_utxo], {self.nodes[0].getnewaddress(): .15})
assert_raises_rpc_error(-4, "Insufficient funds", wallet.fundrawtransaction, raw_tx)
# Error conditions
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.
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.
@MarcoFalke if I had to guess, that would fail due to paying extremely high fee?
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.
fundrawtransaction adjusts the feerate by adjusting the change output
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'm seeing this new https://cirrus-ci.com/task/5958524342632448?logs=functional_tests#L2395
|
…rawtransaction.py 75a9305 Fix intermittent test failures due to missing sync_all (Samuel Dobson) eb02dbb Use self.generate not node.generate throughout tests (Samuel Dobson) Pull request description: After #17211 was merged, there have been a few intermittent test failures in `wallet_send.py`, `rpc_fundrawtransaction.py`, and `rpc_psbt.py` I believe all these failures are due to these missing `sync_all()`s ACKs for top commit: achow101: ACK 75a9305 lsilva01: Tested ACK 75a9305 on Ubuntu 20.04 Tree-SHA512: 91de5763664046e5a35802eb1a9a28f69a1a27d78d26c9fa0024bcfab4ccb4b7ad4feebea7de4b19e141db9c39270c623c57c47942a48dfe679fdc8cad70df43
…pc_fundrawtransaction.py 75a9305 Fix intermittent test failures due to missing sync_all (Samuel Dobson) eb02dbb Use self.generate not node.generate throughout tests (Samuel Dobson) Pull request description: After bitcoin#17211 was merged, there have been a few intermittent test failures in `wallet_send.py`, `rpc_fundrawtransaction.py`, and `rpc_psbt.py` I believe all these failures are due to these missing `sync_all()`s ACKs for top commit: achow101: ACK 75a9305 lsilva01: Tested ACK bitcoin@75a9305 on Ubuntu 20.04 Tree-SHA512: 91de5763664046e5a35802eb1a9a28f69a1a27d78d26c9fa0024bcfab4ccb4b7ad4feebea7de4b19e141db9c39270c623c57c47942a48dfe679fdc8cad70df43
…cleanups 4356878 External input fund support cleanups (Gregory Sanders) Pull request description: Minor cleanups to bitcoin/bitcoin#17211 ACKs for top commit: achow101: ACK 4356878 meshcollider: utACK 4356878 benthecarman: ACK 4356878 Tree-SHA512: 865f8a3804f8c0027f5393a0539041158166a919378f2c3bc99b936843eee2329372bcc2af888fa62babfa5f6baf4f13d4cfef7b4e26a7265a82a908f9719ad6
Upstream bitcoin/bitcoin#17211 solved a long-standing issue for doing atomic trades with names (namely that the buyer's wallet was not able to directly fund a transaction with the non-wallet name input in it). Based on this, we can now streamline name_ant_workflow.py to make use of this new feature. This allows us to properly use fundrawtransaction to fund the final transaction including already the name input and output, and thus with correct fee estimation done. (Previously we just funded a dummy transaction with a guessed higher fee, and then added in the name input and output manually, hoping the fee would still be fine.)
Upstream bitcoin/bitcoin#17211 solved a long-standing issue for doing atomic trades with names (namely that the buyer's wallet was not able to directly fund a transaction with the non-wallet name input in it). Based on this, we can now streamline name_ant_workflow.py to make use of this new feature. This allows us to properly use fundrawtransaction to fund the final transaction including already the name input and output, and thus with correct fee estimation done. (Previously we just funded a dummy transaction with a guessed higher fee, and then added in the name input and output manually, hoping the fee would still be fine.)
fa1eef3 Update name_ant_workflow.py. (Daniel Kraft) Pull request description: Upstream bitcoin/bitcoin#17211 solved a long-standing issue for doing atomic trades with names (namely that the buyer's wallet was not able to directly fund a transaction with the non-wallet name input in it). Based on this, we can now streamline `name_ant_workflow.py` to make use of this new feature. This allows us to properly use `fundrawtransaction` to fund the final transaction including already the name input and output, and thus with correct fee estimation done. (Previously we just funded a dummy transaction with a guessed higher fee, and then added in the name input and output manually, hoping the fee would still be fine.) Top commit has no ACKs. Tree-SHA512: 0cb0eb4796bcc5a0baa3187258867b90f944548e6249110f1da03ddbde1f0ef3c06a0b4d323916d1b04753b3cd5c7823d8d9ed7fe9f2cbf762a72c091ca93817
Currently
fundrawtransaction
andwalletcreatefundedpsbt
both do not allow external inputs as the wallet does not have the information necessary to estimate their fees.This PR adds an additional argument to both those RPCs which allows the user to specify solving data. This way, the wallet can use that solving data to estimate the size of those inputs. The solving data can be public keys, scripts, or descriptors.