-
Notifications
You must be signed in to change notification settings - Fork 35.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mempool util: Add RBF diagram checks for single chunks against clusters of size 2 #29242
Merged
Merged
Changes from 1 commit
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
ce8e225
Add FeeFrac utils
instagibbs 66d966d
Add FeeFrac unit tests
instagibbs 2079b80
Implement ImprovesFeerateDiagram
instagibbs 588a98d
fuzz: Add fuzz target for ImprovesFeerateDiagram
instagibbs e9c5aeb
test: Add tests for CompareFeerateDiagram and CheckConflictTopology
instagibbs 4d6528a
fuzz: fuzz diagram creation and comparison
instagibbs 7e89b65
Add fuzz test for FeeFrac
instagibbs b767e6b
test: unit test for ImprovesFeerateDiagram
instagibbs 7295986
Unit tests for CalculateFeerateDiagramsForRBF
instagibbs File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
// Copyright (c) 2023 The Bitcoin Core developers | ||
// Distributed under the MIT software license, see the accompanying | ||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||
|
||
#include <stdint.h> | ||
|
||
#include <vector> | ||
|
||
#include <util/feefrac.h> | ||
#include <policy/rbf.h> | ||
|
||
#include <test/fuzz/fuzz.h> | ||
#include <test/fuzz/util.h> | ||
|
||
#include <assert.h> | ||
|
||
namespace { | ||
|
||
/** Evaluate a diagram at a specific size, returning the fee as a fraction. | ||
* | ||
* Fees in diagram cannot exceed 2^32, as the returned evaluation could overflow | ||
* the FeeFrac::fee field in the result. */ | ||
FeeFrac EvaluateDiagram(int32_t size, Span<const FeeFrac> diagram) | ||
{ | ||
assert(diagram.size() > 0); | ||
unsigned not_above = 0; | ||
unsigned not_below = diagram.size() - 1; | ||
// If outside the range of diagram, extend begin/end. | ||
if (size < diagram[not_above].size) return {diagram[not_above].fee, 1}; | ||
if (size > diagram[not_below].size) return {diagram[not_below].fee, 1}; | ||
// Perform bisection search to locate the diagram segment that size is in. | ||
while (not_below > not_above + 1) { | ||
unsigned mid = (not_below + not_above) / 2; | ||
if (diagram[mid].size <= size) not_above = mid; | ||
if (diagram[mid].size >= size) not_below = mid; | ||
} | ||
// If the size matches a transition point between segments, return its fee. | ||
if (not_below == not_above) return {diagram[not_below].fee, 1}; | ||
// Otherwise, interpolate. | ||
auto dir_coef = diagram[not_below] - diagram[not_above]; | ||
assert(dir_coef.size > 0); | ||
// Let A = diagram[not_above] and B = diagram[not_below] | ||
const auto& point_a = diagram[not_above]; | ||
// We want to return: | ||
// A.fee + (B.fee - A.fee) / (B.size - A.size) * (size - A.size) | ||
// = A.fee + dir_coef.fee / dir_coef.size * (size - A.size) | ||
// = (A.fee * dir_coef.size + dir_coef.fee * (size - A.size)) / dir_coef.size | ||
assert(size >= point_a.size); | ||
return {point_a.fee * dir_coef.size + dir_coef.fee * (size - point_a.size), dir_coef.size}; | ||
} | ||
|
||
std::weak_ordering CompareFeeFracWithDiagram(const FeeFrac& ff, Span<const FeeFrac> diagram) | ||
{ | ||
return FeeRateCompare(FeeFrac{ff.fee, 1}, EvaluateDiagram(ff.size, diagram)); | ||
} | ||
|
||
std::partial_ordering CompareDiagrams(Span<const FeeFrac> dia1, Span<const FeeFrac> dia2) | ||
{ | ||
bool all_ge = true; | ||
bool all_le = true; | ||
for (const auto p1 : dia1) { | ||
auto cmp = CompareFeeFracWithDiagram(p1, dia2); | ||
if (std::is_lt(cmp)) all_ge = false; | ||
if (std::is_gt(cmp)) all_le = false; | ||
} | ||
for (const auto p2 : dia2) { | ||
auto cmp = CompareFeeFracWithDiagram(p2, dia1); | ||
if (std::is_lt(cmp)) all_le = false; | ||
if (std::is_gt(cmp)) all_ge = false; | ||
} | ||
if (all_ge && all_le) return std::partial_ordering::equivalent; | ||
if (all_ge && !all_le) return std::partial_ordering::greater; | ||
if (!all_ge && all_le) return std::partial_ordering::less; | ||
return std::partial_ordering::unordered; | ||
} | ||
|
||
void PopulateChunks(FuzzedDataProvider& fuzzed_data_provider, std::vector<FeeFrac>& chunks) | ||
{ | ||
chunks.clear(); | ||
|
||
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 50) | ||
{ | ||
chunks.emplace_back(fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(INT32_MIN>>1, INT32_MAX>>1), fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(1, 1000000)); | ||
} | ||
return; | ||
} | ||
|
||
} // namespace | ||
|
||
FUZZ_TARGET(build_and_compare_feerate_diagram) | ||
{ | ||
// Generate a random set of chunks | ||
glozow marked this conversation as resolved.
Show resolved
Hide resolved
|
||
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); | ||
std::vector<FeeFrac> chunks1, chunks2; | ||
FeeFrac empty{0, 0}; | ||
|
||
PopulateChunks(fuzzed_data_provider, chunks1); | ||
PopulateChunks(fuzzed_data_provider, chunks2); | ||
|
||
std::vector<FeeFrac> diagram1{BuildDiagramFromChunks(chunks1)}; | ||
std::vector<FeeFrac> diagram2{BuildDiagramFromChunks(chunks2)}; | ||
|
||
assert(diagram1.front() == empty); | ||
assert(diagram2.front() == empty); | ||
|
||
auto real = CompareFeerateDiagram(diagram1, diagram2); | ||
auto sim = CompareDiagrams(diagram1, diagram2); | ||
assert(real == sim); | ||
|
||
// Do explicit evaluation at up to 1000 points, and verify consistency with the result. | ||
LIMITED_WHILE(fuzzed_data_provider.remaining_bytes(), 1000) { | ||
int32_t size = fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(0, diagram2.back().size); | ||
auto eval1 = EvaluateDiagram(size, diagram1); | ||
auto eval2 = EvaluateDiagram(size, diagram2); | ||
auto cmp = FeeRateCompare(eval1, eval2); | ||
if (std::is_lt(cmp)) assert(!std::is_gt(real)); | ||
if (std::is_gt(cmp)) assert(!std::is_lt(real)); | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In "fuzz: fuzz diagram creation and comparison" (4d6528a):
Nit: The use of
vsize
throughout would help differentiate the mixed occurrences ofdiagram.size()
anddiagram[i].size
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
leaving as is