mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 16:21:02 +00:00
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:
committed by
Bastian Köcher
parent
9004fb3f97
commit
7a5b9bddf5
@@ -56,7 +56,7 @@ use primitives::{ed25519, Pair};
|
||||
use polkadot_primitives::{BlockId, SessionKey, Hash, Block};
|
||||
use polkadot_primitives::parachain::{
|
||||
self, BlockData, DutyRoster, HeadData, ConsolidatedIngress, Message, Id as ParaId, Extrinsic,
|
||||
PoVBlock,
|
||||
PoVBlock, Status as ParachainStatus,
|
||||
};
|
||||
use polkadot_cli::{PolkadotService, CustomConfiguration, ParachainHost};
|
||||
use polkadot_cli::{Worker, IntoExit, ProvideRuntimeApi, TaskExecutor};
|
||||
@@ -105,7 +105,7 @@ pub trait ParachainContext: Clone {
|
||||
fn produce_candidate<I: IntoIterator<Item=(ParaId, Message)>>(
|
||||
&self,
|
||||
relay_parent: Hash,
|
||||
last_head: HeadData,
|
||||
status: ParachainStatus,
|
||||
ingress: I,
|
||||
) -> Self::ProduceCandidate;
|
||||
}
|
||||
@@ -128,7 +128,7 @@ pub trait RelayChainContext {
|
||||
pub fn collate<'a, R, P>(
|
||||
relay_parent: Hash,
|
||||
local_id: ParaId,
|
||||
last_head: HeadData,
|
||||
parachain_status: ParachainStatus,
|
||||
relay_context: R,
|
||||
para_context: P,
|
||||
key: Arc<ed25519::Pair>,
|
||||
@@ -146,7 +146,7 @@ pub fn collate<'a, R, P>(
|
||||
.and_then(move |ingress| {
|
||||
para_context.produce_candidate(
|
||||
relay_parent,
|
||||
last_head,
|
||||
parachain_status,
|
||||
ingress.0.iter().flat_map(|&(id, ref msgs)| msgs.iter().cloned().map(move |msg| (id, msg)))
|
||||
)
|
||||
.into_future()
|
||||
@@ -311,8 +311,8 @@ impl<P, E> Worker for CollationNode<P, E> where
|
||||
|
||||
let work = future::lazy(move || {
|
||||
let api = client.runtime_api();
|
||||
let last_head = match try_fr!(api.parachain_head(&id, para_id)) {
|
||||
Some(last_head) => last_head,
|
||||
let status = match try_fr!(api.parachain_status(&id, para_id)) {
|
||||
Some(status) => status,
|
||||
None => return future::Either::A(future::ok(())),
|
||||
};
|
||||
|
||||
@@ -333,7 +333,7 @@ impl<P, E> Worker for CollationNode<P, E> where
|
||||
let collation_work = collate(
|
||||
relay_parent,
|
||||
para_id,
|
||||
HeadData(last_head),
|
||||
status,
|
||||
context,
|
||||
parachain_context,
|
||||
key,
|
||||
@@ -403,7 +403,7 @@ pub fn run_collator<P, E, I, ArgT>(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
use polkadot_primitives::parachain::OutgoingMessage;
|
||||
use polkadot_primitives::parachain::{OutgoingMessage, FeeSchedule};
|
||||
use keyring::AuthorityKeyring;
|
||||
use super::*;
|
||||
|
||||
@@ -433,7 +433,7 @@ mod tests {
|
||||
fn produce_candidate<I: IntoIterator<Item=(ParaId, Message)>>(
|
||||
&self,
|
||||
_relay_parent: Hash,
|
||||
_last_head: HeadData,
|
||||
_status: ParachainStatus,
|
||||
ingress: I,
|
||||
) -> Result<(BlockData, HeadData, Extrinsic), InvalidHead> {
|
||||
// send messages right back.
|
||||
@@ -484,7 +484,14 @@ mod tests {
|
||||
let collation = collate(
|
||||
Default::default(),
|
||||
id,
|
||||
HeadData(vec![5]),
|
||||
ParachainStatus {
|
||||
head_data: HeadData(vec![5]),
|
||||
balance: 10,
|
||||
fee_schedule: FeeSchedule {
|
||||
base: 0,
|
||||
per_byte: 1,
|
||||
},
|
||||
},
|
||||
context.clone(),
|
||||
DummyParachainContext,
|
||||
AuthorityKeyring::Alice.pair().into(),
|
||||
|
||||
@@ -30,7 +30,8 @@ use polkadot_validation::{SharedTable, MessagesFrom, Network};
|
||||
use polkadot_primitives::{SessionKey, Block, Hash, Header, BlockId};
|
||||
use polkadot_primitives::parachain::{
|
||||
Id as ParaId, Chain, DutyRoster, ParachainHost, OutgoingMessage,
|
||||
ValidatorId, StructuredUnroutedIngress, BlockIngressRoots,
|
||||
ValidatorId, StructuredUnroutedIngress, BlockIngressRoots, Status,
|
||||
FeeSchedule, HeadData,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
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()))
|
||||
}
|
||||
|
||||
fn ParachainHost_parachain_head_runtime_api_impl(
|
||||
fn ParachainHost_parachain_status_runtime_api_impl(
|
||||
&self,
|
||||
_at: &BlockId,
|
||||
_: ExecutionContext,
|
||||
_: Option<ParaId>,
|
||||
_: Vec<u8>,
|
||||
) -> ClientResult<NativeOrEncoded<Option<Vec<u8>>>> {
|
||||
Ok(NativeOrEncoded::Native(Some(Vec::new())))
|
||||
) -> ClientResult<NativeOrEncoded<Option<Status>>> {
|
||||
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(
|
||||
|
||||
@@ -28,7 +28,9 @@ use serde::{Serialize, Deserialize};
|
||||
use primitives::bytes;
|
||||
use primitives::ed25519;
|
||||
|
||||
pub use polkadot_parachain::{Id, AccountIdConversion, ParachainDispatchOrigin};
|
||||
pub use polkadot_parachain::{
|
||||
Id, AccountIdConversion, ParachainDispatchOrigin,
|
||||
};
|
||||
|
||||
/// Identity that collators use.
|
||||
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! {
|
||||
/// The API for querying the state of parachains on-chain.
|
||||
pub trait ParachainHost {
|
||||
@@ -337,8 +372,8 @@ substrate_client::decl_runtime_apis! {
|
||||
fn duty_roster() -> DutyRoster;
|
||||
/// Get the currently active parachains.
|
||||
fn active_parachains() -> Vec<Id>;
|
||||
/// Get the given parachain's head data blob.
|
||||
fn parachain_head(id: Id) -> Option<Vec<u8>>;
|
||||
/// Get the given parachain's status.
|
||||
fn parachain_status(id: Id) -> Option<Status>;
|
||||
/// Get the given parachain's head code blob.
|
||||
fn parachain_code(id: Id) -> Option<Vec<u8>>;
|
||||
/// Get all the unrouted ingress roots at the given block that
|
||||
@@ -354,3 +389,16 @@ pub mod id {
|
||||
/// Parachain host runtime API id.
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,6 +234,7 @@ impl grandpa::Trait for Runtime {
|
||||
impl parachains::Trait for Runtime {
|
||||
type Origin = Origin;
|
||||
type Call = Call;
|
||||
type ParachainCurrency = Balances;
|
||||
}
|
||||
|
||||
parameter_types!{
|
||||
@@ -364,8 +365,8 @@ impl_runtime_apis! {
|
||||
fn active_parachains() -> Vec<parachain::Id> {
|
||||
Parachains::active_parachains()
|
||||
}
|
||||
fn parachain_head(id: parachain::Id) -> Option<Vec<u8>> {
|
||||
Parachains::parachain_head(&id)
|
||||
fn parachain_status(id: parachain::Id) -> Option<parachain::Status> {
|
||||
Parachains::parachain_status(&id)
|
||||
}
|
||||
fn parachain_code(id: parachain::Id) -> Option<Vec<u8>> {
|
||||
Parachains::parachain_code(&id)
|
||||
|
||||
@@ -25,13 +25,14 @@ use bitvec::{bitvec, BigEndian};
|
||||
use sr_primitives::traits::{
|
||||
Hash as HashT, BlakeTwo256, Member, CheckedConversion, Saturating, One, Zero,
|
||||
};
|
||||
use primitives::{Hash, parachain::{
|
||||
Id as ParaId, Chain, DutyRoster, AttestedCandidate, Statement, AccountIdConversion,
|
||||
use primitives::{Hash, Balance, parachain::{
|
||||
self, Id as ParaId, Chain, DutyRoster, AttestedCandidate, Statement, AccountIdConversion,
|
||||
ParachainDispatchOrigin, UpwardMessage, BlockIngressRoots,
|
||||
}};
|
||||
use {system, session};
|
||||
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")]
|
||||
@@ -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(¶_account).into()
|
||||
}
|
||||
|
||||
fn deduct(para_id: ParaId, amount: Balance) -> Result {
|
||||
let para_account = para_id.into_account();
|
||||
|
||||
// burn the fee.
|
||||
let _ = T::withdraw(
|
||||
¶_account,
|
||||
amount.into(),
|
||||
WithdrawReason::Fee,
|
||||
ExistenceRequirement::KeepAlive,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Trait: session::Trait {
|
||||
/// The outer origin type.
|
||||
type Origin: From<Origin> + From<system::RawOrigin<Self::AccountId>>;
|
||||
|
||||
/// The outer call dispatch type.
|
||||
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.
|
||||
@@ -269,7 +304,7 @@ decl_module! {
|
||||
}
|
||||
}
|
||||
|
||||
Self::check_attestations(&heads)?;
|
||||
Self::check_candidates(&heads)?;
|
||||
|
||||
let current_number = <system::Module<T>>::block_number();
|
||||
|
||||
@@ -549,6 +584,21 @@ impl<T: Trait> Module<T> {
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// Get the parachain status necessary for validation.
|
||||
pub fn parachain_status(id: ¶chain::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 {
|
||||
let mut last_egress_id = None;
|
||||
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
|
||||
// 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 sr_primitives::traits::Verify;
|
||||
|
||||
@@ -661,7 +711,8 @@ impl<T: Trait> Module<T> {
|
||||
let mut validator_groups = GroupedDutyIter::new(&sorted_validators[..]);
|
||||
|
||||
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")?;
|
||||
|
||||
ensure!(
|
||||
@@ -669,6 +720,9 @@ impl<T: Trait> Module<T> {
|
||||
"Not enough validity attestations"
|
||||
);
|
||||
|
||||
let fees = candidate.candidate().fees;
|
||||
T::ParachainCurrency::deduct(para_id, fees)?;
|
||||
|
||||
let mut candidate_hash = None;
|
||||
let mut encoded_implicit = None;
|
||||
let mut encoded_explicit = None;
|
||||
@@ -824,7 +878,7 @@ mod tests {
|
||||
}
|
||||
|
||||
impl balances::Trait for Test {
|
||||
type Balance = u64;
|
||||
type Balance = Balance;
|
||||
type OnFreeBalanceZero = ();
|
||||
type OnNewAccount = ();
|
||||
type Event = ();
|
||||
@@ -852,6 +906,7 @@ mod tests {
|
||||
impl Trait for Test {
|
||||
type Origin = Origin;
|
||||
type Call = Call;
|
||||
type ParachainCurrency = balances::Module<Test>;
|
||||
}
|
||||
|
||||
type Parachains = Module<Test>;
|
||||
|
||||
@@ -23,7 +23,10 @@ use std::sync::Arc;
|
||||
use adder::{HeadData as AdderHead, BlockData as AdderBody};
|
||||
use substrate_primitives::Pair;
|
||||
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 parking_lot::Mutex;
|
||||
|
||||
@@ -50,11 +53,11 @@ impl ParachainContext for AdderContext {
|
||||
fn produce_candidate<I: IntoIterator<Item=(ParaId, Message)>>(
|
||||
&self,
|
||||
_relay_parent: Hash,
|
||||
last_head: HeadData,
|
||||
status: ParachainStatus,
|
||||
ingress: I,
|
||||
) -> 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)?;
|
||||
|
||||
let mut db = self.db.lock();
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use polkadot_primitives::{Block, Hash, BlockId, parachain::CollatorId, parachain::{
|
||||
ConsolidatedIngress, StructuredUnroutedIngress, CandidateReceipt, ParachainHost,
|
||||
Id as ParaId, Collation, Extrinsic, OutgoingMessage, UpwardMessage
|
||||
use polkadot_primitives::{Block, Hash, BlockId, Balance, parachain::{
|
||||
CollatorId, ConsolidatedIngress, StructuredUnroutedIngress, CandidateReceipt, ParachainHost,
|
||||
Id as ParaId, Collation, Extrinsic, OutgoingMessage, UpwardMessage, FeeSchedule,
|
||||
}};
|
||||
use runtime_primitives::traits::ProvideRuntimeApi;
|
||||
use parachain::{wasm_executor::{self, ExternalitiesError}, MessageRef, UpwardMessageRef};
|
||||
@@ -167,6 +167,9 @@ pub enum Error {
|
||||
/// Parachain validation produced wrong relay-chain messages
|
||||
#[display(fmt = "Parachain validation produced wrong relay-chain messages (expected: {:?}, got {:?})", expected, got)]
|
||||
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 {
|
||||
@@ -268,17 +271,19 @@ struct Externalities {
|
||||
parachain_index: ParaId,
|
||||
outgoing: Vec<OutgoingMessage>,
|
||||
upward: Vec<UpwardMessage>,
|
||||
fees_charged: Balance,
|
||||
free_balance: Balance,
|
||||
fee_schedule: FeeSchedule,
|
||||
}
|
||||
|
||||
impl wasm_executor::Externalities for Externalities {
|
||||
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();
|
||||
if target == self.parachain_index {
|
||||
return Err(ExternalitiesError::CannotPostMessage("posted message to self"));
|
||||
}
|
||||
|
||||
self.apply_message_fee(message.data.len())?;
|
||||
self.outgoing.push(OutgoingMessage {
|
||||
target,
|
||||
data: message.data.to_vec(),
|
||||
@@ -290,8 +295,8 @@ impl wasm_executor::Externalities for Externalities {
|
||||
fn post_upward_message(&mut self, message: UpwardMessageRef)
|
||||
-> Result<(), ExternalitiesError>
|
||||
{
|
||||
// TODO: https://github.com/paritytech/polkadot/issues/92
|
||||
// check per-message and per-byte fees for the parachain.
|
||||
self.apply_message_fee(message.data.len())?;
|
||||
|
||||
self.upward.push(UpwardMessage {
|
||||
origin: message.origin,
|
||||
data: message.data.to_vec(),
|
||||
@@ -300,9 +305,18 @@ impl wasm_executor::Externalities for 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.
|
||||
fn final_checks(
|
||||
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(
|
||||
self.outgoing,
|
||||
&candidate.egress_queue_roots[..],
|
||||
@@ -383,15 +404,16 @@ pub fn validate_collation<P>(
|
||||
let validation_code = api.parachain_code(relay_parent, 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))?;
|
||||
|
||||
let roots = api.ingress(relay_parent, para_id)?
|
||||
.ok_or_else(|| Error::InactiveParachain(para_id))?;
|
||||
|
||||
validate_incoming(&roots, &collation.pov.ingress)?;
|
||||
|
||||
let params = ValidationParams {
|
||||
parent_head: chain_head,
|
||||
parent_head: chain_status.head_data.0,
|
||||
block_data: collation.pov.block_data.0.clone(),
|
||||
ingress: collation.pov.ingress.0.iter()
|
||||
.flat_map(|&(source, ref messages)| {
|
||||
@@ -407,6 +429,9 @@ pub fn validate_collation<P>(
|
||||
parachain_index: collation.receipt.parachain_index.clone(),
|
||||
outgoing: 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) {
|
||||
@@ -481,6 +506,12 @@ mod tests {
|
||||
parachain_index: 5.into(),
|
||||
outgoing: 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());
|
||||
@@ -495,6 +526,12 @@ mod tests {
|
||||
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,
|
||||
},
|
||||
};
|
||||
let receipt = CandidateReceipt {
|
||||
parachain_index: 5.into(),
|
||||
@@ -550,4 +587,43 @@ mod tests {
|
||||
};
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user