mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 05:51:02 +00:00
Adds validate_block support for runtime
This commit is contained in:
Generated
+555
-116
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -1,2 +1,2 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = [ "consensus" ]
|
members = [ "consensus", "runtime" ]
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name = "cumulus-consensus"
|
name = "cumulus-consensus"
|
||||||
description = "Proxy Polkadot's consensus as a consensus engine for Substrate"
|
description = "Proxy Polkadot's consensus as a consensus engine for Substrate"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Parity Technologies"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
@@ -20,5 +20,5 @@ polkadot-runtime = { git = "https://github.com/paritytech/polkadot" }
|
|||||||
# other deps
|
# other deps
|
||||||
futures = "0.1.21"
|
futures = "0.1.21"
|
||||||
tokio = "0.1.8"
|
tokio = "0.1.8"
|
||||||
parity-codec = "3.0"
|
parity-codec = "3.1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
[package]
|
||||||
|
name = "runtime"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
codec = { package = "parity-codec", version = "3.1", default-features = false, features = [ "derive" ] }
|
||||||
|
rstd = { package = "sr-std", git = "https://github.com/paritytech/substrate", default-features = false, branch = "bkchr-validate_block" }
|
||||||
|
runtime-primitives = { package = "sr-primitives", git = "https://github.com/paritytech/substrate", default-features = false, branch = "bkchr-validate_block" }
|
||||||
|
rio = { package = "sr-io", git = "https://github.com/paritytech/substrate", default-features = false, branch = "bkchr-validate_block" }
|
||||||
|
executive = { package = "srml-executive", git = "https://github.com/paritytech/substrate", default-features = false, branch = "bkchr-validate_block" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
keyring = { package = "substrate-keyring", git = "https://github.com/paritytech/substrate", branch = "bkchr-validate_block" }
|
||||||
|
primitives = { package = "substrate-primitives", git = "https://github.com/paritytech/substrate", branch = "bkchr-validate_block" }
|
||||||
|
executor = { package = "substrate-executor", git = "https://github.com/paritytech/substrate", branch = "bkchr-validate_block" }
|
||||||
|
test-runtime = { package = "substrate-test-runtime", git = "https://github.com/paritytech/substrate", branch = "bkchr-validate_block" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["std"]
|
||||||
|
std = [
|
||||||
|
"codec/std",
|
||||||
|
"rstd/std",
|
||||||
|
"rio/std",
|
||||||
|
"runtime-primitives/std",
|
||||||
|
"executive/std",
|
||||||
|
]
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Cumulus.
|
||||||
|
|
||||||
|
// Cumulus is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Cumulus is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
|
use rstd::{vec::Vec, collections::btree_map::BTreeMap};
|
||||||
|
use codec::{Encode, Decode};
|
||||||
|
use runtime_primitives::traits::Block as BlockT;
|
||||||
|
|
||||||
|
pub mod validate_block;
|
||||||
|
|
||||||
|
type WitnessData = BTreeMap<Vec<u8>, Vec<u8>>;
|
||||||
|
|
||||||
|
/// The parachain block that is created on a collator and validated by a validator.
|
||||||
|
#[derive(Encode, Decode)]
|
||||||
|
struct ParachainBlock<B: BlockT> {
|
||||||
|
extrinsics: Vec<<B as BlockT>::Extrinsic>,
|
||||||
|
/// The data that is required to emulate the storage accesses executed by all extrinsics.
|
||||||
|
witness_data: WitnessData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: BlockT> ParachainBlock<B> {
|
||||||
|
#[cfg(test)]
|
||||||
|
fn new(extrinsics: Vec<<B as BlockT>::Extrinsic>, witness_data: WitnessData) -> Self {
|
||||||
|
Self {
|
||||||
|
extrinsics,
|
||||||
|
witness_data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: BlockT> Default for ParachainBlock<B> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
extrinsics: Vec::default(),
|
||||||
|
witness_data: BTreeMap::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// Substrate is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Substrate is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! A module that enables a runtime to work as parachain.
|
||||||
|
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use rstd::slice;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub mod storage_functions;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
/// Register the `validate_block` function that is used by parachains to validate blocks on a validator.
|
||||||
|
///
|
||||||
|
/// Does *nothing* when `std` feature is enabled.
|
||||||
|
///
|
||||||
|
/// Expects as parameters the block and the block executor.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// struct Block;
|
||||||
|
/// struct BlockExecutor;
|
||||||
|
///
|
||||||
|
/// srml_parachain::register_validate_block!(Block, BlockExecutor);
|
||||||
|
///
|
||||||
|
/// # fn main() {}
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! register_validate_block {
|
||||||
|
($block:ty, $block_executor:ty) => {
|
||||||
|
$crate::register_validate_block_impl!($block, $block_executor);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The actual implementation of `register_validate_block` for `no_std`.
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! register_validate_block_impl {
|
||||||
|
($block:ty, $block_executor:ty) => {
|
||||||
|
#[doc(hidden)]
|
||||||
|
mod parachain_validate_block {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe fn validate_block(block: *const u8, block_len: u64, prev_head: *const u8, prev_head_len: u64) {
|
||||||
|
let block = $crate::slice::from_raw_parts(block, block_len as usize);
|
||||||
|
let prev_head = $crate::slice::from_raw_parts(prev_head, prev_head_len as usize);
|
||||||
|
|
||||||
|
$crate::validate_block::validate_block::<$block, $block_executor>(block, prev_head);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The actual implementation of `register_validate_block` for `std`.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! register_validate_block_impl {
|
||||||
|
($block:ty, $block_executor:ty) => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validate a given parachain block on a validator.
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn validate_block<Block: BlockT, E: ExecuteBlock<Block>>(mut block: &[u8], mut prev_head: &[u8]) {
|
||||||
|
use codec::Decode;
|
||||||
|
|
||||||
|
let block = ParachainBlock::<Block>::decode(&mut block).expect("Could not decode parachain block.");
|
||||||
|
let parent_header = <<Block as BlockT>::Header as Decode>::decode(&mut prev_head).expect("Could not decode parent header.");
|
||||||
|
|
||||||
|
let _guard = unsafe {
|
||||||
|
use storage_functions as storage;
|
||||||
|
STORAGE = Some(block.witness_data);
|
||||||
|
(
|
||||||
|
// Replace storage calls with our own implementations
|
||||||
|
rio::ext_get_allocated_storage.replace_implementation(storage::ext_get_allocated_storage),
|
||||||
|
rio::ext_get_storage_into.replace_implementation(storage::ext_get_storage_into),
|
||||||
|
rio::ext_set_storage.replace_implementation(storage::ext_set_storage),
|
||||||
|
rio::ext_exists_storage.replace_implementation(storage::ext_exists_storage),
|
||||||
|
rio::ext_clear_storage.replace_implementation(storage::ext_clear_storage),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let block_number = *parent_header.number() + One::one();
|
||||||
|
E::execute_extrinsics_without_checks(block_number, block.extrinsics);
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// Substrate is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Substrate is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! All storage functions that are replaced by `validate_block` in the Substrate runtime.
|
||||||
|
|
||||||
|
use crate::{ParachainBlock, WitnessData};
|
||||||
|
use runtime_primitives::traits::{Block as BlockT, One, Header as HeaderT};
|
||||||
|
use rstd::{slice, ptr, cmp};
|
||||||
|
use codec::Decode;
|
||||||
|
use executive::ExecuteBlock;
|
||||||
|
|
||||||
|
pub static mut STORAGE: Option<WitnessData> = None;
|
||||||
|
const STORAGE_SET_EXPECT: &str = "`STORAGE` needs to be set before calling this function.";
|
||||||
|
|
||||||
|
pub unsafe fn ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8 {
|
||||||
|
let key = slice::from_raw_parts(key_data, key_len as usize);
|
||||||
|
match STORAGE.as_mut().expect(STORAGE_SET_EXPECT).get_mut(key) {
|
||||||
|
Some(value) => {
|
||||||
|
*written_out = value.len() as u32;
|
||||||
|
value.as_mut_ptr()
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
*written_out = u32::max_value();
|
||||||
|
ptr::null_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn ext_set_storage(key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32) {
|
||||||
|
let key = slice::from_raw_parts(key_data, key_len as usize);
|
||||||
|
let value = slice::from_raw_parts(value_data, value_len as usize);
|
||||||
|
|
||||||
|
STORAGE.as_mut().map(|s| {
|
||||||
|
s.insert(key.to_vec(), value.to_vec());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32, value_offset: u32) -> u32 {
|
||||||
|
let key = slice::from_raw_parts(key_data, key_len as usize);
|
||||||
|
let out_value = slice::from_raw_parts_mut(value_data, value_len as usize);
|
||||||
|
|
||||||
|
match STORAGE.as_mut().expect(STORAGE_SET_EXPECT).get_mut(key) {
|
||||||
|
Some(value) => {
|
||||||
|
let value = &value[value_offset as usize..];
|
||||||
|
let len = cmp::min(value_len as usize, value.len());
|
||||||
|
out_value[..len].copy_from_slice(&value[..len]);
|
||||||
|
len as u32
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
u32::max_value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn ext_exists_storage(key_data: *const u8, key_len: u32) -> u32 {
|
||||||
|
let key = slice::from_raw_parts(key_data, key_len as usize);
|
||||||
|
|
||||||
|
if STORAGE.as_mut().expect(STORAGE_SET_EXPECT).contains_key(key) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn ext_clear_storage(key_data: *const u8, key_len: u32) {
|
||||||
|
let key = slice::from_raw_parts(key_data, key_len as usize);
|
||||||
|
|
||||||
|
STORAGE.as_mut().expect(STORAGE_SET_EXPECT).remove(key);
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
use crate::ParachainBlock;
|
||||||
|
|
||||||
|
use rio::{twox_128, TestExternalities};
|
||||||
|
use keyring::Keyring;
|
||||||
|
use primitives::map;
|
||||||
|
use runtime_primitives::traits::Block as BlockT;
|
||||||
|
use executor::{WasmExecutor, error::Result, wasmi::RuntimeValue::{I64, I32}};
|
||||||
|
use test_runtime::{Block, Header, Transfer};
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use codec::{KeyedVec, Encode};
|
||||||
|
|
||||||
|
const WASM_CODE: &'static [u8] =
|
||||||
|
include_bytes!("../../../core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm");
|
||||||
|
|
||||||
|
fn create_witness_data() -> BTreeMap<Vec<u8>, Vec<u8>> {
|
||||||
|
map![
|
||||||
|
twox_128(&Keyring::Alice.to_raw_public().to_keyed_vec(b"balance:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call_validate_block(block: ParachainBlock<Block>, prev_header: <Block as BlockT>::Header) -> Result<()> {
|
||||||
|
let mut ext = TestExternalities::default();
|
||||||
|
WasmExecutor::new().call_with_custom_signature(
|
||||||
|
&mut ext,
|
||||||
|
8,
|
||||||
|
&WASM_CODE,
|
||||||
|
"validate_block",
|
||||||
|
|alloc| {
|
||||||
|
let block = block.encode();
|
||||||
|
let prev_header = prev_header.encode();
|
||||||
|
let block_offset = alloc(&block)?;
|
||||||
|
let prev_head_offset = alloc(&prev_header)?;
|
||||||
|
|
||||||
|
Ok(
|
||||||
|
vec![
|
||||||
|
I32(block_offset as i32),
|
||||||
|
I64(block.len() as i64),
|
||||||
|
I32(prev_head_offset as i32),
|
||||||
|
I64(prev_header.len() as i64),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|res, _| {
|
||||||
|
if res.is_none() {
|
||||||
|
Ok(Some(()))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_extrinsics() -> Vec<<Block as BlockT>::Extrinsic> {
|
||||||
|
vec![
|
||||||
|
Transfer {
|
||||||
|
from: Keyring::Alice.to_raw_public().into(),
|
||||||
|
to: Keyring::Bob.to_raw_public().into(),
|
||||||
|
amount: 69,
|
||||||
|
nonce: 0,
|
||||||
|
}.into_signed_tx()
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_prev_header() -> Header {
|
||||||
|
Header {
|
||||||
|
parent_hash: Default::default(),
|
||||||
|
number: 1,
|
||||||
|
state_root: Default::default(),
|
||||||
|
extrinsics_root: Default::default(),
|
||||||
|
digest: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validate_block_with_empty_block() {
|
||||||
|
let prev_header = create_prev_header();
|
||||||
|
call_validate_block(ParachainBlock::default(), prev_header).expect("Calls `validate_block`");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validate_block_with_empty_witness_data() {
|
||||||
|
let prev_header = create_prev_header();
|
||||||
|
|
||||||
|
let block = ParachainBlock::new(create_extrinsics(), Default::default());
|
||||||
|
assert!(call_validate_block(block, prev_header).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validate_block_with_witness_data() {
|
||||||
|
let prev_header = create_prev_header();
|
||||||
|
|
||||||
|
let block = ParachainBlock::new(create_extrinsics(), create_witness_data());
|
||||||
|
call_validate_block(block, prev_header).expect("`validate_block` succeeds");
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user