diff --git a/Cargo.lock b/Cargo.lock index ba98541..29c27fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -501,6 +501,26 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +[[package]] +name = "automation" +version = "0.1.0" +dependencies = [ + "automation-io", + "gclient", + "gear-wasm-builder", + "gstd", + "gtest", + "tokio", +] + +[[package]] +name = "automation-io" +version = "0.1.0" +dependencies = [ + "gmeta", + "gstd", +] + [[package]] name = "backtrace" version = "0.3.71" @@ -2558,22 +2578,10 @@ checksum = "82d4fbb3700df8abeed1753ca211be90d9ae7eba772dee1b097f2e6c0ac27871" dependencies = [ "blake2", "derive_more", - "gmeta-codegen", "hex", "scale-info", ] -[[package]] -name = "gmeta-codegen" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54280c1b89114b9b6490246e1df64dcb4d8bd1a8fabb3885e954677a439f9179" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.71", -] - [[package]] name = "gp-allocator" version = "4.1.2" @@ -6041,36 +6049,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "template" -version = "0.1.0" -dependencies = [ - "gclient", - "gear-wasm-builder", - "gstd", - "gtest", - "template-io", - "tokio", -] - -[[package]] -name = "template-io" -version = "0.1.0" -dependencies = [ - "gmeta", - "gstd", -] - -[[package]] -name = "template-state" -version = "0.1.0" -dependencies = [ - "gear-wasm-builder", - "gmeta", - "gstd", - "template-io", -] - [[package]] name = "termcolor" version = "1.4.1" diff --git a/Cargo.toml b/Cargo.toml index 4eb934e..f145f83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,18 +7,18 @@ edition = "2021" publish = false [package] -name = "template" +name = "automation" version.workspace = true edition.workspace = true publish.workspace = true [dependencies] gstd.workspace = true -template-io.workspace = true +automation-io.workspace = true [build-dependencies] gear-wasm-builder.workspace = true -template-io.workspace = true +automation-io.workspace = true [dev-dependencies] gtest.workspace = true @@ -30,7 +30,6 @@ tokio.workspace = true [workspace] members = [ "io", - "state", "xtask", ] @@ -40,7 +39,7 @@ gmeta = "1.4.2" gear-wasm-builder = "1.4.2" gtest = "1.4.2" gclient = "1.4.2" -template-io.path = "io" +automation-io.path = "io" tokio = "1" xshell = "0.2" anyhow = "1" diff --git a/build.rs b/build.rs index 858a831..bbd85cb 100644 --- a/build.rs +++ b/build.rs @@ -1,4 +1,4 @@ -use template_io::ContractMetadata; +use automation_io::ContractMetadata; fn main() { gear_wasm_builder::build_with_metadata::(); diff --git a/io/Cargo.toml b/io/Cargo.toml index 24e0a9c..f9c99b5 100644 --- a/io/Cargo.toml +++ b/io/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "template-io" +name = "automation-io" version.workspace = true edition.workspace = true publish.workspace = true diff --git a/io/src/lib.rs b/io/src/lib.rs index bd99a96..5ea1cad 100644 --- a/io/src/lib.rs +++ b/io/src/lib.rs @@ -1,7 +1,7 @@ #![no_std] use gmeta::{InOut, Metadata, Out}; -use gstd::{prelude::*, ActorId}; +use gstd::{prelude::*, ActorId, ReservationId}; /// The contract metadata. Used by frontend apps & for describing the types of messages that can be /// sent in contract's entry points. See also [`Metadata`]. @@ -13,9 +13,7 @@ impl Metadata for ContractMetadata { /// I/O types for the `init()` entry point. type Init = (); /// I/O types for the `handle()` entry point. - /// - /// Here the [`PingPong`] type is used for both incoming and outgoing messages. - type Handle = InOut; + type Handle = InOut; /// Types for miscellaneous scenarios. type Others = (); /// The input type for the `handle_reply()` entry point. @@ -23,49 +21,51 @@ impl Metadata for ContractMetadata { /// The output type for the `handle_signal()` entry point. type Signal = (); /// I/O types for the `state()` entry point. - /// - /// You can also specify just an output ([`Out`]) or input ([`In`](gmeta::In)) type, if both - /// ([`InOut`]) are expected like here. type State = Out; } -pub type State = Vec<(ActorId, u128)>; +pub type State = ReservationId; -/// Replies with [`Pong`](PingPong::Pong) if received [`Ping`](PingPong::Ping). #[derive(Encode, Decode, TypeInfo, Debug, PartialEq, Eq)] #[codec(crate = gstd::codec)] #[scale_info(crate = gstd::scale_info)] -pub enum PingPong { - Ping, - Pong, +pub enum Action { + Reserve { + gas: u64, + blocks: u32, + }, + ReserveMany { + gas: u64, + blocks: u32, + times: u32, + }, + Unreserve, + Send { + to: ActorId, + payload: Vec, + value: u128, + }, + SendDelayed { + to: ActorId, + payload: Vec, + value: u128, + delay: u32, + }, + SendFromReservation { + to: ActorId, + payload: Vec, + value: u128, + }, } -/// Queries the contract state. -/// -/// Used in the `state` crate. -#[derive(Encode, Decode, TypeInfo)] -#[codec(crate = gstd::codec)] -#[scale_info(crate = gstd::scale_info)] -pub enum StateQuery { - /// Gets the list of actors who have [`ping`](PingPong::Ping)ed the contract. - /// - /// Returns [`StateQueryReply::Pingers`]. - Pingers, - /// Gets the count of [`ping`](PingPong::Ping)s received from the given [`ActorId`]. - /// - /// Returns [`StateQueryReply::PingCount`]. - PingCount(ActorId), -} - -/// The result of successfully processed [`StateQuery`]. -/// -/// Used in the `state` crate. -#[derive(Encode, Decode, TypeInfo, PartialEq, Eq, Debug)] +#[derive(Encode, Decode, TypeInfo, Debug, PartialEq, Eq)] #[codec(crate = gstd::codec)] #[scale_info(crate = gstd::scale_info)] -pub enum StateQueryReply { - /// Returned from [`StateQuery::Pingers`]. - Pingers(Vec), - /// Returned from [`StateQuery::PingCount`]. - PingCount(u128), +pub enum Event { + Reserved(u64, u32, ReservationId), + ReservedMany(u64, u32, u32), + Unreserved(u64), + Sent(ActorId, Vec, u128), + SentDelayed(ActorId, Vec, u128, u32), + SentFromReservation(ActorId, Vec, u128), } diff --git a/src/lib.rs b/src/lib.rs index 49d7be6..36f5567 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,36 +1,108 @@ #![no_std] -use gstd::{collections::HashMap, msg, prelude::*, ActorId}; -use template_io::*; +use automation_io::*; +use gstd::{actor_id, exec, msg, prelude::*, ReservationId, Reservations}; -static mut STATE: Option> = None; +// 延迟消息 +// 1. send vs send_delayed -// The `init()` entry point. -#[no_mangle] -extern fn init() { - unsafe { STATE = Some(Default::default()) } +// 单次 Gas 预留 +// 1. reserve 限额 +// 2. 超时自动 unreserve +// 3. 手动 unreserve +// 4. send_from_reservation, 同一 rid 不可多次用 +static mut RESERVED: ReservationId = ReservationId::zero(); + +// 多次 Gas 预留 +// 1. 多次 reserve 限额 +// 2. 定时任务, self-execution +static mut MANAGER: Reservations = Reservations::new(); + +fn self_execution() { + // 可选: 向 owner 地址发送消息,观察 mailbox 内容 + let reservation = unsafe { MANAGER.try_take_reservation(100_000_000) }; + if let Some(reservation) = reservation { + msg::send_bytes_from_reservation( + reservation.id(), + actor_id!("0x7453a73e8398c970a2b17319e3084874e47b737bd0b5f1a1f405a382e6b05458"), + format!("remaining: {}", unsafe { MANAGER.count_valid() }), + 0, + ) + .expect("Failed to send message from reservation"); + } + + // 通过向自身发送延迟消息,触发下一次执行 + let reservation = unsafe { MANAGER.try_take_reservation(100_000_000) }; + if let Some(reservation) = reservation { + msg::send_bytes_delayed_from_reservation( + reservation.id(), + exec::program_id(), + "send_bytes_delayed_from_reservation", + 0, + 1, + ) + .expect("Failed to send message from reservation"); + } } // The `handle()` entry point. #[no_mangle] extern fn handle() { - let payload = msg::load().expect("Failed to load payload"); - - if let PingPong::Ping = payload { - let pingers = unsafe { STATE.as_mut().expect("State isn't initialized") }; + if msg::source() == exec::program_id() { + self_execution(); + return; + } - pingers - .entry(msg::source()) - .and_modify(|ping_count| *ping_count = ping_count.saturating_add(1)) - .or_insert(1); + let payload = msg::load().expect("Failed to load payload"); + let mut rid = unsafe { RESERVED }; - msg::reply(PingPong::Pong, 0).expect("Failed to reply from `handle()`"); - } + let reply = match payload { + Action::Reserve { gas, blocks } => { + unsafe { + rid = exec::reserve_gas(gas, blocks).expect("Failed to reserve"); + RESERVED = rid; + } + Event::Reserved(gas, blocks, rid) + } + Action::ReserveMany { gas, blocks, times } => { + unsafe { + for _ in 0..times { + MANAGER + .reserve(gas, blocks) + .expect("Failed to reserve many"); + } + } + Event::ReservedMany(gas, blocks, times) + } + Action::Unreserve => { + let amount = rid.unreserve().expect("Failed to unreserve"); + Event::Unreserved(amount) + } + Action::Send { to, payload, value } => { + msg::send_bytes(to, payload.clone(), value).expect("Failed to send"); + Event::Sent(to, payload, value) + } + Action::SendDelayed { + to, + payload, + value, + delay, + } => { + msg::send_bytes_delayed(to, payload.clone(), value, delay).expect("Failed to send"); + Event::SentDelayed(to, payload, value, delay) + } + Action::SendFromReservation { to, payload, value } => { + msg::send_bytes_from_reservation(rid, to, payload.clone(), value) + .expect("Failed to send"); + Event::SentFromReservation(to, payload, value) + } + }; + let _ = msg::reply(reply, 0).expect("Failed to reply"); } // The `state()` entry point. #[no_mangle] extern fn state() { - let state = unsafe { STATE.take().expect("State isn't initialized") }; - msg::reply(State::from_iter(state), 0).expect("Failed to reply from `state()`"); + let rid = unsafe { RESERVED }; + let _ = msg::reply(rid, 0); } diff --git a/state/Cargo.toml b/state/Cargo.toml deleted file mode 100644 index 337633c..0000000 --- a/state/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "template-state" -version.workspace = true -edition.workspace = true -publish.workspace = true - -[dependencies] -gstd.workspace = true -gmeta = { workspace = true, features = ["codegen"] } -template-io.workspace = true - -[build-dependencies] -gear-wasm-builder = { workspace = true, features = ["metawasm"] } diff --git a/state/build.rs b/state/build.rs deleted file mode 100644 index b4c4a44..0000000 --- a/state/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - gear_wasm_builder::build_metawasm(); -} diff --git a/state/src/lib.rs b/state/src/lib.rs deleted file mode 100644 index 6d43fb1..0000000 --- a/state/src/lib.rs +++ /dev/null @@ -1,28 +0,0 @@ -#![no_std] - -use gstd::{prelude::*, ActorId}; -use template_io::*; - -#[gmeta::metawasm] -pub mod metafns { - pub type State = template_io::State; - - pub fn query(state: State, query: StateQuery) -> StateQueryReply { - match query { - StateQuery::Pingers => StateQueryReply::Pingers(pingers(state)), - StateQuery::PingCount(actor) => StateQueryReply::PingCount(ping_count(state, actor)), - } - } - - pub fn pingers(state: State) -> Vec { - state.iter().map(|(pinger, _)| *pinger).collect() - } - - pub fn ping_count(state: State, actor: ActorId) -> u128 { - state - .iter() - .find_map(|(some_actor, count)| (some_actor == &actor).then_some(count)) - .copied() - .unwrap_or_default() - } -}