neo3/neo_contract/
role_management.rs1use async_trait::async_trait;
2use num_enum::TryFromPrimitive;
3use primitive_types::H160;
4use serde::{Deserialize, Serialize};
5
6use crate::{
7 neo_builder::TransactionBuilder,
8 neo_clients::{APITrait, JsonRpcProvider, RpcClient},
9 neo_contract::{traits::SmartContractTrait, ContractError},
10 neo_crypto::Secp256r1PublicKey,
11 neo_types::{
12 serde_with_utils::{deserialize_script_hash, serialize_script_hash},
13 ContractParameter, ScriptHash, StackItem,
14 },
15 ValueExtension,
16};
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct RoleManagement<'a, P: JsonRpcProvider> {
20 #[serde(deserialize_with = "deserialize_script_hash")]
21 #[serde(serialize_with = "serialize_script_hash")]
22 script_hash: ScriptHash,
23 #[serde(skip)]
24 provider: Option<&'a RpcClient<P>>,
25}
26
27impl<'a, P: JsonRpcProvider + 'static> RoleManagement<'a, P> {
28 const NAME: &'static str = "RoleManagement";
29 pub fn new(provider: Option<&'a RpcClient<P>>) -> Self {
32 Self { script_hash: Self::calc_native_contract_hash(Self::NAME).unwrap(), provider }
33 }
34
35 pub async fn get_designated_by_role(
36 &self,
37 role: Role,
38 block_index: i32,
39 ) -> Result<Vec<Secp256r1PublicKey>, ContractError> {
40 self.check_block_index_validity(block_index).await.unwrap();
41
42 let invocation = self
43 .call_invoke_function(
44 "getDesignatedByRole",
45 vec![role.into(), block_index.into()],
46 vec![],
47 )
48 .await
49 .unwrap();
50
51 let designated = invocation.stack[0]
52 .as_array()
53 .unwrap()
54 .into_iter()
55 .map(|item| {
56 Secp256r1PublicKey::from_bytes(item.as_bytes().unwrap().as_slice()).unwrap()
57 })
58 .collect();
59
60 Ok(designated)
61 }
62
63 async fn check_block_index_validity(&self, block_index: i32) -> Result<(), ContractError> {
64 if block_index < 0 {
65 return Err(ContractError::InvalidNeoName("Block index must be positive".to_string()));
66 }
67
68 let current_block_count = self.provider.unwrap().get_block_count().await.unwrap();
69
70 if block_index > current_block_count as i32 {
71 return Err(ContractError::InvalidNeoName(format!(
72 "Block index {} exceeds current block count {}",
73 block_index, current_block_count
74 )));
75 }
76
77 Ok(())
78 }
79
80 pub async fn designate_as_role(
81 &self,
82 role: Role,
83 pub_keys: Vec<Secp256r1PublicKey>,
84 ) -> Result<TransactionBuilder<P>, ContractError> {
85 if pub_keys.is_empty() {
86 return Err(ContractError::InvalidNeoName(
87 "At least 1 public key is required".to_string(),
88 ));
89 }
90
91 let params: Vec<_> = pub_keys.into_iter().map(|key| key.to_value()).collect();
92
93 self.invoke_function("designateAsRole", vec![role.into(), params.into()]).await
94 }
95}
96
97#[async_trait]
98impl<'a, P: JsonRpcProvider> SmartContractTrait<'a> for RoleManagement<'a, P> {
99 type P = P;
100
101 fn script_hash(&self) -> H160 {
102 self.script_hash.clone()
103 }
104
105 fn set_script_hash(&mut self, script_hash: H160) {
106 self.script_hash = script_hash;
107 }
108
109 fn provider(&self) -> Option<&RpcClient<P>> {
110 self.provider
111 }
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
115#[repr(u8)]
116pub enum Role {
117 Oracle,
118 Policy,
119 Validator,
120 StateRootValidator,
121 PriceFeedOracle,
122 FeeCollector,
123 ComplianceOfficer,
124}
125
126impl Role {
127 pub const fn byte(self) -> u8 {
128 self as u8
129 }
130}
131
132impl From<Role> for StackItem {
133 fn from(role: Role) -> Self {
134 StackItem::Integer { value: role.byte() as i64 }
135 }
136}
137
138impl Into<ContractParameter> for Role {
139 fn into(self) -> ContractParameter {
140 ContractParameter::integer(self.byte() as i64)
141 }
142}