Je ne comprends pas le comportement actuel des frais de transaction.
En théorie
Frais constants égaux à 1 :
impl WeightToFee
{
fn weight_to_fee(_weight: &Weight) -> Self::Balance {
// Force constant fees for now
One::one()
}
}
En pratique (tests end2end)
Frais constants égaux à 2 (cĞD) :
Feature: Balance transfer all
Scenario: If bob sends all his ĞDs to Dave
When bob sends all his ĞDs to dave
"""
Bob is a member, as such he is not allowed to empty his account completely,
if he tries to do so, the existence deposit (2 ĞD) must remain.
"""
Then bob should have 2 ĞD
"""
10 ĞD (initial Bob balance) - 2 ĞD (Existential deposit) - 0.02 ĞD (transaction fees)
"""
Then dave should have 798 cĞD
En pratique (duniter --dev)
Quand je fais un balance.transfer() de Bob vers Charlie dans polkadotjs, il y a un frais de 1 prélevé, et envoyé à Alice (validateur).
(dans ma branche actuelle ça va à la trésorerie, mais c’est le même montant).
D’où peut bien venir cette différence de comportement ??
C’est normal de désactiver les frais pour les benchmarks (mais ça peut fausser nos mesures pour les quotas), par contre les tests end2end et mon test local utilisent le même binaire compilé avec les options par défaut, donc ça n’explique pas la différence.
De la variable DUNITER_GENESIS_CONFIG, qui ne paramètre pas le même Genesis dans le cas d’utilisation de --dev selon que la variable est définie ou non. Cf : État des lieux des différentes chaînes
C’est bien piégeux !
edit : ah ben non, c’est pas si évident, j’ai répondu intuitivement mais je ne vois pas où sont précisés les frais.
edit 2 : ou alors l’exécutable des tests end2end n’est pas le même que celui que tu utilises avec Polkadotjs
C’est dans le fichier runtime/common/src/fees.rs qui convertit les weight en fees. Et ensuite, c’est ce que disait tuxmain, c’est configuré dans runtime/common/src/pallets_config.rs. Mais donc il n’y a pas moyen d’avoir deux runtime avec des règles différentes.
Le binaire utilisé par les tests end2end est le même que celui que j’ai utilisé pour mes tests manuels.
// dans end2end-tests/tests/common/mod.rs
const DUNITER_LOCAL_PATH: &str = "../target/debug/duniter";
J’ai ajouté un test d’intégration sur les frais, lui aussi dit que les frais sont de 2.
/// test currency transfer with extrinsic
// the signer account should pay fees and a tip
// the treasury should get the fees
#[test]
fn test_transfer_xt() {
ExtBuilder::new(1, 3, 4)
.with_initial_balances(vec![
(AccountKeyring::Alice.to_account_id(), 10_000),
(AccountKeyring::Eve.to_account_id(), 10_000),
])
.build()
.execute_with(|| {
let call = RuntimeCall::Balances(BalancesCall::transfer_allow_death {
dest: AccountKeyring::Eve.to_account_id().into(),
value: 500,
});
// 1 cĞD of tip
let xt = get_unchecked_extrinsic(call, 4u64, 8u64, AccountKeyring::Alice, 1u64);
let info = xt.get_dispatch_info();
println!("dispatch info:\n\t {:?}\n", info);
// nothing in treasury at start
// FIXME treasury is initialized at ED
assert_eq!(Balances::free_balance(Treasury::account_id()), 200);
// Alice gives 500 to Eve
assert_ok!(Executive::apply_extrinsic(xt));
// check amounts
assert_eq!(
Balances::free_balance(AccountKeyring::Alice.to_account_id()),
10_000 - 500 - 3 // initial - transfered - fees
);
assert_eq!(
Balances::free_balance(AccountKeyring::Eve.to_account_id()),
10_000 + 500 // initial + transfered
);
assert_eq!(Balances::free_balance(Treasury::account_id()), 203);
})
}
J’ai mis du temps à y arriver parce que j’avais initialisé la trésorerie à zéro et que donc il ne pouvait pas recevoir les frais parce qu’il était en dessous du dépôt existentiel (cf Soldes des comptes au genesis v2 - #7 by HugoTrentesaux). Donc j’ai lu de long en large le code de substrate sur l’exécution d’un extrinsic (pallet-executive et primitives associées) et le paiement des frais de transaction (pallet-transaction-payment et toute la mécanique du imbalance…). Bref c’était long ><
Par contre, le test manuel donne toujours la même chose :
La question du montant initial de la trésorerie sera réglée dans la MR !182 de cgeek, mais la question du comportement des frais reste entière. Dans tous les tests d’intégration et end2end, le montant des frais est de 2 cĞD, mais les tests manuels donnent toujours 1 cĞD.
En posant un point d’arrêt dans la pallet pallet-transaction-payment dans une des méthodes qui comporte le mot fee, j’en arrive à la méthode final_fee qui est celle qui finit par donner un total de 2 ĞD en frais si lancé comme suit (mode e2e) :
Et en creusant, tu verras en posant un point d’arrêt dans pallet-transaction-payment.compute_fee_raw() que c’est la méthode weight_to_fee qui renvoie 1 plutôt que 0 et vient incrémenter la valeur des frais.
Sur ma branche cela s’explique par le fait que les poids calculés par Benjamin sont pris en compte dans un cas et pas dans l’autre. Sur master, je suppose que c’est pareil.
Cette valeur vaut 1,000,000,000,000,000,000 en “mode e2e” et 0 en “mode manuel”.
L’argument --sealing=manual semble être responsable de ce changement (ou alors c’est juste la valeur au bloc zéro que j’observe uniquement en sealing manual ?). Merci de m’avoir mis sur le bon chemin, je vais peut-être laisser Benjamin creuser ça après avoir finalisé les benchmarks !
Effectivement dès que je fais un appel RPC engine.createBlock(Yes,No) la variable adjusted_weight_fee repasse à 0.
Voici le même test e2e qu’avant, passant, mais avec 0,01 ĞD de frais :
@genesis.default
Feature: Balance transfer all
Scenario: If bob sends all his ĞDs to Dave
# Attendre un bloc
When 1 block later
When bob sends all her ĞDs to dave
"""
Bob is a member, as such he is not allowed to empty his account completely,
if he tries to do so, the existence deposit (1 ĞD) must remain.
"""
Then bob should have 1 ĞD
"""
10 ĞD (initial Bob balance) - 1 ĞD (Existential deposit) - 0.01 ĞD (transaction fees)
"""
Then dave should have 899 cĞD
Feature: Balance transfer all
Scenario: If bob sends all his ĞDs to Dave
✔ When 1 block later
✔ When bob sends all her ĞDs to dave
✔ Then bob should have 1 ĞD
✔ Then dave should have 899 cĞD
C’est @bgallois qui a proposé le fix ici, et on va donc utiliser un ConstFeeMultiplier de One dans un premier temps et si on a envie de modifier par la suite on pourra
pub FeeMultiplier: pallet_transaction_payment::Multiplier = pallet_transaction_payment::Multiplier::one();
type FeeMultiplierUpdate = pallet_transaction_payment::ConstFeeMultiplier<FeeMultiplier>;
Sur ma branche, les frais sont toujours de 2 (1 pour le calcul et 1 pour le stockage), on pourra ajuster ça une fois les benchmarks calculés, et il faudra faire des calculs d’ordre de grandeur et des choix sur le coût du calcul vs le coût du stockage.