use crate::{balance::ProductType, Config, Error};
use core::{fmt::Debug, marker::PhantomData};
use codec::DecodeWithMemTracking;
use scale_info::{prelude::vec, TypeInfo};
use derive_more::Constructor;
use frame_suite::{assets::*, misc::PositionIndex, plugins::ModelContext};
use frame_support::{
dispatch::DispatchResult,
ensure,
traits::{
fungible::{Inspect, InspectFreeze},
tokens::Precision,
VariantCountOf,
},
};
use frame_system::pallet;
use sp_core::{Decode, Encode, Get, MaxEncodedLen};
use sp_runtime::{
traits::{CheckedAdd, Zero},
BoundedVec, DispatchError, RuntimeDebug, Vec, WeakBoundedVec,
};
use sp_std::collections::btree_set::BTreeSet;
pub type Digest<T> = <T as pallet::Config>::AccountId;
pub type DirectDigest<T> = Digest<T>;
pub type IndexDigest<T> = Digest<T>;
pub type PoolDigest<T> = Digest<T>;
pub type EntryDigest<T> = Digest<T>;
pub type SlotDigest<T> = Digest<T>;
pub type DigestSource<T> = <T as pallet::Config>::AccountId;
pub type Proprietor<T> = <T as pallet::Config>::AccountId;
pub type AssetOf<T, I = ()> = <<T as Config<I>>::Asset as Inspect<Proprietor<T>>>::Balance;
pub type LazyBalanceOf<T, I = ()> = VirtualBalance<T, I>;
pub type CommitInstance<T, I = ()> = VirtualReceipt<T, I>;
pub type CommitReason<T, I = ()> = <<T as Config<I>>::Asset as InspectFreeze<Proprietor<T>>>::Id;
pub type BalanceContext<T, I = ()> = <T as Config<I>>::BalanceContext;
pub type BalanceModelContext<T, I = ()> = <BalanceContext<T, I> as ModelContext>::Context;
pub type LazyVirtual<T, A, R, Ti, Ad, I = ()> =
ProductType<T, I, BalanceModelContext<T, I>, A, R, Ti, Ad>;
pub type VirtualBalance<T, I = ()> =
LazyVirtual<T, BalanceAsset, BalanceRational, BalanceTime, BalanceAddon, I>;
pub type VirtualSnapShot<T, I = ()> =
LazyVirtual<T, SnapShotAsset, SnapShotRational, SnapShotTime, SnapShotAddon, I>;
pub type VirtualReceipt<T, I = ()> =
LazyVirtual<T, ReceiptAsset, ReceiptRational, ReceiptTime, ReceiptAddon, I>;
#[derive(
Encode,
Decode,
Clone,
RuntimeDebug,
PartialEq,
Eq,
MaxEncodedLen,
TypeInfo,
DecodeWithMemTracking,
)]
#[scale_info(skip_type_params(T, I))]
pub struct DigestInfo<T: Config<I>, I: 'static = ()>(
BoundedVec<LazyBalanceOf<T, I>, VariantCountOf<T::Position>>,
);
impl<T: Config<I>, I: 'static> DigestInfo<T, I> {
pub fn balances(&self) -> Result<Vec<(T::Position, LazyBalanceOf<T, I>)>, DispatchError> {
let bound = &self.0;
let mut collect = Vec::new();
for (i, balance) in bound.iter().enumerate() {
if *balance == Default::default() {
continue;
}
let position = <T::Position as PositionIndex>::position_of(i);
debug_assert!(
position.is_some(),
"commit-variant invalid position found for index {:?},
an example default of the position type for debugging is {:?}",
i,
T::Position::default()
);
let position = position.ok_or(Error::<T, I>::InvalidCommitVariantIndex)?;
collect.push((position, balance.clone()));
}
Ok(collect)
}
pub(crate) fn mut_balance(
&mut self,
variant: &T::Position,
) -> Option<&mut LazyBalanceOf<T, I>> {
let idx = variant.index();
self.0.get_mut(idx)
}
pub fn get_balance(&self, variant: &T::Position) -> Option<&LazyBalanceOf<T, I>> {
let idx = variant.index();
self.0.get(idx)
}
pub fn reveal(&self) -> BoundedVec<LazyBalanceOf<T, I>, VariantCountOf<T::Position>> {
self.0.clone()
}
pub(crate) fn init_balance(&mut self, variant: &T::Position) -> Result<(), DispatchError> {
let idx = variant.index();
let vec = &mut self.0;
for i in 0..=idx {
if let None = vec.get(i) {
let result = vec.try_push(Default::default());
debug_assert!(
result.is_ok(),
"default commit-variants push results bad, where pushed
index {:?} is lesser than or equal to expected variant (position)
{:?} whoose index is {:?}",
i,
variant,
idx
);
result.map_err(|_| Error::<T, I>::VariantsExhausted)?;
}
}
return Ok(());
}
}
#[derive(Encode, Decode, Clone, RuntimeDebug, MaxEncodedLen, TypeInfo, DecodeWithMemTracking)]
#[scale_info(skip_type_params(T, I))]
pub struct Commits<T: Config<I>, I: 'static = ()>(
WeakBoundedVec<CommitInstance<T, I>, T::MaxCommits>,
);
impl<T: Config<I>, I: 'static> Commits<T, I> {
pub(crate) fn new(instance: CommitInstance<T, I>) -> Result<Self, DispatchError> {
let max = T::MaxCommits::get();
ensure!(!max.is_zero(), Error::<T, I>::ZeroMaxCommits);
let vec = vec![instance];
let commits = WeakBoundedVec::<CommitInstance<T, I>, T::MaxCommits>::try_from(vec);
debug_assert!(
commits.is_ok(),
"single commit-instance vec to weak-vec of
max-commit {} is non-zero failed but shouldn't be",
T::MaxCommits::get()
);
let commits = commits.map_err(|_| Error::<T, I>::CommitConstructionFailed)?;
return Ok(Commits(commits));
}
pub(crate) fn add_commit(
&mut self,
instance: CommitInstance<T, I>,
) -> Result<(), DispatchError> {
debug_assert!(
!self.0.is_empty(),
"empty commits constructed without a single
commit-instance, attempting to add a new-instance {:?}",
instance
);
ensure!(!self.0.is_empty(), Error::<T, I>::EmptyCommitsNotAllowed);
let vec = &mut self.0;
vec.try_push(instance)
.map_err(|_| Error::<T, I>::MaxCommitsReached)?;
Ok(())
}
pub fn commits(&self) -> WeakBoundedVec<CommitInstance<T, I>, T::MaxCommits> {
debug_assert!(
!self.0.is_empty(),
"empty commits constructed without
a single commit-instance"
);
self.0.clone()
}
}
#[derive(
Encode,
Decode,
Clone,
RuntimeDebug,
MaxEncodedLen,
TypeInfo,
PartialEq,
Eq,
DecodeWithMemTracking,
)]
#[scale_info(skip_type_params(T, I))]
pub struct CommitInfo<T: Config<I>, I: 'static = ()> {
digest: Digest<T>,
commits: Commits<T, I>,
variant: T::Position,
}
impl<T: Config<I>, I: 'static> CommitInfo<T, I> {
pub(crate) fn new(
digest: Digest<T>,
instance: CommitInstance<T, I>,
variant: T::Position,
) -> Result<Self, DispatchError> {
let commits = Commits::<T, I>::new(instance)?;
let try_position = <T::Position as PositionIndex>::position_of(
<T::Position as PositionIndex>::index(&variant),
);
debug_assert!(
try_position.is_some(),
"cannot equalize new-commit's given variant {:?} and its derived
positional index (not consistent) when creating new commit-info for
proprietor towards non-classified-digest {:?}",
variant,
digest
);
let position = try_position.ok_or(Error::<T, I>::InvalidCommitVariantIndex)?;
debug_assert!(
position == variant,
"new-commit's given variant {:?} and its derived
positional index (not consistent) variant is not same,
found {:?} when creating new commit-info for
proprietor towards non-classified-digest {:?}",
variant,
position,
digest
);
ensure!(
position == variant,
Error::<T, I>::InvalidCommitVariantIndex
);
Ok(Self {
digest,
commits,
variant,
})
}
#[inline]
pub fn commits(&self) -> WeakBoundedVec<CommitInstance<T, I>, T::MaxCommits> {
Commits::<T, I>::commits(&self.commits)
}
pub fn digest(&self) -> Digest<T> {
self.digest.clone()
}
pub fn variant(&self) -> T::Position {
self.variant.clone()
}
#[inline]
pub(crate) fn add_commit(
&mut self,
instance: CommitInstance<T, I>,
) -> Result<(), DispatchError> {
self.commits.add_commit(instance)
}
}
#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DecodeWithMemTracking)]
#[scale_info(skip_type_params(T, I))]
pub struct EntryInfo<T: Config<I>, I: 'static = ()> {
digest: EntryDigest<T>,
shares: T::Shares,
variant: T::Position,
}
impl<T: Config<I>, I: 'static> EntryInfo<T, I> {
pub fn new(
digest: EntryDigest<T>,
shares: T::Shares,
variant: T::Position,
) -> Result<Self, DispatchError> {
ensure!(!shares.is_zero(), Error::<T, I>::ShareCannotBeZero);
let try_position = <T::Position as PositionIndex>::position_of(
<T::Position as PositionIndex>::index(&variant),
);
debug_assert!(
try_position.is_some(),
"cannot equalize new-commit's given variant {:?} and its derived
positional index (not consistent) when creating new entry-info for
entry-digest {:?} of shares {:?}",
variant,
digest,
shares
);
let position = try_position.ok_or(Error::<T, I>::InvalidCommitVariantIndex)?;
debug_assert!(
position == variant,
"new-commit's given variant {:?} and its derived
positional index (not consistent) variant is not same,
found {:?} when creating new entry-info for entry-digest
{:?} of shares {:?}",
variant,
position,
digest,
shares
);
ensure!(
position == variant,
Error::<T, I>::InvalidCommitVariantIndex
);
Ok(Self {
digest,
shares,
variant,
})
}
pub fn shares(&self) -> T::Shares {
self.shares
}
pub fn digest(&self) -> Digest<T> {
self.digest.clone()
}
pub fn variant(&self) -> T::Position {
self.variant.clone()
}
}
impl<T: Config<I>, I: 'static> Clone for EntryInfo<T, I> {
fn clone(&self) -> Self {
Self {
digest: self.digest.clone(),
shares: self.shares,
variant: self.variant.clone(),
}
}
}
#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DecodeWithMemTracking)]
#[scale_info(skip_type_params(T, I))]
pub struct Entries<T: Config<I>, I: 'static = ()>(
WeakBoundedVec<EntryInfo<T, I>, T::MaxIndexEntries>,
);
impl<T: Config<I>, I: 'static> Entries<T, I> {
pub fn new(entries: Vec<EntryInfo<T, I>>) -> Result<Self, DispatchError> {
let max = T::MaxIndexEntries::get();
ensure!(!max.is_zero(), Error::<T, I>::TriedCreatingHaltedIndexes);
ensure!(!entries.is_empty(), Error::<T, I>::EmptyEntriesNotAllowed);
let mut seen = BTreeSet::new();
for entry in &entries {
ensure!(
seen.insert(entry.digest.clone()),
Error::<T, I>::DuplicateEntry
);
}
let entries = WeakBoundedVec::<EntryInfo<T, I>, T::MaxIndexEntries>::try_from(entries)
.map_err(|_| Error::<T, I>::MaxEntriesReached)?;
return Ok(Entries(entries));
}
pub fn entries(&self) -> Vec<EntryInfo<T, I>> {
let bounded = &self.0;
let mut collect = Vec::new();
for entry in bounded {
collect.push(entry.clone())
}
debug_assert!(
!collect.is_empty(),
"empty entries-list initiated which
should not be for indexes"
);
collect
}
pub fn add_entry(&mut self, entry: EntryInfo<T, I>) -> Result<(), DispatchError> {
debug_assert!(
!self.0.is_empty(),
"empty entries constructed without a single
commit-instance, attempting to add a new-entry",
);
ensure!(!self.0.is_empty(), Error::<T, I>::EmptyEntriesNotAllowed);
let vec = &mut self.0;
vec.try_push(entry)
.map_err(|_| Error::<T, I>::MaxEntriesReached)?;
let mut seen = BTreeSet::new();
for entry in vec {
ensure!(
seen.insert(entry.digest.clone()),
Error::<T, I>::DuplicateEntry
);
}
Ok(())
}
pub fn remove_entry(&mut self, entry: &EntryDigest<T>) -> Result<(), DispatchError> {
debug_assert!(
!self.0.is_empty(),
"empty entries constructed without a single
commit-instance, attempting to remove an existing-entry {:?}",
entry
);
ensure!(
(!self.0.is_empty() && self.0.len() > 1),
Error::<T, I>::EmptyEntriesNotAllowed
);
debug_assert!(
self.0.len() > 1,
"attempting to remove an existing-entry {:?}, which
will result in zero-length entries",
entry
);
let mut entry_idx = None;
for (i, entry_of) in self.0.iter().enumerate() {
if entry_of.digest == *entry {
entry_idx = Some(i);
break;
}
}
match entry_idx {
Some(idx) => {
self.0.remove(idx);
}
None => {
return Err(Error::<T, I>::EntryOfIndexNotFound)?;
}
}
Ok(())
}
}
#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DecodeWithMemTracking)]
#[scale_info(skip_type_params(T, I))]
pub struct IndexInfo<T: Config<I>, I: 'static = ()> {
principal: AssetOf<T, I>,
capital: T::Shares,
entries: Entries<T, I>,
}
impl<T: Config<I>, I: 'static> IndexInfo<T, I> {
pub(crate) fn new(entries: &mut Entries<T, I>) -> Result<Self, DispatchError> {
debug_assert!(!entries.0.is_empty(), "entries is constructed empty");
ensure!(!entries.0.is_empty(), Error::<T, I>::EmptyEntriesNotAllowed);
let mut total_capital = T::Shares::zero();
for entry in &entries.0 {
let shares = entry.shares;
debug_assert!(
!shares.is_zero(),
"entry for digest {:?} of variant {:?} share is constructed zero",
entry.digest,
entry.variant
);
ensure!(!shares.is_zero(), Error::<T, I>::ShareCannotBeZero);
total_capital = total_capital
.checked_add(&shares)
.ok_or(Error::<T, I>::CapitalOverflowed)?;
}
debug_assert!(
!total_capital.is_zero(),
"total capital is zero while its entry shares isn't"
);
ensure!(!total_capital.is_zero(), Error::<T, I>::CapitalCannotBeZero);
Ok(Self {
principal: AssetOf::<T, I>::zero(),
capital: total_capital,
entries: entries.clone(),
})
}
pub fn capital(&self) -> T::Shares {
let value = self.capital;
debug_assert!(!value.is_zero(), "index capital is constructed zero");
value
}
pub fn principal(&self) -> AssetOf<T, I> {
self.principal
}
#[inline]
pub fn entries(&self) -> Vec<EntryInfo<T, I>> {
Entries::<T, I>::entries(&self.entries)
}
pub fn reveal_entries(&self) -> Entries<T, I> {
self.entries.clone()
}
pub fn entry_exists(&self, entry: &EntryDigest<T>) -> DispatchResult {
let entries = &self.entries;
debug_assert!(!entries.0.is_empty(), "entries is constructed empty");
ensure!(!entries.0.is_empty(), Error::<T, I>::EmptyEntriesNotAllowed);
let mut idx = None;
for (i, entry_of) in entries.0.iter().enumerate() {
if entry_of.digest == *entry {
idx = Some(i);
}
}
if let Some(_) = idx {
return Ok(());
};
Err(Error::<T, I>::EntryOfIndexNotFound.into())
}
pub(crate) fn set_balance(&mut self, principal: AssetOf<T, I>) {
self.principal = principal
}
}
#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DecodeWithMemTracking)]
#[scale_info(skip_type_params(T, I))]
pub struct SlotInfo<T: Config<I>, I: 'static = ()> {
digest: SlotDigest<T>,
shares: T::Shares,
commit: CommitInstance<T, I>,
variant: T::Position,
}
impl<T: Config<I>, I: 'static> SlotInfo<T, I> {
pub fn shares(&self) -> T::Shares {
self.shares
}
pub fn commit(&self) -> CommitInstance<T, I> {
self.commit.clone()
}
pub fn digest(&self) -> SlotDigest<T> {
self.digest.clone()
}
pub fn variant(&self) -> T::Position {
self.variant.clone()
}
fn set_slot_commit(&mut self, commit: CommitInstance<T, I>) {
self.commit = commit
}
}
impl<T: Config<I>, I: 'static> From<EntryInfo<T, I>> for SlotInfo<T, I> {
fn from(entry: EntryInfo<T, I>) -> Self {
Self {
digest: entry.digest,
shares: entry.shares,
commit: Default::default(),
variant: entry.variant,
}
}
}
#[derive(Encode, Decode, MaxEncodedLen, TypeInfo)]
#[scale_info(skip_type_params(T, I))]
pub struct Slots<T: Config<I>, I: 'static = ()>(WeakBoundedVec<SlotInfo<T, I>, T::MaxIndexEntries>);
impl<T: Config<I>, I: 'static> Slots<T, I> {
pub fn slots(&self) -> Vec<SlotInfo<T, I>> {
let bounded = &self.0;
let mut collect = Vec::new();
for slot in bounded {
collect.push(slot.clone())
}
debug_assert!(
!collect.is_empty(),
"empty slots initiated which should not be for pools"
);
collect
}
fn add_slot(&mut self, entry: EntryInfo<T, I>) -> Result<(), DispatchError> {
debug_assert!(
!self.0.is_empty(),
"empty slots constructed without a single
slot, attempting to add a new-slot via entry",
);
ensure!(!self.0.is_empty(), Error::<T, I>::EmptySlotsNotAllowed);
let vec = &mut self.0;
vec.try_push(entry.into())
.map_err(|_| Error::<T, I>::MaxSlotsReached)?;
let mut seen = BTreeSet::new();
for slot in vec {
ensure!(
seen.insert(slot.digest.clone()),
Error::<T, I>::DuplicateSlot,
);
}
Ok(())
}
fn remove_slot(&mut self, slot: &SlotDigest<T>) -> Result<(), DispatchError> {
debug_assert!(
!self.0.is_empty(),
"empty slots constructed without a single
slot, attempting to remove an existing-slot {:?}",
slot,
);
ensure!(
(!self.0.is_empty() && self.0.len() > 1),
Error::<T, I>::EmptySlotsNotAllowed
);
debug_assert!(
self.0.len() > 1,
"attempting to remove an existing-slot {:?}, which
will result in zero-length slots",
slot
);
let mut slot_idx = None;
for (i, slot_of) in self.0.iter().enumerate() {
if slot_of.digest == *slot {
slot_idx = Some(i);
break;
}
}
match slot_idx {
Some(idx) => {
self.0.remove(idx);
}
None => {
return Err(Error::<T, I>::SlotOfPoolNotFound)?;
}
}
Ok(())
}
fn set_slot_commit(
&mut self,
digest: &SlotDigest<T>,
commit: CommitInstance<T, I>,
) -> Result<(), DispatchError> {
debug_assert!(
!self.0.is_empty(),
"empty slots constructed without a single
slot {:?}",
self,
);
let mut slot_idx = None;
for (i, slot_of) in self.0.iter().enumerate() {
if slot_of.digest == *digest {
slot_idx = Some(i);
break;
}
}
match slot_idx {
Some(idx) => {
let slot_of = self
.0
.get_mut(idx)
.ok_or(Error::<T, I>::SlotOfPoolNotFound)?;
slot_of.set_slot_commit(commit);
}
None => {
return Err(Error::<T, I>::SlotOfPoolNotFound)?;
}
}
Ok(())
}
}
impl<T: Config<I>, I: 'static> TryFrom<Entries<T, I>> for Slots<T, I> {
type Error = DispatchError;
fn try_from(entries: Entries<T, I>) -> Result<Self, Self::Error> {
let raw_vec: Vec<SlotInfo<T, I>> =
entries.0.into_iter().map(|entry| entry.into()).collect();
let entries = WeakBoundedVec::try_from(raw_vec)
.map(Slots)
.map_err(|_| Error::<T, I>::MaxSlotsReached.into());
debug_assert!(
entries.is_ok(),
"both entries and slots have same upper weak-bound
but slots cannot be tried from entries"
);
entries
}
}
#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DecodeWithMemTracking)]
#[scale_info(skip_type_params(T, I))]
pub struct PoolInfo<T: Config<I>, I: 'static = ()> {
balance_of: LazyBalanceOf<T, I>,
capital: T::Shares,
commission: T::Commission,
slots: Slots<T, I>,
}
impl<T: Config<I>, I: 'static> PoolInfo<T, I> {
pub(crate) fn new(
index_entries: Entries<T, I>,
commission: T::Commission,
) -> Result<Self, DispatchError> {
let total_capital = index_entries
.0
.iter()
.try_fold(T::Shares::zero(), |acc, slot| {
acc.checked_add(&slot.shares)
.ok_or(Error::<T, I>::CapitalOverflowed)
})?;
let slots = index_entries.try_into()?;
Ok(Self {
balance_of: Default::default(),
capital: total_capital,
commission,
slots,
})
}
pub fn balance(&self) -> LazyBalanceOf<T, I> {
self.balance_of.clone()
}
pub(crate) fn set_balance(&mut self, balance: LazyBalanceOf<T, I>) {
self.balance_of = balance;
}
pub fn capital(&self) -> T::Shares {
let value = self.capital;
debug_assert!(!value.is_zero(), "index capital is constructed zero");
value
}
pub fn commission(&self) -> T::Commission {
self.commission
}
pub fn slots(&self) -> Vec<SlotInfo<T, I>> {
self.slots.slots()
}
pub(crate) fn balance_reset(&mut self) {
self.balance_of = Default::default();
for slot in &mut self.slots.0 {
slot.commit = Default::default();
}
}
#[inline]
pub(crate) fn set_slot_commit(
&mut self,
digest: &SlotDigest<T>,
commit: CommitInstance<T, I>,
) -> Result<(), DispatchError> {
self.slots.set_slot_commit(digest, commit)
}
pub(crate) fn add_slot(&mut self, entry: EntryInfo<T, I>) -> DispatchResult {
self.slots.add_slot(entry)?;
let total_capital = self
.slots
.0
.iter()
.try_fold(T::Shares::zero(), |acc, slot| {
acc.checked_add(&slot.shares)
.ok_or(Error::<T, I>::CapitalOverflowed)
})?;
self.capital = total_capital;
Ok(())
}
pub(crate) fn remove_slot(&mut self, slot: &SlotDigest<T>) -> DispatchResult {
self.slots.remove_slot(slot)?;
ensure!(
!self.slots.0.is_empty(),
Error::<T, I>::EmptySlotsNotAllowed
);
let total_capital = self
.slots
.0
.iter()
.try_fold(T::Shares::zero(), |acc, slot| {
acc.checked_add(&slot.shares)
.ok_or(Error::<T, I>::CapitalOverflowed)
})?;
self.capital = total_capital;
Ok(())
}
pub fn slot_exists(&self, slot: &SlotDigest<T>) -> DispatchResult {
let slots = &self.slots;
debug_assert!(!slots.0.is_empty(), "slots are constructed empty");
let mut idx = None;
for (i, slot_of) in slots.0.iter().enumerate() {
if slot_of.digest == *slot {
idx = Some(i);
}
}
if let Some(_) = idx {
return Ok(());
};
Err(Error::<T, I>::SlotOfPoolNotFound.into())
}
}
#[derive(
Encode,
Decode,
RuntimeDebug,
MaxEncodedLen,
TypeInfo,
Constructor,
PartialEq,
Eq,
DecodeWithMemTracking,
)]
#[scale_info(skip_type_params(T, I))]
pub struct IndexOfReason<T: Config<I>, I: 'static = ()> {
pub reason: CommitReason<T, I>,
pub index: IndexInfo<T, I>,
}
#[derive(
Encode,
Decode,
RuntimeDebug,
MaxEncodedLen,
TypeInfo,
Constructor,
PartialEq,
Eq,
DecodeWithMemTracking,
)]
#[scale_info(skip_type_params(T, I))]
pub struct PoolOfReason<T: Config<I>, I: 'static = ()> {
pub reason: CommitReason<T, I>,
pub pool: PoolInfo<T, I>,
}
#[derive(
Encode,
Decode,
MaxEncodedLen,
RuntimeDebug,
TypeInfo,
PartialEq,
Clone,
Constructor,
Eq,
DecodeWithMemTracking,
Copy,
)]
#[scale_info(skip_type_params(T, I))]
pub(crate) struct AssetDelta<T: Config<I>, I: 'static = ()> {
pub deposit: AssetOf<T, I>,
pub withdraw: AssetOf<T, I>,
}
#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DecodeWithMemTracking, Clone, Debug, Copy)]
pub enum ChooseDigest {
Direct,
Index,
Pool,
}
impl ChooseDigest {
pub fn digest_model<T: Config<I>, I: 'static>(&self, digest: Digest<T>) -> DigestVariant<T, I> {
match self {
ChooseDigest::Direct => DigestVariant::Direct(digest),
ChooseDigest::Index => DigestVariant::Index(digest),
ChooseDigest::Pool => DigestVariant::Pool(digest),
}
}
}
#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DecodeWithMemTracking)]
#[scale_info(skip_type_params(T, I))]
pub enum DigestVariant<T: Config<I>, I: 'static = ()> {
Direct(Digest<T>),
Index(Digest<T>),
Pool(Digest<T>),
#[codec(skip)]
__Ignore(PhantomData<I>),
}
#[derive(
Encode,
Decode,
DecodeWithMemTracking,
RuntimeDebug,
Clone,
PartialEq,
Eq,
MaxEncodedLen,
TypeInfo,
)]
pub enum PrecisionWrapper {
Exact,
BestEffort,
}
impl From<PrecisionWrapper> for Precision {
fn from(value: PrecisionWrapper) -> Self {
match value {
PrecisionWrapper::Exact => Precision::Exact,
PrecisionWrapper::BestEffort => Precision::BestEffort,
}
}
}
impl<T: Config<I>, I: 'static> Default for DigestInfo<T, I> {
fn default() -> Self {
Self(Default::default())
}
}
impl<T: Config<I>, I: 'static> PartialEq for Commits<T, I> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T: Config<I>, I: 'static> Eq for Commits<T, I> {}
impl<T: Config<I>, I: 'static> PartialEq for EntryInfo<T, I> {
fn eq(&self, other: &Self) -> bool {
self.digest == other.digest && self.shares == other.shares && self.variant == other.variant
}
}
impl<T: Config<I>, I: 'static> Eq for EntryInfo<T, I> {}
impl<T: Config<I>, I: 'static> core::fmt::Debug for EntryInfo<T, I> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("EntryInfo")
.field("digest", &self.digest)
.field("shares", &self.shares)
.field("variant", &self.variant)
.finish()
}
}
impl<T: Config<I>, I: 'static> PartialEq for Entries<T, I> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T: Config<I>, I: 'static> Eq for Entries<T, I> {}
impl<T: Config<I>, I: 'static> core::fmt::Debug for Entries<T, I> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("Entries").field(&self.0).finish()
}
}
impl<T: Config<I>, I: 'static> Clone for Entries<T, I> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T: Config<I>, I: 'static> core::fmt::Debug for IndexInfo<T, I> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("IndexInfo")
.field("principal", &self.principal)
.field("capital", &self.capital)
.field("entries", &self.entries)
.finish()
}
}
impl<T: Config<I>, I: 'static> PartialEq for IndexInfo<T, I> {
fn eq(&self, other: &Self) -> bool {
self.principal == other.principal
&& self.capital == other.capital
&& self.entries == other.entries
}
}
impl<T: Config<I>, I: 'static> Eq for IndexInfo<T, I> {}
impl<T: Config<I>, I: 'static> Clone for IndexInfo<T, I> {
fn clone(&self) -> Self {
Self {
principal: self.principal,
capital: self.capital,
entries: self.entries.clone(),
}
}
}
impl<T: Config<I>, I: 'static> PartialEq for SlotInfo<T, I> {
fn eq(&self, other: &Self) -> bool {
self.digest == other.digest
&& self.shares == other.shares
&& self.commit == other.commit
&& self.variant == other.variant
}
}
impl<T: Config<I>, I: 'static> Eq for SlotInfo<T, I> {}
impl<T: Config<I>, I: 'static> core::fmt::Debug for SlotInfo<T, I> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("SlotInfo")
.field("digest", &self.digest)
.field("shares", &self.shares)
.field("commit", &self.commit)
.field("variant", &self.variant)
.finish()
}
}
impl<T: Config<I>, I: 'static> Clone for SlotInfo<T, I> {
fn clone(&self) -> Self {
Self {
digest: self.digest.clone(),
shares: self.shares,
commit: self.commit.clone(),
variant: self.variant.clone(),
}
}
}
impl<T: Config<I>, I: 'static> PartialEq for Slots<T, I> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T: Config<I>, I: 'static> core::fmt::Debug for Slots<T, I> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("Slots").field(&self.0.as_slice()).finish()
}
}
impl<T: Config<I>, I: 'static> Eq for Slots<T, I> {}
impl<T: Config<I>, I: 'static> Clone for Slots<T, I> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T: Config<I>, I: 'static> PartialEq for PoolInfo<T, I> {
fn eq(&self, other: &Self) -> bool {
self.balance_of == other.balance_of
&& self.capital == other.capital
&& self.commission == other.commission
&& self.slots == other.slots
}
}
impl<T: Config<I>, I: 'static> Eq for PoolInfo<T, I> {}
impl<T: Config<I>, I: 'static> Clone for PoolInfo<T, I> {
fn clone(&self) -> Self {
Self {
balance_of: self.balance_of.clone(),
capital: self.capital,
commission: self.commission,
slots: self.slots.clone(),
}
}
}
impl<T: Config<I>, I: 'static> core::fmt::Debug for PoolInfo<T, I> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PoolInfo")
.field("balance_of", &self.balance_of)
.field("capital", &self.capital)
.field("commission", &self.commission)
.field("slots", &self.slots)
.finish()
}
}
impl<T: Config<I>, I: 'static> Clone for IndexOfReason<T, I> {
fn clone(&self) -> Self {
Self {
reason: self.reason,
index: self.index.clone(),
}
}
}
impl<T: Config<I>, I: 'static> Clone for PoolOfReason<T, I> {
fn clone(&self) -> Self {
Self {
reason: self.reason,
pool: self.pool.clone(),
}
}
}
impl<T: Config<I>, I: 'static> Clone for DigestVariant<T, I> {
fn clone(&self) -> Self {
match self {
DigestVariant::Direct(d) => DigestVariant::Direct(d.clone()),
DigestVariant::Index(d) => DigestVariant::Index(d.clone()),
DigestVariant::Pool(d) => DigestVariant::Pool(d.clone()),
DigestVariant::__Ignore(_) => {
debug_assert!(false, "digest variant phantom variant accessed");
DigestVariant::__Ignore(PhantomData)
}
}
}
}
impl<T: Config<I>, I: 'static> PartialEq for DigestVariant<T, I> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(DigestVariant::Direct(a), DigestVariant::Direct(b)) => a == b,
(DigestVariant::Index(a), DigestVariant::Index(b)) => a == b,
(DigestVariant::Pool(a), DigestVariant::Pool(b)) => a == b,
(DigestVariant::__Ignore(_), DigestVariant::__Ignore(_)) => {
debug_assert!(false, "digest variant phantom variant accessed");
true
}
(DigestVariant::__Ignore(_), _) => {
debug_assert!(false, "digest variant phantom variant accessed");
false
}
(_, DigestVariant::__Ignore(_)) => {
debug_assert!(false, "digest variant phantom variant accessed");
false
}
_ => false,
}
}
}
impl<T: Config<I>, I: 'static> Eq for DigestVariant<T, I> {}
impl<T: Config<I>, I: 'static> Debug for DigestVariant<T, I> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
DigestVariant::Direct(d) => write!(f, "Direct({:?})", d),
DigestVariant::Index(d) => write!(f, "Index({:?})", d),
DigestVariant::Pool(d) => write!(f, "Pool({:?})", d),
DigestVariant::__Ignore(_) => {
debug_assert!(false, "digest variant phantom variant accessed");
write!(f, "Invalid Digest Variant")
}
}
}
}
#[cfg(test)]
mod tests {
use crate::{
balance::{balance_total, mint},
mock::*,
};
use frame_suite::{
commitment::*,
misc::{Directive, PositionIndex},
};
use frame_support::{
assert_err, assert_ok,
traits::tokens::{Fortitude, Precision},
};
#[test]
fn digest_info_balances() {
commit_test_ext().execute_with(|| {
set_default_user_balance_and_standard_hold(ALICE).unwrap();
set_default_user_balance_and_standard_hold(ALAN).unwrap();
set_default_user_balance_and_standard_hold(BOB).unwrap();
let alice_position = Position::default();
Pallet::place_commit(
&ALICE,
&ESCROW,
&CONTRACT_FREELANCE,
STANDARD_COMMIT,
&Directive::new(Precision::BestEffort, Fortitude::Force),
)
.unwrap();
let alan_position = Position::position_of(1).unwrap();
Pallet::place_commit_of_variant(
&ALAN,
&ESCROW,
&CONTRACT_FREELANCE,
LARGE_COMMIT,
&alan_position,
&Directive::new(Precision::BestEffort, Fortitude::Force),
)
.unwrap();
let bob_position = Position::position_of(2).unwrap();
Pallet::place_commit_of_variant(
&BOB,
&GOVERNANCE,
&PROPOSAL_TREASURY_SPEND,
LARGE_COMMIT,
&bob_position,
&Directive::new(Precision::BestEffort, Fortitude::Force),
)
.unwrap();
let digest_info = DigestMap::get((ESCROW, CONTRACT_FREELANCE)).unwrap();
let balances = digest_info.balances().unwrap();
let alice_balances = balances.get(0).unwrap();
assert_eq!(alice_balances.0, alice_position);
let alice_bal = digest_info.get_balance(&alice_position).unwrap();
assert_eq!(alice_balances.1, *alice_bal);
let alan_balances = balances.get(1).unwrap();
assert_eq!(alan_balances.0, alan_position);
let alan_bal = digest_info.get_balance(&alan_position).unwrap();
assert_eq!(alan_balances.1, *alan_bal);
let digest_info = DigestMap::get((GOVERNANCE, PROPOSAL_TREASURY_SPEND)).unwrap();
let balances = digest_info.balances().unwrap();
let bob_balances = balances.get(0).unwrap();
assert_eq!(bob_balances.0, bob_position);
let bob_bal = digest_info.get_balance(&bob_position).unwrap();
assert_eq!(bob_balances.1, *bob_bal);
})
}
#[test]
fn digest_info_get_balance() {
commit_test_ext().execute_with(|| {
set_default_user_balance_and_standard_hold(ALICE).unwrap();
let alice_position = Position::default();
Pallet::place_commit(
&ALICE,
&ESCROW,
&CONTRACT_FREELANCE,
STANDARD_COMMIT,
&Directive::new(Precision::BestEffort, Fortitude::Force),
)
.unwrap();
let digest_info = DigestMap::get((ESCROW, CONTRACT_FREELANCE)).unwrap();
let alice_balance = digest_info.get_balance(&alice_position).unwrap();
let alice_bal_total =
balance_total(alice_balance, &alice_position, &CONTRACT_FREELANCE).unwrap();
assert_eq!(alice_bal_total, STANDARD_COMMIT);
})
}
#[test]
fn digest_info_mut_balance() {
commit_test_ext().execute_with(|| {
set_default_user_balance_and_standard_hold(ALICE).unwrap();
let alice_position = Position::default();
Pallet::place_commit(
&ALICE,
&ESCROW,
&CONTRACT_FREELANCE,
STANDARD_COMMIT,
&Directive::new(Precision::BestEffort, Fortitude::Force),
)
.unwrap();
let mut digest_info = DigestMap::get((ESCROW, CONTRACT_FREELANCE)).unwrap();
let alice_mut_balance = digest_info.mut_balance(&alice_position).unwrap();
let mint_val = 125;
mint(
alice_mut_balance,
&alice_position,
&CONTRACT_FREELANCE,
&mint_val,
&Directive::new(Precision::Exact, Fortitude::Force),
)
.unwrap();
let alice_bal_total =
balance_total(alice_mut_balance, &alice_position, &CONTRACT_FREELANCE).unwrap();
assert_ne!(alice_bal_total, STANDARD_COMMIT);
assert_eq!(alice_bal_total, STANDARD_COMMIT + mint_val);
})
}
#[test]
fn digest_info_reveal() {
commit_test_ext().execute_with(|| {
set_default_user_balance_and_standard_hold(ALICE).unwrap();
set_default_user_balance_and_standard_hold(ALAN).unwrap();
set_default_user_balance_and_standard_hold(BOB).unwrap();
let alice_position = Position::default();
Pallet::place_commit(
&ALICE,
&ESCROW,
&CONTRACT_FREELANCE,
STANDARD_COMMIT,
&Directive::new(Precision::BestEffort, Fortitude::Force),
)
.unwrap();
let alan_position = Position::position_of(1).unwrap();
Pallet::place_commit_of_variant(
&ALAN,
&ESCROW,
&CONTRACT_FREELANCE,
LARGE_COMMIT,
&alan_position,
&Directive::new(Precision::BestEffort, Fortitude::Force),
)
.unwrap();
let bob_position = Position::position_of(2).unwrap();
Pallet::place_commit_of_variant(
&BOB,
&GOVERNANCE,
&PROPOSAL_TREASURY_SPEND,
LARGE_COMMIT,
&bob_position,
&Directive::new(Precision::BestEffort, Fortitude::Force),
)
.unwrap();
let digest_info = DigestMap::get((ESCROW, CONTRACT_FREELANCE)).unwrap();
let reveal_balances = digest_info.reveal();
assert_eq!(reveal_balances.len(), 2);
let alice_total_bal =
balance_total(&reveal_balances[0], &alice_position, &CONTRACT_FREELANCE).unwrap();
assert_eq!(alice_total_bal, STANDARD_COMMIT,);
let alan_total_bal =
balance_total(&reveal_balances[1], &alan_position, &CONTRACT_FREELANCE).unwrap();
assert_eq!(alan_total_bal, LARGE_COMMIT,);
let digest_info = DigestMap::get((GOVERNANCE, PROPOSAL_TREASURY_SPEND)).unwrap();
let reveal_balances = digest_info.reveal();
assert_eq!(reveal_balances.len(), 3);
assert_eq!(
balance_total(&reveal_balances[0], &bob_position, &PROPOSAL_TREASURY_SPEND),
Ok(0)
);
assert_eq!(
balance_total(&reveal_balances[1], &bob_position, &PROPOSAL_TREASURY_SPEND),
Ok(0)
);
let bob_total_bal =
balance_total(&reveal_balances[2], &bob_position, &PROPOSAL_TREASURY_SPEND)
.unwrap();
assert_eq!(bob_total_bal, LARGE_COMMIT,);
})
}
#[test]
fn digest_info_init_balance() {
commit_test_ext().execute_with(|| {
let mut digest_info = DigestInfo::default();
let pos0 = Position::position_of(0).unwrap();
let pos1 = Position::position_of(1).unwrap();
let pos2 = Position::position_of(2).unwrap();
assert!(digest_info.get_balance(&pos0).is_none());
assert!(digest_info.get_balance(&pos1).is_none());
assert!(digest_info.get_balance(&pos2).is_none());
assert_ok!(digest_info.init_balance(&pos1));
assert!(digest_info.get_balance(&pos0).is_some());
assert!(digest_info.get_balance(&pos1).is_some());
assert!(digest_info.get_balance(&pos2).is_none());
assert_eq!(digest_info.reveal().len(), 2);
assert_ok!(digest_info.init_balance(&pos2));
assert!(digest_info.get_balance(&pos2).is_some());
assert_eq!(digest_info.reveal().len(), 3);
})
}
#[test]
fn commits_new_success() {
commit_test_ext().execute_with(|| {
let commit_ins = CommitInstance::default();
let commits = Commits::new(commit_ins.clone()).unwrap();
assert_eq!(commits.0.len(), 1);
let init_commit = commits.0.get(0).unwrap();
assert_eq!(init_commit.clone(), commit_ins);
})
}
#[test]
fn commits_success() {
commit_test_ext().execute_with(|| {
let derive_bal_a = CommitInstance::default();
let mut commits = Commits::new(derive_bal_a.clone()).unwrap();
let commits_vec = commits.commits();
assert_eq!(commits_vec, vec![derive_bal_a.clone()]);
let derive_bal_b = CommitInstance::default();
let derive_bal_c = CommitInstance::default();
commits.add_commit(derive_bal_b.clone()).unwrap();
commits.add_commit(derive_bal_c.clone()).unwrap();
let commits_vec = commits.commits();
assert_eq!(commits_vec, vec![derive_bal_a, derive_bal_b, derive_bal_c]);
})
}
#[test]
fn add_commit_success() {
commit_test_ext().execute_with(|| {
let derive_bal_a = CommitInstance::default();
let mut commits = Commits::new(derive_bal_a.clone()).unwrap();
let commits_vec = commits.commits();
assert_eq!(commits_vec, vec![derive_bal_a.clone()]);
let derive_bal_b = CommitInstance::default();
let derive_bal_c = CommitInstance::default();
commits.add_commit(derive_bal_b.clone()).unwrap();
commits.add_commit(derive_bal_c.clone()).unwrap();
let commits_vec = commits.commits();
assert_eq!(commits_vec, vec![derive_bal_a, derive_bal_b, derive_bal_c]);
let derive_bal_d = CommitInstance::default();
assert_err!(commits.add_commit(derive_bal_d), Error::MaxCommitsReached);
})
}
#[test]
fn commit_info_new_success() {
commit_test_ext().execute_with(|| {
let instance = CommitInstance::default();
let variant = Position::position_of(0).unwrap();
let commit_info = CommitInfo::new(VALIDATOR_ALPHA, instance.clone(), variant).unwrap();
assert_eq!(commit_info.commits().len(), 1);
assert_eq!(commit_info.commits(), vec![instance]);
assert_eq!(commit_info.digest(), VALIDATOR_ALPHA);
assert_eq!(commit_info.variant(), variant);
})
}
#[test]
fn commit_info_add_commit_success() {
commit_test_ext().execute_with(|| {
let instance_a = CommitInstance::default();
let variant = Position::position_of(0).unwrap();
let mut commit_info =
CommitInfo::new(VALIDATOR_ALPHA, instance_a.clone(), variant).unwrap();
assert_eq!(commit_info.commits().len(), 1);
assert_eq!(commit_info.commits(), vec![instance_a.clone()]);
let instance_b = CommitInstance::default();
let instance_c = CommitInstance::default();
commit_info.add_commit(instance_b.clone()).unwrap();
commit_info.add_commit(instance_c.clone()).unwrap();
assert_eq!(commit_info.commits().len(), 3);
assert_eq!(
commit_info.commits(),
vec![instance_a, instance_b, instance_c]
);
})
}
#[test]
fn entry_info_eq_true() {
commit_test_ext().execute_with(|| {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
assert!(entry_info_a.eq(&entry_info_b));
})
}
#[test]
fn entry_info_eq_false() {
commit_test_ext().execute_with(|| {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 100, variant).unwrap();
assert!(!entry_info_a.eq(&entry_info_b));
})
}
#[test]
fn entry_info_new_success() {
commit_test_ext().execute_with(|| {
let variant = Position::position_of(0).unwrap();
let entry_info = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
assert_eq!(entry_info.digest(), VALIDATOR_ALPHA);
assert_eq!(entry_info.shares, 100);
assert_eq!(entry_info.variant(), variant);
})
}
#[test]
fn entry_info_new_err_share_cannot_be_zero() {
let variant = Position::position_of(0).unwrap();
assert_err!(
EntryInfo::new(VALIDATOR_ALPHA, 0, variant),
Error::ShareCannotBeZero
);
}
#[test]
fn entries_eq_true() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entries_a = Entries::new(vec![entry_info_a]).unwrap();
let entries_b = Entries::new(vec![entry_info_b]).unwrap();
assert!(entries_a.eq(&entries_b));
}
#[test]
fn entries_eq_false() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let entries_a = Entries::new(vec![entry_info_a]).unwrap();
let entries_b = Entries::new(vec![entry_info_b]).unwrap();
assert!(!entries_a.eq(&entries_b));
}
#[test]
fn entries_new_success() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let entry_info_c = EntryInfo::new(VALIDATOR_GAMMA, 75, variant).unwrap();
let entries = Entries::new(vec![
entry_info_a.clone(),
entry_info_b.clone(),
entry_info_c.clone(),
])
.unwrap();
assert_eq!(entries.0.get(0), Some(&entry_info_a));
assert_eq!(entries.0.get(1), Some(&entry_info_b));
assert_eq!(entries.0.get(2), Some(&entry_info_c));
assert_eq!(entries.0.get(3), None);
}
#[test]
fn entries_new_err_duplicate_entry() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let _entry_info_c = EntryInfo::new(VALIDATOR_GAMMA, 75, variant).unwrap();
let entries = Entries::new(vec![
entry_info_a.clone(),
entry_info_b.clone(),
entry_info_b.clone(),
]);
assert!(entries.is_err());
assert_err!(entries, Error::DuplicateEntry);
}
#[test]
fn entries_new_err_max_entries_reached() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let entry_info_c = EntryInfo::new(VALIDATOR_GAMMA, 75, variant).unwrap();
let entry_info_d = EntryInfo::new(VALIDATOR_DELTA, 125, variant).unwrap();
let entries = Entries::new(vec![
entry_info_a.clone(),
entry_info_b.clone(),
entry_info_c.clone(),
entry_info_d.clone(),
]);
assert!(entries.is_err());
assert_err!(entries, Error::MaxEntriesReached);
}
#[test]
fn entries_success() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let entry_info_c = EntryInfo::new(VALIDATOR_GAMMA, 75, variant).unwrap();
let entries = Entries::new(vec![
entry_info_a.clone(),
entry_info_b.clone(),
entry_info_c.clone(),
])
.unwrap();
let actual_entries = entries.entries();
let expected_entries = vec![entry_info_a, entry_info_b, entry_info_c];
assert_eq!(actual_entries, expected_entries);
}
#[test]
fn add_entry_success() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let mut entries = Entries::new(vec![entry_info_a.clone()]).unwrap();
let actual_entries = entries.entries();
let expected_entries = vec![entry_info_a.clone()];
assert_eq!(actual_entries, expected_entries);
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let entry_info_c = EntryInfo::new(VALIDATOR_GAMMA, 75, variant).unwrap();
entries.add_entry(entry_info_b.clone()).unwrap();
let actual_entries = entries.entries();
let expected_entries = vec![entry_info_a.clone(), entry_info_b.clone()];
assert_eq!(actual_entries, expected_entries);
entries.add_entry(entry_info_c.clone()).unwrap();
let actual_entries = entries.entries();
let expected_entries = vec![entry_info_a, entry_info_b, entry_info_c];
assert_eq!(actual_entries, expected_entries);
}
#[test]
fn add_entry_err_duplicate_entry() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let mut entries = Entries::new(vec![entry_info_a.clone()]).unwrap();
assert_err!(entries.add_entry(entry_info_a), Error::DuplicateEntry);
}
#[test]
fn add_entry_err_max_entries_reached() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let entry_info_c = EntryInfo::new(VALIDATOR_GAMMA, 75, variant).unwrap();
let mut entries = Entries::new(vec![
entry_info_a.clone(),
entry_info_b.clone(),
entry_info_c.clone(),
])
.unwrap();
let entry_info_d = EntryInfo::new(VALIDATOR_DELTA, 125, variant).unwrap();
assert_err!(entries.add_entry(entry_info_d), Error::MaxEntriesReached);
}
#[test]
fn remove_entry_success() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let entry_info_c = EntryInfo::new(VALIDATOR_GAMMA, 75, variant).unwrap();
let mut entries = Entries::new(vec![
entry_info_a.clone(),
entry_info_b.clone(),
entry_info_c.clone(),
])
.unwrap();
let actual_entries = entries.entries();
let expected_entries = vec![
entry_info_a.clone(),
entry_info_b.clone(),
entry_info_c.clone(),
];
assert_eq!(actual_entries, expected_entries);
entries.remove_entry(&VALIDATOR_ALPHA).unwrap();
let actual_entries = entries.entries();
let expected_entries = vec![entry_info_b.clone(), entry_info_c.clone()];
assert_eq!(actual_entries, expected_entries);
entries.remove_entry(&VALIDATOR_GAMMA).unwrap();
let actual_entries = entries.entries();
let expected_entries = vec![entry_info_b.clone()];
assert_eq!(actual_entries, expected_entries);
}
#[test]
fn remove_entry_err_entry_of_index_not_found() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let mut entries = Entries::new(vec![entry_info_a.clone(), entry_info_b.clone()]).unwrap();
assert_err!(
entries.remove_entry(&VALIDATOR_GAMMA),
Error::EntryOfIndexNotFound
);
}
#[test]
fn remove_entry_err_empty_entries_not_allowed() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let mut entries = Entries::new(vec![entry_info_a.clone()]).unwrap();
assert_err!(
entries.remove_entry(&VALIDATOR_ALPHA),
Error::EmptyEntriesNotAllowed
);
}
#[test]
fn index_info_eq_true() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let mut entries_a = Entries::new(vec![entry_info_a]).unwrap();
let mut entries_b = Entries::new(vec![entry_info_b]).unwrap();
let index_info_a = IndexInfo::new(&mut entries_a).unwrap();
let index_info_b = IndexInfo::new(&mut entries_b).unwrap();
assert!(index_info_a.eq(&index_info_b));
}
#[test]
fn index_info_eq_false() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let mut entries_a = Entries::new(vec![entry_info_a]).unwrap();
let mut entries_b = Entries::new(vec![entry_info_b]).unwrap();
let index_info_a = IndexInfo::new(&mut entries_a).unwrap();
let index_info_b = IndexInfo::new(&mut entries_b).unwrap();
assert!(!index_info_a.eq(&index_info_b));
}
#[test]
fn index_info_new_success() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let mut entries = Entries::new(vec![entry_info_a.clone(), entry_info_b.clone()]).unwrap();
let index_info = IndexInfo::new(&mut entries).unwrap();
assert_eq!(index_info.capital(), 150);
assert_eq!(index_info.principal(), 0);
assert_eq!(index_info.entries(), vec![entry_info_a, entry_info_b]);
}
#[test]
fn index_info_new_err_capital_overflowed() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, MAX_SHARES, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let mut entries = Entries::new(vec![entry_info_a.clone(), entry_info_b.clone()]).unwrap();
let index_info = IndexInfo::new(&mut entries);
assert!(index_info.is_err());
assert_err!(index_info, Error::CapitalOverflowed);
}
#[test]
fn index_info_reveal_entries() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let mut entries = Entries::new(vec![entry_info_a.clone(), entry_info_b.clone()]).unwrap();
let index_info = IndexInfo::new(&mut entries).unwrap();
let reveal_entries = index_info.reveal_entries();
assert_eq!(reveal_entries, entries);
}
#[test]
fn entry_exists_success() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let mut entries = Entries::new(vec![entry_info_a.clone(), entry_info_b.clone()]).unwrap();
let index_info = IndexInfo::new(&mut entries).unwrap();
assert_ok!(index_info.entry_exists(&VALIDATOR_ALPHA));
assert_ok!(index_info.entry_exists(&VALIDATOR_BETA));
}
#[test]
fn entry_exists_err_entry_index_not_found() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let mut entries = Entries::new(vec![entry_info_a.clone(), entry_info_b.clone()]).unwrap();
let index_info = IndexInfo::new(&mut entries).unwrap();
assert_err!(
index_info.entry_exists(&VALIDATOR_GAMMA),
Error::EntryOfIndexNotFound
);
}
#[test]
fn slot_info_eq_true() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let slot_info_a: SlotInfo = entry_info_a.into();
let slot_info_b: SlotInfo = entry_info_b.into();
assert!(slot_info_a.eq(&slot_info_b));
}
#[test]
fn slot_info_eq_false() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let slot_info_a: SlotInfo = entry_info_a.into();
let slot_info_b: SlotInfo = entry_info_b.into();
assert!(!slot_info_a.eq(&slot_info_b));
}
#[test]
fn slot_info_from_entry_success() {
let variant = Position::position_of(0).unwrap();
let entry = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let slot_info: SlotInfo = entry.clone().into();
assert_eq!(slot_info.digest(), VALIDATOR_ALPHA);
assert_eq!(slot_info.shares(), 100);
assert_eq!(slot_info.variant(), variant);
let default_commit = CommitInstance::default();
assert_eq!(slot_info.commit(), default_commit);
}
#[test]
fn slot_info_set_slot_commit() {
commit_test_ext().execute_with(|| {
set_default_user_balance_and_standard_hold(ALICE).unwrap();
let variant = Position::position_of(0).unwrap();
let entry = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let mut slot_info: SlotInfo = entry.clone().into();
let default_commit = CommitInstance::default();
assert_eq!(slot_info.commit(), default_commit);
let alice_position = Position::position_of(1).unwrap();
Pallet::place_commit_of_variant(
&ALICE,
&GOVERNANCE,
&PROPOSAL_TREASURY_SPEND,
LARGE_COMMIT,
&alice_position,
&Directive::new(Precision::BestEffort, Fortitude::Force),
)
.unwrap();
let commit_info = CommitMap::get((ALICE, GOVERNANCE)).unwrap();
let new_instance = commit_info.commits.0.get(0).unwrap();
slot_info.set_slot_commit(new_instance.clone());
assert_ne!(slot_info.commit(), default_commit);
assert_eq!(slot_info.commit(), new_instance.clone());
})
}
#[test]
fn slots_eq_true() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entries_a = Entries::new(vec![entry_info_a]).unwrap();
let entries_b = Entries::new(vec![entry_info_b]).unwrap();
let slots_a = Slots::try_from(entries_a).unwrap();
let slots_b = Slots::try_from(entries_b).unwrap();
assert!(slots_a.eq(&slots_b));
}
#[test]
fn slots_eq_false() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let entries_a = Entries::new(vec![entry_info_a]).unwrap();
let entries_b = Entries::new(vec![entry_info_b]).unwrap();
let slots_a = Slots::try_from(entries_a).unwrap();
let slots_b = Slots::try_from(entries_b).unwrap();
assert!(!slots_a.eq(&slots_b));
}
#[test]
fn slots_success() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let entry_info_c = EntryInfo::new(VALIDATOR_GAMMA, 75, variant).unwrap();
let entries = Entries::new(vec![
entry_info_a.clone(),
entry_info_b.clone(),
entry_info_c.clone(),
])
.unwrap();
let slots = Slots::try_from(entries).unwrap();
let slot_info_a: SlotInfo = entry_info_a.into();
let slot_info_b: SlotInfo = entry_info_b.into();
let slot_info_c: SlotInfo = entry_info_c.into();
let actual_slots = slots.slots();
let expected_slots = vec![slot_info_a, slot_info_b, slot_info_c];
assert_eq!(actual_slots, expected_slots);
}
#[test]
fn add_slot_success() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let entry_info_c = EntryInfo::new(VALIDATOR_GAMMA, 75, variant).unwrap();
let entries = Entries::new(vec![entry_info_a.clone(), entry_info_b.clone()]).unwrap();
let mut slots = Slots::try_from(entries).unwrap();
let slot_info_a: SlotInfo = entry_info_a.into();
let slot_info_b: SlotInfo = entry_info_b.into();
let actual_slots = slots.slots();
let expected_slots = vec![slot_info_a.clone(), slot_info_b.clone()];
assert_eq!(actual_slots, expected_slots);
slots.add_slot(entry_info_c.clone()).unwrap();
let slot_info_c: SlotInfo = entry_info_c.into();
let actual_slots = slots.slots();
let expected_slots = vec![slot_info_a, slot_info_b, slot_info_c];
assert_eq!(actual_slots, expected_slots);
}
#[test]
fn add_slot_err_max_slots_reached() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let entry_info_c = EntryInfo::new(VALIDATOR_GAMMA, 75, variant).unwrap();
let entry_info_d = EntryInfo::new(VALIDATOR_DELTA, 125, variant).unwrap();
let entries = Entries::new(vec![entry_info_a.clone(), entry_info_b.clone()]).unwrap();
let mut slots = Slots::try_from(entries).unwrap();
slots.add_slot(entry_info_c.clone()).unwrap();
assert_err!(slots.add_slot(entry_info_d), Error::MaxSlotsReached);
}
#[test]
fn add_slot_err_duplicate_slot() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let _entry_info_c = EntryInfo::new(VALIDATOR_GAMMA, 75, variant).unwrap();
let entries = Entries::new(vec![entry_info_a.clone(), entry_info_b.clone()]).unwrap();
let mut slots = Slots::try_from(entries).unwrap();
assert_err!(slots.add_slot(entry_info_b), Error::DuplicateSlot);
}
#[test]
fn remove_slot_success() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let entry_info_c = EntryInfo::new(VALIDATOR_GAMMA, 75, variant).unwrap();
let entries = Entries::new(vec![
entry_info_a.clone(),
entry_info_b.clone(),
entry_info_c.clone(),
])
.unwrap();
let mut slots = Slots::try_from(entries).unwrap();
let slot_info_a: SlotInfo = entry_info_a.into();
let slot_info_b: SlotInfo = entry_info_b.into();
let slot_info_c: SlotInfo = entry_info_c.into();
let actual_slots = slots.slots();
let expected_slots = vec![
slot_info_a.clone(),
slot_info_b.clone(),
slot_info_c.clone(),
];
assert_eq!(actual_slots, expected_slots);
slots.remove_slot(&VALIDATOR_ALPHA).unwrap();
let actual_slots = slots.slots();
let expected_slots = vec![slot_info_b.clone(), slot_info_c.clone()];
assert_eq!(actual_slots, expected_slots);
slots.remove_slot(&VALIDATOR_GAMMA).unwrap();
let actual_slots = slots.slots();
let expected_slots = vec![slot_info_b.clone()];
assert_eq!(actual_slots, expected_slots);
}
#[test]
fn remove_slot_err_slot_of_pool_not_found() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let entries = Entries::new(vec![entry_info_a.clone(), entry_info_b.clone()]).unwrap();
let mut slots = Slots::try_from(entries).unwrap();
assert_err!(
slots.remove_slot(&VALIDATOR_GAMMA),
Error::SlotOfPoolNotFound
);
}
#[test]
fn remove_slot_err_empty_slots_not_allowed() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entries = Entries::new(vec![entry_info_a.clone()]).unwrap();
let mut slots = Slots::try_from(entries).unwrap();
assert_err!(
slots.remove_slot(&VALIDATOR_ALPHA),
Error::EmptySlotsNotAllowed
);
}
#[test]
fn set_slot_commit_success() {
commit_test_ext().execute_with(|| {
set_default_user_balance_and_standard_hold(ALICE).unwrap();
let alice_position = Position::position_of(1).unwrap();
Pallet::place_commit_of_variant(
&ALICE,
&GOVERNANCE,
&PROPOSAL_TREASURY_SPEND,
LARGE_COMMIT,
&alice_position,
&Directive::new(Precision::BestEffort, Fortitude::Force),
)
.unwrap();
let commit_info = CommitMap::get((ALICE, GOVERNANCE)).unwrap();
let new_instance = commit_info.commits.0.get(0).unwrap();
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let entry_info_c = EntryInfo::new(VALIDATOR_GAMMA, 75, variant).unwrap();
let entries = Entries::new(vec![
entry_info_a.clone(),
entry_info_b.clone(),
entry_info_c.clone(),
])
.unwrap();
let mut slots = Slots::try_from(entries).unwrap();
let slot_info_a: SlotInfo = entry_info_a.into();
let slot_info_b: SlotInfo = entry_info_b.into();
let slot_info_c: SlotInfo = entry_info_c.into();
let actual_slots = slots.slots();
let expected_slots = vec![
slot_info_a.clone(),
slot_info_b.clone(),
slot_info_c.clone(),
];
assert_eq!(actual_slots, expected_slots);
slots
.set_slot_commit(&VALIDATOR_BETA, new_instance.clone())
.unwrap();
let actual_slots = slots.slots();
let new_slot_b = &actual_slots[1];
assert_ne!(new_slot_b.clone(), slot_info_b.clone());
let expected_slots = vec![slot_info_a.clone(), new_slot_b.clone(), slot_info_c.clone()];
assert_eq!(actual_slots, expected_slots);
})
}
#[test]
fn set_slot_commit_err_slot_of_pool_not_found() {
commit_test_ext().execute_with(|| {
set_default_user_balance_and_standard_hold(ALICE).unwrap();
let alice_position = Position::position_of(1).unwrap();
Pallet::place_commit_of_variant(
&ALICE,
&GOVERNANCE,
&PROPOSAL_TREASURY_SPEND,
LARGE_COMMIT,
&alice_position,
&Directive::new(Precision::BestEffort, Fortitude::Force),
)
.unwrap();
let commit_info = CommitMap::get((ALICE, GOVERNANCE)).unwrap();
let new_instance = commit_info.commits.0.get(0).unwrap();
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let entries = Entries::new(vec![entry_info_a.clone(), entry_info_b.clone()]).unwrap();
let mut slots = Slots::try_from(entries).unwrap();
let slot_info_a: SlotInfo = entry_info_a.into();
let slot_info_b: SlotInfo = entry_info_b.into();
let actual_slots = slots.slots();
let expected_slots = vec![slot_info_a.clone(), slot_info_b.clone()];
assert_eq!(actual_slots, expected_slots);
let result = slots.set_slot_commit(&VALIDATOR_GAMMA, new_instance.clone());
assert_err!(result, Error::SlotOfPoolNotFound);
})
}
#[test]
fn pool_info_eq_true() {
let variant = Position::position_of(0).unwrap();
let entry_info = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entries = Entries::new(vec![entry_info]).unwrap();
let pool_info_a = PoolInfo::new(entries.clone(), COMMISSION_ZERO).unwrap();
let pool_info_b = PoolInfo::new(entries, COMMISSION_ZERO).unwrap();
assert!(pool_info_a.eq(&pool_info_b));
}
#[test]
fn pool_info_eq_false() {
let variant = Position::position_of(0).unwrap();
let entry_info = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entries = Entries::new(vec![entry_info]).unwrap();
let pool_info_a = PoolInfo::new(entries.clone(), COMMISSION_ZERO).unwrap();
let pool_info_b = PoolInfo::new(entries, COMMISSION_STANDARD).unwrap();
assert!(!pool_info_a.eq(&pool_info_b));
}
#[test]
fn pool_info_new_success() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let entries = Entries::new(vec![entry_info_a.clone(), entry_info_b.clone()]).unwrap();
let pool = PoolInfo::new(entries, COMMISSION_STANDARD).unwrap();
let slot_info_a: SlotInfo = entry_info_a.into();
let slot_info_b: SlotInfo = entry_info_b.into();
assert_eq!(pool.balance_of, LazyBalance::default());
assert_eq!(pool.capital(), 150);
assert_eq!(pool.commission, COMMISSION_STANDARD);
assert_eq!(pool.slots().len(), 2);
assert_eq!(pool.slots(), vec![slot_info_a, slot_info_b]);
}
#[test]
fn pool_info_new_err_capital_overflowed() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, MAX_SHARES, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let entries = Entries::new(vec![entry_info_a.clone(), entry_info_b.clone()]).unwrap();
let pool = PoolInfo::new(entries, COMMISSION_STANDARD);
assert!(pool.is_err());
assert_err!(pool, Error::CapitalOverflowed);
}
#[test]
fn pool_info_slots_success() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 50, variant).unwrap();
let entry_info_c = EntryInfo::new(VALIDATOR_GAMMA, 150, variant).unwrap();
let entries = Entries::new(vec![
entry_info_a.clone(),
entry_info_b.clone(),
entry_info_c.clone(),
])
.unwrap();
let pool = PoolInfo::new(entries, COMMISSION_STANDARD).unwrap();
let slot_info_a: SlotInfo = entry_info_a.into();
let slot_info_b: SlotInfo = entry_info_b.into();
let slot_info_c: SlotInfo = entry_info_c.into();
let actual_slots = pool.slots();
let expected_slots = vec![slot_info_a, slot_info_b, slot_info_c];
assert_eq!(actual_slots, expected_slots);
}
#[test]
fn pool_info_balance_reset_success() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 200, variant).unwrap();
let entries = Entries::new(vec![entry_info_a, entry_info_b]).unwrap();
let mut pool = PoolInfo::new(entries, COMMISSION_ZERO).unwrap();
pool.balance_reset();
assert_eq!(pool.balance_of, Default::default());
for slot in pool.slots() {
assert_eq!(slot.commit(), Default::default());
}
}
#[test]
fn pool_info_add_slot_success() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 100, variant).unwrap();
let entries = Entries::new(vec![entry_info_a.clone(), entry_info_b.clone()]).unwrap();
let mut pool = PoolInfo::new(entries, COMMISSION_ZERO).unwrap();
let slot_info_a: SlotInfo = entry_info_a.into();
let slot_info_b: SlotInfo = entry_info_b.into();
assert_eq!(pool.capital(), 200);
assert_eq!(pool.slots().len(), 2);
let actual_slots = pool.slots();
let expected_slots = vec![slot_info_a.clone(), slot_info_b.clone()];
assert_eq!(actual_slots, expected_slots);
let entry_info_c = EntryInfo::new(VALIDATOR_GAMMA, 50, variant).unwrap();
assert_ok!(pool.add_slot(entry_info_c.clone()));
let slot_info_c: SlotInfo = entry_info_c.into();
assert_eq!(pool.capital(), 250);
assert_eq!(pool.slots().len(), 3);
let actual_slots = pool.slots();
let expected_slots = vec![slot_info_a.clone(), slot_info_b.clone(), slot_info_c];
assert_eq!(actual_slots, expected_slots);
}
#[test]
fn pool_info_add_slot_err_capital_overflowed() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 100, variant).unwrap();
let entries = Entries::new(vec![entry_info_a.clone(), entry_info_b.clone()]).unwrap();
let mut pool = PoolInfo::new(entries, COMMISSION_ZERO).unwrap();
let slot_info_a: SlotInfo = entry_info_a.into();
let slot_info_b: SlotInfo = entry_info_b.into();
assert_eq!(pool.capital(), 200);
assert_eq!(pool.slots().len(), 2);
let actual_slots = pool.slots();
let expected_slots = vec![slot_info_a.clone(), slot_info_b.clone()];
assert_eq!(actual_slots, expected_slots);
let entry_info_c = EntryInfo::new(VALIDATOR_GAMMA, MAX_SHARES, variant).unwrap();
assert_err!(pool.add_slot(entry_info_c), Error::CapitalOverflowed);
}
#[test]
fn pool_info_remove_slot_success() {
let variant = Position::position_of(0).unwrap();
let entry_info_a = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entry_info_b = EntryInfo::new(VALIDATOR_BETA, 100, variant).unwrap();
let entry_info_c = EntryInfo::new(VALIDATOR_GAMMA, 50, variant).unwrap();
let entries = Entries::new(vec![
entry_info_a.clone(),
entry_info_b.clone(),
entry_info_c.clone(),
])
.unwrap();
let mut pool = PoolInfo::new(entries, COMMISSION_ZERO).unwrap();
let slot_info_a: SlotInfo = entry_info_a.into();
let slot_info_b: SlotInfo = entry_info_b.into();
let slot_info_c: SlotInfo = entry_info_c.into();
assert_eq!(pool.capital(), 250);
assert_eq!(pool.slots().len(), 3);
let actual_slots = pool.slots();
let expected_slots = vec![
slot_info_a.clone(),
slot_info_b.clone(),
slot_info_c.clone(),
];
assert_eq!(actual_slots, expected_slots);
pool.remove_slot(&VALIDATOR_ALPHA).unwrap();
let actual_slots = pool.slots();
let expected_slots = vec![slot_info_b.clone(), slot_info_c.clone()];
assert_eq!(actual_slots, expected_slots);
assert_eq!(pool.capital(), 150);
assert_eq!(pool.slots().len(), 2);
pool.remove_slot(&VALIDATOR_GAMMA).unwrap();
let actual_slots = pool.slots();
let expected_slots = vec![slot_info_b.clone()];
assert_eq!(actual_slots, expected_slots);
assert_eq!(pool.capital(), 100);
assert_eq!(pool.slots().len(), 1);
}
#[test]
fn pool_info_slot_exists_success() {
let variant = Position::position_of(0).unwrap();
let entry_info = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entries = Entries::new(vec![entry_info]).unwrap();
let pool = PoolInfo::new(entries, COMMISSION_ZERO).unwrap();
assert_ok!(pool.slot_exists(&VALIDATOR_ALPHA));
}
#[test]
fn pool_info_slot_exists_err_not_found() {
let variant = Position::position_of(0).unwrap();
let entry_info = EntryInfo::new(VALIDATOR_ALPHA, 100, variant).unwrap();
let entries = Entries::new(vec![entry_info]).unwrap();
let pool = PoolInfo::new(entries, COMMISSION_ZERO).unwrap();
assert_err!(pool.slot_exists(&VALIDATOR_BETA), Error::SlotOfPoolNotFound);
}
}