pink_extension/system.rs
1#![allow(clippy::let_unit_value)]
2
3use pink_extension_macro as pink;
4
5use alloc::string::String;
6use alloc::vec::Vec;
7use scale::{Decode, Encode};
8
9use crate::{AccountId, Balance, Hash, SidevmConfig, WorkerId};
10
11/// Errors that can occur upon calling the system contract.
12#[derive(Debug, PartialEq, Eq, Encode, Decode)]
13#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
14pub enum Error {
15 PermisionDenied,
16 DriverNotFound,
17 CodeNotFound,
18 ConditionNotMet,
19}
20
21/// The code type for existance check.
22#[derive(Debug, PartialEq, Eq, Encode, Decode)]
23#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
24pub enum CodeType {
25 Ink,
26 Sidevm,
27}
28
29impl CodeType {
30 pub fn is_ink(&self) -> bool {
31 matches!(self, CodeType::Ink)
32 }
33 pub fn is_sidevm(&self) -> bool {
34 matches!(self, CodeType::Sidevm)
35 }
36}
37
38/// Result type for the system contract messages
39pub type Result<T, E = Error> = core::result::Result<T, E>;
40pub use this_crate::VersionTuple;
41
42/// The pink system contract interface.
43///
44/// The system contract, instantiated with each cluster creation, manages access permissions to
45/// the privileged chain extension functions and pink events. Some of these functions or events
46/// are exclusive to the system contract. User contracts wishing to call these functions or
47/// emit these events must first request the system contract, which then checks the permissions
48/// to either execute or reject the request.
49#[pink::system]
50#[ink::trait_definition(namespace = "pink_system")]
51pub trait System {
52 /// Returns the system contract version, indicating its API capabilities.
53 ///
54 /// # Example
55 /// ```no_run
56 /// use pink_extension::system::SystemRef;
57 /// let (major, minor, patch) = SystemRef::instance().version();
58 /// ```
59 #[ink(message, selector = 0x87c98a8d)]
60 fn version(&self) -> VersionTuple;
61
62 /// Grants the administrator role to an address. Administrator contracts can set drivers,
63 /// deploy sidevm, etc.
64 ///
65 /// Must be called by the cluster owner.
66 #[ink(message)]
67 fn grant_admin(&mut self, contract_id: AccountId) -> Result<()>;
68
69 /// Checks if an address is an administrator.
70 #[ink(message)]
71 fn is_admin(&self, contract_id: AccountId) -> bool;
72
73 /// Marks a contract as a driver for a given name, retrievable via `get_driver` or `get_driver2`.
74 /// The caller must be the cluster owner or an administrator. Any valid string can be a name.
75 /// There are predefined names used by the Phat Contract system.
76 ///
77 /// There are some predefined names that are used by the Phat Contract system:
78 /// - `PinkLogger`: The contract that with a sidevm instance that collect the logs and events
79 /// emitted by the ink! contracts in current cluster.
80 /// - `ContractDeposit`: The contract that implements the `trait ContractDeposit` which talks
81 /// to the pallet PhatTokenomic on Phala chain.
82 #[ink(message)]
83 fn set_driver(&mut self, name: String, contract_id: AccountId) -> Result<()>;
84
85 /// Retrieves the driver contract id for a given name.
86 #[ink(message)]
87 fn get_driver(&self, name: String) -> Option<AccountId>;
88
89 /// Retrieves the driver contract id and the set block number for a given name.
90 #[ink(message)]
91 fn get_driver2(&self, name: String) -> Option<(crate::BlockNumber, AccountId)>;
92
93 /// Deploys a sidevm instance attached to a contract. Must be called by an administrator.
94 #[ink(message)]
95 fn deploy_sidevm_to(&self, contract_id: AccountId, code_hash: Hash) -> Result<()>;
96
97 /// Stops a sidevm instance attached to a contract. Must be called by an administrator.
98 #[ink(message)]
99 fn stop_sidevm_at(&self, contract_id: AccountId) -> Result<()>;
100
101 /// Sets a block hook for a contract. Must be called by an administrator.
102 /// Note: This feature is deprecated and will be removed in the future.
103 #[ink(message)]
104 fn set_hook(
105 &mut self,
106 hook: crate::HookPoint,
107 contract_id: AccountId,
108 selector: u32,
109 gas_limit: u64,
110 ) -> Result<()>;
111
112 /// Sets the contract weight for query requests and sidevm scheduling.
113 /// A higher weight allows the contract to access more resources.
114 #[ink(message)]
115 fn set_contract_weight(&self, contract_id: AccountId, weight: u32) -> Result<()>;
116
117 /// Returns the total balance of a given account.
118 #[ink(message)]
119 fn total_balance_of(&self, account: AccountId) -> Balance;
120
121 /// Returns the free balance of a given account.
122 #[ink(message)]
123 fn free_balance_of(&self, account: AccountId) -> Balance;
124
125 /// Upgrades the system contract to the latest version.
126 #[ink(message)]
127 fn upgrade_system_contract(&mut self) -> Result<()>;
128
129 /// Performs upgrade condition checks and state migration if necessary.
130 /// Called by the system contract on the new code version during an upgrade process.
131 #[ink(message)]
132 fn do_upgrade(&self, from_version: VersionTuple) -> Result<()>;
133
134 /// Upgrades the contract runtime.
135 #[ink(message)]
136 fn upgrade_runtime(&mut self, version: (u32, u32)) -> Result<()>;
137
138 /// Checks if the code with a given hash is already uploaded to the cluster.
139 #[ink(message)]
140 fn code_exists(&self, code_hash: Hash, code_type: CodeType) -> bool;
141
142 /// Retrieves the current code hash of a given contract.
143 #[ink(message)]
144 fn code_hash(&self, account: AccountId) -> Option<ink::primitives::Hash>;
145
146 /// Retrieves the history of a given driver, returning a vector of
147 /// (block_number, contract_id) tuples where the block number is the
148 /// block number when the driver is set.
149 #[ink(message)]
150 fn driver_history(&self, name: String) -> Option<Vec<(crate::BlockNumber, AccountId)>>;
151
152 /// Get current event chain head info
153 ///
154 /// Returns (next event block number, last event block hash)
155 #[ink(message)]
156 fn current_event_chain_head(&self) -> (u64, Hash);
157
158 /// Deploys a sidevm instance attached to a contract on selected workers. Must be called by an administrator.
159 #[ink(message)]
160 fn deploy_sidevm_to_workers(
161 &self,
162 contract_id: AccountId,
163 code_hash: Hash,
164 workers: Vec<crate::WorkerId>,
165 config: SidevmConfig,
166 ) -> Result<()>;
167
168 /// Sets a deadline for sidevm instances attached to a contract on selected workers. Must be called by an administrator.
169 #[ink(message)]
170 fn set_sidevm_deadline(
171 &self,
172 contract_id: AccountId,
173 run_until: crate::BlockNumber,
174 ) -> Result<()>;
175}
176
177/// Errors that can occur upon calling a driver contract.
178#[derive(Debug, PartialEq, Eq, Encode, Decode)]
179#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
180pub enum DriverError {
181 Other(String),
182 SystemError(Error),
183 BadOrigin,
184}
185
186impl From<Error> for DriverError {
187 fn from(value: Error) -> Self {
188 Self::SystemError(value)
189 }
190}
191
192/// Driver to manage sidevm deployments.
193#[pink::driver]
194#[ink::trait_definition]
195pub trait SidevmOperation {
196 /// Invoked by a contract to deploy a sidevm instance that attached to itself.
197 #[ink(message)]
198 fn deploy(&self, code_hash: Hash) -> Result<(), DriverError>;
199
200 /// Check if given address has the permission to deploy a sidevm.
201 #[ink(message)]
202 fn can_deploy(&self, contract_id: AccountId) -> bool;
203
204 /// Deploys a paid side VM instance to a set of worker nodes with the specified configurations.
205 ///
206 /// # Parameters
207 /// - `code_hash`: The hash of the code to be deployed.
208 /// - `code_size`: Size of the code.
209 /// - `workers`: A vector of worker IDs to which the code will be deployed.
210 /// - `max_memory_pages`: Maximum memory pages allowed for the side VM.
211 /// - `blocks_to_live`: How many blocks the deployment will live.
212 ///
213 /// # Returns
214 /// - `Ok(())` if the deployment was commited.
215 /// - Various `Err` variants for different types of failures.
216 #[ink(message, payable)]
217 fn deploy_to_workers(
218 &mut self,
219 code_hash: Hash,
220 code_size: u32,
221 workers: Vec<WorkerId>,
222 max_memory_pages: u32,
223 blocks_to_live: u32,
224 ) -> Result<(), DriverError>;
225
226 /// Calculates the price for deploying a paid side VM.
227 ///
228 /// # Parameters
229 /// - `code_size`: Size of the code.
230 /// - `max_memory_pages`: Maximum memory pages.
231 /// - `n_workers`: Number of worker nodes.
232 ///
233 /// # Returns
234 /// - `Result<Balance>` representing the price of the deployment.
235 #[ink(message)]
236 fn calc_price(
237 &self,
238 code_size: u32,
239 max_memory_pages: u32,
240 n_workers: u32,
241 ) -> Result<Balance, DriverError>;
242
243 /// Updates the deadline for a previously deployed side VM.
244 ///
245 /// # Parameters
246 /// - `deadline`: The new deadline (in blocks) for the side VM.
247 ///
248 /// # Returns
249 /// - `Ok(())` if the update was successful.
250 /// - `Err` variants for different types of failures.
251 #[ink(message, payable)]
252 fn update_deadline(&mut self, deadline: u32) -> Result<(), DriverError>;
253
254 /// Retrieves the deadline (in blocks) for the deployed side VM of a given account.
255 ///
256 /// # Parameters
257 /// - `account`: The account ID to query.
258 ///
259 /// # Returns
260 /// - `Some(u32)` containing the deadline if found.
261 /// - `None` if the account has not deployed a side VM.
262 #[ink(message)]
263 fn deadline_of(&self, account: AccountId) -> Option<u32>;
264}
265
266/// Contracts receiving processing deposit events. Can be a driver and the system.
267#[pink::driver]
268#[ink::trait_definition]
269pub trait ContractDeposit {
270 /// Change deposit of a contract. A driver should set the contract weight according to the
271 /// new deposit.
272 #[ink(message, selector = 0xa24bcb44)]
273 fn change_deposit(
274 &mut self,
275 contract_id: AccountId,
276 deposit: Balance,
277 ) -> Result<(), DriverError>;
278}