optimization: look for NULL prevouts in the sorted values

For the 2 input case we simply check them both, like we did with equality.

For the general case, we take advantage of sorting, making invalid value detection constant time instead of linear in the worst case.

> cmake -B build -DBUILD_BENCH=ON -DCMAKE_BUILD_TYPE=Release && cmake --build build -j$(nproc) && build/src/bench/bench_bitcoin -filter='CheckBlockBench|DuplicateInputs|ProcessTransactionBench' -min-time=10000

> C++ compiler .......................... AppleClang 16.0.0.16000026

|            ns/block |             block/s |    err% |     total | benchmark
|--------------------:|--------------------:|--------:|----------:|:----------
|          179,971.00 |            5,556.45 |    0.3% |     11.02 | `CheckBlockBench`

|               ns/op |                op/s |    err% |     total | benchmark
|--------------------:|--------------------:|--------:|----------:|:----------
|          963,177.98 |            1,038.23 |    1.7% |     10.92 | `DuplicateInputs`
|            9,410.90 |          106,259.75 |    0.3% |     11.01 | `ProcessTransactionBench`

> C++ compiler .......................... GNU 13.3.0

|            ns/block |             block/s |    err% |       ins/block |       cyc/block |    IPC |      bra/block |   miss% |     total | benchmark
|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------
|          834,855.94 |            1,197.81 |    0.0% |    6,518,548.86 |    2,656,039.78 |  2.454 |     919,160.84 |    1.5% |     10.78 | `CheckBlockBench`

|               ns/op |                op/s |    err% |          ins/op |          cyc/op |    IPC |         bra/op |   miss% |     total | benchmark
|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------
|        4,261,492.75 |              234.66 |    0.0% |   17,379,823.40 |   13,559,793.33 |  1.282 |   4,265,714.28 |    3.4% |     11.00 | `DuplicateInputs`
|           55,819.53 |           17,914.88 |    0.1% |      227,828.15 |      177,520.09 |  1.283 |      15,184.36 |    0.4% |     10.91 | `ProcessTransactionBench`
This commit is contained in:
Lőrinc 2025-01-25 14:24:02 +01:00
parent cb8c012b87
commit f07c79f034

View file

@ -44,25 +44,27 @@ bool CheckTransaction(const CTransaction& tx, TxValidationState& state)
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-cb-length"); return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-cb-length");
} }
} }
} else if (tx.vin.size() == 2) {
if (tx.vin[0].prevout == tx.vin[1].prevout) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-inputs-duplicate");
}
if (tx.vin[0].prevout.IsNull() || tx.vin[1].prevout.IsNull()) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-prevout-null");
}
} else { } else {
if (tx.vin.size() == 2) { std::vector<COutPoint> sortedPrevouts;
if (tx.vin[0].prevout == tx.vin[1].prevout) { sortedPrevouts.reserve(tx.vin.size());
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-inputs-duplicate"); for (const auto& txin : tx.vin) {
} sortedPrevouts.push_back(txin.prevout);
} else { }
std::vector<COutPoint> sortedPrevouts; std::sort(sortedPrevouts.begin(), sortedPrevouts.end());
sortedPrevouts.reserve(tx.vin.size()); if (std::ranges::adjacent_find(sortedPrevouts) != sortedPrevouts.end()) {
for (const auto& txin : tx.vin) { return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-inputs-duplicate");
sortedPrevouts.push_back(txin.prevout);
}
std::sort(sortedPrevouts.begin(), sortedPrevouts.end());
if (std::ranges::adjacent_find(sortedPrevouts) != sortedPrevouts.end()) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-inputs-duplicate");
}
} }
for (const auto& txin : tx.vin) { for (const auto& in : sortedPrevouts) {
if (txin.prevout.IsNull()) { if (!in.hash.IsNull()) break; // invalid values can only be at the beginning
if (in.IsNull()) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-prevout-null"); return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-prevout-null");
} }
} }