|
| 1 | +// dgb NAUGHTY-PROPAGATION ancestor-punishment KAT. |
| 2 | +// |
| 3 | +// FENCED, additive (no production code touched this slice). Pins |
| 4 | +// src/impl/dgb/coin/naughty_propagation.hpp against the p2pool-dgb-scrypt |
| 5 | +// data.py:543-549 propagation rule: |
| 6 | +// if prev_share_hash and tracker.items[prev_share_hash].naughty: |
| 7 | +// self.naughty = 1 + tracker.items[prev_share_hash].naughty |
| 8 | +// if self.naughty > 6: self.naughty = 0 |
| 9 | +// |
| 10 | +// Every expectation is hand-derived from the oracle Python (the +1-per-generation |
| 11 | +// step and the 6-generation reset clamp), NOT read from the code under test, so |
| 12 | +// the KAT is non-circular. The inline body at share_tracker.hpp:561-577 is NOT |
| 13 | +// yet rewired (delegation is the byte-identity follow-on). Pure unsigned |
| 14 | +// arithmetic -> links only GTest. |
| 15 | +// MUST appear in BOTH this dir CMakeLists.txt AND the build.yml --target |
| 16 | +// allowlist, or it becomes a #143 NOT_BUILT sentinel. |
| 17 | + |
| 18 | +#include <impl/dgb/coin/naughty_propagation.hpp> |
| 19 | + |
| 20 | +#include <cstdint> |
| 21 | +#include <optional> |
| 22 | + |
| 23 | +#include <gtest/gtest.h> |
| 24 | + |
| 25 | +using dgb::naughty_child_generation; |
| 26 | +using dgb::propagate_naughty_from_parent; |
| 27 | + |
| 28 | +// --- bare child generation = 1 + parent, clamp >6 -> 0 (data.py:547-549) ---- |
| 29 | +// Hand-derived table over every reachable parent generation. Parent naughty in |
| 30 | +// the live chain is always in [1,6] (it was itself produced by this same rule), |
| 31 | +// but we pin 7 too as a defensive over-the-clamp input. |
| 32 | +TEST(DgbNaughtyPropagation, ChildGenerationTable) |
| 33 | +{ |
| 34 | + // parent gen 1 (the bad-reward share itself) -> child gen 2 |
| 35 | + EXPECT_EQ(naughty_child_generation(1u), 2u); |
| 36 | + EXPECT_EQ(naughty_child_generation(2u), 3u); |
| 37 | + EXPECT_EQ(naughty_child_generation(3u), 4u); |
| 38 | + EXPECT_EQ(naughty_child_generation(4u), 5u); |
| 39 | + // gen 5 -> 6 is the LAST kept generation (6 is not > 6) |
| 40 | + EXPECT_EQ(naughty_child_generation(5u), 6u); |
| 41 | + // gen 6 -> would-be 7, clamps to 0: the 6th-generation descendant is forgiven |
| 42 | + EXPECT_EQ(naughty_child_generation(6u), 0u); |
| 43 | + // defensive: an out-of-range parent (7) also wraps to 0 (8 > 6) |
| 44 | + EXPECT_EQ(naughty_child_generation(7u), 0u); |
| 45 | +} |
| 46 | + |
| 47 | +// --- the guarded propagation: nullopt when parent not naughty (data.py:543) -- |
| 48 | +TEST(DgbNaughtyPropagation, NonNaughtyParentLeavesChildUntouched) |
| 49 | +{ |
| 50 | + // parent.naughty == 0 is falsy in Python -> no assignment happens. |
| 51 | + EXPECT_FALSE(propagate_naughty_from_parent(0u).has_value()); |
| 52 | +} |
| 53 | + |
| 54 | +// --- guarded propagation matches the bare table for naughty parents --------- |
| 55 | +TEST(DgbNaughtyPropagation, NaughtyParentPropagates) |
| 56 | +{ |
| 57 | + EXPECT_EQ(propagate_naughty_from_parent(1u), std::optional<std::uint32_t>(2u)); |
| 58 | + EXPECT_EQ(propagate_naughty_from_parent(2u), std::optional<std::uint32_t>(3u)); |
| 59 | + EXPECT_EQ(propagate_naughty_from_parent(3u), std::optional<std::uint32_t>(4u)); |
| 60 | + EXPECT_EQ(propagate_naughty_from_parent(4u), std::optional<std::uint32_t>(5u)); |
| 61 | + EXPECT_EQ(propagate_naughty_from_parent(5u), std::optional<std::uint32_t>(6u)); |
| 62 | + // 6th-generation descendant forgiven (assigned 0, NOT left untouched) |
| 63 | + EXPECT_EQ(propagate_naughty_from_parent(6u), std::optional<std::uint32_t>(0u)); |
| 64 | +} |
| 65 | + |
| 66 | +// --- non-circular: a full 8-generation chain walk reproduces the oracle ------ |
| 67 | +// Seed share is itself naughty (gen 1, excessive reward). Walk forward applying |
| 68 | +// the rule and compare against the hand-derived sequence the oracle produces: |
| 69 | +// 1 (seed, bad reward), then each child = clamp(1+parent): |
| 70 | +// 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 0(forgiven) -> 0(parent 0 = clean, untouched) |
| 71 | +TEST(DgbNaughtyPropagation, EightGenerationChainMatchesOracleSequence) |
| 72 | +{ |
| 73 | + const std::uint32_t expected[8] = {1u, 2u, 3u, 4u, 5u, 6u, 0u, 0u}; |
| 74 | + |
| 75 | + std::uint32_t gen = 1u; // seed: excessive-reward share, naughty=1 |
| 76 | + EXPECT_EQ(gen, expected[0]); |
| 77 | + for (int i = 1; i < 8; ++i) { |
| 78 | + auto next = propagate_naughty_from_parent(gen); |
| 79 | + if (next.has_value()) { |
| 80 | + gen = *next; // parent was naughty -> inherit/clamp |
| 81 | + } else { |
| 82 | + gen = 0u; // parent clean -> child stays at its default 0 |
| 83 | + } |
| 84 | + EXPECT_EQ(gen, expected[i]) << "generation " << i; |
| 85 | + } |
| 86 | +} |
0 commit comments