One node two runtimes (#191)

* One node two runtimes

This enables the rococo-collator to run the normal and the contracts runtime.

* Fix tests
This commit is contained in:
Bastian Köcher
2020-08-11 11:35:54 +02:00
committed by GitHub
parent 3ed6030110
commit 3b71c2a6e2
33 changed files with 1958 additions and 1466 deletions
+16
View File
@@ -976,6 +976,7 @@ dependencies = [
"parachain-info",
"parity-scale-codec",
"polkadot-parachain",
"rococo-parachain-primitives",
"serde",
"sp-api",
"sp-block-builder",
@@ -1213,6 +1214,7 @@ dependencies = [
"parachain-info",
"parity-scale-codec",
"polkadot-parachain",
"rococo-parachain-primitives",
"serde",
"sp-api",
"sp-block-builder",
@@ -5831,6 +5833,7 @@ dependencies = [
"polkadot-test-runtime-client",
"polkadot-test-service",
"rand 0.7.3",
"rococo-parachain-primitives",
"sc-basic-authorship",
"sc-chain-spec",
"sc-cli",
@@ -5840,15 +5843,20 @@ dependencies = [
"sc-finality-grandpa",
"sc-informant",
"sc-network",
"sc-rpc",
"sc-service",
"sc-transaction-pool",
"serde",
"sp-api",
"sp-block-builder",
"sp-blockchain",
"sp-consensus",
"sp-core",
"sp-inherents",
"sp-io",
"sp-offchain",
"sp-runtime",
"sp-session",
"sp-timestamp",
"sp-transaction-pool",
"sp-trie",
@@ -5860,6 +5868,14 @@ dependencies = [
"trie-root 0.15.2",
]
[[package]]
name = "rococo-parachain-primitives"
version = "0.1.0"
dependencies = [
"sp-core",
"sp-runtime",
]
[[package]]
name = "rococo-runtime"
version = "0.8.22"
+1
View File
@@ -9,6 +9,7 @@ members = [
"rococo-parachains/contracts-runtime",
"rococo-parachains/pallets/parachain-info",
"rococo-parachains/pallets/token-dealer",
"rococo-parachains/primitives",
"rococo-parachains/runtime",
"runtime",
"service",
+1 -2
View File
@@ -518,8 +518,7 @@ where
Box::new(self.polkadot_network.clone()),
)));
let follow =
match cumulus_consensus::follow_polkadot(
let follow = match cumulus_consensus::follow_polkadot(
self.para_id,
self.client,
polkadot_client,
+4 -1
View File
@@ -23,7 +23,10 @@ mod tests;
use sp_api::ProvideRuntimeApi;
use sp_blockchain::{Error as ClientError, HeaderBackend};
use sp_consensus::{block_validation::{BlockAnnounceValidator, Validation}, SyncOracle};
use sp_consensus::{
block_validation::{BlockAnnounceValidator, Validation},
SyncOracle,
};
use sp_core::traits::SpawnNamed;
use sp_runtime::{
generic::BlockId,
+3 -3
View File
@@ -17,9 +17,9 @@
use super::*;
use cumulus_test_runtime::{Block, Header};
use polkadot_primitives::v0::{
AbridgedCandidateReceipt, Chain, CollatorId, DutyRoster, GlobalValidationData,
Id as ParaId, LocalValidationData, ParachainHost, Retriable, SigningContext,
ValidationCode, ValidatorId, Block as PBlock, Hash as PHash, Header as PHeader,
AbridgedCandidateReceipt, Block as PBlock, Chain, CollatorId, DutyRoster, GlobalValidationData,
Hash as PHash, Header as PHeader, Id as ParaId, LocalValidationData, ParachainHost, Retriable,
SigningContext, ValidationCode, ValidatorId,
};
use polkadot_test_runtime_client::{
DefaultTestClientBuilderExt, TestClient, TestClientBuilder, TestClientBuilderExt,
+6
View File
@@ -25,6 +25,7 @@ hex-literal = "0.2.1"
# Parachain dependencies
parachain-runtime = { package = "cumulus-test-parachain-runtime", path = "runtime" }
parachain-contracts-runtime = { package = "cumulus-contracts-parachain-runtime", path = "contracts-runtime" }
rococo-parachain-primitives = { path = "primitives" }
# Substrate dependencies
sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "rococo-branch" }
@@ -32,6 +33,7 @@ sp-io = { git = "https://github.com/paritytech/substrate", branch = "rococo-bran
sp-core = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch" }
sp-inherents = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch" }
sp-consensus = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch" }
sp-session = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch" }
sc-consensus = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch" }
sc-cli = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch" }
sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch" }
@@ -42,11 +44,15 @@ sp-transaction-pool = { git = "https://github.com/paritytech/substrate", branch
sc-network = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch" }
sc-basic-authorship = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch", version = "0.8.0-rc5" }
sp-timestamp = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch" }
sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch" }
sp-block-builder = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch" }
sp-trie = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch" }
sp-api = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch" }
sc-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch" }
sc-informant = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch" }
sc-chain-spec = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch" }
sc-rpc = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch" }
sp-offchain = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch" }
# RPC related dependencies
cumulus-pallet-contracts-rpc = { path = "./pallets/contracts/rpc" }
@@ -10,6 +10,7 @@ codec = { package = "parity-scale-codec", version = "1.3.0", default-features =
cumulus-token-dealer = { path = "../pallets/token-dealer", default-features = false}
parachain-info = { path = "../pallets/parachain-info", default-features = false}
rococo-parachain-primitives = { path = "../primitives", default-features = false}
# Substrate dependencies
sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "rococo-branch" }
@@ -79,6 +80,7 @@ std = [
"pallet-sudo/std",
"pallet-transaction-payment/std",
"parachain-info/std",
"rococo-parachain-primitives/std",
"cumulus-runtime/std",
"cumulus-parachain-upgrade/std",
"cumulus-message-broker/std",
@@ -23,13 +23,14 @@
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
use cumulus_pallet_contracts_rpc_runtime_api::ContractExecResult;
use rococo_parachain_primitives::*;
use sp_api::impl_runtime_apis;
use sp_core::OpaqueMetadata;
use sp_runtime::{
create_runtime_str, generic, impl_opaque_keys,
traits::{BlakeTwo256, Block as BlockT, IdentifyAccount, IdentityLookup, Saturating, Verify},
traits::{BlakeTwo256, Block as BlockT, IdentityLookup, Saturating},
transaction_validity::{TransactionSource, TransactionValidity},
ApplyExtrinsicResult, MultiSignature,
ApplyExtrinsicResult,
};
use sp_std::prelude::*;
#[cfg(feature = "std")]
@@ -49,61 +50,18 @@ pub use pallet_timestamp::Call as TimestampCall;
pub use sp_runtime::BuildStorage;
pub use sp_runtime::{Perbill, Permill};
/// An index to a block.
pub type BlockNumber = u32;
/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
pub type Signature = MultiSignature;
/// Some way of identifying an account on the chain. We intentionally make it equivalent
/// to the public key of our transaction signing scheme.
pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
/// The type for looking up accounts. We don't expect more than 4 billion of them, but you
/// never know...
pub type AccountIndex = u32;
/// Balance of an account.
pub type Balance = u128;
/// Index of a transaction in the chain.
pub type Index = u32;
/// A hash of some data used by the chain.
pub type Hash = sp_core::H256;
/// Digest item type.
pub type DigestItem = generic::DigestItem<Hash>;
/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know
/// the specifics of the runtime. They can then be made to be agnostic over specific formats
/// of data like extrinsics, allowing for them to continue syncing the network through upgrades
/// to even the core datastructures.
pub mod opaque {
use super::*;
pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;
/// Opaque block header type.
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
/// Opaque block type.
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
/// Opaque block identifier type.
pub type BlockId = generic::BlockId<Block>;
pub type SessionHandlers = ();
impl_opaque_keys! {
pub struct SessionKeys {}
}
}
/// This runtime version.
pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("cumulus-contracts-parachain"),
impl_name: create_runtime_str!("cumulus-contracts-parachain"),
authoring_version: 1,
spec_version: 1,
spec_version: 2,
impl_version: 1,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
@@ -292,7 +250,7 @@ impl cumulus_pallet_contracts::Trait for Runtime {
construct_runtime! {
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
NodeBlock = rococo_parachain_primitives::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
System: frame_system::{Module, Call, Storage, Config, Event<T>},
@@ -405,11 +363,11 @@ impl_runtime_apis! {
fn decode_session_keys(
encoded: Vec<u8>,
) -> Option<Vec<(Vec<u8>, sp_core::crypto::KeyTypeId)>> {
opaque::SessionKeys::decode_into_raw_public_keys(&encoded)
SessionKeys::decode_into_raw_public_keys(&encoded)
}
fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {
opaque::SessionKeys::generate(seed)
SessionKeys::generate(seed)
}
}
@@ -20,9 +20,9 @@
use std::sync::Arc;
use codec::Codec;
use cumulus_pallet_contracts_primitives::RentProjection;
use jsonrpc_core::{Error, ErrorCode, Result};
use jsonrpc_derive::rpc;
use cumulus_pallet_contracts_primitives::RentProjection;
use serde::{Deserialize, Serialize};
use sp_api::ProvideRuntimeApi;
use sp_blockchain::HeaderBackend;
@@ -106,7 +106,11 @@ pub enum RpcContractExecResult {
impl From<ContractExecResult> for RpcContractExecResult {
fn from(r: ContractExecResult) -> Self {
match r {
ContractExecResult::Success { flags, data, gas_consumed } => RpcContractExecResult::Success {
ContractExecResult::Success {
flags,
data,
gas_consumed,
} => RpcContractExecResult::Success {
flags,
data: data.into(),
gas_consumed,
@@ -293,7 +297,8 @@ mod tests {
#[test]
fn call_request_should_serialize_deserialize_properly() {
type Req = CallRequest<String, u128>;
let req: Req = serde_json::from_str(r#"
let req: Req = serde_json::from_str(
r#"
{
"origin": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL",
"dest": "5DRakbLVnjVrW6niwLfHGW24EeCEvDAFGEXrtaYS5M4ynoom",
@@ -301,7 +306,9 @@ mod tests {
"gasLimit": 1000000000000,
"inputData": "0x8c97db39"
}
"#).unwrap();
"#,
)
.unwrap();
assert_eq!(req.gas_limit.into_u256(), U256::from(0xe8d4a51000u64));
}
@@ -21,14 +21,19 @@ use super::{
TrieIdGenerator,
};
use crate::exec::StorageKey;
use sp_std::cell::RefCell;
use sp_std::collections::btree_map::{BTreeMap, Entry};
use sp_std::prelude::*;
use frame_support::{
storage::unhashed as storage,
traits::{Currency, Imbalance, SignedImbalance},
StorageMap,
};
use frame_system;
use sp_io::hashing::blake2_256;
use sp_runtime::traits::{Bounded, Zero};
use frame_support::traits::{Currency, Imbalance, SignedImbalance};
use frame_support::{storage::unhashed as storage, StorageMap};
use frame_system;
use sp_std::{
cell::RefCell,
collections::btree_map::{BTreeMap, Entry},
prelude::*,
};
// Note: we don't provide Option<Contract> because we can't create
// the trie_id in the overlay, thus we provide an overlay on the fields
@@ -133,8 +138,7 @@ impl<T: Trait> AccountDb<T> for DirectAccountDb {
trie_id: Option<&TrieId>,
location: &StorageKey,
) -> Option<Vec<u8>> {
trie_id
.and_then(|id| storage::get_raw(&crate::prefixed_key(id, &blake2_256(location))))
trie_id.and_then(|id| storage::get_raw(&crate::prefixed_key(id, &blake2_256(location))))
}
fn get_code_hash(&self, account: &T::AccountId) -> Option<CodeHash<T>> {
<ContractInfoOf<T>>::get(account).and_then(|i| i.as_alive().map(|i| i.code_hash))
@@ -241,13 +245,13 @@ impl<T: Trait> AccountDb<T> for DirectAccountDb {
if prev_value.is_empty() {
new_info.empty_pair_count -= 1;
}
},
}
(None, Some(new_value)) => {
new_info.total_pair_count += 1;
if new_value.is_empty() {
new_info.empty_pair_count += 1;
}
},
}
(Some(prev_value), Some(new_value)) => {
if prev_value.is_empty() {
new_info.empty_pair_count -= 1;
@@ -14,19 +14,23 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use super::{CodeHash, Config, ContractAddressFor, Event, RawEvent, Trait,
TrieId, BalanceOf, ContractInfo};
use crate::account_db::{AccountDb, DirectAccountDb, OverlayAccountDb};
use crate::gas::{Gas, GasMeter, Token};
use crate::rent;
use super::{
BalanceOf, CodeHash, Config, ContractAddressFor, ContractInfo, Event, RawEvent, Trait, TrieId,
};
use crate::{
account_db::{AccountDb, DirectAccountDb, OverlayAccountDb},
gas::{Gas, GasMeter, Token},
rent,
};
use sp_std::prelude::*;
use sp_runtime::traits::{Bounded, Zero, Convert, CheckedSub, CheckedAdd};
use frame_support::{
storage::unhashed, dispatch::DispatchError,
traits::{Currency, Time, Randomness, WithdrawReason},
dispatch::DispatchError,
storage::unhashed,
traits::{Currency, Randomness, Time, WithdrawReason},
weights::Weight,
};
use sp_runtime::traits::{Bounded, CheckedAdd, CheckedSub, Convert, Zero};
use sp_std::prelude::*;
pub type AccountIdOf<T> = <T as frame_system::Trait>::AccountId;
pub type CallOf<T> = <T as Trait>::Call;
@@ -84,11 +88,14 @@ macro_rules! try_or_exec_error {
($e:expr, $buffer:expr) => {
match $e {
Ok(val) => val,
Err(reason) => return Err(
$crate::exec::ExecError { reason: reason.into(), buffer: $buffer }
),
Err(reason) => {
return Err($crate::exec::ExecError {
reason: reason.into(),
buffer: $buffer,
})
}
}
};
}
/// An interface that provides access to the external environment in which the
@@ -332,9 +339,11 @@ where
}
}
fn nested<'b, 'c: 'b>(&'c self, dest: T::AccountId, trie_id: Option<TrieId>)
-> ExecutionContext<'b, T, V, L>
{
fn nested<'b, 'c: 'b>(
&'c self,
dest: T::AccountId,
trie_id: Option<TrieId>,
) -> ExecutionContext<'b, T, V, L> {
ExecutionContext {
caller: Some(self),
self_trie_id: trie_id,
@@ -355,7 +364,7 @@ where
&mut self,
dest: T::AccountId,
value: BalanceOf<T>,
gas_meter: &mut GasMeter<T>
gas_meter: &mut GasMeter<T>,
) -> Result<(), DispatchError> {
transfer(
gas_meter,
@@ -427,12 +436,9 @@ where
// it is a regular account since tombstone accounts have already been rejected.
match nested.overlay.get_code_hash(&dest) {
Some(dest_code_hash) => {
let executable = try_or_exec_error!(
nested.loader.load_main(&dest_code_hash),
input_data
);
let output = nested.vm
.execute(
let executable =
try_or_exec_error!(nested.loader.load_main(&dest_code_hash), input_data);
let output = nested.vm.execute(
&executable,
nested.new_call_context(caller, value),
input_data,
@@ -441,7 +447,10 @@ where
Ok(output)
}
None => Ok(ExecReturnValue { status: STATUS_SUCCESS, data: Vec::new() }),
None => Ok(ExecReturnValue {
status: STATUS_SUCCESS,
data: Vec::new(),
}),
}
})
}
@@ -471,18 +480,17 @@ where
}
let caller = self.self_account.clone();
let dest = T::DetermineContractAddress::contract_address_for(
code_hash,
&input_data,
&caller,
);
let dest =
T::DetermineContractAddress::contract_address_for(code_hash, &input_data, &caller);
// TrieId has not been generated yet and storage is empty since contract is new.
let dest_trie_id = None;
let output = self.with_nested_context(dest.clone(), dest_trie_id, |nested| {
try_or_exec_error!(
nested.overlay.instantiate_contract(&dest, code_hash.clone()),
nested
.overlay
.instantiate_contract(&dest, code_hash.clone()),
input_data
);
@@ -500,12 +508,8 @@ where
input_data
);
let executable = try_or_exec_error!(
nested.loader.load_init(&code_hash),
input_data
);
let output = nested.vm
.execute(
let executable = try_or_exec_error!(nested.loader.load_init(&code_hash), input_data);
let output = nested.vm.execute(
&executable,
nested.new_call_context(caller.clone(), endowment),
input_data,
@@ -574,9 +578,14 @@ where
}
}
fn with_nested_context<F>(&mut self, dest: T::AccountId, trie_id: Option<TrieId>, func: F)
-> ExecResult
where F: FnOnce(&mut ExecutionContext<T, V, L>) -> ExecResult
fn with_nested_context<F>(
&mut self,
dest: T::AccountId,
trie_id: Option<TrieId>,
func: F,
) -> ExecResult
where
F: FnOnce(&mut ExecutionContext<T, V, L>) -> ExecResult,
{
let (output, change_set, deferred) = {
let mut nested = self.nested(dest, trie_id);
@@ -595,8 +604,7 @@ where
/// Returns whether a contract, identified by address, is currently live in the execution
/// stack, meaning it is in the middle of an execution.
fn is_live(&self, account: &T::AccountId) -> bool {
&self.self_account == account ||
self.caller.map_or(false, |caller| caller.is_live(account))
&self.self_account == account || self.caller.map_or(false, |caller| caller.is_live(account))
}
}
@@ -656,8 +664,7 @@ fn transfer<'a, T: Trait, V: Vm<T>, L: Loader<T>>(
value: BalanceOf<T>,
ctx: &mut ExecutionContext<'a, T, V, L>,
) -> Result<(), DispatchError> {
use self::TransferCause::*;
use self::TransferFeeKind::*;
use self::{TransferCause::*, TransferFeeKind::*};
let token = {
let kind: TransferFeeKind = match cause {
@@ -668,9 +675,7 @@ fn transfer<'a, T: Trait, V: Vm<T>, L: Loader<T>>(
// Otherwise the fee is to transfer to an account.
Call | Terminate => TransferFeeKind::Transfer,
};
TransferFeeToken {
kind,
}
TransferFeeToken { kind }
};
if gas_meter.charge(ctx.config, token).is_out_of_gas() {
@@ -691,14 +696,15 @@ fn transfer<'a, T: Trait, V: Vm<T>, L: Loader<T>>(
// Only ext_terminate is allowed to bring the sender below the existential deposit
let required_balance = match cause {
Terminate => 0.into(),
_ => ctx.config.existential_deposit
_ => ctx.config.existential_deposit,
};
T::Currency::ensure_can_withdraw(
transactor,
value,
WithdrawReason::Transfer.into(),
new_from_balance.checked_sub(&required_balance)
new_from_balance
.checked_sub(&required_balance)
.ok_or("brings sender below existential deposit")?,
)?;
@@ -736,7 +742,9 @@ where
type T = T;
fn get_storage(&self, key: &StorageKey) -> Option<Vec<u8>> {
self.ctx.overlay.get_storage(&self.ctx.self_account, self.ctx.self_trie_id.as_ref(), key)
self.ctx
.overlay
.get_storage(&self.ctx.self_account, self.ctx.self_trie_id.as_ref(), key)
}
fn set_storage(&mut self, key: StorageKey, value: Option<Vec<u8>>) -> Result<(), &'static str> {
@@ -759,7 +767,8 @@ where
gas_meter: &mut GasMeter<T>,
input_data: Vec<u8>,
) -> Result<(AccountIdOf<T>, ExecReturnValue), ExecError> {
self.ctx.instantiate(endowment, gas_meter, code_hash, input_data)
self.ctx
.instantiate(endowment, gas_meter, code_hash, input_data)
}
fn transfer(
@@ -852,15 +861,21 @@ where
}
fn set_rent_allowance(&mut self, rent_allowance: BalanceOf<T>) {
self.ctx.overlay.set_rent_allowance(&self.ctx.self_account, rent_allowance)
self.ctx
.overlay
.set_rent_allowance(&self.ctx.self_account, rent_allowance)
}
fn rent_allowance(&self) -> BalanceOf<T> {
self.ctx.overlay.get_rent_allowance(&self.ctx.self_account)
self.ctx
.overlay
.get_rent_allowance(&self.ctx.self_account)
.unwrap_or(<BalanceOf<T>>::max_value()) // Must never be triggered actually
}
fn block_number(&self) -> T::BlockNumber { self.block_number }
fn block_number(&self) -> T::BlockNumber {
self.block_number
}
fn max_value_size(&self) -> u32 {
self.ctx.config.max_value_size
@@ -888,17 +903,19 @@ where
#[cfg(test)]
mod tests {
use super::{
BalanceOf, ExecFeeToken, ExecutionContext, Ext, Loader, TransferFeeKind, TransferFeeToken,
Vm, ExecResult, RawEvent, DeferredAction,
BalanceOf, DeferredAction, ExecFeeToken, ExecResult, ExecutionContext, Ext, Loader,
RawEvent, TransferFeeKind, TransferFeeToken, Vm,
};
use crate::{
account_db::AccountDb, gas::GasMeter, tests::{ExtBuilder, Test},
exec::{ExecReturnValue, ExecError, STATUS_SUCCESS}, CodeHash, Config,
gas::Gas,
account_db::AccountDb,
exec::{ExecError, ExecReturnValue, STATUS_SUCCESS},
gas::{Gas, GasMeter},
tests::{ExtBuilder, Test},
CodeHash, Config,
};
use std::{cell::RefCell, rc::Rc, collections::HashMap, marker::PhantomData};
use assert_matches::assert_matches;
use sp_runtime::DispatchError;
use std::{cell::RefCell, collections::HashMap, marker::PhantomData, rc::Rc};
const ALICE: u64 = 1;
const BOB: u64 = 2;
@@ -907,7 +924,8 @@ mod tests {
const GAS_LIMIT: Gas = 10_000_000_000;
impl<'a, T, V, L> ExecutionContext<'a, T, V, L>
where T: crate::Trait
where
T: crate::Trait,
{
fn events(&self) -> Vec<DeferredAction<T>> {
self.deferred
@@ -965,7 +983,9 @@ mod tests {
impl<'a> MockVm<'a> {
fn new() -> Self {
MockVm { _marker: PhantomData }
MockVm {
_marker: PhantomData,
}
}
}
@@ -1005,7 +1025,10 @@ mod tests {
}
fn exec_success() -> ExecResult {
Ok(ExecReturnValue { status: STATUS_SUCCESS, data: Vec::new() })
Ok(ExecReturnValue {
status: STATUS_SUCCESS,
data: Vec::new(),
})
}
#[test]
@@ -1029,10 +1052,7 @@ mod tests {
let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader);
ctx.overlay.instantiate_contract(&BOB, exec_ch).unwrap();
assert_matches!(
ctx.call(BOB, value, &mut gas_meter, data),
Ok(_)
);
assert_matches!(ctx.call(BOB, value, &mut gas_meter, data), Ok(_));
});
assert_eq!(&*test_data.borrow(), &vec![0, 1]);
@@ -1098,12 +1118,9 @@ mod tests {
ctx.overlay.set_balance(&origin, 100);
ctx.overlay.set_balance(&dest, 0);
let output = ctx.call(
dest,
55,
&mut GasMeter::<Test>::new(GAS_LIMIT),
vec![],
).unwrap();
let output = ctx
.call(dest, 55, &mut GasMeter::<Test>::new(GAS_LIMIT), vec![])
.unwrap();
assert!(output.is_success());
assert_eq!(ctx.overlay.get_balance(&origin), 45);
@@ -1120,9 +1137,12 @@ mod tests {
let vm = MockVm::new();
let mut loader = MockLoader::empty();
let return_ch = loader.insert(
|_| Ok(ExecReturnValue { status: 1, data: Vec::new() })
);
let return_ch = loader.insert(|_| {
Ok(ExecReturnValue {
status: 1,
data: Vec::new(),
})
});
ExtBuilder::default().build().execute_with(|| {
let cfg = Config::preload();
@@ -1131,12 +1151,9 @@ mod tests {
ctx.overlay.set_balance(&origin, 100);
ctx.overlay.set_balance(&dest, 0);
let output = ctx.call(
dest,
55,
&mut GasMeter::<Test>::new(GAS_LIMIT),
vec![],
).unwrap();
let output = ctx
.call(dest, 55, &mut GasMeter::<Test>::new(GAS_LIMIT), vec![])
.unwrap();
assert!(!output.is_success());
assert_eq!(ctx.overlay.get_balance(&origin), 100);
@@ -1152,7 +1169,10 @@ mod tests {
// This test sends 50 units of currency to a non-existent account.
// This should lead to creation of a new account thus
// a fee should be charged.
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
ExtBuilder::default()
.existential_deposit(15)
.build()
.execute_with(|| {
let vm = MockVm::new();
let loader = MockLoader::empty();
let cfg = Config::preload();
@@ -1177,7 +1197,10 @@ mod tests {
// This one is similar to the previous one but transfer to an existing account.
// In this test we expect that a regular transfer fee is charged.
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
ExtBuilder::default()
.existential_deposit(15)
.build()
.execute_with(|| {
let vm = MockVm::new();
let loader = MockLoader::empty();
let cfg = Config::preload();
@@ -1202,7 +1225,10 @@ mod tests {
// This test sends 50 units of currency as an endowment to a newly
// instantiated contract.
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
ExtBuilder::default()
.existential_deposit(15)
.build()
.execute_with(|| {
let mut loader = MockLoader::empty();
let code = loader.insert(|_| exec_success());
@@ -1244,12 +1270,7 @@ mod tests {
let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader);
ctx.overlay.set_balance(&origin, 0);
let result = ctx.call(
dest,
100,
&mut GasMeter::<Test>::new(GAS_LIMIT),
vec![],
);
let result = ctx.call(dest, 100, &mut GasMeter::<Test>::new(GAS_LIMIT), vec![]);
assert_matches!(
result,
@@ -1272,21 +1293,19 @@ mod tests {
let vm = MockVm::new();
let mut loader = MockLoader::empty();
let return_ch = loader.insert(
|_| Ok(ExecReturnValue { status: STATUS_SUCCESS, data: vec![1, 2, 3, 4] })
);
let return_ch = loader.insert(|_| {
Ok(ExecReturnValue {
status: STATUS_SUCCESS,
data: vec![1, 2, 3, 4],
})
});
ExtBuilder::default().build().execute_with(|| {
let cfg = Config::preload();
let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader);
ctx.overlay.instantiate_contract(&BOB, return_ch).unwrap();
let result = ctx.call(
dest,
0,
&mut GasMeter::<Test>::new(GAS_LIMIT),
vec![],
);
let result = ctx.call(dest, 0, &mut GasMeter::<Test>::new(GAS_LIMIT), vec![]);
let output = result.unwrap();
assert!(output.is_success());
@@ -1303,21 +1322,19 @@ mod tests {
let vm = MockVm::new();
let mut loader = MockLoader::empty();
let return_ch = loader.insert(
|_| Ok(ExecReturnValue { status: 1, data: vec![1, 2, 3, 4] })
);
let return_ch = loader.insert(|_| {
Ok(ExecReturnValue {
status: 1,
data: vec![1, 2, 3, 4],
})
});
ExtBuilder::default().build().execute_with(|| {
let cfg = Config::preload();
let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader);
ctx.overlay.instantiate_contract(&BOB, return_ch).unwrap();
let result = ctx.call(
dest,
0,
&mut GasMeter::<Test>::new(GAS_LIMIT),
vec![],
);
let result = ctx.call(dest, 0, &mut GasMeter::<Test>::new(GAS_LIMIT), vec![]);
let output = result.unwrap();
assert!(!output.is_success());
@@ -1338,7 +1355,9 @@ mod tests {
ExtBuilder::default().build().execute_with(|| {
let cfg = Config::preload();
let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader);
ctx.overlay.instantiate_contract(&BOB, input_data_ch).unwrap();
ctx.overlay
.instantiate_contract(&BOB, input_data_ch)
.unwrap();
let result = ctx.call(
BOB,
@@ -1415,12 +1434,7 @@ mod tests {
ctx.overlay.set_balance(&BOB, 1);
ctx.overlay.instantiate_contract(&BOB, recurse_ch).unwrap();
let result = ctx.call(
BOB,
value,
&mut GasMeter::<Test>::new(GAS_LIMIT),
vec![],
);
let result = ctx.call(BOB, value, &mut GasMeter::<Test>::new(GAS_LIMIT), vec![]);
assert_matches!(result, Ok(_));
});
@@ -1442,10 +1456,7 @@ mod tests {
*witnessed_caller_bob.borrow_mut() = Some(*ctx.ext.caller());
// Call into CHARLIE contract.
assert_matches!(
ctx.ext.call(&CHARLIE, 0, ctx.gas_meter, vec![]),
Ok(_)
);
assert_matches!(ctx.ext.call(&CHARLIE, 0, ctx.gas_meter, vec![]), Ok(_));
exec_success()
});
let charlie_ch = loader.insert(|ctx| {
@@ -1459,14 +1470,11 @@ mod tests {
let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader);
ctx.overlay.instantiate_contract(&dest, bob_ch).unwrap();
ctx.overlay.instantiate_contract(&CHARLIE, charlie_ch).unwrap();
ctx.overlay
.instantiate_contract(&CHARLIE, charlie_ch)
.unwrap();
let result = ctx.call(
dest,
0,
&mut GasMeter::<Test>::new(GAS_LIMIT),
vec![],
);
let result = ctx.call(dest, 0, &mut GasMeter::<Test>::new(GAS_LIMIT), vec![]);
assert_matches!(result, Ok(_));
});
@@ -1485,10 +1493,7 @@ mod tests {
assert_eq!(*ctx.ext.address(), BOB);
// Call into charlie contract.
assert_matches!(
ctx.ext.call(&CHARLIE, 0, ctx.gas_meter, vec![]),
Ok(_)
);
assert_matches!(ctx.ext.call(&CHARLIE, 0, ctx.gas_meter, vec![]), Ok(_));
exec_success()
});
let charlie_ch = loader.insert(|ctx| {
@@ -1500,14 +1505,11 @@ mod tests {
let cfg = Config::preload();
let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader);
ctx.overlay.instantiate_contract(&BOB, bob_ch).unwrap();
ctx.overlay.instantiate_contract(&CHARLIE, charlie_ch).unwrap();
ctx.overlay
.instantiate_contract(&CHARLIE, charlie_ch)
.unwrap();
let result = ctx.call(
BOB,
0,
&mut GasMeter::<Test>::new(GAS_LIMIT),
vec![],
);
let result = ctx.call(BOB, 0, &mut GasMeter::<Test>::new(GAS_LIMIT), vec![]);
assert_matches!(result, Ok(_));
});
@@ -1520,7 +1522,10 @@ mod tests {
let mut loader = MockLoader::empty();
let dummy_ch = loader.insert(|_| exec_success());
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
ExtBuilder::default()
.existential_deposit(15)
.build()
.execute_with(|| {
let cfg = Config::preload();
let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader);
@@ -1541,11 +1546,17 @@ mod tests {
let vm = MockVm::new();
let mut loader = MockLoader::empty();
let dummy_ch = loader.insert(
|_| Ok(ExecReturnValue { status: STATUS_SUCCESS, data: vec![80, 65, 83, 83] })
);
let dummy_ch = loader.insert(|_| {
Ok(ExecReturnValue {
status: STATUS_SUCCESS,
data: vec![80, 65, 83, 83],
})
});
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
ExtBuilder::default()
.existential_deposit(15)
.build()
.execute_with(|| {
let cfg = Config::preload();
let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader);
ctx.overlay.set_balance(&ALICE, 1000);
@@ -1562,8 +1573,15 @@ mod tests {
// Check that the newly created account has the expected code hash and
// there are instantiation event.
assert_eq!(ctx.overlay.get_code_hash(&instantiated_contract_address).unwrap(), dummy_ch);
assert_eq!(&ctx.events(), &[
assert_eq!(
ctx.overlay
.get_code_hash(&instantiated_contract_address)
.unwrap(),
dummy_ch
);
assert_eq!(
&ctx.events(),
&[
DeferredAction::DepositEvent {
event: RawEvent::Transfer(ALICE, instantiated_contract_address, 100),
topics: Vec::new(),
@@ -1572,7 +1590,8 @@ mod tests {
event: RawEvent::Instantiated(ALICE, instantiated_contract_address),
topics: Vec::new(),
}
]);
]
);
});
}
@@ -1581,11 +1600,17 @@ mod tests {
let vm = MockVm::new();
let mut loader = MockLoader::empty();
let dummy_ch = loader.insert(
|_| Ok(ExecReturnValue { status: 1, data: vec![70, 65, 73, 76] })
);
let dummy_ch = loader.insert(|_| {
Ok(ExecReturnValue {
status: 1,
data: vec![70, 65, 73, 76],
})
});
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
ExtBuilder::default()
.existential_deposit(15)
.build()
.execute_with(|| {
let cfg = Config::preload();
let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader);
ctx.overlay.set_balance(&ALICE, 1000);
@@ -1601,7 +1626,10 @@ mod tests {
);
// Check that the account has not been created.
assert!(ctx.overlay.get_code_hash(&instantiated_contract_address).is_none());
assert!(ctx
.overlay
.get_code_hash(&instantiated_contract_address)
.is_none());
assert!(ctx.events().is_empty());
});
}
@@ -1618,36 +1646,50 @@ mod tests {
let instantiated_contract_address = Rc::clone(&instantiated_contract_address);
move |ctx| {
// Instantiate a contract and save it's address in `instantiated_contract_address`.
let (address, output) = ctx.ext.instantiate(
&dummy_ch,
15u64,
ctx.gas_meter,
vec![]
).unwrap();
let (address, output) = ctx
.ext
.instantiate(&dummy_ch, 15u64, ctx.gas_meter, vec![])
.unwrap();
*instantiated_contract_address.borrow_mut() = address.into();
Ok(output)
}
});
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
ExtBuilder::default()
.existential_deposit(15)
.build()
.execute_with(|| {
let cfg = Config::preload();
let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader);
ctx.overlay.set_balance(&ALICE, 1000);
ctx.overlay.set_balance(&BOB, 100);
ctx.overlay.instantiate_contract(&BOB, instantiator_ch).unwrap();
ctx.overlay
.instantiate_contract(&BOB, instantiator_ch)
.unwrap();
assert_matches!(
ctx.call(BOB, 20, &mut GasMeter::<Test>::new(GAS_LIMIT), vec![]),
Ok(_)
);
let instantiated_contract_address = instantiated_contract_address.borrow().as_ref().unwrap().clone();
let instantiated_contract_address = instantiated_contract_address
.borrow()
.as_ref()
.unwrap()
.clone();
// Check that the newly created account has the expected code hash and
// there are instantiation event.
assert_eq!(ctx.overlay.get_code_hash(&instantiated_contract_address).unwrap(), dummy_ch);
assert_eq!(&ctx.events(), &[
assert_eq!(
ctx.overlay
.get_code_hash(&instantiated_contract_address)
.unwrap(),
dummy_ch
);
assert_eq!(
&ctx.events(),
&[
DeferredAction::DepositEvent {
event: RawEvent::Transfer(ALICE, BOB, 20),
topics: Vec::new(),
@@ -1660,7 +1702,8 @@ mod tests {
event: RawEvent::Instantiated(BOB, instantiated_contract_address),
topics: Vec::new(),
},
]);
]
);
});
}
@@ -1669,33 +1712,39 @@ mod tests {
let vm = MockVm::new();
let mut loader = MockLoader::empty();
let dummy_ch = loader.insert(
|_| Err(ExecError { reason: "It's a trap!".into(), buffer: Vec::new() })
);
let dummy_ch = loader.insert(|_| {
Err(ExecError {
reason: "It's a trap!".into(),
buffer: Vec::new(),
})
});
let instantiator_ch = loader.insert({
let dummy_ch = dummy_ch.clone();
move |ctx| {
// Instantiate a contract and save it's address in `instantiated_contract_address`.
assert_matches!(
ctx.ext.instantiate(
&dummy_ch,
15u64,
ctx.gas_meter,
vec![]
),
Err(ExecError { reason: DispatchError::Other("It's a trap!"), buffer: _ })
ctx.ext.instantiate(&dummy_ch, 15u64, ctx.gas_meter, vec![]),
Err(ExecError {
reason: DispatchError::Other("It's a trap!"),
buffer: _,
})
);
exec_success()
}
});
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
ExtBuilder::default()
.existential_deposit(15)
.build()
.execute_with(|| {
let cfg = Config::preload();
let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader);
ctx.overlay.set_balance(&ALICE, 1000);
ctx.overlay.set_balance(&BOB, 100);
ctx.overlay.instantiate_contract(&BOB, instantiator_ch).unwrap();
ctx.overlay
.instantiate_contract(&BOB, instantiator_ch)
.unwrap();
assert_matches!(
ctx.call(BOB, 20, &mut GasMeter::<Test>::new(GAS_LIMIT), vec![]),
@@ -1704,12 +1753,13 @@ mod tests {
// The contract wasn't instantiated so we don't expect to see an instantiation
// event here.
assert_eq!(&ctx.events(), &[
DeferredAction::DepositEvent {
assert_eq!(
&ctx.events(),
&[DeferredAction::DepositEvent {
event: RawEvent::Transfer(ALICE, BOB, 20),
topics: Vec::new(),
},
]);
},]
);
});
}
@@ -1745,10 +1795,7 @@ mod tests {
}) if buffer == Vec::<u8>::new()
);
assert_eq!(
&ctx.events(),
&[]
);
assert_eq!(&ctx.events(), &[]);
});
}
@@ -15,11 +15,11 @@
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use crate::Trait;
use sp_std::marker::PhantomData;
use sp_runtime::traits::Zero;
use frame_support::dispatch::{
DispatchError, DispatchResultWithPostInfo, PostDispatchInfo, DispatchErrorWithPostInfo,
DispatchError, DispatchErrorWithPostInfo, DispatchResultWithPostInfo, PostDispatchInfo,
};
use sp_runtime::traits::Zero;
use sp_std::marker::PhantomData;
#[cfg(test)]
use std::{any::Any, fmt::Debug};
@@ -189,7 +189,8 @@ impl<T: Trait> GasMeter<T> {
}
/// Turn this GasMeter into a DispatchResult that contains the actually used gas.
pub fn into_dispatch_result<R, E>(self, result: Result<R, E>) -> DispatchResultWithPostInfo where
pub fn into_dispatch_result<R, E>(self, result: Result<R, E>) -> DispatchResultWithPostInfo
where
E: Into<DispatchError>,
{
let post_info = PostDispatchInfo {
@@ -199,7 +200,10 @@ impl<T: Trait> GasMeter<T> {
result
.map(|_| post_info)
.map_err(|e| DispatchErrorWithPostInfo { post_info, error: e.into() })
.map_err(|e| DispatchErrorWithPostInfo {
post_info,
error: e.into(),
})
}
#[cfg(test)]
@@ -256,7 +260,9 @@ mod tests {
struct SimpleToken(u64);
impl Token<Test> for SimpleToken {
type Metadata = ();
fn calculate_amount(&self, _metadata: &()) -> u64 { self.0 }
fn calculate_amount(&self, _metadata: &()) -> u64 {
self.0
}
}
struct MultiplierTokenMetadata {
@@ -285,8 +291,10 @@ mod tests {
fn simple() {
let mut gas_meter = GasMeter::<Test>::new(50000);
let result = gas_meter
.charge(&MultiplierTokenMetadata { multiplier: 3 }, MultiplierToken(10));
let result = gas_meter.charge(
&MultiplierTokenMetadata { multiplier: 3 },
MultiplierToken(10),
);
assert!(!result.is_out_of_gas());
assert_eq!(gas_meter.gas_left(), 49_970);
@@ -297,7 +305,10 @@ mod tests {
let mut gas_meter = GasMeter::<Test>::new(50000);
assert!(!gas_meter.charge(&(), SimpleToken(1)).is_out_of_gas());
assert!(!gas_meter
.charge(&MultiplierTokenMetadata { multiplier: 3 }, MultiplierToken(10))
.charge(
&MultiplierTokenMetadata { multiplier: 3 },
MultiplierToken(10)
)
.is_out_of_gas());
let mut tokens = gas_meter.tokens()[0..2].iter();
@@ -327,7 +338,6 @@ mod tests {
assert!(gas_meter.charge(&(), SimpleToken(1)).is_out_of_gas());
}
// Charging the exact amount that the user paid for should be
// possible.
#[test]
@@ -84,42 +84,42 @@ mod gas;
mod account_db;
mod exec;
mod wasm;
mod rent;
mod wasm;
#[cfg(test)]
mod tests;
use crate::exec::ExecutionContext;
use crate::account_db::{AccountDb, DirectAccountDb};
use crate::wasm::{WasmLoader, WasmVm};
use crate::{
account_db::{AccountDb, DirectAccountDb},
exec::ExecutionContext,
wasm::{WasmLoader, WasmVm},
};
pub use crate::gas::{Gas, GasMeter};
pub use crate::exec::{ExecResult, ExecReturnValue, ExecError, StatusCode};
pub use crate::{
exec::{ExecError, ExecResult, ExecReturnValue, StatusCode},
gas::{Gas, GasMeter},
};
use codec::{Codec, Decode, Encode};
use cumulus_pallet_contracts_primitives::{ContractAccessError, RentProjection};
use frame_support::{
decl_error, decl_event, decl_module, decl_storage,
dispatch::{DispatchResult, DispatchResultWithPostInfo, Dispatchable, PostDispatchInfo},
parameter_types,
traits::{Currency, Get, OnUnbalanced, Randomness, Time},
weights::{GetDispatchInfo, Weight},
Parameter,
};
use frame_system::{ensure_root, ensure_signed, RawOrigin};
#[cfg(feature = "std")]
use serde::{Serialize, Deserialize};
use serde::{Deserialize, Serialize};
use sp_core::crypto::UncheckedFrom;
use sp_std::{prelude::*, marker::PhantomData, fmt::Debug};
use codec::{Codec, Encode, Decode};
use sp_runtime::{
traits::{
Hash, StaticLookup, Zero, MaybeSerializeDeserialize, Member, Convert,
},
traits::{Convert, Hash, MaybeSerializeDeserialize, Member, StaticLookup, Zero},
RuntimeDebug,
};
use frame_support::dispatch::{
PostDispatchInfo, DispatchResult, Dispatchable, DispatchResultWithPostInfo
};
use frame_support::{
Parameter, decl_module, decl_event, decl_storage, decl_error,
parameter_types,
};
use frame_support::traits::{OnUnbalanced, Currency, Get, Time, Randomness};
use frame_support::weights::GetDispatchInfo;
use frame_system::{ensure_signed, RawOrigin, ensure_root};
use cumulus_pallet_contracts_primitives::{RentProjection, ContractAccessError};
use frame_support::weights::Weight;
use sp_std::{fmt::Debug, marker::PhantomData, prelude::*};
pub type CodeHash<T> = <T as frame_system::Trait>::Hash;
pub type TrieId = Vec<u8>;
@@ -240,9 +240,15 @@ pub struct RawTombstoneContractInfo<H, Hasher>(H, PhantomData<Hasher>);
impl<H, Hasher> RawTombstoneContractInfo<H, Hasher>
where
H: Member + MaybeSerializeDeserialize+ Debug
+ AsRef<[u8]> + AsMut<[u8]> + Copy + Default
+ sp_std::hash::Hash + Codec,
H: Member
+ MaybeSerializeDeserialize
+ Debug
+ AsRef<[u8]>
+ AsMut<[u8]>
+ Copy
+ Default
+ sp_std::hash::Hash
+ Codec,
Hasher: Hash<Output = H>,
{
fn new(storage_root: &[u8], code_hash: H) -> Self {
@@ -272,7 +278,7 @@ pub struct TrieIdFromParentCounter<T: Trait>(PhantomData<T>);
/// accountid_counter`.
impl<T: Trait> TrieIdGenerator<T::AccountId> for TrieIdFromParentCounter<T>
where
T::AccountId: AsRef<[u8]>
T::AccountId: AsRef<[u8]>,
{
fn trie_id(account_id: &T::AccountId) -> TrieId {
// Note that skipping a value due to error is not an issue here.
@@ -321,10 +327,9 @@ pub trait Trait: frame_system::Trait {
type Currency: Currency<Self::AccountId>;
/// The outer call dispatch type.
type Call:
Parameter +
Dispatchable<PostInfo=PostDispatchInfo, Origin=<Self as frame_system::Trait>::Origin> +
GetDispatchInfo;
type Call: Parameter
+ Dispatchable<PostInfo = PostDispatchInfo, Origin = <Self as frame_system::Trait>::Origin>
+ GetDispatchInfo;
/// The overarching event type.
type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
@@ -390,9 +395,13 @@ pub trait Trait: frame_system::Trait {
pub struct SimpleAddressDeterminer<T: Trait>(PhantomData<T>);
impl<T: Trait> ContractAddressFor<CodeHash<T>, T::AccountId> for SimpleAddressDeterminer<T>
where
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
{
fn contract_address_for(code_hash: &CodeHash<T>, data: &[u8], origin: &T::AccountId) -> T::AccountId {
fn contract_address_for(
code_hash: &CodeHash<T>,
data: &[u8],
origin: &T::AccountId,
) -> T::AccountId {
let data_hash = T::Hashing::hash(data);
let mut buf = Vec::new();
@@ -641,13 +650,15 @@ impl<T: Trait> Module<T> {
impl<T: Trait> Module<T> {
fn calc_code_put_costs(code: &Vec<u8>) -> Gas {
<Module<T>>::current_schedule().put_code_per_byte_cost.saturating_mul(code.len() as Gas)
<Module<T>>::current_schedule()
.put_code_per_byte_cost
.saturating_mul(code.len() as Gas)
}
fn execute_wasm(
origin: T::AccountId,
gas_meter: &mut GasMeter<T>,
func: impl FnOnce(&mut ExecutionContext<T, WasmVm, WasmLoader>, &mut GasMeter<T>) -> ExecResult
func: impl FnOnce(&mut ExecutionContext<T, WasmVm, WasmLoader>, &mut GasMeter<T>) -> ExecResult,
) -> ExecResult {
let cfg = Config::preload();
let vm = WasmVm::new(&cfg.schedule);
@@ -656,7 +667,11 @@ impl<T: Trait> Module<T> {
let result = func(&mut ctx, gas_meter);
if result.as_ref().map(|output| output.is_success()).unwrap_or(false) {
if result
.as_ref()
.map(|output| output.is_success())
.unwrap_or(false)
{
// Commit all changes that made it thus far into the persistent storage.
DirectAccountDb.commit(ctx.overlay.into_change_set());
}
@@ -665,17 +680,11 @@ impl<T: Trait> Module<T> {
ctx.deferred.into_iter().for_each(|deferred| {
use self::exec::DeferredAction::*;
match deferred {
DepositEvent {
topics,
event,
} => <frame_system::Module<T>>::deposit_event_indexed(
DepositEvent { topics, event } => <frame_system::Module<T>>::deposit_event_indexed(
&*topics,
<T as Trait>::Event::from(event).into(),
),
DispatchRuntimeCall {
origin: who,
call,
} => {
DispatchRuntimeCall { origin: who, call } => {
let info = call.get_dispatch_info();
let result = call.dispatch(RawOrigin::Signed(who.clone()).into());
let post_info = match result {
@@ -693,11 +702,19 @@ impl<T: Trait> Module<T> {
delta,
} => {
let result = Self::restore_to(
donor.clone(), dest.clone(), code_hash.clone(), rent_allowance.clone(), delta
);
Self::deposit_event(
RawEvent::Restored(donor, dest, code_hash, rent_allowance, result.is_ok())
donor.clone(),
dest.clone(),
code_hash.clone(),
rent_allowance.clone(),
delta,
);
Self::deposit_event(RawEvent::Restored(
donor,
dest,
code_hash,
rent_allowance,
result.is_ok(),
));
}
}
});
@@ -20,10 +20,14 @@ use crate::{
AliveContractInfo, BalanceOf, ContractInfo, ContractInfoOf, Module, RawEvent,
TombstoneContractInfo, Trait,
};
use frame_support::storage::unhashed as storage;
use frame_support::traits::{Currency, ExistenceRequirement, Get, OnUnbalanced, WithdrawReason};
use frame_support::StorageMap;
use cumulus_pallet_contracts_primitives::{ContractAccessError, RentProjection, RentProjectionResult};
use cumulus_pallet_contracts_primitives::{
ContractAccessError, RentProjection, RentProjectionResult,
};
use frame_support::{
storage::unhashed as storage,
traits::{Currency, ExistenceRequirement, Get, OnUnbalanced, WithdrawReason},
StorageMap,
};
use sp_runtime::traits::{Bounded, CheckedDiv, CheckedMul, SaturatedConversion, Saturating, Zero};
/// The amount to charge.
@@ -238,10 +242,8 @@ fn enact_verdict<T: Trait>(
// Use a dummy storage root because restoration is currentlyy unsupported
// for parachains anyways.
let tombstone = <TombstoneContractInfo<T>>::new(
&[0u8; 32],
alive_contract_info.code_hash,
);
let tombstone =
<TombstoneContractInfo<T>>::new(&[0u8; 32], alive_contract_info.code_hash);
let tombstone_info = ContractInfo::Tombstone(tombstone);
<ContractInfoOf<T>>::insert(account, &tombstone_info);
File diff suppressed because it is too large Load Diff
@@ -26,11 +26,13 @@
//! this guarantees that every instrumented contract code in cache cannot have the version equal to the current one.
//! Thus, before executing a contract it should be reinstrument with new schedule.
use crate::wasm::{prepare, runtime::Env, PrefabWasmModule};
use crate::{CodeHash, CodeStorage, PristineCode, Schedule, Trait};
use sp_std::prelude::*;
use sp_runtime::traits::Hash;
use crate::{
wasm::{prepare, runtime::Env, PrefabWasmModule},
CodeHash, CodeStorage, PristineCode, Schedule, Trait,
};
use frame_support::StorageMap;
use sp_runtime::traits::Hash;
use sp_std::prelude::*;
/// Put code in the storage. The hash of code is used as a key and is returned
/// as a result of this function.
@@ -58,8 +60,7 @@ pub fn load<T: Trait>(
code_hash: &CodeHash<T>,
schedule: &Schedule,
) -> Result<PrefabWasmModule, &'static str> {
let mut prefab_module =
<CodeStorage<T>>::get(code_hash).ok_or_else(|| "code is not found")?;
let mut prefab_module = <CodeStorage<T>>::get(code_hash).ok_or_else(|| "code is not found")?;
if prefab_module.schedule_version < schedule.version {
// The current schedule version is greater than the version of the one cached
@@ -193,14 +193,14 @@ macro_rules! define_env {
#[cfg(test)]
mod tests {
use parity_wasm::elements::FunctionType;
use parity_wasm::elements::ValueType;
use crate::{
exec::Ext,
gas::Gas,
wasm::{tests::MockExt, Runtime},
};
use parity_wasm::elements::{FunctionType, ValueType};
use sp_runtime::traits::Zero;
use sp_sandbox::{ReturnValue, Value};
use crate::wasm::tests::MockExt;
use crate::wasm::Runtime;
use crate::exec::Ext;
use crate::gas::Gas;
#[test]
fn macro_unmarshall_then_body_then_marshall_value_or_trap() {
@@ -263,8 +263,10 @@ mod tests {
Err(sp_sandbox::HostError)
}
});
let _f: fn(&mut Runtime<MockExt>, &[sp_sandbox::Value])
-> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError> = ext_gas::<MockExt>;
let _f: fn(
&mut Runtime<MockExt>,
&[sp_sandbox::Value],
) -> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError> = ext_gas::<MockExt>;
}
#[test]
@@ -317,7 +319,13 @@ mod tests {
},
);
assert!(Env::can_satisfy(b"ext_gas", &FunctionType::new(vec![ValueType::I32], None)));
assert!(!Env::can_satisfy(b"not_exists", &FunctionType::new(vec![], None)));
assert!(Env::can_satisfy(
b"ext_gas",
&FunctionType::new(vec![ValueType::I32], None)
));
assert!(!Env::can_satisfy(
b"not_exists",
&FunctionType::new(vec![], None)
));
}
}
@@ -17,8 +17,8 @@
use super::Runtime;
use crate::exec::Ext;
use sp_sandbox::Value;
use parity_wasm::elements::{FunctionType, ValueType};
use sp_sandbox::Value;
#[macro_use]
pub(crate) mod macros;
@@ -66,10 +66,9 @@ impl ConvertibleToWasm for u64 {
}
}
pub(crate) type HostFunc<E> =
fn(
pub(crate) type HostFunc<E> = fn(
&mut Runtime<E>,
&[sp_sandbox::Value]
&[sp_sandbox::Value],
) -> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError>;
pub(crate) trait FunctionImplProvider<E: Ext> {
@@ -17,14 +17,16 @@
//! This module provides a means for executing contracts
//! represented in wasm.
use crate::{CodeHash, Schedule, Trait};
use crate::wasm::env_def::FunctionImplProvider;
use crate::exec::{Ext, ExecResult};
use crate::gas::GasMeter;
use crate::{
exec::{ExecResult, Ext},
gas::GasMeter,
wasm::env_def::FunctionImplProvider,
CodeHash, Schedule, Trait,
};
use sp_std::prelude::*;
use codec::{Encode, Decode};
use codec::{Decode, Encode};
use sp_sandbox;
use sp_std::prelude::*;
#[macro_use]
mod env_def;
@@ -32,8 +34,10 @@ mod code_cache;
mod prepare;
mod runtime;
use self::runtime::{to_execution_result, Runtime};
use self::code_cache::load as load_code;
use self::{
code_cache::load as load_code,
runtime::{to_execution_result, Runtime},
};
pub use self::code_cache::save as save_code;
@@ -131,13 +135,7 @@ impl<'a, T: Trait> crate::exec::Vm<T> for WasmVm<'a> {
imports.add_host_func("env", name, func_ptr);
});
let mut runtime = Runtime::new(
&mut ext,
input_data,
&self.schedule,
memory,
gas_meter,
);
let mut runtime = Runtime::new(&mut ext, input_data, &self.schedule, memory, gas_meter);
// Instantiate the instance from the instrumented module code and invoke the contract
// entrypoint.
@@ -150,19 +148,20 @@ impl<'a, T: Trait> crate::exec::Vm<T> for WasmVm<'a> {
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
use std::cell::RefCell;
use sp_core::H256;
use crate::exec::{Ext, StorageKey, ExecError, ExecReturnValue, STATUS_SUCCESS};
use crate::gas::{Gas, GasMeter};
use crate::tests::{Test, Call};
use crate::wasm::prepare::prepare_contract;
use crate::{CodeHash, BalanceOf};
use wabt;
use hex_literal::hex;
use crate::{
exec::{ExecError, ExecReturnValue, Ext, StorageKey, STATUS_SUCCESS},
gas::{Gas, GasMeter},
tests::{Call, Test},
wasm::prepare::prepare_contract,
BalanceOf, CodeHash,
};
use assert_matches::assert_matches;
use sp_runtime::DispatchError;
use frame_support::weights::Weight;
use hex_literal::hex;
use sp_core::H256;
use sp_runtime::DispatchError;
use std::{cell::RefCell, collections::HashMap};
use wabt;
const GAS_LIMIT: Gas = 10_000_000_000;
@@ -230,9 +229,11 @@ mod tests {
fn get_storage(&self, key: &StorageKey) -> Option<Vec<u8>> {
self.storage.get(key).cloned()
}
fn set_storage(&mut self, key: StorageKey, value: Option<Vec<u8>>)
-> Result<(), &'static str>
{
fn set_storage(
&mut self,
key: StorageKey,
value: Option<Vec<u8>>,
) -> Result<(), &'static str> {
*self.storage.entry(key).or_insert(Vec::new()) = value.unwrap_or(Vec::new());
Ok(())
}
@@ -289,7 +290,10 @@ mod tests {
});
// Assume for now that it was just a plain transfer.
// TODO: Add tests for different call outcomes.
Ok(ExecReturnValue { status: STATUS_SUCCESS, data: Vec::new() })
Ok(ExecReturnValue {
status: STATUS_SUCCESS,
data: Vec::new(),
})
}
fn terminate(
&mut self,
@@ -360,21 +364,22 @@ mod tests {
self.rent_allowance
}
fn block_number(&self) -> u64 { 121 }
fn block_number(&self) -> u64 {
121
}
fn max_value_size(&self) -> u32 { 16_384 }
fn max_value_size(&self) -> u32 {
16_384
}
fn get_runtime_storage(&self, key: &[u8]) -> Option<Vec<u8>> {
let opt_value = self.runtime_storage_keys
.borrow_mut()
.remove(key);
opt_value.unwrap_or_else(||
let opt_value = self.runtime_storage_keys.borrow_mut().remove(key);
opt_value.unwrap_or_else(|| {
panic!(
"{:?} doesn't exist. values that do exist {:?}",
key,
self.runtime_storage_keys
)
key, self.runtime_storage_keys
)
})
}
fn get_weight_price(&self, weight: Weight) -> BalanceOf<Self::T> {
BalanceOf::<Self::T>::from(1312_u32).saturating_mul(weight.into())
@@ -387,9 +392,11 @@ mod tests {
fn get_storage(&self, key: &[u8; 32]) -> Option<Vec<u8>> {
(**self).get_storage(key)
}
fn set_storage(&mut self, key: [u8; 32], value: Option<Vec<u8>>)
-> Result<(), &'static str>
{
fn set_storage(
&mut self,
key: [u8; 32],
value: Option<Vec<u8>>,
) -> Result<(), &'static str> {
(**self).set_storage(key, value)
}
fn instantiate(
@@ -435,12 +442,7 @@ mod tests {
rent_allowance: u64,
delta: Vec<StorageKey>,
) {
(**self).note_restore_to(
dest,
code_hash,
rent_allowance,
delta,
)
(**self).note_restore_to(dest, code_hash, rent_allowance, delta)
}
fn caller(&self) -> &u64 {
(**self).caller()
@@ -499,8 +501,7 @@ mod tests {
let wasm = wabt::wat2wasm(wat).unwrap();
let schedule = crate::Schedule::default();
let prefab_module =
prepare_contract::<super::runtime::Env>(&wasm, &schedule).unwrap();
let prefab_module = prepare_contract::<super::runtime::Env>(&wasm, &schedule).unwrap();
let exec = WasmExecutable {
// Use a "call" convention.
@@ -554,7 +555,8 @@ mod tests {
vec![],
&mut mock_ext,
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
assert_eq!(
&mock_ext.transfers,
@@ -614,7 +616,8 @@ mod tests {
vec![],
&mut mock_ext,
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
assert_eq!(
&mock_ext.transfers,
@@ -676,7 +679,8 @@ mod tests {
vec![],
&mut mock_ext,
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
assert_eq!(
&mock_ext.instantiates,
@@ -719,7 +723,8 @@ mod tests {
vec![],
&mut mock_ext,
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
assert_eq!(
&mock_ext.terminations,
@@ -777,7 +782,8 @@ mod tests {
vec![],
&mut mock_ext,
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
assert_eq!(
&mock_ext.transfers,
@@ -861,18 +867,23 @@ mod tests {
#[test]
fn get_storage_puts_data_into_scratch_buf() {
let mut mock_ext = MockExt::default();
mock_ext
.storage
.insert([0x11; 32], [0x22; 32].to_vec());
mock_ext.storage.insert([0x11; 32], [0x22; 32].to_vec());
let output = execute(
CODE_GET_STORAGE,
vec![],
mock_ext,
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
assert_eq!(output, ExecReturnValue { status: STATUS_SUCCESS, data: [0x22; 32].to_vec() });
assert_eq!(
output,
ExecReturnValue {
status: STATUS_SUCCESS,
data: [0x22; 32].to_vec()
}
);
}
/// calls `ext_caller`, loads the address from the scratch buffer and
@@ -934,7 +945,8 @@ mod tests {
vec![],
MockExt::default(),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
}
/// calls `ext_address`, loads the address from the scratch buffer and
@@ -996,7 +1008,8 @@ mod tests {
vec![],
MockExt::default(),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
}
const CODE_BALANCE: &str = r#"
@@ -1051,12 +1064,7 @@ mod tests {
#[test]
fn balance() {
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let _ = execute(
CODE_BALANCE,
vec![],
MockExt::default(),
&mut gas_meter,
).unwrap();
let _ = execute(CODE_BALANCE, vec![], MockExt::default(), &mut gas_meter).unwrap();
}
const CODE_GAS_PRICE: &str = r#"
@@ -1111,12 +1119,7 @@ mod tests {
#[test]
fn gas_price() {
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let _ = execute(
CODE_GAS_PRICE,
vec![],
MockExt::default(),
&mut gas_meter,
).unwrap();
let _ = execute(CODE_GAS_PRICE, vec![], MockExt::default(), &mut gas_meter).unwrap();
}
const CODE_GAS_LEFT: &str = r#"
@@ -1170,16 +1173,14 @@ mod tests {
fn gas_left() {
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let output = execute(
CODE_GAS_LEFT,
vec![],
MockExt::default(),
&mut gas_meter,
).unwrap();
let output = execute(CODE_GAS_LEFT, vec![], MockExt::default(), &mut gas_meter).unwrap();
let gas_left = Gas::decode(&mut output.data.as_slice()).unwrap();
assert!(gas_left < GAS_LIMIT, "gas_left must be less than initial");
assert!(gas_left > gas_meter.gas_left(), "gas_left must be greater than final");
assert!(
gas_left > gas_meter.gas_left(),
"gas_left must be greater than final"
);
}
const CODE_VALUE_TRANSFERRED: &str = r#"
@@ -1239,7 +1240,8 @@ mod tests {
vec![],
MockExt::default(),
&mut gas_meter,
).unwrap();
)
.unwrap();
}
const CODE_DISPATCH_CALL: &str = r#"
@@ -1270,13 +1272,14 @@ mod tests {
vec![],
&mut mock_ext,
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
assert_eq!(
&mock_ext.dispatches,
&[DispatchEntry(
Call::Balances(pallet_balances::Call::set_balance(42, 1337, 0)),
)]
&[DispatchEntry(Call::Balances(
pallet_balances::Call::set_balance(42, 1337, 0)
),)]
);
}
@@ -1310,9 +1313,16 @@ mod tests {
vec![],
MockExt::default(),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
assert_eq!(output, ExecReturnValue { status: STATUS_SUCCESS, data: vec![1, 2, 3, 4] });
assert_eq!(
output,
ExecReturnValue {
status: STATUS_SUCCESS,
data: vec![1, 2, 3, 4]
}
);
}
const CODE_TIMESTAMP_NOW: &str = r#"
@@ -1372,7 +1382,8 @@ mod tests {
vec![],
MockExt::default(),
&mut gas_meter,
).unwrap();
)
.unwrap();
}
const CODE_MINIMUM_BALANCE: &str = r#"
@@ -1431,7 +1442,8 @@ mod tests {
vec![],
MockExt::default(),
&mut gas_meter,
).unwrap();
)
.unwrap();
}
const CODE_TOMBSTONE_DEPOSIT: &str = r#"
@@ -1490,7 +1502,8 @@ mod tests {
vec![],
MockExt::default(),
&mut gas_meter,
).unwrap();
)
.unwrap();
}
const CODE_RANDOM: &str = r#"
@@ -1554,19 +1567,15 @@ mod tests {
fn random() {
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let output = execute(
CODE_RANDOM,
vec![],
MockExt::default(),
&mut gas_meter,
).unwrap();
let output = execute(CODE_RANDOM, vec![], MockExt::default(), &mut gas_meter).unwrap();
// The mock ext just returns the same data that was passed as the subject.
assert_eq!(
output,
ExecReturnValue {
status: STATUS_SUCCESS,
data: hex!("000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F").to_vec(),
data: hex!("000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F")
.to_vec(),
},
);
}
@@ -1598,17 +1607,15 @@ mod tests {
fn deposit_event() {
let mut mock_ext = MockExt::default();
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let _ = execute(
CODE_DEPOSIT_EVENT,
vec![],
&mut mock_ext,
&mut gas_meter
).unwrap();
let _ = execute(CODE_DEPOSIT_EVENT, vec![], &mut mock_ext, &mut gas_meter).unwrap();
assert_eq!(mock_ext.events, vec![
(vec![H256::repeat_byte(0x33)],
vec![0x00, 0x01, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x14, 0x00])
]);
assert_eq!(
mock_ext.events,
vec![(
vec![H256::repeat_byte(0x33)],
vec![0x00, 0x01, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x14, 0x00]
)]
);
assert_eq!(gas_meter.gas_left(), 9967000000);
}
@@ -1653,7 +1660,8 @@ mod tests {
&mut gas_meter
),
Err(ExecError {
reason: DispatchError::Other("contract trapped during execution"), buffer: _
reason: DispatchError::Other("contract trapped during execution"),
buffer: _,
})
);
}
@@ -1696,7 +1704,10 @@ mod tests {
MockExt::default(),
&mut gas_meter
),
Err(ExecError { reason: DispatchError::Other("contract trapped during execution"), buffer: _ })
Err(ExecError {
reason: DispatchError::Other("contract trapped during execution"),
buffer: _,
})
);
}
@@ -1759,7 +1770,8 @@ mod tests {
vec![],
MockExt::default(),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
}
// asserts that the size of the input data is 4.
@@ -1799,7 +1811,8 @@ mod tests {
input_data,
MockExt::default(),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
assert_eq!(output.data.len(), 0);
assert_eq!(output.data.capacity(), 1_234);
@@ -1815,7 +1828,9 @@ mod tests {
input_data,
MockExt::default(),
&mut GasMeter::new(GAS_LIMIT),
).err().unwrap();
)
.err()
.unwrap();
assert_eq!(error.buffer.capacity(), 1_234);
}
@@ -1869,9 +1884,16 @@ mod tests {
hex!("00112233445566778899").to_vec(),
MockExt::default(),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
assert_eq!(output, ExecReturnValue { status: 0, data: hex!("445566778899").to_vec() });
assert_eq!(
output,
ExecReturnValue {
status: 0,
data: hex!("445566778899").to_vec()
}
);
assert!(output.is_success());
}
@@ -1882,9 +1904,16 @@ mod tests {
hex!("112233445566778899").to_vec(),
MockExt::default(),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
assert_eq!(output, ExecReturnValue { status: 17, data: hex!("5566778899").to_vec() });
assert_eq!(
output,
ExecReturnValue {
status: 17,
data: hex!("5566778899").to_vec()
}
);
assert!(!output.is_success());
}
@@ -1973,17 +2002,15 @@ mod tests {
// "\01\02\03\04" - Some(0x14144020)
// "\02\03\04\05" - None
*mock_ext.runtime_storage_keys.borrow_mut() = [
([1, 2, 3, 4].to_vec(), Some(0x14144020u32.to_le_bytes().to_vec())),
(
[1, 2, 3, 4].to_vec(),
Some(0x14144020u32.to_le_bytes().to_vec()),
),
([2, 3, 4, 5].to_vec().to_vec(), None),
]
.iter()
.cloned()
.collect();
let _ = execute(
CODE_GET_RUNTIME_STORAGE,
vec![],
mock_ext,
&mut gas_meter,
).unwrap();
let _ = execute(CODE_GET_RUNTIME_STORAGE, vec![], mock_ext, &mut gas_meter).unwrap();
}
}
@@ -18,15 +18,15 @@
//! wasm module before execution. It also extracts some essential information
//! from a module.
use crate::wasm::env_def::ImportSatisfyCheck;
use crate::wasm::PrefabWasmModule;
use crate::Schedule;
use crate::{
wasm::{env_def::ImportSatisfyCheck, PrefabWasmModule},
Schedule,
};
use parity_wasm::elements::{self, Internal, External, MemoryType, Type, ValueType};
use pwasm_utils;
use pwasm_utils::rules;
use parity_wasm::elements::{self, External, Internal, MemoryType, Type, ValueType};
use pwasm_utils::{self, rules};
use sp_runtime::traits::SaturatedConversion;
use sp_std::prelude::*;
use sp_runtime::traits::{SaturatedConversion};
struct ContractModule<'a> {
/// A deserialized module. The module is valid (this is Guaranteed by `new` method).
@@ -39,10 +39,7 @@ impl<'a> ContractModule<'a> {
///
/// Returns `Err` if the `original_code` couldn't be decoded or
/// if it contains an invalid module.
fn new(
original_code: &[u8],
schedule: &'a Schedule,
) -> Result<Self, &'static str> {
fn new(original_code: &[u8], schedule: &'a Schedule) -> Result<Self, &'static str> {
use wasmi_validation::{validate_module, PlainValidator};
let module =
@@ -53,10 +50,7 @@ impl<'a> ContractModule<'a> {
// Return a `ContractModule` instance with
// __valid__ module.
Ok(ContractModule {
module,
schedule,
})
Ok(ContractModule { module, schedule })
}
/// Ensures that module doesn't declare internal memories.
@@ -65,7 +59,8 @@ impl<'a> ContractModule<'a> {
/// Memory section contains declarations of internal linear memories, so if we find one
/// we reject such a module.
fn ensure_no_internal_memory(&self) -> Result<(), &'static str> {
if self.module
if self
.module
.memory_section()
.map_or(false, |ms| ms.entries().len() > 0)
{
@@ -86,7 +81,7 @@ impl<'a> ContractModule<'a> {
// Check the table's initial size as there is no instruction or environment function
// capable of growing the table.
if table_type.limits().initial() > limit {
return Err("table exceeds maximum size allowed")
return Err("table exceeds maximum size allowed");
}
}
}
@@ -98,8 +93,9 @@ impl<'a> ContractModule<'a> {
if let Some(global_section) = self.module.global_section() {
for global in global_section.entries() {
match global.global_type().content_type() {
ValueType::F32 | ValueType::F64 =>
return Err("use of floating point type in globals is forbidden"),
ValueType::F32 | ValueType::F64 => {
return Err("use of floating point type in globals is forbidden")
}
_ => {}
}
}
@@ -109,8 +105,9 @@ impl<'a> ContractModule<'a> {
for func_body in code_section.bodies() {
for local in func_body.locals() {
match local.value_type() {
ValueType::F32 | ValueType::F64 =>
return Err("use of floating point type in locals is forbidden"),
ValueType::F32 | ValueType::F64 => {
return Err("use of floating point type in locals is forbidden")
}
_ => {}
}
}
@@ -124,8 +121,11 @@ impl<'a> ContractModule<'a> {
let return_type = func_type.return_type();
for value_type in func_type.params().iter().chain(return_type.iter()) {
match value_type {
ValueType::F32 | ValueType::F64 =>
return Err("use of floating point type in function types is forbidden"),
ValueType::F32 | ValueType::F64 => {
return Err(
"use of floating point type in function types is forbidden",
)
}
_ => {}
}
}
@@ -138,8 +138,7 @@ impl<'a> ContractModule<'a> {
}
fn inject_gas_metering(self) -> Result<Self, &'static str> {
let gas_rules =
rules::Set::new(
let gas_rules = rules::Set::new(
self.schedule.regular_op_cost.clone().saturated_into(),
Default::default(),
)
@@ -195,11 +194,9 @@ impl<'a> ContractModule<'a> {
.map(|is| is.entries())
.unwrap_or(&[])
.iter()
.filter(|entry| {
match *entry.external() {
.filter(|entry| match *entry.external() {
External::Function(_) => true,
_ => false,
}
})
.count();
@@ -232,15 +229,17 @@ impl<'a> ContractModule<'a> {
// The [] -> [] signature predates the [] -> [i32] signature and is supported for
// backwards compatibility. This will likely be removed once ink! is updated to
// generate modules with the new function signatures.
let func_ty_idx = func_entries.get(fn_idx as usize)
let func_ty_idx = func_entries
.get(fn_idx as usize)
.ok_or_else(|| "export refers to non-existent function")?
.type_ref();
let Type::Function(ref func_ty) = types
.get(func_ty_idx as usize)
.ok_or_else(|| "function has a non-existent type")?;
if !func_ty.params().is_empty() ||
!(func_ty.return_type().is_none() ||
func_ty.return_type() == Some(ValueType::I32)) {
if !func_ty.params().is_empty()
|| !(func_ty.return_type().is_none()
|| func_ty.return_type() == Some(ValueType::I32))
{
return Err("entry point has wrong signature");
}
}
@@ -286,10 +285,10 @@ impl<'a> ContractModule<'a> {
&External::Function(ref type_idx) => type_idx,
&External::Memory(ref memory_type) => {
if import.field() != "memory" {
return Err("Memory import must have the field name 'memory'")
return Err("Memory import must have the field name 'memory'");
}
if imported_mem_type.is_some() {
return Err("Multiple memory imports defined")
return Err("Multiple memory imports defined");
}
imported_mem_type = Some(memory_type);
continue;
@@ -317,8 +316,7 @@ impl<'a> ContractModule<'a> {
}
fn into_wasm_code(self) -> Result<Vec<u8>, &'static str> {
elements::serialize(self.module)
.map_err(|_| "error serializing instrumented module")
elements::serialize(self.module).map_err(|_| "error serializing instrumented module")
}
}
@@ -394,9 +392,9 @@ pub fn prepare_contract<C: ImportSatisfyCheck>(
mod tests {
use super::*;
use crate::exec::Ext;
use assert_matches::assert_matches;
use std::fmt;
use wabt;
use assert_matches::assert_matches;
impl fmt::Debug for PrefabWasmModule {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -429,7 +427,8 @@ mod tests {
};
}
prepare_test!(no_floats,
prepare_test!(
no_floats,
r#"
(module
(func (export "call")
@@ -454,7 +453,8 @@ mod tests {
assert_eq!(Schedule::default().max_memory_pages, 16);
}
prepare_test!(memory_with_one_page,
prepare_test!(
memory_with_one_page,
r#"
(module
(import "env" "memory" (memory 1 1))
@@ -466,7 +466,8 @@ mod tests {
Ok(_)
);
prepare_test!(internal_memory_declaration,
prepare_test!(
internal_memory_declaration,
r#"
(module
(memory 1 1)
@@ -478,7 +479,8 @@ mod tests {
Err("module declares internal memory")
);
prepare_test!(no_memory_import,
prepare_test!(
no_memory_import,
r#"
(module
;; no memory imported
@@ -489,7 +491,8 @@ mod tests {
Ok(_)
);
prepare_test!(initial_exceeds_maximum,
prepare_test!(
initial_exceeds_maximum,
r#"
(module
(import "env" "memory" (memory 16 1))
@@ -501,7 +504,8 @@ mod tests {
Err("Module is not valid")
);
prepare_test!(no_maximum,
prepare_test!(
no_maximum,
r#"
(module
(import "env" "memory" (memory 1))
@@ -513,7 +517,8 @@ mod tests {
Err("Maximum number of pages should be always declared.")
);
prepare_test!(requested_maximum_exceeds_configured_maximum,
prepare_test!(
requested_maximum_exceeds_configured_maximum,
r#"
(module
(import "env" "memory" (memory 1 17))
@@ -525,7 +530,8 @@ mod tests {
Err("Maximum number of pages should not exceed the configured maximum.")
);
prepare_test!(field_name_not_memory,
prepare_test!(
field_name_not_memory,
r#"
(module
(import "env" "forgetit" (memory 1 1))
@@ -537,7 +543,8 @@ mod tests {
Err("Memory import must have the field name 'memory'")
);
prepare_test!(multiple_memory_imports,
prepare_test!(
multiple_memory_imports,
r#"
(module
(import "env" "memory" (memory 1 1))
@@ -550,7 +557,8 @@ mod tests {
Err("Module is not valid")
);
prepare_test!(table_import,
prepare_test!(
table_import,
r#"
(module
(import "env" "table" (table 1 anyfunc))
@@ -562,7 +570,8 @@ mod tests {
Err("Cannot import tables")
);
prepare_test!(global_import,
prepare_test!(
global_import,
r#"
(module
(global $g (import "env" "global") i32)
@@ -583,7 +592,8 @@ mod tests {
assert_eq!(Schedule::default().max_table_size, 16384);
}
prepare_test!(no_tables,
prepare_test!(
no_tables,
r#"
(module
(func (export "call"))
@@ -593,7 +603,8 @@ mod tests {
Ok(_)
);
prepare_test!(table_valid_size,
prepare_test!(
table_valid_size,
r#"
(module
(table 10000 funcref)
@@ -605,7 +616,8 @@ mod tests {
Ok(_)
);
prepare_test!(table_too_big,
prepare_test!(
table_too_big,
r#"
(module
(table 20000 funcref)
@@ -620,7 +632,8 @@ mod tests {
mod imports {
use super::*;
prepare_test!(can_import_legit_function,
prepare_test!(
can_import_legit_function,
r#"
(module
(import "env" "nop" (func (param i64)))
@@ -634,7 +647,8 @@ mod tests {
// even though gas is defined the contract can't import it since
// it is an implementation defined.
prepare_test!(can_not_import_gas_function,
prepare_test!(
can_not_import_gas_function,
r#"
(module
(import "env" "gas" (func (param i32)))
@@ -647,7 +661,8 @@ mod tests {
);
// nothing can be imported from non-"env" module for now.
prepare_test!(non_env_import,
prepare_test!(
non_env_import,
r#"
(module
(import "another_module" "memory" (memory 1 1))
@@ -660,7 +675,8 @@ mod tests {
);
// wrong signature
prepare_test!(wrong_signature,
prepare_test!(
wrong_signature,
r#"
(module
(import "env" "gas" (func (param i64)))
@@ -672,7 +688,8 @@ mod tests {
Err("module imports a non-existent function")
);
prepare_test!(unknown_func_name,
prepare_test!(
unknown_func_name,
r#"
(module
(import "env" "unknown_func" (func))
@@ -684,7 +701,8 @@ mod tests {
Err("module imports a non-existent function")
);
prepare_test!(ext_println_debug_disabled,
prepare_test!(
ext_println_debug_disabled,
r#"
(module
(import "env" "ext_println" (func $ext_println (param i32 i32)))
@@ -698,7 +716,9 @@ mod tests {
#[test]
fn ext_println_debug_enabled() {
let wasm = wabt::Wat2Wasm::new().validate(false).convert(
let wasm = wabt::Wat2Wasm::new()
.validate(false)
.convert(
r#"
(module
(import "env" "ext_println" (func $ext_println (param i32 i32)))
@@ -706,8 +726,9 @@ mod tests {
(func (export "call"))
(func (export "deploy"))
)
"#
).unwrap();
"#,
)
.unwrap();
let mut schedule = Schedule::default();
schedule.enable_println = true;
let r = prepare_contract::<TestEnv>(wasm.as_ref(), &schedule);
@@ -718,7 +739,8 @@ mod tests {
mod entrypoints {
use super::*;
prepare_test!(it_works,
prepare_test!(
it_works,
r#"
(module
(func (export "call"))
@@ -728,7 +750,8 @@ mod tests {
Ok(_)
);
prepare_test!(omit_deploy,
prepare_test!(
omit_deploy,
r#"
(module
(func (export "call"))
@@ -737,7 +760,8 @@ mod tests {
Err("deploy function isn't exported")
);
prepare_test!(omit_call,
prepare_test!(
omit_call,
r#"
(module
(func (export "deploy"))
@@ -747,7 +771,8 @@ mod tests {
);
// Try to use imported function as an entry point.
prepare_test!(try_sneak_export_as_entrypoint,
prepare_test!(
try_sneak_export_as_entrypoint,
r#"
(module
(import "env" "panic" (func))
@@ -761,7 +786,8 @@ mod tests {
);
// Try to use imported function as an entry point.
prepare_test!(try_sneak_export_as_global,
prepare_test!(
try_sneak_export_as_global,
r#"
(module
(func (export "deploy"))
@@ -771,7 +797,8 @@ mod tests {
Err("expected a function")
);
prepare_test!(wrong_signature,
prepare_test!(
wrong_signature,
r#"
(module
(func (export "deploy"))
@@ -781,7 +808,8 @@ mod tests {
Err("entry point has wrong signature")
);
prepare_test!(unknown_exports,
prepare_test!(
unknown_exports,
r#"
(module
(func (export "call"))
@@ -792,7 +820,8 @@ mod tests {
Err("unknown export: expecting only deploy and call functions")
);
prepare_test!(global_float,
prepare_test!(
global_float,
r#"
(module
(global $x f32 (f32.const 0))
@@ -803,7 +832,8 @@ mod tests {
Err("use of floating point type in globals is forbidden")
);
prepare_test!(local_float,
prepare_test!(
local_float,
r#"
(module
(func $foo (local f32))
@@ -814,7 +844,8 @@ mod tests {
Err("use of floating point type in locals is forbidden")
);
prepare_test!(param_float,
prepare_test!(
param_float,
r#"
(module
(func $foo (param f32))
@@ -825,7 +856,8 @@ mod tests {
Err("use of floating point type in function types is forbidden")
);
prepare_test!(result_float,
prepare_test!(
result_float,
r#"
(module
(func $foo (result f32) (f32.const 0))
@@ -16,23 +16,18 @@
//! Environment definition of the wasm smart-contract runtime.
use crate::{Schedule, Trait, CodeHash, BalanceOf};
use crate::exec::{
Ext, ExecResult, ExecError, ExecReturnValue, StorageKey, TopicOf, STATUS_SUCCESS,
use crate::{
exec::{ExecError, ExecResult, ExecReturnValue, Ext, StorageKey, TopicOf, STATUS_SUCCESS},
gas::{Gas, GasMeter, GasMeterResult, Token},
BalanceOf, CodeHash, Schedule, Trait,
};
use crate::gas::{Gas, GasMeter, Token, GasMeterResult};
use sp_sandbox;
use frame_system;
use sp_std::{prelude::*, mem, convert::TryInto};
use codec::{Decode, Encode};
use sp_runtime::traits::{Bounded, SaturatedConversion};
use sp_io::hashing::{
keccak_256,
blake2_256,
blake2_128,
sha2_256,
};
use frame_support::weights::GetDispatchInfo;
use frame_system;
use sp_io::hashing::{blake2_128, blake2_256, keccak_256, sha2_256};
use sp_runtime::traits::{Bounded, SaturatedConversion};
use sp_sandbox;
use sp_std::{convert::TryInto, mem, prelude::*};
/// The value returned from ext_call and ext_instantiate contract external functions if the call or
/// instantiation traps. This value is chosen as if the execution does not trap, the return value
@@ -93,19 +88,19 @@ pub(crate) fn to_execution_result<E: Ext>(
status: STATUS_SUCCESS,
data,
})
},
}
Some(SpecialTrap::Termination) => {
return Ok(ExecReturnValue {
status: STATUS_SUCCESS,
data: Vec::new(),
})
},
}
Some(SpecialTrap::OutOfGas) => {
return Err(ExecError {
reason: "ran out of gas during contract execution".into(),
buffer: runtime.scratch_buf,
})
},
}
None => (),
}
@@ -115,29 +110,43 @@ pub(crate) fn to_execution_result<E: Ext>(
Ok(sp_sandbox::ReturnValue::Unit) => {
let mut buffer = runtime.scratch_buf;
buffer.clear();
Ok(ExecReturnValue { status: STATUS_SUCCESS, data: buffer })
Ok(ExecReturnValue {
status: STATUS_SUCCESS,
data: buffer,
})
}
Ok(sp_sandbox::ReturnValue::Value(sp_sandbox::Value::I32(exit_code))) => {
let status = (exit_code & 0xFF).try_into()
let status = (exit_code & 0xFF)
.try_into()
.expect("exit_code is masked into the range of a u8; qed");
Ok(ExecReturnValue { status, data: runtime.scratch_buf })
Ok(ExecReturnValue {
status,
data: runtime.scratch_buf,
})
}
// This should never happen as the return type of exported functions should have been
// validated by the code preparation process. However, because panics are really
// undesirable in the runtime code, we treat this as a trap for now. Eventually, we might
// want to revisit this.
Ok(_) => Err(ExecError { reason: "return type error".into(), buffer: runtime.scratch_buf }),
Ok(_) => Err(ExecError {
reason: "return type error".into(),
buffer: runtime.scratch_buf,
}),
// `Error::Module` is returned only if instantiation or linking failed (i.e.
// wasm binary tried to import a function that is not provided by the host).
// This shouldn't happen because validation process ought to reject such binaries.
//
// Because panics are really undesirable in the runtime code, we treat this as
// a trap for now. Eventually, we might want to revisit this.
Err(sp_sandbox::Error::Module) =>
Err(ExecError { reason: "validation error".into(), buffer: runtime.scratch_buf }),
Err(sp_sandbox::Error::Module) => Err(ExecError {
reason: "validation error".into(),
buffer: runtime.scratch_buf,
}),
// Any other kind of a trap should result in a failure.
Err(sp_sandbox::Error::Execution) | Err(sp_sandbox::Error::OutOfBounds) =>
Err(ExecError { reason: "contract trapped during execution".into(), buffer: runtime.scratch_buf }),
Err(sp_sandbox::Error::Execution) | Err(sp_sandbox::Error::OutOfBounds) => Err(ExecError {
reason: "contract trapped during execution".into(),
buffer: runtime.scratch_buf,
}),
}
}
@@ -188,14 +197,12 @@ impl<T: Trait> Token<T> for RuntimeToken {
data_cost
.and_then(|data_cost| {
topics_cost.and_then(|topics_cost| {
data_cost.checked_add(topics_cost)
topics_cost.and_then(|topics_cost| data_cost.checked_add(topics_cost))
})
})
.and_then(|data_and_topics_cost|
.and_then(|data_and_topics_cost| {
data_and_topics_cost.checked_add(metadata.event_base_cost)
)
},
})
}
DispatchWithWeight(gas) => gas.checked_add(metadata.dispatch_base_cost),
};
@@ -217,7 +224,7 @@ fn charge_gas<T: Trait, Tok: Token<T>>(
GasMeterResult::OutOfGas => {
*special_trap = Some(SpecialTrap::OutOfGas);
Err(sp_sandbox::HostError)
},
}
}
}
@@ -242,7 +249,9 @@ fn read_sandbox_memory<E: Ext>(
)?;
let mut buf = vec![0u8; len as usize];
ctx.memory.get(ptr, buf.as_mut_slice()).map_err(|_| sp_sandbox::HostError)?;
ctx.memory
.get(ptr, buf.as_mut_slice())
.map_err(|_| sp_sandbox::HostError)?;
Ok(buf)
}
@@ -267,7 +276,9 @@ fn read_sandbox_memory_into_scratch<E: Ext>(
)?;
ctx.scratch_buf.resize(len as usize, 0);
ctx.memory.get(ptr, ctx.scratch_buf.as_mut_slice()).map_err(|_| sp_sandbox::HostError)?;
ctx.memory
.get(ptr, ctx.scratch_buf.as_mut_slice())
.map_err(|_| sp_sandbox::HostError)?;
Ok(())
}
@@ -1165,14 +1176,10 @@ where
/// the order of items is not preserved.
fn has_duplicates<T: PartialEq + AsRef<[u8]>>(items: &mut Vec<T>) -> bool {
// Sort the vector
items.sort_unstable_by(|a, b| {
Ord::cmp(a.as_ref(), b.as_ref())
});
items.sort_unstable_by(|a, b| Ord::cmp(a.as_ref(), b.as_ref()));
// And then find any two consecutive equal elements.
items.windows(2).any(|w| {
match w {
items.windows(2).any(|w| match w {
&[ref a, ref b] => a == b,
_ => false,
}
})
}
@@ -17,7 +17,8 @@
#![cfg_attr(not(feature = "std"), no_std)]
use frame_support::{
decl_event, decl_module, dispatch::DispatchResult,
decl_event, decl_module,
dispatch::DispatchResult,
traits::{Currency, ExistenceRequirement, WithdrawReason},
};
use frame_system::ensure_signed;
@@ -0,0 +1,17 @@
[package]
name = "rococo-parachain-primitives"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
[dependencies]
# Substrate dependencies
sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "rococo-branch" }
sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "rococo-branch" }
[features]
default = [ "std" ]
std = [
"sp-runtime/std",
"sp-core/std"
]
@@ -0,0 +1,59 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Cumulus.
// 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/>.
//! Primitives used by the Parachains Tick, Trick and Track.
#![cfg_attr(not(feature = "std"), no_std)]
use sp_runtime::{
generic,
traits::{BlakeTwo256, IdentifyAccount, Verify},
MultiSignature,
};
pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;
/// Opaque block header type.
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
/// Opaque block type.
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
/// Opaque block identifier type.
pub type BlockId = generic::BlockId<Block>;
/// An index to a block.
pub type BlockNumber = u32;
/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
pub type Signature = MultiSignature;
/// Some way of identifying an account on the chain. We intentionally make it equivalent
/// to the public key of our transaction signing scheme.
pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
/// The type for looking up accounts. We don't expect more than 4 billion of them, but you
/// never know...
pub type AccountIndex = u32;
/// Balance of an account.
pub type Balance = u128;
/// Index of a transaction in the chain.
pub type Index = u32;
/// A hash of some data used by the chain.
pub type Hash = sp_core::H256;
/// Digest item type.
pub type DigestItem = generic::DigestItem<Hash>;
@@ -10,6 +10,7 @@ codec = { package = "parity-scale-codec", version = "1.3.0", default-features =
cumulus-token-dealer = { path = "../pallets/token-dealer", default-features = false }
parachain-info = { path = "../pallets/parachain-info", default-features = false }
rococo-parachain-primitives = { path = "../primitives", default-features = false }
# Substrate dependencies
sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "rococo-branch" }
@@ -71,6 +72,7 @@ std = [
"pallet-sudo/std",
"pallet-transaction-payment/std",
"parachain-info/std",
"rococo-parachain-primitives/std",
"cumulus-runtime/std",
"cumulus-parachain-upgrade/std",
"cumulus-message-broker/std",
+8 -50
View File
@@ -22,13 +22,14 @@
#[cfg(feature = "std")]
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
use rococo_parachain_primitives::*;
use sp_api::impl_runtime_apis;
use sp_core::OpaqueMetadata;
use sp_runtime::{
create_runtime_str, generic, impl_opaque_keys,
traits::{BlakeTwo256, Block as BlockT, IdentifyAccount, IdentityLookup, Saturating, Verify},
traits::{BlakeTwo256, Block as BlockT, IdentityLookup, Saturating},
transaction_validity::{TransactionSource, TransactionValidity},
ApplyExtrinsicResult, MultiSignature,
ApplyExtrinsicResult,
};
use sp_std::prelude::*;
#[cfg(feature = "std")]
@@ -51,61 +52,18 @@ pub use pallet_timestamp::Call as TimestampCall;
pub use sp_runtime::BuildStorage;
pub use sp_runtime::{Perbill, Permill};
/// An index to a block.
pub type BlockNumber = u32;
/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
pub type Signature = MultiSignature;
/// Some way of identifying an account on the chain. We intentionally make it equivalent
/// to the public key of our transaction signing scheme.
pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
/// The type for looking up accounts. We don't expect more than 4 billion of them, but you
/// never know...
pub type AccountIndex = u32;
/// Balance of an account.
pub type Balance = u128;
/// Index of a transaction in the chain.
pub type Index = u32;
/// A hash of some data used by the chain.
pub type Hash = sp_core::H256;
/// Digest item type.
pub type DigestItem = generic::DigestItem<Hash>;
/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know
/// the specifics of the runtime. They can then be made to be agnostic over specific formats
/// of data like extrinsics, allowing for them to continue syncing the network through upgrades
/// to even the core datastructures.
pub mod opaque {
use super::*;
pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;
/// Opaque block header type.
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
/// Opaque block type.
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
/// Opaque block identifier type.
pub type BlockId = generic::BlockId<Block>;
pub type SessionHandlers = ();
impl_opaque_keys! {
pub struct SessionKeys {}
}
}
/// This runtime version.
pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("cumulus-test-parachain"),
impl_name: create_runtime_str!("cumulus-test-parachain"),
authoring_version: 1,
spec_version: 1,
spec_version: 2,
impl_version: 1,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
@@ -266,8 +224,8 @@ impl parachain_info::Trait for Runtime {}
construct_runtime! {
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
NodeBlock = rococo_parachain_primitives::Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
System: frame_system::{Module, Call, Storage, Config, Event<T>},
Timestamp: pallet_timestamp::{Module, Call, Storage, Inherent},
@@ -378,11 +336,11 @@ impl_runtime_apis! {
fn decode_session_keys(
encoded: Vec<u8>,
) -> Option<Vec<(Vec<u8>, sp_core::crypto::KeyTypeId)>> {
opaque::SessionKeys::decode_into_raw_public_keys(&encoded)
SessionKeys::decode_into_raw_public_keys(&encoded)
}
fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {
opaque::SessionKeys::generate(seed)
SessionKeys::generate(seed)
}
}
}
+81 -17
View File
@@ -15,19 +15,20 @@
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
use cumulus_primitives::ParaId;
use parachain_runtime::{
AccountId, BalancesConfig, GenesisConfig, Signature, SudoConfig, SystemConfig,
ParachainInfoConfig, WASM_BINARY,
};
use hex_literal::hex;
use rococo_parachain_primitives::{AccountId, Signature};
use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup};
use sc_service::ChainType;
use serde::{Deserialize, Serialize};
use sp_core::{sr25519, Pair, Public};
use sp_runtime::traits::{IdentifyAccount, Verify};
use hex_literal::hex;
/// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type.
pub type ChainSpec = sc_service::GenericChainSpec<GenesisConfig, Extensions>;
/// Specialized `ChainSpec` for the normal parachain runtime.
pub type ChainSpec = sc_service::GenericChainSpec<parachain_runtime::GenesisConfig, Extensions>;
/// Specialized `ChainSpec` for the contracts parachain runtime.
pub type ContractsChainSpec =
sc_service::GenericChainSpec<parachain_contracts_runtime::GenesisConfig, Extensions>;
/// Helper function to generate a crypto pair from seed
pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {
@@ -99,6 +100,42 @@ pub fn get_chain_spec(id: ParaId) -> ChainSpec {
)
}
pub fn get_contracts_chain_spec(id: ParaId) -> ContractsChainSpec {
ContractsChainSpec::from_genesis(
"Contracts Local Testnet",
"contracts_local_testnet",
ChainType::Local,
move || {
contracts_testnet_genesis(
get_account_id_from_seed::<sr25519::Public>("Alice"),
vec![
get_account_id_from_seed::<sr25519::Public>("Alice"),
get_account_id_from_seed::<sr25519::Public>("Bob"),
get_account_id_from_seed::<sr25519::Public>("Charlie"),
get_account_id_from_seed::<sr25519::Public>("Dave"),
get_account_id_from_seed::<sr25519::Public>("Eve"),
get_account_id_from_seed::<sr25519::Public>("Ferdie"),
get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
get_account_id_from_seed::<sr25519::Public>("Charlie//stash"),
get_account_id_from_seed::<sr25519::Public>("Dave//stash"),
get_account_id_from_seed::<sr25519::Public>("Eve//stash"),
get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),
],
id,
)
},
vec![],
None,
None,
None,
Extensions {
relay_chain: "westend-dev".into(),
para_id: id.into(),
},
)
}
pub fn staging_test_net(id: ParaId) -> ChainSpec {
ChainSpec::from_genesis(
"Staging Testnet",
@@ -107,7 +144,9 @@ pub fn staging_test_net(id: ParaId) -> ChainSpec {
move || {
testnet_genesis(
hex!["9ed7705e3c7da027ba0583a22a3212042f7e715d3c168ba14f1424e2bc111d00"].into(),
vec![hex!["9ed7705e3c7da027ba0583a22a3212042f7e715d3c168ba14f1424e2bc111d00"].into()],
vec![
hex!["9ed7705e3c7da027ba0583a22a3212042f7e715d3c168ba14f1424e2bc111d00"].into(),
],
id,
)
},
@@ -126,22 +165,47 @@ fn testnet_genesis(
root_key: AccountId,
endowed_accounts: Vec<AccountId>,
id: ParaId,
) -> GenesisConfig {
GenesisConfig {
frame_system: Some(SystemConfig {
code: WASM_BINARY.expect("WASM binary was not build, please build it!").to_vec(),
) -> parachain_runtime::GenesisConfig {
parachain_runtime::GenesisConfig {
frame_system: Some(parachain_runtime::SystemConfig {
code: parachain_runtime::WASM_BINARY
.expect("WASM binary was not build, please build it!")
.to_vec(),
changes_trie_config: Default::default(),
}),
pallet_balances: Some(BalancesConfig {
pallet_balances: Some(parachain_runtime::BalancesConfig {
balances: endowed_accounts
.iter()
.cloned()
.map(|k| (k, 1 << 60))
.collect(),
}),
pallet_sudo: Some(SudoConfig { key: root_key }),
parachain_info: Some(ParachainInfoConfig { parachain_id: id }),
// TODO: add contracts genesis for the contracts runtime
// pallet_contracts: Some(parachain_runtime::ContractsConfig { current_schedule: Default::default() }),
pallet_sudo: Some(parachain_runtime::SudoConfig { key: root_key }),
parachain_info: Some(parachain_runtime::ParachainInfoConfig { parachain_id: id }),
}
}
fn contracts_testnet_genesis(
root_key: AccountId,
endowed_accounts: Vec<AccountId>,
id: ParaId,
) -> parachain_contracts_runtime::GenesisConfig {
parachain_contracts_runtime::GenesisConfig {
frame_system: Some(parachain_contracts_runtime::SystemConfig {
code: parachain_contracts_runtime::WASM_BINARY
.expect("WASM binary was not build, please build it!")
.to_vec(),
changes_trie_config: Default::default(),
}),
pallet_balances: Some(parachain_contracts_runtime::BalancesConfig {
balances: endowed_accounts
.iter()
.cloned()
.map(|k| (k, 1 << 60))
.collect(),
}),
pallet_sudo: Some(parachain_contracts_runtime::SudoConfig { key: root_key }),
parachain_info: Some(parachain_contracts_runtime::ParachainInfoConfig { parachain_id: id }),
cumulus_pallet_contracts: None,
}
}
+40 -5
View File
@@ -24,8 +24,8 @@ use log::info;
use parachain_runtime::Block;
use polkadot_parachain::primitives::AccountIdConversion;
use sc_cli::{
ChainSpec, CliConfiguration, ImportParams, KeystoreParams, NetworkParams, Result,
RuntimeVersion, SharedParams, SubstrateCli, DefaultConfigurationValues,
ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams,
NetworkParams, Result, RuntimeVersion, SharedParams, SubstrateCli,
};
use sc_service::config::{BasePath, PrometheusConfig};
use sp_core::hexdisplay::HexDisplay;
@@ -77,6 +77,9 @@ impl SubstrateCli for Cli {
"track" => Ok(Box::new(chain_spec::ChainSpec::from_json_bytes(
&include_bytes!("../res/track.json")[..],
)?)),
"contracts" => Ok(Box::new(chain_spec::get_contracts_chain_spec(
self.run.parachain_id.unwrap_or(100).into(),
))),
"" => Ok(Box::new(chain_spec::get_chain_spec(
self.run.parachain_id.unwrap_or(100).into(),
))),
@@ -167,6 +170,10 @@ fn extract_genesis_wasm(chain_spec: &Box<dyn sc_service::ChainSpec>) -> Result<V
.ok_or_else(|| "Could not find wasm file in genesis state!".into())
}
fn use_contracts_runtime(chain_spec: &Box<dyn ChainSpec>) -> bool {
chain_spec.id().starts_with("trick") || chain_spec.id().starts_with("contracts")
}
/// Parse command line arguments into service configuration.
pub fn run() -> Result<()> {
let cli = Cli::from_args();
@@ -175,8 +182,12 @@ pub fn run() -> Result<()> {
Some(Subcommand::Base(subcommand)) => {
let runner = cli.create_runner(subcommand)?;
if use_contracts_runtime(&runner.config().chain_spec) {
runner.run_subcommand(subcommand, |mut config| {
let params = crate::service::new_partial(&mut config)?;
let params = crate::service::new_partial::<
parachain_contracts_runtime::RuntimeApi,
crate::service::ContractsRuntimeExecutor,
>(&mut config)?;
Ok((
params.client,
@@ -185,6 +196,21 @@ pub fn run() -> Result<()> {
params.task_manager,
))
})
} else {
runner.run_subcommand(subcommand, |mut config| {
let params = crate::service::new_partial::<
parachain_runtime::RuntimeApi,
crate::service::RuntimeExecutor,
>(&mut config)?;
Ok((
params.client,
params.backend,
params.import_queue,
params.task_manager,
))
})
}
}
Some(Subcommand::ExportGenesisState(params)) => {
sc_cli::init_logger("");
@@ -256,14 +282,23 @@ pub fn run() -> Result<()> {
if cli.run.base.validator { "yes" } else { "no" }
);
crate::service::run_node(
if use_contracts_runtime(&config.chain_spec) {
crate::service::start_contracts_node(
config,
key,
polkadot_config,
id,
cli.run.base.validator,
)
.map(|(x, _)| x)
} else {
crate::service::start_node(
config,
key,
polkadot_config,
id,
cli.run.base.validator,
).map(|r| r.0)
}
})
}
}
@@ -60,7 +60,8 @@ async fn integration_test() {
INTEGRATION_TEST_ALLOWED_TIME
.and_then(|x| x.parse().ok())
.unwrap_or(600),
)).fuse();
))
.fuse();
let t2 = async {
let para_id = ParaId::from(100);
@@ -105,7 +106,7 @@ async fn integration_test() {
let parachain_config =
parachain_config(task_executor.clone(), Charlie, vec![], para_id).unwrap();
let (_service, charlie_client) =
crate::service::run_node(parachain_config, key, polkadot_config, para_id, true)
crate::service::start_node(parachain_config, key, polkadot_config, para_id, true)
.unwrap();
sleep(Duration::from_secs(3)).await;
charlie_client.wait_for_blocks(4).await;
+112 -56
View File
@@ -20,67 +20,74 @@ use cumulus_service::{
prepare_node_config, start_collator, start_full_node, StartCollatorParams, StartFullNodeParams,
};
use polkadot_primitives::v0::CollatorPair;
use rococo_parachain_primitives::Block;
use sc_executor::native_executor_instance;
pub use sc_executor::NativeExecutor;
use sc_informant::OutputFormat;
use sc_service::{Configuration, PartialComponents, Role, TFullBackend, TFullClient, TaskManager};
use sp_api::ConstructRuntimeApi;
use sp_runtime::traits::BlakeTwo256;
use sp_trie::PrefixedMemoryDB;
use std::sync::Arc;
// Our native executor instance.
// Native executor instance.
native_executor_instance!(
pub Executor,
pub RuntimeExecutor,
parachain_runtime::api::dispatch,
parachain_runtime::native_version,
);
// Native executor instance for the contracts runtime.
native_executor_instance!(
pub ContractsRuntimeExecutor,
parachain_contracts_runtime::api::dispatch,
parachain_contracts_runtime::native_version,
);
/// Starts a `ServiceBuilder` for a full service.
///
/// Use this macro if you don't actually need the full service, but just the builder in order to
/// be able to perform chain operations.
pub fn new_partial(
pub fn new_partial<RuntimeApi, Executor>(
config: &mut Configuration,
) -> Result<
PartialComponents<
TFullClient<
parachain_runtime::opaque::Block,
parachain_runtime::RuntimeApi,
crate::service::Executor,
>,
TFullBackend<parachain_runtime::opaque::Block>,
TFullClient<Block, RuntimeApi, Executor>,
TFullBackend<Block>,
(),
sp_consensus::import_queue::BasicQueue<
parachain_runtime::opaque::Block,
PrefixedMemoryDB<BlakeTwo256>,
>,
sc_transaction_pool::FullPool<
parachain_runtime::opaque::Block,
TFullClient<
parachain_runtime::opaque::Block,
parachain_runtime::RuntimeApi,
crate::service::Executor,
>,
>,
sp_consensus::import_queue::BasicQueue<Block, PrefixedMemoryDB<BlakeTwo256>>,
sc_transaction_pool::FullPool<Block, TFullClient<Block, RuntimeApi, Executor>>,
(),
>,
sc_service::Error,
> {
>
where
RuntimeApi: ConstructRuntimeApi<Block, TFullClient<Block, RuntimeApi, Executor>>
+ Send
+ Sync
+ 'static,
RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block>
+ sp_api::Metadata<Block>
+ sp_session::SessionKeys<Block>
+ sp_api::ApiExt<
Block,
Error = sp_blockchain::Error,
StateBackend = sc_client_api::StateBackendFor<TFullBackend<Block>, Block>,
> + sp_offchain::OffchainWorkerApi<Block>
+ sp_block_builder::BlockBuilder<Block>,
sc_client_api::StateBackendFor<TFullBackend<Block>, Block>: sp_api::StateBackend<BlakeTwo256>,
Executor: sc_executor::NativeExecutionDispatch + 'static,
{
let inherent_data_providers = sp_inherents::InherentDataProviders::new();
let (client, backend, keystore, task_manager) = sc_service::new_full_parts::<
parachain_runtime::opaque::Block,
parachain_runtime::RuntimeApi,
crate::service::Executor,
>(&config)?;
let (client, backend, keystore, task_manager) =
sc_service::new_full_parts::<Block, RuntimeApi, Executor>(&config)?;
let client = Arc::new(client);
//let select_chain = sc_consensus::LongestChain::new(backend.clone());
let registry = config.prometheus_registry();
let transaction_pool = sc_transaction_pool::BasicPool::new_full(
config.transaction_pool.clone(),
//std::sync::Arc::new(pool_api),
config.prometheus_registry(),
task_manager.spawn_handle(),
client.clone(),
@@ -109,25 +116,39 @@ pub fn new_partial(
Ok(params)
}
/// Run a node with the given parachain `Configuration` and relay chain `Configuration`
/// Start a node with the given parachain `Configuration` and relay chain `Configuration`.
///
/// This function blocks until done.
pub fn run_node(
/// This is the actual implementation that is abstract over the executor and the runtime api.
fn start_node_impl<RuntimeApi, Executor, RB>(
parachain_config: Configuration,
collator_key: Arc<CollatorPair>,
mut polkadot_config: polkadot_collator::Configuration,
id: polkadot_primitives::v0::Id,
validator: bool,
) -> sc_service::error::Result<(
TaskManager,
Arc<
TFullClient<
parachain_runtime::opaque::Block,
parachain_runtime::RuntimeApi,
crate::service::Executor,
>,
>,
)> {
rpc_ext_builder: RB,
) -> sc_service::error::Result<(TaskManager, Arc<TFullClient<Block, RuntimeApi, Executor>>)>
where
RuntimeApi: ConstructRuntimeApi<Block, TFullClient<Block, RuntimeApi, Executor>>
+ Send
+ Sync
+ 'static,
RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block>
+ sp_api::Metadata<Block>
+ sp_session::SessionKeys<Block>
+ sp_api::ApiExt<
Block,
Error = sp_blockchain::Error,
StateBackend = sc_client_api::StateBackendFor<TFullBackend<Block>, Block>,
> + sp_offchain::OffchainWorkerApi<Block>
+ sp_block_builder::BlockBuilder<Block>,
sc_client_api::StateBackendFor<TFullBackend<Block>, Block>: sp_api::StateBackend<BlakeTwo256>,
Executor: sc_executor::NativeExecutionDispatch + 'static,
RB: Fn(
Arc<TFullClient<Block, RuntimeApi, Executor>>,
) -> jsonrpc_core::IoHandler<sc_rpc::Metadata>
+ Send
+ 'static,
{
if matches!(parachain_config.role, Role::Light) {
return Err("Light client not supported!".into());
}
@@ -143,7 +164,7 @@ pub fn run_node(
prefix: format!("[{}] ", Color::Blue.bold().paint("Relaychain")),
};
let params = new_partial(&mut parachain_config)?;
let params = new_partial::<RuntimeApi, Executor>(&mut parachain_config)?;
params
.inherent_data_providers
.register_provider(sp_timestamp::InherentDataProvider)
@@ -175,20 +196,9 @@ pub fn run_node(
})?;
let rpc_extensions_builder = {
let _client = client.clone();
let client = client.clone();
Box::new(move |_deny_unsafe| {
let io = jsonrpc_core::IoHandler::default();
// TODO: add this rpc when running the contracts runtime
/*
use pallet_contracts_rpc::{Contracts, ContractsApi};
io.extend_with(
ContractsApi::to_delegate(Contracts::new(client.clone()))
);
*/
io
})
Box::new(move |_deny_unsafe| rpc_ext_builder(client.clone()))
};
sc_service::spawn_tasks(sc_service::SpawnTasksParams {
@@ -249,3 +259,49 @@ pub fn run_node(
Ok((task_manager, client))
}
/// Start a normal parachain node.
pub fn start_node(
parachain_config: Configuration,
collator_key: Arc<CollatorPair>,
polkadot_config: polkadot_collator::Configuration,
id: polkadot_primitives::v0::Id,
validator: bool,
) -> sc_service::error::Result<(
TaskManager,
Arc<TFullClient<Block, parachain_runtime::RuntimeApi, RuntimeExecutor>>,
)> {
start_node_impl::<parachain_runtime::RuntimeApi, RuntimeExecutor, _>(
parachain_config,
collator_key,
polkadot_config,
id,
validator,
|_| Default::default(),
)
}
/// Start a contracts parachain node.
pub fn start_contracts_node(
parachain_config: Configuration,
collator_key: Arc<CollatorPair>,
polkadot_config: polkadot_collator::Configuration,
id: polkadot_primitives::v0::Id,
validator: bool,
) -> sc_service::error::Result<TaskManager> {
start_node_impl::<parachain_contracts_runtime::RuntimeApi, ContractsRuntimeExecutor, _>(
parachain_config,
collator_key,
polkadot_config,
id,
validator,
|client| {
let mut io = jsonrpc_core::IoHandler::default();
use cumulus_pallet_contracts_rpc::{Contracts, ContractsApi};
io.extend_with(ContractsApi::to_delegate(Contracts::new(client)));
io
},
)
.map(|r| r.0)
}
@@ -51,7 +51,9 @@ static mut STORAGE: Option<Box<dyn Storage>> = None;
/// Panics if the [`STORAGE`] is not initialized.
fn with_storage<R>(call: impl FnOnce(&mut dyn Storage) -> R) -> R {
let mut storage = unsafe {
STORAGE.take().expect("`STORAGE` needs to be set before calling this function.")
STORAGE
.take()
.expect("`STORAGE` needs to be set before calling this function.")
};
let res = call(&mut *storage);
@@ -142,7 +144,8 @@ pub fn validate_block<B: BlockT, E: ExecuteBlock<B>>(params: ValidationParams) -
// If in the course of block execution new validation code was set, insert
// its scheduled upgrade so we can validate that block number later.
let new_validation_code = with_storage(|storage| storage.get(NEW_VALIDATION_CODE)).map(ValidationCode);
let new_validation_code =
with_storage(|storage| storage.get(NEW_VALIDATION_CODE)).map(ValidationCode);
if new_validation_code.is_some() && validation_function_params.code_upgrade_allowed.is_none() {
panic!("Attempt to upgrade validation function when not permitted!");
}
@@ -154,7 +157,8 @@ pub fn validate_block<B: BlockT, E: ExecuteBlock<B>>(params: ValidationParams) -
None => Vec::new(),
};
let processed_downward_messages = with_storage(|storage| storage.get(PROCESSED_DOWNWARD_MESSAGES))
let processed_downward_messages =
with_storage(|storage| storage.get(PROCESSED_DOWNWARD_MESSAGES))
.and_then(|v| Decode::decode(&mut &v[..]).ok())
.unwrap_or_default();
@@ -266,13 +270,11 @@ impl<B: BlockT> Storage for WitnessStorage<B> {
let witness_data = &self.witness_data;
let storage_root = &self.storage_root;
let current_value = overlay.entry(key.to_vec()).or_insert_with(||
read_trie_value::<Layout<HashFor<B>>, _>(
witness_data,
storage_root,
key,
).ok().flatten()
);
let current_value = overlay.entry(key.to_vec()).or_insert_with(|| {
read_trie_value::<Layout<HashFor<B>>, _>(witness_data, storage_root, key)
.ok()
.flatten()
});
let item = current_value.take().unwrap_or_default();
*current_value = Some(
+1 -1
View File
@@ -130,7 +130,7 @@ fn build_block_with_proof(
built_block.block,
built_block
.proof
.expect("We enabled proof recording before.")
.expect("We enabled proof recording before."),
)
}