|
| 1 | +"""Test vectors for Discovery v5 distance computations. |
| 2 | +
|
| 3 | +XOR distance and log2 distance are the foundation of Kademlia routing. |
| 4 | +Every client must compute identical distances for correct peer discovery |
| 5 | +and k-bucket assignment. |
| 6 | +""" |
| 7 | + |
| 8 | +import pytest |
| 9 | +from consensus_testing import NetworkingCodecTestFiller |
| 10 | + |
| 11 | +pytestmark = pytest.mark.valid_until("Devnet") |
| 12 | + |
| 13 | +# Official devp2p spec node IDs. |
| 14 | +NODE_A = "0xaaaa8419e9f49d0083561b48287df592939a8d19947d8c0ef88f2a4856a69fbb" |
| 15 | +NODE_B = "0xbbbb9d047f0488c0b5a93c1c3f2d8bafc7c8ff337024a55434a0d0555de64db9" |
| 16 | + |
| 17 | +ZERO = "0x" + "00" * 32 |
| 18 | +MAX = "0x" + "ff" * 32 |
| 19 | +ONE = "0x" + "00" * 31 + "01" |
| 20 | +TWO = "0x" + "00" * 31 + "02" |
| 21 | +HIGH_BIT = "0x80" + "00" * 31 |
| 22 | +LOW_BYTE_80 = "0x" + "00" * 31 + "80" |
| 23 | +BUCKET_9 = "0x" + "00" * 30 + "0100" |
| 24 | + |
| 25 | + |
| 26 | +# --- XOR distance --- |
| 27 | + |
| 28 | + |
| 29 | +def test_xor_distance_self(networking_codec: NetworkingCodecTestFiller) -> None: |
| 30 | + """XOR distance to self is always zero (identity property).""" |
| 31 | + networking_codec(codec_name="xor_distance", input={"nodeA": NODE_A, "nodeB": NODE_A}) |
| 32 | + |
| 33 | + |
| 34 | +def test_xor_distance_symmetric(networking_codec: NetworkingCodecTestFiller) -> None: |
| 35 | + """d(A, B) == d(B, A) (symmetry property).""" |
| 36 | + networking_codec(codec_name="xor_distance", input={"nodeA": NODE_A, "nodeB": NODE_B}) |
| 37 | + |
| 38 | + |
| 39 | +def test_xor_distance_symmetric_reverse( |
| 40 | + networking_codec: NetworkingCodecTestFiller, |
| 41 | +) -> None: |
| 42 | + """Reverse order produces identical distance.""" |
| 43 | + networking_codec(codec_name="xor_distance", input={"nodeA": NODE_B, "nodeB": NODE_A}) |
| 44 | + |
| 45 | + |
| 46 | +def test_xor_distance_max(networking_codec: NetworkingCodecTestFiller) -> None: |
| 47 | + """XOR of zero and all-ones produces maximum distance (2^256 - 1).""" |
| 48 | + networking_codec(codec_name="xor_distance", input={"nodeA": ZERO, "nodeB": MAX}) |
| 49 | + |
| 50 | + |
| 51 | +def test_xor_distance_adjacent(networking_codec: NetworkingCodecTestFiller) -> None: |
| 52 | + """XOR of 0x01 and 0x02 is 0x03 (lowest bits).""" |
| 53 | + networking_codec(codec_name="xor_distance", input={"nodeA": ONE, "nodeB": TWO}) |
| 54 | + |
| 55 | + |
| 56 | +def test_xor_distance_high_bit(networking_codec: NetworkingCodecTestFiller) -> None: |
| 57 | + """Single high bit difference. Distance = 2^255.""" |
| 58 | + networking_codec(codec_name="xor_distance", input={"nodeA": HIGH_BIT, "nodeB": ZERO}) |
| 59 | + |
| 60 | + |
| 61 | +# --- Log2 distance (k-bucket assignment) --- |
| 62 | + |
| 63 | + |
| 64 | +def test_log2_distance_self(networking_codec: NetworkingCodecTestFiller) -> None: |
| 65 | + """Log2 distance to self is 0.""" |
| 66 | + networking_codec(codec_name="log2_distance", input={"nodeA": NODE_A, "nodeB": NODE_A}) |
| 67 | + |
| 68 | + |
| 69 | +def test_log2_distance_spec_nodes( |
| 70 | + networking_codec: NetworkingCodecTestFiller, |
| 71 | +) -> None: |
| 72 | + """Log2 distance between official spec nodes A and B is 253.""" |
| 73 | + networking_codec(codec_name="log2_distance", input={"nodeA": NODE_A, "nodeB": NODE_B}) |
| 74 | + |
| 75 | + |
| 76 | +def test_log2_distance_max(networking_codec: NetworkingCodecTestFiller) -> None: |
| 77 | + """Maximum log2 distance is 256 (all bits differ).""" |
| 78 | + networking_codec(codec_name="log2_distance", input={"nodeA": ZERO, "nodeB": MAX}) |
| 79 | + |
| 80 | + |
| 81 | +def test_log2_distance_bucket_1(networking_codec: NetworkingCodecTestFiller) -> None: |
| 82 | + """Single lowest bit difference lands in bucket 1.""" |
| 83 | + networking_codec(codec_name="log2_distance", input={"nodeA": ZERO, "nodeB": ONE}) |
| 84 | + |
| 85 | + |
| 86 | +def test_log2_distance_bucket_2(networking_codec: NetworkingCodecTestFiller) -> None: |
| 87 | + """Bit 1 set lands in bucket 2.""" |
| 88 | + networking_codec(codec_name="log2_distance", input={"nodeA": ZERO, "nodeB": TWO}) |
| 89 | + |
| 90 | + |
| 91 | +def test_log2_distance_bucket_8(networking_codec: NetworkingCodecTestFiller) -> None: |
| 92 | + """Byte boundary: 0x80 in last byte lands in bucket 8.""" |
| 93 | + networking_codec(codec_name="log2_distance", input={"nodeA": ZERO, "nodeB": LOW_BYTE_80}) |
| 94 | + |
| 95 | + |
| 96 | +def test_log2_distance_bucket_9(networking_codec: NetworkingCodecTestFiller) -> None: |
| 97 | + """0x0100 in last two bytes lands in bucket 9.""" |
| 98 | + networking_codec(codec_name="log2_distance", input={"nodeA": ZERO, "nodeB": BUCKET_9}) |
| 99 | + |
| 100 | + |
| 101 | +def test_log2_distance_bucket_256( |
| 102 | + networking_codec: NetworkingCodecTestFiller, |
| 103 | +) -> None: |
| 104 | + """Highest bit only (0x80 in first byte) lands in bucket 256.""" |
| 105 | + networking_codec(codec_name="log2_distance", input={"nodeA": ZERO, "nodeB": HIGH_BIT}) |
0 commit comments