Skip to content
This repository was archived by the owner on Nov 1, 2023. It is now read-only.

Commit 1172cef

Browse files
atheiHCastano
authored andcommitted
contracts: Allow ChainExtension::call() to access &mut self (paritytech#11874)
* Give chain extensions the ability to store some temporary values * Update frame/contracts/src/wasm/runtime.rs Co-authored-by: Hernando Castano <[email protected]> * Rename func_id -> id * Replace `id` param by two functions on `env` Co-authored-by: Hernando Castano <[email protected]>
1 parent d91313b commit 1172cef

6 files changed

Lines changed: 254 additions & 56 deletions

File tree

frame/contracts/fixtures/chain_extension.wat

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,14 @@
3131

3232
;; the chain extension passes through the input and returns it as output
3333
(call $seal_call_chain_extension
34-
(i32.load (i32.const 4)) ;; func_id
34+
(i32.load (i32.const 4)) ;; id
3535
(i32.const 4) ;; input_ptr
3636
(i32.load (i32.const 0)) ;; input_len
3737
(i32.const 16) ;; output_ptr
3838
(i32.const 12) ;; output_len_ptr
3939
)
4040

41-
;; the chain extension passes through the func_id
41+
;; the chain extension passes through the id
4242
(call $assert (i32.eq (i32.load (i32.const 4))))
4343

4444
(call $seal_return (i32.const 0) (i32.const 16) (i32.load (i32.const 12)))
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
;; Call chain extension two times with the specified func_ids
2+
;; It then calls itself once
3+
(module
4+
(import "seal0" "seal_call_chain_extension"
5+
(func $seal_call_chain_extension (param i32 i32 i32 i32 i32) (result i32))
6+
)
7+
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
8+
(import "seal0" "seal_address" (func $seal_address (param i32 i32)))
9+
(import "seal1" "seal_call" (func $seal_call (param i32 i32 i64 i32 i32 i32 i32 i32) (result i32)))
10+
(import "env" "memory" (memory 16 16))
11+
12+
(func $assert (param i32)
13+
(block $ok
14+
(br_if $ok (get_local 0))
15+
(unreachable)
16+
)
17+
)
18+
19+
;; [0, 4) len of input buffer: 8 byte (func_ids) + 1byte (stop_recurse)
20+
(data (i32.const 0) "\09")
21+
22+
;; [4, 16) buffer for input
23+
24+
;; [16, 48] buffer for self address
25+
26+
;; [48, 52] len of self address buffer
27+
(data (i32.const 48) "\20")
28+
29+
(func (export "deploy"))
30+
31+
(func (export "call")
32+
;; input: (func_id1: i32, func_id2: i32, stop_recurse: i8)
33+
(call $seal_input (i32.const 4) (i32.const 0))
34+
35+
(call $seal_call_chain_extension
36+
(i32.load (i32.const 4)) ;; id
37+
(i32.const 0) ;; input_ptr
38+
(i32.const 0) ;; input_len
39+
(i32.const 0xffffffff) ;; u32 max sentinel value: do not copy output
40+
(i32.const 0) ;; output_len_ptr
41+
)
42+
drop
43+
44+
(call $seal_call_chain_extension
45+
(i32.load (i32.const 8)) ;; _id
46+
(i32.const 0) ;; input_ptr
47+
(i32.const 0) ;; input_len
48+
(i32.const 0xffffffff) ;; u32 max sentinel value: do not copy output
49+
(i32.const 0) ;; output_len_ptr
50+
)
51+
drop
52+
53+
(if (i32.eqz (i32.load8_u (i32.const 12)))
54+
(then
55+
;; stop recursion
56+
(i32.store8 (i32.const 12) (i32.const 1))
57+
58+
;; load own address into buffer
59+
(call $seal_address (i32.const 16) (i32.const 48))
60+
61+
;; call function 2 + 3 of chainext 3 next time
62+
;; (3 << 16) | 2
63+
;; (3 << 16) | 3
64+
(i32.store (i32.const 4) (i32.const 196610))
65+
(i32.store (i32.const 8) (i32.const 196611))
66+
67+
;; call self
68+
(call $seal_call
69+
(i32.const 8) ;; Set ALLOW_REENTRY
70+
(i32.const 16) ;; Pointer to "callee" address.
71+
(i64.const 0) ;; How much gas to devote for the execution. 0 = all.
72+
(i32.const 512) ;; Pointer to the buffer with value to transfer
73+
(i32.const 4) ;; Pointer to input data buffer address
74+
(i32.load (i32.const 0)) ;; Length of input data buffer
75+
(i32.const 4294967295) ;; u32 max value is the sentinel value: do not copy output
76+
(i32.const 0) ;; Length is ignored in this case
77+
)
78+
79+
;; check that call succeeded of call
80+
(call $assert (i32.eqz))
81+
)
82+
(else)
83+
)
84+
)
85+
)

frame/contracts/src/chain_extension.rs

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
//!
3434
//! Often there is a need for having multiple chain extensions. This is often the case when
3535
//! some generally useful off-the-shelf extensions should be included. To have multiple chain
36-
//! extensions they can be put into a tuple which is then passed to `[Config::ChainExtension]` like
36+
//! extensions they can be put into a tuple which is then passed to [`Config::ChainExtension`] like
3737
//! this `type Extensions = (ExtensionA, ExtensionB)`.
3838
//!
3939
//! However, only extensions implementing [`RegisteredChainExtension`] can be put into a tuple.
@@ -94,6 +94,12 @@ pub type Result<T> = sp_std::result::Result<T, DispatchError>;
9494
/// In order to create a custom chain extension this trait must be implemented and supplied
9595
/// to the pallet contracts configuration trait as the associated type of the same name.
9696
/// Consult the [module documentation](self) for a general explanation of chain extensions.
97+
///
98+
/// # Lifetime
99+
///
100+
/// The extension will be [`Default`] initialized at the beginning of each call
101+
/// (**not** per call stack) and dropped afterwards. Hence any value held inside the extension
102+
/// can be used as a per-call scratch buffer.
97103
pub trait ChainExtension<C: Config> {
98104
/// Call the chain extension logic.
99105
///
@@ -102,16 +108,14 @@ pub trait ChainExtension<C: Config> {
102108
/// imported wasm function.
103109
///
104110
/// # Parameters
105-
/// - `func_id`: The first argument to `seal_call_chain_extension`. Usually used to determine
106-
/// which function to realize.
107111
/// - `env`: Access to the remaining arguments and the execution environment.
108112
///
109113
/// # Return
110114
///
111115
/// In case of `Err` the contract execution is immediately suspended and the passed error
112116
/// is returned to the caller. Otherwise the value of [`RetVal`] determines the exit
113117
/// behaviour.
114-
fn call<E>(func_id: u32, env: Environment<E, InitState>) -> Result<RetVal>
118+
fn call<E>(&mut self, env: Environment<E, InitState>) -> Result<RetVal>
115119
where
116120
E: Ext<T = C>,
117121
<E::T as SysConfig>::AccountId: UncheckedFrom<<E::T as SysConfig>::Hash> + AsRef<[u8]>;
@@ -132,7 +136,7 @@ pub trait ChainExtension<C: Config> {
132136
///
133137
/// An extension that implements this trait can be put in a tuple in order to have multiple
134138
/// extensions available. The tuple implementation routes requests based on the first two
135-
/// most significant bytes of the `func_id` passed to `call`.
139+
/// most significant bytes of the `id` passed to `call`.
136140
///
137141
/// If this extensions is to be used by multiple runtimes consider
138142
/// [registering it](https://github.com/paritytech/chainextension-registry) to ensure that there
@@ -150,15 +154,15 @@ pub trait RegisteredChainExtension<C: Config>: ChainExtension<C> {
150154
#[impl_trait_for_tuples::impl_for_tuples(10)]
151155
#[tuple_types_custom_trait_bound(RegisteredChainExtension<C>)]
152156
impl<C: Config> ChainExtension<C> for Tuple {
153-
fn call<E>(func_id: u32, mut env: Environment<E, InitState>) -> Result<RetVal>
157+
fn call<E>(&mut self, mut env: Environment<E, InitState>) -> Result<RetVal>
154158
where
155159
E: Ext<T = C>,
156160
<E::T as SysConfig>::AccountId: UncheckedFrom<<E::T as SysConfig>::Hash> + AsRef<[u8]>,
157161
{
158162
for_tuples!(
159163
#(
160-
if (Tuple::ID == (func_id >> 16) as u16) && Tuple::enabled() {
161-
return Tuple::call(func_id, env);
164+
if (Tuple::ID == env.ext_id()) && Tuple::enabled() {
165+
return Tuple.call(env);
162166
}
163167
)*
164168
);
@@ -206,6 +210,22 @@ impl<'a, 'b, E: Ext, S: state::State> Environment<'a, 'b, E, S>
206210
where
207211
<E::T as SysConfig>::AccountId: UncheckedFrom<<E::T as SysConfig>::Hash> + AsRef<[u8]>,
208212
{
213+
/// The function id within the `id` passed by a contract.
214+
///
215+
/// It returns the two least significant bytes of the `id` passed by a contract as the other
216+
/// two bytes represent the chain extension itself (the code which is calling this function).
217+
pub fn func_id(&self) -> u16 {
218+
(self.inner.id & 0x00FF) as u16
219+
}
220+
221+
/// The chain extension id within the `id` passed by a contract.
222+
///
223+
/// It returns the two most significant bytes of the `id` passed by a contract which represent
224+
/// the chain extension itself (the code which is calling this function).
225+
pub fn ext_id(&self) -> u16 {
226+
(self.inner.id >> 16) as u16
227+
}
228+
209229
/// Charge the passed `amount` of weight from the overall limit.
210230
///
211231
/// It returns `Ok` when there the remaining weight budget is larger than the passed
@@ -251,13 +271,14 @@ impl<'a, 'b, E: Ext> Environment<'a, 'b, E, state::Init> {
251271
/// ever create this type. Chain extensions merely consume it.
252272
pub(crate) fn new(
253273
runtime: &'a mut Runtime<'b, E>,
274+
id: u32,
254275
input_ptr: u32,
255276
input_len: u32,
256277
output_ptr: u32,
257278
output_len_ptr: u32,
258279
) -> Self {
259280
Environment {
260-
inner: Inner { runtime, input_ptr, input_len, output_ptr, output_len_ptr },
281+
inner: Inner { runtime, id, input_ptr, input_len, output_ptr, output_len_ptr },
261282
phantom: PhantomData,
262283
}
263284
}
@@ -406,6 +427,8 @@ struct Inner<'a, 'b, E: Ext> {
406427
/// The runtime contains all necessary functions to interact with the running contract.
407428
runtime: &'a mut Runtime<'b, E>,
408429
/// Verbatim argument passed to `seal_call_chain_extension`.
430+
id: u32,
431+
/// Verbatim argument passed to `seal_call_chain_extension`.
409432
input_ptr: u32,
410433
/// Verbatim argument passed to `seal_call_chain_extension`.
411434
input_len: u32,

frame/contracts/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ pub mod pallet {
280280
type WeightInfo: WeightInfo;
281281

282282
/// Type that allows the runtime authors to add new host functions for a contract to call.
283-
type ChainExtension: chain_extension::ChainExtension<Self>;
283+
type ChainExtension: chain_extension::ChainExtension<Self> + Default;
284284

285285
/// Cost schedule and limits.
286286
#[pallet::constant]

0 commit comments

Comments
 (0)