Newer
Older
}
fn post_info_from_pays(p: Pays) -> PostDispatchInfo {
PostDispatchInfo { actual_weight: None, pays_fee: p }
}
fn default_post_info() -> PostDispatchInfo {
PostDispatchInfo { actual_weight: None, pays_fee: Default::default() }
#[test]
fn signed_extension_transaction_payment_work() {
ExtBuilder::default()
.balance_factor(10)
.base_weight(5)
.execute_with(|| {
let len = 10;
let pre = ChargeTransactionPayment::<Runtime>::from(0)
.pre_dispatch(&1, CALL, &info_from_weight(5), len)
.unwrap();
assert_eq!(Balances::free_balance(1), 100 - 5 - 5 - 10);
assert_ok!(ChargeTransactionPayment::<Runtime>::post_dispatch(
&info_from_weight(5),
&default_post_info(),
len,
&Ok(())
));
assert_eq!(Balances::free_balance(1), 100 - 5 - 5 - 10);
assert_eq!(FEE_UNBALANCED_AMOUNT.with(|a| a.borrow().clone()), 5 + 5 + 10);
assert_eq!(TIP_UNBALANCED_AMOUNT.with(|a| a.borrow().clone()), 0);
FEE_UNBALANCED_AMOUNT.with(|a| *a.borrow_mut() = 0);
let pre = ChargeTransactionPayment::<Runtime>::from(5 /* tipped */)
.pre_dispatch(&2, CALL, &info_from_weight(100), len)
.unwrap();
assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5);
assert_ok!(ChargeTransactionPayment::<Runtime>::post_dispatch(
&info_from_weight(100),
&post_info_from_weight(50),
len,
&Ok(())
));
assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 50 - 5);
assert_eq!(FEE_UNBALANCED_AMOUNT.with(|a| a.borrow().clone()), 5 + 10 + 50);
assert_eq!(TIP_UNBALANCED_AMOUNT.with(|a| a.borrow().clone()), 5);
});
#[test]
fn signed_extension_transaction_payment_multiplied_refund_works() {
ExtBuilder::default()
.balance_factor(10)
.base_weight(5)
.execute_with(|| {
let len = 10;
<NextFeeMultiplier<Runtime>>::put(Multiplier::saturating_from_rational(3, 2));
let pre = ChargeTransactionPayment::<Runtime>::from(5 /* tipped */)
.pre_dispatch(&2, CALL, &info_from_weight(100), len)
.unwrap();
// 5 base fee, 10 byte fee, 3/2 * 100 weight fee, 5 tip
assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 150 - 5);
assert_ok!(ChargeTransactionPayment::<Runtime>::post_dispatch(
&info_from_weight(100),
&post_info_from_weight(50),
len,
&Ok(())
));
// 75 (3/2 of the returned 50 units of weight) is refunded
assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 75 - 5);
});
#[test]
fn signed_extension_transaction_payment_is_bounded() {
ExtBuilder::default().balance_factor(1000).byte_fee(0).build().execute_with(|| {
assert_ok!(ChargeTransactionPayment::<Runtime>::from(0).pre_dispatch(
&1,
CALL,
&info_from_weight(Weight::max_value()),
10
));
// fee will be proportional to what is the actual maximum weight in the runtime.
assert_eq!(
Balances::free_balance(&1),
(10000 - <Runtime as frame_system::Config>::BlockWeights::get().max_block) as u64
);
});
}
#[test]
fn signed_extension_allows_free_transactions() {
ExtBuilder::default()
.base_weight(100)
.balance_factor(0)
.build()
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
.execute_with(|| {
// 1 ain't have a penny.
assert_eq!(Balances::free_balance(1), 0);
let len = 100;
// This is a completely free (and thus wholly insecure/DoS-ridden) transaction.
let operational_transaction = DispatchInfo {
weight: 0,
class: DispatchClass::Operational,
pays_fee: Pays::No,
};
assert_ok!(ChargeTransactionPayment::<Runtime>::from(0).validate(
&1,
CALL,
&operational_transaction,
len
));
// like a InsecureFreeNormal
let free_transaction =
DispatchInfo { weight: 0, class: DispatchClass::Normal, pays_fee: Pays::Yes };
assert_noop!(
ChargeTransactionPayment::<Runtime>::from(0).validate(
&1,
CALL,
&free_transaction,
len
),
TransactionValidityError::Invalid(InvalidTransaction::Payment),
);
});
}
#[test]
fn signed_ext_length_fee_is_also_updated_per_congestion() {
ExtBuilder::default()
.base_weight(5)
.balance_factor(10)
.build()
.execute_with(|| {
// all fees should be x1.5
<NextFeeMultiplier<Runtime>>::put(Multiplier::saturating_from_rational(3, 2));
let len = 10;
assert_ok!(ChargeTransactionPayment::<Runtime>::from(10) // tipped
.pre_dispatch(&1, CALL, &info_from_weight(3), len));
assert_eq!(
Balances::free_balance(1),
100 // original
- 10 // tip
- 5 // base
- 10 // len
- (3 * 3 / 2) // adjusted weight
let call = Call::Balances(BalancesCall::transfer { dest: 2, value: 69 });
let origin = 111111;
let extra = ();
let xt = TestXt::new(call.clone(), Some((origin, extra)));
let info = xt.get_dispatch_info();
let ext = xt.encode();
let len = ext.len() as u32;
let unsigned_xt = TestXt::<_, ()>::new(call, None);
let unsigned_xt_info = unsigned_xt.get_dispatch_info();
ExtBuilder::default().base_weight(5).weight_fee(2).build().execute_with(|| {
// all fees should be x1.5
<NextFeeMultiplier<Runtime>>::put(Multiplier::saturating_from_rational(3, 2));
TransactionPayment::query_info(xt.clone(), len),
RuntimeDispatchInfo {
weight: info.weight,
class: info.class,
partial_fee: 5 * 2 /* base * weight_fee */
+ len as u64 /* len * 1 */
+ info.weight.min(BlockWeights::get().max_block) as u64 * 2 * 3 / 2 /* weight */
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
assert_eq!(
TransactionPayment::query_info(unsigned_xt.clone(), len),
RuntimeDispatchInfo {
weight: unsigned_xt_info.weight,
class: unsigned_xt_info.class,
partial_fee: 0,
},
);
assert_eq!(
TransactionPayment::query_fee_details(xt, len),
FeeDetails {
inclusion_fee: Some(InclusionFee {
base_fee: 5 * 2,
len_fee: len as u64,
adjusted_weight_fee: info.weight.min(BlockWeights::get().max_block) as u64 *
2 * 3 / 2
}),
tip: 0,
},
);
assert_eq!(
TransactionPayment::query_fee_details(unsigned_xt, len),
FeeDetails { inclusion_fee: None, tip: 0 },
);
#[test]
fn compute_fee_works_without_multiplier() {
ExtBuilder::default()
.base_weight(100)
.byte_fee(10)
.balance_factor(0)
.build()
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
.execute_with(|| {
// Next fee multiplier is zero
assert_eq!(<NextFeeMultiplier<Runtime>>::get(), Multiplier::one());
// Tip only, no fees works
let dispatch_info = DispatchInfo {
weight: 0,
class: DispatchClass::Operational,
pays_fee: Pays::No,
};
assert_eq!(Pallet::<Runtime>::compute_fee(0, &dispatch_info, 10), 10);
// No tip, only base fee works
let dispatch_info = DispatchInfo {
weight: 0,
class: DispatchClass::Operational,
pays_fee: Pays::Yes,
};
assert_eq!(Pallet::<Runtime>::compute_fee(0, &dispatch_info, 0), 100);
// Tip + base fee works
assert_eq!(Pallet::<Runtime>::compute_fee(0, &dispatch_info, 69), 169);
// Len (byte fee) + base fee works
assert_eq!(Pallet::<Runtime>::compute_fee(42, &dispatch_info, 0), 520);
// Weight fee + base fee works
let dispatch_info = DispatchInfo {
weight: 1000,
class: DispatchClass::Operational,
pays_fee: Pays::Yes,
};
assert_eq!(Pallet::<Runtime>::compute_fee(0, &dispatch_info, 0), 1100);
});
}
#[test]
fn compute_fee_works_with_multiplier() {
ExtBuilder::default()
.base_weight(100)
.byte_fee(10)
.balance_factor(0)
.build()
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
.execute_with(|| {
// Add a next fee multiplier. Fees will be x3/2.
<NextFeeMultiplier<Runtime>>::put(Multiplier::saturating_from_rational(3, 2));
// Base fee is unaffected by multiplier
let dispatch_info = DispatchInfo {
weight: 0,
class: DispatchClass::Operational,
pays_fee: Pays::Yes,
};
assert_eq!(Pallet::<Runtime>::compute_fee(0, &dispatch_info, 0), 100);
// Everything works together :)
let dispatch_info = DispatchInfo {
weight: 123,
class: DispatchClass::Operational,
pays_fee: Pays::Yes,
};
// 123 weight, 456 length, 100 base
assert_eq!(
Pallet::<Runtime>::compute_fee(456, &dispatch_info, 789),
100 + (3 * 123 / 2) + 4560 + 789,
);
});
#[test]
fn compute_fee_works_with_negative_multiplier() {
ExtBuilder::default()
.base_weight(100)
.byte_fee(10)
.balance_factor(0)
.build()
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
.execute_with(|| {
// Add a next fee multiplier. All fees will be x1/2.
<NextFeeMultiplier<Runtime>>::put(Multiplier::saturating_from_rational(1, 2));
// Base fee is unaffected by multiplier.
let dispatch_info = DispatchInfo {
weight: 0,
class: DispatchClass::Operational,
pays_fee: Pays::Yes,
};
assert_eq!(Pallet::<Runtime>::compute_fee(0, &dispatch_info, 0), 100);
// Everything works together.
let dispatch_info = DispatchInfo {
weight: 123,
class: DispatchClass::Operational,
pays_fee: Pays::Yes,
};
// 123 weight, 456 length, 100 base
assert_eq!(
Pallet::<Runtime>::compute_fee(456, &dispatch_info, 789),
100 + (123 / 2) + 4560 + 789,
);
});
}
#[test]
fn compute_fee_does_not_overflow() {
ExtBuilder::default()
.base_weight(100)
.byte_fee(10)
.balance_factor(0)
.build()
.execute_with(|| {
// Overflow is handled
let dispatch_info = DispatchInfo {
weight: Weight::max_value(),
class: DispatchClass::Operational,
pays_fee: Pays::Yes,
};
assert_eq!(
Pallet::<Runtime>::compute_fee(u32::MAX, &dispatch_info, u64::MAX),
#[test]
fn refund_does_not_recreate_account() {
ExtBuilder::default()
.balance_factor(10)
.base_weight(5)
.execute_with(|| {
// So events are emitted
System::set_block_number(10);
let len = 10;
let pre = ChargeTransactionPayment::<Runtime>::from(5 /* tipped */)
.pre_dispatch(&2, CALL, &info_from_weight(100), len)
.unwrap();
assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5);
// kill the account between pre and post dispatch
assert_ok!(Balances::transfer(Some(2).into(), 3, Balances::free_balance(2)));
assert_eq!(Balances::free_balance(2), 0);
assert_ok!(ChargeTransactionPayment::<Runtime>::post_dispatch(
&info_from_weight(100),
&post_info_from_weight(50),
len,
&Ok(())
));
assert_eq!(Balances::free_balance(2), 0);
// Transfer Event
System::assert_has_event(Event::Balances(pallet_balances::Event::Transfer {
from: 2,
to: 3,
amount: 80,
}));
System::assert_has_event(Event::System(system::Event::KilledAccount {
account: 2,
}));
}
#[test]
fn actual_weight_higher_than_max_refunds_nothing() {
ExtBuilder::default()
.balance_factor(10)
.base_weight(5)
.execute_with(|| {
let len = 10;
let pre = ChargeTransactionPayment::<Runtime>::from(5 /* tipped */)
.pre_dispatch(&2, CALL, &info_from_weight(100), len)
.unwrap();
assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5);
assert_ok!(ChargeTransactionPayment::<Runtime>::post_dispatch(
&info_from_weight(100),
&post_info_from_weight(101),
len,
&Ok(())
));
assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5);
});
#[test]
fn zero_transfer_on_free_transaction() {
ExtBuilder::default()
.balance_factor(10)
.base_weight(5)
.build()
.execute_with(|| {
// So events are emitted
System::set_block_number(10);
let len = 10;
let dispatch_info =
DispatchInfo { weight: 100, pays_fee: Pays::No, class: DispatchClass::Normal };
let user = 69;
let pre = ChargeTransactionPayment::<Runtime>::from(0)
.pre_dispatch(&user, CALL, &dispatch_info, len)
.unwrap();
assert_eq!(Balances::total_balance(&user), 0);
assert_ok!(ChargeTransactionPayment::<Runtime>::post_dispatch(
&dispatch_info,
&default_post_info(),
len,
&Ok(())
));
assert_eq!(Balances::total_balance(&user), 0);
// No events for such a scenario
assert_eq!(System::events().len(), 0);
});
#[test]
fn refund_consistent_with_actual_weight() {
ExtBuilder::default()
.balance_factor(10)
.base_weight(7)
.build()
.execute_with(|| {
let info = info_from_weight(100);
let post_info = post_info_from_weight(33);
let prev_balance = Balances::free_balance(2);
let len = 10;
let tip = 5;
<NextFeeMultiplier<Runtime>>::put(Multiplier::saturating_from_rational(5, 4));
let pre = ChargeTransactionPayment::<Runtime>::from(tip)
.pre_dispatch(&2, CALL, &info, len)
.unwrap();
ChargeTransactionPayment::<Runtime>::post_dispatch(
&info,
&post_info,
len,
&Ok(()),
)
.unwrap();
let refund_based_fee = prev_balance - Balances::free_balance(2);
let actual_fee =
Pallet::<Runtime>::compute_actual_fee(len as u32, &info, &post_info, tip);
// 33 weight, 10 length, 7 base, 5 tip
assert_eq!(actual_fee, 7 + 10 + (33 * 5 / 4) + 5);
assert_eq!(refund_based_fee, actual_fee);
});
}
#[test]
fn should_alter_operational_priority() {
let tip = 5;
let len = 10;
ExtBuilder::default().balance_factor(100).build().execute_with(|| {
let normal =
DispatchInfo { weight: 100, class: DispatchClass::Normal, pays_fee: Pays::Yes };
let priority = ChargeTransactionPayment::<Runtime>(tip)
.validate(&2, CALL, &normal, len)
.unwrap()
.priority;
let priority = ChargeTransactionPayment::<Runtime>(2 * tip)
.validate(&2, CALL, &normal, len)
.unwrap()
.priority;
});
ExtBuilder::default().balance_factor(100).build().execute_with(|| {
let op = DispatchInfo {
weight: 100,
class: DispatchClass::Operational,
pays_fee: Pays::Yes,
};
let priority = ChargeTransactionPayment::<Runtime>(tip)
.validate(&2, CALL, &op, len)
.unwrap()
.priority;
let priority = ChargeTransactionPayment::<Runtime>(2 * tip)
.validate(&2, CALL, &op, len)
.unwrap()
.priority;
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
});
}
#[test]
fn no_tip_has_some_priority() {
let tip = 0;
let len = 10;
ExtBuilder::default().balance_factor(100).build().execute_with(|| {
let normal =
DispatchInfo { weight: 100, class: DispatchClass::Normal, pays_fee: Pays::Yes };
let priority = ChargeTransactionPayment::<Runtime>(tip)
.validate(&2, CALL, &normal, len)
.unwrap()
.priority;
assert_eq!(priority, 10);
});
ExtBuilder::default().balance_factor(100).build().execute_with(|| {
let op = DispatchInfo {
weight: 100,
class: DispatchClass::Operational,
pays_fee: Pays::Yes,
};
let priority = ChargeTransactionPayment::<Runtime>(tip)
.validate(&2, CALL, &op, len)
.unwrap()
.priority;
assert_eq!(priority, 5510);
});
}
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
#[test]
fn higher_tip_have_higher_priority() {
let get_priorities = |tip: u64| {
let mut priority1 = 0;
let mut priority2 = 0;
let len = 10;
ExtBuilder::default().balance_factor(100).build().execute_with(|| {
let normal =
DispatchInfo { weight: 100, class: DispatchClass::Normal, pays_fee: Pays::Yes };
priority1 = ChargeTransactionPayment::<Runtime>(tip)
.validate(&2, CALL, &normal, len)
.unwrap()
.priority;
});
ExtBuilder::default().balance_factor(100).build().execute_with(|| {
let op = DispatchInfo {
weight: 100,
class: DispatchClass::Operational,
pays_fee: Pays::Yes,
};
priority2 = ChargeTransactionPayment::<Runtime>(tip)
.validate(&2, CALL, &op, len)
.unwrap()
.priority;
});
(priority1, priority2)
};
let mut prev_priorities = get_priorities(0);
for tip in 1..3 {
let priorities = get_priorities(tip);
assert!(prev_priorities.0 < priorities.0);
assert!(prev_priorities.1 < priorities.1);
prev_priorities = priorities;
}
}
#[test]
fn post_info_can_change_pays_fee() {
ExtBuilder::default()
.balance_factor(10)
.base_weight(7)
.build()
.execute_with(|| {
let info = info_from_weight(100);
let post_info = post_info_from_pays(Pays::No);
let prev_balance = Balances::free_balance(2);
let len = 10;
let tip = 5;
<NextFeeMultiplier<Runtime>>::put(Multiplier::saturating_from_rational(5, 4));
let pre = ChargeTransactionPayment::<Runtime>::from(tip)
.pre_dispatch(&2, CALL, &info, len)
.unwrap();
ChargeTransactionPayment::<Runtime>::post_dispatch(
&info,
&post_info,
len,
&Ok(()),
)
let refund_based_fee = prev_balance - Balances::free_balance(2);
let actual_fee =
Pallet::<Runtime>::compute_actual_fee(len as u32, &info, &post_info, tip);
// Only 5 tip is paid
assert_eq!(actual_fee, 5);
assert_eq!(refund_based_fee, actual_fee);
});