Skip to content

Commit 9d55b95

Browse files
committed
nervous-system-agent: extract 'is_canister_stopped_error' from 'CallCanisters'
'is_canister_stopped' is currently used by PocketIc and ic-agent implementations. However, the 'CallCanisters' trait requires implementing this function. As a result, some trait implementations have dummy implementation for this method which panics in runtime. To avoid runtime panic, this method is extracted into a dedicated 'CallCanistersWithStoppedCanisterError' trait that inherits 'CallCanisters'. This trait is only implemented for PocketIc and ic-agent's. An attempt to use 'is_canister_stopped_error' for other implementation will cause compilation error instead of runtime panic.
1 parent e293b47 commit 9d55b95

File tree

8 files changed

+29
-43
lines changed

8 files changed

+29
-43
lines changed

rs/nervous_system/agent/src/agent_impl.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{CallCanisters, ProgressNetwork};
1+
use crate::{CallCanisters, CallCanistersWithStoppedCanisterError, ProgressNetwork};
22
use crate::{CanisterInfo, Request};
33
use candid::Principal;
44
use ic_agent::agent::{RejectCode, RejectResponse};
@@ -141,7 +141,9 @@ impl CallCanisters for Agent {
141141
fn caller(&self) -> Result<Principal, Self::Error> {
142142
self.get_principal().map_err(Self::Error::Identity)
143143
}
144+
}
144145

146+
impl CallCanistersWithStoppedCanisterError for Agent {
145147
fn is_canister_stopped_error(&self, err: &Self::Error) -> bool {
146148
match err {
147149
AgentCallError::Agent(AgentError::CertifiedReject(RejectResponse {

rs/nervous_system/agent/src/helpers/nns.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ use icp_ledger::{AccountIdentifier, Subaccount, Tokens, TransferArgs};
1414
use crate::nns::governance::{get_proposal_info, manage_neuron};
1515
use crate::nns::sns_wasm::get_deployed_sns_by_proposal_id;
1616
use crate::sns::Sns;
17-
use crate::{CallCanisters, ProgressNetwork};
17+
use crate::{CallCanisters, CallCanistersWithStoppedCanisterError, ProgressNetwork};
1818

1919
// TODO @rvem: we probably need more meaningful error type rather than just 'String'
2020

21-
pub async fn propose_and_wait<C: CallCanisters + ProgressNetwork>(
21+
pub async fn propose_and_wait<C: CallCanistersWithStoppedCanisterError + ProgressNetwork>(
2222
agent: &C,
2323
neuron_id: NeuronId,
2424
proposal: MakeProposalRequest,
@@ -40,7 +40,7 @@ pub async fn propose_and_wait<C: CallCanisters + ProgressNetwork>(
4040
}
4141
}
4242

43-
pub async fn propose_to_deploy_sns_and_wait<C: CallCanisters + ProgressNetwork>(
43+
pub async fn propose_to_deploy_sns_and_wait<C: CallCanistersWithStoppedCanisterError + ProgressNetwork>(
4444
agent: &C,
4545
neuron_id: NeuronId,
4646
create_service_nervous_system: CreateServiceNervousSystem,
@@ -73,7 +73,7 @@ pub async fn propose_to_deploy_sns_and_wait<C: CallCanisters + ProgressNetwork>(
7373
Ok((sns, nns_proposal_id))
7474
}
7575

76-
pub async fn wait_for_proposal_execution<C: CallCanisters + ProgressNetwork>(
76+
pub async fn wait_for_proposal_execution<C: CallCanistersWithStoppedCanisterError + ProgressNetwork>(
7777
agent: &C,
7878
proposal_id: ProposalId,
7979
) -> Result<ProposalInfo, String> {

rs/nervous_system/agent/src/helpers/sns.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use thiserror::Error;
55
use crate::nns::ledger::icrc1_transfer;
66
use crate::sns::governance::{GovernanceCanister, ProposalSubmissionError, SubmittedProposal};
77
use crate::sns::swap::SwapCanister;
8-
use crate::{CallCanisters, ProgressNetwork};
8+
use crate::{CallCanisters, CallCanistersWithStoppedCanisterError, ProgressNetwork};
99
use candid::Nat;
1010
use ic_sns_governance_api::pb::v1::{
1111
get_proposal_response, ListNeurons, NeuronId, Proposal, ProposalData, ProposalId,
@@ -26,7 +26,7 @@ pub enum SnsProposalError {
2626
ProposalError(ProposalId, String),
2727
}
2828

29-
pub async fn propose_and_wait<C: CallCanisters + ProgressNetwork>(
29+
pub async fn propose_and_wait<C: CallCanistersWithStoppedCanisterError + ProgressNetwork>(
3030
agent: &C,
3131
neuron_id: NeuronId,
3232
sns_governance_canister: GovernanceCanister,
@@ -42,7 +42,7 @@ pub async fn propose_and_wait<C: CallCanisters + ProgressNetwork>(
4242
wait_for_proposal_execution(agent, sns_governance_canister, proposal_id).await
4343
}
4444

45-
pub async fn wait_for_proposal_execution<C: CallCanisters + ProgressNetwork>(
45+
pub async fn wait_for_proposal_execution<C: CallCanistersWithStoppedCanisterError + ProgressNetwork>(
4646
agent: &C,
4747
sns_governance_canister: GovernanceCanister,
4848
proposal_id: ProposalId,

rs/nervous_system/agent/src/lib.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,16 @@ pub trait CallCanisters: sealed::Sealed {
5454
&self,
5555
canister_id: impl Into<Principal> + Send,
5656
) -> impl Future<Output = Result<CanisterInfo, Self::Error>> + Send;
57+
}
5758

58-
// Functions that use 'call' need to be able
59-
// to determine if a call to the canister failed due to the canister being stopped.
60-
// Matching on a specific error outside of the trait implementation is not viable
61-
// since the 'Error' type is different for each trait implementation and thus we can
62-
// only match on specific implementations errors in the trait implementation directly.
59+
// Functions that use 'call' need to be able
60+
// to determine if a call to the canister failed due to the canister being stopped.
61+
// Matching on a specific error outside of the trait implementation is not viable
62+
// since the 'Error' type is different for each trait implementation and thus we can
63+
// only match on specific implementations errors in the trait implementation directly.
64+
//
65+
// We're extending CallCanisters trait to allow this.
66+
pub trait CallCanistersWithStoppedCanisterError: CallCanisters {
6367
fn is_canister_stopped_error(&self, err: &Self::Error) -> bool;
6468
}
6569

rs/nervous_system/agent/src/mock.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,6 @@ impl CallCanisters for MockCallCanisters {
9696
#[allow(unreachable_code)]
9797
std::future::ready(Err(MockCallCanistersError("UNREACHABLE".to_string())))
9898
}
99-
100-
/// This could be implemented later, but for now, it's not needed.
101-
fn is_canister_stopped_error(&self, _err: &Self::Error) -> bool {
102-
unimplemented!();
103-
}
10499
}
105100

106101
#[derive(Debug, PartialEq, Eq)]

rs/nervous_system/agent/src/pocketic_impl.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::time::Duration;
33
use crate::management_canister::requests::{
44
CanisterStatusArgs, DeleteCanisterArgs, StopCanisterArgs, StoredChunksArgs, UploadChunkArgs,
55
};
6-
use crate::Request;
6+
use crate::{CallCanistersWithStoppedCanisterError, Request};
77
use crate::{CallCanisters, CanisterInfo, ProgressNetwork};
88
use candid::Principal;
99
use ic_management_canister_types::{CanisterStatusResult, DefiniteCanisterSettings};
@@ -181,7 +181,9 @@ impl CallCanisters for PocketIcAgent<'_> {
181181
fn caller(&self) -> Result<Principal, Self::Error> {
182182
Ok(self.sender)
183183
}
184+
}
184185

186+
impl CallCanistersWithStoppedCanisterError for PocketIcAgent<'_> {
185187
fn is_canister_stopped_error(&self, err: &Self::Error) -> bool {
186188
self.pocket_ic.is_canister_stopped_error(err)
187189
}
@@ -211,7 +213,9 @@ impl CallCanisters for PocketIc {
211213
fn caller(&self) -> Result<Principal, Self::Error> {
212214
Ok(Principal::anonymous())
213215
}
216+
}
214217

218+
impl CallCanistersWithStoppedCanisterError for PocketIc {
215219
fn is_canister_stopped_error(&self, err: &Self::Error) -> bool {
216220
match err {
217221
PocketIcCallError::PocketIc(err) => {

rs/nervous_system/agent/src/state_machine_impl.rs

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use crate::{CallCanisters, CanisterInfo, Request};
22
use candid::Principal;
33
use ic_base_types::{CanisterId, PrincipalId};
4-
use ic_error_types::ErrorCode;
54
use ic_state_machine_tests::{StateMachine, UserError, WasmResult};
65
use std::time::Duration;
76
use thiserror::Error;
@@ -99,10 +98,6 @@ impl CallCanisters for StateMachineAgent<'_> {
9998
.collect(),
10099
})
101100
}
102-
103-
fn is_canister_stopped_error(&self, err: &Self::Error) -> bool {
104-
self.state_machine.is_canister_stopped_error(err)
105-
}
106101
}
107102

108103
impl CallCanisters for StateMachine {
@@ -130,13 +125,4 @@ impl CallCanisters for StateMachine {
130125
.canister_info(canister_id)
131126
.await
132127
}
133-
134-
fn is_canister_stopped_error(&self, err: &Self::Error) -> bool {
135-
match err {
136-
StateMachineCallError::IngressStateError(err) => {
137-
[ErrorCode::CanisterStopped, ErrorCode::CanisterStopping].contains(&err.code())
138-
}
139-
_ => false,
140-
}
141-
}
142128
}

rs/sns/testing/src/sns.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use ic_nervous_system_agent::{
1212
},
1313
nns::{ledger::transfer, sns_wasm::list_deployed_snses},
1414
sns::{governance::GovernanceCanister, swap::SwapCanister, Sns},
15-
CallCanisters, ProgressNetwork,
15+
CallCanisters, CallCanistersWithStoppedCanisterError, ProgressNetwork,
1616
};
1717
use ic_nervous_system_clients::canister_status::CanisterStatusType;
1818
use ic_nervous_system_integration_tests::{
@@ -48,17 +48,12 @@ pub const DEFAULT_SWAP_PARTICIPANTS_NUMBER: usize = 20;
4848
// Creates SNS using agents provided as arguments:
4949
// 1) neuron_agent - agent that controlls 'neuron_id'.
5050
// 2) neuron_id - ID of the neuron that has a sufficient amount of stake to propose the SNS creation and adopt the proposal.
51-
// 2) dev_participant_agent - Agent that will be used as an initial neuron in a newly created SNS. All other
51+
// 3) dev_participant_agent - Agent that will be used as an initial neuron in a newly created SNS. All other
5252
// neurons will follow the dev neuron.
53-
// 3) swap_treasury_agent - Agent for the identity that has sufficient amout of ICP tokens to close the swap and
54-
// pay for cycles needed canister upgrade as well as all associated costs.
55-
// 4) swap_participants_agents - Agents for the identities that will participate in the swap. The actual number of participants
56-
// is determined by the swap parameters. So only some of these agents might be used. Each agent participates in the swap,
57-
// follows the dev neuron for 'UpgradeSnsControlledCanister' proposals and increases its dissolve delay to be able to vote.
58-
// 5) dapp_canister_ids - Canister IDs of the DApps that will be added to the SNS.
53+
// 4) dapp_canister_ids - Canister IDs of the DApps that will be added to the SNS.
5954
//
6055
// Returns SNS canisters IDs.
61-
pub async fn create_sns<C: CallCanisters + ProgressNetwork + BuildEphemeralAgent>(
56+
pub async fn create_sns<C: CallCanistersWithStoppedCanisterError + ProgressNetwork + BuildEphemeralAgent>(
6257
neuron_agent: &C,
6358
neuron_id: NeuronId,
6459
dev_participant_agent: &C,
@@ -367,7 +362,7 @@ pub async fn complete_sns_swap<C: CallCanisters + ProgressNetwork + BuildEphemer
367362
// 2) sns - SNS canisters.
368363
// 3) canister_id - ID of the canister that will be upgraded.
369364
// 4) upgrade_arg - Arguments that will be passed to the canister during the upgrade.
370-
pub async fn upgrade_sns_controlled_test_canister<C: CallCanisters + ProgressNetwork>(
365+
pub async fn upgrade_sns_controlled_test_canister<C: CallCanistersWithStoppedCanisterError + ProgressNetwork>(
371366
dev_participant_agent: &C,
372367
sns: Sns,
373368
canister_id: CanisterId,

0 commit comments

Comments
 (0)