Skip to content

Commit f06d7bc

Browse files
committed
Merge branch 'sendraw_force-28+knots' into rbf_opts-28+knots
2 parents ac36413 + b7c5c0c commit f06d7bc

29 files changed

+399
-171
lines changed

src/interfaces/node.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <stdint.h>
2121
#include <string>
2222
#include <tuple>
23+
#include <variant>
2324
#include <vector>
2425

2526
class BanMan;
@@ -214,7 +215,7 @@ class Node
214215
virtual std::optional<Coin> getUnspentOutput(const COutPoint& output) = 0;
215216

216217
//! Broadcast transaction.
217-
virtual node::TransactionError broadcastTransaction(CTransactionRef tx, CAmount max_tx_fee, std::string& err_string) = 0;
218+
virtual node::TransactionError broadcastTransaction(CTransactionRef tx, const std::variant<CAmount, CFeeRate>& max_tx_fee, std::string& err_string) = 0;
218219

219220
//! Get wallet loader.
220221
virtual WalletLoader& walletLoader() = 0;

src/kernel/mempool_entry.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ struct NewMempoolTransactionInfo {
226226
* This boolean indicates whether the transaction was added
227227
* without enforcing mempool fee limits.
228228
*/
229-
const bool m_mempool_limit_bypassed;
229+
const ignore_rejects_type m_ignore_rejects;
230230
/* This boolean indicates whether the transaction is part of a package. */
231231
const bool m_submitted_in_package;
232232
/*
@@ -239,11 +239,11 @@ struct NewMempoolTransactionInfo {
239239

240240
explicit NewMempoolTransactionInfo(const CTransactionRef& tx, const CAmount& fee,
241241
const int64_t vsize, const unsigned int height,
242-
const bool mempool_limit_bypassed, const bool submitted_in_package,
242+
const ignore_rejects_type& ignore_rejects, const bool submitted_in_package,
243243
const bool chainstate_is_current,
244244
const bool has_no_mempool_parents)
245245
: info{tx, fee, vsize, height},
246-
m_mempool_limit_bypassed{mempool_limit_bypassed},
246+
m_ignore_rejects{ignore_rejects},
247247
m_submitted_in_package{submitted_in_package},
248248
m_chainstate_is_current{chainstate_is_current},
249249
m_has_no_mempool_parents{has_no_mempool_parents} {}

src/node/interfaces.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ class NodeImpl : public Node
355355
if (chainman().ActiveChainstate().CoinsTip().GetCoin(output, coin)) return coin;
356356
return {};
357357
}
358-
TransactionError broadcastTransaction(CTransactionRef tx, CAmount max_tx_fee, std::string& err_string) override
358+
TransactionError broadcastTransaction(CTransactionRef tx, const std::variant<CAmount, CFeeRate>& max_tx_fee, std::string& err_string) override
359359
{
360360
return BroadcastTransaction(*m_context, std::move(tx), err_string, max_tx_fee, /*relay=*/ true, /*wait_callback=*/ false);
361361
}

src/node/mempool_persist.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, Chainstate& active
9898
}
9999
if (nTime > TicksSinceEpoch<std::chrono::seconds>(now - pool.m_opts.expiry)) {
100100
LOCK(cs_main);
101-
const auto& accepted = AcceptToMemoryPool(active_chainstate, tx, nTime, /*bypass_limits=*/false, /*test_accept=*/false);
101+
const auto& accepted = AcceptToMemoryPool(active_chainstate, tx, nTime, empty_ignore_rejects, /*test_accept=*/false);
102102
if (accepted.m_result_type == MempoolAcceptResult::ResultType::VALID) {
103103
++count;
104104
} else {

src/node/transaction.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ static TransactionError HandleATMPError(const TxValidationState& state, std::str
3131
}
3232
}
3333

34-
TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef tx, std::string& err_string, const CAmount& max_tx_fee, bool relay, bool wait_callback)
34+
TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef tx, std::string& err_string, const std::variant<CAmount, CFeeRate>& max_tx_fee, bool relay, bool wait_callback, const ignore_rejects_type& ignore_rejects)
3535
{
3636
// BroadcastTransaction can be called by RPC or by the wallet.
3737
// chainman, mempool and peerman are initialized before the RPC server and wallet are started
@@ -69,18 +69,30 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
6969
wtxid = mempool_tx->GetWitnessHash();
7070
} else {
7171
// Transaction is not already in the mempool.
72-
if (max_tx_fee > 0) {
72+
bool max_tx_fee_set{(std::holds_alternative<CAmount>(max_tx_fee) ? std::get<CAmount>(max_tx_fee) : std::get<CFeeRate>(max_tx_fee).GetFeePerK()) > 0};
73+
if (ignore_rejects.count("absurdly-high-fee") || ignore_rejects.count("max-fee-exceeded")) {
74+
max_tx_fee_set = false;
75+
}
76+
if (max_tx_fee_set) {
7377
// First, call ATMP with test_accept and check the fee. If ATMP
7478
// fails here, return error immediately.
75-
const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/ true);
79+
const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/ true, ignore_rejects);
7680
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
7781
return HandleATMPError(result.m_state, err_string);
78-
} else if (result.m_base_fees.value() > max_tx_fee) {
79-
return TransactionError::MAX_FEE_EXCEEDED;
82+
} else {
83+
CAmount max_tx_fee_abs;
84+
if (std::holds_alternative<CFeeRate>(max_tx_fee)) {
85+
max_tx_fee_abs = std::get<CFeeRate>(max_tx_fee).GetFee(*Assert(result.m_vsize));
86+
} else {
87+
max_tx_fee_abs = std::get<CAmount>(max_tx_fee);
88+
}
89+
if (result.m_base_fees.value() > max_tx_fee_abs) {
90+
return TransactionError::MAX_FEE_EXCEEDED;
91+
}
8092
}
8193
}
8294
// Try to submit the transaction to the mempool.
83-
const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/ false);
95+
const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/ false, ignore_rejects);
8496
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
8597
return HandleATMPError(result.m_state, err_string);
8698
}

src/node/transaction.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
#include <common/messages.h>
99
#include <policy/feerate.h>
1010
#include <primitives/transaction.h>
11+
#include <policy/policy.h>
12+
13+
#include <variant>
1114

1215
class CBlockIndex;
1316
class CTxMemPool;
@@ -49,7 +52,7 @@ static const CAmount DEFAULT_MAX_BURN_AMOUNT{0};
4952
* @param[in] wait_callback wait until callbacks have been processed to avoid stale result due to a sequentially RPC.
5053
* return error
5154
*/
52-
[[nodiscard]] TransactionError BroadcastTransaction(NodeContext& node, CTransactionRef tx, std::string& err_string, const CAmount& max_tx_fee, bool relay, bool wait_callback);
55+
[[nodiscard]] TransactionError BroadcastTransaction(NodeContext& node, CTransactionRef tx, std::string& err_string, const std::variant<CAmount, CFeeRate>& max_tx_fee, bool relay, bool wait_callback, const ignore_rejects_type& ignore_rejects=empty_ignore_rejects);
5356

5457
/**
5558
* Return transaction with a given hash.

src/policy/fees.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,7 @@ void CBlockPolicyEstimator::processTransaction(const NewMempoolTransactionInfo&
612612
// - the node is not behind
613613
// - the transaction is not dependent on any other transactions in the mempool
614614
// - it's not part of a package.
615-
const bool validForFeeEstimation = !tx.m_mempool_limit_bypassed && !tx.m_submitted_in_package && tx.m_chainstate_is_current && tx.m_has_no_mempool_parents;
615+
const bool validForFeeEstimation = tx.m_ignore_rejects.empty() && !tx.m_submitted_in_package && tx.m_chainstate_is_current && tx.m_has_no_mempool_parents;
616616

617617
// Only want to be updating estimates when our blockchain is synced,
618618
// otherwise we'll miscalculate how many blocks its taking to get included.

src/policy/policy.cpp

Lines changed: 88 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
6767
return (txout.nValue < GetDustThreshold(txout, dustRelayFeeIn));
6868
}
6969

70+
/**
71+
* Note this must assign whichType even if returning false, in case
72+
* IsStandardTx ignores the "scriptpubkey" rejection.
73+
*/
7074
bool IsStandard(const CScript& scriptPubKey, const std::optional<unsigned>& max_datacarrier_bytes, TxoutType& whichType)
7175
{
7276
std::vector<std::vector<unsigned char> > vSolutions;
@@ -91,11 +95,27 @@ bool IsStandard(const CScript& scriptPubKey, const std::optional<unsigned>& max_
9195
return true;
9296
}
9397

94-
bool IsStandardTx(const CTransaction& tx, const std::optional<unsigned>& max_datacarrier_bytes, bool permit_bare_multisig, const CFeeRate& dust_relay_fee, std::string& reason)
98+
static inline bool MaybeReject_(std::string& out_reason, const std::string& reason, const std::string& reason_prefix, const ignore_rejects_type& ignore_rejects) {
99+
if (ignore_rejects.count(reason_prefix + reason)) {
100+
return false;
101+
}
102+
103+
out_reason = reason_prefix + reason;
104+
return true;
105+
}
106+
107+
#define MaybeReject(reason) do { \
108+
if (MaybeReject_(out_reason, reason, reason_prefix, ignore_rejects)) { \
109+
return false; \
110+
} \
111+
} while(0)
112+
113+
bool IsStandardTx(const CTransaction& tx, const std::optional<unsigned>& max_datacarrier_bytes, bool permit_bare_multisig, const CFeeRate& dust_relay_fee, std::string& out_reason, const ignore_rejects_type& ignore_rejects)
95114
{
115+
const std::string reason_prefix;
116+
96117
if (tx.version > TX_MAX_STANDARD_VERSION || tx.version < 1) {
97-
reason = "version";
98-
return false;
118+
MaybeReject("version");
99119
}
100120

101121
// Extremely large transactions with lots of inputs can cost the network
@@ -104,8 +124,7 @@ bool IsStandardTx(const CTransaction& tx, const std::optional<unsigned>& max_dat
104124
// to MAX_STANDARD_TX_WEIGHT mitigates CPU exhaustion attacks.
105125
unsigned int sz = GetTransactionWeight(tx);
106126
if (sz > MAX_STANDARD_TX_WEIGHT) {
107-
reason = "tx-size";
108-
return false;
127+
MaybeReject("tx-size");
109128
}
110129

111130
for (const CTxIn& txin : tx.vin)
@@ -119,38 +138,39 @@ bool IsStandardTx(const CTransaction& tx, const std::optional<unsigned>& max_dat
119138
// 20-of-20 CHECKMULTISIG scriptPubKey, though such a scriptPubKey
120139
// is not considered standard.
121140
if (txin.scriptSig.size() > MAX_STANDARD_SCRIPTSIG_SIZE) {
122-
reason = "scriptsig-size";
123-
return false;
141+
MaybeReject("scriptsig-size");
124142
}
125143
if (!txin.scriptSig.IsPushOnly()) {
126-
reason = "scriptsig-not-pushonly";
127-
return false;
144+
MaybeReject("scriptsig-not-pushonly");
128145
}
129146
}
130147

131148
unsigned int nDataOut = 0;
132149
TxoutType whichType;
133150
for (const CTxOut& txout : tx.vout) {
134151
if (!::IsStandard(txout.scriptPubKey, max_datacarrier_bytes, whichType)) {
135-
reason = "scriptpubkey";
136-
return false;
152+
if (whichType == TxoutType::WITNESS_UNKNOWN) {
153+
MaybeReject("scriptpubkey-unknown-witnessversion");
154+
} else {
155+
MaybeReject("scriptpubkey");
156+
}
137157
}
138158

139-
if (whichType == TxoutType::NULL_DATA)
159+
if (whichType == TxoutType::NULL_DATA) {
140160
nDataOut++;
161+
continue;
162+
}
141163
else if ((whichType == TxoutType::MULTISIG) && (!permit_bare_multisig)) {
142-
reason = "bare-multisig";
143-
return false;
144-
} else if (IsDust(txout, dust_relay_fee)) {
145-
reason = "dust";
146-
return false;
164+
MaybeReject("bare-multisig");
165+
}
166+
if (IsDust(txout, dust_relay_fee)) {
167+
MaybeReject("dust");
147168
}
148169
}
149170

150171
// only one OP_RETURN txout is permitted
151172
if (nDataOut > 1) {
152-
reason = "multi-op-return";
153-
return false;
173+
MaybeReject("multi-op-return");
154174
}
155175

156176
return true;
@@ -174,7 +194,7 @@ bool IsStandardTx(const CTransaction& tx, const std::optional<unsigned>& max_dat
174194
*
175195
* Note that only the non-witness portion of the transaction is checked here.
176196
*/
177-
bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
197+
bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, const std::string& reason_prefix, std::string& out_reason, const ignore_rejects_type& ignore_rejects)
178198
{
179199
if (tx.IsCoinBase()) {
180200
return true; // Coinbases don't use vin normally
@@ -185,30 +205,46 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
185205

186206
std::vector<std::vector<unsigned char> > vSolutions;
187207
TxoutType whichType = Solver(prev.scriptPubKey, vSolutions);
188-
if (whichType == TxoutType::NONSTANDARD || whichType == TxoutType::WITNESS_UNKNOWN) {
208+
if (whichType == TxoutType::NONSTANDARD) {
209+
MaybeReject("script-unknown");
210+
} else if (whichType == TxoutType::WITNESS_UNKNOWN) {
189211
// WITNESS_UNKNOWN failures are typically also caught with a policy
190212
// flag in the script interpreter, but it can be helpful to catch
191213
// this type of NONSTANDARD transaction earlier in transaction
192214
// validation.
193-
return false;
215+
MaybeReject("witness-unknown");
194216
} else if (whichType == TxoutType::SCRIPTHASH) {
217+
if (!tx.vin[i].scriptSig.IsPushOnly()) {
218+
// The only way we got this far, is if the user ignored scriptsig-not-pushonly.
219+
// However, this case is invalid, and will be caught later on.
220+
// But for now, we don't want to run the [possibly expensive] script here.
221+
continue;
222+
}
195223
std::vector<std::vector<unsigned char> > stack;
196224
// convert the scriptSig into a stack, so we can inspect the redeemScript
197225
if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), SigVersion::BASE))
226+
{
227+
// This case is also invalid or a bug
228+
out_reason = reason_prefix + "scriptsig-failure";
198229
return false;
230+
}
199231
if (stack.empty())
232+
{
233+
// Also invalid
234+
out_reason = reason_prefix + "scriptcheck-missing";
200235
return false;
236+
}
201237
CScript subscript(stack.back().begin(), stack.back().end());
202238
if (subscript.GetSigOpCount(true) > MAX_P2SH_SIGOPS) {
203-
return false;
239+
MaybeReject("scriptcheck-sigops");
204240
}
205241
}
206242
}
207243

208244
return true;
209245
}
210246

211-
bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
247+
bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, const std::string& reason_prefix, std::string& out_reason, const ignore_rejects_type& ignore_rejects)
212248
{
213249
if (tx.IsCoinBase())
214250
return true; // Coinbases are skipped
@@ -227,7 +263,7 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
227263

228264
// witness stuffing detected
229265
if (prevScript.IsPayToAnchor()) {
230-
return false;
266+
MaybeReject("anchor-not-empty");
231267
}
232268

233269
bool p2sh = false;
@@ -237,9 +273,15 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
237273
// into a stack. We do not check IsPushOnly nor compare the hash as these will be done later anyway.
238274
// If the check fails at this stage, we know that this txid must be a bad one.
239275
if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), SigVersion::BASE))
276+
{
277+
out_reason = reason_prefix + "scriptsig-failure";
240278
return false;
279+
}
241280
if (stack.empty())
281+
{
282+
out_reason = reason_prefix + "scriptcheck-missing";
242283
return false;
284+
}
243285
prevScript = CScript(stack.back().begin(), stack.back().end());
244286
p2sh = true;
245287
}
@@ -249,18 +291,21 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
249291

250292
// Non-witness program must not be associated with any witness
251293
if (!prevScript.IsWitnessProgram(witnessversion, witnessprogram))
294+
{
295+
out_reason = reason_prefix + "nonwitness-input";
252296
return false;
297+
}
253298

254299
// Check P2WSH standard limits
255300
if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) {
256301
if (tx.vin[i].scriptWitness.stack.back().size() > MAX_STANDARD_P2WSH_SCRIPT_SIZE)
257-
return false;
302+
MaybeReject("script-size");
258303
size_t sizeWitnessStack = tx.vin[i].scriptWitness.stack.size() - 1;
259304
if (sizeWitnessStack > MAX_STANDARD_P2WSH_STACK_ITEMS)
260-
return false;
305+
MaybeReject("stackitem-count");
261306
for (unsigned int j = 0; j < sizeWitnessStack; j++) {
262307
if (tx.vin[i].scriptWitness.stack[j].size() > MAX_STANDARD_P2WSH_STACK_ITEM_SIZE)
263-
return false;
308+
MaybeReject("stackitem-size");
264309
}
265310
}
266311

@@ -272,24 +317,36 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
272317
Span stack{tx.vin[i].scriptWitness.stack};
273318
if (stack.size() >= 2 && !stack.back().empty() && stack.back()[0] == ANNEX_TAG) {
274319
// Annexes are nonstandard as long as no semantics are defined for them.
275-
return false;
320+
MaybeReject("taproot-annex");
321+
// If reject reason is ignored, continue as if the annex wasn't there.
322+
SpanPopBack(stack);
276323
}
277324
if (stack.size() >= 2) {
278325
// Script path spend (2 or more stack elements after removing optional annex)
279326
const auto& control_block = SpanPopBack(stack);
280327
SpanPopBack(stack); // Ignore script
281-
if (control_block.empty()) return false; // Empty control block is invalid
328+
if (control_block.empty()) {
329+
// Empty control block is invalid
330+
out_reason = reason_prefix + "taproot-control-missing";
331+
return false;
332+
}
282333
if ((control_block[0] & TAPROOT_LEAF_MASK) == TAPROOT_LEAF_TAPSCRIPT) {
283334
// Leaf version 0xc0 (aka Tapscript, see BIP 342)
335+
if (!ignore_rejects.count(reason_prefix + "taproot-stackitem-size")) {
284336
for (const auto& item : stack) {
285-
if (item.size() > MAX_STANDARD_TAPSCRIPT_STACK_ITEM_SIZE) return false;
337+
if (item.size() > MAX_STANDARD_TAPSCRIPT_STACK_ITEM_SIZE) {
338+
out_reason = reason_prefix + "taproot-stackitem-size";
339+
return false;
340+
}
341+
}
286342
}
287343
}
288344
} else if (stack.size() == 1) {
289345
// Key path spend (1 stack element after removing optional annex)
290346
// (no policy rules apply)
291347
} else {
292348
// 0 stack elements; this is already invalid by consensus rules
349+
out_reason = reason_prefix + "taproot-witness-missing";
293350
return false;
294351
}
295352
}

0 commit comments

Comments
 (0)