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}