mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-14 05:11:09 +00:00
contracts: Add new seal_call that offers new features (#8909)
* Add new `seal_call` that offers new features * Fix doc typo Co-authored-by: Michael Müller <michi@parity.io> * Fix doc typos Co-authored-by: Michael Müller <michi@parity.io> * Fix comment on assert * Update CHANGELOG.md Co-authored-by: Michael Müller <michi@parity.io>
This commit is contained in:
committed by
GitHub
parent
5c14dd3f32
commit
60256d752e
@@ -289,7 +289,14 @@ mod tests {
|
||||
struct TransferEntry {
|
||||
to: AccountIdOf<Test>,
|
||||
value: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct CallEntry {
|
||||
to: AccountIdOf<Test>,
|
||||
value: u64,
|
||||
data: Vec<u8>,
|
||||
allows_reentry: bool,
|
||||
}
|
||||
|
||||
pub struct MockExt {
|
||||
@@ -297,6 +304,7 @@ mod tests {
|
||||
rent_allowance: u64,
|
||||
instantiates: Vec<InstantiateEntry>,
|
||||
terminations: Vec<TerminationEntry>,
|
||||
calls: Vec<CallEntry>,
|
||||
transfers: Vec<TransferEntry>,
|
||||
restores: Vec<RestoreEntry>,
|
||||
// (topics, data)
|
||||
@@ -307,6 +315,11 @@ mod tests {
|
||||
debug_buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
/// The call is mocked and just returns this hardcoded value.
|
||||
fn call_return_data() -> Bytes {
|
||||
Bytes(vec![0xDE, 0xAD, 0xBE, 0xEF])
|
||||
}
|
||||
|
||||
impl Default for MockExt {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
@@ -314,6 +327,7 @@ mod tests {
|
||||
rent_allowance: Default::default(),
|
||||
instantiates: Default::default(),
|
||||
terminations: Default::default(),
|
||||
calls: Default::default(),
|
||||
transfers: Default::default(),
|
||||
restores: Default::default(),
|
||||
events: Default::default(),
|
||||
@@ -334,13 +348,15 @@ mod tests {
|
||||
to: AccountIdOf<Self::T>,
|
||||
value: u64,
|
||||
data: Vec<u8>,
|
||||
allows_reentry: bool,
|
||||
) -> Result<(ExecReturnValue, u32), (ExecError, u32)> {
|
||||
self.transfers.push(TransferEntry {
|
||||
self.calls.push(CallEntry {
|
||||
to,
|
||||
value,
|
||||
data: data,
|
||||
data,
|
||||
allows_reentry,
|
||||
});
|
||||
Ok((ExecReturnValue { flags: ReturnFlags::empty(), data: Bytes(Vec::new()) }, 0))
|
||||
Ok((ExecReturnValue { flags: ReturnFlags::empty(), data: call_return_data() }, 0))
|
||||
}
|
||||
fn instantiate(
|
||||
&mut self,
|
||||
@@ -374,7 +390,6 @@ mod tests {
|
||||
self.transfers.push(TransferEntry {
|
||||
to: to.clone(),
|
||||
value,
|
||||
data: Vec::new(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
@@ -526,7 +541,6 @@ mod tests {
|
||||
&[TransferEntry {
|
||||
to: ALICE,
|
||||
value: 153,
|
||||
data: Vec::new(),
|
||||
}]
|
||||
);
|
||||
}
|
||||
@@ -587,11 +601,192 @@ mod tests {
|
||||
));
|
||||
|
||||
assert_eq!(
|
||||
&mock_ext.transfers,
|
||||
&[TransferEntry {
|
||||
&mock_ext.calls,
|
||||
&[CallEntry {
|
||||
to: ALICE,
|
||||
value: 6,
|
||||
data: vec![1, 2, 3, 4],
|
||||
allows_reentry: true,
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
fn contract_call_forward_input() {
|
||||
const CODE: &str = r#"
|
||||
(module
|
||||
(import "__unstable__" "seal_call" (func $seal_call (param i32 i32 i32 i64 i32 i32 i32 i32 i32 i32) (result i32)))
|
||||
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
(func (export "call")
|
||||
(drop
|
||||
(call $seal_call
|
||||
(i32.const 1) ;; Set FORWARD_INPUT bit
|
||||
(i32.const 4) ;; Pointer to "callee" address.
|
||||
(i32.const 32) ;; Length of "callee" address.
|
||||
(i64.const 0) ;; How much gas to devote for the execution. 0 = all.
|
||||
(i32.const 36) ;; Pointer to the buffer with value to transfer
|
||||
(i32.const 8) ;; Length of the buffer with value to transfer.
|
||||
(i32.const 44) ;; Pointer to input data buffer address
|
||||
(i32.const 4) ;; Length of input data buffer
|
||||
(i32.const 4294967295) ;; u32 max value is the sentinel value: do not copy output
|
||||
(i32.const 0) ;; Length is ignored in this case
|
||||
)
|
||||
)
|
||||
|
||||
;; triggers a trap because we already forwarded the input
|
||||
(call $seal_input (i32.const 1) (i32.const 44))
|
||||
)
|
||||
|
||||
(func (export "deploy"))
|
||||
|
||||
;; Destination AccountId (ALICE)
|
||||
(data (i32.const 4)
|
||||
"\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
|
||||
"\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
|
||||
)
|
||||
|
||||
;; Amount of value to transfer.
|
||||
;; Represented by u64 (8 bytes long) in little endian.
|
||||
(data (i32.const 36) "\2A\00\00\00\00\00\00\00")
|
||||
|
||||
;; The input is ignored because we forward our own input
|
||||
(data (i32.const 44) "\01\02\03\04")
|
||||
)
|
||||
"#;
|
||||
let mut mock_ext = MockExt::default();
|
||||
let input = vec![0xff, 0x2a, 0x99, 0x88];
|
||||
frame_support::assert_err!(
|
||||
execute(CODE, input.clone(), &mut mock_ext),
|
||||
<Error<Test>>::InputForwarded,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
&mock_ext.calls,
|
||||
&[CallEntry {
|
||||
to: ALICE,
|
||||
value: 0x2a,
|
||||
data: input,
|
||||
allows_reentry: false,
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
fn contract_call_clone_input() {
|
||||
const CODE: &str = r#"
|
||||
(module
|
||||
(import "__unstable__" "seal_call" (func $seal_call (param i32 i32 i32 i64 i32 i32 i32 i32 i32 i32) (result i32)))
|
||||
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
|
||||
(import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
(func (export "call")
|
||||
(drop
|
||||
(call $seal_call
|
||||
(i32.const 11) ;; Set FORWARD_INPUT | CLONE_INPUT | ALLOW_REENTRY bits
|
||||
(i32.const 4) ;; Pointer to "callee" address.
|
||||
(i32.const 32) ;; Length of "callee" address.
|
||||
(i64.const 0) ;; How much gas to devote for the execution. 0 = all.
|
||||
(i32.const 36) ;; Pointer to the buffer with value to transfer
|
||||
(i32.const 8) ;; Length of the buffer with value to transfer.
|
||||
(i32.const 44) ;; Pointer to input data buffer address
|
||||
(i32.const 4) ;; Length of input data buffer
|
||||
(i32.const 4294967295) ;; u32 max value is the sentinel value: do not copy output
|
||||
(i32.const 0) ;; Length is ignored in this case
|
||||
)
|
||||
)
|
||||
|
||||
;; works because the input was cloned
|
||||
(call $seal_input (i32.const 0) (i32.const 44))
|
||||
|
||||
;; return the input to caller for inspection
|
||||
(call $seal_return (i32.const 0) (i32.const 0) (i32.load (i32.const 44)))
|
||||
)
|
||||
|
||||
(func (export "deploy"))
|
||||
|
||||
;; Destination AccountId (ALICE)
|
||||
(data (i32.const 4)
|
||||
"\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
|
||||
"\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
|
||||
)
|
||||
|
||||
;; Amount of value to transfer.
|
||||
;; Represented by u64 (8 bytes long) in little endian.
|
||||
(data (i32.const 36) "\2A\00\00\00\00\00\00\00")
|
||||
|
||||
;; The input is ignored because we forward our own input
|
||||
(data (i32.const 44) "\01\02\03\04")
|
||||
)
|
||||
"#;
|
||||
let mut mock_ext = MockExt::default();
|
||||
let input = vec![0xff, 0x2a, 0x99, 0x88];
|
||||
let result = execute(CODE, input.clone(), &mut mock_ext).unwrap();
|
||||
assert_eq!(result.data.0, input);
|
||||
assert_eq!(
|
||||
&mock_ext.calls,
|
||||
&[CallEntry {
|
||||
to: ALICE,
|
||||
value: 0x2a,
|
||||
data: input,
|
||||
allows_reentry: true,
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
fn contract_call_tail_call() {
|
||||
const CODE: &str = r#"
|
||||
(module
|
||||
(import "__unstable__" "seal_call" (func $seal_call (param i32 i32 i32 i64 i32 i32 i32 i32 i32 i32) (result i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
(func (export "call")
|
||||
(drop
|
||||
(call $seal_call
|
||||
(i32.const 5) ;; Set FORWARD_INPUT | TAIL_CALL bit
|
||||
(i32.const 4) ;; Pointer to "callee" address.
|
||||
(i32.const 32) ;; Length of "callee" address.
|
||||
(i64.const 0) ;; How much gas to devote for the execution. 0 = all.
|
||||
(i32.const 36) ;; Pointer to the buffer with value to transfer
|
||||
(i32.const 8) ;; Length of the buffer with value to transfer.
|
||||
(i32.const 0) ;; Pointer to input data buffer address
|
||||
(i32.const 0) ;; Length of input data buffer
|
||||
(i32.const 4294967295) ;; u32 max value is the sentinel value: do not copy output
|
||||
(i32.const 0) ;; Length is ignored in this case
|
||||
)
|
||||
)
|
||||
|
||||
;; a tail call never returns
|
||||
(unreachable)
|
||||
)
|
||||
|
||||
(func (export "deploy"))
|
||||
|
||||
;; Destination AccountId (ALICE)
|
||||
(data (i32.const 4)
|
||||
"\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
|
||||
"\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
|
||||
)
|
||||
|
||||
;; Amount of value to transfer.
|
||||
;; Represented by u64 (8 bytes long) in little endian.
|
||||
(data (i32.const 36) "\2A\00\00\00\00\00\00\00")
|
||||
)
|
||||
"#;
|
||||
let mut mock_ext = MockExt::default();
|
||||
let input = vec![0xff, 0x2a, 0x99, 0x88];
|
||||
let result = execute(CODE, input.clone(), &mut mock_ext).unwrap();
|
||||
assert_eq!(result.data, call_return_data());
|
||||
assert_eq!(
|
||||
&mock_ext.calls,
|
||||
&[CallEntry {
|
||||
to: ALICE,
|
||||
value: 0x2a,
|
||||
data: input,
|
||||
allows_reentry: false,
|
||||
}]
|
||||
);
|
||||
}
|
||||
@@ -772,11 +967,12 @@ mod tests {
|
||||
));
|
||||
|
||||
assert_eq!(
|
||||
&mock_ext.transfers,
|
||||
&[TransferEntry {
|
||||
&mock_ext.calls,
|
||||
&[CallEntry {
|
||||
to: ALICE,
|
||||
value: 6,
|
||||
data: vec![1, 2, 3, 4],
|
||||
allows_reentry: true,
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user