Skip to content

Commit 9e81d6e

Browse files
committed
Merge branch 'devnet-ready' into feat/recycle-from-burn-uid
2 parents 1d4c5cf + 978c40c commit 9e81d6e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+6188
-773
lines changed

Cargo.lock

Lines changed: 10 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ expander = "2"
113113
ahash = { version = "0.8", default-features = false }
114114
regex = { version = "1.11.1", default-features = false }
115115

116+
frame = { package = "polkadot-sdk-frame", git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-stable2503-6", default-features = false }
116117
frame-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-stable2503-6", default-features = false }
117118
frame-benchmarking-cli = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-stable2503-6", default-features = false }
118119
frame-executive = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-stable2503-6", default-features = false }

evm-tests/test/runtime.call.precompile.test.ts

Lines changed: 80 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,40 @@
11
import * as assert from "assert";
2-
import { getAliceSigner, getDevnetApi } from "../src/substrate"
2+
import { getAliceSigner, getDevnetApi, getRandomSubstrateKeypair } from "../src/substrate"
33
import { generateRandomEthersWallet, getPublicClient } from "../src/utils";
44
import { IDISPATCH_ADDRESS, ISTORAGE_QUERY_ADDRESS, ETH_LOCAL_URL } from "../src/config";
55
import { devnet, MultiAddress } from "@polkadot-api/descriptors"
6-
import { hexToNumber, PublicClient } from "viem";
7-
import { PolkadotSigner, TypedApi } from "polkadot-api";
6+
import { PublicClient } from "viem";
7+
import { PolkadotSigner, TypedApi, getTypedCodecs } from "polkadot-api";
88
import { convertPublicKeyToSs58 } from "../src/address-utils"
9-
import { forceSetBalanceToEthAddress, setMaxChildkeyTake } from "../src/subtensor";
10-
import { xxhashAsU8a } from '@polkadot/util-crypto';
11-
import { u8aToHex } from '@polkadot/util';
9+
import { forceSetBalanceToEthAddress, setMaxChildkeyTake, burnedRegister, forceSetBalanceToSs58Address, addStake, setTxRateLimit, addNewSubnetwork, startCall, setTempo } from "../src/subtensor";
1210

1311
describe("Test the dispatch precompile", () => {
1412
let publicClient: PublicClient;
1513
const wallet1 = generateRandomEthersWallet();
1614
let api: TypedApi<typeof devnet>
1715
let alice: PolkadotSigner;
16+
const hotkey = getRandomSubstrateKeypair();
17+
const coldkey = getRandomSubstrateKeypair();
18+
let netuid: number;
1819

1920
before(async () => {
2021
publicClient = await getPublicClient(ETH_LOCAL_URL)
2122
api = await getDevnetApi()
2223
alice = await getAliceSigner()
2324
await forceSetBalanceToEthAddress(api, wallet1.address)
25+
26+
await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey.publicKey))
27+
await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey))
28+
29+
30+
netuid = await addNewSubnetwork(api, hotkey, coldkey)
31+
// set tempo big enough to avoid stake value updated with fast block feature
32+
await setTempo(api, netuid, 10000)
33+
await startCall(api, netuid, coldkey)
34+
await setTxRateLimit(api, BigInt(0))
35+
36+
await burnedRegister(api, netuid, convertPublicKeyToSs58(hotkey.publicKey), coldkey)
37+
await addStake(api, netuid, convertPublicKeyToSs58(hotkey.publicKey), BigInt(1_000_000_000), coldkey)
2438
})
2539

2640
it("Dispatch transfer call via precompile contract works correctly", async () => {
@@ -49,27 +63,73 @@ describe("Test the dispatch precompile", () => {
4963
})
5064

5165

52-
it("Storage query call via precompile contract works correctly", async () => {
53-
const palletPrefixBytes = xxhashAsU8a("SubtensorModule", 128);
54-
const storageItemPrefixBytes = xxhashAsU8a("MaxChildkeyTake", 128);
55-
const fullStorageKeyBytes = new Uint8Array([...palletPrefixBytes, ...storageItemPrefixBytes]);
56-
// 0x658faa385070e074c85bf6b568cf0555dba018859cab7e989f77669457b394be
57-
// key for max child key take
58-
const fullStorageKeyHex = u8aToHex(fullStorageKeyBytes);
66+
it("Value type storage query call via precompile contract works correctly", async () => {
67+
const key = await api.query.SubtensorModule.MaxChildkeyTake.getKey();
5968

6069
let maxChildkeyTake = 257;
6170
await setMaxChildkeyTake(api, maxChildkeyTake)
6271

6372
api.query.SubtensorModule.MaxChildkeyTake.getValue();
6473
const rawCallResponse = await publicClient.call({
6574
to: ISTORAGE_QUERY_ADDRESS,
66-
data: fullStorageKeyHex,
75+
data: key.toString() as `0x${string}`,
76+
})
77+
const rawResultData = rawCallResponse.data ?? "";
78+
79+
const codec = await getTypedCodecs(devnet);
80+
const maxChildkeyTakeCodec = codec.query.SubtensorModule.MaxChildkeyTake.value;
81+
const maxChildkeyTakeFromContract = maxChildkeyTakeCodec.dec(rawResultData);
82+
assert.equal(maxChildkeyTakeFromContract, maxChildkeyTake, "value should be 257")
83+
})
84+
85+
it("Map type storage query call via precompile contract works correctly", async () => {
86+
87+
const key = await api.query.SubtensorModule.Tempo.getKey(netuid);
88+
89+
const tempoOnChain = await api.query.SubtensorModule.Tempo.getValue(netuid);
90+
const rawCallResponse = await publicClient.call({
91+
to: ISTORAGE_QUERY_ADDRESS,
92+
data: key.toString() as `0x${string}`,
6793
})
68-
const rawResultData = rawCallResponse.data;
69-
if (rawResultData === undefined) {
70-
throw new Error("rawResultData is undefined");
71-
}
72-
let value = hexToNumber(rawResultData);
73-
assert.equal(value, maxChildkeyTake, "value should be 257")
94+
const rawResultData = rawCallResponse.data ?? "";
95+
96+
const codec = await getTypedCodecs(devnet);
97+
const maxChildkeyTakeValueCodec = codec.query.SubtensorModule.Tempo.value;
98+
const decodedValue = maxChildkeyTakeValueCodec.dec(rawResultData);
99+
assert.equal(tempoOnChain, decodedValue, "value should be the same as on chain")
100+
})
101+
102+
it("Double map type storage query call via precompile contract works correctly", async () => {
103+
const key = await api.query.SubtensorModule.TotalHotkeyAlpha.getKey(convertPublicKeyToSs58(hotkey.publicKey), netuid);
104+
const totalHotkeyAlphaOnChain = await api.query.SubtensorModule.TotalHotkeyAlpha.getValue(convertPublicKeyToSs58(hotkey.publicKey), netuid);
105+
106+
const rawCallResponse = await publicClient.call({
107+
to: ISTORAGE_QUERY_ADDRESS,
108+
data: key.toString() as `0x${string}`,
109+
})
110+
const rawResultData = rawCallResponse.data ?? "";
111+
const codec = await getTypedCodecs(devnet);
112+
const totalHotkeyAlphaValueCodec = codec.query.SubtensorModule.TotalHotkeyAlpha.value;
113+
const decodedValue = totalHotkeyAlphaValueCodec.dec(rawResultData);
114+
assert.equal(totalHotkeyAlphaOnChain, decodedValue, "value should be the same as on chain")
115+
74116
})
117+
118+
// Polkadot api can't decode the boolean type for now.
119+
// it("Double map type storage query call via precompile contract works correctly", async () => {
120+
// const key = await api.query.SubtensorModule.IsNetworkMember.getKey(convertPublicKeyToSs58(alice.publicKey), netuid);
121+
122+
// const isNetworkMemberOnChain = await api.query.SubtensorModule.IsNetworkMember.getValue(convertPublicKeyToSs58(alice.publicKey), netuid);
123+
// const rawCallResponse = await publicClient.call({
124+
// to: ISTORAGE_QUERY_ADDRESS,
125+
// data: key.toString() as `0x${string}`,
126+
// })
127+
128+
// const rawResultData = rawCallResponse.data ?? "";
129+
// const codec = await getTypedCodecs(devnet);
130+
// const isNetworkMemberValueCodec = codec.query.SubtensorModule.IsNetworkMember.value;
131+
// const decodedValue = isNetworkMemberValueCodec.dec(rawResultData);
132+
// assert.equal(isNetworkMemberOnChain, decodedValue, "value should be the same as on chain")
133+
// })
134+
75135
});

pallets/admin-utils/src/benchmarking.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use frame_benchmarking::v1::account;
1111
use frame_benchmarking::v2::*;
1212
use frame_support::BoundedVec;
1313
use frame_system::RawOrigin;
14+
use pallet_subtensor::SubnetworkN;
15+
use subtensor_runtime_common::NetUid;
1416

1517
use super::*;
1618

@@ -238,6 +240,19 @@ mod benchmarks {
238240
_(RawOrigin::Root, 1u16.into()/*netuid*/, 3u16/*kappa*/)/*set_kappa*/;
239241
}
240242

243+
#[benchmark]
244+
fn sudo_set_min_allowed_uids() {
245+
let netuid = NetUid::from(1);
246+
pallet_subtensor::Pallet::<T>::set_admin_freeze_window(0);
247+
pallet_subtensor::Pallet::<T>::init_new_network(netuid, 1u16 /*tempo*/);
248+
249+
// Artificially set that some neurons are already registered
250+
SubnetworkN::<T>::set(netuid, 32);
251+
252+
#[extrinsic_call]
253+
_(RawOrigin::Root, netuid, 16u16/*min_allowed_uids*/)/*sudo_set_min_allowed_uids*/;
254+
}
255+
241256
#[benchmark]
242257
fn sudo_set_max_allowed_uids() {
243258
// disable admin freeze window
@@ -432,5 +447,17 @@ mod benchmarks {
432447
_(RawOrigin::Root, 1u16.into()/*netuid*/, 5u16/*immune_neurons*/)/*sudo_set_owner_immune_neuron_limit()*/;
433448
}
434449

450+
#[benchmark]
451+
fn sudo_trim_to_max_allowed_uids() {
452+
pallet_subtensor::Pallet::<T>::set_admin_freeze_window(0);
453+
pallet_subtensor::Pallet::<T>::init_new_network(
454+
1u16.into(), /*netuid*/
455+
1u16, /*sudo_tempo*/
456+
);
457+
458+
#[extrinsic_call]
459+
_(RawOrigin::Root, 1u16.into()/*netuid*/, 256u16/*max_n*/)/*sudo_trim_to_max_allowed_uids()*/;
460+
}
461+
435462
//impl_benchmark_test_suite!(AdminUtils, crate::mock::new_test_ext(), crate::mock::Test);
436463
}

pallets/admin-utils/src/lib.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ pub mod pallet {
106106
NegativeSigmoidSteepness,
107107
/// Value not in allowed bounds.
108108
ValueNotInBounds,
109+
/// The minimum allowed UIDs must be less than the current number of UIDs in the subnet.
110+
MinAllowedUidsGreaterThanCurrentUids,
111+
/// The minimum allowed UIDs must be less than the maximum allowed UIDs.
112+
MinAllowedUidsGreaterThanMaxAllowedUids,
109113
}
110114
/// Enum for specifying the type of precompile operation.
111115
#[derive(
@@ -1080,6 +1084,23 @@ pub mod pallet {
10801084
Ok(())
10811085
}
10821086

1087+
/// The extrinsic sets the subnet limit for the network.
1088+
/// It is only callable by the root account.
1089+
/// The extrinsic will call the Subtensor pallet to set the subnet limit.
1090+
#[pallet::call_index(37)]
1091+
#[pallet::weight((
1092+
Weight::from_parts(14_000_000, 0)
1093+
.saturating_add(<T as frame_system::Config>::DbWeight::get().writes(1)),
1094+
DispatchClass::Operational,
1095+
Pays::No
1096+
))]
1097+
pub fn sudo_set_subnet_limit(origin: OriginFor<T>, max_subnets: u16) -> DispatchResult {
1098+
ensure_root(origin)?;
1099+
pallet_subtensor::Pallet::<T>::set_max_subnets(max_subnets);
1100+
log::debug!("MaxSubnets ( max_subnets: {max_subnets:?} ) ");
1101+
Ok(())
1102+
}
1103+
10831104
/// The extrinsic sets the lock reduction interval for the network.
10841105
/// It is only callable by the root account.
10851106
/// The extrinsic will call the Subtensor pallet to set the lock reduction interval.
@@ -1902,6 +1923,70 @@ pub mod pallet {
19021923
);
19031924
Ok(())
19041925
}
1926+
1927+
/// Trims the maximum number of UIDs for a subnet.
1928+
///
1929+
/// The trimming is done by sorting the UIDs by emission descending and then trimming
1930+
/// the lowest emitters while preserving temporally and owner immune UIDs. The UIDs are
1931+
/// then compressed to the left and storage is migrated to the new compressed UIDs.
1932+
#[pallet::call_index(78)]
1933+
#[pallet::weight(Weight::from_parts(32_880_000, 0)
1934+
.saturating_add(<T as frame_system::Config>::DbWeight::get().reads(6_u64))
1935+
.saturating_add(<T as frame_system::Config>::DbWeight::get().writes(1_u64)))]
1936+
pub fn sudo_trim_to_max_allowed_uids(
1937+
origin: OriginFor<T>,
1938+
netuid: NetUid,
1939+
max_n: u16,
1940+
) -> DispatchResult {
1941+
let maybe_owner = pallet_subtensor::Pallet::<T>::ensure_sn_owner_or_root_with_limits(
1942+
origin.clone(),
1943+
netuid,
1944+
&[TransactionType::MaxUidsTrimming],
1945+
)?;
1946+
1947+
pallet_subtensor::Pallet::<T>::trim_to_max_allowed_uids(netuid, max_n)?;
1948+
1949+
pallet_subtensor::Pallet::<T>::record_owner_rl(
1950+
maybe_owner,
1951+
netuid,
1952+
&[TransactionType::MaxUidsTrimming],
1953+
);
1954+
Ok(())
1955+
}
1956+
1957+
/// The extrinsic sets the minimum allowed UIDs for a subnet.
1958+
/// It is only callable by the root account.
1959+
#[pallet::call_index(79)]
1960+
#[pallet::weight(Weight::from_parts(31_550_000, 0)
1961+
.saturating_add(<T as frame_system::Config>::DbWeight::get().reads(5_u64))
1962+
.saturating_add(<T as frame_system::Config>::DbWeight::get().writes(1_u64)))]
1963+
pub fn sudo_set_min_allowed_uids(
1964+
origin: OriginFor<T>,
1965+
netuid: NetUid,
1966+
min_allowed_uids: u16,
1967+
) -> DispatchResult {
1968+
pallet_subtensor::Pallet::<T>::ensure_root_with_rate_limit(origin, netuid)?;
1969+
1970+
ensure!(
1971+
pallet_subtensor::Pallet::<T>::if_subnet_exist(netuid),
1972+
Error::<T>::SubnetDoesNotExist
1973+
);
1974+
ensure!(
1975+
min_allowed_uids < pallet_subtensor::Pallet::<T>::get_max_allowed_uids(netuid),
1976+
Error::<T>::MinAllowedUidsGreaterThanMaxAllowedUids
1977+
);
1978+
ensure!(
1979+
min_allowed_uids < pallet_subtensor::Pallet::<T>::get_subnetwork_n(netuid),
1980+
Error::<T>::MinAllowedUidsGreaterThanCurrentUids
1981+
);
1982+
1983+
pallet_subtensor::Pallet::<T>::set_min_allowed_uids(netuid, min_allowed_uids);
1984+
1985+
log::debug!(
1986+
"MinAllowedUidsSet( netuid: {netuid:?} min_allowed_uids: {min_allowed_uids:?} ) "
1987+
);
1988+
Ok(())
1989+
}
19051990
}
19061991
}
19071992

0 commit comments

Comments
 (0)