mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 22:11:06 +00:00
contracts: Add seal_rent_status (#8780)
* Move public functions up in rent.rs * Added RentStatus * Fix test name for consistency Co-authored-by: Michael Müller <michi@parity.io> * Mark rent functions as unstable * Add unstable interfaces to README * Fix doc typos Co-authored-by: Andrew Jones <ascjones@gmail.com> * Use DefaultNoBound * Simplify calc_share(1) * Don't output empty debug messages * Make `seal_debug_message` unstable Co-authored-by: Michael Müller <michi@parity.io> Co-authored-by: Andrew Jones <ascjones@gmail.com>
This commit is contained in:
committed by
GitHub
parent
f29a6fdad3
commit
0057c0b53f
@@ -50,7 +50,12 @@ macro_rules! gen_signature_dispatch {
|
||||
$name:ident
|
||||
( $ctx:ident $( , $names:ident : $params:ty )* ) $( -> $returns:ty )* , $($rest:tt)*
|
||||
) => {
|
||||
if stringify!($module).as_bytes() == $needle_module && stringify!($name).as_bytes() == $needle_name {
|
||||
let module = stringify!($module).as_bytes();
|
||||
#[cfg(not(feature = "unstable-interface"))]
|
||||
if module == b"__unstable__" {
|
||||
return false;
|
||||
}
|
||||
if module == $needle_module && stringify!($name).as_bytes() == $needle_name {
|
||||
let signature = gen_signature!( ( $( $params ),* ) $( -> $returns )* );
|
||||
if $needle_sig == &signature {
|
||||
return true;
|
||||
@@ -127,8 +132,8 @@ macro_rules! unmarshall_then_body_then_marshall {
|
||||
}
|
||||
|
||||
macro_rules! define_func {
|
||||
( < E: $seal_ty:tt > $name:ident ( $ctx: ident $(, $names:ident : $params:ty)*) $(-> $returns:ty)* => $body:tt ) => {
|
||||
fn $name< E: $seal_ty >(
|
||||
( $trait:tt $name:ident ( $ctx: ident $(, $names:ident : $params:ty)*) $(-> $returns:ty)* => $body:tt ) => {
|
||||
fn $name< E: $trait >(
|
||||
$ctx: &mut $crate::wasm::Runtime<E>,
|
||||
args: &[sp_sandbox::Value],
|
||||
) -> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError>
|
||||
@@ -149,24 +154,52 @@ macro_rules! define_func {
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! register_func {
|
||||
( $reg_cb:ident, < E: $seal_ty:tt > ; ) => {};
|
||||
|
||||
( $reg_cb:ident, < E: $seal_ty:tt > ;
|
||||
macro_rules! register_body {
|
||||
( $reg_cb:ident, $trait:tt;
|
||||
$module:ident $name:ident ( $ctx:ident $( , $names:ident : $params:ty )* )
|
||||
$( -> $returns:ty )* => $body:tt $($rest:tt)*
|
||||
$( -> $returns:ty )* => $body:tt
|
||||
) => {
|
||||
$reg_cb(
|
||||
stringify!($module).as_bytes(),
|
||||
stringify!($name).as_bytes(),
|
||||
{
|
||||
define_func!(
|
||||
< E: $seal_ty > $name ( $ctx $(, $names : $params )* ) $( -> $returns )* => $body
|
||||
$trait $name ( $ctx $(, $names : $params )* ) $( -> $returns )* => $body
|
||||
);
|
||||
$name::<E>
|
||||
}
|
||||
);
|
||||
register_func!( $reg_cb, < E: $seal_ty > ; $($rest)* );
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! register_func {
|
||||
( $reg_cb:ident, $trait:tt; ) => {};
|
||||
|
||||
( $reg_cb:ident, $trait:tt;
|
||||
__unstable__ $name:ident ( $ctx:ident $( , $names:ident : $params:ty )* )
|
||||
$( -> $returns:ty )* => $body:tt $($rest:tt)*
|
||||
) => {
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
register_body!(
|
||||
$reg_cb, $trait;
|
||||
__unstable__ $name
|
||||
( $ctx $( , $names : $params )* )
|
||||
$( -> $returns )* => $body
|
||||
);
|
||||
register_func!( $reg_cb, $trait; $($rest)* );
|
||||
};
|
||||
|
||||
( $reg_cb:ident, $trait:tt;
|
||||
$module:ident $name:ident ( $ctx:ident $( , $names:ident : $params:ty )* )
|
||||
$( -> $returns:ty )* => $body:tt $($rest:tt)*
|
||||
) => {
|
||||
register_body!(
|
||||
$reg_cb, $trait;
|
||||
$module $name
|
||||
( $ctx $( , $names : $params )* )
|
||||
$( -> $returns )* => $body
|
||||
);
|
||||
register_func!( $reg_cb, $trait; $($rest)* );
|
||||
};
|
||||
}
|
||||
|
||||
@@ -178,7 +211,7 @@ macro_rules! register_func {
|
||||
/// It's up to the user of this macro to check signatures of wasm code to be executed
|
||||
/// and reject the code if any imported function has a mismatched signature.
|
||||
macro_rules! define_env {
|
||||
( $init_name:ident , < E: $seal_ty:tt > ,
|
||||
( $init_name:ident , < E: $trait:tt > ,
|
||||
$( [$module:ident] $name:ident ( $ctx:ident $( , $names:ident : $params:ty )* )
|
||||
$( -> $returns:ty )* => $body:tt , )*
|
||||
) => {
|
||||
@@ -204,7 +237,7 @@ macro_rules! define_env {
|
||||
fn impls<F: FnMut(&[u8], &[u8], $crate::wasm::env_def::HostFunc<E>)>(f: &mut F) {
|
||||
register_func!(
|
||||
f,
|
||||
< E: $seal_ty > ;
|
||||
$trait;
|
||||
$( $module $name ( $ctx $( , $names : $params )* ) $( -> $returns)* => $body )*
|
||||
);
|
||||
}
|
||||
@@ -285,7 +318,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn macro_define_func() {
|
||||
define_func!( <E: Ext> seal_gas (_ctx, amount: u32) => {
|
||||
define_func!( Ext seal_gas (_ctx, amount: u32) => {
|
||||
let amount = Weight::from(amount);
|
||||
if !amount.is_zero() {
|
||||
Ok(())
|
||||
|
||||
@@ -247,6 +247,7 @@ mod tests {
|
||||
RentParams, ExecError, ErrorOrigin,
|
||||
},
|
||||
gas::GasMeter,
|
||||
rent::RentStatus,
|
||||
tests::{Test, Call, ALICE, BOB},
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
@@ -452,6 +453,9 @@ mod tests {
|
||||
fn rent_params(&self) -> &RentParams<Self::T> {
|
||||
&self.rent_params
|
||||
}
|
||||
fn rent_status(&mut self, _at_refcount: u32) -> RentStatus<Self::T> {
|
||||
Default::default()
|
||||
}
|
||||
fn gas_meter(&mut self) -> &mut GasMeter<Self::T> {
|
||||
&mut self.gas_meter
|
||||
}
|
||||
@@ -1817,9 +1821,14 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
const CODE_RENT_PARAMS: &str = r#"
|
||||
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
fn rent_params_work() {
|
||||
const CODE_RENT_PARAMS: &str = r#"
|
||||
(module
|
||||
(import "seal0" "seal_rent_params" (func $seal_rent_params (param i32 i32)))
|
||||
(import "__unstable__" "seal_rent_params" (func $seal_rent_params (param i32 i32)))
|
||||
(import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
@@ -1846,9 +1855,6 @@ mod tests {
|
||||
(func (export "deploy"))
|
||||
)
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn rent_params_work() {
|
||||
let output = execute(
|
||||
CODE_RENT_PARAMS,
|
||||
vec![],
|
||||
@@ -1858,9 +1864,56 @@ mod tests {
|
||||
assert_eq!(output, ExecReturnValue { flags: ReturnFlags::empty(), data: rent_params });
|
||||
}
|
||||
|
||||
const CODE_DEBUG_MESSAGE: &str = r#"
|
||||
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
fn rent_status_works() {
|
||||
const CODE_RENT_STATUS: &str = r#"
|
||||
(module
|
||||
(import "seal0" "seal_debug_message" (func $seal_debug_message (param i32 i32) (result i32)))
|
||||
(import "__unstable__" "seal_rent_status" (func $seal_rent_status (param i32 i32 i32)))
|
||||
(import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
;; [0, 4) buffer size = 128 bytes
|
||||
(data (i32.const 0) "\80")
|
||||
|
||||
;; [4; inf) buffer where the result is copied
|
||||
|
||||
(func (export "call")
|
||||
;; Load the rent params into memory
|
||||
(call $seal_rent_status
|
||||
(i32.const 1) ;; at_refcount
|
||||
(i32.const 4) ;; Pointer to the output buffer
|
||||
(i32.const 0) ;; Pointer to the size of the buffer
|
||||
)
|
||||
|
||||
;; Return the contents of the buffer
|
||||
(call $seal_return
|
||||
(i32.const 0) ;; return flags
|
||||
(i32.const 4) ;; buffer pointer
|
||||
(i32.load (i32.const 0)) ;; buffer size
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "deploy"))
|
||||
)
|
||||
"#;
|
||||
let output = execute(
|
||||
CODE_RENT_STATUS,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
).unwrap();
|
||||
let rent_status = Bytes(<RentStatus<Test>>::default().encode());
|
||||
assert_eq!(output, ExecReturnValue { flags: ReturnFlags::empty(), data: rent_status });
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
fn debug_message_works() {
|
||||
const CODE_DEBUG_MESSAGE: &str = r#"
|
||||
(module
|
||||
(import "__unstable__" "seal_debug_message" (func $seal_debug_message (param i32 i32) (result i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
(data (i32.const 0) "Hello World!")
|
||||
@@ -1876,9 +1929,6 @@ mod tests {
|
||||
(func (export "deploy"))
|
||||
)
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn debug_message_works() {
|
||||
let mut ext = MockExt::default();
|
||||
execute(
|
||||
CODE_DEBUG_MESSAGE,
|
||||
@@ -1889,39 +1939,39 @@ mod tests {
|
||||
assert_eq!(std::str::from_utf8(&ext.debug_buffer).unwrap(), "Hello World!");
|
||||
}
|
||||
|
||||
const CODE_DEBUG_MESSAGE_FAIL: &str = r#"
|
||||
(module
|
||||
(import "seal0" "seal_debug_message" (func $seal_debug_message (param i32 i32) (result i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
#[test]
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
fn debug_message_invalid_utf8_fails() {
|
||||
const CODE_DEBUG_MESSAGE_FAIL: &str = r#"
|
||||
(module
|
||||
(import "__unstable__" "seal_debug_message" (func $seal_debug_message (param i32 i32) (result i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
(data (i32.const 0) "\fc")
|
||||
(data (i32.const 0) "\fc")
|
||||
|
||||
(func (export "call")
|
||||
(call $seal_debug_message
|
||||
(i32.const 0) ;; Pointer to the text buffer
|
||||
(i32.const 1) ;; The size of the buffer
|
||||
)
|
||||
drop
|
||||
(func (export "call")
|
||||
(call $seal_debug_message
|
||||
(i32.const 0) ;; Pointer to the text buffer
|
||||
(i32.const 1) ;; The size of the buffer
|
||||
)
|
||||
|
||||
(func (export "deploy"))
|
||||
drop
|
||||
)
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn debug_message_invalid_utf8_fails() {
|
||||
let mut ext = MockExt::default();
|
||||
let result = execute(
|
||||
CODE_DEBUG_MESSAGE_FAIL,
|
||||
vec![],
|
||||
&mut ext,
|
||||
);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(ExecError {
|
||||
error: Error::<Test>::DebugMessageInvalidUTF8.into(),
|
||||
origin: ErrorOrigin::Caller,
|
||||
})
|
||||
);
|
||||
}
|
||||
(func (export "deploy"))
|
||||
)
|
||||
"#;
|
||||
let mut ext = MockExt::default();
|
||||
let result = execute(
|
||||
CODE_DEBUG_MESSAGE_FAIL,
|
||||
vec![],
|
||||
&mut ext,
|
||||
);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(ExecError {
|
||||
error: Error::<Test>::DebugMessageInvalidUTF8.into(),
|
||||
origin: ErrorOrigin::Caller,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ pub enum ReturnCode {
|
||||
NotCallable = 8,
|
||||
/// The call to `seal_debug_message` had no effect because debug message
|
||||
/// recording was disabled.
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
LoggingDisabled = 9,
|
||||
}
|
||||
|
||||
@@ -179,6 +180,7 @@ pub enum RuntimeCosts {
|
||||
/// Weight of calling `seal_deposit_event` with the given number of topics and event size.
|
||||
DepositEvent{num_topic: u32, len: u32},
|
||||
/// Weight of calling `seal_debug_message`.
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
DebugMessage,
|
||||
/// Weight of calling `seal_set_rent_allowance`.
|
||||
SetRentAllowance,
|
||||
@@ -220,8 +222,6 @@ pub enum RuntimeCosts {
|
||||
ChainExtension(u64),
|
||||
/// Weight charged for copying data from the sandbox.
|
||||
CopyIn(u32),
|
||||
/// Weight of calling `seal_rent_params`.
|
||||
RentParams,
|
||||
}
|
||||
|
||||
impl RuntimeCosts {
|
||||
@@ -260,6 +260,7 @@ impl RuntimeCosts {
|
||||
DepositEvent{num_topic, len} => s.deposit_event
|
||||
.saturating_add(s.deposit_event_per_topic.saturating_mul(num_topic.into()))
|
||||
.saturating_add(s.deposit_event_per_byte.saturating_mul(len.into())),
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
DebugMessage => s.debug_message,
|
||||
SetRentAllowance => s.set_rent_allowance,
|
||||
SetStorage(len) => s.set_storage
|
||||
@@ -290,7 +291,6 @@ impl RuntimeCosts {
|
||||
.saturating_add(s.hash_blake2_128_per_byte.saturating_mul(len.into())),
|
||||
ChainExtension(amount) => amount,
|
||||
CopyIn(len) => s.return_per_byte.saturating_mul(len.into()),
|
||||
RentParams => s.rent_params,
|
||||
};
|
||||
RuntimeToken {
|
||||
#[cfg(test)]
|
||||
@@ -1390,35 +1390,6 @@ define_env!(Env, <E: Ext>,
|
||||
)?)
|
||||
},
|
||||
|
||||
// Emit a custom debug message.
|
||||
//
|
||||
// No newlines are added to the supplied message.
|
||||
// Specifying invalid UTF-8 triggers a trap.
|
||||
//
|
||||
// This is a no-op if debug message recording is disabled which is always the case
|
||||
// when the code is executing on-chain. The message is interpreted as UTF-8 and
|
||||
// appended to the debug buffer which is then supplied to the calling RPC client.
|
||||
//
|
||||
// # Note
|
||||
//
|
||||
// Even though no action is taken when debug message recording is disabled there is still
|
||||
// a non trivial overhead (and weight cost) associated with calling this function. Contract
|
||||
// languages should remove calls to this function (either at runtime or compile time) when
|
||||
// not being executed as an RPC. For example, they could allow users to disable logging
|
||||
// through compile time flags (cargo features) for on-chain deployment. Additionally, the
|
||||
// return value of this function can be cached in order to prevent further calls at runtime.
|
||||
[seal0] seal_debug_message(ctx, str_ptr: u32, str_len: u32) -> ReturnCode => {
|
||||
ctx.charge_gas(RuntimeCosts::DebugMessage)?;
|
||||
if ctx.ext.append_debug_buffer("") {
|
||||
let data = ctx.read_sandbox_memory(str_ptr, str_len)?;
|
||||
let msg = core::str::from_utf8(&data)
|
||||
.map_err(|_| <Error<E::T>>::DebugMessageInvalidUTF8)?;
|
||||
ctx.ext.append_debug_buffer(msg);
|
||||
return Ok(ReturnCode::Success);
|
||||
}
|
||||
Ok(ReturnCode::LoggingDisabled)
|
||||
},
|
||||
|
||||
// Stores the current block number of the current contract into the supplied buffer.
|
||||
//
|
||||
// The value is stored to linear memory at the address pointed to by `out_ptr`.
|
||||
@@ -1565,6 +1536,35 @@ define_env!(Env, <E: Ext>,
|
||||
}
|
||||
},
|
||||
|
||||
// Emit a custom debug message.
|
||||
//
|
||||
// No newlines are added to the supplied message.
|
||||
// Specifying invalid UTF-8 triggers a trap.
|
||||
//
|
||||
// This is a no-op if debug message recording is disabled which is always the case
|
||||
// when the code is executing on-chain. The message is interpreted as UTF-8 and
|
||||
// appended to the debug buffer which is then supplied to the calling RPC client.
|
||||
//
|
||||
// # Note
|
||||
//
|
||||
// Even though no action is taken when debug message recording is disabled there is still
|
||||
// a non trivial overhead (and weight cost) associated with calling this function. Contract
|
||||
// languages should remove calls to this function (either at runtime or compile time) when
|
||||
// not being executed as an RPC. For example, they could allow users to disable logging
|
||||
// through compile time flags (cargo features) for on-chain deployment. Additionally, the
|
||||
// return value of this function can be cached in order to prevent further calls at runtime.
|
||||
[__unstable__] seal_debug_message(ctx, str_ptr: u32, str_len: u32) -> ReturnCode => {
|
||||
ctx.charge_gas(RuntimeCosts::DebugMessage)?;
|
||||
if ctx.ext.append_debug_buffer("") {
|
||||
let data = ctx.read_sandbox_memory(str_ptr, str_len)?;
|
||||
let msg = core::str::from_utf8(&data)
|
||||
.map_err(|_| <Error<E::T>>::DebugMessageInvalidUTF8)?;
|
||||
ctx.ext.append_debug_buffer(msg);
|
||||
return Ok(ReturnCode::Success);
|
||||
}
|
||||
Ok(ReturnCode::LoggingDisabled)
|
||||
},
|
||||
|
||||
// Stores the rent params into the supplied buffer.
|
||||
//
|
||||
// The value is stored to linear memory at the address pointed to by `out_ptr`.
|
||||
@@ -1579,10 +1579,38 @@ define_env!(Env, <E: Ext>,
|
||||
// The returned information was collected and cached when the current contract call
|
||||
// started execution. Any change to those values that happens due to actions of the
|
||||
// current call or contracts that are called by this contract are not considered.
|
||||
[seal0] seal_rent_params(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
||||
ctx.charge_gas(RuntimeCosts::RentParams)?;
|
||||
//
|
||||
// # Unstable
|
||||
//
|
||||
// This function is unstable and subject to change (or removal) in the future. Do not
|
||||
// deploy a contract using it to a production chain.
|
||||
[__unstable__] seal_rent_params(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
||||
Ok(ctx.write_sandbox_output(
|
||||
out_ptr, out_len_ptr, &ctx.ext.rent_params().encode(), false, already_charged
|
||||
)?)
|
||||
},
|
||||
|
||||
// Stores the rent status into the supplied buffer.
|
||||
//
|
||||
// The value is stored to linear memory at the address pointed to by `out_ptr`.
|
||||
// `out_len_ptr` must point to a u32 value that describes the available space at
|
||||
// `out_ptr`. This call overwrites it with the size of the value. If the available
|
||||
// space at `out_ptr` is less than the size of the value a trap is triggered.
|
||||
//
|
||||
// The data is encoded as [`crate::rent::RentStatus`].
|
||||
//
|
||||
// # Parameters
|
||||
//
|
||||
// - `at_refcount`: The refcount assumed for the returned `custom_refcount_*` fields
|
||||
//
|
||||
// # Unstable
|
||||
//
|
||||
// This function is unstable and subject to change (or removal) in the future. Do not
|
||||
// deploy a contract using it to a production chain.
|
||||
[__unstable__] seal_rent_status(ctx, at_refcount: u32, out_ptr: u32, out_len_ptr: u32) => {
|
||||
let rent_status = ctx.ext.rent_status(at_refcount).encode();
|
||||
Ok(ctx.write_sandbox_output(
|
||||
out_ptr, out_len_ptr, &rent_status, false, already_charged
|
||||
)?)
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user