diff --git a/Cargo.lock b/Cargo.lock index 5571b9d3bb..7e4bbca77f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4862,7 +4862,7 @@ checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes 1.0.11", - "rustix 0.36.14", + "rustix 0.36.15", "windows-sys 0.45.0", ] @@ -5944,7 +5944,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b20a59d985586e4a5aef64564ac77299f8586d8be6cf9106a5a40207e8908efb" dependencies = [ - "rustix 0.36.14", + "rustix 0.36.15", ] [[package]] @@ -8665,7 +8665,7 @@ dependencies = [ [[package]] name = "pink-extension" -version = "0.4.3" +version = "0.4.4" dependencies = [ "dlmalloc", "ink", @@ -8680,7 +8680,7 @@ dependencies = [ [[package]] name = "pink-extension-macro" -version = "0.4.2" +version = "0.4.4" dependencies = [ "heck 0.4.1", "ink_ir", @@ -10184,9 +10184,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.14" +version = "0.36.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14e4d67015953998ad0eb82887a0eb0129e18a7e2f3b7b0f6c422fddcd503d62" +checksum = "c37f1bd5ef1b5422177b7646cba67430579cfe2ace80f284fee876bca52ad941" dependencies = [ "bitflags 1.3.2", "errno 0.3.1", @@ -10198,9 +10198,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.21" +version = "0.37.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25693a73057a1b4cb56179dd3c7ea21a7c6c5ee7d85781f5749b46f34b79c" +checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" dependencies = [ "bitflags 1.3.2", "errno 0.3.1", @@ -10730,7 +10730,7 @@ dependencies = [ "libc", "log", "once_cell", - "rustix 0.36.14", + "rustix 0.36.15", "sc-allocator", "sc-executor-common", "sp-runtime-interface", @@ -13695,7 +13695,7 @@ dependencies = [ "cfg-if", "fastrand", "redox_syscall 0.3.5", - "rustix 0.37.21", + "rustix 0.37.23", "windows-sys 0.48.0", ] @@ -15326,7 +15326,7 @@ dependencies = [ "directories-next", "file-per-thread-logger", "log", - "rustix 0.36.14", + "rustix 0.36.15", "serde", "sha2 0.10.2", "toml 0.5.9", @@ -15422,7 +15422,7 @@ checksum = "6e0554b84c15a27d76281d06838aed94e13a77d7bf604bbbaf548aa20eb93846" dependencies = [ "object 0.30.4", "once_cell", - "rustix 0.36.14", + "rustix 0.36.15", ] [[package]] @@ -15453,7 +15453,7 @@ dependencies = [ "memoffset 0.8.0", "paste", "rand 0.8.5", - "rustix 0.36.14", + "rustix 0.36.15", "wasmtime-asm-macros", "wasmtime-environ", "wasmtime-jit-debug", diff --git a/crates/phactory/src/contracts/support.rs b/crates/phactory/src/contracts/support.rs index bb976c0228..aafa64d1a0 100644 --- a/crates/phactory/src/contracts/support.rs +++ b/crates/phactory/src/contracts/support.rs @@ -183,9 +183,9 @@ impl Contract { phala_mq::select! { next_cmd = self.cmd_rcv_mq => match next_cmd { Ok((_, cmd, origin)) => { - info!("Contract {:?} handling command", self.address()); + info!("Contract {:?} handling tx call", self.address()); let Ok(command) = Decode::decode(&mut &cmd.0[..]) else { - error!("Failed to decode command input"); + error!("Failed to decode tx input"); return Some(Err(TransactionError::BadInput)); }; env.contract_cluster.handle_command(self.address(), origin, command, &mut context) diff --git a/crates/phactory/src/system/mod.rs b/crates/phactory/src/system/mod.rs index 2fc26b7d92..b1299a9c66 100644 --- a/crates/phactory/src/system/mod.rs +++ b/crates/phactory/src/system/mod.rs @@ -1675,7 +1675,7 @@ pub fn handle_contract_command_result( ) { let effects = match result { Err(err) => { - error!("Run contract command failed: {:?}", err); + error!("Run contract tx call failed: {:?}", err); return; } Ok(Some(effects)) => effects, diff --git a/crates/pink/pink-extension/Cargo.toml b/crates/pink/pink-extension/Cargo.toml index 2cac2c3954..2473332418 100644 --- a/crates/pink/pink-extension/Cargo.toml +++ b/crates/pink/pink-extension/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pink-extension" -version = "0.4.3" +version = "0.4.4" edition = "2018" description = "Phala's ink! for writing phat contracts" license = "Apache-2.0" diff --git a/crates/pink/pink-extension/README.md b/crates/pink/pink-extension/README.md index 1b676e55c0..32ac7a86d0 100644 --- a/crates/pink/pink-extension/README.md +++ b/crates/pink/pink-extension/README.md @@ -1,11 +1,68 @@ -

- Phala's ink! for writing phat contracts -

+

Phala's ink! - Writing Enhanced Smart Contracts

-pink! is based on Parity's ink! language and provide some extra functionality to interact with phala's phat contract runtime. +Pink! is a smart contract language extending Parity's `ink!`. It extends the basic functionality with additional features, tailored to interact efficiently with Phala's Phat Contract runtime. -# Getting started +## Getting Started -The orignal `ink!` contract can run under Phala's phat contract platform without any modifacation. So you can follow Parity's `ink!` [documentation](https://paritytech.github.io/ink-docs/) to get started. +Unaltered `ink!` contracts are fully compatible and executable on the Phala's Phat Contract platform. To learn how to start writing contracts with `ink!`, follow the Parity's [ink! documentation](https://paritytech.github.io/ink-docs/). -If you want to use phat contract's specific features, such as phala-mq message, HTTP requests, you can use `pink_extension` to achieve this. See [examples](https://github.com/Phala-Network/phala-blockchain/tree/master/crates/pink/examples) for more detail. +To get started with Pink!, add the following dependency to your `Cargo.toml`: + +```toml +[dependencies] +ink = { version = "4", default-features = false } +pink = { package = "pink-extension", version = "0.4", default-features = false } + +[features] +std = [ + "ink/std", + "pink/std", +] +``` + +Then, you can use the `http_get!` macro to make a GET request to a remote server: + +```ignore +#[ink::message] +fn http_get_example(&self) { + let response = pink::http_get!("https://httpbin.org/get"); + assert_eq!(response.status_code, 200); +} +``` + +## Phat Contract-Specific Features + +The Pink! crate is designed to empower you, enabling you to leverage the unique features of the Phat Contract, such as making HTTP requests as demonstrated in our examples. This crate supplies the crucial types and functions needed to seamlessly interact with the Phat Contract runtime. + +There are three kind of APIs to communication with the runtime: + +- Emitting Events: + These APIs are primarily used in situations where the operation could lead to side effects that need to be deterministically recorded and may be rolled back during the execution of the contract call. For additional information on Emitting Events APIs, please refer to the [PinkEvent documentation](crate::PinkEvent). + +- Chain Extension: + These APIs are predominantly used for read-only operations or operations that aren't expected to create deterministic side effects. For an in-depth understanding of Chain Extension APIs, please refer to the [PinkExt documentation](crate::chain_extension::PinkExtBackend). + +- System contract: + There is a special contract called the System contract in each cluster. The system contract is instantiated when the cluster is created. Either ink contracts or external accounts can call the system contract to perform certain operations. For more information on the System contract, please refer to the [System documentation](crate::system::SystemForDoc). + +For practical implementation examples, explore our [Phat Contract examples](https://github.com/Phala-Network/phat-contract-examples) repository. + +## Using JavaScript with Phat Contract + +Phat Contract supports JavaScript through the [phat-quickjs](https://github.com/Phala-Network/phat-quickjs) contract. + +There are two ways to use JavaScript in your contract: + +- You can deploy your phat-quickjs contract instance through a standard deployment process. + +- However, for a more convenient approach, most public clusters should already have a public driver quickjs contract deployed. You can obtain the contract code_hash with `System::get_driver("JsDelegate")`. + +- For the simplest integration, consider using the [`phat_js`](https://docs.rs/phat_js/) crate. It provides an `eval` function that lets you evaluate JavaScript code snippets directly. + For example: + ```ignore + #[ink::message] + fn eval_js_example(&self) { + let result = phat_js::eval("'Hello,' + 'World'", &[]); + assert_eq!(result, phat_js::Output::String("Hello,World".to_string())); + } + ``` diff --git a/crates/pink/pink-extension/macro/Cargo.toml b/crates/pink/pink-extension/macro/Cargo.toml index 5c4feabea6..3eb0fd777b 100644 --- a/crates/pink/pink-extension/macro/Cargo.toml +++ b/crates/pink/pink-extension/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pink-extension-macro" -version = "0.4.2" +version = "0.4.4" edition = "2018" description = "Macros for writing phat contract" license = "Apache-2.0" diff --git a/crates/pink/pink-extension/macro/src/chain_extension.rs b/crates/pink/pink-extension/macro/src/chain_extension.rs index 46e2114c4e..6f882c7715 100644 --- a/crates/pink/pink-extension/macro/src/chain_extension.rs +++ b/crates/pink/pink-extension/macro/src/chain_extension.rs @@ -50,7 +50,9 @@ fn patch_chain_extension_or_err(input: TokenStream2) -> Result { for item in item_trait.items.iter_mut() { if let syn::TraitItem::Fn(item_method) = item { - item_method.attrs.clear(); + item_method + .attrs + .retain(|attr| !attr.path().is_ident("ink")); // Turn &[u8] into Cow<[u8]> for input in item_method.sig.inputs.iter_mut() { diff --git a/crates/pink/pink-extension/macro/src/driver_system.rs b/crates/pink/pink-extension/macro/src/driver_system.rs index 0b4f5a78f0..0388760487 100644 --- a/crates/pink/pink-extension/macro/src/driver_system.rs +++ b/crates/pink/pink-extension/macro/src/driver_system.rs @@ -1,6 +1,6 @@ use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::quote; -use syn::{spanned::Spanned, FnArg, Result}; +use syn::{parse_quote, spanned::Spanned, FnArg, Result}; pub(crate) enum InterfaceType { System, @@ -25,6 +25,8 @@ fn patch_or_err( ) -> Result { use heck::{ToLowerCamelCase, ToSnakeCase}; let the_trait: syn::ItemTrait = syn::parse2(input)?; + let trait_for_doc = generate_trait_for_doc(the_trait.clone()); + let the_trait = patch_origin_system_doc(the_trait); let trait_ident = &the_trait.ident; let trait_name = the_trait.ident.to_string(); let trait_impl_mod = Ident::new( @@ -133,6 +135,9 @@ fn patch_or_err( #config #the_trait + #[cfg(doc)] + #trait_for_doc + pub use #trait_impl_mod::#impl_type; mod #trait_impl_mod { use super::*; @@ -223,6 +228,38 @@ fn patch_or_err( }) } +fn patch_origin_system_doc(mut trait_item: syn::ItemTrait) -> syn::ItemTrait { + let additonal_doc = format!( + "**The doc is messed up by the ink macro. See [`{}ForDoc`] for a clean version**\n\n", + trait_item.ident + ); + trait_item + .attrs + .insert(0, parse_quote!(#[doc = #additonal_doc])); + trait_item +} + +fn generate_trait_for_doc(mut trait_item: syn::ItemTrait) -> syn::ItemTrait { + let additonal_doc = format!( + "**This is the clean version doc of [`{}`]**\n\n", + trait_item.ident + ); + trait_item.attrs.retain(|attr| attr.path().is_ident("doc")); + trait_item + .attrs + .insert(0, parse_quote!(#[doc = #additonal_doc])); + trait_item.ident = syn::Ident::new( + &format!("{}ForDoc", trait_item.ident), + trait_item.ident.span(), + ); + for item in trait_item.items.iter_mut() { + if let syn::TraitItem::Fn(method) = item { + method.attrs.retain(|attr| !attr.path().is_ident("ink")); + } + } + trait_item +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/pink/pink-extension/macro/src/lib.rs b/crates/pink/pink-extension/macro/src/lib.rs index 759c806b70..abf1bc0dbc 100644 --- a/crates/pink/pink-extension/macro/src/lib.rs +++ b/crates/pink/pink-extension/macro/src/lib.rs @@ -27,7 +27,7 @@ pub fn chain_extension(_: TokenStream, input: TokenStream) -> TokenStream { output.into() } -/// Mark an ink trait as pink's system contract +/// Mark an ink trait as pink's system contract. Internal use only. #[proc_macro_attribute] pub fn system(arg: TokenStream, input: TokenStream) -> TokenStream { let config = parse_macro_input!(arg as TokenStream2); @@ -36,7 +36,58 @@ pub fn system(arg: TokenStream, input: TokenStream) -> TokenStream { module.into() } -/// Mark an ink trait as pink's driver contract +/// This procedural macro marks an ink! trait as a 'driver contract' for the Pink system. +/// +/// # What is the driver system? +/// +/// The driver system is a straightforward concept. In the Pink system, there is a registry mapping driver names +/// to driver contract addresses. The System contract provides two methods to manage this registry: +/// `System::set_driver(driver_name, contract_address)` and `System::get_driver(driver_name)`. +/// +/// # How does this macro work? +/// +/// When this attribute is used, it modifies the given trait to be utilized as a driver contract +/// within the Pink system. This is achieved by adding a new type, TraitNameRef, which implements +/// the marked trait and provides a static method `instance()` to retrieve an instance of the driver. +/// +/// # Example +/// +/// Below, the `SidevmOperation` trait is annotated with `#[pink::driver]`. This marks it +/// as a driver contract, enabling it to manage SideVM deployments. +/// +/// ```ignore +/// #[pink::driver] +/// #[ink::trait_definition] +/// pub trait SidevmOperation { +/// #[ink(message)] +/// fn deploy(&self, code_hash: Hash) -> Result<(), DriverError>; +/// +/// #[ink(message)] +/// fn can_deploy(&self, contract_id: AccountId) -> bool; +/// } +/// ``` +/// +/// Once a trait has been defined as a driver using this macro, it can be set as a driver +/// by invoking the `System::set_driver(driver_name, contract_address)` method. +/// +/// # Usage +/// +/// The actual driver can then be retrieved and its methods, defined by the trait, can be used. +/// For instance, to start a SideVM, one would get the driver instance and call its `deploy` method: +/// +/// ```ignore +/// pub fn start_sidevm(code_hash: Hash) -> Result<(), system::DriverError> { +/// let driver = +/// SidevmOperationRef::instance().ok_or(system::Error::DriverNotFound)?; +/// driver.deploy(code_hash) +/// } +/// ``` +/// +/// Here, `SidevmOperationRef::instance()` retrieves an instance of the driver contract for "SidevmOperation", +/// and then the `deploy` method of the driver is invoked to deploy a SideVM instance. +/// Internally, `SidevmOperationRef::instance()` retrieves the driver contract by invoking `System::get_driver("SidevmOperation")`. +/// +/// Note: The name of the driver contract instance (e.g., "SidevmOperation") is generated by the macro. #[proc_macro_attribute] pub fn driver(arg: TokenStream, input: TokenStream) -> TokenStream { let config = parse_macro_input!(arg as TokenStream2); diff --git a/crates/pink/pink-extension/macro/src/snapshots/pink_extension_macro__driver_system__tests__show_patch_result.snap b/crates/pink/pink-extension/macro/src/snapshots/pink_extension_macro__driver_system__tests__show_patch_result.snap index 0ae5912bf8..1d8f135fa4 100644 --- a/crates/pink/pink-extension/macro/src/snapshots/pink_extension_macro__driver_system__tests__show_patch_result.snap +++ b/crates/pink/pink-extension/macro/src/snapshots/pink_extension_macro__driver_system__tests__show_patch_result.snap @@ -1,8 +1,9 @@ --- source: crates/pink/pink-extension/macro/src/driver_system.rs -assertion_line: 250 +assertion_line: 286 expression: "rustfmt_snippet::rustfmt_token_stream(&stream).unwrap()" --- +#[doc = "**The doc is messed up by the ink macro. See [`SystemForDoc`] for a clean version**\n\n"] #[ink::trait_definition(namespace = "pink_system")] pub trait System { #[ink(message)] @@ -12,6 +13,13 @@ pub trait System { #[ink(message)] fn deploy_sidevm_to(&self, code_hash: Hash, contract_id: AccountId) -> Result<()>; } +#[cfg(doc)] +#[doc = "**This is the clean version doc of [`System`]**\n\n"] +pub trait SystemForDoc { + fn get_driver(&self, name: String) -> Option; + fn set_driver(&self, name: String, driver: AccountId); + fn deploy_sidevm_to(&self, code_hash: Hash, contract_id: AccountId) -> Result<()>; +} pub use _pink_system_impl::SystemRef; mod _pink_system_impl { use super::*; diff --git a/crates/pink/pink-extension/src/chain_extension.rs b/crates/pink/pink-extension/src/chain_extension.rs index 4a490c9db3..5e3d203a53 100644 --- a/crates/pink/pink-extension/src/chain_extension.rs +++ b/crates/pink/pink-extension/src/chain_extension.rs @@ -8,6 +8,9 @@ pub use signing::SigType; use crate::{Balance, EcdsaPublicKey, EcdsaSignature, Hash}; +#[cfg(doc)] +use crate::{http_get, http_post, http_put, debug, info, warn, error}; + mod http_request; pub mod signing; @@ -104,105 +107,526 @@ pub type BatchHttpResult = Result>, H pub trait PinkExt { type ErrorCode = ErrorCode; + /// Make a HTTP request. + /// + /// # Arguments + /// + /// * `request`: A `HttpRequest` struct containing all the details for the HTTP request. + /// + /// # Returns + /// + /// * `HttpResponse` - The response from the HTTP request which contains the status code, headers, and body. + /// + /// # Example + /// + /// ```ignore + /// let request = HttpRequest::new("https://httpbin.org/get", "GET", Defualt::default(), Defualt::default()); + /// let response = pink::ext().http_request(request); + /// ``` + /// + /// There are also some shortcut macros for this function: + /// - [`http_get!`] + /// - [`http_post!`] + /// - [`http_put!`] + /// + /// # Availability + /// any contract | query only #[ink(extension = 1, handle_status = false)] fn http_request(request: HttpRequest) -> HttpResponse; + /// Sign a message with a given key. + /// + /// # Arguments + /// + /// * `sigtype`: The signature type to use for signing the message. + /// * `key`: The private key used for signing the message. + /// * `message`: The message to be signed. + /// + /// # Returns + /// + /// * `Vec` - The signed message as a byte vector. + /// + /// # Example + /// + /// ```ignore + /// let derived_key = pink::ext().derive_sr25519_key(b"some salt".into()); + /// let message = b"Hello, world!"; + /// let signature = pink::ext().sign(SigType::Sr25519, &key, message); + /// let pubkey = pink::ext().get_public_key(SigType::Sr25519, &derived_key); + /// let is_valid = pink::ext().verify(SigType::Sr25519, &pubkey, message, &signature); + /// ``` + /// + /// # Availability + /// For SigType::Sr25519: + /// any contract | query only + /// + /// For Others: + /// any contract | query | transaction #[ink(extension = 2, handle_status = false)] fn sign(sigtype: SigType, key: &[u8], message: &[u8]) -> Vec; + /// Verify a signature. + /// + /// This method verifies a digital signature given the signature type, public key, message, and signature. + /// + /// # Arguments + /// + /// * `sigtype`: The type of signature to verify. + /// * `pubkey`: The public key associated with the private key that signed the message. + /// * `message`: The original message that was signed. + /// * `signature`: The digital signature to verify. + /// + /// # Returns + /// + /// * `bool` - `true` if the signature is valid, `false` otherwise. + /// + /// # Example + /// + /// ```ignore + /// let derived_key = pink::ext().derive_sr25519_key(b"some salt".into()); + /// let message = b"Hello, world!"; + /// let signature = pink::ext().sign(SigType::Sr25519, &key, message); + /// let pubkey = pink::ext().get_public_key(SigType::Sr25519, &derived_key); + /// let is_valid = pink::ext().verify(SigType::Sr25519, &pubkey, message, &signature); + /// ``` + /// + /// # Availability + /// any contract | query | transaction #[ink(extension = 3, handle_status = false)] fn verify(sigtype: SigType, pubkey: &[u8], message: &[u8], signature: &[u8]) -> bool; + /// Derive a sr25519 key. + /// + /// This method derives a sr25519 key using the provided salt and the contract private key. + /// The derived key is deterministic so it could be used in transactions to sign messages. + /// + /// The derived key can also be used as a cryptographically secure entropy source. + /// + /// # Arguments + /// + /// * `salt`: The salt to use in the key derivation function. + /// + /// # Returns + /// + /// * `Vec` - The derived sr25519 key as a byte vector. + /// + /// # Example + /// + /// ```ignore + /// let derived_key = pink::ext().derive_sr25519_key(b"some salt".into()); + /// ``` + /// + /// # Availability + /// any contract | query | transaction #[ink(extension = 4, handle_status = false)] fn derive_sr25519_key(salt: Cow<[u8]>) -> Vec; + /// Derive the public key from a private key. + /// + /// This method takes a signature type and private key and returns the associated public key. + /// + /// # Arguments + /// + /// * `sigtype`: The type of signature to generate the public key for. + /// * `key`: The private key used to generate the public key. + /// + /// # Returns + /// + /// * `Vec` - The public key associated with the given private key as a byte vector. + /// + /// # Example + /// + /// ```ignore + /// let derived_key = pink::ext().derive_sr25519_key(b"some salt".into()); + /// let pubkey = pink::ext().get_public_key(SigType::Sr25519, &derived_key); + /// ``` + /// + /// # Availability + /// any contract | query | transaction #[ink(extension = 5, handle_status = false)] fn get_public_key(sigtype: SigType, key: &[u8]) -> Vec; /// Set a value in the local cache. /// - /// The default expiration time is 7 days. Use `cache_set_expiration` to set a custom expiration - /// time. + /// This method sets a value in the local cache with the default expiration time of 7 days. + /// To set a custom expiration time, use `cache_set_expiration`. /// Values stored in cache can only be read in query functions. + /// Always returns `Ok(())` if it is called from a transaction context. + /// + /// # Arguments + /// + /// * `key`: The key used to identify the value in the cache. + /// * `value`: The value to be stored in the cache. + /// + /// # Returns /// - /// Alwasy returns `Ok(())` if it is called from a command context. + /// * `Result<(), StorageQuotaExceeded>` - `Ok(())` or `Err(StorageQuotaExceeded)` if the storage quota is exceeded. + /// + ///

+ /// Warning: + /// The cache is not guaranteed to be persistent. It may be cleared at any time due + /// to various reasons: + /// + /// - The cached item is expired. + /// - The entire cache in pRuntime is full and a new value needs to be stored (either from the contract itself or + /// other contracts). + /// - The worker is restarted. + ///

+ /// + /// In order to use cache, the contract need to be staked via the phala on-chain API `PhatTokenomic::adjust_stake`. + /// All contracts will share the 20MB cache storage by the ratio of stake. + /// + /// # Example + /// + /// ```ignore + /// let key = b"my key"; + /// let value = b"my value"; + /// let result = pink::ext().cache_set(key, value); + /// ``` + /// + /// # Availability + /// any contract | query | transaction #[ink(extension = 6, handle_status = true)] fn cache_set(key: &[u8], value: &[u8]) -> Result<(), StorageQuotaExceeded>; /// Set the expiration time of a value in the local cache. /// - /// Arguments: - /// - `key`: The key of the value to set the expiration time for. - /// - `expire`: The expiration time from now in seconds. + /// This method sets the expiration time for a given key in the local cache. + /// + /// # Arguments + /// + /// * `key`: The key of the value to set the expiration time for. + /// * `expire`: The expiration time from now in seconds. + /// + /// # Example + /// + /// ```ignore + /// let key = b"my key"; + /// let expire = 60; // 1 minute + /// pink::ext().cache_set_expiration(key, expire); + /// ``` + /// + /// # Availability + /// any contract | query | transaction #[ink(extension = 7, handle_status = false)] fn cache_set_expiration(key: &[u8], expire: u64); /// Get a value from the local cache. /// - /// Only for query functions. Always returns `None` if it is called from a command context. + /// This method retrieves a value from the local cache. It can only be used in query functions. + /// If called from a transaction context, it will always return `None`. + /// + /// # Arguments + /// + /// * `key`: The key used to identify the value in the cache. + /// + /// # Returns + /// + /// * `Option>` - The value from the cache as a byte vector wrapped in an Option, + /// or `None` if the value does not exist or called in transaction. + /// + /// # Example + /// + /// ```ignore + /// let key = b"my key"; + /// let value = pink::ext().cache_get(key); + /// ``` + /// + /// # Availability + /// any contract | query #[ink(extension = 8, handle_status = false)] fn cache_get(key: &[u8]) -> Option>; /// Remove a value from the local cache. /// - /// Returns the removed value if it existed. Always returns `None` if it is called from a - /// command context. + /// This method removes a value from the local cache and returns the removed value if it existed. + /// If called from a transaction context, it will always return `None`. + /// + /// # Arguments + /// + /// * `args`: The key used to identify the value in the cache. + /// + /// # Returns + /// + /// * `Option>` - The removed value as a byte vector wrapped in an Option + /// or `None` if the value did not exist or called in transaction. + /// + /// # Availability + /// any contract | query | transaction #[ink(extension = 9, handle_status = false)] fn cache_remove(args: &[u8]) -> Option>; - /// Print log message. + /// Log a message. + /// + /// This method logs a message at a given level. + /// + /// The logs would be shown in the worker log file. Additionally, if a log server + /// contract has been deployed in the cluster, the logs would be sent to the log server. + /// Users can query the logs via the log server API. + /// + /// # Arguments + /// + /// * `level`: The level of the log message. + /// * `message`: The message to be logged. + /// + /// # Example + /// + /// ```ignore + /// let level = 1; + /// let message = "Hello, world!"; + /// pink::ext().log(level, message); + /// ``` + /// + /// # Note + /// This is the low-level method for logging. It is recommended to use shortcuts macros below instead: + /// + /// - [`debug!`] + /// - [`info!`] + /// - [`warn!`] + /// - [`error!`] + /// + /// # Availability + /// any contract | query | transaction #[ink(extension = 10, handle_status = false)] fn log(level: u8, message: &str); - /// Get random bytes, for query only + /// Get random bytes. + /// + /// This method generates a vector of random bytes of a given length. It returns random bytes + /// generated by hardware RNG. So it is not deterministic and only available in a query context. + /// + /// # Note + /// It always returns an empty vec![] when called in a transaction. + /// + /// + /// # Arguments + /// + /// * `length`: The length of the random bytes vector. + /// + /// # Returns + /// + /// * `Vec` - A vector of random bytes of the given length. + /// + /// # Example + /// + /// ```ignore + /// let length = 32; + /// let random_bytes = pink::ext().getrandom(length); + /// ``` + /// + /// # Availability + /// any contract | query only #[ink(extension = 11, handle_status = false)] fn getrandom(length: u8) -> Vec; - /// Check if it is running in a Command context. + /// Check if it is running in a transaction context. + /// + /// # Returns + /// + /// * `bool` - `true` if it is running in a transaction context, `false` if in query. + /// + /// # Availability + /// any contract | query | transaction #[allow(clippy::wrong_self_convention)] #[ink(extension = 12, handle_status = false)] fn is_in_transaction() -> bool; + /// Sign a prehashed message with a given key. + /// + /// This method uses the given key and prehashed message to create a ECDSA signature. + /// + /// # Arguments + /// + /// * `key`: The private key used for signing the message. + /// * `message_hash`: The prehashed message to be signed. + /// + /// # Returns + /// + /// * `EcdsaSignature` - The signature of the message. + /// + /// # Example + /// + /// ```ignore + /// let key = [0u8; 32]; // replace with actual key + /// let message_hash = Hash::zero(); // replace with actual hash + /// let signature = pink::ext().ecdsa_sign_prehashed(&key, message_hash); + /// ``` + /// + /// # Availability + /// any contract | query | transaction #[ink(extension = 13, handle_status = false)] fn ecdsa_sign_prehashed(key: &[u8], message_hash: Hash) -> EcdsaSignature; + /// Verify a prehashed ECDSA signature. + /// + /// This method verifies a prehashed ECDSA signature given the signature, prehashed message, and public key. + /// + /// # Arguments + /// + /// * `signature`: The ECDSA digital signature to verify. + /// * `message_hash`: The prehashed original message that was signed. + /// * `pubkey`: The public key associated with the private key that signed the message. + /// + /// # Returns + /// + /// * `bool` - `true` if the signature is valid, `false` otherwise. + /// + /// # Example + /// + /// ```ignore + /// let signature = EcdsaSignature::default(); // replace with actual signature + /// let message_hash = Hash::zero(); // replace with actual hash + /// let pubkey = EcdsaPublicKey::default(); // replace with actual pubkey + /// let is_valid = pink::ext().ecdsa_verify_prehashed(signature, message_hash, pubkey); + /// ``` + /// + /// # Availability + /// any contract | query | transaction #[ink(extension = 14, handle_status = false)] fn ecdsa_verify_prehashed( signature: EcdsaSignature, message_hash: Hash, pubkey: EcdsaPublicKey, ) -> bool; - /// Get the contract id of the preinstalled pink-system + + /// Get the contract id of the preinstalled system contract. + /// + /// # Availability + /// any contract | query | transaction #[ink(extension = 15, handle_status = false)] fn system_contract_id() -> AccountId; - /// Get (total, free) balance of given contract + /// Get balance of a given address. + /// + /// # Arguments + /// + /// * `account`: The `AccountId` of the contract. + /// + /// # Returns + /// + /// * `(Balance, Balance)` - The total and free balance of a given contract. + /// + /// # Availability + /// any contract | query | transaction #[ink(extension = 16, handle_status = false)] fn balance_of(account: AccountId) -> (Balance, Balance); - /// Get worker public key. Query only. + /// Get the public key of the worker running this query. + /// + /// # Returns + /// + /// * `crate::EcdhPublicKey` - The public key of the worker. + /// + /// # Availability + /// any contract | query only #[ink(extension = 17, handle_status = false)] fn worker_pubkey() -> crate::EcdhPublicKey; - /// Get current millis since unix epoch from the OS. (Query only) + /// Get current millis since Unix epoch. + /// + /// This method returns the current time as milliseconds since the Unix epoch from the OS. + /// + /// # Returns + /// + /// * `u64` - The current time as milliseconds since the Unix epoch from the OS. + /// + /// # Note + /// Because this method uses the OS time, it is not deterministic and may be manipulated by compromised OS. + /// + /// # Example + /// + /// ```ignore + /// let current_millis = pink::ext().untrusted_millis_since_unix_epoch(); + /// ``` + /// + /// # Availability + /// any contract | query only #[ink(extension = 18, handle_status = false)] fn untrusted_millis_since_unix_epoch() -> u64; /// Check whether the code exists in the cluster storage. + /// + /// # Returns + /// + /// * `bool` - `true` if the code exists, `false` otherwise. + /// + /// # Example + /// + /// ```ignore + /// let code_hash = Hash::zero(); // replace with actual code hash + /// let exists = pink::ext().code_exists(code_hash, false); + /// ``` + /// + /// # Availability + /// any contract | query | transaction #[ink(extension = 19, handle_status = false)] fn code_exists(code_hash: Hash, sidevm: bool) -> bool; - /// This loads the latest system contract code from chain storage to the cluster storage. + /// Import the latest system contract code from chain storage to the cluster storage. + /// + /// # Returns + /// + /// * `Option` - The code hash of the latest system contract code, or `None` if the import failed. + /// + /// # Example /// - /// Returns the code hash of the latest system contract code. + /// ```ignore + /// let payer = AccountId::default(); // replace with actual payer id + /// let code_hash = pink::ext().import_latest_system_code(payer); + /// ``` + /// + /// # Availability + /// system only | query | transaction #[ink(extension = 20, handle_status = false)] fn import_latest_system_code(payer: AccountId) -> Option; - /// Get the version of the current contract runtime in this cluster. + /// Get the version of the current contract runtime. + /// + /// # Returns + /// + /// * `(u32, u32)` - The version of the current contract runtime in this cluster as a tuple (major, minor). + /// + /// # Example + /// + /// ```ignore + /// let (major, minor) = pink::ext().runtime_version(); + /// ``` + /// + /// # Availability + /// any contract | query | transaction #[ink(extension = 21, handle_status = false)] fn runtime_version() -> (u32, u32); - /// Batch http request + /// Batch HTTP request. + /// + /// This method sends a batch of HTTP requests with a given timeout and returns the results. + /// + /// # Arguments + /// + /// * `requests`: A vector of `HttpRequest` structs containing all the details for the HTTP requests. + /// * `timeout_ms`: The timeout for the batch request in milliseconds. + /// + /// # Returns + /// + /// * `BatchHttpResult` - A vector of response to eahch HTTP requests. + /// + /// # Example + /// + /// ```ignore + /// let requests = vec![ + /// HttpRequest::new("https://httpbin.org/get", + /// "GET", + /// Default::default(), + /// Default::default(), + /// ), + /// HttpRequest::new("https://httpbin.org/post", + /// "POST", + /// Default::default(), + /// b"Hello, world!".to_vec(), + /// ), + /// ]; + /// let result = pink::ext().batch_http_request(requests, 5000); + /// ``` #[ink(extension = 22, handle_status = true)] fn batch_http_request(requests: Vec, timeout_ms: u64) -> BatchHttpResult; } diff --git a/crates/pink/pink-extension/src/chain_extension/http_request.rs b/crates/pink/pink-extension/src/chain_extension/http_request.rs index 24cfbe0d04..a2010cd5bf 100644 --- a/crates/pink/pink-extension/src/chain_extension/http_request.rs +++ b/crates/pink/pink-extension/src/chain_extension/http_request.rs @@ -12,6 +12,23 @@ pub struct HttpRequest { pub body: Vec, } +impl HttpRequest { + /// Create a new http request. + pub fn new( + url: impl Into, + method: impl Into, + headers: Vec<(String, String)>, + body: Vec, + ) -> Self { + Self { + url: url.into(), + method: method.into(), + headers, + body, + } + } +} + #[derive(scale::Encode, scale::Decode)] #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] pub struct HttpResponse { @@ -126,8 +143,8 @@ macro_rules! http_req { /// Make a simple HTTP GET request /// /// # Arguments -/// url: The URL to GET -/// headers: The headers to send with the request +/// - url: The URL to GET +/// - headers: The headers to send with the request /// /// # Examples /// @@ -156,9 +173,9 @@ macro_rules! http_get { /// Make a simple HTTP POST request /// /// # Arguments -/// url: The URL to POST -/// data: The payload to POST -/// headers: The headers to send with the request +/// - url: The URL to POST +/// - data: The payload to POST +/// - headers: The headers to send with the request /// /// # Examples /// @@ -187,9 +204,9 @@ macro_rules! http_post { /// Make a simple HTTP PUT request /// /// # Arguments -/// url: The destination URL -/// data: The payload to PUT -/// headers: The headers to send with the request +/// - url: The destination URL +/// - data: The payload to PUT +/// - headers: The headers to send with the request /// /// # Examples /// diff --git a/crates/pink/pink-extension/src/lib.rs b/crates/pink/pink-extension/src/lib.rs index 94f90e8433..b08c4fcbe1 100644 --- a/crates/pink/pink-extension/src/lib.rs +++ b/crates/pink/pink-extension/src/lib.rs @@ -1,5 +1,6 @@ #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), feature(alloc_error_handler))] +#![doc = include_str!("../README.md")] extern crate alloc; @@ -61,9 +62,11 @@ pub struct OspMessage { pub remote_pubkey: Option, } +/// Hook points defined in the runtime. #[derive(Encode, Decode, Debug, Clone)] #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] pub enum HookPoint { + /// When all events in a block are processed. OnBlockEnd, } @@ -72,6 +75,11 @@ pub enum HookPoint { #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] pub enum PinkEvent { /// Set contract hook + /// + /// Please do not use this event directly, use [`set_hook()`] instead. + /// + /// # Availability + /// System contract #[codec(index = 2)] SetHook { /// The event to hook @@ -84,6 +92,11 @@ pub enum PinkEvent { gas_limit: u64, }, /// Deploy a sidevm instance to given contract instance + /// + /// Please do not use this event directly, use [`deploy_sidevm_to()`] instead. + /// + /// # Availability + /// System contract #[codec(index = 3)] DeploySidevmTo { /// The target contract address @@ -92,27 +105,60 @@ pub enum PinkEvent { code_hash: Hash, }, /// Push a message to the associated sidevm instance. + /// + /// Please do not use this event directly, use [`push_sidevm_message()`] instead. + /// + /// # Availability + /// Any contract #[codec(index = 4)] SidevmMessage(Vec), - /// CacheOperation + /// Instructions to manipulate the cache. Including set, remove and set expiration. + /// + /// # Availability + /// Any contract #[codec(index = 5)] CacheOp(CacheOp), - /// Stop the side VM instance if it is running. + /// Stop the side VM instance associated with the caller contract if it is running. + /// + /// Please do not use this event directly, use [`force_stop_sidevm()`] instead. + /// + /// # Availability + /// Any contract #[codec(index = 6)] StopSidevm, - /// Force stop the side VM instance if it is running. + /// Force stop the side VM instance associated with the given contract if it is running. + /// + /// Please do not use this event directly, use [`stop_sidevm_at()`] instead. + /// + /// # Availability + /// System contract #[codec(index = 7)] ForceStopSidevm { /// The target contract address contract: AccountId, }, /// Set the log handler contract for current cluster. + /// + /// Please do not use this event directly, use [`set_log_handler()`] instead. + /// + /// # Availability + /// System contract #[codec(index = 8)] SetLogHandler(AccountId), - /// Set the weight of contract used to schedule queries and sidevm vruntime + /// Set the weight of contract used to schedule queries and sidevm virtual runtime + /// + /// Please do not use this event directly, use [`set_contract_weight()`] instead. + /// + /// # Availability + /// System contract #[codec(index = 9)] SetContractWeight { contract: AccountId, weight: u32 }, /// Upgrade the runtime to given version + /// + /// Please do not use this event directly, use [`upgrade_runtime()`] instead. + /// + /// # Availability + /// System contract #[codec(index = 10)] UpgradeRuntimeTo { version: (u32, u32) }, } @@ -147,11 +193,15 @@ impl PinkEvent { } } +/// Instructions to manipulate the cache. #[derive(Encode, Decode, Debug, Clone)] #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] pub enum CacheOp { + /// Set a key-value pair in the cache. Set { key: Vec, value: Vec }, + /// Set the expiration of a key-value pair in the cache. SetExpiration { key: Vec, expiration: u64 }, + /// Remove a key-value pair from the cache. Remove { key: Vec }, } @@ -183,8 +233,25 @@ impl PinkEvent { } } -/// Turn on on_block_end feature and set it's selector +/// Sets a hook receiver for a given hook point. /// +/// A hook is a mechanism that allows certain actions to be triggered when a specific situation arises. +/// When the situation corresponding to the hook point occurs, the runtime will call the receiver contract +/// using the specified selector. +/// +/// # Supported Hook Points +/// - `OnBlockEnd`: The receiver contract will be invoked once all events in a Phala chain block have been processed. +/// +/// # Arguments +/// +/// * `hook`: The hook point for which the receiver is set. +/// * `contract`: The AccountId of the contract to be called when the hook is triggered. +/// * `selector`: The function selector to be used when calling the receiver contract. +/// * `gas_limit`: The maximum amount of gas that can be used when calling the receiver contract. +/// +/// Note: The cost of the execution would be charged to the contract itself. +/// +/// This api is only available for the system contract. User contracts should use `System::set_hook` instead. pub fn set_hook(hook: HookPoint, contract: AccountId, selector: u32, gas_limit: u64) { emit_event::(PinkEvent::SetHook { hook, @@ -194,15 +261,28 @@ pub fn set_hook(hook: HookPoint, contract: AccountId, selector: u32, gas_limit: }) } -/// Start a SideVM instance +/// Starts a SideVM instance with the provided code hash. +/// +/// The calling contract must be authorized by the `SidevmOperation` driver contract. +/// +/// If the code corresponding to the provided hash hasn't been uploaded to the cluster storage yet, +/// it will create an empty SideVM instance. This instance will wait for the code to be uploaded +/// via `prpc::UploadSidevmCode`. +/// +///# Arguments +/// +///* `code_hash`: The hash of the code to be used for starting the SideVM instance. +/// +///# Returns +/// +/// A `Result` indicating success or failure, specifically a `system::DriverError` in case of failure. pub fn start_sidevm(code_hash: Hash) -> Result<(), system::DriverError> { let driver = crate::system::SidevmOperationRef::instance().ok_or(system::Error::DriverNotFound)?; driver.deploy(code_hash) } -/// Deploy a SideVM instance to a given contract. -/// The caller must be the system contract. +/// Deploy a SideVM instance to a given contract. (system only) pub fn deploy_sidevm_to(contract: AccountId, code_hash: Hash) { emit_event::(PinkEvent::DeploySidevmTo { contract, @@ -210,8 +290,7 @@ pub fn deploy_sidevm_to(contract: AccountId, code_hash: Hash) { }); } -/// Stop a SideVM instance running at given contract address. -/// The caller must be the system contract. +/// Stop a SideVM instance running at given contract address. (system only) pub fn stop_sidevm_at(contract: AccountId) { emit_event::(PinkEvent::ForceStopSidevm { contract }); } @@ -224,27 +303,61 @@ pub fn force_stop_sidevm() { emit_event::(PinkEvent::StopSidevm) } -/// Push a message to the associated sidevm instance. +/// Pushes a message to the associated SideVM instance. +/// +/// Note: There is no guarantee that the message will be received by the SideVM instance. +/// The message may be dropped due to several reasons: +/// +/// - The SideVM instance is not currently running. +/// - The SideVM instance is running, but the message queue is full. This may occur when the SideVM +/// instance is busy processing other messages. +/// +///# Arguments +/// +///* `message`: The message to be pushed to the SideVM instance. pub fn push_sidevm_message(message: Vec) { emit_event::(PinkEvent::SidevmMessage(message)) } -/// Set the log handler contract of current cluster +/// Set the log handler contract of current cluster. (system only) pub fn set_log_handler(contract: AccountId) { emit_event::(PinkEvent::SetLogHandler(contract)) } -/// Set the weight of contract used to schedule queries and sidevm vruntime +/// Set the weight of contract used to schedule queries and sidevm virtual runtime. (system only) pub fn set_contract_weight(contract: AccountId, weight: u32) { emit_event::(PinkEvent::SetContractWeight { contract, weight }); } -/// Upgrade the runtime to given version +/// Upgrade the pink runtime to given version. (system only) +/// +/// Note: pRuntime would exit if the version is not supported. pub fn upgrade_runtime(version: (u32, u32)) { emit_event::(PinkEvent::UpgradeRuntimeTo { version }); } -/// Pink defined environment. Used this environment to access the phat contract runtime features. +/// Pink defined environment. This environment is used to access the phat contract extended runtime features. +/// +/// # Example +/// ``` +/// #[ink::contract(env = PinkEnvironment)] +/// mod my_contract { +/// use pink_extension::PinkEnvironment; +/// #[ink(storage)] +/// pub struct MyContract {} +/// impl MyContract { +/// #[ink(constructor)] +/// pub fn new() -> Self { +/// Self {} +/// } +/// #[ink(message)] +/// pub fn my_message(&self) { +/// // Access the pink environment. +/// let _pink_version = self.env().extension().runtime_version(); +/// } +/// } +/// } +/// ``` #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] pub enum PinkEnvironment {} @@ -261,6 +374,7 @@ impl Environment for PinkEnvironment { type ChainExtension = chain_extension::PinkExt; } +/// Returns the PinkEnvironment. pub fn env() -> EnvAccess<'static, PinkEnvironment> { Default::default() } diff --git a/crates/pink/pink-extension/src/logger.rs b/crates/pink/pink-extension/src/logger.rs index 89f1f785fe..ed156ebc30 100644 --- a/crates/pink/pink-extension/src/logger.rs +++ b/crates/pink/pink-extension/src/logger.rs @@ -34,37 +34,58 @@ pub fn log(level: Level, args: Arguments<'_>) { Logger.log(&record); } -/// Same as log::log! +/// The `log!` macro allows you to log messages with specific logging levels in pink contract. +/// +/// It is a flexible macro that uses a provided log level (trace, debug, info, warn, error), +/// followed by a format string and an optional list of arguments to generate the final log message. #[macro_export] macro_rules! log { ($level: expr, $($arg:tt)+) => {{ $crate::logger::log($level, ::core::format_args!($($arg)+)) }} } -/// Same as log::error! +/// Same as `info!` but at Error level. #[macro_export(local_inner_macros)] macro_rules! error { ($($arg:tt)+) => {{ log!($crate::logger::Level::Error, $($arg)+) }} } -/// Same as log::warn! +/// Same as `info!` but at Warn level. #[macro_export(local_inner_macros)] macro_rules! warn { ($($arg:tt)+) => {{ log!($crate::logger::Level::Warn, $($arg)+) }} } -/// Same as log::info! +/// Macro `info!` logs messages at the Info level in pink contract. +/// +/// This macro is used to log information that would be helpful to understand the general flow +/// of the system's execution. It is similar to `log::info`, but it is specifically designed +/// to work within the pink contract environment. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ```ignore +/// use pink_extension as pink; +/// pink::info!("This is an information message."); +/// let answer = 42; +/// pink::info!("The answer is {}.", answer); +/// ``` +/// +/// The above example would log "This is an information message." and +/// "The answer is 42." at the Info level. #[macro_export(local_inner_macros)] macro_rules! info { ($($arg:tt)+) => {{ log!($crate::logger::Level::Info, $($arg)+) }} } -/// Same as log::debug! +/// Same as `info!` but at Debug level. #[macro_export(local_inner_macros)] macro_rules! debug { ($($arg:tt)+) => {{ log!($crate::logger::Level::Debug, $($arg)+) }} } -/// Same as log::trace! +/// Same as `info!` but at Trace level. #[macro_export(local_inner_macros)] macro_rules! trace { ($($arg:tt)+) => {{ log!($crate::logger::Level::Trace, $($arg)+) }} diff --git a/crates/pink/pink-extension/src/system.rs b/crates/pink/pink-extension/src/system.rs index f06b166293..48775de94b 100644 --- a/crates/pink/pink-extension/src/system.rs +++ b/crates/pink/pink-extension/src/system.rs @@ -41,52 +41,65 @@ pub use this_crate::VersionTuple; /// The pink system contract interface. /// -/// A system contract would be instantiated whenever a cluster is created. +/// The system contract, instantiated with each cluster creation, manages access permissions to +/// the privileged chain extension functions and pink events. Some of these functions or events +/// are exclusive to the system contract. User contracts wishing to call these functions or +/// emit these events must first request the system contract, which then checks the permissions +/// to either execute or reject the request. #[pink::system] #[ink::trait_definition(namespace = "pink_system")] pub trait System { - /// The version of the system. Can be used to determine the api ability. + /// Returns the system contract version, indicating its API capabilities. + /// + /// # Example + /// ```no_run + /// use pink_extension::system::SystemRef; + /// let (major, minor, patch) = SystemRef::instance().version(); + /// ``` #[ink(message, selector = 0x87c98a8d)] fn version(&self) -> VersionTuple; - /// Grant an address the administrator role. + + /// Grants the administrator role to an address. Administrator contracts can set drivers, + /// deploy sidevm, etc. /// - /// The caller must be the owner of the cluster. + /// Must be called by the cluster owner. #[ink(message)] fn grant_admin(&mut self, contract_id: AccountId) -> Result<()>; - /// Check if an address is an administrator + /// Checks if an address is an administrator. #[ink(message)] fn is_admin(&self, contract_id: AccountId) -> bool; - /// Set a contract as a driver for `name`. + /// Marks a contract as a driver for a given name, retrievable via `get_driver` or `get_driver2`. + /// The caller must be the cluster owner or an administrator. Any valid string can be a name. + /// There are predefined names used by the Phat Contract system. /// - /// The caller must be the owner of the cluster or an administrator. + /// There are some predefined names that are used by the Phat Contract system: + /// - `PinkLogger`: The contract that with a sidevm instance that collect the logs and events + /// emitted by the ink! contracts in current cluster. + /// - `ContractDeposit`: The contract that implements the `trait ContractDeposit` which talks + /// to the pallet PhatTokenomic on Phala chain. #[ink(message)] fn set_driver(&mut self, name: String, contract_id: AccountId) -> Result<()>; - /// Get driver contract id for `name`. + /// Retrieves the driver contract id for a given name. #[ink(message)] fn get_driver(&self, name: String) -> Option; - /// Get driver contract id for `name` and the set block number. + /// Retrieves the driver contract id and the set block number for a given name. #[ink(message)] fn get_driver2(&self, name: String) -> Option<(crate::BlockNumber, AccountId)>; - /// Deploy a sidevm instance attached to a given contract. - /// - /// The caller must be an administrator. + /// Deploys a sidevm instance attached to a contract. Must be called by an administrator. #[ink(message)] fn deploy_sidevm_to(&self, contract_id: AccountId, code_hash: Hash) -> Result<()>; - /// Stop a sidevm instance attached to a given contract. - /// - /// The caller must be an administrator. + /// Stops a sidevm instance attached to a contract. Must be called by an administrator. #[ink(message)] fn stop_sidevm_at(&self, contract_id: AccountId) -> Result<()>; - /// Set block hook, such as OnBlockEnd, for given contract - /// - /// The caller must be an administrator. + /// Sets a block hook for a contract. Must be called by an administrator. + /// Note: This feature is deprecated and will be removed in the future. #[ink(message)] fn set_hook( &mut self, @@ -96,44 +109,43 @@ pub trait System { gas_limit: u64, ) -> Result<()>; - /// Set weight of the contract for query requests and sidevm scheduling. - /// - /// Higher weight would let the contract to get more resource. + /// Sets the contract weight for query requests and sidevm scheduling. + /// A higher weight allows the contract to access more resources. #[ink(message)] fn set_contract_weight(&self, contract_id: AccountId, weight: u32) -> Result<()>; - /// Return the total balance of given account + /// Returns the total balance of a given account. #[ink(message)] fn total_balance_of(&self, account: AccountId) -> Balance; - /// Return the free balance of given account + /// Returns the free balance of a given account. #[ink(message)] fn free_balance_of(&self, account: AccountId) -> Balance; - /// Upgrade the system contract to the latest version. + /// Upgrades the system contract to the latest version. #[ink(message)] fn upgrade_system_contract(&mut self) -> Result<()>; - /// Do the upgrade condition checks and state migration if necessary. - /// - /// This function is called by the system contract itself on the new version - /// of code in the upgrading process. + /// Performs upgrade condition checks and state migration if necessary. + /// Called by the system contract on the new code version during an upgrade process. #[ink(message)] fn do_upgrade(&self, from_version: VersionTuple) -> Result<()>; - /// Upgrade the contract runtime + /// Upgrades the contract runtime. #[ink(message)] fn upgrade_runtime(&mut self, version: (u32, u32)) -> Result<()>; - /// Check if the code is already uploaded to the cluster with given code hash. + /// Checks if the code with a given hash is already uploaded to the cluster. #[ink(message)] fn code_exists(&self, code_hash: Hash, code_type: CodeType) -> bool; - /// Get the current code hash of given contract. + /// Retrieves the current code hash of a given contract. #[ink(message)] fn code_hash(&self, account: AccountId) -> Option; - /// Get the history of given driver. + /// Retrieves the history of a given driver, returning a vector of + /// (block_number, contract_id) tuples where the block number is the + /// block number when the driver is set. #[ink(message)] fn driver_history(&self, name: String) -> Option>; } diff --git a/standalone/pruntime/Cargo.lock b/standalone/pruntime/Cargo.lock index fe1bcc20a8..657d1ac278 100644 --- a/standalone/pruntime/Cargo.lock +++ b/standalone/pruntime/Cargo.lock @@ -4869,7 +4869,7 @@ dependencies = [ [[package]] name = "pink-extension" -version = "0.4.3" +version = "0.4.4" dependencies = [ "ink", "log", @@ -4882,7 +4882,7 @@ dependencies = [ [[package]] name = "pink-extension-macro" -version = "0.4.2" +version = "0.4.4" dependencies = [ "heck 0.4.0", "ink_ir",