Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automation #1

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 20 additions & 42 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 4 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -30,7 +30,6 @@ tokio.workspace = true
[workspace]
members = [
"io",
"state",
"xtask",
]

Expand All @@ -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"
2 changes: 1 addition & 1 deletion build.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use template_io::ContractMetadata;
use automation_io::ContractMetadata;

fn main() {
gear_wasm_builder::build_with_metadata::<ContractMetadata>();
Expand Down
2 changes: 1 addition & 1 deletion io/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "template-io"
name = "automation-io"
version.workspace = true
edition.workspace = true
publish.workspace = true
Expand Down
76 changes: 38 additions & 38 deletions io/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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`].
Expand All @@ -13,59 +13,59 @@ 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<PingPong, PingPong>;
type Handle = InOut<Action, Event>;
/// Types for miscellaneous scenarios.
type Others = ();
/// The input type for the `handle_reply()` entry point.
type Reply = ();
/// 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<State>;
}

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<u8>,
value: u128,
},
SendDelayed {
to: ActorId,
payload: Vec<u8>,
value: u128,
delay: u32,
},
SendFromReservation {
to: ActorId,
payload: Vec<u8>,
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<ActorId>),
/// Returned from [`StateQuery::PingCount`].
PingCount(u128),
pub enum Event {
Reserved(u64, u32, ReservationId),
ReservedMany(u64, u32, u32),
Unreserved(u64),
Sent(ActorId, Vec<u8>, u128),
SentDelayed(ActorId, Vec<u8>, u128, u32),
SentFromReservation(ActorId, Vec<u8>, u128),
}
110 changes: 91 additions & 19 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -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<HashMap<ActorId, u128>> = 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);
}
13 changes: 0 additions & 13 deletions state/Cargo.toml

This file was deleted.

3 changes: 0 additions & 3 deletions state/build.rs

This file was deleted.

Loading