Skip to content

Commit cb72b4f

Browse files
authored
Merge pull request #750 from backend-developers-ltd/encrypt
add neuron certificate handling
2 parents d12309b + eacda1e commit cb72b4f

File tree

9 files changed

+281
-3
lines changed

9 files changed

+281
-3
lines changed

pallets/subtensor/rpc/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ pub trait SubtensorCustomApi<BlockHash> {
4141
fn get_neurons(&self, netuid: u16, at: Option<BlockHash>) -> RpcResult<Vec<u8>>;
4242
#[method(name = "neuronInfo_getNeuron")]
4343
fn get_neuron(&self, netuid: u16, uid: u16, at: Option<BlockHash>) -> RpcResult<Vec<u8>>;
44-
4544
#[method(name = "subnetInfo_getSubnetInfo")]
4645
fn get_subnet_info(&self, netuid: u16, at: Option<BlockHash>) -> RpcResult<Vec<u8>>;
4746
#[method(name = "subnetInfo_getSubnetsInfo")]

pallets/subtensor/src/lib.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,14 @@ pub mod pallet {
6767
traits::{
6868
tokens::fungible, OriginTrait, QueryPreimage, StorePreimage, UnfilteredDispatchable,
6969
},
70+
BoundedVec,
7071
};
7172
use frame_system::pallet_prelude::*;
7273
use sp_core::H256;
7374
use sp_runtime::traits::{Dispatchable, TrailingZeroInput};
7475
use sp_std::vec;
7576
use sp_std::vec::Vec;
77+
use subtensor_macros::freeze_struct;
7678

7779
#[cfg(not(feature = "std"))]
7880
use alloc::boxed::Box;
@@ -129,6 +131,36 @@ pub mod pallet {
129131
pub placeholder2: u8,
130132
}
131133

134+
/// Struct for NeuronCertificate.
135+
pub type NeuronCertificateOf = NeuronCertificate;
136+
/// Data structure for NeuronCertificate information.
137+
#[freeze_struct("1c232be200d9ec6c")]
138+
#[derive(Decode, Encode, Default, TypeInfo, PartialEq, Eq, Clone, Debug)]
139+
pub struct NeuronCertificate {
140+
/// The neuron TLS public key
141+
pub public_key: BoundedVec<u8, ConstU32<64>>,
142+
/// The algorithm used to generate the public key
143+
pub algorithm: u8,
144+
}
145+
146+
impl TryFrom<Vec<u8>> for NeuronCertificate {
147+
type Error = ();
148+
149+
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
150+
if value.len() > 65 {
151+
return Err(());
152+
}
153+
// take the first byte as the algorithm
154+
let algorithm = value.first().ok_or(())?;
155+
// and the rest as the public_key
156+
let certificate = value.get(1..).ok_or(())?.to_vec();
157+
Ok(Self {
158+
public_key: BoundedVec::try_from(certificate).map_err(|_| ())?,
159+
algorithm: *algorithm,
160+
})
161+
}
162+
}
163+
132164
/// Struct for Prometheus.
133165
pub type PrometheusInfoOf = PrometheusInfo;
134166

@@ -1162,6 +1194,17 @@ pub mod pallet {
11621194
/// --- MAP ( netuid, hotkey ) --> axon_info
11631195
pub type Axons<T: Config> =
11641196
StorageDoubleMap<_, Identity, u16, Blake2_128Concat, T::AccountId, AxonInfoOf, OptionQuery>;
1197+
/// --- MAP ( netuid, hotkey ) --> certificate
1198+
#[pallet::storage]
1199+
pub type NeuronCertificates<T: Config> = StorageDoubleMap<
1200+
_,
1201+
Identity,
1202+
u16,
1203+
Blake2_128Concat,
1204+
T::AccountId,
1205+
NeuronCertificateOf,
1206+
OptionQuery,
1207+
>;
11651208
#[pallet::storage]
11661209
/// --- MAP ( netuid, hotkey ) --> prometheus_info
11671210
pub type Prometheus<T: Config> = StorageDoubleMap<
@@ -1538,6 +1581,10 @@ where
15381581
let transaction_fee = 0;
15391582
Ok((CallType::Serve, transaction_fee, who.clone()))
15401583
}
1584+
Some(Call::serve_axon_tls { .. }) => {
1585+
let transaction_fee = 0;
1586+
Ok((CallType::Serve, transaction_fee, who.clone()))
1587+
}
15411588
Some(Call::register_network { .. }) => {
15421589
let transaction_fee = 0;
15431590
Ok((CallType::RegisterNetwork, transaction_fee, who.clone()))

pallets/subtensor/src/macros/dispatches.rs

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ mod dispatches {
435435
Self::do_remove_stake(origin, hotkey, amount_unstaked)
436436
}
437437

438-
/// Serves or updates axon /promethteus information for the neuron associated with the caller. If the caller is
438+
/// Serves or updates axon /prometheus information for the neuron associated with the caller. If the caller is
439439
/// already registered the metadata is updated. If the caller is not registered this call throws NotRegistered.
440440
///
441441
/// # Args:
@@ -511,6 +511,92 @@ mod dispatches {
511511
protocol,
512512
placeholder1,
513513
placeholder2,
514+
None,
515+
)
516+
}
517+
518+
/// Same as `serve_axon` but takes a certificate as an extra optional argument.
519+
/// Serves or updates axon /prometheus information for the neuron associated with the caller. If the caller is
520+
/// already registered the metadata is updated. If the caller is not registered this call throws NotRegistered.
521+
///
522+
/// # Args:
523+
/// * 'origin': (<T as frame_system::Config>Origin):
524+
/// - The signature of the caller.
525+
///
526+
/// * 'netuid' (u16):
527+
/// - The u16 network identifier.
528+
///
529+
/// * 'version' (u64):
530+
/// - The bittensor version identifier.
531+
///
532+
/// * 'ip' (u64):
533+
/// - The endpoint ip information as a u128 encoded integer.
534+
///
535+
/// * 'port' (u16):
536+
/// - The endpoint port information as a u16 encoded integer.
537+
///
538+
/// * 'ip_type' (u8):
539+
/// - The endpoint ip version as a u8, 4 or 6.
540+
///
541+
/// * 'protocol' (u8):
542+
/// - UDP:1 or TCP:0
543+
///
544+
/// * 'placeholder1' (u8):
545+
/// - Placeholder for further extra params.
546+
///
547+
/// * 'placeholder2' (u8):
548+
/// - Placeholder for further extra params.
549+
///
550+
/// * 'certificate' (Vec<u8>):
551+
/// - TLS certificate for inter neuron communitation.
552+
///
553+
/// # Event:
554+
/// * AxonServed;
555+
/// - On successfully serving the axon info.
556+
///
557+
/// # Raises:
558+
/// * 'SubNetworkDoesNotExist':
559+
/// - Attempting to set weights on a non-existent network.
560+
///
561+
/// * 'NotRegistered':
562+
/// - Attempting to set weights from a non registered account.
563+
///
564+
/// * 'InvalidIpType':
565+
/// - The ip type is not 4 or 6.
566+
///
567+
/// * 'InvalidIpAddress':
568+
/// - The numerically encoded ip address does not resolve to a proper ip.
569+
///
570+
/// * 'ServingRateLimitExceeded':
571+
/// - Attempting to set prometheus information withing the rate limit min.
572+
///
573+
#[pallet::call_index(40)]
574+
#[pallet::weight((Weight::from_parts(46_000_000, 0)
575+
.saturating_add(T::DbWeight::get().reads(4))
576+
.saturating_add(T::DbWeight::get().writes(1)), DispatchClass::Normal, Pays::No))]
577+
pub fn serve_axon_tls(
578+
origin: OriginFor<T>,
579+
netuid: u16,
580+
version: u32,
581+
ip: u128,
582+
port: u16,
583+
ip_type: u8,
584+
protocol: u8,
585+
placeholder1: u8,
586+
placeholder2: u8,
587+
certificate: Vec<u8>,
588+
) -> DispatchResult {
589+
Self::do_serve_axon(
590+
origin,
591+
netuid,
592+
version,
593+
ip,
594+
port,
595+
ip_type,
596+
protocol,
597+
placeholder1,
598+
placeholder2,
599+
Some(certificate),
514600
)
515601
}
516602

pallets/subtensor/src/subnets/serving.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ impl<T: Config> Pallet<T> {
3131
/// * 'placeholder2' (u8):
3232
/// - Placeholder for further extra params.
3333
///
34+
/// * 'certificate' (Option<Vec<u8>>):
35+
/// - Certificate for mutual Tls connection between neurons
36+
///
3437
/// # Event:
3538
/// * AxonServed;
3639
/// - On successfully serving the axon info.
@@ -61,6 +64,7 @@ impl<T: Config> Pallet<T> {
6164
protocol: u8,
6265
placeholder1: u8,
6366
placeholder2: u8,
67+
certificate: Option<Vec<u8>>,
6468
) -> dispatch::DispatchResult {
6569
// We check the callers (hotkey) signature.
6670
let hotkey_id = ensure_signed(origin)?;
@@ -86,6 +90,13 @@ impl<T: Config> Pallet<T> {
8690
Error::<T>::ServingRateLimitExceeded
8791
);
8892

93+
// Check certificate
94+
if let Some(certificate) = certificate {
95+
if let Ok(certificate) = NeuronCertificateOf::try_from(certificate) {
96+
NeuronCertificates::<T>::insert(netuid, hotkey_id.clone(), certificate)
97+
}
98+
}
99+
89100
// We insert the axon meta.
90101
prev_axon.block = Self::get_current_block_as_u64();
91102
prev_axon.version = version;

pallets/subtensor/src/subnets/uids.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ impl<T: Config> Pallet<T> {
4545
Uids::<T>::insert(netuid, new_hotkey.clone(), uid_to_replace); // Make uid - hotkey association.
4646
BlockAtRegistration::<T>::insert(netuid, uid_to_replace, block_number); // Fill block at registration.
4747
IsNetworkMember::<T>::insert(new_hotkey.clone(), netuid, true); // Fill network is member.
48+
49+
// 4. Clear neuron certificates
50+
NeuronCertificates::<T>::remove(netuid, old_hotkey.clone());
4851
}
4952

5053
/// Appends the uid to the network.

pallets/subtensor/src/swap/swap_hotkey.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,18 @@ impl<T: Config> Pallet<T> {
276276
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2));
277277
}
278278
}
279+
280+
// 9.7. Swap neuron TLS certificates.
281+
// NeuronCertificates( netuid, hotkey ) -> Vec<u8> -- the neuron certificate for the hotkey.
282+
if is_network_member {
283+
if let Ok(old_neuron_certificates) =
284+
NeuronCertificates::<T>::try_get(netuid, old_hotkey)
285+
{
286+
NeuronCertificates::<T>::remove(netuid, old_hotkey);
287+
NeuronCertificates::<T>::insert(netuid, new_hotkey, old_neuron_certificates);
288+
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2));
289+
}
290+
}
279291
}
280292

281293
// 10. Swap Stake.

pallets/subtensor/tests/serving.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,64 @@ fn test_serving_ok() {
9999
});
100100
}
101101

102+
#[test]
103+
fn test_serving_tls_ok() {
104+
new_test_ext(1).execute_with(|| {
105+
let hotkey_account_id = U256::from(1);
106+
let netuid: u16 = 1;
107+
let tempo: u16 = 13;
108+
let version: u32 = 2;
109+
let ip: u128 = 1676056785;
110+
let port: u16 = 128;
111+
let ip_type: u8 = 4;
112+
let modality: u16 = 0;
113+
let protocol: u8 = 0;
114+
let placeholder1: u8 = 0;
115+
let placeholder2: u8 = 0;
116+
let certificate: Vec<u8> = "CERT".as_bytes().to_vec();
117+
add_network(netuid, tempo, modality);
118+
register_ok_neuron(netuid, hotkey_account_id, U256::from(66), 0);
119+
assert_ok!(SubtensorModule::serve_axon_tls(
120+
<<Test as Config>::RuntimeOrigin>::signed(hotkey_account_id),
121+
netuid,
122+
version,
123+
ip,
124+
port,
125+
ip_type,
126+
protocol,
127+
placeholder1,
128+
placeholder2,
129+
certificate.clone()
130+
));
131+
132+
let stored_certificate = NeuronCertificates::<Test>::get(netuid, hotkey_account_id)
133+
.expect("Certificate should exist");
134+
assert_eq!(
135+
stored_certificate.public_key.clone().into_inner(),
136+
certificate.get(1..).expect("Certificate should exist")
137+
);
138+
let new_certificate = "UPDATED_CERT".as_bytes().to_vec();
139+
assert_ok!(SubtensorModule::serve_axon_tls(
140+
<<Test as Config>::RuntimeOrigin>::signed(hotkey_account_id),
141+
netuid,
142+
version,
143+
ip,
144+
port,
145+
ip_type,
146+
protocol,
147+
placeholder1,
148+
placeholder2,
149+
new_certificate.clone()
150+
));
151+
let stored_certificate = NeuronCertificates::<Test>::get(netuid, hotkey_account_id)
152+
.expect("Certificate should exist");
153+
assert_eq!(
154+
stored_certificate.public_key.clone().into_inner(),
155+
new_certificate.get(1..).expect("Certificate should exist")
156+
);
157+
});
158+
}
159+
102160
#[test]
103161
fn test_serving_set_metadata_update() {
104162
new_test_ext(1).execute_with(|| {

pallets/subtensor/tests/swap_hotkey.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,38 @@ fn test_swap_axons() {
311311
});
312312
}
313313

314+
// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_hotkey -- test_swap_certificates --exact --nocapture
315+
#[test]
316+
fn test_swap_certificates() {
317+
new_test_ext(1).execute_with(|| {
318+
let old_hotkey = U256::from(1);
319+
let new_hotkey = U256::from(2);
320+
let coldkey = U256::from(3);
321+
let netuid = 0u16;
322+
let certificate = NeuronCertificate::try_from(vec![1, 2, 3]).unwrap();
323+
let mut weight = Weight::zero();
324+
325+
add_network(netuid, 0, 1);
326+
IsNetworkMember::<Test>::insert(old_hotkey, netuid, true);
327+
NeuronCertificates::<Test>::insert(netuid, old_hotkey, certificate.clone());
328+
329+
assert_ok!(SubtensorModule::perform_hotkey_swap(
330+
&old_hotkey,
331+
&new_hotkey,
332+
&coldkey,
333+
&mut weight
334+
));
335+
336+
assert!(!NeuronCertificates::<Test>::contains_key(
337+
netuid, old_hotkey
338+
));
339+
assert_eq!(
340+
NeuronCertificates::<Test>::get(netuid, new_hotkey),
341+
Some(certificate)
342+
);
343+
});
344+
}
345+
314346
// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_hotkey -- test_swap_weight_commits --exact --nocapture
315347
#[test]
316348
fn test_swap_weight_commits() {

0 commit comments

Comments
 (0)