mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 23:21:02 +00:00
contracts: Change define_env! to expect a Result<T, DispatchError> for every function (#7762)
* Make host functions return TrapReason This avoids the need to manually store any trap reasons to the `Runtime` from the host function. This adds the following benefits: * It properly composes with the upcoming chain extensions * Missing to set a trap value is now a compile error * review: Remove superflous .into()
This commit is contained in:
committed by
GitHub
parent
dd8e7587cb
commit
ab876be9e9
@@ -378,6 +378,14 @@ decl_error! {
|
|||||||
/// on the call stack. Those actions are contract self destruction and restoration
|
/// on the call stack. Those actions are contract self destruction and restoration
|
||||||
/// of a tombstone.
|
/// of a tombstone.
|
||||||
ReentranceDenied,
|
ReentranceDenied,
|
||||||
|
/// `seal_input` was called twice from the same contract execution context.
|
||||||
|
InputAlreadyRead,
|
||||||
|
/// The subject passed to `seal_random` exceeds the limit.
|
||||||
|
RandomSubjectTooLong,
|
||||||
|
/// The amount of topics passed to `seal_deposit_events` exceeds the limit.
|
||||||
|
TooManyTopics,
|
||||||
|
/// The topics passed to `seal_deposit_events` contains at least one duplicate.
|
||||||
|
DuplicateTopics,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ macro_rules! unmarshall_then_body {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn constrain_closure<R, F>(f: F) -> F
|
pub fn constrain_closure<R, F>(f: F) -> F
|
||||||
where
|
where
|
||||||
F: FnOnce() -> Result<R, sp_sandbox::HostError>,
|
F: FnOnce() -> Result<R, crate::wasm::runtime::TrapReason>,
|
||||||
{
|
{
|
||||||
f
|
f
|
||||||
}
|
}
|
||||||
@@ -109,14 +109,20 @@ macro_rules! unmarshall_then_body_then_marshall {
|
|||||||
>(|| {
|
>(|| {
|
||||||
unmarshall_then_body!($body, $ctx, $args_iter, $( $names : $params ),*)
|
unmarshall_then_body!($body, $ctx, $args_iter, $( $names : $params ),*)
|
||||||
});
|
});
|
||||||
let r = body()?;
|
let r = body().map_err(|reason| {
|
||||||
|
$ctx.set_trap_reason(reason);
|
||||||
|
sp_sandbox::HostError
|
||||||
|
})?;
|
||||||
return Ok(sp_sandbox::ReturnValue::Value({ use $crate::wasm::env_def::ConvertibleToWasm; r.to_typed_value() }))
|
return Ok(sp_sandbox::ReturnValue::Value({ use $crate::wasm::env_def::ConvertibleToWasm; r.to_typed_value() }))
|
||||||
});
|
});
|
||||||
( $args_iter:ident, $ctx:ident, ( $( $names:ident : $params:ty ),* ) => $body:tt ) => ({
|
( $args_iter:ident, $ctx:ident, ( $( $names:ident : $params:ty ),* ) => $body:tt ) => ({
|
||||||
let body = $crate::wasm::env_def::macros::constrain_closure::<(), _>(|| {
|
let body = $crate::wasm::env_def::macros::constrain_closure::<(), _>(|| {
|
||||||
unmarshall_then_body!($body, $ctx, $args_iter, $( $names : $params ),*)
|
unmarshall_then_body!($body, $ctx, $args_iter, $( $names : $params ),*)
|
||||||
});
|
});
|
||||||
body()?;
|
body().map_err(|reason| {
|
||||||
|
$ctx.set_trap_reason(reason);
|
||||||
|
sp_sandbox::HostError
|
||||||
|
})?;
|
||||||
return Ok(sp_sandbox::ReturnValue::Unit)
|
return Ok(sp_sandbox::ReturnValue::Unit)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -207,15 +213,24 @@ mod tests {
|
|||||||
use parity_wasm::elements::ValueType;
|
use parity_wasm::elements::ValueType;
|
||||||
use sp_runtime::traits::Zero;
|
use sp_runtime::traits::Zero;
|
||||||
use sp_sandbox::{ReturnValue, Value};
|
use sp_sandbox::{ReturnValue, Value};
|
||||||
use crate::wasm::tests::MockExt;
|
use crate::{
|
||||||
use crate::wasm::Runtime;
|
wasm::{Runtime, runtime::TrapReason, tests::MockExt},
|
||||||
use crate::exec::Ext;
|
exec::Ext,
|
||||||
use crate::gas::Gas;
|
gas::Gas,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TestRuntime {
|
||||||
|
value: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestRuntime {
|
||||||
|
fn set_trap_reason(&mut self, _reason: TrapReason) {}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn macro_unmarshall_then_body_then_marshall_value_or_trap() {
|
fn macro_unmarshall_then_body_then_marshall_value_or_trap() {
|
||||||
fn test_value(
|
fn test_value(
|
||||||
_ctx: &mut u32,
|
_ctx: &mut TestRuntime,
|
||||||
args: &[sp_sandbox::Value],
|
args: &[sp_sandbox::Value],
|
||||||
) -> Result<ReturnValue, sp_sandbox::HostError> {
|
) -> Result<ReturnValue, sp_sandbox::HostError> {
|
||||||
let mut args = args.iter();
|
let mut args = args.iter();
|
||||||
@@ -224,7 +239,7 @@ mod tests {
|
|||||||
_ctx,
|
_ctx,
|
||||||
(a: u32, b: u32) -> u32 => {
|
(a: u32, b: u32) -> u32 => {
|
||||||
if b == 0 {
|
if b == 0 {
|
||||||
Err(sp_sandbox::HostError)
|
Err(crate::wasm::runtime::TrapReason::Termination)
|
||||||
} else {
|
} else {
|
||||||
Ok(a / b)
|
Ok(a / b)
|
||||||
}
|
}
|
||||||
@@ -232,7 +247,7 @@ mod tests {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let ctx = &mut 0;
|
let ctx = &mut TestRuntime { value: 0 };
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
test_value(ctx, &[Value::I32(15), Value::I32(3)]).unwrap(),
|
test_value(ctx, &[Value::I32(15), Value::I32(3)]).unwrap(),
|
||||||
ReturnValue::Value(Value::I32(5)),
|
ReturnValue::Value(Value::I32(5)),
|
||||||
@@ -243,7 +258,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn macro_unmarshall_then_body_then_marshall_unit() {
|
fn macro_unmarshall_then_body_then_marshall_unit() {
|
||||||
fn test_unit(
|
fn test_unit(
|
||||||
ctx: &mut u32,
|
ctx: &mut TestRuntime,
|
||||||
args: &[sp_sandbox::Value],
|
args: &[sp_sandbox::Value],
|
||||||
) -> Result<ReturnValue, sp_sandbox::HostError> {
|
) -> Result<ReturnValue, sp_sandbox::HostError> {
|
||||||
let mut args = args.iter();
|
let mut args = args.iter();
|
||||||
@@ -251,16 +266,16 @@ mod tests {
|
|||||||
args,
|
args,
|
||||||
ctx,
|
ctx,
|
||||||
(a: u32, b: u32) => {
|
(a: u32, b: u32) => {
|
||||||
*ctx = a + b;
|
ctx.value = a + b;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let ctx = &mut 0;
|
let ctx = &mut TestRuntime { value: 0 };
|
||||||
let result = test_unit(ctx, &[Value::I32(2), Value::I32(3)]).unwrap();
|
let result = test_unit(ctx, &[Value::I32(2), Value::I32(3)]).unwrap();
|
||||||
assert_eq!(result, ReturnValue::Unit);
|
assert_eq!(result, ReturnValue::Unit);
|
||||||
assert_eq!(*ctx, 5);
|
assert_eq!(ctx.value, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -270,7 +285,7 @@ mod tests {
|
|||||||
if !amount.is_zero() {
|
if !amount.is_zero() {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(sp_sandbox::HostError)
|
Err(TrapReason::Termination)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let _f: fn(&mut Runtime<MockExt>, &[sp_sandbox::Value])
|
let _f: fn(&mut Runtime<MockExt>, &[sp_sandbox::Value])
|
||||||
@@ -322,7 +337,7 @@ mod tests {
|
|||||||
if !amount.is_zero() {
|
if !amount.is_zero() {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(sp_sandbox::HostError)
|
Err(crate::wasm::runtime::TrapReason::Termination)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1537,7 +1537,7 @@ mod tests {
|
|||||||
&mut gas_meter
|
&mut gas_meter
|
||||||
),
|
),
|
||||||
Err(ExecError {
|
Err(ExecError {
|
||||||
error: Error::<Test>::ContractTrapped.into(),
|
error: Error::<Test>::TooManyTopics.into(),
|
||||||
origin: ErrorOrigin::Caller,
|
origin: ErrorOrigin::Caller,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -1582,7 +1582,7 @@ mod tests {
|
|||||||
&mut gas_meter
|
&mut gas_meter
|
||||||
),
|
),
|
||||||
Err(ExecError {
|
Err(ExecError {
|
||||||
error: Error::<Test>::ContractTrapped.into(),
|
error: Error::<Test>::DuplicateTopics.into(),
|
||||||
origin: ErrorOrigin::Caller,
|
origin: ErrorOrigin::Caller,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -491,9 +491,14 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Using unreachable statements triggers unreachable warnings in the generated code
|
||||||
|
#[allow(unreachable_code)]
|
||||||
|
mod env {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
// Define test environment for tests. We need ImportSatisfyCheck
|
// Define test environment for tests. We need ImportSatisfyCheck
|
||||||
// implementation from it. So actual implementations doesn't matter.
|
// implementation from it. So actual implementations doesn't matter.
|
||||||
define_env!(TestEnv, <E: Ext>,
|
define_env!(Test, <E: Ext>,
|
||||||
panic(_ctx) => { unreachable!(); },
|
panic(_ctx) => { unreachable!(); },
|
||||||
|
|
||||||
// gas is an implementation defined function and a contract can't import it.
|
// gas is an implementation defined function and a contract can't import it.
|
||||||
@@ -503,6 +508,7 @@ mod tests {
|
|||||||
|
|
||||||
seal_println(_ctx, _ptr: u32, _len: u32) => { unreachable!(); },
|
seal_println(_ctx, _ptr: u32, _len: u32) => { unreachable!(); },
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! prepare_test {
|
macro_rules! prepare_test {
|
||||||
($name:ident, $wat:expr, $($expected:tt)*) => {
|
($name:ident, $wat:expr, $($expected:tt)*) => {
|
||||||
@@ -520,7 +526,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
.. Default::default()
|
.. Default::default()
|
||||||
};
|
};
|
||||||
let r = prepare_contract::<TestEnv, crate::tests::Test>(wasm.as_ref(), &schedule);
|
let r = prepare_contract::<env::Test, crate::tests::Test>(wasm.as_ref(), &schedule);
|
||||||
assert_matches!(r, $($expected)*);
|
assert_matches!(r, $($expected)*);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -931,7 +937,7 @@ mod tests {
|
|||||||
).unwrap();
|
).unwrap();
|
||||||
let mut schedule = Schedule::default();
|
let mut schedule = Schedule::default();
|
||||||
schedule.enable_println = true;
|
schedule.enable_println = true;
|
||||||
let r = prepare_contract::<TestEnv, crate::tests::Test>(wasm.as_ref(), &schedule);
|
let r = prepare_contract::<env::Test, crate::tests::Test>(wasm.as_ref(), &schedule);
|
||||||
assert_matches!(r, Ok(_));
|
assert_matches!(r, Ok(_));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ impl From<ExecReturnValue> for ReturnCode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The data passed through when a contract uses `seal_return`.
|
/// The data passed through when a contract uses `seal_return`.
|
||||||
struct ReturnData {
|
pub struct ReturnData {
|
||||||
/// The flags as passed through by the contract. They are still unchecked and
|
/// The flags as passed through by the contract. They are still unchecked and
|
||||||
/// will later be parsed into a `ReturnFlags` bitflags struct.
|
/// will later be parsed into a `ReturnFlags` bitflags struct.
|
||||||
flags: u32,
|
flags: u32,
|
||||||
@@ -104,7 +104,7 @@ struct ReturnData {
|
|||||||
/// occurred (the SupervisorError variant).
|
/// occurred (the SupervisorError variant).
|
||||||
/// The other case is where the trap does not constitute an error but rather was invoked
|
/// The other case is where the trap does not constitute an error but rather was invoked
|
||||||
/// as a quick way to terminate the application (all other variants).
|
/// as a quick way to terminate the application (all other variants).
|
||||||
enum TrapReason {
|
pub enum TrapReason {
|
||||||
/// The supervisor trapped the contract because of an error condition occurred during
|
/// The supervisor trapped the contract because of an error condition occurred during
|
||||||
/// execution in privileged code.
|
/// execution in privileged code.
|
||||||
SupervisorError(DispatchError),
|
SupervisorError(DispatchError),
|
||||||
@@ -117,9 +117,15 @@ enum TrapReason {
|
|||||||
Restoration,
|
Restoration,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Into<DispatchError>> From<T> for TrapReason {
|
||||||
|
fn from(from: T) -> Self {
|
||||||
|
Self::SupervisorError(from.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
|
#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum RuntimeToken {
|
enum RuntimeToken {
|
||||||
/// Charge the gas meter with the cost of a metering block. The charged costs are
|
/// Charge the gas meter with the cost of a metering block. The charged costs are
|
||||||
/// the supplied cost of the block plus the overhead of the metering itself.
|
/// the supplied cost of the block plus the overhead of the metering itself.
|
||||||
MeteringBlock(u32),
|
MeteringBlock(u32),
|
||||||
@@ -369,21 +375,25 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Store the reason for a host function triggered trap.
|
||||||
|
///
|
||||||
|
/// This is called by the `define_env` macro in order to store any error returned by
|
||||||
|
/// the host functions defined through the said macro. It should **not** be called
|
||||||
|
/// manually.
|
||||||
|
pub fn set_trap_reason(&mut self, reason: TrapReason) {
|
||||||
|
self.trap_reason = Some(reason);
|
||||||
|
}
|
||||||
|
|
||||||
/// Charge the gas meter with the specified token.
|
/// Charge the gas meter with the specified token.
|
||||||
///
|
///
|
||||||
/// Returns `Err(HostError)` if there is not enough gas.
|
/// Returns `Err(HostError)` if there is not enough gas.
|
||||||
fn charge_gas<Tok>(&mut self, token: Tok) -> Result<(), sp_sandbox::HostError>
|
fn charge_gas<Tok>(&mut self, token: Tok) -> Result<(), DispatchError>
|
||||||
where
|
where
|
||||||
Tok: Token<E::T, Metadata=HostFnWeights<E::T>>,
|
Tok: Token<E::T, Metadata=HostFnWeights<E::T>>,
|
||||||
{
|
{
|
||||||
match self.gas_meter.charge(&self.schedule.host_fn_weights, token) {
|
match self.gas_meter.charge(&self.schedule.host_fn_weights, token) {
|
||||||
GasMeterResult::Proceed => Ok(()),
|
GasMeterResult::Proceed => Ok(()),
|
||||||
GasMeterResult::OutOfGas => {
|
GasMeterResult::OutOfGas => Err(Error::<E::T>::OutOfGas.into())
|
||||||
self.trap_reason = Some(
|
|
||||||
TrapReason::SupervisorError(Error::<E::T>::OutOfGas.into())
|
|
||||||
);
|
|
||||||
Err(sp_sandbox::HostError)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,12 +402,12 @@ where
|
|||||||
/// Returns `Err` if one of the following conditions occurs:
|
/// Returns `Err` if one of the following conditions occurs:
|
||||||
///
|
///
|
||||||
/// - requested buffer is not within the bounds of the sandbox memory.
|
/// - requested buffer is not within the bounds of the sandbox memory.
|
||||||
fn read_sandbox_memory(&mut self, ptr: u32, len: u32)
|
fn read_sandbox_memory(&self, ptr: u32, len: u32)
|
||||||
-> Result<Vec<u8>, sp_sandbox::HostError>
|
-> Result<Vec<u8>, DispatchError>
|
||||||
{
|
{
|
||||||
let mut buf = vec![0u8; len as usize];
|
let mut buf = vec![0u8; len as usize];
|
||||||
self.memory.get(ptr, buf.as_mut_slice())
|
self.memory.get(ptr, buf.as_mut_slice())
|
||||||
.map_err(|_| self.store_err(Error::<E::T>::OutOfBounds))?;
|
.map_err(|_| Error::<E::T>::OutOfBounds)?;
|
||||||
Ok(buf)
|
Ok(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,10 +416,10 @@ where
|
|||||||
/// Returns `Err` if one of the following conditions occurs:
|
/// Returns `Err` if one of the following conditions occurs:
|
||||||
///
|
///
|
||||||
/// - requested buffer is not within the bounds of the sandbox memory.
|
/// - requested buffer is not within the bounds of the sandbox memory.
|
||||||
fn read_sandbox_memory_into_buf(&mut self, ptr: u32, buf: &mut [u8])
|
fn read_sandbox_memory_into_buf(&self, ptr: u32, buf: &mut [u8])
|
||||||
-> Result<(), sp_sandbox::HostError>
|
-> Result<(), DispatchError>
|
||||||
{
|
{
|
||||||
self.memory.get(ptr, buf).map_err(|_| self.store_err(Error::<E::T>::OutOfBounds))
|
self.memory.get(ptr, buf).map_err(|_| Error::<E::T>::OutOfBounds.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read designated chunk from the sandbox memory and attempt to decode into the specified type.
|
/// Read designated chunk from the sandbox memory and attempt to decode into the specified type.
|
||||||
@@ -418,11 +428,11 @@ where
|
|||||||
///
|
///
|
||||||
/// - requested buffer is not within the bounds of the sandbox memory.
|
/// - requested buffer is not within the bounds of the sandbox memory.
|
||||||
/// - the buffer contents cannot be decoded as the required type.
|
/// - the buffer contents cannot be decoded as the required type.
|
||||||
fn read_sandbox_memory_as<D: Decode>(&mut self, ptr: u32, len: u32)
|
fn read_sandbox_memory_as<D: Decode>(&self, ptr: u32, len: u32)
|
||||||
-> Result<D, sp_sandbox::HostError>
|
-> Result<D, DispatchError>
|
||||||
{
|
{
|
||||||
let buf = self.read_sandbox_memory(ptr, len)?;
|
let buf = self.read_sandbox_memory(ptr, len)?;
|
||||||
D::decode(&mut &buf[..]).map_err(|_| self.store_err(Error::<E::T>::DecodingFailed))
|
D::decode(&mut &buf[..]).map_err(|_| Error::<E::T>::DecodingFailed.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write the given buffer to the designated location in the sandbox memory.
|
/// Write the given buffer to the designated location in the sandbox memory.
|
||||||
@@ -430,8 +440,8 @@ where
|
|||||||
/// Returns `Err` if one of the following conditions occurs:
|
/// Returns `Err` if one of the following conditions occurs:
|
||||||
///
|
///
|
||||||
/// - designated area is not within the bounds of the sandbox memory.
|
/// - designated area is not within the bounds of the sandbox memory.
|
||||||
fn write_sandbox_memory(&mut self, ptr: u32, buf: &[u8]) -> Result<(), sp_sandbox::HostError> {
|
fn write_sandbox_memory(&mut self, ptr: u32, buf: &[u8]) -> Result<(), DispatchError> {
|
||||||
self.memory.set(ptr, buf).map_err(|_| self.store_err(Error::<E::T>::OutOfBounds))
|
self.memory.set(ptr, buf).map_err(|_| Error::<E::T>::OutOfBounds.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write the given buffer and its length to the designated locations in sandbox memory and
|
/// Write the given buffer and its length to the designated locations in sandbox memory and
|
||||||
@@ -460,7 +470,7 @@ where
|
|||||||
buf: &[u8],
|
buf: &[u8],
|
||||||
allow_skip: bool,
|
allow_skip: bool,
|
||||||
create_token: impl FnOnce(u32) -> Option<RuntimeToken>,
|
create_token: impl FnOnce(u32) -> Option<RuntimeToken>,
|
||||||
) -> Result<(), sp_sandbox::HostError>
|
) -> Result<(), DispatchError>
|
||||||
{
|
{
|
||||||
if allow_skip && out_ptr == u32::max_value() {
|
if allow_skip && out_ptr == u32::max_value() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -470,7 +480,7 @@ where
|
|||||||
let len: u32 = self.read_sandbox_memory_as(out_len_ptr, 4)?;
|
let len: u32 = self.read_sandbox_memory_as(out_len_ptr, 4)?;
|
||||||
|
|
||||||
if len < buf_len {
|
if len < buf_len {
|
||||||
Err(self.store_err(Error::<E::T>::OutputBufferTooSmall))?
|
Err(Error::<E::T>::OutputBufferTooSmall)?
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(token) = create_token(buf_len) {
|
if let Some(token) = create_token(buf_len) {
|
||||||
@@ -480,7 +490,7 @@ where
|
|||||||
self.memory.set(out_ptr, buf).and_then(|_| {
|
self.memory.set(out_ptr, buf).and_then(|_| {
|
||||||
self.memory.set(out_len_ptr, &buf_len.encode())
|
self.memory.set(out_len_ptr, &buf_len.encode())
|
||||||
})
|
})
|
||||||
.map_err(|_| self.store_err(Error::<E::T>::OutOfBounds))?;
|
.map_err(|_| Error::<E::T>::OutOfBounds)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -503,7 +513,7 @@ where
|
|||||||
input_ptr: u32,
|
input_ptr: u32,
|
||||||
input_len: u32,
|
input_len: u32,
|
||||||
output_ptr: u32,
|
output_ptr: u32,
|
||||||
) -> Result<(), sp_sandbox::HostError>
|
) -> Result<(), DispatchError>
|
||||||
where
|
where
|
||||||
F: FnOnce(&[u8]) -> R,
|
F: FnOnce(&[u8]) -> R,
|
||||||
R: AsRef<[u8]>,
|
R: AsRef<[u8]>,
|
||||||
@@ -517,48 +527,6 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stores a DispatchError returned from an Ext function into the trap_reason.
|
|
||||||
///
|
|
||||||
/// This allows through supervisor generated errors to the caller.
|
|
||||||
fn store_err<Error>(&mut self, err: Error) -> sp_sandbox::HostError
|
|
||||||
where
|
|
||||||
Error: Into<DispatchError>,
|
|
||||||
{
|
|
||||||
self.trap_reason = Some(TrapReason::SupervisorError(err.into()));
|
|
||||||
sp_sandbox::HostError
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Used by Runtime API that calls into other contracts.
|
|
||||||
///
|
|
||||||
/// Those need to transform the the `ExecResult` returned from the execution into
|
|
||||||
/// a `ReturnCode`. If this conversion fails because the `ExecResult` constitutes a
|
|
||||||
/// a fatal error then this error is stored in the `ExecutionContext` so it can be
|
|
||||||
/// extracted for display in the UI.
|
|
||||||
fn map_exec_result(&mut self, result: ExecResult) -> Result<ReturnCode, sp_sandbox::HostError> {
|
|
||||||
match Self::exec_into_return_code(result) {
|
|
||||||
Ok(code) => Ok(code),
|
|
||||||
Err(err) => Err(self.store_err(err)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Try to convert an error into a `ReturnCode`.
|
|
||||||
///
|
|
||||||
/// Used to decide between fatal and non-fatal errors.
|
|
||||||
fn map_dispatch_result<T>(&mut self, result: Result<T, DispatchError>)
|
|
||||||
-> Result<ReturnCode, sp_sandbox::HostError>
|
|
||||||
{
|
|
||||||
let err = if let Err(err) = result {
|
|
||||||
err
|
|
||||||
} else {
|
|
||||||
return Ok(ReturnCode::Success)
|
|
||||||
};
|
|
||||||
|
|
||||||
match Self::err_into_return_code(err) {
|
|
||||||
Ok(code) => Ok(code),
|
|
||||||
Err(err) => Err(self.store_err(err)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fallible conversion of `DispatchError` to `ReturnCode`.
|
/// Fallible conversion of `DispatchError` to `ReturnCode`.
|
||||||
fn err_into_return_code(from: DispatchError) -> Result<ReturnCode, DispatchError> {
|
fn err_into_return_code(from: DispatchError) -> Result<ReturnCode, DispatchError> {
|
||||||
use ReturnCode::*;
|
use ReturnCode::*;
|
||||||
@@ -638,7 +606,7 @@ define_env!(Env, <E: Ext>,
|
|||||||
seal_set_storage(ctx, key_ptr: u32, value_ptr: u32, value_len: u32) => {
|
seal_set_storage(ctx, key_ptr: u32, value_ptr: u32, value_len: u32) => {
|
||||||
ctx.charge_gas(RuntimeToken::SetStorage(value_len))?;
|
ctx.charge_gas(RuntimeToken::SetStorage(value_len))?;
|
||||||
if value_len > ctx.ext.max_value_size() {
|
if value_len > ctx.ext.max_value_size() {
|
||||||
Err(ctx.store_err(Error::<E::T>::ValueTooLarge))?;
|
Err(Error::<E::T>::ValueTooLarge)?;
|
||||||
}
|
}
|
||||||
let mut key: StorageKey = [0; 32];
|
let mut key: StorageKey = [0; 32];
|
||||||
ctx.read_sandbox_memory_into_buf(key_ptr, &mut key)?;
|
ctx.read_sandbox_memory_into_buf(key_ptr, &mut key)?;
|
||||||
@@ -715,7 +683,13 @@ define_env!(Env, <E: Ext>,
|
|||||||
ctx.read_sandbox_memory_as(value_ptr, value_len)?;
|
ctx.read_sandbox_memory_as(value_ptr, value_len)?;
|
||||||
|
|
||||||
let result = ctx.ext.transfer(&callee, value);
|
let result = ctx.ext.transfer(&callee, value);
|
||||||
ctx.map_dispatch_result(result)
|
match result {
|
||||||
|
Ok(()) => Ok(ReturnCode::Success),
|
||||||
|
Err(err) => {
|
||||||
|
let code = Runtime::<E>::err_into_return_code(err)?;
|
||||||
|
Ok(code)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Make a call to another contract.
|
// Make a call to another contract.
|
||||||
@@ -797,7 +771,7 @@ define_env!(Env, <E: Ext>,
|
|||||||
Some(RuntimeToken::CallCopyOut(len))
|
Some(RuntimeToken::CallCopyOut(len))
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
ctx.map_exec_result(call_outcome)
|
Ok(Runtime::<E>::exec_into_return_code(call_outcome)?)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Instantiate a contract with the specified code hash.
|
// Instantiate a contract with the specified code hash.
|
||||||
@@ -899,7 +873,7 @@ define_env!(Env, <E: Ext>,
|
|||||||
Some(RuntimeToken::InstantiateCopyOut(len))
|
Some(RuntimeToken::InstantiateCopyOut(len))
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
ctx.map_exec_result(instantiate_outcome.map(|(_id, retval)| retval))
|
Ok(Runtime::<E>::exec_into_return_code(instantiate_outcome.map(|(_id, retval)| retval))?)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Remove the calling account and transfer remaining balance.
|
// Remove the calling account and transfer remaining balance.
|
||||||
@@ -925,10 +899,8 @@ define_env!(Env, <E: Ext>,
|
|||||||
let beneficiary: <<E as Ext>::T as frame_system::Config>::AccountId =
|
let beneficiary: <<E as Ext>::T as frame_system::Config>::AccountId =
|
||||||
ctx.read_sandbox_memory_as(beneficiary_ptr, beneficiary_len)?;
|
ctx.read_sandbox_memory_as(beneficiary_ptr, beneficiary_len)?;
|
||||||
|
|
||||||
if let Ok(_) = ctx.ext.terminate(&beneficiary).map_err(|e| ctx.store_err(e)) {
|
ctx.ext.terminate(&beneficiary)?;
|
||||||
ctx.trap_reason = Some(TrapReason::Termination);
|
Err(TrapReason::Termination)
|
||||||
}
|
|
||||||
Err(sp_sandbox::HostError)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
seal_input(ctx, buf_ptr: u32, buf_len_ptr: u32) => {
|
seal_input(ctx, buf_ptr: u32, buf_len_ptr: u32) => {
|
||||||
@@ -936,9 +908,10 @@ define_env!(Env, <E: Ext>,
|
|||||||
if let Some(input) = ctx.input_data.take() {
|
if let Some(input) = ctx.input_data.take() {
|
||||||
ctx.write_sandbox_output(buf_ptr, buf_len_ptr, &input, false, |len| {
|
ctx.write_sandbox_output(buf_ptr, buf_len_ptr, &input, false, |len| {
|
||||||
Some(RuntimeToken::InputCopyOut(len))
|
Some(RuntimeToken::InputCopyOut(len))
|
||||||
})
|
})?;
|
||||||
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(sp_sandbox::HostError)
|
Err(Error::<E::T>::InputAlreadyRead.into())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -961,15 +934,10 @@ define_env!(Env, <E: Ext>,
|
|||||||
// Using a reserved bit triggers a trap.
|
// Using a reserved bit triggers a trap.
|
||||||
seal_return(ctx, flags: u32, data_ptr: u32, data_len: u32) => {
|
seal_return(ctx, flags: u32, data_ptr: u32, data_len: u32) => {
|
||||||
ctx.charge_gas(RuntimeToken::Return(data_len))?;
|
ctx.charge_gas(RuntimeToken::Return(data_len))?;
|
||||||
ctx.trap_reason = Some(TrapReason::Return(ReturnData {
|
Err(TrapReason::Return(ReturnData {
|
||||||
flags,
|
flags,
|
||||||
data: ctx.read_sandbox_memory(data_ptr, data_len)?,
|
data: ctx.read_sandbox_memory(data_ptr, data_len)?,
|
||||||
}));
|
}))
|
||||||
|
|
||||||
// The trap mechanism is used to immediately terminate the execution.
|
|
||||||
// This trap should be handled appropriately before returning the result
|
|
||||||
// to the user of this crate.
|
|
||||||
Err(sp_sandbox::HostError)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Stores the address of the caller into the supplied buffer.
|
// Stores the address of the caller into the supplied buffer.
|
||||||
@@ -984,9 +952,9 @@ define_env!(Env, <E: Ext>,
|
|||||||
// address of the contract will be returned. The value is encoded as T::AccountId.
|
// address of the contract will be returned. The value is encoded as T::AccountId.
|
||||||
seal_caller(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
seal_caller(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
||||||
ctx.charge_gas(RuntimeToken::Caller)?;
|
ctx.charge_gas(RuntimeToken::Caller)?;
|
||||||
ctx.write_sandbox_output(
|
Ok(ctx.write_sandbox_output(
|
||||||
out_ptr, out_len_ptr, &ctx.ext.caller().encode(), false, already_charged
|
out_ptr, out_len_ptr, &ctx.ext.caller().encode(), false, already_charged
|
||||||
)
|
)?)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Stores the address of the current contract into the supplied buffer.
|
// Stores the address of the current contract into the supplied buffer.
|
||||||
@@ -997,9 +965,9 @@ define_env!(Env, <E: Ext>,
|
|||||||
// space at `out_ptr` is less than the size of the value a trap is triggered.
|
// space at `out_ptr` is less than the size of the value a trap is triggered.
|
||||||
seal_address(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
seal_address(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
||||||
ctx.charge_gas(RuntimeToken::Address)?;
|
ctx.charge_gas(RuntimeToken::Address)?;
|
||||||
ctx.write_sandbox_output(
|
Ok(ctx.write_sandbox_output(
|
||||||
out_ptr, out_len_ptr, &ctx.ext.address().encode(), false, already_charged
|
out_ptr, out_len_ptr, &ctx.ext.address().encode(), false, already_charged
|
||||||
)
|
)?)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Stores the price for the specified amount of gas into the supplied buffer.
|
// Stores the price for the specified amount of gas into the supplied buffer.
|
||||||
@@ -1017,9 +985,9 @@ define_env!(Env, <E: Ext>,
|
|||||||
// gas can be smaller than one.
|
// gas can be smaller than one.
|
||||||
seal_weight_to_fee(ctx, gas: u64, out_ptr: u32, out_len_ptr: u32) => {
|
seal_weight_to_fee(ctx, gas: u64, out_ptr: u32, out_len_ptr: u32) => {
|
||||||
ctx.charge_gas(RuntimeToken::WeightToFee)?;
|
ctx.charge_gas(RuntimeToken::WeightToFee)?;
|
||||||
ctx.write_sandbox_output(
|
Ok(ctx.write_sandbox_output(
|
||||||
out_ptr, out_len_ptr, &ctx.ext.get_weight_price(gas).encode(), false, already_charged
|
out_ptr, out_len_ptr, &ctx.ext.get_weight_price(gas).encode(), false, already_charged
|
||||||
)
|
)?)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Stores the amount of gas left into the supplied buffer.
|
// Stores the amount of gas left into the supplied buffer.
|
||||||
@@ -1032,9 +1000,9 @@ define_env!(Env, <E: Ext>,
|
|||||||
// The data is encoded as Gas.
|
// The data is encoded as Gas.
|
||||||
seal_gas_left(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
seal_gas_left(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
||||||
ctx.charge_gas(RuntimeToken::GasLeft)?;
|
ctx.charge_gas(RuntimeToken::GasLeft)?;
|
||||||
ctx.write_sandbox_output(
|
Ok(ctx.write_sandbox_output(
|
||||||
out_ptr, out_len_ptr, &ctx.gas_meter.gas_left().encode(), false, already_charged
|
out_ptr, out_len_ptr, &ctx.gas_meter.gas_left().encode(), false, already_charged
|
||||||
)
|
)?)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Stores the balance of the current account into the supplied buffer.
|
// Stores the balance of the current account into the supplied buffer.
|
||||||
@@ -1047,9 +1015,9 @@ define_env!(Env, <E: Ext>,
|
|||||||
// The data is encoded as T::Balance.
|
// The data is encoded as T::Balance.
|
||||||
seal_balance(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
seal_balance(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
||||||
ctx.charge_gas(RuntimeToken::Balance)?;
|
ctx.charge_gas(RuntimeToken::Balance)?;
|
||||||
ctx.write_sandbox_output(
|
Ok(ctx.write_sandbox_output(
|
||||||
out_ptr, out_len_ptr, &ctx.ext.balance().encode(), false, already_charged
|
out_ptr, out_len_ptr, &ctx.ext.balance().encode(), false, already_charged
|
||||||
)
|
)?)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Stores the value transferred along with this call or as endowment into the supplied buffer.
|
// Stores the value transferred along with this call or as endowment into the supplied buffer.
|
||||||
@@ -1062,9 +1030,9 @@ define_env!(Env, <E: Ext>,
|
|||||||
// The data is encoded as T::Balance.
|
// The data is encoded as T::Balance.
|
||||||
seal_value_transferred(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
seal_value_transferred(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
||||||
ctx.charge_gas(RuntimeToken::ValueTransferred)?;
|
ctx.charge_gas(RuntimeToken::ValueTransferred)?;
|
||||||
ctx.write_sandbox_output(
|
Ok(ctx.write_sandbox_output(
|
||||||
out_ptr, out_len_ptr, &ctx.ext.value_transferred().encode(), false, already_charged
|
out_ptr, out_len_ptr, &ctx.ext.value_transferred().encode(), false, already_charged
|
||||||
)
|
)?)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Stores a random number for the current block and the given subject into the supplied buffer.
|
// Stores a random number for the current block and the given subject into the supplied buffer.
|
||||||
@@ -1078,12 +1046,12 @@ define_env!(Env, <E: Ext>,
|
|||||||
seal_random(ctx, subject_ptr: u32, subject_len: u32, out_ptr: u32, out_len_ptr: u32) => {
|
seal_random(ctx, subject_ptr: u32, subject_len: u32, out_ptr: u32, out_len_ptr: u32) => {
|
||||||
ctx.charge_gas(RuntimeToken::Random)?;
|
ctx.charge_gas(RuntimeToken::Random)?;
|
||||||
if subject_len > ctx.schedule.limits.subject_len {
|
if subject_len > ctx.schedule.limits.subject_len {
|
||||||
return Err(sp_sandbox::HostError);
|
Err(Error::<E::T>::RandomSubjectTooLong)?;
|
||||||
}
|
}
|
||||||
let subject_buf = ctx.read_sandbox_memory(subject_ptr, subject_len)?;
|
let subject_buf = ctx.read_sandbox_memory(subject_ptr, subject_len)?;
|
||||||
ctx.write_sandbox_output(
|
Ok(ctx.write_sandbox_output(
|
||||||
out_ptr, out_len_ptr, &ctx.ext.random(&subject_buf).encode(), false, already_charged
|
out_ptr, out_len_ptr, &ctx.ext.random(&subject_buf).encode(), false, already_charged
|
||||||
)
|
)?)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Load the latest block timestamp into the supplied buffer
|
// Load the latest block timestamp into the supplied buffer
|
||||||
@@ -1094,9 +1062,9 @@ define_env!(Env, <E: Ext>,
|
|||||||
// space at `out_ptr` is less than the size of the value a trap is triggered.
|
// space at `out_ptr` is less than the size of the value a trap is triggered.
|
||||||
seal_now(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
seal_now(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
||||||
ctx.charge_gas(RuntimeToken::Now)?;
|
ctx.charge_gas(RuntimeToken::Now)?;
|
||||||
ctx.write_sandbox_output(
|
Ok(ctx.write_sandbox_output(
|
||||||
out_ptr, out_len_ptr, &ctx.ext.now().encode(), false, already_charged
|
out_ptr, out_len_ptr, &ctx.ext.now().encode(), false, already_charged
|
||||||
)
|
)?)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer.
|
// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer.
|
||||||
@@ -1104,9 +1072,9 @@ define_env!(Env, <E: Ext>,
|
|||||||
// The data is encoded as T::Balance.
|
// The data is encoded as T::Balance.
|
||||||
seal_minimum_balance(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
seal_minimum_balance(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
||||||
ctx.charge_gas(RuntimeToken::MinimumBalance)?;
|
ctx.charge_gas(RuntimeToken::MinimumBalance)?;
|
||||||
ctx.write_sandbox_output(
|
Ok(ctx.write_sandbox_output(
|
||||||
out_ptr, out_len_ptr, &ctx.ext.minimum_balance().encode(), false, already_charged
|
out_ptr, out_len_ptr, &ctx.ext.minimum_balance().encode(), false, already_charged
|
||||||
)
|
)?)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Stores the tombstone deposit into the supplied buffer.
|
// Stores the tombstone deposit into the supplied buffer.
|
||||||
@@ -1126,9 +1094,9 @@ define_env!(Env, <E: Ext>,
|
|||||||
// is commonly referred as subsistence threshold in code.
|
// is commonly referred as subsistence threshold in code.
|
||||||
seal_tombstone_deposit(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
seal_tombstone_deposit(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
||||||
ctx.charge_gas(RuntimeToken::TombstoneDeposit)?;
|
ctx.charge_gas(RuntimeToken::TombstoneDeposit)?;
|
||||||
ctx.write_sandbox_output(
|
Ok(ctx.write_sandbox_output(
|
||||||
out_ptr, out_len_ptr, &ctx.ext.tombstone_deposit().encode(), false, already_charged
|
out_ptr, out_len_ptr, &ctx.ext.tombstone_deposit().encode(), false, already_charged
|
||||||
)
|
)?)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Try to restore the given destination contract sacrificing the caller.
|
// Try to restore the given destination contract sacrificing the caller.
|
||||||
@@ -1189,21 +1157,14 @@ define_env!(Env, <E: Ext>,
|
|||||||
delta.push(delta_key);
|
delta.push(delta_key);
|
||||||
|
|
||||||
// Offset key_ptr to the next element.
|
// Offset key_ptr to the next element.
|
||||||
key_ptr = key_ptr.checked_add(KEY_SIZE as u32).ok_or_else(|| sp_sandbox::HostError)?;
|
key_ptr = key_ptr.checked_add(KEY_SIZE as u32).ok_or(Error::<E::T>::OutOfBounds)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
delta
|
delta
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Ok(()) = ctx.ext.restore_to(
|
ctx.ext.restore_to(dest, code_hash, rent_allowance, delta)?;
|
||||||
dest,
|
Err(TrapReason::Restoration)
|
||||||
code_hash,
|
|
||||||
rent_allowance,
|
|
||||||
delta,
|
|
||||||
).map_err(|e| ctx.store_err(e)) {
|
|
||||||
ctx.trap_reason = Some(TrapReason::Restoration);
|
|
||||||
}
|
|
||||||
Err(sp_sandbox::HostError)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Deposit a contract event with the data buffer and optional list of topics. There is a limit
|
// Deposit a contract event with the data buffer and optional list of topics. There is a limit
|
||||||
@@ -1217,13 +1178,13 @@ define_env!(Env, <E: Ext>,
|
|||||||
seal_deposit_event(ctx, topics_ptr: u32, topics_len: u32, data_ptr: u32, data_len: u32) => {
|
seal_deposit_event(ctx, topics_ptr: u32, topics_len: u32, data_ptr: u32, data_len: u32) => {
|
||||||
let num_topic = topics_len
|
let num_topic = topics_len
|
||||||
.checked_div(sp_std::mem::size_of::<TopicOf<E::T>>() as u32)
|
.checked_div(sp_std::mem::size_of::<TopicOf<E::T>>() as u32)
|
||||||
.ok_or_else(|| ctx.store_err("Zero sized topics are not allowed"))?;
|
.ok_or_else(|| "Zero sized topics are not allowed")?;
|
||||||
ctx.charge_gas(RuntimeToken::DepositEvent {
|
ctx.charge_gas(RuntimeToken::DepositEvent {
|
||||||
num_topic,
|
num_topic,
|
||||||
len: data_len,
|
len: data_len,
|
||||||
})?;
|
})?;
|
||||||
if data_len > ctx.ext.max_value_size() {
|
if data_len > ctx.ext.max_value_size() {
|
||||||
Err(ctx.store_err(Error::<E::T>::ValueTooLarge))?;
|
Err(Error::<E::T>::ValueTooLarge)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut topics: Vec::<TopicOf<<E as Ext>::T>> = match topics_len {
|
let mut topics: Vec::<TopicOf<<E as Ext>::T>> = match topics_len {
|
||||||
@@ -1233,12 +1194,12 @@ define_env!(Env, <E: Ext>,
|
|||||||
|
|
||||||
// If there are more than `event_topics`, then trap.
|
// If there are more than `event_topics`, then trap.
|
||||||
if topics.len() > ctx.schedule.limits.event_topics as usize {
|
if topics.len() > ctx.schedule.limits.event_topics as usize {
|
||||||
return Err(sp_sandbox::HostError);
|
Err(Error::<E::T>::TooManyTopics)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for duplicate topics. If there are any, then trap.
|
// Check for duplicate topics. If there are any, then trap.
|
||||||
if has_duplicates(&mut topics) {
|
if has_duplicates(&mut topics) {
|
||||||
return Err(sp_sandbox::HostError);
|
Err(Error::<E::T>::DuplicateTopics)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let event_data = ctx.read_sandbox_memory(data_ptr, data_len)?;
|
let event_data = ctx.read_sandbox_memory(data_ptr, data_len)?;
|
||||||
@@ -1272,9 +1233,9 @@ define_env!(Env, <E: Ext>,
|
|||||||
// The data is encoded as T::Balance.
|
// The data is encoded as T::Balance.
|
||||||
seal_rent_allowance(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
seal_rent_allowance(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
||||||
ctx.charge_gas(RuntimeToken::RentAllowance)?;
|
ctx.charge_gas(RuntimeToken::RentAllowance)?;
|
||||||
ctx.write_sandbox_output(
|
Ok(ctx.write_sandbox_output(
|
||||||
out_ptr, out_len_ptr, &ctx.ext.rent_allowance().encode(), false, already_charged
|
out_ptr, out_len_ptr, &ctx.ext.rent_allowance().encode(), false, already_charged
|
||||||
)
|
)?)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Prints utf8 encoded string from the data buffer.
|
// Prints utf8 encoded string from the data buffer.
|
||||||
@@ -1296,9 +1257,9 @@ define_env!(Env, <E: Ext>,
|
|||||||
// space at `out_ptr` is less than the size of the value a trap is triggered.
|
// space at `out_ptr` is less than the size of the value a trap is triggered.
|
||||||
seal_block_number(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
seal_block_number(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
||||||
ctx.charge_gas(RuntimeToken::BlockNumber)?;
|
ctx.charge_gas(RuntimeToken::BlockNumber)?;
|
||||||
ctx.write_sandbox_output(
|
Ok(ctx.write_sandbox_output(
|
||||||
out_ptr, out_len_ptr, &ctx.ext.block_number().encode(), false, already_charged
|
out_ptr, out_len_ptr, &ctx.ext.block_number().encode(), false, already_charged
|
||||||
)
|
)?)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Computes the SHA2 256-bit hash on the given input buffer.
|
// Computes the SHA2 256-bit hash on the given input buffer.
|
||||||
@@ -1323,7 +1284,7 @@ define_env!(Env, <E: Ext>,
|
|||||||
// directly into this buffer.
|
// directly into this buffer.
|
||||||
seal_hash_sha2_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => {
|
seal_hash_sha2_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => {
|
||||||
ctx.charge_gas(RuntimeToken::HashSha256(input_len))?;
|
ctx.charge_gas(RuntimeToken::HashSha256(input_len))?;
|
||||||
ctx.compute_hash_on_intermediate_buffer(sha2_256, input_ptr, input_len, output_ptr)
|
Ok(ctx.compute_hash_on_intermediate_buffer(sha2_256, input_ptr, input_len, output_ptr)?)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Computes the KECCAK 256-bit hash on the given input buffer.
|
// Computes the KECCAK 256-bit hash on the given input buffer.
|
||||||
@@ -1348,7 +1309,7 @@ define_env!(Env, <E: Ext>,
|
|||||||
// directly into this buffer.
|
// directly into this buffer.
|
||||||
seal_hash_keccak_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => {
|
seal_hash_keccak_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => {
|
||||||
ctx.charge_gas(RuntimeToken::HashKeccak256(input_len))?;
|
ctx.charge_gas(RuntimeToken::HashKeccak256(input_len))?;
|
||||||
ctx.compute_hash_on_intermediate_buffer(keccak_256, input_ptr, input_len, output_ptr)
|
Ok(ctx.compute_hash_on_intermediate_buffer(keccak_256, input_ptr, input_len, output_ptr)?)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Computes the BLAKE2 256-bit hash on the given input buffer.
|
// Computes the BLAKE2 256-bit hash on the given input buffer.
|
||||||
@@ -1373,7 +1334,7 @@ define_env!(Env, <E: Ext>,
|
|||||||
// directly into this buffer.
|
// directly into this buffer.
|
||||||
seal_hash_blake2_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => {
|
seal_hash_blake2_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => {
|
||||||
ctx.charge_gas(RuntimeToken::HashBlake256(input_len))?;
|
ctx.charge_gas(RuntimeToken::HashBlake256(input_len))?;
|
||||||
ctx.compute_hash_on_intermediate_buffer(blake2_256, input_ptr, input_len, output_ptr)
|
Ok(ctx.compute_hash_on_intermediate_buffer(blake2_256, input_ptr, input_len, output_ptr)?)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Computes the BLAKE2 128-bit hash on the given input buffer.
|
// Computes the BLAKE2 128-bit hash on the given input buffer.
|
||||||
@@ -1398,6 +1359,6 @@ define_env!(Env, <E: Ext>,
|
|||||||
// directly into this buffer.
|
// directly into this buffer.
|
||||||
seal_hash_blake2_128(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => {
|
seal_hash_blake2_128(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => {
|
||||||
ctx.charge_gas(RuntimeToken::HashBlake128(input_len))?;
|
ctx.charge_gas(RuntimeToken::HashBlake128(input_len))?;
|
||||||
ctx.compute_hash_on_intermediate_buffer(blake2_128, input_ptr, input_len, output_ptr)
|
Ok(ctx.compute_hash_on_intermediate_buffer(blake2_128, input_ptr, input_len, output_ptr)?)
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user