Skip to content

Commit ac0ce8e

Browse files
Merge pull request #10 from opentensor/release/0.1.0
Release/0.1.0
2 parents 85d3c12 + b0b7592 commit ac0ce8e

File tree

5 files changed

+284
-26
lines changed

5 files changed

+284
-26
lines changed

.circleci/config.yml

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
version: 2.1
2+
3+
orbs:
4+
python: circleci/python@2.1.1
5+
6+
jobs:
7+
ruff:
8+
resource_class: small
9+
parameters:
10+
python-version:
11+
type: string
12+
docker:
13+
- image: cimg/python:<< parameters.python-version >>
14+
steps:
15+
- checkout
16+
- run:
17+
name: Install Ruff
18+
command: pip install ruff
19+
- run:
20+
name: Run Ruff
21+
command: ruff check .
22+
23+
build-and-test:
24+
resource_class: medium
25+
parallelism: 2
26+
parameters:
27+
python-version:
28+
type: string
29+
docker:
30+
- image: cimg/python:<< parameters.python-version >>
31+
steps:
32+
- checkout
33+
- run:
34+
name: Set Up Virtual Environment
35+
command: |
36+
curl https://sh.rustup.rs -sSf | sh -s -- -y
37+
. "$HOME/.cargo/env"
38+
python -m venv .venv
39+
. .venv/bin/activate
40+
python -m pip install --upgrade pip
41+
python -m pip install '.[dev]'
42+
- run:
43+
name: Run Tests
44+
command: |
45+
. .venv/bin/activate
46+
pytest tests/
47+
- store_test_results:
48+
path: test-results
49+
- store_artifacts:
50+
path: test-results
51+
52+
workflows:
53+
test-and-lint:
54+
jobs:
55+
- ruff:
56+
python-version: "3.9.13"
57+
- build-and-test:
58+
matrix:
59+
parameters:
60+
python-version: ["3.9", "3.10", "3.11", "3.12"]

CHANGELOG.MD

+8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## v0.1.0 /2024-12-12
4+
5+
## What's Changed
6+
* fix reveal_round calculation edge cases by @JohnReedV in https://github.com/opentensor/bittensor-commit-reveal/pull/8
7+
* Adds circleci test config by @ibraheem-opentensor in https://github.com/opentensor/bittensor-commit-reveal/pull/9
8+
9+
**Full Changelog**: https://github.com/opentensor/bittensor-commit-reveal/compare/v0.1.0a1...v0.1.0
10+
311
## v0.1.0a1 /2024-12-03
412

513
## What's Changed

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "bittensor-commit-reveal"
3-
version = "0.1.0a1"
3+
version = "0.1.0"
44
description = ""
55
readme = "README.md"
66
license = {file = "LICENSE"}

src/lib.rs

+18-25
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,14 @@ async fn generate_commit(
3333
block_time: u64,
3434
) -> Result<(Vec<u8>, u64), (std::io::Error, String)> {
3535
// Steps comes from here https://github.com/opentensor/subtensor/pull/982/files#diff-7261bf1c7f19fc66a74c1c644ec2b4b277a341609710132fb9cd5f622350a6f5R120-R131
36-
// 1 Instantiate payload
36+
// Instantiate payload
3737
let payload = WeightsTlockPayload {
3838
uids,
3939
values,
4040
version_key,
4141
};
42-
43-
// 2 Serialize payload
4442
let serialized_payload = payload.encode();
4543

46-
// Calculate reveal_round
47-
// all of 3 variables are constants for drand quicknet
4844
let period = 3;
4945
let genesis_time = 1692803367;
5046
let public_key = "83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a";
@@ -54,31 +50,29 @@ async fn generate_commit(
5450
.unwrap()
5551
.as_secs();
5652

57-
// Compute the current epoch index
5853
let tempo_plus_one = tempo + 1;
5954
let netuid_plus_one = (netuid as u64) + 1;
6055
let block_with_offset = current_block + netuid_plus_one;
6156
let current_epoch = block_with_offset / tempo_plus_one;
6257

63-
// Compute the reveal epoch
64-
let reveal_epoch = current_epoch + subnet_reveal_period_epochs;
65-
66-
// Compute the block number when the reveal epoch starts
67-
let reveal_block_number = reveal_epoch * tempo_plus_one - netuid_plus_one;
68-
69-
// Compute the number of blocks until the reveal epoch
70-
let blocks_until_reveal = reveal_block_number.saturating_sub(current_block);
71-
72-
// Compute the time until the reveal in seconds
73-
let time_until_reveal = blocks_until_reveal * block_time;
58+
// Calculate reveal epoch and ensure enough time for SUBTENSOR_PULSE_DELAY pulses
59+
let mut reveal_epoch = current_epoch + subnet_reveal_period_epochs;
60+
let mut reveal_block_number = reveal_epoch * tempo_plus_one - netuid_plus_one;
61+
let mut blocks_until_reveal = reveal_block_number.saturating_sub(current_block);
62+
let mut time_until_reveal = blocks_until_reveal * block_time;
63+
64+
// Ensure at least SUBTENSOR_PULSE_DELAY * period seconds lead time
65+
while time_until_reveal < SUBTENSOR_PULSE_DELAY * period {
66+
reveal_epoch += 1;
67+
reveal_block_number = reveal_epoch * tempo_plus_one - netuid_plus_one;
68+
blocks_until_reveal = reveal_block_number.saturating_sub(current_block);
69+
time_until_reveal = blocks_until_reveal * block_time;
70+
}
7471

75-
// Compute the reveal time in seconds since UNIX_EPOCH
7672
let reveal_time = now + time_until_reveal;
77-
78-
// Compute the reveal round, ensuring we round up
7973
let reveal_round = ((reveal_time - genesis_time + period - 1) / period) - SUBTENSOR_PULSE_DELAY;
8074

81-
// 3. Deserialize the public key
75+
// Deserialize public key
8276
let pub_key_bytes = hex::decode(public_key).map_err(|e| {
8377
(
8478
std::io::Error::new(std::io::ErrorKind::InvalidData, format!("{:?}", e)),
@@ -94,15 +88,15 @@ async fn generate_commit(
9488
)
9589
})?;
9690

97-
// 4 Create identity
91+
// Create identity from reveal_round
9892
let message = {
9993
let mut hasher = sha2::Sha256::new();
10094
hasher.update(reveal_round.to_be_bytes());
10195
hasher.finalize().to_vec()
10296
};
10397
let identity = Identity::new(b"", vec![message]);
10498

105-
// 5. Encryption via tle with t-lock under the hood
99+
// Encrypt payload
106100
let esk = [2; 32];
107101
let ct = tle::<TinyBLS381, AESGCMStreamCipherProvider, OsRng>(
108102
pub_key,
@@ -118,7 +112,7 @@ async fn generate_commit(
118112
)
119113
})?;
120114

121-
// 6. Compress ct
115+
// Compress ciphertext
122116
let mut ct_bytes: Vec<u8> = Vec::new();
123117
ct.serialize_compressed(&mut ct_bytes).map_err(|e| {
124118
(
@@ -127,7 +121,6 @@ async fn generate_commit(
127121
)
128122
})?;
129123

130-
// 7. Return result
131124
Ok((ct_bytes, reveal_round))
132125
}
133126

src/tests/test_commit_reveal.py

+197
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
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

Comments
 (0)