Skip to content

Commit e2a1f9c

Browse files
author
Felipe Sodré
authored
chore(icp-rosetta): [FI-1563] Migrate Rosetta's voting tests (dfinity#2815)
This PR migrates ICP rosetta's voting functionality tests to pocket ic based system tests
1 parent 9d42a91 commit e2a1f9c

File tree

13 files changed

+616
-241
lines changed

13 files changed

+616
-241
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rs/rosetta-api/icp/client/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ DEPENDENCIES = [
88
"//rs/crypto/ed25519",
99
"//rs/crypto/secp256k1",
1010
"//rs/ledger_suite/icp:icp_ledger",
11+
"//rs/nns/governance/api",
1112
"//rs/rosetta-api/common/rosetta_core:rosetta-core",
1213
"//rs/rosetta-api/icp:ic-rosetta-api",
1314
"//rs/rosetta-api/icp:rosetta-api",

rs/rosetta-api/icp/client/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ ic-crypto-ed25519 = { path = "../../../crypto/ed25519" }
2424
icp-ledger = { path = "../../../ledger_suite/icp" }
2525
icrc-ledger-types = { path = "../../../../packages/icrc-ledger-types" }
2626
ic-base-types = { path = "../../../types/base_types" }
27+
ic-nns-governance-api = { path = "../../../nns/governance/api" }
2728

2829
[dev-dependencies]
2930
ic-icp-rosetta-runner = { path = "../runner" }

rs/rosetta-api/icp/client/src/lib.rs

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ use anyhow::Context;
33
use candid::Nat;
44
use candid::Principal;
55
use ic_base_types::PrincipalId;
6+
use ic_nns_governance_api::pb::v1::Proposal;
7+
use ic_rosetta_api::ledger_client::pending_proposals_response::PendingProposalsResponse;
68
use ic_rosetta_api::models::seconds::Seconds;
79
use ic_rosetta_api::models::AccountType;
810
use ic_rosetta_api::models::BlockIdentifier;
@@ -16,6 +18,7 @@ use ic_rosetta_api::request_types::KeyMetadata;
1618
use ic_rosetta_api::request_types::NeuronIdentifierMetadata;
1719
use ic_rosetta_api::request_types::NeuronInfoMetadata;
1820
use ic_rosetta_api::request_types::PublicKeyOrPrincipal;
21+
use ic_rosetta_api::request_types::RegisterVoteMetadata;
1922
use ic_rosetta_api::request_types::RequestType;
2023
use ic_rosetta_api::request_types::SetDissolveTimestampMetadata;
2124
use ic_rosetta_api::request_types::SpawnMetadata;
@@ -32,6 +35,7 @@ use rosetta_core::identifiers::TransactionIdentifier;
3235
use rosetta_core::models::CurveType;
3336
use rosetta_core::models::RosettaSupportedKeyPair;
3437
use rosetta_core::objects::Amount;
38+
use rosetta_core::objects::ObjectMap;
3539
use rosetta_core::objects::Operation;
3640
use rosetta_core::objects::PublicKey;
3741
use rosetta_core::objects::Signature;
@@ -40,7 +44,6 @@ use rosetta_core::response_types::*;
4044
use serde::{Deserialize, Serialize};
4145
use std::fmt::Debug;
4246
use url::ParseError;
43-
4447
pub struct RosettaClient {
4548
pub url: Url,
4649
pub http_client: Client,
@@ -304,6 +307,37 @@ impl RosettaClient {
304307
}])
305308
}
306309

310+
pub async fn build_register_vote_operations(
311+
signer_principal: Principal,
312+
neuron_index: u64,
313+
proposal: u64,
314+
vote: i32,
315+
) -> anyhow::Result<Vec<Operation>> {
316+
Ok(vec![Operation {
317+
operation_identifier: OperationIdentifier {
318+
index: 0,
319+
network_index: None,
320+
},
321+
related_operations: None,
322+
type_: "REGISTER_VOTE".to_string(),
323+
status: None,
324+
account: Some(rosetta_core::identifiers::AccountIdentifier::from(
325+
AccountIdentifier::new(PrincipalId(signer_principal), None),
326+
)),
327+
amount: None,
328+
coin_change: None,
329+
metadata: Some(
330+
RegisterVoteMetadata {
331+
neuron_index,
332+
vote,
333+
proposal: Some(proposal),
334+
}
335+
.try_into()
336+
.map_err(|e| anyhow::anyhow!("Failed to convert metadata: {:?}", e))?,
337+
),
338+
}])
339+
}
340+
307341
pub async fn build_change_auto_stake_maturity_operations(
308342
signer_principal: Principal,
309343
neuron_index: u64,
@@ -1129,6 +1163,59 @@ impl RosettaClient {
11291163
.await
11301164
}
11311165

1166+
// Register a vote on a proposal using a specific neuron.
1167+
pub async fn register_vote<T>(
1168+
&self,
1169+
network_identifier: NetworkIdentifier,
1170+
signer_keypair: &T,
1171+
register_vote_args: RosettaRegisterVoteArgs,
1172+
) -> anyhow::Result<ConstructionSubmitResponse>
1173+
where
1174+
T: RosettaSupportedKeyPair,
1175+
{
1176+
let register_vote_operations = RosettaClient::build_register_vote_operations(
1177+
signer_keypair.generate_principal_id()?.0,
1178+
register_vote_args.neuron_index.unwrap_or(0),
1179+
register_vote_args.proposal,
1180+
register_vote_args.vote,
1181+
)
1182+
.await?;
1183+
1184+
self.make_submit_and_wait_for_transaction(
1185+
signer_keypair,
1186+
network_identifier,
1187+
register_vote_operations,
1188+
None,
1189+
None,
1190+
)
1191+
.await
1192+
}
1193+
1194+
// Retrieves the list of proposals that are currently pending.
1195+
pub async fn get_pending_proposals(
1196+
&self,
1197+
network_identifier: NetworkIdentifier,
1198+
) -> anyhow::Result<Vec<Proposal>, String> {
1199+
let response = self
1200+
.call(CallRequest::new(
1201+
network_identifier.clone(),
1202+
"get_pending_proposals".to_owned(),
1203+
ObjectMap::new(),
1204+
))
1205+
.await
1206+
.unwrap();
1207+
1208+
let pending_proposals: Vec<Proposal> =
1209+
PendingProposalsResponse::try_from(Some(response.result))
1210+
.unwrap()
1211+
.pending_proposals
1212+
.into_iter()
1213+
.map(|p| p.proposal.unwrap())
1214+
.collect();
1215+
1216+
Ok(pending_proposals)
1217+
}
1218+
11321219
/// A neuron can be set to automatically restake its maturity.
11331220
pub async fn change_auto_stake_maturity<T>(
11341221
&self,
@@ -1536,6 +1623,47 @@ impl RosettaSetNeuronDissolveDelayArgsBuilder {
15361623
}
15371624
}
15381625

1626+
pub struct RosettaRegisterVoteArgs {
1627+
pub neuron_index: Option<u64>,
1628+
pub proposal: u64,
1629+
pub vote: i32,
1630+
}
1631+
1632+
impl RosettaRegisterVoteArgs {
1633+
pub fn builder(proposal: u64, vote: i32) -> RosettaRegisterVoteArgsBuilder {
1634+
RosettaRegisterVoteArgsBuilder::new(proposal, vote)
1635+
}
1636+
}
1637+
1638+
pub struct RosettaRegisterVoteArgsBuilder {
1639+
proposal: u64,
1640+
vote: i32,
1641+
neuron_index: Option<u64>,
1642+
}
1643+
1644+
impl RosettaRegisterVoteArgsBuilder {
1645+
pub fn new(proposal: u64, vote: i32) -> Self {
1646+
Self {
1647+
proposal,
1648+
vote,
1649+
neuron_index: None,
1650+
}
1651+
}
1652+
1653+
pub fn with_neuron_index(mut self, neuron_index: u64) -> Self {
1654+
self.neuron_index = Some(neuron_index);
1655+
self
1656+
}
1657+
1658+
pub fn build(self) -> RosettaRegisterVoteArgs {
1659+
RosettaRegisterVoteArgs {
1660+
proposal: self.proposal,
1661+
vote: self.vote,
1662+
neuron_index: self.neuron_index,
1663+
}
1664+
}
1665+
}
1666+
15391667
pub struct RosettaIncreaseNeuronStakeArgs {
15401668
pub neuron_index: Option<u64>,
15411669
pub additional_stake: Nat,
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
use candid::{Decode, Encode, Principal};
2+
use ic_agent::Agent;
3+
use ic_base_types::PrincipalId;
4+
use ic_nns_common::{pb::v1::ProposalId, types::NeuronId};
5+
use ic_nns_governance_api::pb::v1::Motion;
6+
use ic_nns_governance_api::pb::v1::ProposalActionRequest;
7+
use ic_nns_governance_api::{
8+
pb::v1::{
9+
manage_neuron_response, manage_neuron_response::MakeProposalResponse, MakeProposalRequest,
10+
ManageNeuronResponse, ProposalInfo,
11+
},
12+
proposal_submission_helpers::create_make_proposal_payload,
13+
};
14+
15+
pub struct GovernanceClient {
16+
agent: Agent,
17+
governance_principal: Principal,
18+
}
19+
20+
impl GovernanceClient {
21+
pub fn new(agent: Agent, governance_principal: Principal) -> GovernanceClient {
22+
GovernanceClient {
23+
agent,
24+
governance_principal,
25+
}
26+
}
27+
28+
pub async fn submit_proposal(
29+
&self,
30+
principal: Principal,
31+
neuron_id: NeuronId,
32+
title: &str,
33+
summary: &str,
34+
motion_text: &str,
35+
) -> ProposalId {
36+
let proposal = MakeProposalRequest {
37+
title: Some(title.to_string()),
38+
summary: summary.to_string(),
39+
action: Some(ProposalActionRequest::Motion(Motion {
40+
motion_text: motion_text.to_string(),
41+
})),
42+
..Default::default()
43+
};
44+
45+
let neuron_controller = PrincipalId::from(principal);
46+
47+
let manage_neuron = create_make_proposal_payload(proposal.clone(), &neuron_id);
48+
let arg = Encode!(&manage_neuron).expect("Error while encoding arg.");
49+
let res = self
50+
.agent
51+
.update(&self.governance_principal, "manage_neuron")
52+
.with_arg(arg)
53+
.call_and_wait()
54+
.await
55+
.expect("Error while calling endpoint.");
56+
//Make sure that the one making the proposal is also the controller of the neuron
57+
assert_eq!(
58+
PrincipalId::from(self.agent.get_principal().unwrap()),
59+
neuron_controller
60+
);
61+
let manage_neuron_res =
62+
Decode!(res.as_slice(), ManageNeuronResponse).expect("Error while decoding response.");
63+
if let ManageNeuronResponse {
64+
command:
65+
Some(manage_neuron_response::Command::MakeProposal(MakeProposalResponse {
66+
proposal_id,
67+
..
68+
})),
69+
} = manage_neuron_res
70+
{
71+
assert!(proposal_id.is_some());
72+
let arg = Encode!(&proposal_id.unwrap().id).expect("Error while encoding arg.");
73+
self.agent
74+
.query(&self.governance_principal, "get_proposal_info")
75+
.with_arg(arg)
76+
.call()
77+
.await
78+
.expect("Error while calling endpoint.");
79+
proposal_id.unwrap()
80+
} else {
81+
panic!(
82+
"Making Proposal was unsuccessful --> Response : {:?}",
83+
manage_neuron_res
84+
)
85+
}
86+
}
87+
88+
pub async fn get_pending_proposals(&self) -> Vec<ProposalInfo> {
89+
let arg = Encode!(&"").expect("Error while encoding arg.");
90+
let res = self
91+
.agent
92+
.query(&self.governance_principal, "get_pending_proposals")
93+
.with_arg(arg)
94+
.call()
95+
.await
96+
.expect("Error while calling endpoint.");
97+
Decode!(res.as_slice(), Vec<ProposalInfo>).expect("Error while decoding response.")
98+
}
99+
100+
pub async fn get_proposal_info(&self, proposal_id: ProposalId) -> Option<ProposalInfo> {
101+
let arg = Encode!(&proposal_id.id).expect("Error while encoding arg.");
102+
let res = self
103+
.agent
104+
.query(&self.governance_principal, "get_proposal_info")
105+
.with_arg(arg)
106+
.call()
107+
.await
108+
.expect("Error while calling endpoint.");
109+
Decode!(res.as_slice(), Option<ProposalInfo>).expect("Error while decoding response.")
110+
}
111+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
pub mod constants;
2+
pub mod governance_client;
23
pub mod system_test_environment;
34
pub mod utils;

rs/rosetta-api/icp/tests/system_tests/common/system_test_environment.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use super::utils::memo_bytebuf_to_u64;
12
use crate::common::utils::get_custom_agent;
23
use crate::common::utils::get_test_agent;
34
use crate::common::utils::wait_for_rosetta_to_catch_up_with_icp_ledger;
@@ -6,7 +7,7 @@ use crate::common::{
67
utils::test_identity,
78
};
89
use candid::{Encode, Principal};
9-
use ic_agent::Identity;
10+
use ic_agent::{Agent, Identity};
1011
use ic_icp_rosetta_client::RosettaClient;
1112
use ic_icp_rosetta_client::RosettaTransferArgs;
1213
use ic_icp_rosetta_runner::RosettaOptions;
@@ -41,8 +42,6 @@ use rosetta_core::identifiers::NetworkIdentifier;
4142
use std::collections::HashMap;
4243
use tempfile::TempDir;
4344

44-
use super::utils::memo_bytebuf_to_u64;
45-
4645
pub struct RosettaTestingEnvironment {
4746
pub pocket_ic: PocketIc,
4847
pub rosetta_context: RosettaContext,
@@ -154,6 +153,10 @@ impl RosettaTestingEnvironment {
154153
.await;
155154
self
156155
}
156+
157+
pub async fn get_test_agent(&self) -> Agent {
158+
get_test_agent(self.pocket_ic.url().unwrap().port().unwrap()).await
159+
}
157160
}
158161

159162
pub struct RosettaTestingEnvironmentBuilder {

rs/rosetta-api/icp/tests/system_tests/test_cases/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ pub mod network;
77
pub mod neuron_management;
88
pub mod search_transactions;
99
pub mod transfers;
10+
pub mod voting;

0 commit comments

Comments
 (0)