Skip to content

Commit

Permalink
Automatically spawn neurons then disburse into the treasury (#5097)
Browse files Browse the repository at this point in the history
  • Loading branch information
hpeebles authored Jan 3, 2024
1 parent 4519022 commit 71b3add
Show file tree
Hide file tree
Showing 22 changed files with 217 additions and 59 deletions.
22 changes: 22 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ members = [
"backend/canisters/user_index/c2c_client",
"backend/canisters/user_index/client",
"backend/canisters/user_index/impl",
"backend/external_canisters/cmc/api",
"backend/external_canisters/cmc/c2c_client",
"backend/external_canisters/icdex/api",
"backend/external_canisters/icdex/c2c_client",
"backend/external_canisters/icp_ledger/api",
Expand Down
1 change: 1 addition & 0 deletions backend/canister_installer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ async fn install_service_canisters_impl(
governance_principals: vec![principal],
nns_governance_canister_id: canister_ids.nns_governance,
nns_ledger_canister_id: canister_ids.nns_ledger,
cycles_minting_canister_id: canister_ids.nns_cmc,
cycles_dispenser_canister_id: canister_ids.cycles_dispenser,
wasm_version: version,
test_mode,
Expand Down
4 changes: 4 additions & 0 deletions backend/canisters/neuron_controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [unreleased]

### Added

- Automatically spawn neurons then disburse into the treasury ([#5097](https://github.com/open-chat-labs/open-chat/pull/5097))

## [[2.0.939](https://github.com/open-chat-labs/open-chat/releases/tag/v2.0.939-neuron_controller)] - 2023-11-23

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub struct Args {
pub governance_principals: Vec<Principal>,
pub nns_governance_canister_id: CanisterId,
pub nns_ledger_canister_id: CanisterId,
pub cycles_minting_canister_id: CanisterId,
pub cycles_dispenser_canister_id: CanisterId,
pub wasm_version: BuildVersion,
pub test_mode: bool,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use candid::CandidType;
use human_readable::HumanReadable;
use nns_governance_canister::types::manage_neuron::Command;
use nns_governance_canister::types::{ManageNeuron, NeuronId};
use serde::{Deserialize, Serialize};

#[derive(CandidType, Serialize, Deserialize, HumanReadable, Clone, Debug)]
Expand All @@ -15,13 +14,3 @@ pub enum Response {
Success(String),
InternalError(String),
}

impl From<Args> for ManageNeuron {
fn from(value: Args) -> Self {
ManageNeuron {
id: Some(NeuronId { id: value.neuron_id }),
neuron_id_or_subaccount: None,
command: Some(value.command),
}
}
}
2 changes: 2 additions & 0 deletions backend/canisters/neuron_controller/impl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ canister_api_macros = { path = "../../../libraries/canister_api_macros" }
canister_logger = { path = "../../../libraries/canister_logger" }
canister_state_macros = { path = "../../../libraries/canister_state_macros" }
canister_tracing_macros = { path = "../../../libraries/canister_tracing_macros" }
cycles_minting_canister_c2c_client = { path = "../../../external_canisters/cmc/c2c_client" }
hex = { workspace = true }
http_request = { path = "../../../libraries/http_request" }
human_readable = { path = "../../../libraries/human_readable" }
ic-cdk = { workspace = true }
ic-cdk-macros = { workspace = true }
ic-cdk-timers = { workspace = true }
ic-ledger-types = { workspace = true }
ic-stable-structures = { workspace = true }
ic-transport-types = { workspace = true }
icrc_ledger_canister_c2c_client = { path = "../../../external_canisters/icrc_ledger/c2c_client" }
Expand Down
4 changes: 2 additions & 2 deletions backend/canisters/neuron_controller/impl/src/jobs/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::RuntimeState;

pub mod refresh_neurons;
pub mod process_neurons;

pub(crate) fn start(_state: &RuntimeState) {
refresh_neurons::start_job();
process_neurons::start_job();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use crate::updates::manage_nns_neuron::manage_nns_neuron_impl;
use crate::{mutate_state, read_state};
use ic_ledger_types::{AccountIdentifier, DEFAULT_SUBACCOUNT};
use nns_governance_canister::types::manage_neuron::{Command, Disburse, Spawn};
use nns_governance_canister::types::ListNeurons;
use std::time::Duration;
use tracing::info;
use types::{Milliseconds, Timestamped};
use utils::canister_timers::run_now_then_interval;
use utils::consts::SNS_GOVERNANCE_CANISTER_ID;
use utils::time::DAY_IN_MS;

const REFRESH_NEURONS_INTERVAL: Milliseconds = DAY_IN_MS;
const E8S_PER_ICP: u64 = 100_000_000;

pub fn start_job() {
run_now_then_interval(Duration::from_millis(REFRESH_NEURONS_INTERVAL), run);
}

fn run() {
ic_cdk::spawn(run_async());
}

async fn run_async() {
let nns_governance_canister_id = read_state(|state| state.data.nns_governance_canister_id);

if let Ok(response) = nns_governance_canister_c2c_client::list_neurons(
nns_governance_canister_id,
&ListNeurons {
neuron_ids: Vec::new(),
include_neurons_readable_by_caller: true,
},
)
.await
{
let now = read_state(|state| state.env.now());

let neurons_to_spawn: Vec<_> = response
.full_neurons
.iter()
.filter(|n| n.maturity_e8s_equivalent > 1000 * E8S_PER_ICP)
.filter_map(|n| n.id.as_ref().map(|id| id.id))
.collect();

let neurons_to_disburse: Vec<_> = response
.full_neurons
.iter()
.filter(|n| n.is_dissolved(now) && n.cached_neuron_stake_e8s > 0)
.filter_map(|n| n.id.as_ref().map(|id| id.id))
.collect();

mutate_state(|state| {
state.data.neurons = Timestamped::new(response.full_neurons, now);
});

if !neurons_to_spawn.is_empty() {
spawn_neurons(neurons_to_spawn).await;
}

if !neurons_to_disburse.is_empty() {
disburse_neurons(neurons_to_disburse).await;
}
}
}

async fn spawn_neurons(neuron_ids: Vec<u64>) {
let cycles_minting_canister_id = read_state(|state| state.data.cycles_minting_canister_id);

if let Ok(Ok(modulation)) = cycles_minting_canister_c2c_client::neuron_maturity_modulation(cycles_minting_canister_id).await
{
// Only spawn when the modulation is at least 102.5%
if modulation >= 250 {
for neuron_id in neuron_ids {
info!(neuron_id, "Spawning neuron from maturity");
manage_nns_neuron_impl(neuron_id, Command::Spawn(Spawn::default())).await;
}
}
}
}

async fn disburse_neurons(neuron_ids: Vec<u64>) {
for neuron_id in neuron_ids {
info!(neuron_id, "Disbursing neuron");
manage_nns_neuron_impl(
neuron_id,
Command::Disburse(Disburse {
to_account: Some(AccountIdentifier::new(&SNS_GOVERNANCE_CANISTER_ID, &DEFAULT_SUBACCOUNT)),
amount: None,
}),
)
.await;
}
}

This file was deleted.

18 changes: 14 additions & 4 deletions backend/canisters/neuron_controller/impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ impl RuntimeState {
.filter_map(|n| n.id.as_ref().map(|i| i.id))
.collect(),
canister_ids: CanisterIds {
nns_governance_canister_id: self.data.nns_governance_canister_id,
nns_ledger_canister_id: self.data.nns_ledger_canister_id,
nns_governance_canister: self.data.nns_governance_canister_id,
nns_ledger_canister: self.data.nns_ledger_canister_id,
cycles_minting_canister: self.data.cycles_minting_canister_id,
cycles_dispenser: self.data.cycles_dispenser_canister_id,
},
}
Expand All @@ -70,17 +71,24 @@ struct Data {
pub governance_principals: Vec<Principal>,
pub nns_governance_canister_id: CanisterId,
pub nns_ledger_canister_id: CanisterId,
#[serde(default = "cmc")]
pub cycles_minting_canister_id: CanisterId,
pub cycles_dispenser_canister_id: CanisterId,
pub neurons: Timestamped<Vec<Neuron>>,
pub rng_seed: [u8; 32],
pub test_mode: bool,
}

fn cmc() -> CanisterId {
CanisterId::from_text("rkp4c-7iaaa-aaaaa-aaaca-cai").unwrap()
}

impl Data {
pub fn new(
governance_principals: Vec<Principal>,
nns_governance_canister_id: CanisterId,
nns_ledger_canister_id: CanisterId,
cycles_minting_canister_id: CanisterId,
cycles_dispenser_canister_id: CanisterId,
test_mode: bool,
) -> Data {
Expand All @@ -89,6 +97,7 @@ impl Data {
governance_principals,
nns_governance_canister_id,
nns_ledger_canister_id,
cycles_minting_canister_id,
cycles_dispenser_canister_id,
neurons: Timestamped::default(),
rng_seed: [0; 32],
Expand Down Expand Up @@ -126,7 +135,8 @@ pub struct Metrics {

#[derive(Serialize, Debug)]
pub struct CanisterIds {
pub nns_governance_canister_id: CanisterId,
pub nns_ledger_canister_id: CanisterId,
pub nns_governance_canister: CanisterId,
pub nns_ledger_canister: CanisterId,
pub cycles_minting_canister: CanisterId,
pub cycles_dispenser: CanisterId,
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ fn init(args: Args) {
args.governance_principals,
args.nns_governance_canister_id,
args.nns_ledger_canister_id,
args.cycles_minting_canister_id,
args.cycles_dispenser_canister_id,
args.test_mode,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use ic_cdk::api::management_canister::http_request::{
use ic_cdk::query;
use ic_transport_types::EnvelopeContent;
use neuron_controller_canister::manage_nns_neuron::{Response::*, *};
use nns_governance_canister::types::manage_neuron::Command;
use nns_governance_canister::types::ManageNeuron;
use rand::Rng;
use types::CanisterId;
Expand All @@ -20,13 +21,17 @@ const IC_URL: &str = "https://icp-api.io";
#[proposal(guard = "caller_is_governance_principal")]
#[trace]
async fn manage_nns_neuron(args: Args) -> Response {
manage_nns_neuron_impl(args.neuron_id, args.command).await
}

pub(crate) async fn manage_nns_neuron_impl(neuron_id: u64, command: Command) -> Response {
let PrepareResult {
envelope_content,
request_url,
public_key,
key_id,
this_canister_id,
} = mutate_state(|state| prepare(args, state));
} = mutate_state(|state| prepare(neuron_id, command, state));

let body = match sign_envelope(envelope_content, public_key, key_id).await {
Ok(bytes) => bytes,
Expand Down Expand Up @@ -71,7 +76,7 @@ struct PrepareResult {
this_canister_id: CanisterId,
}

fn prepare(args: Args, state: &mut RuntimeState) -> PrepareResult {
fn prepare(neuron_id: u64, command: Command, state: &mut RuntimeState) -> PrepareResult {
let nonce: [u8; 8] = state.env.rng().gen();
let nns_governance_canister_id = state.data.nns_governance_canister_id;

Expand All @@ -81,7 +86,7 @@ fn prepare(args: Args, state: &mut RuntimeState) -> PrepareResult {
sender: state.data.get_principal(),
canister_id: nns_governance_canister_id,
method_name: "manage_neuron".to_string(),
arg: candid::encode_one(ManageNeuron::from(args)).unwrap(),
arg: candid::encode_one(ManageNeuron::new(neuron_id, command)).unwrap(),
};

PrepareResult {
Expand Down
6 changes: 3 additions & 3 deletions backend/canisters/neuron_controller/impl/src/updates/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
mod manage_nns_neuron;
mod stake_nns_neuron;
mod wallet_receive;
pub mod manage_nns_neuron;
pub mod stake_nns_neuron;
pub mod wallet_receive;
11 changes: 11 additions & 0 deletions backend/external_canisters/cmc/api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "cycles_minting_canister"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
candid = { workspace = true }
candid_gen = { path = "../../../libraries/candid_gen" }
serde = { workspace = true }
3 changes: 3 additions & 0 deletions backend/external_canisters/cmc/api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod queries;

pub use queries::*;
8 changes: 8 additions & 0 deletions backend/external_canisters/cmc/api/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use candid_gen::generate_candid_method_no_args;

fn main() {
generate_candid_method_no_args!(cycles_minting, neuron_maturity_modulation, query);

candid::export_service!();
std::print!("{}", __export_service());
}
1 change: 1 addition & 0 deletions backend/external_canisters/cmc/api/src/queries/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod neuron_maturity_modulation;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub type Response = Result<i32, String>;
Loading

0 comments on commit 71b3add

Please sign in to comment.