Skip to content
tests.rs 157 KiB
Newer Older
	// Then
	assert_eq!(ledger.unlocking, vec![c(4, 100), c(5, 100), c(6, 30), c(7, 30)]);
	assert_eq!(ledger.total, 4 * 100 - 140);
	assert_eq!(LedgerSlashPerEra::get().0, 0);
	assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(6, 30), (7, 30)]));

	// Given
	ledger.unlocking = bounded_vec![c(4, 40), c(5, 100), c(6, 10), c(7, 250)];
	ledger.active = 500;
	// 900
	ledger.total = 40 + 10 + 100 + 250 + 500;
	// When we have a partial slash that touches all chunks
	assert_eq!(ledger.slash(900 / 2, 0, 0), 450);
	// Then
	assert_eq!(ledger.active, 500 / 2);
	assert_eq!(ledger.unlocking, vec![c(4, 40 / 2), c(5, 100 / 2), c(6, 10 / 2), c(7, 250 / 2)]);
	assert_eq!(ledger.total, 900 / 2);
	assert_eq!(LedgerSlashPerEra::get().0, 500 / 2);
	assert_eq!(
		LedgerSlashPerEra::get().1,
		BTreeMap::from([(4, 40 / 2), (5, 100 / 2), (6, 10 / 2), (7, 250 / 2)])
	);

	// slash 1/4th with not chunk.
	ledger.unlocking = bounded_vec![];
	ledger.active = 500;
	ledger.total = 500;
	// When we have a partial slash that touches all chunks
	assert_eq!(ledger.slash(500 / 4, 0, 0), 500 / 4);
	// Then
	assert_eq!(ledger.active, 3 * 500 / 4);
	assert_eq!(ledger.unlocking, vec![]);
	assert_eq!(ledger.total, ledger.active);
	assert_eq!(LedgerSlashPerEra::get().0, 3 * 500 / 4);
	assert_eq!(LedgerSlashPerEra::get().1, Default::default());

	// Given we have the same as above,
	ledger.unlocking = bounded_vec![c(4, 40), c(5, 100), c(6, 10), c(7, 250)];
	ledger.active = 500;
	ledger.total = 40 + 10 + 100 + 250 + 500; // 900
	assert_eq!(ledger.total, 900);
	// When  we have a higher min balance
	assert_eq!(
		ledger.slash(
			900 / 2,
			25, /* min balance - chunks with era 0 & 2 will be slashed to <=25, causing it to
			     * get swept */
			0
		),
		475
	);
	let dust = (10 / 2) + (40 / 2);
	assert_eq!(ledger.active, 500 / 2);
	assert_eq!(ledger.unlocking, vec![c(5, 100 / 2), c(7, 250 / 2)]);
	assert_eq!(ledger.total, 900 / 2 - dust);
	assert_eq!(LedgerSlashPerEra::get().0, 500 / 2);
	assert_eq!(
		LedgerSlashPerEra::get().1,
		BTreeMap::from([(4, 0), (5, 100 / 2), (6, 0), (7, 250 / 2)])
	);

	// Given
	// slash order --------------------NA--------2----------0----------1----
	ledger.unlocking = bounded_vec![c(4, 40), c(5, 100), c(6, 10), c(7, 250)];
	ledger.active = 500;
	ledger.total = 40 + 10 + 100 + 250 + 500; // 900
	assert_eq!(
		ledger.slash(
			500 + 10 + 250 + 100 / 2, // active + era 6 + era 7 + era 5 / 2
			0,
			2 /* slash era 2+4 first, so the affected parts are era 2+4, era 3+4 and
			   * ledge.active. This will cause the affected to go to zero, and then we will
			   * start slashing older chunks */
		),
		500 + 250 + 10 + 100 / 2
	);
	// Then
	assert_eq!(ledger.active, 0);
	assert_eq!(ledger.unlocking, vec![c(4, 40), c(5, 100 / 2)]);
	assert_eq!(ledger.total, 90);
	assert_eq!(LedgerSlashPerEra::get().0, 0);
	assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(5, 100 / 2), (6, 0), (7, 0)]));

	// Given
	// iteration order------------------NA---------2----------0----------1----
	ledger.unlocking = bounded_vec![c(4, 100), c(5, 100), c(6, 100), c(7, 100)];
	ledger.active = 100;
	ledger.total = 5 * 100;
	// When
	assert_eq!(
		ledger.slash(
			351, // active + era 6 + era 7 + era 5 / 2 + 1
			50,  // min balance - everything slashed below 50 will get dusted
			2    /* slash era 2+4 first, so the affected parts are era 2+4, era 3+4 and
			      * ledge.active. This will cause the affected to go to zero, and then we will
			      * start slashing older chunks */
		),
		400
	);
	// Then
	assert_eq!(ledger.active, 0);
	assert_eq!(ledger.unlocking, vec![c(4, 100)]);
	assert_eq!(ledger.total, 100);
	assert_eq!(LedgerSlashPerEra::get().0, 0);
	assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(5, 0), (6, 0), (7, 0)]));

	// Tests for saturating arithmetic

	// Given
	let slash = u64::MAX as Balance * 2;
	let value = slash
		- (9 * 4) // The value of the other parts of ledger that will get slashed
		+ 1;

	ledger.active = 10;
	ledger.unlocking = bounded_vec![c(4, 10), c(5, 10), c(6, 10), c(7, value)];
	ledger.total = value + 40;
	// When
	let slash_amount = ledger.slash(slash, 0, 0);
	assert_eq_error_rate!(slash_amount, slash, 5);
	// Then
	assert_eq!(ledger.active, 0); // slash of 9
	assert_eq!(ledger.unlocking, vec![]);
	assert_eq!(ledger.total, 0);
	assert_eq!(LedgerSlashPerEra::get().0, 0);
	assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(4, 0), (5, 0), (6, 0), (7, 0)]));

	// Given
	let slash = u64::MAX as Balance * 2;
	let value = u64::MAX as Balance * 2;
	let unit = 100;
	// slash * value that will saturate
	assert!(slash.checked_mul(value).is_none());
	// but slash * unit won't.
	assert!(slash.checked_mul(unit).is_some());
	ledger.unlocking = bounded_vec![c(4, unit), c(5, value), c(6, unit), c(7, unit)];
	//--------------------------------------note value^^^
	ledger.active = unit;
	ledger.total = unit * 4 + value;
	// When
	assert_eq!(ledger.slash(slash, 0, 0), slash - 43);
	// Then
	// The amount slashed out of `unit`
	let affected_balance = value + unit * 4;
	let ratio = Perquintill::from_rational(slash, affected_balance);
	// `unit` after the slash is applied
	let unit_slashed = {
		let unit_slash = ratio * unit;
		unit - unit_slash
	};
	let value_slashed = {
		let value_slash = ratio * value;
		value - value_slash
	};
	assert_eq!(ledger.active, unit_slashed);
	assert_eq!(ledger.unlocking, vec![c(5, value_slashed)]);
	assert_eq!(ledger.total, value_slashed);
	assert_eq!(LedgerSlashPerEra::get().0, 0);
	assert_eq!(
		LedgerSlashPerEra::get().1,
		BTreeMap::from([(4, 0), (5, value_slashed), (6, 0), (7, 0)])
	);
}