use super::*;
use core::ops::{Add, Index, IndexMut};
use enum_iterator::cardinality;
use gear_core::ids::ReservationId;
use sp_runtime::{
codec::{self, MaxEncodedLen},
scale_info,
traits::Zero,
};
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
#[codec(crate = codec)]
#[scale_info(crate = scale_info)]
pub enum GasNodeId<T, U> {
Node(T),
Reservation(U),
}
impl<T, U> GasNodeId<T, U> {
pub fn to_node_id(self) -> Option<T> {
match self {
GasNodeId::Node(message_id) => Some(message_id),
GasNodeId::Reservation(_) => None,
}
}
pub fn to_reservation_id(self) -> Option<U> {
match self {
GasNodeId::Node(_) => None,
GasNodeId::Reservation(reservation_id) => Some(reservation_id),
}
}
}
impl<T, U> fmt::Display for GasNodeId<T, U>
where
T: fmt::Display,
U: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
GasNodeId::Node(id) => fmt::Display::fmt(id, f),
GasNodeId::Reservation(id) => fmt::Display::fmt(id, f),
}
}
}
impl<U> From<MessageId> for GasNodeId<MessageId, U> {
fn from(id: MessageId) -> Self {
Self::Node(id)
}
}
impl<T> From<ReservationId> for GasNodeId<T, ReservationId> {
fn from(id: ReservationId) -> Self {
Self::Reservation(id)
}
}
#[derive(Clone, Copy, Decode, Encode, Debug, Default, PartialEq, Eq, TypeInfo, MaxEncodedLen)]
#[codec(crate = codec)]
#[scale_info(crate = scale_info)]
pub struct NodeLock<Balance>([Balance; cardinality::<LockId>()]);
impl<Balance> Index<LockId> for NodeLock<Balance> {
type Output = Balance;
fn index(&self, index: LockId) -> &Self::Output {
&self.0[index as usize]
}
}
impl<Balance> IndexMut<LockId> for NodeLock<Balance> {
fn index_mut(&mut self, index: LockId) -> &mut Self::Output {
&mut self.0[index as usize]
}
}
impl<Balance: Zero + Copy> Zero for NodeLock<Balance> {
fn zero() -> Self {
Self([Balance::zero(); cardinality::<LockId>()])
}
fn is_zero(&self) -> bool {
self.0.iter().all(|x| x.is_zero())
}
}
impl<Balance: Add<Output = Balance> + Copy> Add<Self> for NodeLock<Balance> {
type Output = Self;
fn add(self, other: Self) -> Self::Output {
let NodeLock(mut inner) = self;
let NodeLock(other) = other;
for (i, elem) in inner.iter_mut().enumerate() {
*elem = *elem + other[i];
}
Self(inner)
}
}
impl<Balance: Zero + Copy + sp_runtime::traits::Saturating> NodeLock<Balance> {
pub fn total_locked(&self) -> Balance {
self.0
.iter()
.fold(Balance::zero(), |acc, v| acc.saturating_add(*v))
}
}
#[derive(Clone, Decode, Debug, Encode, MaxEncodedLen, TypeInfo, PartialEq, Eq)]
#[codec(crate = codec)]
#[scale_info(crate = scale_info)]
pub enum GasNode<ExternalId: Clone, Id: Clone, Balance: Zero + Clone, Funds> {
External {
id: ExternalId,
multiplier: GasMultiplier<Funds, Balance>,
value: Balance,
lock: NodeLock<Balance>,
system_reserve: Balance,
refs: ChildrenRefs,
consumed: bool,
deposit: bool,
},
Cut {
id: ExternalId,
multiplier: GasMultiplier<Funds, Balance>,
value: Balance,
lock: NodeLock<Balance>,
},
Reserved {
id: ExternalId,
multiplier: GasMultiplier<Funds, Balance>,
value: Balance,
lock: NodeLock<Balance>,
refs: ChildrenRefs,
consumed: bool,
},
SpecifiedLocal {
parent: Id,
root: Id,
value: Balance,
lock: NodeLock<Balance>,
system_reserve: Balance,
refs: ChildrenRefs,
consumed: bool,
},
UnspecifiedLocal {
parent: Id,
root: Id,
lock: NodeLock<Balance>,
system_reserve: Balance,
},
}
#[derive(Clone, Copy, Default, Decode, Debug, Encode, MaxEncodedLen, TypeInfo, PartialEq, Eq)]
#[codec(crate = codec)]
#[scale_info(crate = scale_info)]
pub struct ChildrenRefs {
spec_refs: u32,
unspec_refs: u32,
}
impl<
ExternalId: Clone,
Id: Clone + Copy,
Balance: Default + Zero + Clone + Copy + sp_runtime::traits::Saturating,
Funds: Clone,
> GasNode<ExternalId, Id, Balance, Funds>
{
pub fn total_value(&self) -> Balance {
self.value()
.unwrap_or_default()
.saturating_add(self.lock().total_locked())
.saturating_add(self.system_reserve().unwrap_or_default())
}
}
impl<ExternalId: Clone, Id: Clone + Copy, Balance: Default + Zero + Clone + Copy, Funds: Clone>
GasNode<ExternalId, Id, Balance, Funds>
{
pub fn new(
id: ExternalId,
multiplier: GasMultiplier<Funds, Balance>,
value: Balance,
deposit: bool,
) -> Self {
Self::External {
id,
multiplier,
value,
lock: Zero::zero(),
system_reserve: Zero::zero(),
refs: Default::default(),
consumed: false,
deposit,
}
}
pub fn increase_spec_refs(&mut self) {
self.adjust_refs(true, true);
}
pub fn decrease_spec_refs(&mut self) {
self.adjust_refs(false, true);
}
pub fn increase_unspec_refs(&mut self) {
self.adjust_refs(true, false);
}
pub fn decrease_unspec_refs(&mut self) {
self.adjust_refs(false, false);
}
pub fn mark_consumed(&mut self) {
if let Self::External { consumed, .. }
| Self::SpecifiedLocal { consumed, .. }
| Self::Reserved { consumed, .. } = self
{
*consumed = true;
}
}
pub fn is_consumed(&self) -> bool {
if let Self::External { consumed, .. }
| Self::SpecifiedLocal { consumed, .. }
| Self::Reserved { consumed, .. } = self
{
*consumed
} else {
false
}
}
pub fn is_patron(&self) -> bool {
if let Self::External { refs, consumed, .. }
| Self::SpecifiedLocal { refs, consumed, .. }
| Self::Reserved { refs, consumed, .. } = self
{
!consumed || refs.unspec_refs != 0
} else {
false
}
}
pub fn value(&self) -> Option<Balance> {
match self {
Self::External { value, .. }
| Self::Cut { value, .. }
| Self::Reserved { value, .. }
| Self::SpecifiedLocal { value, .. } => Some(*value),
Self::UnspecifiedLocal { .. } => None,
}
}
pub fn value_mut(&mut self) -> Option<&mut Balance> {
match self {
Self::External { ref mut value, .. }
| Self::Cut { ref mut value, .. }
| Self::Reserved { ref mut value, .. }
| Self::SpecifiedLocal { ref mut value, .. } => Some(value),
Self::UnspecifiedLocal { .. } => None,
}
}
pub fn lock(&self) -> &NodeLock<Balance> {
match self {
Self::External { lock, .. }
| Self::UnspecifiedLocal { lock, .. }
| Self::SpecifiedLocal { lock, .. }
| Self::Reserved { lock, .. }
| Self::Cut { lock, .. } => lock,
}
}
pub fn lock_mut(&mut self) -> &mut NodeLock<Balance> {
match self {
Self::External { ref mut lock, .. }
| Self::UnspecifiedLocal { ref mut lock, .. }
| Self::SpecifiedLocal { ref mut lock, .. }
| Self::Reserved { ref mut lock, .. }
| Self::Cut { ref mut lock, .. } => lock,
}
}
pub fn system_reserve(&self) -> Option<Balance> {
match self {
GasNode::External { system_reserve, .. }
| GasNode::SpecifiedLocal { system_reserve, .. }
| GasNode::UnspecifiedLocal { system_reserve, .. } => Some(*system_reserve),
GasNode::Cut { .. } | GasNode::Reserved { .. } => None,
}
}
pub fn system_reserve_mut(&mut self) -> Option<&mut Balance> {
match self {
GasNode::External { system_reserve, .. }
| GasNode::SpecifiedLocal { system_reserve, .. }
| GasNode::UnspecifiedLocal { system_reserve, .. } => Some(system_reserve),
GasNode::Cut { .. } | GasNode::Reserved { .. } => None,
}
}
pub fn parent(&self) -> Option<Id> {
match self {
Self::External { .. } | Self::Cut { .. } | Self::Reserved { .. } => None,
Self::SpecifiedLocal { parent, .. } | Self::UnspecifiedLocal { parent, .. } => {
Some(*parent)
}
}
}
pub fn refs(&self) -> u32 {
self.spec_refs().saturating_add(self.unspec_refs())
}
pub fn spec_refs(&self) -> u32 {
match self {
Self::External { refs, .. }
| Self::SpecifiedLocal { refs, .. }
| Self::Reserved { refs, .. } => refs.spec_refs,
_ => 0,
}
}
pub fn unspec_refs(&self) -> u32 {
match self {
Self::External { refs, .. }
| Self::SpecifiedLocal { refs, .. }
| Self::Reserved { refs, .. } => refs.unspec_refs,
_ => 0,
}
}
pub fn root_id(&self) -> Option<Id> {
match self {
Self::SpecifiedLocal { root, .. } | Self::UnspecifiedLocal { root, .. } => Some(*root),
Self::External { .. } | Self::Cut { .. } | Self::Reserved { .. } => None,
}
}
pub fn external_data(&self) -> Option<(ExternalId, GasMultiplier<Funds, Balance>)> {
match self {
Self::External { id, multiplier, .. }
| Self::Cut { id, multiplier, .. }
| Self::Reserved { id, multiplier, .. } => Some((id.clone(), multiplier.clone())),
Self::SpecifiedLocal { .. } | Self::UnspecifiedLocal { .. } => None,
}
}
pub(crate) fn is_external(&self) -> bool {
matches!(self, Self::External { .. })
}
pub(crate) fn is_specified_local(&self) -> bool {
matches!(self, Self::SpecifiedLocal { .. })
}
pub(crate) fn is_unspecified_local(&self) -> bool {
matches!(self, Self::UnspecifiedLocal { .. })
}
pub(crate) fn is_cut(&self) -> bool {
matches!(self, Self::Cut { .. })
}
pub(crate) fn is_reserved(&self) -> bool {
matches!(self, Self::Reserved { .. })
}
pub(crate) fn is_system_reservable(&self) -> bool {
self.system_reserve().is_some()
}
fn adjust_refs(&mut self, increase: bool, spec: bool) {
if let Self::External { refs, .. }
| Self::SpecifiedLocal { refs, .. }
| Self::Reserved { refs, .. } = self
{
match (increase, spec) {
(true, true) => refs.spec_refs = refs.spec_refs.saturating_add(1),
(true, false) => refs.unspec_refs = refs.unspec_refs.saturating_add(1),
(false, true) => refs.spec_refs = refs.spec_refs.saturating_sub(1),
(false, false) => refs.unspec_refs = refs.unspec_refs.saturating_sub(1),
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn asserts_node_have_either_external_data_or_root_id() {
let nodes_with_external_data: [gas_provider::node::GasNode<i32, i32, i32, i32>; 3] = [
GasNode::External {
id: Default::default(),
multiplier: Default::default(),
value: Default::default(),
lock: Default::default(),
system_reserve: Default::default(),
refs: Default::default(),
consumed: Default::default(),
deposit: Default::default(),
},
GasNode::Cut {
id: Default::default(),
multiplier: Default::default(),
value: Default::default(),
lock: Default::default(),
},
GasNode::Reserved {
id: Default::default(),
multiplier: Default::default(),
value: Default::default(),
lock: Default::default(),
refs: Default::default(),
consumed: Default::default(),
},
];
for node in nodes_with_external_data {
assert!(node.external_data().is_some() || node.root_id().is_none());
}
let nodes_with_root_id: [gas_provider::node::GasNode<i32, i32, i32, i32>; 2] = [
GasNode::SpecifiedLocal {
parent: Default::default(),
root: Default::default(),
value: Default::default(),
lock: Default::default(),
system_reserve: Default::default(),
refs: Default::default(),
consumed: Default::default(),
},
GasNode::UnspecifiedLocal {
parent: Default::default(),
root: Default::default(),
lock: Default::default(),
system_reserve: Default::default(),
},
];
for node in nodes_with_root_id {
assert!(node.external_data().is_none() || node.root_id().is_some());
}
}
}