Created
December 15, 2023 20:43
-
-
Save instagibbs/c5cb0796ceec81f0374ae614f8cdab7f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/src/test/fuzz/package_eval.cpp b/src/test/fuzz/package_eval.cpp | |
index 9c64c39699..989373b044 100644 | |
--- a/src/test/fuzz/package_eval.cpp | |
+++ b/src/test/fuzz/package_eval.cpp | |
@@ -98,51 +98,52 @@ struct TransactionsDelta final : public CValidationInterface { | |
m_added.erase(tx); | |
} | |
}; | |
void MockTime(FuzzedDataProvider& fuzzed_data_provider, const Chainstate& chainstate) | |
{ | |
const auto time = ConsumeTime(fuzzed_data_provider, | |
chainstate.m_chain.Tip()->GetMedianTimePast() + 1, | |
std::numeric_limits<decltype(chainstate.m_chain.Tip()->nTime)>::max()); | |
SetMockTime(time); | |
} | |
CTxMemPool MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeContext& node) | |
{ | |
// Take the default options for tests... | |
CTxMemPool::Options mempool_opts{MemPoolOptionsForTest(node)}; | |
// ...override specific options for this specific fuzz suite | |
mempool_opts.limits.ancestor_count = fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 50); | |
mempool_opts.limits.ancestor_size_vbytes = fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 202) * 1'000; | |
mempool_opts.limits.descendant_count = fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 50); | |
mempool_opts.limits.descendant_size_vbytes = fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 202) * 1'000; | |
mempool_opts.max_size_bytes = fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 200) * 1'000'000; | |
mempool_opts.expiry = std::chrono::hours{fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 999)}; | |
- nBytesPerSigOp = fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(1, 999); | |
+ // Make them free or cost a single legacy sigop >> 1kVB | |
+ nBytesPerSigOp = fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 1) * 10'000; | |
mempool_opts.check_ratio = 1; | |
mempool_opts.require_standard = fuzzed_data_provider.ConsumeBool(); | |
// ...and construct a CTxMemPool from it | |
return CTxMemPool{mempool_opts}; | |
} | |
FUZZ_TARGET(tx_package_eval, .init = initialize_tx_pool) | |
{ | |
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); | |
const auto& node = g_setup->m_node; | |
auto& chainstate{static_cast<DummyChainState&>(node.chainman->ActiveChainstate())}; | |
MockTime(fuzzed_data_provider, chainstate); | |
// All RBF-spendable outpoints outside of the unsubmitted package | |
std::set<COutPoint> mempool_outpoints; | |
std::map<COutPoint, CAmount> outpoints_value; | |
for (const auto& outpoint : g_outpoints_coinbase_init_mature) { | |
Assert(mempool_outpoints.insert(outpoint).second); | |
outpoints_value[outpoint] = 50 * COIN; | |
} | |
auto outpoints_updater = std::make_shared<OutpointsUpdater>(mempool_outpoints); | |
@@ -195,56 +196,62 @@ FUZZ_TARGET(tx_package_eval, .init = initialize_tx_pool) | |
// Create input | |
const auto sequence = ConsumeSequence(fuzzed_data_provider); | |
const auto script_sig = CScript{}; | |
const auto script_wit_stack = fuzzed_data_provider.ConsumeBool() ? P2WSH_EMPTY_TRUE_STACK : P2WSH_EMPTY_TWO_STACK; | |
CTxIn in; | |
in.prevout = outpoint; | |
in.nSequence = sequence; | |
in.scriptSig = script_sig; | |
in.scriptWitness.stack = script_wit_stack; | |
tx_mut.vin.push_back(in); | |
} | |
// Duplicate an input | |
bool dup_input = fuzzed_data_provider.ConsumeBool(); | |
if (dup_input) { | |
tx_mut.vin.push_back(tx_mut.vin.back()); | |
} | |
// Refer to a non-existent input | |
if (fuzzed_data_provider.ConsumeBool()) { | |
tx_mut.vin.emplace_back(); | |
} | |
+ // Make a p2pk output to make sigops adjusted vsize to violate v3, potentially, which is never spent | |
+ if (last_tx && amount_in > 1000 && fuzzed_data_provider.ConsumeBool()) { | |
+ //num_out = 1; // let the vsize hit come in input non-adjusted size | |
+ tx_mut.vout.emplace_back(1000, CScript() << std::vector<unsigned char>(33, 0x02) << OP_CHECKSIG); | |
+ amount_in -= 1000; | |
+ } | |
+ | |
const auto amount_fee = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, amount_in); | |
const auto amount_out = (amount_in - amount_fee) / num_out; | |
for (int i = 0; i < num_out; ++i) { | |
tx_mut.vout.emplace_back(amount_out, P2WSH_EMPTY); | |
} | |
- // TODO vary transaction sizes to catch size-related issues | |
auto tx = MakeTransactionRef(tx_mut); | |
// Restore previously removed outpoints, except in-package outpoints | |
if (!last_tx) { | |
for (const auto& in : tx->vin) { | |
// It's a fake input, or a new input, or a duplicate | |
Assert(in == CTxIn() || outpoints.insert(in.prevout).second || dup_input); | |
} | |
// Cache the in-package outpoints being made | |
for (size_t i = 0; i < tx->vout.size(); ++i) { | |
package_outpoints.emplace(tx->GetHash(), i); | |
} | |
} | |
// We need newly-created values for the duration of this run | |
for (size_t i = 0; i < tx->vout.size(); ++i) { | |
outpoints_value[COutPoint(tx->GetHash(), i)] = tx->vout[i].nValue; | |
} | |
return tx; | |
}(); | |
txs.push_back(tx); | |
} | |
if (fuzzed_data_provider.ConsumeBool()) { | |
MockTime(fuzzed_data_provider, chainstate); | |
} | |
if (fuzzed_data_provider.ConsumeBool()) { | |
diff --git a/src/validation.cpp b/src/validation.cpp | |
index 9120f96eb9..dbf1769927 100644 | |
--- a/src/validation.cpp | |
+++ b/src/validation.cpp | |
@@ -944,51 +944,51 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) | |
// This allows protocols which rely on distrusting counterparties | |
// being able to broadcast descendants of an unconfirmed transaction | |
// to be secure by simply only having two immediately-spendable | |
// outputs - one for each counterparty. For more info on the uses for | |
// this, see https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-November/016518.html | |
CTxMemPool::Limits cpfp_carve_out_limits{ | |
.ancestor_count = 2, | |
.ancestor_size_vbytes = maybe_rbf_limits.ancestor_size_vbytes, | |
.descendant_count = maybe_rbf_limits.descendant_count + 1, | |
.descendant_size_vbytes = maybe_rbf_limits.descendant_size_vbytes + EXTRA_DESCENDANT_TX_SIZE_LIMIT, | |
}; | |
const auto error_message{util::ErrorString(ancestors).original}; | |
if (ws.m_vsize > EXTRA_DESCENDANT_TX_SIZE_LIMIT) { | |
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "too-long-mempool-chain", error_message); | |
} | |
ancestors = m_pool.CalculateMemPoolAncestors(*entry, cpfp_carve_out_limits); | |
if (!ancestors) return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "too-long-mempool-chain", error_message); | |
} | |
ws.m_ancestors = *ancestors; | |
// Even though just checking direct mempool parents for inheritance would be sufficient, we | |
// check using the full ancestor set here because it's more convenient to use what we have | |
// already calculated. | |
CTxMemPool::setEntries collective_ancestors = *ancestors; | |
collective_ancestors.merge(ws.m_ancestors_of_in_package_ancestors); | |
- if (const auto err_string{ApplyV3Rules(ws.m_ptx, collective_ancestors, ws.m_num_in_package_ancestors, ws.m_conflicts, ws.m_vsize)}) { | |
+ if (const auto err_string{ApplyV3Rules(ws.m_ptx, collective_ancestors, ws.m_num_in_package_ancestors, ws.m_conflicts, entry->GetTxWeight()/4)}) { | |
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "v3-rule-violation", *err_string); | |
} | |
// A transaction that spends outputs that would be replaced by it is invalid. Now | |
// that we have the set of all ancestors we can detect this | |
// pathological case by making sure ws.m_conflicts and ws.m_ancestors don't | |
// intersect. | |
if (const auto err_string{EntriesAndTxidsDisjoint(ws.m_ancestors, ws.m_conflicts, hash)}) { | |
// We classify this as a consensus error because a transaction depending on something it | |
// conflicts with would be inconsistent. | |
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-spends-conflicting-tx", *err_string); | |
} | |
m_rbf = !ws.m_conflicts.empty(); | |
return true; | |
} | |
bool MemPoolAccept::ReplacementChecks(Workspace& ws) | |
{ | |
AssertLockHeld(cs_main); | |
AssertLockHeld(m_pool.cs); | |
const CTransaction& tx = *ws.m_ptx; | |
const uint256& hash = ws.m_hash; | |
TxValidationState& state = ws.m_state; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment