mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 14:01: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::{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(),
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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(¶_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 {
|
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: ¶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 {
|
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();
|
||||||
|
|||||||
@@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user