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