Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration test: Generic runtime and environment #1583

Merged
merged 20 commits into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion runtime/development/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -922,7 +922,7 @@ impl pallet_treasury::Config for Runtime {
type Burn = Burn;
// we burn and dont handle the unbalance
type BurnDestination = ();
type Currency = Tokens;
lemunozm marked this conversation as resolved.
Show resolved Hide resolved
type Currency = Balances;
type MaxApprovals = MaxApprovals;
// slashed amount goes to treasury account
type OnSlash = Treasury;
Expand Down
12 changes: 12 additions & 0 deletions runtime/integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,25 @@ pallet-democracy = { git = "https://github.com/paritytech//substrate", rev = "bc
pallet-preimage = { git = "https://github.com/paritytech//substrate", rev = "bcff60a227d455d95b4712b6cb356ce56b1ff672" }
pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.38" }
pallet-uniques = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.38" }
pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
pallet-treasury = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
pallet-authorship = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }


## Substrate-Primitives
sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
#sp-authorship = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
sp-block-builder = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
sp-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
sp-consensus-babe = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
sp-consensus-slots = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
sp-inherents = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
sp-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.38" }
sp-tracing = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
fp-self-contained = { git = "https://github.com/PureStake/frontier", branch = "moonbeam-polkadot-v0.9.38" }

## Substrate-Client
node-primitives = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
Expand All @@ -61,6 +68,10 @@ xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.3

# Cumulus
cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.38" }
cumulus-pallet-parachain-system = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.38" }
cumulus-primitives-parachain-inherent = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.38" }
cumulus-test-relay-sproof-builder = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.38" }

parachain-info = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.38" }

# Orml pallets
Expand Down Expand Up @@ -109,6 +120,7 @@ pallet-permissions = { path = "../../pallets/permissions" }
pallet-pool-registry = { path = "../../pallets/pool-registry" }
pallet-pool-system = { path = "../../pallets/pool-system" }
pallet-rewards = { path = "../../pallets/rewards" }
pallet-restricted-tokens = { path = "../../pallets/restricted-tokens" }

pallet-session = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
pallet-xcm-transactor = { git = "https://github.com/PureStake/moonbeam", default-features = false, rev = "00b3e3d97806e889b02e1bcb4b69e65433dd805d" }
Expand Down
86 changes: 86 additions & 0 deletions runtime/integration-tests/src/generic/cases/example.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use cfg_primitives::{AuraId, Balance, CFG};
use frame_support::{assert_ok, traits::Get};

use crate::{
generic::{
environment::{self, Blocks, Env},
envs::runtime_env::RuntimeEnv,
runtime::Runtime,
utils::genesis::Genesis,
},
utils::accounts::Keyring,
};

fn transfer_balance<T: Runtime>() {
const TRANSFER: Balance = 1000 * CFG;
const FOR_FEES: Balance = 1 * CFG;

// Set up all GenesisConfig for your initial state
let mut env = RuntimeEnv::<T>::from_genesis(
Genesis::default()
.add(pallet_aura::GenesisConfig::<T> {
authorities: vec![AuraId::from(Keyring::Charlie.public())],
})
.add(pallet_balances::GenesisConfig::<T> {
balances: vec![(
Keyring::Alice.to_account_id(),
T::ExistentialDeposit::get() + FOR_FEES + TRANSFER,
)],
}),
);

// Call an extrinsic that would be processed immediately
assert_ok!(env.submit(
Keyring::Alice,
pallet_balances::Call::<T>::transfer {
dest: Keyring::Bob.into(),
value: TRANSFER,
},
));

// Check for an even occurred in this block
assert!(env.has_event(pallet_balances::Event::Transfer {
from: Keyring::Alice.to_account_id(),
to: Keyring::Bob.to_account_id(),
amount: TRANSFER,
}));

// Pass blocks to evolve the system
env.pass(Blocks::ByNumber(1));

// Check the state
env.state(|| {
assert_eq!(
pallet_balances::Pallet::<T>::free_balance(Keyring::Bob.to_account_id()),
TRANSFER
);
});
}

fn call_api<T: Runtime>() {
// Set up all GenesisConfig for your initial state
let mut env =
RuntimeEnv::<T>::from_genesis(Genesis::default().add(pallet_aura::GenesisConfig::<T> {
authorities: vec![AuraId::from(Keyring::Charlie.public())],
}));

env.state(|| {
// Call to Core::version() API.
// It's automatically implemented by the runtime T, so you can easily do:
// T::version()
assert_eq!(T::version(), <T as frame_system::Config>::Version::get());
})
}

// Generate tests for all runtimes
crate::test_with_all_runtimes!(transfer_balance);
lemunozm marked this conversation as resolved.
Show resolved Hide resolved
crate::test_with_all_runtimes!(call_api);

// Output: for `cargo test -p runtime-integration-tests generic`
// running 6 tests
// test generic::cases::example::call_api::centrifuge ... ok
// test generic::cases::example::call_api::altair ... ok
// test generic::cases::example::call_api::development ... ok
// test generic::cases::example::transfer_balance::altair ... ok
// test generic::cases::example::transfer_balance::development ... ok
// test generic::cases::example::transfer_balance::centrifuge ... ok
109 changes: 109 additions & 0 deletions runtime/integration-tests/src/generic/environment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use std::fmt::Debug;

use cfg_primitives::{
AccountId, Address, AuraId, Balance, BlockNumber, CollectionId, Header, Index, ItemId, LoanId,
Moment, PoolId, Signature, TrancheId,
};
use cfg_types::{
permissions::{PermissionScope, Role},
tokens::{CurrencyId, CustomMetadata, TrancheCurrency},
};
use codec::Codec;
use cumulus_primitives_core::PersistedValidationData;
use cumulus_primitives_parachain_inherent::ParachainInherentData;
use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder;
use fp_self_contained::{SelfContainedCall, UncheckedExtrinsic};
use frame_support::{
dispatch::{
DispatchClass, DispatchInfo, GetDispatchInfo, Pays, PostDispatchInfo,
UnfilteredDispatchable,
},
inherent::{InherentData, ProvideInherent},
traits::{Get, IsType},
weights::WeightToFee as _,
Parameter,
};
use frame_system::{ChainContext, RawOrigin};
use pallet_transaction_payment::CurrencyAdapter;
use runtime_common::{
apis,
fees::{DealWithFees, WeightToFee},
};
use sp_io::TestExternalities;
use sp_runtime::{
traits::{AccountIdLookup, Block, Checkable, Dispatchable, Extrinsic, Lookup, Member},
ApplyExtrinsicResult, DispatchResult,
};
use sp_timestamp::Timestamp;

use crate::{
generic::{runtime::Runtime, utils::genesis::Genesis},
utils::accounts::Keyring,
};

/// Used by Env::pass() to determine how many blocks should be passed
#[derive(Clone)]
pub enum Blocks<T: Runtime> {
/// Pass X blocks
ByNumber(BlockNumber),

/// Pass a number of blocks proportional to these seconds
BySeconds(Moment),

/// Pass a number of block until find an event or reach the limit
UntilEvent {
event: T::RuntimeEventExt,
limit: BlockNumber,
},
}
lemunozm marked this conversation as resolved.
Show resolved Hide resolved

/// Define an environment behavior
pub trait Env<T: Runtime> {
/// Loan the environment from a genesis
lemunozm marked this conversation as resolved.
Show resolved Hide resolved
fn from_genesis(genesis: Genesis) -> Self;

/// Submit an extrinsic mutating the state
fn submit(&mut self, who: Keyring, call: impl Into<T::RuntimeCall>) -> DispatchResult;

/// Pass any number of blocks
fn pass(&mut self, blocks: Blocks<T>);

/// Allows to mutate the storage state through the closure
fn state_mut<R>(&mut self, f: impl FnOnce() -> R) -> R;

/// Allows to read the storage state through the closure
/// If storage is modified, it would not be applied.
fn state<R>(&self, f: impl FnOnce() -> R) -> R;

/// Check for an event introduced in the current block
fn has_event(&self, event: impl Into<T::RuntimeEventExt>) -> bool {
self.state(|| {
let event = event.into();
frame_system::Pallet::<T>::events()
.into_iter()
.find(|record| record.event == event)
.is_some()
})
}

/// Retrieve the fees used in the last submit call
fn last_xt_fees(&self) -> Balance {
self.state(|| {
let runtime_event = frame_system::Pallet::<T>::events()
.last()
.unwrap()
.clone()
.event;

let dispatch_info = match runtime_event.try_into() {
Ok(frame_system::Event::<T>::ExtrinsicSuccess { dispatch_info }) => dispatch_info,
_ => panic!("expected to be called after a successful extrinsic"),
};

match dispatch_info.pays_fee {
Pays::Yes => WeightToFee::weight_to_fee(&dispatch_info.weight),
Pays::No => 0,
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not exactly the fees the last executed extrinsic has consumed. Not sure why or what I'm missing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have expected your solution to be correct. Have you compared the current result against the fee returned by transaction_payment::Event::<T>:TransactionFeePaid { actual_fee, tip } which should be the event before the last one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They're different, not sure why, but the way of calculating them comes from different paths. The difference is really subtle anyway:

  • From TransactionFeePaid:
    • 2004154679487179
  • From ExtrinsicSuccess:
    • 2003074679487179

Still investigating, thanks for addressing me to the TransactionFeePaid event!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why the difference. But as far as I am concerned, using the value from TransactionFeePaid works so... I'll give this as solved.

Copy link
Contributor

@wischli wischli Oct 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe TransactionFeePaid includes another signature check which is not included in ExtrinsicSuccess. Just a guess, haven't looked into the code further.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe there's some extra fees for dispatching the transaction? That happens with xcm-related transactions, maybe there's a similar mechanism here involved?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, there must be some extra fees that do not depend on the weight of the call, so when I do WeightToFee::weight_to_fee(&dispatch_info.weight) I'm leaving some fees behind.

})
}
}
1 change: 1 addition & 0 deletions runtime/integration-tests/src/generic/envs/fudge_env.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// TODO: implement generic::env::Env for an environment using fudge
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still want to do this in this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this will be a future PR not to block this. That file is just a placeholder.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should create an issue for this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Loading
Loading