|
| 1 | +import pytest |
| 2 | +import time |
| 3 | +from bittensor_commit_reveal import get_encrypted_commit |
| 4 | + |
| 5 | +SUBTENSOR_PULSE_DELAY = 24 |
| 6 | +PERIOD = 3 # Drand period in seconds |
| 7 | +GENESIS_TIME = 1692803367 |
| 8 | + |
| 9 | + |
| 10 | +def test_get_encrypted_commits(): |
| 11 | + uids = [1, 2] |
| 12 | + weights = [11, 22] |
| 13 | + version_key = 50 |
| 14 | + tempo = 100 |
| 15 | + current_block = 1000 |
| 16 | + netuid = 1 |
| 17 | + reveal_period = 2 |
| 18 | + block_time = 12 |
| 19 | + |
| 20 | + start_time = int(time.time()) |
| 21 | + ct_pybytes, reveal_round = get_encrypted_commit( |
| 22 | + uids, |
| 23 | + weights, |
| 24 | + version_key, |
| 25 | + tempo, |
| 26 | + current_block, |
| 27 | + netuid, |
| 28 | + reveal_period, |
| 29 | + block_time, |
| 30 | + ) |
| 31 | + |
| 32 | + # Basic checks |
| 33 | + assert ( |
| 34 | + ct_pybytes is not None and len(ct_pybytes) > 0 |
| 35 | + ), "Ciphertext should not be empty" |
| 36 | + assert reveal_round > 0, "Reveal round should be positive" |
| 37 | + |
| 38 | + expected_reveal_round, _, _ = compute_expected_reveal_round( |
| 39 | + start_time, tempo, current_block, netuid, reveal_period, block_time |
| 40 | + ) |
| 41 | + |
| 42 | + # The reveal_round should be close to what we predict |
| 43 | + assert ( |
| 44 | + abs(reveal_round - expected_reveal_round) <= 1 |
| 45 | + ), f"Reveal round {reveal_round} not close to expected {expected_reveal_round}" |
| 46 | + |
| 47 | + |
| 48 | +def test_generate_commit_success(): |
| 49 | + uids = [1, 2, 3] |
| 50 | + values = [10, 20, 30] |
| 51 | + version_key = 42 |
| 52 | + tempo = 50 |
| 53 | + current_block = 500 |
| 54 | + netuid = 100 |
| 55 | + subnet_reveal_period_epochs = 2 |
| 56 | + block_time = 12 |
| 57 | + |
| 58 | + start_time = int(time.time()) |
| 59 | + ct_pybytes, reveal_round = get_encrypted_commit( |
| 60 | + uids, |
| 61 | + values, |
| 62 | + version_key, |
| 63 | + tempo, |
| 64 | + current_block, |
| 65 | + netuid, |
| 66 | + subnet_reveal_period_epochs, |
| 67 | + block_time, |
| 68 | + ) |
| 69 | + |
| 70 | + assert ( |
| 71 | + ct_pybytes is not None and len(ct_pybytes) > 0 |
| 72 | + ), "Ciphertext should not be empty" |
| 73 | + assert reveal_round > 0, "Reveal round should be positive" |
| 74 | + |
| 75 | + expected_reveal_round, expected_reveal_time, time_until_reveal = ( |
| 76 | + compute_expected_reveal_round( |
| 77 | + start_time, |
| 78 | + tempo, |
| 79 | + current_block, |
| 80 | + netuid, |
| 81 | + subnet_reveal_period_epochs, |
| 82 | + block_time, |
| 83 | + ) |
| 84 | + ) |
| 85 | + |
| 86 | + assert ( |
| 87 | + abs(reveal_round - expected_reveal_round) <= 1 |
| 88 | + ), f"Reveal round {reveal_round} differs from expected {expected_reveal_round}" |
| 89 | + |
| 90 | + required_lead_time = SUBTENSOR_PULSE_DELAY * PERIOD |
| 91 | + computed_reveal_time = ( |
| 92 | + GENESIS_TIME + (reveal_round + SUBTENSOR_PULSE_DELAY) * PERIOD |
| 93 | + ) |
| 94 | + assert computed_reveal_time - start_time >= required_lead_time, ( |
| 95 | + "Not enough lead time before reveal. " |
| 96 | + f"computed_reveal_time={computed_reveal_time}, start_time={start_time}, required={required_lead_time}" |
| 97 | + ) |
| 98 | + |
| 99 | + assert ( |
| 100 | + time_until_reveal >= SUBTENSOR_PULSE_DELAY * PERIOD |
| 101 | + ), f"time_until_reveal {time_until_reveal} is less than required {SUBTENSOR_PULSE_DELAY * PERIOD}" |
| 102 | + |
| 103 | + |
| 104 | +@pytest.mark.asyncio |
| 105 | +async def test_generate_commit_various_tempos(): |
| 106 | + NETUID = 1 |
| 107 | + CURRENT_BLOCK = 100_000 |
| 108 | + SUBNET_REVEAL_PERIOD_EPOCHS = 1 |
| 109 | + BLOCK_TIME = 6 |
| 110 | + TEMPOS = [10, 50, 100, 250, 360, 500, 750, 1000] |
| 111 | + |
| 112 | + uids = [0] |
| 113 | + values = [100] |
| 114 | + version_key = 1 |
| 115 | + |
| 116 | + for tempo in TEMPOS: |
| 117 | + start_time = int(time.time()) |
| 118 | + |
| 119 | + ct_pybytes, reveal_round = get_encrypted_commit( |
| 120 | + uids, |
| 121 | + values, |
| 122 | + version_key, |
| 123 | + tempo, |
| 124 | + CURRENT_BLOCK, |
| 125 | + NETUID, |
| 126 | + SUBNET_REVEAL_PERIOD_EPOCHS, |
| 127 | + BLOCK_TIME, |
| 128 | + ) |
| 129 | + |
| 130 | + assert len(ct_pybytes) > 0, f"Ciphertext is empty for tempo {tempo}" |
| 131 | + assert reveal_round > 0, f"Reveal round is zero or negative for tempo {tempo}" |
| 132 | + |
| 133 | + expected_reveal_round, _, time_until_reveal = compute_expected_reveal_round( |
| 134 | + start_time, |
| 135 | + tempo, |
| 136 | + CURRENT_BLOCK, |
| 137 | + NETUID, |
| 138 | + SUBNET_REVEAL_PERIOD_EPOCHS, |
| 139 | + BLOCK_TIME, |
| 140 | + ) |
| 141 | + |
| 142 | + assert ( |
| 143 | + abs(reveal_round - expected_reveal_round) <= 1 |
| 144 | + ), f"Tempo {tempo}: reveal_round {reveal_round} not close to expected {expected_reveal_round}" |
| 145 | + |
| 146 | + computed_reveal_time = ( |
| 147 | + GENESIS_TIME + (reveal_round + SUBTENSOR_PULSE_DELAY) * PERIOD |
| 148 | + ) |
| 149 | + required_lead_time = SUBTENSOR_PULSE_DELAY * PERIOD |
| 150 | + |
| 151 | + assert computed_reveal_time - start_time >= required_lead_time, ( |
| 152 | + f"Tempo {tempo}: Not enough lead time: reveal_time={computed_reveal_time}, " |
| 153 | + f"start_time={start_time}, required={required_lead_time}" |
| 154 | + ) |
| 155 | + |
| 156 | + assert ( |
| 157 | + time_until_reveal >= SUBTENSOR_PULSE_DELAY * PERIOD |
| 158 | + ), f"Tempo {tempo}: time_until_reveal {time_until_reveal} is less than required {SUBTENSOR_PULSE_DELAY * PERIOD}" |
| 159 | + |
| 160 | + |
| 161 | +def compute_expected_reveal_round( |
| 162 | + now: int, |
| 163 | + tempo: int, |
| 164 | + current_block: int, |
| 165 | + netuid: int, |
| 166 | + subnet_reveal_period_epochs: int, |
| 167 | + block_time: int, |
| 168 | +): |
| 169 | + tempo_plus_one = tempo + 1 |
| 170 | + netuid_plus_one = netuid + 1 |
| 171 | + block_with_offset = current_block + netuid_plus_one |
| 172 | + current_epoch = block_with_offset // tempo_plus_one |
| 173 | + |
| 174 | + # Initial guess for reveal_epoch |
| 175 | + reveal_epoch = current_epoch + subnet_reveal_period_epochs |
| 176 | + reveal_block_number = reveal_epoch * tempo_plus_one - netuid_plus_one |
| 177 | + |
| 178 | + # Compute blocks_until_reveal, ensure non-negative |
| 179 | + blocks_until_reveal = reveal_block_number - current_block |
| 180 | + if blocks_until_reveal < 0: |
| 181 | + blocks_until_reveal = 0 |
| 182 | + time_until_reveal = blocks_until_reveal * block_time |
| 183 | + |
| 184 | + # Adjust until we have enough lead time (at least SUBTENSOR_PULSE_DELAY pulses * period seconds) |
| 185 | + while time_until_reveal < SUBTENSOR_PULSE_DELAY * PERIOD: |
| 186 | + reveal_epoch += 1 |
| 187 | + reveal_block_number = reveal_epoch * tempo_plus_one - netuid_plus_one |
| 188 | + blocks_until_reveal = reveal_block_number - current_block |
| 189 | + if blocks_until_reveal < 0: |
| 190 | + blocks_until_reveal = 0 |
| 191 | + time_until_reveal = blocks_until_reveal * block_time |
| 192 | + |
| 193 | + reveal_time = now + time_until_reveal |
| 194 | + reveal_round = ( |
| 195 | + (reveal_time - GENESIS_TIME + PERIOD - 1) // PERIOD |
| 196 | + ) - SUBTENSOR_PULSE_DELAY |
| 197 | + return reveal_round, reveal_time, time_until_reveal |
0 commit comments