contracts: Allow contracts to dispatch calls into the runtime (#9276)

* contracts: Allow contracts to dispatch calls into the runtime

* Fix RPC tests

* Fix typo

* Replace () by AllowAllFilter and DenyAllFilter

* Add rust doc

* Fixup for `()` removal

* Fix lowest gas calculation

* Rename AllowAllFilter and DenyAllFilter

* Updated changelog
This commit is contained in:
Alexander Theißen
2021-07-12 22:40:27 +02:00
committed by GitHub
parent 2f31602896
commit e01ac8cea0
80 changed files with 674 additions and 107 deletions
+93 -6
View File
@@ -254,18 +254,22 @@ mod tests {
rent::RentStatus,
tests::{Test, Call, ALICE, BOB},
};
use std::collections::HashMap;
use std::{
borrow::BorrowMut,
cell::RefCell,
collections::HashMap,
};
use sp_core::{Bytes, H256};
use hex_literal::hex;
use sp_runtime::DispatchError;
use frame_support::{assert_ok, dispatch::DispatchResult, weights::Weight};
use frame_support::{
assert_ok,
dispatch::{DispatchResult, DispatchResultWithPostInfo},
weights::Weight,
};
use assert_matches::assert_matches;
use pallet_contracts_primitives::{ExecReturnValue, ReturnFlags};
use pretty_assertions::assert_eq;
use sp_std::borrow::BorrowMut;
#[derive(Debug, PartialEq, Eq)]
struct DispatchEntry(Call);
#[derive(Debug, PartialEq, Eq)]
struct RestoreEntry {
@@ -313,6 +317,7 @@ mod tests {
restores: Vec<RestoreEntry>,
// (topics, data)
events: Vec<(Vec<H256>, Vec<u8>)>,
runtime_calls: RefCell<Vec<Call>>,
schedule: Schedule<Test>,
rent_params: RentParams<Test>,
gas_meter: GasMeter<Test>,
@@ -335,6 +340,7 @@ mod tests {
transfers: Default::default(),
restores: Default::default(),
events: Default::default(),
runtime_calls: Default::default(),
schedule: Default::default(),
rent_params: Default::default(),
gas_meter: GasMeter::new(10_000_000_000),
@@ -481,6 +487,10 @@ mod tests {
self.debug_buffer.extend(msg.as_bytes());
true
}
fn call_runtime(&self, call: <Self::T as Config>::Call) -> DispatchResultWithPostInfo {
self.runtime_calls.borrow_mut().push(call);
Ok(Default::default())
}
}
fn execute<E: BorrowMut<MockExt>>(
@@ -2160,4 +2170,81 @@ mod tests {
})
);
}
#[cfg(feature = "unstable-interface")]
const CODE_CALL_RUNTIME: &str = r#"
(module
(import "__unstable__" "seal_call_runtime" (func $seal_call_runtime (param 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))
;; 0x1000 = 4k in little endian
;; size of input buffer
(data (i32.const 0) "\00\10")
(func (export "call")
;; Receive the encoded call
(call $seal_input
(i32.const 4) ;; Pointer to the input buffer
(i32.const 0) ;; Size of the length buffer
)
;; Just use the call passed as input and store result to memory
(i32.store (i32.const 0)
(call $seal_call_runtime
(i32.const 4) ;; Pointer where the call is stored
(i32.load (i32.const 0)) ;; Size of the call
)
)
(call $seal_return
(i32.const 0) ;; flags
(i32.const 0) ;; returned value
(i32.const 4) ;; length of returned value
)
)
(func (export "deploy"))
)
"#;
#[test]
#[cfg(feature = "unstable-interface")]
fn call_runtime_works() {
use std::convert::TryInto;
let call = Call::System(frame_system::Call::remark(b"Hello World".to_vec()));
let mut ext = MockExt::default();
let result = execute(
CODE_CALL_RUNTIME,
call.encode(),
&mut ext,
).unwrap();
assert_eq!(
*ext.runtime_calls.borrow(),
vec![call],
);
// 0 = ReturnCode::Success
assert_eq!(u32::from_le_bytes(result.data.0.try_into().unwrap()), 0);
}
#[test]
#[cfg(feature = "unstable-interface")]
fn call_runtime_panics_on_invalid_call() {
let mut ext = MockExt::default();
let result = execute(
CODE_CALL_RUNTIME,
vec![0x42],
&mut ext,
);
assert_eq!(
result,
Err(ExecError {
error: Error::<Test>::DecodingFailed.into(),
origin: ErrorOrigin::Caller,
})
);
assert_eq!(
*ext.runtime_calls.borrow(),
vec![],
);
}
}