neo3/neo_contract/
role_management.rs

1use 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	// const SCRIPT_HASH: H160 = Self::calc_native_contract_hash(Self::NAME).unwrap(); // compute hash
30
31	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}