diff --git a/Cargo.lock b/Cargo.lock
index bd513d4afba..5f080df16e4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3769,6 +3769,16 @@ dependencies = [
"scale-info",
]
+[[package]]
+name = "demo-proxy-broker"
+version = "0.1.0"
+dependencies = [
+ "gbuiltin-proxy",
+ "gear-wasm-builder",
+ "gstd",
+ "parity-scale-codec",
+]
+
[[package]]
name = "demo-proxy-relay"
version = "0.1.0"
@@ -6213,6 +6223,15 @@ dependencies = [
"scale-info",
]
+[[package]]
+name = "gbuiltin-proxy"
+version = "1.6.2"
+dependencies = [
+ "derive_more 0.99.18",
+ "gprimitives",
+ "scale-info",
+]
+
[[package]]
name = "gbuiltin-staking"
version = "1.6.2"
@@ -11485,6 +11504,7 @@ dependencies = [
"ark-scale 0.0.12",
"ark-serialize 0.4.2",
"ark-std 0.4.0",
+ "demo-proxy-broker",
"demo-staking-broker",
"demo-waiting-proxy",
"derive_more 0.99.18",
@@ -11496,6 +11516,7 @@ dependencies = [
"frame-support-test",
"frame-system",
"gbuiltin-bls381",
+ "gbuiltin-proxy",
"gbuiltin-staking",
"gear-common",
"gear-core",
@@ -11514,6 +11535,7 @@ dependencies = [
"pallet-gear-messenger",
"pallet-gear-program",
"pallet-gear-scheduler",
+ "pallet-proxy",
"pallet-session",
"pallet-staking",
"pallet-timestamp",
@@ -18846,6 +18868,7 @@ dependencies = [
"frame-system-benchmarking",
"frame-system-rpc-runtime-api",
"frame-try-runtime",
+ "gbuiltin-proxy",
"gbuiltin-staking",
"gear-common",
"gear-core",
diff --git a/Cargo.toml b/Cargo.toml
index da0cb93b1f8..d8ed67f243c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -62,6 +62,7 @@ members = [
"examples/program-factory",
"examples/program-generator",
"examples/proxy",
+ "examples/proxy-broker",
"examples/proxy-relay",
"examples/proxy-reservation-with-gas",
"examples/read-big-state",
@@ -216,6 +217,7 @@ galloc = { path = "galloc" }
gbuiltin-bls381 = { path = "gbuiltins/bls381", default-features = false }
gbuiltin-eth-bridge = { path = "gbuiltins/eth-bridge", default-features = false }
gbuiltin-staking = { path = "gbuiltins/staking" }
+gbuiltin-proxy = { path = "gbuiltins/proxy" }
gcore = { path = "gcore" }
gcli = { path = "gcli" }
gclient = { path = "gclient" }
@@ -484,6 +486,7 @@ demo-rwlock = { path = "examples/rwlock" }
demo-send-from-reservation = { path = "examples/send-from-reservation" }
demo-signal-entry = { path = "examples/signal-entry", default-features = false }
demo-staking-broker = { path = "examples/staking-broker" }
+demo-proxy-broker = { path = "examples/proxy-broker" }
demo-state-rollback = { path = "examples/state-rollback" }
demo-sync-duplicate = { path = "examples/sync-duplicate" }
demo-vec = { path = "examples/vec" }
diff --git a/examples/proxy-broker/Cargo.toml b/examples/proxy-broker/Cargo.toml
new file mode 100644
index 00000000000..d0d1a943a99
--- /dev/null
+++ b/examples/proxy-broker/Cargo.toml
@@ -0,0 +1,21 @@
+[package]
+name = "demo-proxy-broker"
+version = "0.1.0"
+authors.workspace = true
+edition.workspace = true
+license.workspace = true
+homepage.workspace = true
+repository.workspace = true
+
+[dependencies]
+gbuiltin-proxy.workspace = true
+gstd.workspace = true
+parity-scale-codec.workspace = true
+
+[build-dependencies]
+gear-wasm-builder.workspace = true
+
+[features]
+debug = ["gstd/debug"]
+std = []
+default = ["std"]
diff --git a/examples/proxy-broker/build.rs b/examples/proxy-broker/build.rs
new file mode 100644
index 00000000000..6a370b53a71
--- /dev/null
+++ b/examples/proxy-broker/build.rs
@@ -0,0 +1,21 @@
+// This file is part of Gear.
+
+// Copyright (C) 2024 Gear Technologies Inc.
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+fn main() {
+ gear_wasm_builder::build();
+}
diff --git a/examples/proxy-broker/src/lib.rs b/examples/proxy-broker/src/lib.rs
new file mode 100644
index 00000000000..28b7cf0f8c3
--- /dev/null
+++ b/examples/proxy-broker/src/lib.rs
@@ -0,0 +1,33 @@
+// This file is part of Gear.
+//
+// Copyright (C) 2024 Gear Technologies Inc.
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+#[cfg(feature = "std")]
+mod code {
+ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
+}
+
+#[cfg(feature = "std")]
+pub use code::WASM_BINARY_OPT as WASM_BINARY;
+
+#[cfg(not(feature = "std"))]
+pub const WASM_BINARY: &[u8] = &[];
+
+#[cfg(not(feature = "std"))]
+pub mod wasm;
diff --git a/examples/proxy-broker/src/wasm.rs b/examples/proxy-broker/src/wasm.rs
new file mode 100644
index 00000000000..f3f9225360e
--- /dev/null
+++ b/examples/proxy-broker/src/wasm.rs
@@ -0,0 +1,72 @@
+// This file is part of Gear.
+//
+// Copyright (C) 2024 Gear Technologies Inc.
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+//! Basic implementation of the proxy-broker for demo purpose only.
+
+use gbuiltin_proxy::Request;
+use gstd::{actor_id, debug, errors::Error, msg, ActorId};
+
+// Proxy builtin actor program id (hardcoded for all runtimes);
+//
+// Calculated as hash((b"built/in", 3u64).encode())
+const BUILTIN_ADDRESS: ActorId =
+ actor_id!("0xf2816ced0b15749595392d3a18b5a2363d6fefe5b3b6153739f218151b7acdbf");
+
+#[gstd::async_main]
+async fn main() {
+ let request: Request = msg::load().expect("handle: invalid payload received");
+ match request {
+ add_proxy @ Request::AddProxy { .. } => {
+ debug!(
+ "handle: Sending `add_proxy` request with data {:?}",
+ add_proxy
+ );
+
+ send_request(add_proxy).await;
+ }
+ remove_proxy @ Request::RemoveProxy { .. } => {
+ debug!(
+ "handle: Sending `remove_proxy` request with data {:?}",
+ remove_proxy
+ );
+
+ send_request(remove_proxy).await;
+ }
+ }
+}
+
+async fn send_request(req: Request) {
+ let res = msg::send_for_reply(BUILTIN_ADDRESS, req, 0, 0)
+ .expect("handle::send_request: failed sending message for reply")
+ .await;
+ match res {
+ Ok(_) => {
+ debug!("handle::send_request: Success reply from builtin actor received");
+ msg::reply_bytes(b"Success", 0).expect("Failed to send reply");
+ }
+ Err(e) => {
+ debug!("handle::send_request: Error reply from builtin actor received: {e:?}");
+ match e {
+ Error::ErrorReply(payload, _) => {
+ panic!("{}", payload);
+ }
+ _ => panic!("Error in upstream program"),
+ }
+ }
+ }
+}
diff --git a/gbuiltins/proxy/Cargo.toml b/gbuiltins/proxy/Cargo.toml
new file mode 100644
index 00000000000..4912223625b
--- /dev/null
+++ b/gbuiltins/proxy/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "gbuiltin-proxy"
+description = "Types and traits to interact with proxy builtin actor."
+version.workspace = true
+authors.workspace = true
+edition.workspace = true
+license.workspace = true
+homepage.workspace = true
+repository.workspace = true
+
+[dependencies]
+gprimitives.workspace = true
+derive_more.workspace = true
+scale-info = { workspace = true, features = ["derive"] }
diff --git a/gbuiltins/proxy/src/lib.rs b/gbuiltins/proxy/src/lib.rs
new file mode 100644
index 00000000000..49eecad834b
--- /dev/null
+++ b/gbuiltins/proxy/src/lib.rs
@@ -0,0 +1,64 @@
+// This file is part of Gear.
+
+// Copyright (C) 2021-2024 Gear Technologies Inc.
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+//! Types used to communicate with proxy built-in.
+
+#![no_std]
+
+use gprimitives::ActorId;
+use scale_info::scale::{self, Decode, Encode};
+
+/// Request that can be handled by the proxy builtin.
+///
+/// Currently all proxies aren't required to send announcement,
+/// i.e. no delays for the delegate actions.
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Encode, Decode)]
+#[codec(crate = scale)]
+pub enum Request {
+ /// Add proxy request.
+ ///
+ /// Requests to add `delegate` as a delegate for the actions
+ /// defined by `proxy_type` to be done on behalf of the request
+ /// sender.
+ AddProxy {
+ delegate: ActorId,
+ proxy_type: ProxyType,
+ },
+ /// Remove proxy request.
+ ///
+ /// Request sender asks to remove `delegate` with set of allowed actions
+ /// defined in `proxy_type` from his list of proxies.
+ RemoveProxy {
+ delegate: ActorId,
+ proxy_type: ProxyType,
+ },
+}
+
+/// Proxy type.
+///
+/// The mirror enum for the one defined in vara-runtime crate.
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Encode, Decode)]
+#[codec(crate = scale)]
+pub enum ProxyType {
+ Any,
+ NonTransfer,
+ Governance,
+ Staking,
+ IdentityJudgement,
+ CancelProxy,
+}
diff --git a/pallets/gear-builtin/Cargo.toml b/pallets/gear-builtin/Cargo.toml
index cbb9c77edf4..4430f848c20 100644
--- a/pallets/gear-builtin/Cargo.toml
+++ b/pallets/gear-builtin/Cargo.toml
@@ -30,6 +30,7 @@ common.workspace = true
core-processor.workspace = true
gbuiltin-bls381.workspace = true
gbuiltin-staking.workspace = true
+gbuiltin-proxy.workspace = true
gear-core.workspace = true
gear-core-errors.workspace = true
gear-runtime-interface.workspace = true
@@ -41,10 +42,12 @@ sp-std.workspace = true
sp-runtime = { workspace = true, features = ["serde"] }
pallet-gear.workspace = true
pallet-staking.workspace = true
+pallet-proxy.workspace = true
[dev-dependencies]
demo-waiting-proxy.workspace = true
demo-staking-broker.workspace = true
+demo-proxy-broker.workspace = true
gprimitives.workspace = true
sp-core = { workspace = true, features = ["std"] }
sp-externalities = { workspace = true, features = ["std"] }
@@ -81,6 +84,7 @@ std = [
"gear-runtime-interface/std",
"pallet-gear/std",
"pallet-staking/std",
+ "pallet-proxy/std",
"parity-scale-codec/std",
"scale-info/std",
"sp-crypto-ec-utils/std",
diff --git a/pallets/gear-builtin/src/lib.rs b/pallets/gear-builtin/src/lib.rs
index e31069a873e..4109567c7ad 100644
--- a/pallets/gear-builtin/src/lib.rs
+++ b/pallets/gear-builtin/src/lib.rs
@@ -36,6 +36,7 @@ extern crate alloc;
pub mod benchmarking;
pub mod bls12_381;
+pub mod proxy;
pub mod staking;
pub mod weights;
@@ -58,6 +59,7 @@ use core_processor::{
process_execution_error, process_success, SuccessfulDispatchResultKind,
SystemReservationContext,
};
+use frame_support::dispatch::extract_actual_weight;
use gear_core::{
gas::GasCounter,
ids::{hash, ProgramId},
@@ -73,6 +75,8 @@ use sp_std::prelude::*;
pub use pallet::*;
+type CallOf = ::RuntimeCall;
+
const LOG_TARGET: &str = "gear::builtin";
/// Built-in actor error type
@@ -172,6 +176,7 @@ impl BuiltinCollection for Tuple {
#[frame_support::pallet]
pub mod pallet {
use super::*;
+ use common::Origin;
use frame_support::{
dispatch::{GetDispatchInfo, PostDispatchInfo},
pallet_prelude::*,
@@ -212,6 +217,45 @@ pub mod pallet {
pub fn generate_actor_id(builtin_id: u64) -> ProgramId {
hash((SEED, builtin_id).encode().as_slice()).into()
}
+
+ pub(crate) fn dispatch_call(
+ origin: ProgramId,
+ call: CallOf,
+ gas_limit: u64,
+ ) -> (Result<(), BuiltinActorError>, u64)
+ where
+ T::AccountId: Origin,
+ {
+ let call_info = call.get_dispatch_info();
+
+ // Necessary upfront gas sufficiency check
+ if gas_limit < call_info.weight.ref_time() {
+ return (Err(BuiltinActorError::InsufficientGas), 0_u64);
+ }
+
+ // Execute call
+ let res = call.dispatch(frame_system::RawOrigin::Signed(origin.cast()).into());
+ let actual_gas = extract_actual_weight(&res, &call_info).ref_time();
+
+ match res {
+ Ok(_post_info) => {
+ log::debug!(
+ target: LOG_TARGET,
+ "Call dispatched successfully",
+ );
+ (Ok(()), actual_gas)
+ }
+ Err(e) => {
+ log::debug!(target: LOG_TARGET, "Error dispatching call: {:?}", e);
+ (
+ Err(BuiltinActorError::Custom(LimitedStr::from_small_str(
+ e.into(),
+ ))),
+ actual_gas,
+ )
+ }
+ }
+ }
}
}
diff --git a/pallets/gear-builtin/src/mock.rs b/pallets/gear-builtin/src/mock.rs
index 98ce908b8b7..77d22b0809b 100644
--- a/pallets/gear-builtin/src/mock.rs
+++ b/pallets/gear-builtin/src/mock.rs
@@ -16,23 +16,27 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-use crate::{self as pallet_gear_builtin, bls12_381, ActorWithId, BuiltinActor, BuiltinActorError};
+use crate::{
+ self as pallet_gear_builtin, bls12_381, proxy, ActorWithId, BuiltinActor, BuiltinActorError,
+};
use common::{GasProvider, GasTree};
use core::cell::RefCell;
use frame_support::{
construct_runtime, parameter_types,
- traits::{ConstBool, ConstU32, ConstU64, FindAuthor, OnFinalize, OnInitialize},
+ traits::{ConstBool, ConstU32, ConstU64, FindAuthor, InstanceFilter, OnFinalize, OnInitialize},
};
use frame_support_test::TestRandomness;
use frame_system::{self as system, pallet_prelude::BlockNumberFor};
+use gbuiltin_proxy::ProxyType as BuiltinProxyType;
use gear_core::{
ids::ProgramId,
message::{Payload, StoredDispatch},
};
+use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use sp_core::H256;
use sp_runtime::{
traits::{BlakeTwo256, IdentityLookup},
- BuildStorage, Perbill, Permill,
+ BuildStorage, Perbill, Permill, RuntimeDebug,
};
use sp_std::convert::{TryFrom, TryInto};
@@ -79,6 +83,7 @@ construct_runtime!(
Authorship: pallet_authorship,
Timestamp: pallet_timestamp,
Staking: pallet_staking,
+ Proxy: pallet_proxy,
GearProgram: pallet_gear_program,
GearMessenger: pallet_gear_messenger,
GearScheduler: pallet_gear_scheduler,
@@ -94,12 +99,105 @@ parameter_types! {
pub const ExistentialDeposit: Balance = EXISTENTIAL_DEPOSIT;
}
+#[derive(
+ Copy,
+ Clone,
+ Eq,
+ PartialEq,
+ Ord,
+ PartialOrd,
+ Encode,
+ Decode,
+ RuntimeDebug,
+ MaxEncodedLen,
+ scale_info::TypeInfo,
+)]
+pub enum ProxyType {
+ Any,
+ NonTransfer,
+ Governance,
+ Staking,
+ IdentityJudgement,
+ CancelProxy,
+}
+
+impl Default for ProxyType {
+ fn default() -> Self {
+ Self::Any
+ }
+}
+
+impl From for ProxyType {
+ fn from(proxy_type: BuiltinProxyType) -> Self {
+ match proxy_type {
+ BuiltinProxyType::Any => ProxyType::Any,
+ BuiltinProxyType::NonTransfer => ProxyType::NonTransfer,
+ BuiltinProxyType::Governance => ProxyType::Governance,
+ BuiltinProxyType::Staking => ProxyType::Staking,
+ BuiltinProxyType::IdentityJudgement => ProxyType::IdentityJudgement,
+ BuiltinProxyType::CancelProxy => ProxyType::CancelProxy,
+ }
+ }
+}
+
+impl InstanceFilter for ProxyType {
+ fn filter(&self, c: &RuntimeCall) -> bool {
+ match self {
+ ProxyType::Any => true,
+ ProxyType::NonTransfer => !matches!(c, RuntimeCall::Balances(..)),
+ ProxyType::CancelProxy => {
+ matches!(
+ c,
+ RuntimeCall::Proxy(pallet_proxy::Call::reject_announcement { .. })
+ )
+ }
+ ProxyType::Staking => matches!(c, RuntimeCall::Staking(..)),
+ ProxyType::Governance | ProxyType::IdentityJudgement => {
+ unimplemented!("No pallets defined in test runtime")
+ }
+ }
+ }
+ fn is_superset(&self, o: &Self) -> bool {
+ match (self, o) {
+ (x, y) if x == y => true,
+ (ProxyType::Any, _) => true,
+ (_, ProxyType::Any) => false,
+ (ProxyType::NonTransfer, _) => true,
+ _ => false,
+ }
+ }
+}
+
common::impl_pallet_system!(Test);
common::impl_pallet_balances!(Test);
common::impl_pallet_authorship!(Test);
common::impl_pallet_timestamp!(Test);
common::impl_pallet_staking!(Test);
+parameter_types! {
+ pub const ProxyDepositBase: Balance = 1;
+ pub const ProxyDepositFactor: Balance = 1;
+ pub const MaxProxies: u32 = 100;
+ pub const MaxPending: u32 = 100;
+ pub const AnnouncementDepositBase: Balance = 1;
+ pub const AnnouncementDepositFactor: Balance = 1;
+}
+
+impl pallet_proxy::Config for Test {
+ type RuntimeEvent = RuntimeEvent;
+ type RuntimeCall = RuntimeCall;
+ type Currency = Balances;
+ type ProxyType = ProxyType;
+ type ProxyDepositBase = ProxyDepositBase;
+ type ProxyDepositFactor = ProxyDepositFactor;
+ type MaxProxies = MaxProxies;
+ type WeightInfo = ();
+ type MaxPending = MaxPending;
+ type CallHasher = BlakeTwo256;
+ type AnnouncementDepositBase = AnnouncementDepositBase;
+ type AnnouncementDepositFactor = AnnouncementDepositBase;
+}
+
parameter_types! {
pub const BlockGasLimit: u64 = 100_000_000_000;
pub const OutgoingLimit: u32 = 1024;
@@ -222,6 +320,7 @@ impl pallet_gear_builtin::Config for Test {
ActorWithId,
ActorWithId,
ActorWithId<1, bls12_381::Actor>,
+ ActorWithId<3, proxy::Actor>,
);
type WeightInfo = ();
}
diff --git a/pallets/gear-builtin/src/proxy.rs b/pallets/gear-builtin/src/proxy.rs
new file mode 100644
index 00000000000..0a68ca2c5c2
--- /dev/null
+++ b/pallets/gear-builtin/src/proxy.rs
@@ -0,0 +1,95 @@
+// This file is part of Gear.
+
+// Copyright (C) 2024 Gear Technologies Inc.
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+//! Proxy builtin actor implementation.
+
+use super::*;
+use common::Origin;
+use gbuiltin_proxy::{ProxyType as BuiltinProxyType, Request};
+use pallet_proxy::Config as ProxyConfig;
+use sp_runtime::traits::StaticLookup;
+
+/// Proxy builtin actor.
+pub struct Actor(PhantomData);
+
+impl Actor
+where
+ T::AccountId: Origin,
+ ::ProxyType: From,
+ CallOf: From>,
+{
+ /// Casts received request to a runtime call.
+ fn cast(request: Request) -> Result, BuiltinActorError> {
+ Ok(match request {
+ Request::AddProxy {
+ delegate,
+ proxy_type,
+ } => {
+ let delegate = T::Lookup::unlookup(delegate.cast());
+ let proxy_type = proxy_type.into();
+ let delay = 0u32.into();
+
+ pallet_proxy::Call::::add_proxy {
+ delegate,
+ proxy_type,
+ delay,
+ }
+ }
+ Request::RemoveProxy {
+ delegate,
+ proxy_type,
+ } => {
+ let delegate = T::Lookup::unlookup(delegate.cast());
+ let proxy_type = proxy_type.into();
+ let delay = 0u32.into();
+
+ pallet_proxy::Call::::remove_proxy {
+ delegate,
+ proxy_type,
+ delay,
+ }
+ }
+ }
+ .into())
+ }
+}
+
+impl BuiltinActor for Actor
+where
+ T::AccountId: Origin,
+ ::ProxyType: From,
+ CallOf: From>,
+{
+ type Error = BuiltinActorError;
+
+ fn handle(dispatch: &StoredDispatch, gas_limit: u64) -> (Result, u64) {
+ let Ok(request) = Request::decode(&mut dispatch.payload_bytes()) else {
+ return (Err(BuiltinActorError::DecodingError), 0);
+ };
+
+ let origin = dispatch.source();
+
+ match Self::cast(request) {
+ Ok(call) => {
+ let (result, actual_gas) = Pallet::::dispatch_call(origin, call, gas_limit);
+ (result.map(|_| Default::default()), actual_gas)
+ }
+ Err(e) => (Err(e), gas_limit),
+ }
+ }
+}
diff --git a/pallets/gear-builtin/src/staking.rs b/pallets/gear-builtin/src/staking.rs
index d6900bb5f68..a5a24370cd5 100644
--- a/pallets/gear-builtin/src/staking.rs
+++ b/pallets/gear-builtin/src/staking.rs
@@ -21,13 +21,10 @@
use super::*;
use common::Origin;
use core::marker::PhantomData;
-use frame_support::dispatch::{extract_actual_weight, GetDispatchInfo};
use gbuiltin_staking::*;
use pallet_staking::{Config as StakingConfig, NominationsQuota, RewardDestination};
use parity_scale_codec::Decode;
-use sp_runtime::traits::{Dispatchable, StaticLookup, UniqueSaturatedInto};
-
-type CallOf = ::RuntimeCall;
+use sp_runtime::traits::{StaticLookup, UniqueSaturatedInto};
pub struct Actor(PhantomData);
@@ -36,41 +33,6 @@ where
T::AccountId: Origin,
CallOf: From>,
{
- fn dispatch_call(
- origin: T::AccountId,
- call: CallOf,
- gas_limit: u64,
- ) -> (Result<(), BuiltinActorError>, u64) {
- let call_info = call.get_dispatch_info();
-
- // Necessary upfront gas sufficiency check
- if gas_limit < call_info.weight.ref_time() {
- return (Err(BuiltinActorError::InsufficientGas), 0_u64);
- }
-
- // Execute call
- let res = call.dispatch(frame_system::RawOrigin::Signed(origin).into());
- let actual_gas = extract_actual_weight(&res, &call_info).ref_time();
- match res {
- Ok(_post_info) => {
- log::debug!(
- target: LOG_TARGET,
- "Call dispatched successfully",
- );
- (Ok(()), actual_gas)
- }
- Err(e) => {
- log::debug!(target: LOG_TARGET, "Error dispatching call: {:?}", e);
- (
- Err(BuiltinActorError::Custom(LimitedStr::from_small_str(
- e.into(),
- ))),
- actual_gas,
- )
- }
- }
- }
-
fn cast(request: Request) -> CallOf {
match request {
Request::Bond { value, payee } => {
@@ -146,7 +108,7 @@ where
fn handle(dispatch: &StoredDispatch, gas_limit: u64) -> (Result, u64) {
let message = dispatch.message();
- let origin: T::AccountId = dispatch.source().cast();
+ let origin = dispatch.source();
let mut payload = message.payload_bytes();
// Rule out payloads that exceed the largest reasonable size.
@@ -169,7 +131,7 @@ where
// Handle staking requests
let call = Self::cast(request);
- let (result, gas_spent) = Self::dispatch_call(origin, call, gas_limit);
+ let (result, gas_spent) = Pallet::::dispatch_call(origin, call, gas_limit);
(result.map(|_| Default::default()), gas_spent)
}
diff --git a/pallets/gear-builtin/src/tests/mod.rs b/pallets/gear-builtin/src/tests/mod.rs
index d071cfd12b6..99ab57e66a4 100644
--- a/pallets/gear-builtin/src/tests/mod.rs
+++ b/pallets/gear-builtin/src/tests/mod.rs
@@ -18,7 +18,8 @@
//! Builtin actor pallet tests.
-pub mod bad_builtin_ids;
-pub mod basic;
-pub mod bls381;
-pub mod staking;
+mod bad_builtin_ids;
+mod basic;
+mod bls381;
+mod proxy;
+mod staking;
diff --git a/pallets/gear-builtin/src/tests/proxy.rs b/pallets/gear-builtin/src/tests/proxy.rs
new file mode 100644
index 00000000000..d693d9b902c
--- /dev/null
+++ b/pallets/gear-builtin/src/tests/proxy.rs
@@ -0,0 +1,160 @@
+// This file is part of Gear.
+
+// Copyright (C) 2021-2024 Gear Technologies Inc.
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+//! Proxy builtin tests.
+
+use super::basic::init_logger;
+use crate::mock::*;
+use common::Origin;
+use demo_proxy_broker::WASM_BINARY;
+use frame_support::{assert_err, assert_ok};
+use gbuiltin_proxy::{ProxyType, Request};
+use gear_core::ids::{prelude::*, CodeId, ProgramId};
+use pallet_balances::Call as BalancesCall;
+use pallet_proxy::{Error as ProxyError, Event as ProxyEvent};
+use parity_scale_codec::Encode;
+
+#[test]
+fn add_remove_proxy_works() {
+ init_logger();
+ new_test_ext().execute_with(|| {
+ let proxy_pid = utils::upload_and_initialize_broker();
+
+ // Add proxy
+ let add_proxy_req = Request::AddProxy {
+ delegate: SIGNER.cast(),
+ proxy_type: ProxyType::Any,
+ };
+ utils::send_proxy_request(proxy_pid, add_proxy_req);
+
+ System::assert_has_event(RuntimeEvent::Proxy(ProxyEvent::ProxyAdded {
+ delegator: proxy_pid.cast(),
+ delegatee: SIGNER,
+ proxy_type: ProxyType::Any.into(),
+ delay: 0,
+ }));
+
+ System::reset_events();
+
+ // Remove proxy
+ let remove_proxy_req = Request::RemoveProxy {
+ delegate: SIGNER.cast(),
+ proxy_type: ProxyType::Any,
+ };
+ utils::send_proxy_request(proxy_pid, remove_proxy_req);
+
+ System::assert_has_event(RuntimeEvent::Proxy(ProxyEvent::ProxyRemoved {
+ delegator: proxy_pid.cast(),
+ delegatee: SIGNER,
+ proxy_type: ProxyType::Any.into(),
+ delay: 0,
+ }));
+
+ // Execute proxy
+ let dest = 42;
+ let value = EXISTENTIAL_DEPOSIT * 3;
+ let call = RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest, value });
+
+ assert_err!(
+ Proxy::proxy(
+ RuntimeOrigin::signed(SIGNER),
+ proxy_pid.cast(),
+ None,
+ Box::new(call)
+ ),
+ ProxyError::::NotProxy,
+ );
+ })
+}
+
+#[test]
+fn add_execute_proxy_works() {
+ init_logger();
+ new_test_ext().execute_with(|| {
+ let proxy_pid = utils::upload_and_initialize_broker();
+
+ // Add proxy
+ let add_proxy_req = Request::AddProxy {
+ delegate: SIGNER.cast(),
+ proxy_type: ProxyType::Any,
+ };
+ utils::send_proxy_request(proxy_pid, add_proxy_req);
+
+ System::assert_has_event(RuntimeEvent::Proxy(ProxyEvent::ProxyAdded {
+ delegator: proxy_pid.cast(),
+ delegatee: SIGNER,
+ proxy_type: ProxyType::Any.into(),
+ delay: 0,
+ }));
+
+ // Execute proxy
+ let dest = 42;
+ let value = EXISTENTIAL_DEPOSIT * 3;
+ let call = RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest, value });
+
+ assert_ok!(Proxy::proxy(
+ RuntimeOrigin::signed(SIGNER),
+ proxy_pid.cast(),
+ None,
+ Box::new(call)
+ ));
+ assert_eq!(Balances::free_balance(dest), value);
+ })
+}
+
+mod utils {
+ use super::*;
+
+ pub(super) fn upload_and_initialize_broker() -> ProgramId {
+ let code = WASM_BINARY;
+ let salt = b"proxy_broker";
+ let pid = ProgramId::generate_from_user(CodeId::generate(code), salt);
+ assert_ok!(Gear::upload_program(
+ RuntimeOrigin::signed(SIGNER),
+ code.to_vec(),
+ salt.to_vec(),
+ Default::default(),
+ 10_000_000_000,
+ 0,
+ false,
+ ));
+ run_to_next_block();
+
+ // Top-up balance of the proxy so it can pay adding proxy deposit
+ assert_ok!(>::transfer(
+ &1,
+ &pid.cast(),
+ 10 * EXISTENTIAL_DEPOSIT,
+ frame_support::traits::ExistenceRequirement::AllowDeath
+ ));
+
+ pid
+ }
+
+ pub(super) fn send_proxy_request(proxy_pid: ProgramId, req: Request) {
+ assert_ok!(Gear::send_message(
+ RuntimeOrigin::signed(SIGNER),
+ proxy_pid,
+ req.encode(),
+ 10_000_000_000,
+ 0,
+ false,
+ ));
+ run_to_next_block();
+ }
+}
diff --git a/runtime/vara/Cargo.toml b/runtime/vara/Cargo.toml
index 512d3bc73c0..fc902592b4c 100644
--- a/runtime/vara/Cargo.toml
+++ b/runtime/vara/Cargo.toml
@@ -106,6 +106,7 @@ pallet-gear-builtin-rpc-runtime-api.workspace = true
pallet-gear-eth-bridge-rpc-runtime-api.workspace = true
runtime-primitives.workspace = true
gbuiltin-staking.workspace = true
+gbuiltin-proxy.workspace = true
[dev-dependencies]
sp-io.workspace = true
diff --git a/runtime/vara/src/lib.rs b/runtime/vara/src/lib.rs
index 1ffec3ed121..0d950b7a5f8 100644
--- a/runtime/vara/src/lib.rs
+++ b/runtime/vara/src/lib.rs
@@ -65,6 +65,7 @@ use frame_system::{
limits::{BlockLength, BlockWeights},
EnsureRoot,
};
+use gbuiltin_proxy::ProxyType as BuiltinProxyType;
use pallet_election_provider_multi_phase::{GeometricDepositBase, SolutionAccuracyOf};
pub use pallet_gear::manager::{ExtManager, HandleKind};
use pallet_gear_builtin::ActorWithId;
@@ -1003,6 +1004,19 @@ impl Default for ProxyType {
}
}
+impl From for ProxyType {
+ fn from(proxy_type: BuiltinProxyType) -> Self {
+ match proxy_type {
+ BuiltinProxyType::Any => ProxyType::Any,
+ BuiltinProxyType::NonTransfer => ProxyType::NonTransfer,
+ BuiltinProxyType::Governance => ProxyType::Governance,
+ BuiltinProxyType::Staking => ProxyType::Staking,
+ BuiltinProxyType::IdentityJudgement => ProxyType::IdentityJudgement,
+ BuiltinProxyType::CancelProxy => ProxyType::CancelProxy,
+ }
+ }
+}
+
impl InstanceFilter for ProxyType {
fn filter(&self, c: &RuntimeCall) -> bool {
match self {
@@ -1180,6 +1194,8 @@ impl pallet_gear_messenger::Config for Runtime {
pub type BuiltinActors = (
ActorWithId<1, pallet_gear_builtin::bls12_381::Actor>,
ActorWithId<2, pallet_gear_builtin::staking::Actor>,
+ // The ID = 3 is for the pallet_gear_eth_bridge::Actor.
+ ActorWithId<4, pallet_gear_builtin::proxy::Actor>,
);
/// Builtin actors arranged in a tuple.
@@ -1188,6 +1204,7 @@ pub type BuiltinActors = (
ActorWithId<1, pallet_gear_builtin::bls12_381::Actor>,
ActorWithId<2, pallet_gear_builtin::staking::Actor>,
ActorWithId<3, pallet_gear_eth_bridge::Actor>,
+ ActorWithId<4, pallet_gear_builtin::proxy::Actor>,
);
impl pallet_gear_builtin::Config for Runtime {