Skip to content

Commit baf6301

Browse files
authored
Merge pull request #47 from opentensor/Release/1.0.0
Release/1.0.0
2 parents 8f78f2a + fd4962e commit baf6301

File tree

8 files changed

+99
-42
lines changed

8 files changed

+99
-42
lines changed

CHANGELOG.MD

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Changelog
22

3+
## v1.0.0 /2025-08-07
4+
5+
## What's Changed
6+
* fix annotation by @basfroman in https://github.com/opentensor/bittensor-drand/pull/32
7+
* Add `arm` wheels by @basfroman in https://github.com/opentensor/bittensor-drand/pull/34
8+
* CR Improvements by @JohnReedV in https://github.com/opentensor/bittensor-drand/pull/45
9+
* Add hotkey to `WeightsTlockPayload` by @JohnReedV in https://github.com/opentensor/bittensor-drand/pull/44
10+
* Update FFI calculations by @JohnReedV in https://github.com/opentensor/bittensor-drand/pull/43
11+
12+
13+
**Full Changelog**: https://github.com/opentensor/bittensor-drand/compare/v0.5.0...v1.0.0
14+
315
## v0.5.1 /2025-06-11
416

517
## What's Changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "bittensor-drand"
3-
version = "0.5.0"
3+
version = "1.0.0"
44
edition = "2021"
55

66
[lib]

bittensor_drand/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ def get_encrypted_commit(
1717
current_block: int,
1818
netuid: int,
1919
subnet_reveal_period_epochs: int,
20-
block_time: Union[int, float] = 12,
20+
block_time: Union[int, float],
21+
hotkey: bytes,
2122
) -> tuple[bytes, int]:
2223
"""Returns encrypted commit and target round for `commit_crv3_weights` extrinsic.
2324
@@ -30,6 +31,7 @@ def get_encrypted_commit(
3031
netuid: The network unique identifier (NetUID) for the subnet.
3132
subnet_reveal_period_epochs: Number of epochs after which the reveal will be performed. Corresponds to the hyperparameter `commit_reveal_weights_interval` of the subnet. In epochs.
3233
block_time: Amount of time in seconds for one block. Defaults to 12 seconds.
34+
hotkey: The hotkey of a neuron-committer is represented as public_key bytes (wallet.hotkey.public_key).
3335
3436
Returns:
3537
commit (bytes): Raw bytes of the encrypted and compressed uids & weights values for setting weights.
@@ -47,6 +49,7 @@ def get_encrypted_commit(
4749
netuid,
4850
subnet_reveal_period_epochs,
4951
block_time,
52+
hotkey,
5053
)
5154

5255

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "bittensor-drand"
3-
version = "0.5.1"
3+
version = "1.0.0"
44
description = ""
55
readme = "README.md"
66
license = {file = "LICENSE"}

src/drand.rs

Lines changed: 57 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
22
use codec::{Decode, Encode};
33

4-
use rand_core::OsRng;
4+
use rand_core::{OsRng, RngCore};
55
use serde::Deserialize;
66
use sha2::Digest;
77
use std::os::raw::c_char;
@@ -14,7 +14,6 @@ use tle::{
1414
};
1515
use w3f_bls::EngineBLS;
1616

17-
pub const SUBTENSOR_PULSE_DELAY: u64 = 24; //Drand rounds amount
1817
const PUBLIC_KEY: &str = "83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a";
1918
pub const GENESIS_TIME: u64 = 1692803367;
2019
pub const DRAND_PERIOD: u64 = 3;
@@ -34,6 +33,7 @@ const ENDPOINTS: [&str; 5] = [
3433

3534
#[derive(Encode, Decode, Debug, PartialEq)]
3635
pub struct WeightsTlockPayload {
36+
pub hotkey: Vec<u8>,
3737
pub uids: Vec<u16>,
3838
pub values: Vec<u16>,
3939
pub version_key: u64,
@@ -94,7 +94,8 @@ pub fn encrypt_and_compress(
9494
let identity = Identity::new(b"", vec![message]);
9595

9696
// Encrypt payload
97-
let esk = [2; 32];
97+
let mut esk = [0u8; 32];
98+
OsRng.fill_bytes(&mut esk);
9899
let ct = tle::<TinyBLS381, AESGCMStreamCipherProvider, OsRng>(
99100
pub_key,
100101
esk,
@@ -178,6 +179,7 @@ pub fn decrypt_and_decompress(
178179
/// * `netuid` - A u16 representing the network's unique identifier.
179180
/// * `subnet_reveal_period_epochs` - A u64 indicating the number of epochs before reveal.
180181
/// * `block_time` - Duration of each block in seconds as u64.
182+
/// * `hotkey` - The hotkey of the committing validator
181183
///
182184
/// # Returns
183185
///
@@ -194,49 +196,66 @@ pub fn generate_commit(
194196
netuid: u16,
195197
subnet_reveal_period_epochs: u64,
196198
block_time: f64,
199+
hotkey: Vec<u8>,
197200
) -> Result<(Vec<u8>, u64), (std::io::Error, String)> {
198-
// Steps comes from here https://github.com/opentensor/subtensor/pull/982/files#diff-7261bf1c7f19fc66a74c1c644ec2b4b277a341609710132fb9cd5f622350a6f5R120-R131
199-
200-
// Instantiate payload
201-
let payload = WeightsTlockPayload {
202-
uids,
203-
values,
204-
version_key,
205-
};
206-
let serialized_payload = payload.encode();
207-
208-
let now = SystemTime::now()
201+
//----------------------------------------------------------------------
202+
// 1 ▸ derive the first block of the reveal epoch
203+
//----------------------------------------------------------------------
204+
let tempo_plus_one = tempo.saturating_add(1);
205+
let netuid_plus_one = netuid as u64 + 1;
206+
207+
// epoch index of `current_block`
208+
let current_epoch = (current_block + netuid_plus_one) / tempo_plus_one;
209+
210+
// epoch index in which the commit must be revealed
211+
let reveal_epoch = current_epoch + subnet_reveal_period_epochs;
212+
213+
// very first block *inside* the reveal epoch
214+
let first_reveal_blk = reveal_epoch
215+
.saturating_mul(tempo_plus_one)
216+
.saturating_sub(netuid_plus_one);
217+
218+
//----------------------------------------------------------------------
219+
// 2 ▸ decide in which *block* we want the pulse to be emitted
220+
// – we aim for first_reveal_blk + 3
221+
//----------------------------------------------------------------------
222+
pub const SECURITY_BLOCK_OFFSET: u64 = 3;
223+
let target_ingest_blk = first_reveal_blk.saturating_add(SECURITY_BLOCK_OFFSET);
224+
let blocks_until_ingest = target_ingest_blk.saturating_sub(current_block);
225+
let secs_until_ingest = blocks_until_ingest as f64 * block_time;
226+
227+
//----------------------------------------------------------------------
228+
// 3 ▸ convert the desired timestamp into a DRAND round
229+
//----------------------------------------------------------------------
230+
let now_secs = SystemTime::now()
209231
.duration_since(UNIX_EPOCH)
210232
.unwrap()
211233
.as_secs_f64();
212234

213-
let tempo_plus_one = tempo + 1;
214-
let netuid_plus_one = (netuid as u64) + 1;
215-
let block_with_offset = current_block + netuid_plus_one;
216-
let current_epoch = block_with_offset / tempo_plus_one;
217-
218-
let mut reveal_epoch = current_epoch + subnet_reveal_period_epochs;
219-
let mut reveal_block_number = reveal_epoch * tempo_plus_one - netuid_plus_one;
220-
let mut blocks_until_reveal = reveal_block_number - current_block;
221-
let mut time_until_reveal = (blocks_until_reveal as f64) * block_time;
222-
223-
//
224-
while time_until_reveal < (SUBTENSOR_PULSE_DELAY * DRAND_PERIOD) as f64 {
225-
reveal_epoch += 1;
226-
reveal_block_number = reveal_epoch * tempo_plus_one - netuid_plus_one;
227-
blocks_until_reveal = reveal_block_number - current_block;
228-
time_until_reveal = (blocks_until_reveal as f64) * block_time;
235+
let target_secs = now_secs + secs_until_ingest;
236+
237+
// Round ***down*** so we never request a not‑yet‑ingested pulse.
238+
let mut reveal_round =
239+
((target_secs - GENESIS_TIME as f64) / DRAND_PERIOD as f64).floor() as u64;
240+
241+
if reveal_round < 1 {
242+
reveal_round = 1;
229243
}
230244

231-
let reveal_time = now + time_until_reveal;
232-
let reveal_round = ((reveal_time - GENESIS_TIME as f64) / DRAND_PERIOD as f64).ceil() as u64
233-
- SUBTENSOR_PULSE_DELAY;
245+
//----------------------------------------------------------------------
246+
// 5 ▸ build & encrypt payload
247+
//----------------------------------------------------------------------
248+
let payload = WeightsTlockPayload {
249+
hotkey,
250+
uids,
251+
values,
252+
version_key,
253+
};
234254

235-
let ct_bytes = encrypt_and_compress(&serialized_payload, reveal_round)?;
255+
let ct_bytes = encrypt_and_compress(&payload.encode(), reveal_round)?;
236256

237257
Ok((ct_bytes, reveal_round))
238258
}
239-
240259
/// Encrypts a string-based commitment using Drand timelock encryption for a future reveal round.
241260
///
242261
/// This function encodes the input `data` and calculates the corresponding Drand round number
@@ -470,6 +489,7 @@ mod tests {
470489
let current_block = 1000;
471490
let netuid = 1;
472491
let reveal_epochs = 3;
492+
let hotkey = vec![1, 2, 3];
473493

474494
let (encrypted, reveal_round) = generate_commit(
475495
uids.clone(),
@@ -480,6 +500,7 @@ mod tests {
480500
netuid,
481501
reveal_epochs,
482502
12.0,
503+
hotkey.clone(),
483504
)
484505
.expect("Commit generation failed");
485506

@@ -499,6 +520,7 @@ mod tests {
499520
assert_eq!(payload.uids, uids);
500521
assert_eq!(payload.values, values);
501522
assert_eq!(payload.version_key, version_key);
523+
assert_eq!(payload.hotkey, hotkey)
502524
}
503525
}
504526
}

src/ffi.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,8 @@ pub extern "C" fn cr_encrypt_commitment(
375375
/// * `netuid` - Network UID
376376
/// * `subnet_reveal_epochs` - Number of epochs to wait before revealing
377377
/// * `block_time` - Duration of a single block in seconds
378+
/// * `hotkey_ptr` - Pointer to a byte array representing the hotkey (Vec<u8>)
379+
/// * `hotkey_len` - Length of the hotkey byte array
378380
/// * `round_out` - Output parameter that will be set to the reveal round number
379381
/// * `err_out` - Output parameter that will be set to an error message on failure
380382
///
@@ -401,13 +403,18 @@ pub extern "C" fn cr_generate_commit(
401403
netuid: u16,
402404
subnet_reveal_epochs: u64,
403405
block_time: f64,
406+
hotkey_ptr: *const u8,
407+
hotkey_len: usize,
404408
round_out: *mut u64,
405409
err_out: *mut *mut c_char,
406410
) -> CRByteBuffer {
407411
unsafe { *err_out = ptr::null_mut() }
408412

409-
if (uids_ptr.is_null() && uids_len > 0) || (vals_ptr.is_null() && vals_len > 0) {
410-
unsafe { *err_out = err_to_cstring("uids/values ptr is null") };
413+
if (uids_ptr.is_null() && uids_len > 0)
414+
|| (vals_ptr.is_null() && vals_len > 0)
415+
|| (hotkey_ptr.is_null() && hotkey_len > 0)
416+
{
417+
unsafe { *err_out = err_to_cstring("uids/values/hotkey ptr is null") };
411418
return CRByteBuffer {
412419
ptr: ptr::null_mut(),
413420
len: 0,
@@ -426,6 +433,7 @@ pub extern "C" fn cr_generate_commit(
426433

427434
let uids = unsafe { std::slice::from_raw_parts(uids_ptr, uids_len) }.to_vec();
428435
let values = unsafe { std::slice::from_raw_parts(vals_ptr, vals_len) }.to_vec();
436+
let hotkey = unsafe { std::slice::from_raw_parts(hotkey_ptr, hotkey_len) }.to_vec();
429437

430438
match drand::generate_commit(
431439
uids,
@@ -436,6 +444,7 @@ pub extern "C" fn cr_generate_commit(
436444
netuid,
437445
subnet_reveal_epochs,
438446
block_time,
447+
hotkey,
439448
) {
440449
Ok((ct, rr)) => {
441450
unsafe { *round_out = rr }
@@ -785,6 +794,7 @@ mod tests {
785794
fn test_generate_commit_success() {
786795
let uids: [u16; 3] = [1, 2, 3];
787796
let vals: [u16; 3] = [10, 20, 30];
797+
let hotkey: [u8; 3] = [11, 22, 33];
788798

789799
let mut round: u64 = 0;
790800
let mut err_ptr: *mut c_char = ptr::null_mut();
@@ -801,6 +811,8 @@ mod tests {
801811
1, // netuid
802812
2, // subnet_reveal_epochs
803813
12.0, // block_time
814+
hotkey.as_ptr(),
815+
hotkey.len(),
804816
&mut round,
805817
&mut err_ptr,
806818
)
@@ -819,6 +831,7 @@ mod tests {
819831
#[test]
820832
fn test_generate_commit_null_uids() {
821833
let vals: [u16; 2] = [1, 2];
834+
let hotkey: [u8; 3] = [11, 22, 33];
822835
let mut round: u64 = 0;
823836
let mut err_ptr: *mut c_char = ptr::null_mut();
824837

@@ -834,6 +847,8 @@ mod tests {
834847
0,
835848
0,
836849
12.0,
850+
hotkey.as_ptr(),
851+
hotkey.len(),
837852
&mut round,
838853
&mut err_ptr,
839854
)
@@ -851,6 +866,7 @@ mod tests {
851866
fn test_generate_commit_mismatched_lengths() {
852867
let uids: [u16; 2] = [1, 2];
853868
let vals: [u16; 3] = [10, 20, 30];
869+
let hotkey: [u8; 3] = [11, 22, 33];
854870
let mut round: u64 = 0;
855871
let mut err_ptr: *mut c_char = ptr::null_mut();
856872

@@ -866,6 +882,8 @@ mod tests {
866882
0,
867883
0,
868884
12.0,
885+
hotkey.as_ptr(),
886+
hotkey.len(),
869887
&mut round,
870888
&mut err_ptr,
871889
)

src/python_bindings.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
2727
/// - the encrypted commitment (as bytes)
2828
/// - the reveal round number (int) when it can be decrypted.
2929
#[pyfunction]
30-
#[pyo3(signature = (uids, weights, version_key, tempo, current_block, netuid, subnet_reveal_period_epochs, block_time=12.0))]
30+
#[pyo3(signature = (uids, weights, version_key, tempo, current_block, netuid, subnet_reveal_period_epochs, block_time, hotkey))]
3131
fn get_encrypted_commit(
3232
py: Python,
3333
uids: Vec<u16>,
@@ -38,6 +38,7 @@ fn get_encrypted_commit(
3838
netuid: u16,
3939
subnet_reveal_period_epochs: u64,
4040
block_time: f64,
41+
hotkey: Vec<u8>,
4142
) -> PyResult<(Py<PyBytes>, u64)> {
4243
// create runtime to make async call
4344
let result = drand::generate_commit(
@@ -49,6 +50,7 @@ fn get_encrypted_commit(
4950
netuid,
5051
subnet_reveal_period_epochs,
5152
block_time,
53+
hotkey,
5254
);
5355
// matching the result
5456
match result {

0 commit comments

Comments
 (0)