Skip to content

Commit be356fc

Browse files
committed
Merge bitcoin/bitcoin#32896: wallet, rpc: add v3 transaction creation and wallet support
5c8bf7b doc: add release notes for version 3 transactions (ishaanam) 4ef8065 test: add truc wallet tests (ishaanam) 5d932e1 test: extract `bulk_vout` from `bulk_tx` so it can be used by wallet tests (ishaanam) 2cb473d rpc: Support version 3 transaction creation (Bue-von-hon) 4c20343 rpc: Add transaction min standard version parameter (Bue-von-hon) c5a2d08 wallet: don't return utxos from multiple truc txs in AvailableCoins (ishaanam) da8748a wallet: limit v3 tx weight in coin selection (ishaanam) 85c5410 wallet: mark unconfirmed v3 siblings as mempool conflicts (ishaanam) 0804fc3 wallet: throw error at conflicting tx versions in pre-selected inputs (ishaanam) cc15522 wallet: set m_version in coin control to default value (ishaanam) 2e96176 wallet: don't include unconfirmed v3 txs with children in available coins (ishaanam) ec2676b wallet: unconfirmed ancestors and descendants are always truc (ishaanam) Pull request description: This PR Implements the following: - If creating a v3 transaction, `AvailableCoins` doesn't return unconfirmed v2 utxos (and vice versa) - `AvailableCoins` doesn't return an unconfirmed v3 utxo if its transaction already has a child - If a v3 transaction is kicked out of the mempool by a sibling, mark the sibling as a mempool conflict - Throw an error if pre-selected inputs are of the wrong transaction version - Allow setting version to 3 manually in `createrawtransaction` (uses commits from #31936) - Limits a v3 transaction weight in coin selection Closes #31348 To-Do: - [x] Test a v3 sibling conflict kicking out one of our transactions from the mempool - [x] Implement separate size limit for TRUC children - [x] Test that we can't fund a v2 transaction when everything is v3 unconfirmed - [x] Test a v3 sibling conflict being removed from the mempool - [x] Test limiting v3 transaction weight in coin selection - [x] Simplify tests - [x] Add documentation - [x] Test that user-input max weight is not overwritten by truc max weight - [x] Test v3 in RPCs other than `createrawtransaction` ACKs for top commit: glozow: reACK 5c8bf7b achow101: ACK 5c8bf7b rkrux: ACK 5c8bf7b Tree-SHA512: da8aea51c113e193dd0b442eff765bd6b8dc0e5066272d3e52190a223c903f48788795f32c554f268af0d2607b5b8c3985c648879cb176c65540837c05d0abb5
2 parents f58de87 + 5c8bf7b commit be356fc

25 files changed

+836
-32
lines changed

doc/release-notes-32896.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Updated RPCs
2+
------------
3+
The following RPCs now contain a `version` parameter that allows
4+
the user to create transactions of any standard version number (1-3):
5+
- `createrawtransaction`
6+
- `createpsbt`
7+
- `send`
8+
- `sendall`
9+
- `walletcreatefundedpsbt`
10+
11+
Wallet
12+
------
13+
Support has been added for spending TRUC transactions received by the
14+
wallet, as well as creating TRUC transactions. The wallet ensures that
15+
TRUC policy rules are being met. The wallet will throw an error if the
16+
user is trying to spend TRUC utxos with utxos of other versions.
17+
Additionally, the wallet will treat unconfirmed TRUC sibling
18+
transactions as mempool conflicts. The wallet will also ensure that
19+
transactions spending TRUC utxos meet the required size restrictions.

src/policy/policy.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ bool IsStandard(const CScript& scriptPubKey, TxoutType& whichType)
9898

9999
bool IsStandardTx(const CTransaction& tx, const std::optional<unsigned>& max_datacarrier_bytes, bool permit_bare_multisig, const CFeeRate& dust_relay_fee, std::string& reason)
100100
{
101-
if (tx.version > TX_MAX_STANDARD_VERSION || tx.version < 1) {
101+
if (tx.version > TX_MAX_STANDARD_VERSION || tx.version < TX_MIN_STANDARD_VERSION) {
102102
reason = "version";
103103
return false;
104104
}

src/policy/policy.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ std::vector<uint32_t> GetDust(const CTransaction& tx, CFeeRate dust_relay_rate);
145145
// Changing the default transaction version requires a two step process: first
146146
// adapting relay policy by bumping TX_MAX_STANDARD_VERSION, and then later
147147
// allowing the new transaction version in the wallet/RPC.
148+
static constexpr decltype(CTransaction::version) TX_MIN_STANDARD_VERSION{1};
148149
static constexpr decltype(CTransaction::version) TX_MAX_STANDARD_VERSION{3};
149150

150151
/**

src/policy/truc_policy.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ static constexpr unsigned int TRUC_ANCESTOR_LIMIT{2};
2828

2929
/** Maximum sigop-adjusted virtual size of all v3 transactions. */
3030
static constexpr int64_t TRUC_MAX_VSIZE{10000};
31+
static constexpr int64_t TRUC_MAX_WEIGHT{TRUC_MAX_VSIZE * WITNESS_SCALE_FACTOR};
3132
/** Maximum sigop-adjusted virtual size of a tx which spends from an unconfirmed TRUC transaction. */
3233
static constexpr int64_t TRUC_CHILD_MAX_VSIZE{1000};
34+
static constexpr int64_t TRUC_CHILD_MAX_WEIGHT{TRUC_CHILD_MAX_VSIZE * WITNESS_SCALE_FACTOR};
3335
// These limits are within the default ancestor/descendant limits.
3436
static_assert(TRUC_MAX_VSIZE + TRUC_CHILD_MAX_VSIZE <= DEFAULT_ANCESTOR_SIZE_LIMIT_KVB * 1000);
3537
static_assert(TRUC_MAX_VSIZE + TRUC_CHILD_MAX_VSIZE <= DEFAULT_DESCENDANT_SIZE_LIMIT_KVB * 1000);

src/rpc/client.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
119119
{ "createrawtransaction", 1, "outputs" },
120120
{ "createrawtransaction", 2, "locktime" },
121121
{ "createrawtransaction", 3, "replaceable" },
122+
{ "createrawtransaction", 4, "version" },
122123
{ "decoderawtransaction", 1, "iswitness" },
123124
{ "signrawtransactionwithkey", 1, "privkeys" },
124125
{ "signrawtransactionwithkey", 2, "prevtxs" },
@@ -167,6 +168,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
167168
{ "walletcreatefundedpsbt", 3, "solving_data"},
168169
{ "walletcreatefundedpsbt", 3, "max_tx_weight"},
169170
{ "walletcreatefundedpsbt", 4, "bip32derivs" },
171+
{ "walletcreatefundedpsbt", 5, "version" },
170172
{ "walletprocesspsbt", 1, "sign" },
171173
{ "walletprocesspsbt", 3, "bip32derivs" },
172174
{ "walletprocesspsbt", 4, "finalize" },
@@ -177,6 +179,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
177179
{ "createpsbt", 1, "outputs" },
178180
{ "createpsbt", 2, "locktime" },
179181
{ "createpsbt", 3, "replaceable" },
182+
{ "createpsbt", 4, "version" },
180183
{ "combinepsbt", 0, "txs"},
181184
{ "joinpsbts", 0, "txs"},
182185
{ "finalizepsbt", 1, "extract"},
@@ -213,6 +216,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
213216
{ "send", 4, "replaceable"},
214217
{ "send", 4, "solving_data"},
215218
{ "send", 4, "max_tx_weight"},
219+
{ "send", 5, "version"},
216220
{ "sendall", 0, "recipients" },
217221
{ "sendall", 1, "conf_target" },
218222
{ "sendall", 3, "fee_rate"},
@@ -230,6 +234,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
230234
{ "sendall", 4, "conf_target"},
231235
{ "sendall", 4, "replaceable"},
232236
{ "sendall", 4, "solving_data"},
237+
{ "sendall", 4, "version"},
233238
{ "simulaterawtransaction", 0, "rawtxs" },
234239
{ "simulaterawtransaction", 1, "options" },
235240
{ "simulaterawtransaction", 1, "include_watchonly"},

src/rpc/rawtransaction.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ using node::GetTransaction;
5353
using node::NodeContext;
5454
using node::PSBTAnalysis;
5555

56+
static constexpr decltype(CTransaction::version) DEFAULT_RAWTX_VERSION{CTransaction::CURRENT_VERSION};
57+
5658
static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry,
5759
Chainstate& active_chainstate, const CTxUndo* txundo = nullptr,
5860
TxVerbosity verbosity = TxVerbosity::SHOW_DETAILS)
@@ -158,6 +160,7 @@ static std::vector<RPCArg> CreateTxDoc()
158160
{"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"},
159161
{"replaceable", RPCArg::Type::BOOL, RPCArg::Default{true}, "Marks this transaction as BIP125-replaceable.\n"
160162
"Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible."},
163+
{"version", RPCArg::Type::NUM, RPCArg::Default{DEFAULT_RAWTX_VERSION}, "Transaction version"},
161164
};
162165
}
163166

@@ -437,7 +440,7 @@ static RPCHelpMan createrawtransaction()
437440
if (!request.params[3].isNull()) {
438441
rbf = request.params[3].get_bool();
439442
}
440-
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
443+
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf, self.Arg<uint32_t>("version"));
441444

442445
return EncodeHexTx(CTransaction(rawTx));
443446
},
@@ -1679,7 +1682,7 @@ static RPCHelpMan createpsbt()
16791682
if (!request.params[3].isNull()) {
16801683
rbf = request.params[3].get_bool();
16811684
}
1682-
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
1685+
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf, self.Arg<uint32_t>("version"));
16831686

16841687
// Make a blank psbt
16851688
PartiallySignedTransaction psbtx;

src/rpc/rawtransaction_util.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <tinyformat.h>
1919
#include <univalue.h>
2020
#include <util/rbf.h>
21+
#include <util/string.h>
2122
#include <util/strencodings.h>
2223
#include <util/translation.h>
2324

@@ -143,7 +144,7 @@ void AddOutputs(CMutableTransaction& rawTx, const UniValue& outputs_in)
143144
}
144145
}
145146

146-
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, std::optional<bool> rbf)
147+
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, std::optional<bool> rbf, const uint32_t version)
147148
{
148149
CMutableTransaction rawTx;
149150

@@ -154,6 +155,11 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
154155
rawTx.nLockTime = nLockTime;
155156
}
156157

158+
if (version < TX_MIN_STANDARD_VERSION || version > TX_MAX_STANDARD_VERSION) {
159+
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, version out of range(%d~%d)", TX_MIN_STANDARD_VERSION, TX_MAX_STANDARD_VERSION));
160+
}
161+
rawTx.version = version;
162+
157163
AddInputs(rawTx, inputs_in, rbf);
158164
AddOutputs(rawTx, outputs_in);
159165

src/rpc/rawtransaction_util.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,6 @@ std::vector<std::pair<CTxDestination, CAmount>> ParseOutputs(const UniValue& out
5353
void AddOutputs(CMutableTransaction& rawTx, const UniValue& outputs_in);
5454

5555
/** Create a transaction from univalue parameters */
56-
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, std::optional<bool> rbf);
56+
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, std::optional<bool> rbf, const uint32_t version);
5757

5858
#endif // BITCOIN_RPC_RAWTRANSACTION_UTIL_H

src/rpc/util.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,7 @@ TMPL_INST(CheckRequiredOrDefault, const UniValue&, *CHECK_NONFATAL(maybe_arg););
731731
TMPL_INST(CheckRequiredOrDefault, bool, CHECK_NONFATAL(maybe_arg)->get_bool(););
732732
TMPL_INST(CheckRequiredOrDefault, int, CHECK_NONFATAL(maybe_arg)->getInt<int>(););
733733
TMPL_INST(CheckRequiredOrDefault, uint64_t, CHECK_NONFATAL(maybe_arg)->getInt<uint64_t>(););
734+
TMPL_INST(CheckRequiredOrDefault, uint32_t, CHECK_NONFATAL(maybe_arg)->getInt<uint32_t>(););
734735
TMPL_INST(CheckRequiredOrDefault, const std::string&, CHECK_NONFATAL(maybe_arg)->get_str(););
735736

736737
bool RPCHelpMan::IsValidNumArgs(size_t num_args) const

src/wallet/coincontrol.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ namespace wallet {
2121
const int DEFAULT_MIN_DEPTH = 0;
2222
const int DEFAULT_MAX_DEPTH = 9999999;
2323

24+
const int DEFAULT_WALLET_TX_VERSION = CTransaction::CURRENT_VERSION;
25+
2426
//! Default for -avoidpartialspends
2527
static constexpr bool DEFAULT_AVOIDPARTIALSPENDS = false;
2628

@@ -109,10 +111,10 @@ class CCoinControl
109111
int m_max_depth = DEFAULT_MAX_DEPTH;
110112
//! SigningProvider that has pubkeys and scripts to do spend size estimation for external inputs
111113
FlatSigningProvider m_external_provider;
114+
//! Version
115+
uint32_t m_version = DEFAULT_WALLET_TX_VERSION;
112116
//! Locktime
113117
std::optional<uint32_t> m_locktime;
114-
//! Version
115-
std::optional<uint32_t> m_version;
116118
//! Caps weight of resulting tx
117119
std::optional<int> m_max_tx_weight{std::nullopt};
118120

0 commit comments

Comments
 (0)