Skip to content

Commit 7583476

Browse files
authored
feat(sns): add Root.register_extension (#5946)
This PR adds a new function `Root.register_extension`, that allows registering SNS extensions. For context, the first SNS extension will be the [SNS Liquidity Pools extension](https://forum.dfinity.org/t/sns-liquidity-pools-design/50439).
1 parent 1b73132 commit 7583476

File tree

12 files changed

+488
-112
lines changed

12 files changed

+488
-112
lines changed

rs/nervous_system/agent/src/sns/root.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub struct RootCanister {
2121
pub struct SnsCanisters {
2222
pub sns: Sns,
2323
pub dapps: Vec<PrincipalId>,
24+
pub extensions: Vec<PrincipalId>,
2425
}
2526

2627
impl TryFrom<ListSnsCanistersResponse> for SnsCanisters {
@@ -35,6 +36,7 @@ impl TryFrom<ListSnsCanistersResponse> for SnsCanisters {
3536
index: Some(index_canister_id),
3637
archives,
3738
dapps,
39+
extensions,
3840
} = src
3941
else {
4042
return Err(format!("Some SNS canisters were missing: {:?}", src));
@@ -49,7 +51,14 @@ impl TryFrom<ListSnsCanistersResponse> for SnsCanisters {
4951
archive: archives.into_iter().map(ArchiveCanister::new).collect(),
5052
};
5153

52-
Ok(Self { sns, dapps })
54+
let extensions =
55+
extensions.map_or_else(Vec::new, |extensions| extensions.extension_canister_ids);
56+
57+
Ok(Self {
58+
sns,
59+
dapps,
60+
extensions,
61+
})
5362
}
5463
}
5564

rs/sns/cli/src/upgrade_sns_controlled_canister.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -323,11 +323,19 @@ pub async fn find_sns<C: CallCanisters>(
323323
let root_canister = sns::root::RootCanister { canister_id };
324324

325325
let response = root_canister.list_sns_canisters(agent).await?;
326-
let SnsCanisters { sns, dapps } =
327-
SnsCanisters::try_from(response).map_err(UpgradeSnsControlledCanisterError::Client)?;
326+
let SnsCanisters {
327+
sns,
328+
dapps,
329+
extensions,
330+
} = response
331+
.try_into()
332+
.map_err(UpgradeSnsControlledCanisterError::Client)?;
333+
334+
let mut sns_controlled_canisters = dapps;
335+
sns_controlled_canisters.extend(extensions.iter());
328336

329337
// Check that the target is indeed controlled by this SNS.
330-
if !BTreeSet::from_iter(&dapps[..]).contains(&target_canister_id.get()) {
338+
if !BTreeSet::from_iter(&sns_controlled_canisters[..]).contains(&target_canister_id.get()) {
331339
return Err(UpgradeSnsControlledCanisterError::Client(format!(
332340
"{} is not one of the canisters controlled by the SNS with Root canister {}",
333341
target_canister_id.get(),

rs/sns/init/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,11 +652,15 @@ impl SnsInitPayload {
652652
.collect(),
653653
};
654654

655+
// TODO: Support specifying extensions at SNS initialization.
656+
let extensions = Some(vec![].into());
657+
655658
SnsRootCanister {
656659
governance_canister_id: Some(sns_canister_ids.governance),
657660
ledger_canister_id: Some(sns_canister_ids.ledger),
658661
swap_canister_id: Some(sns_canister_ids.swap),
659662
dapp_canister_ids,
663+
extensions,
660664
archive_canister_ids: vec![],
661665
index_canister_id: Some(sns_canister_ids.index),
662666
testflight,

rs/sns/integration_tests/src/root.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ use ic_nervous_system_root::change_canister::ChangeCanisterRequest;
1313
use ic_nns_constants::GOVERNANCE_CANISTER_ID;
1414
use ic_nns_test_utils::state_test_helpers::{get_controllers, set_controllers, update_with_sender};
1515
use ic_sns_root::{
16-
pb::v1::SnsRootCanister, GetSnsCanistersSummaryRequest, GetSnsCanistersSummaryResponse,
16+
pb::v1::{Extensions, SnsRootCanister},
17+
GetSnsCanistersSummaryRequest, GetSnsCanistersSummaryResponse,
1718
};
1819
use ic_sns_test_utils::{
1920
itest_helpers::{
@@ -40,6 +41,9 @@ fn test_get_status() {
4041
ledger_canister_id: Some(PrincipalId::new_user_test_id(43)),
4142
swap_canister_id: Some(PrincipalId::new_user_test_id(44)),
4243
dapp_canister_ids: vec![],
44+
extensions: Some(Extensions {
45+
extension_canister_ids: vec![],
46+
}),
4347
archive_canister_ids: vec![],
4448
index_canister_id: Some(PrincipalId::new_user_test_id(45)),
4549
testflight: false,

rs/sns/integration_tests/src/timers.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use ic_nervous_system_proto::pb::v1::{
66
use ic_nns_test_utils::sns_wasm::{build_governance_sns_wasm, build_root_sns_wasm};
77
use ic_sns_governance::pb::v1::governance::GovernanceCachedMetrics;
88
use ic_sns_governance::{init::GovernanceCanisterInitPayloadBuilder, pb::v1::Governance};
9-
use ic_sns_root::pb::v1::SnsRootCanister;
9+
use ic_sns_root::pb::v1::{Extensions, SnsRootCanister};
1010
use ic_sns_swap::pb::v1::{
1111
GetStateRequest, GetStateResponse, Init, Lifecycle, NeuronBasketConstructionParameters,
1212
};
@@ -80,6 +80,9 @@ fn root_init() -> SnsRootCanister {
8080
index_canister_id: Some(PrincipalId::new_anonymous()),
8181
archive_canister_ids: vec![],
8282
dapp_canister_ids: vec![],
83+
extensions: Some(Extensions {
84+
extension_canister_ids: vec![],
85+
}),
8386
testflight: false,
8487
timers: None,
8588
}

rs/sns/root/canister/canister.rs

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use ic_nervous_system_proto::pb::v1::{
1919
};
2020
use ic_nervous_system_root::change_canister::ChangeCanisterRequest;
2121
use ic_nervous_system_runtime::{CdkRuntime, Runtime};
22+
use ic_sns_root::pb::v1::{RegisterExtensionRequest, RegisterExtensionResponse};
2223
use ic_sns_root::{
2324
logs::{ERROR, INFO},
2425
pb::v1::{
@@ -203,7 +204,7 @@ fn list_sns_canisters(_request: ListSnsCanistersRequest) -> ListSnsCanistersResp
203204
STATE.with(|sns_root_canister| {
204205
sns_root_canister
205206
.borrow()
206-
.list_sns_canisters(ic_cdk::api::id())
207+
.list_sns_canisters(PrincipalId(ic_cdk::api::id()))
207208
})
208209
}
209210

@@ -248,6 +249,32 @@ fn change_canister(request: ChangeCanisterRequest) {
248249
});
249250
}
250251

252+
#[candid_method(update)]
253+
#[update]
254+
async fn register_extension(request: RegisterExtensionRequest) -> RegisterExtensionResponse {
255+
log!(INFO, "register_extension");
256+
assert_eq_governance_canister_id(PrincipalId(ic_cdk::api::caller()));
257+
258+
let canister_id = match PrincipalId::try_from(request) {
259+
Ok(canister_id) => canister_id,
260+
Err(err) => {
261+
return RegisterExtensionResponse::from(Err(err));
262+
}
263+
};
264+
265+
let root_canister_id = PrincipalId(ic_cdk::api::id());
266+
267+
let result = SnsRootCanister::register_extension(
268+
&STATE,
269+
&ManagementCanisterClientImpl::<CanisterRuntime>::new(None),
270+
root_canister_id,
271+
canister_id,
272+
)
273+
.await;
274+
275+
RegisterExtensionResponse::from(result)
276+
}
277+
251278
/// This function is deprecated, and `register_dapp_canisters` should be used
252279
/// instead. (NNS1-1991)
253280
///
@@ -277,7 +304,7 @@ async fn register_dapp_canister(
277304
let RegisterDappCanistersResponse {} = SnsRootCanister::register_dapp_canisters(
278305
&STATE,
279306
&ManagementCanisterClientImpl::<CanisterRuntime>::new(None),
280-
ic_cdk::api::id(),
307+
PrincipalId(ic_cdk::api::id()),
281308
request,
282309
)
283310
.await;
@@ -304,7 +331,7 @@ async fn register_dapp_canisters(
304331
SnsRootCanister::register_dapp_canisters(
305332
&STATE,
306333
&ManagementCanisterClientImpl::<CanisterRuntime>::new(None),
307-
ic_cdk::api::id(),
334+
PrincipalId(ic_cdk::api::id()),
308335
request,
309336
)
310337
.await
@@ -333,7 +360,7 @@ async fn set_dapp_controllers(request: SetDappControllersRequest) -> SetDappCont
333360
SnsRootCanister::set_dapp_controllers(
334361
&STATE,
335362
&ManagementCanisterClientImpl::<CanisterRuntime>::new(None),
336-
ic_cdk::api::id(),
363+
PrincipalId(ic_cdk::api::id()),
337364
PrincipalId(ic_cdk::api::caller()),
338365
&request,
339366
)

rs/sns/root/canister/root.did

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ type ListSnsCanistersResponse = record {
113113
index : opt principal;
114114
governance : opt principal;
115115
dapps : vec principal;
116+
extensions : opt Extensions;
116117
archives : vec principal;
117118
};
118119

@@ -137,6 +138,19 @@ type ManageDappCanisterSettingsResponse = record {
137138
failure_reason : opt text;
138139
};
139140

141+
type RegisterExtensionRequest = record {
142+
canister_id : opt principal;
143+
};
144+
145+
type RegisterExtensionResult = variant {
146+
Ok : record {};
147+
Err : CanisterCallError;
148+
};
149+
150+
type RegisterExtensionResponse = record {
151+
result : opt RegisterExtensionResult;
152+
};
153+
140154
type RegisterDappCanisterRequest = record {
141155
canister_id : opt principal;
142156
};
@@ -154,8 +168,13 @@ type SetDappControllersResponse = record {
154168
failed_updates : vec FailedUpdate;
155169
};
156170

171+
type Extensions = record {
172+
extension_canister_ids : vec principal;
173+
};
174+
157175
type SnsRootCanister = record {
158176
dapp_canister_ids : vec principal;
177+
extensions : opt Extensions;
159178
testflight : bool;
160179
archive_canister_ids : vec principal;
161180
governance_canister_id : opt principal;
@@ -186,6 +205,7 @@ service : (SnsRootCanister) -> {
186205
manage_dapp_canister_settings : (ManageDappCanisterSettingsRequest) -> (
187206
ManageDappCanisterSettingsResponse,
188207
);
208+
register_extension : (RegisterExtensionRequest) -> (RegisterExtensionResponse);
189209
register_dapp_canister : (RegisterDappCanisterRequest) -> (record {});
190210
register_dapp_canisters : (RegisterDappCanistersRequest) -> (record {});
191211
set_dapp_controllers : (SetDappControllersRequest) -> (

rs/sns/root/proto/ic_sns_root/pb/v1/root.proto

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ message SnsRootCanister {
2626
// Dapp canister IDs.
2727
repeated ic_base_types.pb.v1.PrincipalId dapp_canister_ids = 3;
2828

29+
// Extension canister IDs.
30+
optional Extensions extensions = 11;
31+
2932
// Required.
3033
//
3134
// The swap canister ID.
@@ -52,6 +55,19 @@ message SnsRootCanister {
5255
optional ic_nervous_system.pb.v1.Timers timers = 10;
5356
}
5457

58+
message RegisterExtensionRequest {
59+
ic_base_types.pb.v1.PrincipalId canister_id = 1;
60+
}
61+
62+
message RegisterExtensionResponse {
63+
message Ok {}
64+
65+
oneof result {
66+
Ok ok = 1;
67+
CanisterCallError err = 2;
68+
}
69+
}
70+
5571
message RegisterDappCanisterRequest {
5672
ic_base_types.pb.v1.PrincipalId canister_id = 1;
5773
}
@@ -97,6 +113,10 @@ message ListSnsCanistersRequest {
97113
// This struct intentionally left blank (for now).
98114
}
99115

116+
message Extensions {
117+
repeated ic_base_types.pb.v1.PrincipalId extension_canister_ids = 1;
118+
}
119+
100120
// Response struct for the ListSnsCanisters API on the
101121
// SNS Root canister. ListSnsCanisters will return Principals
102122
// of all the associated canisters in an SNS.
@@ -108,6 +128,7 @@ message ListSnsCanistersResponse {
108128
repeated ic_base_types.pb.v1.PrincipalId dapps = 5;
109129
repeated ic_base_types.pb.v1.PrincipalId archives = 6;
110130
ic_base_types.pb.v1.PrincipalId index = 7;
131+
optional Extensions extensions = 8;
111132
}
112133

113134
enum LogVisibility {

rs/sns/root/src/gen/ic_sns_root.pb.v1.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ pub struct SnsRootCanister {
2828
/// Dapp canister IDs.
2929
#[prost(message, repeated, tag = "3")]
3030
pub dapp_canister_ids: ::prost::alloc::vec::Vec<::ic_base_types::PrincipalId>,
31+
/// Extension canister IDs.
32+
#[prost(message, optional, tag = "11")]
33+
pub extensions: ::core::option::Option<Extensions>,
3134
/// Required.
3235
///
3336
/// The swap canister ID.
@@ -57,6 +60,57 @@ pub struct SnsRootCanister {
5760
PartialEq,
5861
::prost::Message,
5962
)]
63+
pub struct RegisterExtensionRequest {
64+
#[prost(message, optional, tag = "1")]
65+
pub canister_id: ::core::option::Option<::ic_base_types::PrincipalId>,
66+
}
67+
#[derive(
68+
candid::CandidType,
69+
candid::Deserialize,
70+
comparable::Comparable,
71+
Clone,
72+
PartialEq,
73+
::prost::Message,
74+
)]
75+
pub struct RegisterExtensionResponse {
76+
#[prost(oneof = "register_extension_response::Result", tags = "1, 2")]
77+
pub result: ::core::option::Option<register_extension_response::Result>,
78+
}
79+
/// Nested message and enum types in `RegisterExtensionResponse`.
80+
pub mod register_extension_response {
81+
#[derive(
82+
candid::CandidType,
83+
candid::Deserialize,
84+
comparable::Comparable,
85+
Clone,
86+
Copy,
87+
PartialEq,
88+
::prost::Message,
89+
)]
90+
pub struct Ok {}
91+
#[derive(
92+
candid::CandidType,
93+
candid::Deserialize,
94+
comparable::Comparable,
95+
Clone,
96+
PartialEq,
97+
::prost::Oneof,
98+
)]
99+
pub enum Result {
100+
#[prost(message, tag = "1")]
101+
Ok(Ok),
102+
#[prost(message, tag = "2")]
103+
Err(super::CanisterCallError),
104+
}
105+
}
106+
#[derive(
107+
candid::CandidType,
108+
candid::Deserialize,
109+
comparable::Comparable,
110+
Clone,
111+
PartialEq,
112+
::prost::Message,
113+
)]
60114
pub struct RegisterDappCanisterRequest {
61115
#[prost(message, optional, tag = "1")]
62116
pub canister_id: ::core::option::Option<::ic_base_types::PrincipalId>,
@@ -184,6 +238,18 @@ pub struct CanisterCallError {
184238
::prost::Message,
185239
)]
186240
pub struct ListSnsCanistersRequest {}
241+
#[derive(
242+
candid::CandidType,
243+
candid::Deserialize,
244+
comparable::Comparable,
245+
Clone,
246+
PartialEq,
247+
::prost::Message,
248+
)]
249+
pub struct Extensions {
250+
#[prost(message, repeated, tag = "1")]
251+
pub extension_canister_ids: ::prost::alloc::vec::Vec<::ic_base_types::PrincipalId>,
252+
}
187253
/// Response struct for the ListSnsCanisters API on the
188254
/// SNS Root canister. ListSnsCanisters will return Principals
189255
/// of all the associated canisters in an SNS.
@@ -210,6 +276,8 @@ pub struct ListSnsCanistersResponse {
210276
pub archives: ::prost::alloc::vec::Vec<::ic_base_types::PrincipalId>,
211277
#[prost(message, optional, tag = "7")]
212278
pub index: ::core::option::Option<::ic_base_types::PrincipalId>,
279+
#[prost(message, optional, tag = "8")]
280+
pub extensions: ::core::option::Option<Extensions>,
213281
}
214282
#[derive(
215283
candid::CandidType,

0 commit comments

Comments
 (0)