Charge fees for parachain execution (#293)

* burn parachain funds depending on candidate fees

* charge fees when executing parachain

* fix test compilation

* branch grumble addressed

* test that Balance >= usize
This commit is contained in:
Robert Habermeier
2019-06-25 21:09:54 +02:00
committed by Bastian Köcher
parent 9004fb3f97
commit 7a5b9bddf5
7 changed files with 238 additions and 40 deletions
+17 -10
View File
@@ -56,7 +56,7 @@ use primitives::{ed25519, Pair};
use polkadot_primitives::{BlockId, SessionKey, Hash, Block}; use polkadot_primitives::{BlockId, SessionKey, Hash, Block};
use polkadot_primitives::parachain::{ use polkadot_primitives::parachain::{
self, BlockData, DutyRoster, HeadData, ConsolidatedIngress, Message, Id as ParaId, Extrinsic, self, BlockData, DutyRoster, HeadData, ConsolidatedIngress, Message, Id as ParaId, Extrinsic,
PoVBlock, PoVBlock, Status as ParachainStatus,
}; };
use polkadot_cli::{PolkadotService, CustomConfiguration, ParachainHost}; use polkadot_cli::{PolkadotService, CustomConfiguration, ParachainHost};
use polkadot_cli::{Worker, IntoExit, ProvideRuntimeApi, TaskExecutor}; use polkadot_cli::{Worker, IntoExit, ProvideRuntimeApi, TaskExecutor};
@@ -105,7 +105,7 @@ pub trait ParachainContext: Clone {
fn produce_candidate<I: IntoIterator<Item=(ParaId, Message)>>( fn produce_candidate<I: IntoIterator<Item=(ParaId, Message)>>(
&self, &self,
relay_parent: Hash, relay_parent: Hash,
last_head: HeadData, status: ParachainStatus,
ingress: I, ingress: I,
) -> Self::ProduceCandidate; ) -> Self::ProduceCandidate;
} }
@@ -128,7 +128,7 @@ pub trait RelayChainContext {
pub fn collate<'a, R, P>( pub fn collate<'a, R, P>(
relay_parent: Hash, relay_parent: Hash,
local_id: ParaId, local_id: ParaId,
last_head: HeadData, parachain_status: ParachainStatus,
relay_context: R, relay_context: R,
para_context: P, para_context: P,
key: Arc<ed25519::Pair>, key: Arc<ed25519::Pair>,
@@ -146,7 +146,7 @@ pub fn collate<'a, R, P>(
.and_then(move |ingress| { .and_then(move |ingress| {
para_context.produce_candidate( para_context.produce_candidate(
relay_parent, relay_parent,
last_head, parachain_status,
ingress.0.iter().flat_map(|&(id, ref msgs)| msgs.iter().cloned().map(move |msg| (id, msg))) ingress.0.iter().flat_map(|&(id, ref msgs)| msgs.iter().cloned().map(move |msg| (id, msg)))
) )
.into_future() .into_future()
@@ -311,8 +311,8 @@ impl<P, E> Worker for CollationNode<P, E> where
let work = future::lazy(move || { let work = future::lazy(move || {
let api = client.runtime_api(); let api = client.runtime_api();
let last_head = match try_fr!(api.parachain_head(&id, para_id)) { let status = match try_fr!(api.parachain_status(&id, para_id)) {
Some(last_head) => last_head, Some(status) => status,
None => return future::Either::A(future::ok(())), None => return future::Either::A(future::ok(())),
}; };
@@ -333,7 +333,7 @@ impl<P, E> Worker for CollationNode<P, E> where
let collation_work = collate( let collation_work = collate(
relay_parent, relay_parent,
para_id, para_id,
HeadData(last_head), status,
context, context,
parachain_context, parachain_context,
key, key,
@@ -403,7 +403,7 @@ pub fn run_collator<P, E, I, ArgT>(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::collections::HashMap; use std::collections::HashMap;
use polkadot_primitives::parachain::OutgoingMessage; use polkadot_primitives::parachain::{OutgoingMessage, FeeSchedule};
use keyring::AuthorityKeyring; use keyring::AuthorityKeyring;
use super::*; use super::*;
@@ -433,7 +433,7 @@ mod tests {
fn produce_candidate<I: IntoIterator<Item=(ParaId, Message)>>( fn produce_candidate<I: IntoIterator<Item=(ParaId, Message)>>(
&self, &self,
_relay_parent: Hash, _relay_parent: Hash,
_last_head: HeadData, _status: ParachainStatus,
ingress: I, ingress: I,
) -> Result<(BlockData, HeadData, Extrinsic), InvalidHead> { ) -> Result<(BlockData, HeadData, Extrinsic), InvalidHead> {
// send messages right back. // send messages right back.
@@ -484,7 +484,14 @@ mod tests {
let collation = collate( let collation = collate(
Default::default(), Default::default(),
id, id,
HeadData(vec![5]), ParachainStatus {
head_data: HeadData(vec![5]),
balance: 10,
fee_schedule: FeeSchedule {
base: 0,
per_byte: 1,
},
},
context.clone(), context.clone(),
DummyParachainContext, DummyParachainContext,
AuthorityKeyring::Alice.pair().into(), AuthorityKeyring::Alice.pair().into(),
+12 -4
View File
@@ -30,7 +30,8 @@ use polkadot_validation::{SharedTable, MessagesFrom, Network};
use polkadot_primitives::{SessionKey, Block, Hash, Header, BlockId}; use polkadot_primitives::{SessionKey, Block, Hash, Header, BlockId};
use polkadot_primitives::parachain::{ use polkadot_primitives::parachain::{
Id as ParaId, Chain, DutyRoster, ParachainHost, OutgoingMessage, Id as ParaId, Chain, DutyRoster, ParachainHost, OutgoingMessage,
ValidatorId, StructuredUnroutedIngress, BlockIngressRoots, ValidatorId, StructuredUnroutedIngress, BlockIngressRoots, Status,
FeeSchedule, HeadData,
}; };
use parking_lot::Mutex; use parking_lot::Mutex;
use substrate_client::error::Result as ClientResult; use substrate_client::error::Result as ClientResult;
@@ -282,14 +283,21 @@ impl ParachainHost<Block> for RuntimeApi {
Ok(NativeOrEncoded::Native(self.data.lock().active_parachains.clone())) Ok(NativeOrEncoded::Native(self.data.lock().active_parachains.clone()))
} }
fn ParachainHost_parachain_head_runtime_api_impl( fn ParachainHost_parachain_status_runtime_api_impl(
&self, &self,
_at: &BlockId, _at: &BlockId,
_: ExecutionContext, _: ExecutionContext,
_: Option<ParaId>, _: Option<ParaId>,
_: Vec<u8>, _: Vec<u8>,
) -> ClientResult<NativeOrEncoded<Option<Vec<u8>>>> { ) -> ClientResult<NativeOrEncoded<Option<Status>>> {
Ok(NativeOrEncoded::Native(Some(Vec::new()))) Ok(NativeOrEncoded::Native(Some(Status {
head_data: HeadData(Vec::new()),
balance: 0,
fee_schedule: FeeSchedule {
base: 0,
per_byte: 0,
}
})))
} }
fn ParachainHost_parachain_code_runtime_api_impl( fn ParachainHost_parachain_code_runtime_api_impl(
+51 -3
View File
@@ -28,7 +28,9 @@ use serde::{Serialize, Deserialize};
use primitives::bytes; use primitives::bytes;
use primitives::ed25519; use primitives::ed25519;
pub use polkadot_parachain::{Id, AccountIdConversion, ParachainDispatchOrigin}; pub use polkadot_parachain::{
Id, AccountIdConversion, ParachainDispatchOrigin,
};
/// Identity that collators use. /// Identity that collators use.
pub type CollatorId = ed25519::Public; pub type CollatorId = ed25519::Public;
@@ -328,6 +330,39 @@ impl AttestedCandidate {
} }
} }
/// A fee schedule for messages. This is a linear function in the number of bytes of a message.
#[derive(PartialEq, Eq, PartialOrd, Hash, Default, Clone, Copy, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
pub struct FeeSchedule {
/// The base fee charged for all messages.
pub base: Balance,
/// The per-byte fee charged on top of that.
pub per_byte: Balance,
}
impl FeeSchedule {
/// Compute the fee for a message of given size.
pub fn compute_fee(&self, n_bytes: usize) -> Balance {
use rstd::mem;
debug_assert!(mem::size_of::<Balance>() >= mem::size_of::<usize>());
let n_bytes = n_bytes as Balance;
self.base.saturating_add(n_bytes.saturating_mul(self.per_byte))
}
}
/// Current Status of a parachain.
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
pub struct Status {
/// The head of the parachain.
pub head_data: HeadData,
/// The current balance of the parachain.
pub balance: Balance,
/// The fee schedule for messages coming from this parachain.
pub fee_schedule: FeeSchedule,
}
substrate_client::decl_runtime_apis! { substrate_client::decl_runtime_apis! {
/// The API for querying the state of parachains on-chain. /// The API for querying the state of parachains on-chain.
pub trait ParachainHost { pub trait ParachainHost {
@@ -337,8 +372,8 @@ substrate_client::decl_runtime_apis! {
fn duty_roster() -> DutyRoster; fn duty_roster() -> DutyRoster;
/// Get the currently active parachains. /// Get the currently active parachains.
fn active_parachains() -> Vec<Id>; fn active_parachains() -> Vec<Id>;
/// Get the given parachain's head data blob. /// Get the given parachain's status.
fn parachain_head(id: Id) -> Option<Vec<u8>>; fn parachain_status(id: Id) -> Option<Status>;
/// Get the given parachain's head code blob. /// Get the given parachain's head code blob.
fn parachain_code(id: Id) -> Option<Vec<u8>>; fn parachain_code(id: Id) -> Option<Vec<u8>>;
/// Get all the unrouted ingress roots at the given block that /// Get all the unrouted ingress roots at the given block that
@@ -354,3 +389,16 @@ pub mod id {
/// Parachain host runtime API id. /// Parachain host runtime API id.
pub const PARACHAIN_HOST: ApiId = *b"parahost"; pub const PARACHAIN_HOST: ApiId = *b"parahost";
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn balance_bigger_than_usize() {
let zero_b: Balance = 0;
let zero_u: usize = 0;
assert!(zero_b.leading_zeros() >= zero_u.leading_zeros());
}
}
+3 -2
View File
@@ -234,6 +234,7 @@ impl grandpa::Trait for Runtime {
impl parachains::Trait for Runtime { impl parachains::Trait for Runtime {
type Origin = Origin; type Origin = Origin;
type Call = Call; type Call = Call;
type ParachainCurrency = Balances;
} }
parameter_types!{ parameter_types!{
@@ -364,8 +365,8 @@ impl_runtime_apis! {
fn active_parachains() -> Vec<parachain::Id> { fn active_parachains() -> Vec<parachain::Id> {
Parachains::active_parachains() Parachains::active_parachains()
} }
fn parachain_head(id: parachain::Id) -> Option<Vec<u8>> { fn parachain_status(id: parachain::Id) -> Option<parachain::Status> {
Parachains::parachain_head(&id) Parachains::parachain_status(&id)
} }
fn parachain_code(id: parachain::Id) -> Option<Vec<u8>> { fn parachain_code(id: parachain::Id) -> Option<Vec<u8>> {
Parachains::parachain_code(&id) Parachains::parachain_code(&id)
+62 -7
View File
@@ -25,13 +25,14 @@ use bitvec::{bitvec, BigEndian};
use sr_primitives::traits::{ use sr_primitives::traits::{
Hash as HashT, BlakeTwo256, Member, CheckedConversion, Saturating, One, Zero, Hash as HashT, BlakeTwo256, Member, CheckedConversion, Saturating, One, Zero,
}; };
use primitives::{Hash, parachain::{ use primitives::{Hash, Balance, parachain::{
Id as ParaId, Chain, DutyRoster, AttestedCandidate, Statement, AccountIdConversion, self, Id as ParaId, Chain, DutyRoster, AttestedCandidate, Statement, AccountIdConversion,
ParachainDispatchOrigin, UpwardMessage, BlockIngressRoots, ParachainDispatchOrigin, UpwardMessage, BlockIngressRoots,
}}; }};
use {system, session}; use {system, session};
use srml_support::{ use srml_support::{
StorageValue, StorageMap, storage::AppendableStorageMap, Parameter, Dispatchable, dispatch::Result StorageValue, StorageMap, storage::AppendableStorageMap, Parameter, Dispatchable, dispatch::Result,
traits::{Currency, WithdrawReason, ExistenceRequirement}
}; };
#[cfg(feature = "std")] #[cfg(feature = "std")]
@@ -142,12 +143,46 @@ impl<T: Trait> ParachainRegistrar<T::AccountId> for Module<T> {
} }
} }
// wrapper trait because an associated type of `Currency<Self::AccountId,Balance=Balance>`
// doesn't work.`
pub trait ParachainCurrency<AccountId> {
fn free_balance(para_id: ParaId) -> Balance;
fn deduct(para_id: ParaId, amount: Balance) -> Result;
}
impl<AccountId, T: Currency<AccountId>> ParachainCurrency<AccountId> for T where
T::Balance: From<Balance> + Into<Balance>,
ParaId: AccountIdConversion<AccountId>,
{
fn free_balance(para_id: ParaId) -> Balance {
let para_account = para_id.into_account();
T::free_balance(&para_account).into()
}
fn deduct(para_id: ParaId, amount: Balance) -> Result {
let para_account = para_id.into_account();
// burn the fee.
let _ = T::withdraw(
&para_account,
amount.into(),
WithdrawReason::Fee,
ExistenceRequirement::KeepAlive,
)?;
Ok(())
}
}
pub trait Trait: session::Trait { pub trait Trait: session::Trait {
/// The outer origin type. /// The outer origin type.
type Origin: From<Origin> + From<system::RawOrigin<Self::AccountId>>; type Origin: From<Origin> + From<system::RawOrigin<Self::AccountId>>;
/// The outer call dispatch type. /// The outer call dispatch type.
type Call: Parameter + Dispatchable<Origin=<Self as Trait>::Origin>; type Call: Parameter + Dispatchable<Origin=<Self as Trait>::Origin>;
/// Some way of interacting with balances for fees.
type ParachainCurrency: ParachainCurrency<Self::AccountId>;
} }
/// Origin for the parachains module. /// Origin for the parachains module.
@@ -269,7 +304,7 @@ decl_module! {
} }
} }
Self::check_attestations(&heads)?; Self::check_candidates(&heads)?;
let current_number = <system::Module<T>>::block_number(); let current_number = <system::Module<T>>::block_number();
@@ -549,6 +584,21 @@ impl<T: Trait> Module<T> {
.collect()) .collect())
} }
/// Get the parachain status necessary for validation.
pub fn parachain_status(id: &parachain::Id) -> Option<parachain::Status> {
let balance = T::ParachainCurrency::free_balance(*id);
Self::parachain_head(id).map(|head_data| parachain::Status {
head_data: parachain::HeadData(head_data),
balance,
// TODO: https://github.com/paritytech/polkadot/issues/92
// plug in some real values here. most likely governable.
fee_schedule: parachain::FeeSchedule {
base: 0,
per_byte: 0,
}
})
}
fn check_egress_queue_roots(head: &AttestedCandidate, active_parachains: &[ParaId]) -> Result { fn check_egress_queue_roots(head: &AttestedCandidate, active_parachains: &[ParaId]) -> Result {
let mut last_egress_id = None; let mut last_egress_id = None;
let mut iter = active_parachains.iter(); let mut iter = active_parachains.iter();
@@ -584,7 +634,7 @@ impl<T: Trait> Module<T> {
// check the attestations on these candidates. The candidates should have been checked // check the attestations on these candidates. The candidates should have been checked
// that each candidates' chain ID is valid. // that each candidates' chain ID is valid.
fn check_attestations(attested_candidates: &[AttestedCandidate]) -> Result { fn check_candidates(attested_candidates: &[AttestedCandidate]) -> Result{
use primitives::parachain::ValidityAttestation; use primitives::parachain::ValidityAttestation;
use sr_primitives::traits::Verify; use sr_primitives::traits::Verify;
@@ -661,7 +711,8 @@ impl<T: Trait> Module<T> {
let mut validator_groups = GroupedDutyIter::new(&sorted_validators[..]); let mut validator_groups = GroupedDutyIter::new(&sorted_validators[..]);
for candidate in attested_candidates { for candidate in attested_candidates {
let validator_group = validator_groups.group_for(candidate.parachain_index()) let para_id = candidate.parachain_index();
let validator_group = validator_groups.group_for(para_id)
.ok_or("no validator group for parachain")?; .ok_or("no validator group for parachain")?;
ensure!( ensure!(
@@ -669,6 +720,9 @@ impl<T: Trait> Module<T> {
"Not enough validity attestations" "Not enough validity attestations"
); );
let fees = candidate.candidate().fees;
T::ParachainCurrency::deduct(para_id, fees)?;
let mut candidate_hash = None; let mut candidate_hash = None;
let mut encoded_implicit = None; let mut encoded_implicit = None;
let mut encoded_explicit = None; let mut encoded_explicit = None;
@@ -824,7 +878,7 @@ mod tests {
} }
impl balances::Trait for Test { impl balances::Trait for Test {
type Balance = u64; type Balance = Balance;
type OnFreeBalanceZero = (); type OnFreeBalanceZero = ();
type OnNewAccount = (); type OnNewAccount = ();
type Event = (); type Event = ();
@@ -852,6 +906,7 @@ mod tests {
impl Trait for Test { impl Trait for Test {
type Origin = Origin; type Origin = Origin;
type Call = Call; type Call = Call;
type ParachainCurrency = balances::Module<Test>;
} }
type Parachains = Module<Test>; type Parachains = Module<Test>;
@@ -23,7 +23,10 @@ use std::sync::Arc;
use adder::{HeadData as AdderHead, BlockData as AdderBody}; use adder::{HeadData as AdderHead, BlockData as AdderBody};
use substrate_primitives::Pair; use substrate_primitives::Pair;
use parachain::codec::{Encode, Decode}; use parachain::codec::{Encode, Decode};
use primitives::{Hash, parachain::{HeadData, BlockData, Id as ParaId, Message, Extrinsic}}; use primitives::Hash;
use primitives::parachain::{
HeadData, BlockData, Id as ParaId, Message, Extrinsic, Status as ParachainStatus,
};
use collator::{InvalidHead, ParachainContext, VersionInfo}; use collator::{InvalidHead, ParachainContext, VersionInfo};
use parking_lot::Mutex; use parking_lot::Mutex;
@@ -50,11 +53,11 @@ impl ParachainContext for AdderContext {
fn produce_candidate<I: IntoIterator<Item=(ParaId, Message)>>( fn produce_candidate<I: IntoIterator<Item=(ParaId, Message)>>(
&self, &self,
_relay_parent: Hash, _relay_parent: Hash,
last_head: HeadData, status: ParachainStatus,
ingress: I, ingress: I,
) -> Result<(BlockData, HeadData, Extrinsic), InvalidHead> ) -> Result<(BlockData, HeadData, Extrinsic), InvalidHead>
{ {
let adder_head = AdderHead::decode(&mut &last_head.0[..]) let adder_head = AdderHead::decode(&mut &status.head_data.0[..])
.ok_or(InvalidHead)?; .ok_or(InvalidHead)?;
let mut db = self.db.lock(); let mut db = self.db.lock();
+87 -11
View File
@@ -21,9 +21,9 @@
use std::sync::Arc; use std::sync::Arc;
use polkadot_primitives::{Block, Hash, BlockId, parachain::CollatorId, parachain::{ use polkadot_primitives::{Block, Hash, BlockId, Balance, parachain::{
ConsolidatedIngress, StructuredUnroutedIngress, CandidateReceipt, ParachainHost, CollatorId, ConsolidatedIngress, StructuredUnroutedIngress, CandidateReceipt, ParachainHost,
Id as ParaId, Collation, Extrinsic, OutgoingMessage, UpwardMessage Id as ParaId, Collation, Extrinsic, OutgoingMessage, UpwardMessage, FeeSchedule,
}}; }};
use runtime_primitives::traits::ProvideRuntimeApi; use runtime_primitives::traits::ProvideRuntimeApi;
use parachain::{wasm_executor::{self, ExternalitiesError}, MessageRef, UpwardMessageRef}; use parachain::{wasm_executor::{self, ExternalitiesError}, MessageRef, UpwardMessageRef};
@@ -167,6 +167,9 @@ pub enum Error {
/// Parachain validation produced wrong relay-chain messages /// Parachain validation produced wrong relay-chain messages
#[display(fmt = "Parachain validation produced wrong relay-chain messages (expected: {:?}, got {:?})", expected, got)] #[display(fmt = "Parachain validation produced wrong relay-chain messages (expected: {:?}, got {:?})", expected, got)]
UpwardMessagesInvalid { expected: Vec<UpwardMessage>, got: Vec<UpwardMessage> }, UpwardMessagesInvalid { expected: Vec<UpwardMessage>, got: Vec<UpwardMessage> },
/// Parachain validation produced wrong fees to charge to parachain.
#[display(fmt = "Parachain validation produced wrong relay-chain fees (expected: {:?}, got {:?})", expected, got)]
FeesChargedInvalid { expected: Balance, got: Balance },
} }
impl std::error::Error for Error { impl std::error::Error for Error {
@@ -268,17 +271,19 @@ struct Externalities {
parachain_index: ParaId, parachain_index: ParaId,
outgoing: Vec<OutgoingMessage>, outgoing: Vec<OutgoingMessage>,
upward: Vec<UpwardMessage>, upward: Vec<UpwardMessage>,
fees_charged: Balance,
free_balance: Balance,
fee_schedule: FeeSchedule,
} }
impl wasm_executor::Externalities for Externalities { impl wasm_executor::Externalities for Externalities {
fn post_message(&mut self, message: MessageRef) -> Result<(), ExternalitiesError> { fn post_message(&mut self, message: MessageRef) -> Result<(), ExternalitiesError> {
// TODO: https://github.com/paritytech/polkadot/issues/92
// check per-message and per-byte fees for the parachain.
let target: ParaId = message.target.into(); let target: ParaId = message.target.into();
if target == self.parachain_index { if target == self.parachain_index {
return Err(ExternalitiesError::CannotPostMessage("posted message to self")); return Err(ExternalitiesError::CannotPostMessage("posted message to self"));
} }
self.apply_message_fee(message.data.len())?;
self.outgoing.push(OutgoingMessage { self.outgoing.push(OutgoingMessage {
target, target,
data: message.data.to_vec(), data: message.data.to_vec(),
@@ -290,8 +295,8 @@ impl wasm_executor::Externalities for Externalities {
fn post_upward_message(&mut self, message: UpwardMessageRef) fn post_upward_message(&mut self, message: UpwardMessageRef)
-> Result<(), ExternalitiesError> -> Result<(), ExternalitiesError>
{ {
// TODO: https://github.com/paritytech/polkadot/issues/92 self.apply_message_fee(message.data.len())?;
// check per-message and per-byte fees for the parachain.
self.upward.push(UpwardMessage { self.upward.push(UpwardMessage {
origin: message.origin, origin: message.origin,
data: message.data.to_vec(), data: message.data.to_vec(),
@@ -300,9 +305,18 @@ impl wasm_executor::Externalities for Externalities {
} }
} }
impl Externalities { impl Externalities {
fn apply_message_fee(&mut self, message_len: usize) -> Result<(), ExternalitiesError> {
let fee = self.fee_schedule.compute_fee(message_len);
let new_fees_charged = self.fees_charged.saturating_add(fee);
if new_fees_charged > self.free_balance {
Err(ExternalitiesError::CannotPostMessage("could not cover fee."))
} else {
self.fees_charged = new_fees_charged;
Ok(())
}
}
// Performs final checks of validity, producing the extrinsic data. // Performs final checks of validity, producing the extrinsic data.
fn final_checks( fn final_checks(
self, self,
@@ -315,6 +329,13 @@ impl Externalities {
}); });
} }
if self.fees_charged != candidate.fees {
return Err(Error::FeesChargedInvalid {
expected: candidate.fees.clone(),
got: self.fees_charged.clone(),
});
}
check_extrinsic( check_extrinsic(
self.outgoing, self.outgoing,
&candidate.egress_queue_roots[..], &candidate.egress_queue_roots[..],
@@ -383,15 +404,16 @@ pub fn validate_collation<P>(
let validation_code = api.parachain_code(relay_parent, para_id)? let validation_code = api.parachain_code(relay_parent, para_id)?
.ok_or_else(|| Error::InactiveParachain(para_id))?; .ok_or_else(|| Error::InactiveParachain(para_id))?;
let chain_head = api.parachain_head(relay_parent, para_id)? let chain_status = api.parachain_status(relay_parent, para_id)?
.ok_or_else(|| Error::InactiveParachain(para_id))?; .ok_or_else(|| Error::InactiveParachain(para_id))?;
let roots = api.ingress(relay_parent, para_id)? let roots = api.ingress(relay_parent, para_id)?
.ok_or_else(|| Error::InactiveParachain(para_id))?; .ok_or_else(|| Error::InactiveParachain(para_id))?;
validate_incoming(&roots, &collation.pov.ingress)?; validate_incoming(&roots, &collation.pov.ingress)?;
let params = ValidationParams { let params = ValidationParams {
parent_head: chain_head, parent_head: chain_status.head_data.0,
block_data: collation.pov.block_data.0.clone(), block_data: collation.pov.block_data.0.clone(),
ingress: collation.pov.ingress.0.iter() ingress: collation.pov.ingress.0.iter()
.flat_map(|&(source, ref messages)| { .flat_map(|&(source, ref messages)| {
@@ -407,6 +429,9 @@ pub fn validate_collation<P>(
parachain_index: collation.receipt.parachain_index.clone(), parachain_index: collation.receipt.parachain_index.clone(),
outgoing: Vec::new(), outgoing: Vec::new(),
upward: Vec::new(), upward: Vec::new(),
free_balance: chain_status.balance,
fee_schedule: chain_status.fee_schedule,
fees_charged: 0,
}; };
match wasm_executor::validate_candidate(&validation_code, params, &mut ext) { match wasm_executor::validate_candidate(&validation_code, params, &mut ext) {
@@ -481,6 +506,12 @@ mod tests {
parachain_index: 5.into(), parachain_index: 5.into(),
outgoing: Vec::new(), outgoing: Vec::new(),
upward: Vec::new(), upward: Vec::new(),
fees_charged: 0,
free_balance: 1_000_000,
fee_schedule: FeeSchedule {
base: 1000,
per_byte: 10,
},
}; };
assert!(ext.post_message(MessageRef { target: 1.into(), data: &[] }).is_ok()); assert!(ext.post_message(MessageRef { target: 1.into(), data: &[] }).is_ok());
@@ -495,6 +526,12 @@ mod tests {
upward: vec![ upward: vec![
UpwardMessage{ data: vec![42], origin: ParachainDispatchOrigin::Parachain }, UpwardMessage{ data: vec![42], origin: ParachainDispatchOrigin::Parachain },
], ],
fees_charged: 0,
free_balance: 1_000_000,
fee_schedule: FeeSchedule {
base: 1000,
per_byte: 10,
},
}; };
let receipt = CandidateReceipt { let receipt = CandidateReceipt {
parachain_index: 5.into(), parachain_index: 5.into(),
@@ -550,4 +587,43 @@ mod tests {
}; };
assert!(ext().final_checks(&receipt).is_ok()); assert!(ext().final_checks(&receipt).is_ok());
} }
#[test]
fn ext_checks_fees_and_updates_correctly() {
let mut ext = Externalities {
parachain_index: 5.into(),
outgoing: Vec::new(),
upward: vec![
UpwardMessage{ data: vec![42], origin: ParachainDispatchOrigin::Parachain },
],
fees_charged: 0,
free_balance: 1_000_000,
fee_schedule: FeeSchedule {
base: 1000,
per_byte: 10,
},
};
ext.apply_message_fee(100).unwrap();
assert_eq!(ext.fees_charged, 2000);
ext.post_message(MessageRef {
target: 1.into(),
data: &[0u8; 100],
}).unwrap();
assert_eq!(ext.fees_charged, 4000);
ext.post_upward_message(UpwardMessageRef {
origin: ParachainDispatchOrigin::Signed,
data: &[0u8; 100],
}).unwrap();
assert_eq!(ext.fees_charged, 6000);
ext.apply_message_fee((1_000_000 - 6000 - 1000) / 10).unwrap();
assert_eq!(ext.fees_charged, 1_000_000);
// cannot pay fee.
assert!(ext.apply_message_fee(1).is_err());
}
} }