Skip to content

Commit b22901d

Browse files
committed
Avoid explicitly computing diagram; compare based on chunks
1 parent ba7c67f commit b22901d

File tree

9 files changed

+140
-190
lines changed

9 files changed

+140
-190
lines changed

src/policy/rbf.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,13 +191,13 @@ std::optional<std::pair<DiagramCheckError, std::string>> ImprovesFeerateDiagram(
191191
int64_t replacement_vsize)
192192
{
193193
// Require that the replacement strictly improves the mempool's feerate diagram.
194-
const auto diagram_results{pool.CalculateFeerateDiagramsForRBF(replacement_fees, replacement_vsize, direct_conflicts, all_conflicts)};
194+
const auto chunk_results{pool.CalculateChunksForRBF(replacement_fees, replacement_vsize, direct_conflicts, all_conflicts)};
195195

196-
if (!diagram_results.has_value()) {
197-
return std::make_pair(DiagramCheckError::UNCALCULABLE, util::ErrorString(diagram_results).original);
196+
if (!chunk_results.has_value()) {
197+
return std::make_pair(DiagramCheckError::UNCALCULABLE, util::ErrorString(chunk_results).original);
198198
}
199199

200-
if (!std::is_gt(CompareFeerateDiagram(diagram_results.value().second, diagram_results.value().first))) {
200+
if (!std::is_gt(CompareChunks(chunk_results.value().second, chunk_results.value().first))) {
201201
return std::make_pair(DiagramCheckError::FAILURE, "insufficient feerate: does not improve feerate diagram");
202202
}
203203
return std::nullopt;

src/test/feefrac_tests.cpp

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -82,43 +82,4 @@ BOOST_AUTO_TEST_CASE(feefrac_operators)
8282

8383
}
8484

85-
BOOST_AUTO_TEST_CASE(build_diagram_test)
86-
{
87-
FeeFrac p1{1000, 100};
88-
FeeFrac empty{0, 0};
89-
FeeFrac zero_fee{0, 1};
90-
FeeFrac oversized_1{4611686000000, 4000000};
91-
FeeFrac oversized_2{184467440000000, 100000};
92-
93-
// Diagram-building will reorder chunks
94-
std::vector<FeeFrac> chunks;
95-
chunks.push_back(p1);
96-
chunks.push_back(zero_fee);
97-
chunks.push_back(empty);
98-
chunks.push_back(oversized_1);
99-
chunks.push_back(oversized_2);
100-
101-
// Caller in charge of sorting chunks in case chunk size limit
102-
// differs from cluster size limit
103-
std::sort(chunks.begin(), chunks.end(), [](const FeeFrac& a, const FeeFrac& b) { return a > b; });
104-
105-
// Chunks are now sorted in reverse order (largest first)
106-
BOOST_CHECK(chunks[0] == empty); // empty is considered "highest" fee
107-
BOOST_CHECK(chunks[1] == oversized_2);
108-
BOOST_CHECK(chunks[2] == oversized_1);
109-
BOOST_CHECK(chunks[3] == p1);
110-
BOOST_CHECK(chunks[4] == zero_fee);
111-
112-
std::vector<FeeFrac> generated_diagram{BuildDiagramFromChunks(chunks)};
113-
BOOST_CHECK(generated_diagram.size() == 1 + chunks.size());
114-
115-
// Prepended with an empty, then the chunks summed in order as above
116-
BOOST_CHECK(generated_diagram[0] == empty);
117-
BOOST_CHECK(generated_diagram[1] == empty);
118-
BOOST_CHECK(generated_diagram[2] == oversized_2);
119-
BOOST_CHECK(generated_diagram[3] == oversized_2 + oversized_1);
120-
BOOST_CHECK(generated_diagram[4] == oversized_2 + oversized_1 + p1);
121-
BOOST_CHECK(generated_diagram[5] == oversized_2 + oversized_1 + p1 + zero_fee);
122-
}
123-
12485
BOOST_AUTO_TEST_SUITE_END()

src/test/fuzz/feeratediagram.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,20 @@
1616

1717
namespace {
1818

19+
/** Takes the pre-computed and topologically-valid chunks and generates a fee diagram which starts at FeeFrac of (0, 0) */
20+
std::vector<FeeFrac> BuildDiagramFromChunks(const Span<const FeeFrac> chunks)
21+
{
22+
std::vector<FeeFrac> diagram;
23+
diagram.reserve(chunks.size() + 1);
24+
25+
diagram.emplace_back(0, 0);
26+
for (auto& chunk : chunks) {
27+
diagram.emplace_back(diagram.back() + chunk);
28+
}
29+
return diagram;
30+
}
31+
32+
1933
/** Evaluate a diagram at a specific size, returning the fee as a fraction.
2034
*
2135
* Fees in diagram cannot exceed 2^32, as the returned evaluation could overflow
@@ -103,7 +117,7 @@ FUZZ_TARGET(build_and_compare_feerate_diagram)
103117
assert(diagram1.front() == empty);
104118
assert(diagram2.front() == empty);
105119

106-
auto real = CompareFeerateDiagram(diagram1, diagram2);
120+
auto real = CompareChunks(chunks1, chunks2);
107121
auto sim = CompareDiagrams(diagram1, diagram2);
108122
assert(real == sim);
109123

src/test/fuzz/rbf.cpp

Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -82,17 +82,6 @@ FUZZ_TARGET(rbf, .init = initialize_rbf)
8282
}
8383
}
8484

85-
void CheckDiagramConcave(std::vector<FeeFrac>& diagram)
86-
{
87-
// Diagrams are in monotonically-decreasing feerate order.
88-
FeeFrac last_chunk = diagram.front();
89-
for (size_t i = 1; i<diagram.size(); ++i) {
90-
FeeFrac next_chunk = diagram[i] - diagram[i-1];
91-
assert(next_chunk <= last_chunk);
92-
last_chunk = next_chunk;
93-
}
94-
}
95-
9685
FUZZ_TARGET(package_rbf, .init = initialize_package_rbf)
9786
{
9887
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
@@ -107,7 +96,7 @@ FUZZ_TARGET(package_rbf, .init = initialize_package_rbf)
10796
std::vector<CTransaction> mempool_txs;
10897
size_t iter{0};
10998

110-
int64_t replacement_vsize = fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(1, 1000000);
99+
int32_t replacement_vsize = fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(1, 1000000);
111100

112101
// Keep track of the total vsize of CTxMemPoolEntry's being added to the mempool to avoid overflow
113102
// Add replacement_vsize since this is added to new diagram during RBF check
@@ -167,32 +156,33 @@ FUZZ_TARGET(package_rbf, .init = initialize_package_rbf)
167156
pool.CalculateDescendants(txiter, all_conflicts);
168157
}
169158

170-
// Calculate the feerate diagrams for a replacement.
159+
// Calculate the chunks for a replacement.
171160
CAmount replacement_fees = ConsumeMoney(fuzzed_data_provider);
172-
auto calc_results{pool.CalculateFeerateDiagramsForRBF(replacement_fees, replacement_vsize, direct_conflicts, all_conflicts)};
161+
auto calc_results{pool.CalculateChunksForRBF(replacement_fees, replacement_vsize, direct_conflicts, all_conflicts)};
173162

174163
if (calc_results.has_value()) {
175-
// Sanity checks on the diagrams.
164+
// Sanity checks on the chunks.
176165

177-
// Diagrams start at 0.
178-
assert(calc_results->first.front().size == 0);
179-
assert(calc_results->first.front().fee == 0);
180-
assert(calc_results->second.front().size == 0);
181-
assert(calc_results->second.front().fee == 0);
182-
183-
CheckDiagramConcave(calc_results->first);
184-
CheckDiagramConcave(calc_results->second);
166+
// Feerates are monotonically decreasing.
167+
FeeFrac first_sum;
168+
for (size_t i = 0; i < calc_results->first.size(); ++i) {
169+
first_sum += calc_results->first[i];
170+
if (i) assert(!(calc_results->first[i - 1] << calc_results->first[i]));
171+
}
172+
FeeFrac second_sum;
173+
for (size_t i = 0; i < calc_results->second.size(); ++i) {
174+
second_sum += calc_results->second[i];
175+
if (i) assert(!(calc_results->second[i - 1] << calc_results->second[i]));
176+
}
185177

186-
CAmount replaced_fee{0};
187-
int64_t replaced_size{0};
178+
FeeFrac replaced;
188179
for (auto txiter : all_conflicts) {
189-
replaced_fee += txiter->GetModifiedFee();
190-
replaced_size += txiter->GetTxSize();
180+
replaced.fee += txiter->GetModifiedFee();
181+
replaced.size += txiter->GetTxSize();
191182
}
192-
// The total fee of the new diagram should be the total fee of the old
193-
// diagram - replaced_fee + replacement_fees
194-
assert(calc_results->first.back().fee - replaced_fee + replacement_fees == calc_results->second.back().fee);
195-
assert(calc_results->first.back().size - replaced_size + replacement_vsize == calc_results->second.back().size);
183+
// The total fee & size of the new diagram minus replaced fee & size should be the total
184+
// fee & size of the old diagram minus replacement fee & size.
185+
assert((first_sum - replaced) == (second_sum - FeeFrac{replacement_fees, replacement_vsize}));
196186
}
197187

198188
// If internals report error, wrapper should too
@@ -201,10 +191,12 @@ FUZZ_TARGET(package_rbf, .init = initialize_package_rbf)
201191
assert(err_tuple.value().first == DiagramCheckError::UNCALCULABLE);
202192
} else {
203193
// Diagram check succeeded
194+
auto old_sum = std::accumulate(calc_results->first.begin(), calc_results->first.end(), FeeFrac{});
195+
auto new_sum = std::accumulate(calc_results->second.begin(), calc_results->second.end(), FeeFrac{});
204196
if (!err_tuple.has_value()) {
205197
// New diagram's final fee should always match or exceed old diagram's
206-
assert(calc_results->first.back().fee <= calc_results->second.back().fee);
207-
} else if (calc_results->first.back().fee > calc_results->second.back().fee) {
198+
assert(old_sum.fee <= new_sum.fee);
199+
} else if (old_sum.fee > new_sum.fee) {
208200
// Or it failed, and if old diagram had higher fees, it should be a failure
209201
assert(err_tuple.value().first == DiagramCheckError::FAILURE);
210202
}

0 commit comments

Comments
 (0)