Skip to content

Commit 3325a0a

Browse files
committed
Merge bitcoin/bitcoin#30272: doc: use TRUC instead of v3 and add release note
926b8e3 [doc] add release note for TRUC (glozow) 19a9b90 use version=3 instead of v3 in debug strings (glozow) 881fac8 scripted-diff: change names from V3 to TRUC (glozow) a573dd2 [doc] replace mentions of v3 with TRUC (glozow) 089b575 rename mempool_accept_v3.py to mempool_truc.py (glozow) f543852 rename policy/v3_policy.* to policy/truc_policy.* (glozow) Pull request description: Adds a release note for TRUC policy which will be live in v28.0. For clarity, replaces mentions of "v3" with "TRUC" in most places. Suggested in - bitcoin/bitcoin#29496 (comment) - bitcoin/bitcoin#29496 (comment) I changed error strings from "v3-violation" to "TRUC-violation" but left v3 in the debug strings because I think it might be clearer for somebody who is debugging. Similarly, I left some variables unchanged because I think they're more descriptive this way, e.g. `tx_v3_from_v2_and_v3`. I'm happy to debate places that should or shouldn't be documented differently in this PR, whatever is clearest to everyone. ACKs for top commit: instagibbs: reACK bitcoin/bitcoin@926b8e3 achow101: ACK 926b8e3 ismaelsadeeq: Code review ACK 926b8e3 Tree-SHA512: 16c88add0a29dc6d1236c4d45f34a17b850f6727b231953cbd52eb9f7268d1d802563eadfc8b7928c94ed3d7a615275dd103e57e81439ebf3ba2b12efa1e42af
2 parents 9251bc7 + 926b8e3 commit 3325a0a

14 files changed

+314
-302
lines changed

doc/release-notes-29496.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Mempool Policy Changes
2+
----------------------
3+
4+
- Transactions with version number set to 3 are now treated as standard on all networks (#29496),
5+
subject to Opt-in Topologically Restricted Until Confirmation (TRUC) Transactions policy as
6+
described in [BIP 431](https://github.com/bitcoin/bips/blob/master/bip-0431.mediawiki). The
7+
policy includes limits on spending unconfirmed outputs (#28948), eviction of a previous descendant
8+
if a more incentive-compatible one is submitted (#29306), and a maximum transaction size of 10,000vB
9+
(#29873). These restrictions simplify the assessment of incentive compatibility of accepting or
10+
replacing TRUC transactions, thus ensuring any replacements are more profitable for the node and
11+
making fee-bumping more reliable.

src/Makefile.am

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -244,14 +244,14 @@ BITCOIN_CORE_H = \
244244
node/warnings.h \
245245
noui.h \
246246
outputtype.h \
247-
policy/v3_policy.h \
248247
policy/feerate.h \
249248
policy/fees.h \
250249
policy/fees_args.h \
251250
policy/packages.h \
252251
policy/policy.h \
253252
policy/rbf.h \
254253
policy/settings.h \
254+
policy/truc_policy.h \
255255
pow.h \
256256
protocol.h \
257257
psbt.h \
@@ -448,12 +448,12 @@ libbitcoin_node_a_SOURCES = \
448448
node/validation_cache_args.cpp \
449449
node/warnings.cpp \
450450
noui.cpp \
451-
policy/v3_policy.cpp \
452451
policy/fees.cpp \
453452
policy/fees_args.cpp \
454453
policy/packages.cpp \
455454
policy/rbf.cpp \
456455
policy/settings.cpp \
456+
policy/truc_policy.cpp \
457457
pow.cpp \
458458
rest.cpp \
459459
rpc/blockchain.cpp \
@@ -708,9 +708,9 @@ libbitcoin_common_a_SOURCES = \
708708
netbase.cpp \
709709
net_permissions.cpp \
710710
outputtype.cpp \
711-
policy/v3_policy.cpp \
712711
policy/feerate.cpp \
713712
policy/policy.cpp \
713+
policy/truc_policy.cpp \
714714
protocol.cpp \
715715
psbt.cpp \
716716
rpc/external_signer.cpp \
@@ -955,12 +955,12 @@ libbitcoinkernel_la_SOURCES = \
955955
node/blockstorage.cpp \
956956
node/chainstate.cpp \
957957
node/utxo_snapshot.cpp \
958-
policy/v3_policy.cpp \
959958
policy/feerate.cpp \
960959
policy/packages.cpp \
961960
policy/policy.cpp \
962961
policy/rbf.cpp \
963962
policy/settings.cpp \
963+
policy/truc_policy.cpp \
964964
pow.cpp \
965965
primitives/block.cpp \
966966
primitives/transaction.cpp \

src/policy/v3_policy.cpp renamed to src/policy/truc_policy.cpp

Lines changed: 42 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Distributed under the MIT software license, see the accompanying
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

5-
#include <policy/v3_policy.h>
5+
#include <policy/truc_policy.h>
66

77
#include <coins.h>
88
#include <consensus/amount.h>
@@ -14,7 +14,7 @@
1414
#include <numeric>
1515
#include <vector>
1616

17-
/** Helper for PackageV3Checks: Returns a vector containing the indices of transactions (within
17+
/** Helper for PackageTRUCChecks: Returns a vector containing the indices of transactions (within
1818
* package) that are direct parents of ptx. */
1919
std::vector<size_t> FindInPackageParents(const Package& package, const CTransactionRef& ptx)
2020
{
@@ -37,13 +37,13 @@ std::vector<size_t> FindInPackageParents(const Package& package, const CTransact
3737
return in_package_parents;
3838
}
3939

40-
/** Helper for PackageV3Checks, storing info for a mempool or package parent. */
40+
/** Helper for PackageTRUCChecks, storing info for a mempool or package parent. */
4141
struct ParentInfo {
4242
/** Txid used to identify this parent by prevout */
4343
const Txid& m_txid;
4444
/** Wtxid used for debug string */
4545
const Wtxid& m_wtxid;
46-
/** version used to check inheritance of v3 and non-v3 */
46+
/** version used to check inheritance of TRUC and non-TRUC */
4747
decltype(CTransaction::version) m_version;
4848
/** If parent is in mempool, whether it has any descendants in mempool. */
4949
bool m_has_mempool_descendant;
@@ -55,36 +55,36 @@ struct ParentInfo {
5555
{}
5656
};
5757

58-
std::optional<std::string> PackageV3Checks(const CTransactionRef& ptx, int64_t vsize,
58+
std::optional<std::string> PackageTRUCChecks(const CTransactionRef& ptx, int64_t vsize,
5959
const Package& package,
6060
const CTxMemPool::setEntries& mempool_ancestors)
6161
{
6262
// This function is specialized for these limits, and must be reimplemented if they ever change.
63-
static_assert(V3_ANCESTOR_LIMIT == 2);
64-
static_assert(V3_DESCENDANT_LIMIT == 2);
63+
static_assert(TRUC_ANCESTOR_LIMIT == 2);
64+
static_assert(TRUC_DESCENDANT_LIMIT == 2);
6565

6666
const auto in_package_parents{FindInPackageParents(package, ptx)};
6767

68-
// Now we have all ancestors, so we can start checking v3 rules.
68+
// Now we have all ancestors, so we can start checking TRUC rules.
6969
if (ptx->version == TRUC_VERSION) {
70-
// SingleV3Checks should have checked this already.
71-
if (!Assume(vsize <= V3_MAX_VSIZE)) {
72-
return strprintf("v3 tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
73-
ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), vsize, V3_MAX_VSIZE);
70+
// SingleTRUCChecks should have checked this already.
71+
if (!Assume(vsize <= TRUC_MAX_VSIZE)) {
72+
return strprintf("version=3 tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
73+
ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), vsize, TRUC_MAX_VSIZE);
7474
}
7575

76-
if (mempool_ancestors.size() + in_package_parents.size() + 1 > V3_ANCESTOR_LIMIT) {
76+
if (mempool_ancestors.size() + in_package_parents.size() + 1 > TRUC_ANCESTOR_LIMIT) {
7777
return strprintf("tx %s (wtxid=%s) would have too many ancestors",
7878
ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString());
7979
}
8080

8181
const bool has_parent{mempool_ancestors.size() + in_package_parents.size() > 0};
8282
if (has_parent) {
83-
// A v3 child cannot be too large.
84-
if (vsize > V3_CHILD_MAX_VSIZE) {
85-
return strprintf("v3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
83+
// A TRUC child cannot be too large.
84+
if (vsize > TRUC_CHILD_MAX_VSIZE) {
85+
return strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
8686
ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),
87-
vsize, V3_CHILD_MAX_VSIZE);
87+
vsize, TRUC_CHILD_MAX_VSIZE);
8888
}
8989

9090
// Exactly 1 parent exists, either in mempool or package. Find it.
@@ -107,7 +107,7 @@ std::optional<std::string> PackageV3Checks(const CTransactionRef& ptx, int64_t v
107107

108108
// If there is a parent, it must have the right version.
109109
if (parent_info.m_version != TRUC_VERSION) {
110-
return strprintf("v3 tx %s (wtxid=%s) cannot spend from non-v3 tx %s (wtxid=%s)",
110+
return strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)",
111111
ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),
112112
parent_info.m_txid.ToString(), parent_info.m_wtxid.ToString());
113113
}
@@ -118,7 +118,7 @@ std::optional<std::string> PackageV3Checks(const CTransactionRef& ptx, int64_t v
118118

119119
for (auto& input : package_tx->vin) {
120120
// Fail if we find another tx with the same parent. We don't check whether the
121-
// sibling is to-be-replaced (done in SingleV3Checks) because these transactions
121+
// sibling is to-be-replaced (done in SingleTRUCChecks) because these transactions
122122
// are within the same package.
123123
if (input.prevout.hash == parent_info.m_txid) {
124124
return strprintf("tx %s (wtxid=%s) would exceed descendant count limit",
@@ -140,17 +140,17 @@ std::optional<std::string> PackageV3Checks(const CTransactionRef& ptx, int64_t v
140140
}
141141
}
142142
} else {
143-
// Non-v3 transactions cannot have v3 parents.
143+
// Non-TRUC transactions cannot have TRUC parents.
144144
for (auto it : mempool_ancestors) {
145145
if (it->GetTx().version == TRUC_VERSION) {
146-
return strprintf("non-v3 tx %s (wtxid=%s) cannot spend from v3 tx %s (wtxid=%s)",
146+
return strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",
147147
ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),
148148
it->GetSharedTx()->GetHash().ToString(), it->GetSharedTx()->GetWitnessHash().ToString());
149149
}
150150
}
151151
for (const auto& index: in_package_parents) {
152152
if (package.at(index)->version == TRUC_VERSION) {
153-
return strprintf("non-v3 tx %s (wtxid=%s) cannot spend from v3 tx %s (wtxid=%s)",
153+
return strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",
154154
ptx->GetHash().ToString(),
155155
ptx->GetWitnessHash().ToString(),
156156
package.at(index)->GetHash().ToString(),
@@ -161,52 +161,52 @@ std::optional<std::string> PackageV3Checks(const CTransactionRef& ptx, int64_t v
161161
return std::nullopt;
162162
}
163163

164-
std::optional<std::pair<std::string, CTransactionRef>> SingleV3Checks(const CTransactionRef& ptx,
164+
std::optional<std::pair<std::string, CTransactionRef>> SingleTRUCChecks(const CTransactionRef& ptx,
165165
const CTxMemPool::setEntries& mempool_ancestors,
166166
const std::set<Txid>& direct_conflicts,
167167
int64_t vsize)
168168
{
169-
// Check v3 and non-v3 inheritance.
169+
// Check TRUC and non-TRUC inheritance.
170170
for (const auto& entry : mempool_ancestors) {
171171
if (ptx->version != TRUC_VERSION && entry->GetTx().version == TRUC_VERSION) {
172-
return std::make_pair(strprintf("non-v3 tx %s (wtxid=%s) cannot spend from v3 tx %s (wtxid=%s)",
172+
return std::make_pair(strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",
173173
ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),
174174
entry->GetSharedTx()->GetHash().ToString(), entry->GetSharedTx()->GetWitnessHash().ToString()),
175175
nullptr);
176176
} else if (ptx->version == TRUC_VERSION && entry->GetTx().version != TRUC_VERSION) {
177-
return std::make_pair(strprintf("v3 tx %s (wtxid=%s) cannot spend from non-v3 tx %s (wtxid=%s)",
177+
return std::make_pair(strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)",
178178
ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),
179179
entry->GetSharedTx()->GetHash().ToString(), entry->GetSharedTx()->GetWitnessHash().ToString()),
180180
nullptr);
181181
}
182182
}
183183

184184
// This function is specialized for these limits, and must be reimplemented if they ever change.
185-
static_assert(V3_ANCESTOR_LIMIT == 2);
186-
static_assert(V3_DESCENDANT_LIMIT == 2);
185+
static_assert(TRUC_ANCESTOR_LIMIT == 2);
186+
static_assert(TRUC_DESCENDANT_LIMIT == 2);
187187

188188
// The rest of the rules only apply to transactions with version=3.
189189
if (ptx->version != TRUC_VERSION) return std::nullopt;
190190

191-
if (vsize > V3_MAX_VSIZE) {
192-
return std::make_pair(strprintf("v3 tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
193-
ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), vsize, V3_MAX_VSIZE),
191+
if (vsize > TRUC_MAX_VSIZE) {
192+
return std::make_pair(strprintf("version=3 tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
193+
ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), vsize, TRUC_MAX_VSIZE),
194194
nullptr);
195195
}
196196

197-
// Check that V3_ANCESTOR_LIMIT would not be violated.
198-
if (mempool_ancestors.size() + 1 > V3_ANCESTOR_LIMIT) {
197+
// Check that TRUC_ANCESTOR_LIMIT would not be violated.
198+
if (mempool_ancestors.size() + 1 > TRUC_ANCESTOR_LIMIT) {
199199
return std::make_pair(strprintf("tx %s (wtxid=%s) would have too many ancestors",
200200
ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString()),
201201
nullptr);
202202
}
203203

204204
// Remaining checks only pertain to transactions with unconfirmed ancestors.
205205
if (mempool_ancestors.size() > 0) {
206-
// If this transaction spends V3 parents, it cannot be too large.
207-
if (vsize > V3_CHILD_MAX_VSIZE) {
208-
return std::make_pair(strprintf("v3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
209-
ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), vsize, V3_CHILD_MAX_VSIZE),
206+
// If this transaction spends TRUC parents, it cannot be too large.
207+
if (vsize > TRUC_CHILD_MAX_VSIZE) {
208+
return std::make_pair(strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
209+
ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), vsize, TRUC_CHILD_MAX_VSIZE),
210210
nullptr);
211211
}
212212

@@ -217,14 +217,14 @@ std::optional<std::pair<std::string, CTransactionRef>> SingleV3Checks(const CTra
217217
// possible through a reorg.
218218
const auto& children = parent_entry->GetMemPoolChildrenConst();
219219
// Don't double-count a transaction that is going to be replaced. This logic assumes that
220-
// any descendant of the V3 transaction is a direct child, which makes sense because a V3
221-
// transaction can only have 1 descendant.
220+
// any descendant of the TRUC transaction is a direct child, which makes sense because a
221+
// TRUC transaction can only have 1 descendant.
222222
const bool child_will_be_replaced = !children.empty() &&
223223
std::any_of(children.cbegin(), children.cend(),
224224
[&direct_conflicts](const CTxMemPoolEntry& child){return direct_conflicts.count(child.GetTx().GetHash()) > 0;});
225-
if (parent_entry->GetCountWithDescendants() + 1 > V3_DESCENDANT_LIMIT && !child_will_be_replaced) {
226-
// Allow sibling eviction for v3 transaction: if another child already exists, even if
227-
// we don't conflict inputs with it, consider evicting it under RBF rules. We rely on v3 rules
225+
if (parent_entry->GetCountWithDescendants() + 1 > TRUC_DESCENDANT_LIMIT && !child_will_be_replaced) {
226+
// Allow sibling eviction for TRUC transaction: if another child already exists, even if
227+
// we don't conflict inputs with it, consider evicting it under RBF rules. We rely on TRUC rules
228228
// only permitting 1 descendant, as otherwise we would need to have logic for deciding
229229
// which descendant to evict. Skip if this isn't true, e.g. if the transaction has
230230
// multiple children or the sibling also has descendants due to a reorg.

src/policy/truc_policy.h

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright (c) 2022 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_POLICY_TRUC_POLICY_H
6+
#define BITCOIN_POLICY_TRUC_POLICY_H
7+
8+
#include <consensus/amount.h>
9+
#include <policy/packages.h>
10+
#include <policy/policy.h>
11+
#include <primitives/transaction.h>
12+
#include <txmempool.h>
13+
#include <util/result.h>
14+
15+
#include <set>
16+
#include <string>
17+
18+
// This module enforces rules for BIP 431 TRUC transactions which help make
19+
// RBF abilities more robust. A transaction with version=3 is treated as TRUC.
20+
static constexpr decltype(CTransaction::version) TRUC_VERSION{3};
21+
22+
// TRUC only allows 1 parent and 1 child when unconfirmed. This translates to a descendant set size
23+
// of 2 and ancestor set size of 2.
24+
/** Maximum number of transactions including an unconfirmed tx and its descendants. */
25+
static constexpr unsigned int TRUC_DESCENDANT_LIMIT{2};
26+
/** Maximum number of transactions including a TRUC tx and all its mempool ancestors. */
27+
static constexpr unsigned int TRUC_ANCESTOR_LIMIT{2};
28+
29+
/** Maximum sigop-adjusted virtual size of all v3 transactions. */
30+
static constexpr int64_t TRUC_MAX_VSIZE{10000};
31+
/** Maximum sigop-adjusted virtual size of a tx which spends from an unconfirmed TRUC transaction. */
32+
static constexpr int64_t TRUC_CHILD_MAX_VSIZE{1000};
33+
// These limits are within the default ancestor/descendant limits.
34+
static_assert(TRUC_MAX_VSIZE + TRUC_CHILD_MAX_VSIZE <= DEFAULT_ANCESTOR_SIZE_LIMIT_KVB * 1000);
35+
static_assert(TRUC_MAX_VSIZE + TRUC_CHILD_MAX_VSIZE <= DEFAULT_DESCENDANT_SIZE_LIMIT_KVB * 1000);
36+
37+
/** Must be called for every transaction, even if not TRUC. Not strictly necessary for transactions
38+
* accepted through AcceptMultipleTransactions.
39+
*
40+
* Checks the following rules:
41+
* 1. A TRUC tx must only have TRUC unconfirmed ancestors.
42+
* 2. A non-TRUC tx must only have non-TRUC unconfirmed ancestors.
43+
* 3. A TRUC's ancestor set, including itself, must be within TRUC_ANCESTOR_LIMIT.
44+
* 4. A TRUC's descendant set, including itself, must be within TRUC_DESCENDANT_LIMIT.
45+
* 5. If a TRUC tx has any unconfirmed ancestors, the tx's sigop-adjusted vsize must be within
46+
* TRUC_CHILD_MAX_VSIZE.
47+
* 6. A TRUC tx must be within TRUC_MAX_VSIZE.
48+
*
49+
*
50+
* @param[in] mempool_ancestors The in-mempool ancestors of ptx.
51+
* @param[in] direct_conflicts In-mempool transactions this tx conflicts with. These conflicts
52+
* are used to more accurately calculate the resulting descendant
53+
* count of in-mempool ancestors.
54+
* @param[in] vsize The sigop-adjusted virtual size of ptx.
55+
*
56+
* @returns 3 possibilities:
57+
* - std::nullopt if all TRUC checks were applied successfully
58+
* - debug string + pointer to a mempool sibling if this transaction would be the second child in a
59+
* 1-parent-1-child cluster; the caller may consider evicting the specified sibling or return an
60+
* error with the debug string.
61+
* - debug string + nullptr if this transaction violates some TRUC rule and sibling eviction is not
62+
* applicable.
63+
*/
64+
std::optional<std::pair<std::string, CTransactionRef>> SingleTRUCChecks(const CTransactionRef& ptx,
65+
const CTxMemPool::setEntries& mempool_ancestors,
66+
const std::set<Txid>& direct_conflicts,
67+
int64_t vsize);
68+
69+
/** Must be called for every transaction that is submitted within a package, even if not TRUC.
70+
*
71+
* For each transaction in a package:
72+
* If it's not a TRUC transaction, verify it has no direct TRUC parents in the mempool or the package.
73+
74+
* If it is a TRUC transaction, verify that any direct parents in the mempool or the package are TRUC.
75+
* If such a parent exists, verify that parent has no other children in the package or the mempool,
76+
* and that the transaction itself has no children in the package.
77+
*
78+
* If any TRUC violations in the package exist, this test will fail for one of them:
79+
* - if a TRUC transaction T has a parent in the mempool and a child in the package, then PTRUCC(T) will fail
80+
* - if a TRUC transaction T has a parent in the package and a child in the package, then PTRUCC(T) will fail
81+
* - if a TRUC transaction T and a TRUC (sibling) transaction U have some parent in the mempool,
82+
* then PTRUCC(T) and PTRUCC(U) will fail
83+
* - if a TRUC transaction T and a TRUC (sibling) transaction U have some parent in the package,
84+
* then PTRUCC(T) and PTRUCC(U) will fail
85+
* - if a TRUC transaction T has a parent P and a grandparent G in the package, then
86+
* PTRUCC(P) will fail (though PTRUCC(G) and PTRUCC(T) might succeed).
87+
*
88+
* @returns debug string if an error occurs, std::nullopt otherwise.
89+
* */
90+
std::optional<std::string> PackageTRUCChecks(const CTransactionRef& ptx, int64_t vsize,
91+
const Package& package,
92+
const CTxMemPool::setEntries& mempool_ancestors);
93+
94+
#endif // BITCOIN_POLICY_TRUC_POLICY_H

0 commit comments

Comments
 (0)