Skip to content

Commit 5b2dde6

Browse files
authored
Merge pull request #726 from VectorChat/feat/neuron-pruning-change
Neuron pruning based on registration date of UID
2 parents 8389088 + ab0e99a commit 5b2dde6

File tree

7 files changed

+173
-250
lines changed

7 files changed

+173
-250
lines changed

Diff for: .github/workflows/check-finney.yml

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ jobs:
1111
check-spec-version:
1212
name: Check spec_version bump
1313
runs-on: SubtensorCI
14+
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-spec-version-bump') }}
1415
steps:
1516
- name: Dependencies
1617
run: |

Diff for: CODEOWNERS

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
* @sacha-l @lisa-parity
1+
* @unconst

Diff for: README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ Requirements:
5151

5252
## For Subnet Development
5353

54-
If you are developing and testing subnet incentive mechanism, you will need to run a local subtensor node. Follow the detailed step-by-step instructions provided in the document [Running subtensor locally](./docs/running-subtensor-locally.md) to run either a lite node or an archive node. Also see the [**Subtensor Nodes** section in Bittensor Developer Documentation](https://docs.bittensor.com/subtensor-nodes).
54+
If you are developing and testing subnet incentive mechanism, you will need to run a local subtensor node. Follow the detailed step-by-step instructions provided in the [**Subtensor Nodes** section in Bittensor Developer Documentation](https://docs.bittensor.com/subtensor-nodes).
5555

5656
### Lite node vs Archive node
5757

Diff for: docs/running-subtensor-locally.md

+1-204
Original file line numberDiff line numberDiff line change
@@ -1,206 +1,3 @@
11
# Running subtensor node locally
22

3-
- [Method 1: Using Docker](#method-1-using-docker)
4-
- [Method 2: Using Source Code](#method-2-using-source-code)
5-
- [Running on Cloud](#running-on-cloud)
6-
7-
## Method 1: Using Docker
8-
9-
To run a subtensor node with Docker, follow the below steps.
10-
11-
If you are already running a subtensor node using Docker, then go directly to [Step 5 Prepare to Run](#step-5-prepare-to-run) to restart the Docker container. The below steps 1 through 4 are for first time users only.
12-
13-
### Step 1: Install git
14-
15-
Make sure you installed `git` on your machine. See [GitHub docs](https://docs.github.com/en/get-started).
16-
17-
### Step 2: Install Docker
18-
19-
Follow Docker's [official installation guides](https://docs.docker.com/engine/install/) and install Docker.
20-
21-
**Run Docker first**
22-
Before you proceed, make sure that Docker is running.
23-
24-
### Step 3: Clone the subtensor repo
25-
26-
Clone the subtensor repo:
27-
28-
```bash
29-
git clone https://github.com/opentensor/subtensor.git
30-
```
31-
32-
### Step 4: Go into subtensor directory
33-
34-
Then `cd` into the subtensor directory:
35-
36-
```bash
37-
cd subtensor
38-
```
39-
40-
### Step 5: Prepare to run
41-
42-
Execute the below three commands in this order:
43-
44-
Make sure you are on the `main` branch. If not, switch to it:
45-
46-
```bash
47-
git checkout main
48-
```
49-
50-
Pull the latest `main` branch contents:
51-
52-
```bash
53-
git pull
54-
```
55-
56-
Stop the currently running Docker containers:
57-
58-
```bash
59-
docker compose down --volumes
60-
```
61-
62-
### Run a lite node on mainchain
63-
64-
To run a lite node connected to the Bittensor mainchain, run the below command.
65-
66-
```bash
67-
sudo ./scripts/run/subtensor.sh -e docker --network mainnet --node-type lite
68-
```
69-
70-
### Run an archive node on mainchain
71-
72-
To run an archive node connected to the Bittensor mainchain, run the below command.
73-
74-
```bash
75-
sudo ./scripts/run/subtensor.sh -e docker --network mainnet --node-type archive
76-
```
77-
78-
### Run a lite node on testchain
79-
80-
To run a lite node connected to the Bittensor testchain, run the below command.
81-
82-
```bash
83-
sudo ./scripts/run/subtensor.sh -e docker --network testnet --node-type lite
84-
```
85-
86-
### Run an archive node on testchain
87-
88-
To run an archive node connected to the Bittensor testchain, run the below command.
89-
90-
```bash
91-
sudo ./scripts/run/subtensor.sh -e docker --network testnet --node-type archive
92-
```
93-
94-
---
95-
96-
## Method 2: Using Source Code
97-
98-
To install and run a subtensor node by compiling the source code, follow the below steps.
99-
100-
## Install basic packages
101-
102-
Install the basic requirements by running the below commands on a Linux terminal.
103-
104-
```bash title="On Linux"
105-
sudo apt-get update
106-
sudo apt install build-essential
107-
sudo apt-get install clang
108-
sudo apt-get install curl
109-
sudo apt-get install git
110-
sudo apt-get install make
111-
sudo apt install --assume-yes git clang curl libssl-dev protobuf-compiler
112-
sudo apt install --assume-yes git clang curl libssl-dev llvm libudev-dev make protobuf-compiler
113-
```
114-
115-
## Install Rust
116-
117-
Next, install Rust and update the environment by running the following commands:
118-
119-
```bash
120-
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
121-
source ~/.cargo/env
122-
```
123-
124-
Next, install Rust toolchain:
125-
126-
```bash
127-
rustup default stable
128-
rustup update
129-
rustup target add wasm32-unknown-unknown
130-
rustup toolchain install nightly
131-
rustup target add --toolchain nightly wasm32-unknown-unknown
132-
```
133-
134-
## Compile subtensor code
135-
136-
Next, to compile the subtensor source code, follow the below steps:
137-
138-
Clone the subtensor repo:
139-
140-
```bash
141-
git clone https://github.com/opentensor/subtensor.git
142-
```
143-
144-
`cd` into the subtensor directory:
145-
146-
```bash
147-
cd subtensor
148-
```
149-
150-
Make sure you are on the `main` branch. If not, switch to it:
151-
152-
```bash
153-
git checkout main
154-
```
155-
156-
Remove previous chain state:
157-
158-
```bash
159-
rm -rf /tmp/blockchain
160-
```
161-
162-
Install subtensor by compiling with `cargo`:
163-
164-
```bash
165-
cargo build --profile production --features=runtime-benchmarks
166-
```
167-
168-
## Run the subtensor node
169-
170-
You can now run the public subtensor node either as a lite node or as an archive node. See below:
171-
172-
### Lite node on mainchain
173-
174-
To run a lite node connected to the mainchain, execute the below command (note the `--sync=warp` flag which runs the subtensor node in lite mode):
175-
176-
```bash title="With --sync=warp setting, for lite node"
177-
./target/production/node-subtensor --chain raw_spec.json --base-path /tmp/blockchain --sync=warp --port 30333 --max-runtime-instances 32 --rpc-max-response-size 2048 --rpc-cors all --rpc-port 9944 --bootnodes /ip4/13.58.175.193/tcp/30333/p2p/12D3KooWDe7g2JbNETiKypcKT1KsCEZJbTzEHCn8hpd4PHZ6pdz5 --no-mdns --in-peers 8000 --out-peers 8000 --prometheus-external --rpc-external
178-
```
179-
180-
### Archive node on mainchain
181-
182-
To run an archive node connected to the mainchain, execute the below command (note the `--sync=full` which syncs the node to the full chain and `--pruning archive` flags, which disables the node's automatic pruning of older historical data):
183-
184-
```bash title="With --sync=full and --pruning archive setting, for archive node"
185-
./target/production/node-subtensor --chain raw_spec.json --base-path /tmp/blockchain --sync=full --pruning archive --port 30333 --max-runtime-instances 32 --rpc-max-response-size 2048 --rpc-cors all --rpc-port 9944 --bootnodes /ip4/13.58.175.193/tcp/30333/p2p/12D3KooWDe7g2JbNETiKypcKT1KsCEZJbTzEHCn8hpd4PHZ6pdz5 --no-mdns --in-peers 8000 --out-peers 8000 --prometheus-external --rpc-external
186-
```
187-
188-
### Lite node on testchain
189-
190-
To run a lite node connected to the testchain, execute the below command:
191-
192-
```bash title="With bootnodes set to testnet and --sync=warp setting, for lite node."
193-
./target/production/node-subtensor --chain raw_testspec.json --base-path /tmp/blockchain --sync=warp --port 30333 --max-runtime-instances 32 --rpc-max-response-size 2048 --rpc-cors all --rpc-port 9944 --bootnodes /dns/bootnode.test.finney.opentensor.ai/tcp/30333/p2p/12D3KooWPM4mLcKJGtyVtkggqdG84zWrd7Rij6PGQDoijh1X86Vr --no-mdns --in-peers 8000 --out-peers 8000 --prometheus-external --rpc-external
194-
```
195-
196-
### Archive node on testchain
197-
198-
To run an archive node connected to the testchain, execute the below command:
199-
200-
```bash title="With bootnodes set to testnet and --sync=full and --pruning archive setting, for archive node"
201-
./target/production/node-subtensor --chain raw_testspec.json --base-path /tmp/blockchain --sync=full --pruning archive --port 30333 --max-runtime-instances 32 --rpc-max-response-size 2048 --rpc-cors all --rpc-port 9944 --bootnodes /dns/bootnode.test.finney.opentensor.ai/tcp/30333/p2p/12D3KooWPM4mLcKJGtyVtkggqdG84zWrd7Rij6PGQDoijh1X86Vr --no-mdns --in-peers 8000 --out-peers 8000 --prometheus-external --rpc-external
202-
```
203-
204-
## Running on cloud
205-
206-
We have not tested these installation scripts on any cloud service. In addition, if you are using Runpod cloud service, then note that this service is already [containerized](https://docs.runpod.io/pods/overview). Hence, the only option available to you is to compile from the source, as described in the above [Method 2: Using Source Code](#method-2-using-source-code) section. Note that these scripts have not been tested on Runpod.
3+
See the [**Subtensor Nodes** section in Bittensor Developer Documentation](https://docs.bittensor.com/subtensor-nodes).

Diff for: pallets/subtensor/src/subnets/registration.rs

+46-44
Original file line numberDiff line numberDiff line change
@@ -419,65 +419,67 @@ impl<T: Config> Pallet<T> {
419419
}
420420

421421
/// Determine which peer to prune from the network by finding the element with the lowest pruning score out of
422-
/// immunity period. If all neurons are in immunity period, return node with lowest prunning score.
423-
/// This function will always return an element to prune.
422+
/// immunity period. If there is a tie for lowest pruning score, the neuron registered earliest is pruned.
423+
/// If all neurons are in immunity period, the neuron with the lowest pruning score is pruned. If there is a tie for
424+
/// the lowest pruning score, the immune neuron registered earliest is pruned.
425+
/// Ties for earliest registration are broken by the neuron with the lowest uid.
424426
pub fn get_neuron_to_prune(netuid: u16) -> u16 {
425427
let mut min_score: u16 = u16::MAX;
426-
let mut min_score_in_immunity_period = u16::MAX;
427-
let mut uid_with_min_score = 0;
428-
let mut uid_with_min_score_in_immunity_period: u16 = 0;
428+
let mut min_score_in_immunity: u16 = u16::MAX;
429+
let mut earliest_registration: u64 = u64::MAX;
430+
let mut earliest_registration_in_immunity: u64 = u64::MAX;
431+
let mut uid_to_prune: u16 = 0;
432+
let mut uid_to_prune_in_immunity: u16 = 0;
433+
434+
// This boolean is used instead of checking if min_score == u16::MAX, to avoid the case
435+
// where all non-immune neurons have pruning score u16::MAX
436+
// This may be unlikely in practice.
437+
let mut found_non_immune = false;
429438

430439
let neurons_n = Self::get_subnetwork_n(netuid);
431440
if neurons_n == 0 {
432441
return 0; // If there are no neurons in this network.
433442
}
434443

435-
let current_block: u64 = Self::get_current_block_as_u64();
436-
let immunity_period: u64 = Self::get_immunity_period(netuid) as u64;
437-
for neuron_uid_i in 0..neurons_n {
438-
let pruning_score: u16 = Self::get_pruning_score_for_uid(netuid, neuron_uid_i);
444+
for neuron_uid in 0..neurons_n {
445+
let pruning_score: u16 = Self::get_pruning_score_for_uid(netuid, neuron_uid);
439446
let block_at_registration: u64 =
440-
Self::get_neuron_block_at_registration(netuid, neuron_uid_i);
441-
#[allow(clippy::comparison_chain)]
442-
if min_score == pruning_score {
443-
if current_block.saturating_sub(block_at_registration) < immunity_period {
444-
//neuron is in immunity period
445-
if min_score_in_immunity_period > pruning_score {
446-
min_score_in_immunity_period = pruning_score;
447-
uid_with_min_score_in_immunity_period = neuron_uid_i;
448-
}
449-
} else {
450-
uid_with_min_score = neuron_uid_i;
447+
Self::get_neuron_block_at_registration(netuid, neuron_uid);
448+
let is_immune = Self::get_neuron_is_immune(netuid, neuron_uid);
449+
450+
if is_immune {
451+
// if the immune neuron has a lower pruning score than the minimum for immune neurons,
452+
// or, if the pruning scores are equal and the immune neuron was registered earlier than the current minimum for immune neurons,
453+
// then update the minimum pruning score and the uid to prune for immune neurons
454+
if pruning_score < min_score_in_immunity
455+
|| (pruning_score == min_score_in_immunity
456+
&& block_at_registration < earliest_registration_in_immunity)
457+
{
458+
min_score_in_immunity = pruning_score;
459+
earliest_registration_in_immunity = block_at_registration;
460+
uid_to_prune_in_immunity = neuron_uid;
451461
}
452-
}
453-
// Find min pruning score.
454-
else if min_score > pruning_score {
455-
if current_block.saturating_sub(block_at_registration) < immunity_period {
456-
//neuron is in immunity period
457-
if min_score_in_immunity_period > pruning_score {
458-
min_score_in_immunity_period = pruning_score;
459-
uid_with_min_score_in_immunity_period = neuron_uid_i;
460-
}
461-
} else {
462+
} else {
463+
found_non_immune = true;
464+
// if the non-immune neuron has a lower pruning score than the minimum for non-immune neurons,
465+
// or, if the pruning scores are equal and the non-immune neuron was registered earlier than the current minimum for non-immune neurons,
466+
// then update the minimum pruning score and the uid to prune for non-immune neurons
467+
if pruning_score < min_score
468+
|| (pruning_score == min_score && block_at_registration < earliest_registration)
469+
{
462470
min_score = pruning_score;
463-
uid_with_min_score = neuron_uid_i;
471+
earliest_registration = block_at_registration;
472+
uid_to_prune = neuron_uid;
464473
}
465474
}
466475
}
467-
if min_score == u16::MAX {
468-
//all neuorns are in immunity period
469-
Self::set_pruning_score_for_uid(
470-
netuid,
471-
uid_with_min_score_in_immunity_period,
472-
u16::MAX,
473-
);
474-
uid_with_min_score_in_immunity_period
476+
477+
if found_non_immune {
478+
Self::set_pruning_score_for_uid(netuid, uid_to_prune, u16::MAX);
479+
uid_to_prune
475480
} else {
476-
// We replace the pruning score here with u16 max to ensure that all peers always have a
477-
// pruning score. In the event that every peer has been pruned this function will prune
478-
// the last element in the network continually.
479-
Self::set_pruning_score_for_uid(netuid, uid_with_min_score, u16::MAX);
480-
uid_with_min_score
481+
Self::set_pruning_score_for_uid(netuid, uid_to_prune_in_immunity, u16::MAX);
482+
uid_to_prune_in_immunity
481483
}
482484
}
483485

Diff for: pallets/subtensor/src/utils/misc.rs

+7
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,13 @@ impl<T: Config> Pallet<T> {
461461
ImmunityPeriod::<T>::insert(netuid, immunity_period);
462462
Self::deposit_event(Event::ImmunityPeriodSet(netuid, immunity_period));
463463
}
464+
/// Check if a neuron is in immunity based on the current block
465+
pub fn get_neuron_is_immune(netuid: u16, uid: u16) -> bool {
466+
let registered_at = Self::get_neuron_block_at_registration(netuid, uid);
467+
let current_block = Self::get_current_block_as_u64();
468+
let immunity_period = Self::get_immunity_period(netuid);
469+
current_block.saturating_sub(registered_at) < u64::from(immunity_period)
470+
}
464471

465472
pub fn get_min_allowed_weights(netuid: u16) -> u16 {
466473
MinAllowedWeights::<T>::get(netuid)

0 commit comments

Comments
 (0)