1use super::*;
20use core::ops::{Add, Index, IndexMut};
21use enum_iterator::cardinality;
22use gear_core::ids::ReservationId;
23use scale_decode::DecodeAsType;
24use scale_encode::EncodeAsType;
25use sp_runtime::{
26 codec::{self, MaxEncodedLen},
27 scale_info,
28 traits::Zero,
29};
30
31#[derive(
33 Debug,
34 Copy,
35 Clone,
36 Hash,
37 Eq,
38 PartialEq,
39 Ord,
40 PartialOrd,
41 Encode,
42 EncodeAsType,
43 Decode,
44 DecodeAsType,
45 TypeInfo,
46)]
47#[codec(crate = codec)]
48#[scale_info(crate = scale_info)]
49pub enum GasNodeId<T, U> {
50 Node(T),
51 Reservation(U),
52}
53
54impl<T, U> GasNodeId<T, U> {
55 pub fn to_node_id(self) -> Option<T> {
56 match self {
57 GasNodeId::Node(message_id) => Some(message_id),
58 GasNodeId::Reservation(_) => None,
59 }
60 }
61
62 pub fn to_reservation_id(self) -> Option<U> {
63 match self {
64 GasNodeId::Node(_) => None,
65 GasNodeId::Reservation(reservation_id) => Some(reservation_id),
66 }
67 }
68}
69
70impl<T, U> fmt::Display for GasNodeId<T, U>
71where
72 T: fmt::Display,
73 U: fmt::Display,
74{
75 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
76 match self {
77 GasNodeId::Node(id) => fmt::Display::fmt(id, f),
78 GasNodeId::Reservation(id) => fmt::Display::fmt(id, f),
79 }
80 }
81}
82
83impl<U> From<MessageId> for GasNodeId<MessageId, U> {
84 fn from(id: MessageId) -> Self {
85 Self::Node(id)
86 }
87}
88
89impl<T> From<ReservationId> for GasNodeId<T, ReservationId> {
90 fn from(id: ReservationId) -> Self {
91 Self::Reservation(id)
92 }
93}
94
95#[derive(Clone, Copy, Decode, Encode, Debug, Default, PartialEq, Eq, TypeInfo, MaxEncodedLen)]
96#[codec(crate = codec)]
97#[scale_info(crate = scale_info)]
98pub struct NodeLock<Balance>([Balance; cardinality::<LockId>()]);
99
100impl<Balance> Index<LockId> for NodeLock<Balance> {
101 type Output = Balance;
102
103 fn index(&self, index: LockId) -> &Self::Output {
104 &self.0[index as usize]
105 }
106}
107
108impl<Balance> IndexMut<LockId> for NodeLock<Balance> {
109 fn index_mut(&mut self, index: LockId) -> &mut Self::Output {
110 &mut self.0[index as usize]
111 }
112}
113
114impl<Balance: Zero + Copy> Zero for NodeLock<Balance> {
115 fn zero() -> Self {
116 Self([Balance::zero(); cardinality::<LockId>()])
117 }
118
119 fn is_zero(&self) -> bool {
120 self.0.iter().all(|x| x.is_zero())
121 }
122}
123
124impl<Balance: Add<Output = Balance> + Copy> Add<Self> for NodeLock<Balance> {
125 type Output = Self;
126
127 fn add(self, other: Self) -> Self::Output {
128 let NodeLock(mut inner) = self;
129 let NodeLock(other) = other;
130
131 for (i, elem) in inner.iter_mut().enumerate() {
132 *elem = *elem + other[i];
133 }
134
135 Self(inner)
136 }
137}
138
139impl<Balance: Zero + Copy + sp_runtime::traits::Saturating> NodeLock<Balance> {
142 pub fn total_locked(&self) -> Balance {
143 self.0
144 .iter()
145 .fold(Balance::zero(), |acc, v| acc.saturating_add(*v))
146 }
147}
148
149#[derive(Clone, Decode, Debug, Encode, MaxEncodedLen, TypeInfo, PartialEq, Eq)]
151#[codec(crate = codec)]
152#[scale_info(crate = scale_info)]
153pub enum GasNode<ExternalId: Clone, Id: Clone, Balance: Zero + Clone, Funds> {
154 External {
158 id: ExternalId,
159 multiplier: GasMultiplier<Funds, Balance>,
160 value: Balance,
161 lock: NodeLock<Balance>,
162 system_reserve: Balance,
163 refs: ChildrenRefs,
164 consumed: bool,
165 deposit: bool,
166 },
167
168 Cut {
173 id: ExternalId,
174 multiplier: GasMultiplier<Funds, Balance>,
175 value: Balance,
176 lock: NodeLock<Balance>,
177 },
178
179 Reserved {
183 id: ExternalId,
184 multiplier: GasMultiplier<Funds, Balance>,
185 value: Balance,
186 lock: NodeLock<Balance>,
187 refs: ChildrenRefs,
188 consumed: bool,
189 },
190
191 SpecifiedLocal {
200 parent: Id,
201 root: Id,
202 value: Balance,
203 lock: NodeLock<Balance>,
204 system_reserve: Balance,
205 refs: ChildrenRefs,
206 consumed: bool,
207 },
208
209 UnspecifiedLocal {
214 parent: Id,
215 root: Id,
216 lock: NodeLock<Balance>,
217 system_reserve: Balance,
218 },
219}
220
221#[derive(Clone, Copy, Default, Decode, Debug, Encode, MaxEncodedLen, TypeInfo, PartialEq, Eq)]
223#[codec(crate = codec)]
224#[scale_info(crate = scale_info)]
225pub struct ChildrenRefs {
226 spec_refs: u32,
227 unspec_refs: u32,
228}
229
230impl<
231 ExternalId: Clone,
232 Id: Clone + Copy,
233 Balance: Default + Zero + Clone + Copy + sp_runtime::traits::Saturating,
234 Funds: Clone,
235> GasNode<ExternalId, Id, Balance, Funds>
236{
237 pub fn total_value(&self) -> Balance {
239 self.value()
240 .unwrap_or_default()
241 .saturating_add(self.lock().total_locked())
242 .saturating_add(self.system_reserve().unwrap_or_default())
243 }
244}
245
246impl<ExternalId: Clone, Id: Clone + Copy, Balance: Default + Zero + Clone + Copy, Funds: Clone>
247 GasNode<ExternalId, Id, Balance, Funds>
248{
249 pub fn new(
251 id: ExternalId,
252 multiplier: GasMultiplier<Funds, Balance>,
253 value: Balance,
254 deposit: bool,
255 ) -> Self {
256 Self::External {
257 id,
258 multiplier,
259 value,
260 lock: Zero::zero(),
261 system_reserve: Zero::zero(),
262 refs: Default::default(),
263 consumed: false,
264 deposit,
265 }
266 }
267
268 pub fn increase_spec_refs(&mut self) {
270 self.adjust_refs(true, true);
271 }
272
273 pub fn decrease_spec_refs(&mut self) {
275 self.adjust_refs(false, true);
276 }
277
278 pub fn increase_unspec_refs(&mut self) {
280 self.adjust_refs(true, false);
281 }
282
283 pub fn decrease_unspec_refs(&mut self) {
285 self.adjust_refs(false, false);
286 }
287
288 pub fn mark_consumed(&mut self) {
290 if let Self::External { consumed, .. }
291 | Self::SpecifiedLocal { consumed, .. }
292 | Self::Reserved { consumed, .. } = self
293 {
294 *consumed = true;
295 }
296 }
297
298 pub fn is_consumed(&self) -> bool {
303 if let Self::External { consumed, .. }
304 | Self::SpecifiedLocal { consumed, .. }
305 | Self::Reserved { consumed, .. } = self
306 {
307 *consumed
308 } else {
309 false
310 }
311 }
312
313 pub fn is_patron(&self) -> bool {
325 if let Self::External { refs, consumed, .. }
326 | Self::SpecifiedLocal { refs, consumed, .. }
327 | Self::Reserved { refs, consumed, .. } = self
328 {
329 !consumed || refs.unspec_refs != 0
330 } else {
331 false
332 }
333 }
334
335 pub fn value(&self) -> Option<Balance> {
337 match self {
338 Self::External { value, .. }
339 | Self::Cut { value, .. }
340 | Self::Reserved { value, .. }
341 | Self::SpecifiedLocal { value, .. } => Some(*value),
342 Self::UnspecifiedLocal { .. } => None,
343 }
344 }
345
346 pub fn value_mut(&mut self) -> Option<&mut Balance> {
348 match *self {
349 Self::External { ref mut value, .. }
350 | Self::Cut { ref mut value, .. }
351 | Self::Reserved { ref mut value, .. }
352 | Self::SpecifiedLocal { ref mut value, .. } => Some(value),
353 Self::UnspecifiedLocal { .. } => None,
354 }
355 }
356
357 pub fn lock(&self) -> &NodeLock<Balance> {
359 match self {
360 Self::External { lock, .. }
361 | Self::UnspecifiedLocal { lock, .. }
362 | Self::SpecifiedLocal { lock, .. }
363 | Self::Reserved { lock, .. }
364 | Self::Cut { lock, .. } => lock,
365 }
366 }
367
368 pub fn lock_mut(&mut self) -> &mut NodeLock<Balance> {
370 match *self {
371 Self::External { ref mut lock, .. }
372 | Self::UnspecifiedLocal { ref mut lock, .. }
373 | Self::SpecifiedLocal { ref mut lock, .. }
374 | Self::Reserved { ref mut lock, .. }
375 | Self::Cut { ref mut lock, .. } => lock,
376 }
377 }
378
379 pub fn system_reserve(&self) -> Option<Balance> {
381 match self {
382 GasNode::External { system_reserve, .. }
383 | GasNode::SpecifiedLocal { system_reserve, .. }
384 | GasNode::UnspecifiedLocal { system_reserve, .. } => Some(*system_reserve),
385 GasNode::Cut { .. } | GasNode::Reserved { .. } => None,
386 }
387 }
388
389 pub fn system_reserve_mut(&mut self) -> Option<&mut Balance> {
391 match self {
392 GasNode::External { system_reserve, .. }
393 | GasNode::SpecifiedLocal { system_reserve, .. }
394 | GasNode::UnspecifiedLocal { system_reserve, .. } => Some(system_reserve),
395 GasNode::Cut { .. } | GasNode::Reserved { .. } => None,
396 }
397 }
398
399 pub fn parent(&self) -> Option<Id> {
405 match self {
406 Self::External { .. } | Self::Cut { .. } | Self::Reserved { .. } => None,
407 Self::SpecifiedLocal { parent, .. } | Self::UnspecifiedLocal { parent, .. } => {
408 Some(*parent)
409 }
410 }
411 }
412
413 pub fn refs(&self) -> u32 {
415 self.spec_refs().saturating_add(self.unspec_refs())
416 }
417
418 pub fn spec_refs(&self) -> u32 {
420 match self {
421 Self::External { refs, .. }
422 | Self::SpecifiedLocal { refs, .. }
423 | Self::Reserved { refs, .. } => refs.spec_refs,
424 _ => 0,
425 }
426 }
427
428 pub fn unspec_refs(&self) -> u32 {
430 match self {
431 Self::External { refs, .. }
432 | Self::SpecifiedLocal { refs, .. }
433 | Self::Reserved { refs, .. } => refs.unspec_refs,
434 _ => 0,
435 }
436 }
437
438 pub fn root_id(&self) -> Option<Id> {
440 match self {
441 Self::SpecifiedLocal { root, .. } | Self::UnspecifiedLocal { root, .. } => Some(*root),
442 Self::External { .. } | Self::Cut { .. } | Self::Reserved { .. } => None,
443 }
444 }
445
446 pub fn external_data(&self) -> Option<(ExternalId, GasMultiplier<Funds, Balance>)> {
448 match self {
449 Self::External { id, multiplier, .. }
450 | Self::Cut { id, multiplier, .. }
451 | Self::Reserved { id, multiplier, .. } => Some((id.clone(), multiplier.clone())),
452 Self::SpecifiedLocal { .. } | Self::UnspecifiedLocal { .. } => None,
453 }
454 }
455
456 pub(crate) fn is_external(&self) -> bool {
458 matches!(self, Self::External { .. })
459 }
460
461 pub(crate) fn is_specified_local(&self) -> bool {
463 matches!(self, Self::SpecifiedLocal { .. })
464 }
465
466 pub(crate) fn is_unspecified_local(&self) -> bool {
468 matches!(self, Self::UnspecifiedLocal { .. })
469 }
470
471 pub(crate) fn is_cut(&self) -> bool {
473 matches!(self, Self::Cut { .. })
474 }
475
476 pub(crate) fn is_reserved(&self) -> bool {
478 matches!(self, Self::Reserved { .. })
479 }
480
481 pub(crate) fn is_system_reservable(&self) -> bool {
483 self.system_reserve().is_some()
484 }
485
486 fn adjust_refs(&mut self, increase: bool, spec: bool) {
487 if let Self::External { refs, .. }
488 | Self::SpecifiedLocal { refs, .. }
489 | Self::Reserved { refs, .. } = self
490 {
491 match (increase, spec) {
492 (true, true) => refs.spec_refs = refs.spec_refs.saturating_add(1),
493 (true, false) => refs.unspec_refs = refs.unspec_refs.saturating_add(1),
494 (false, true) => refs.spec_refs = refs.spec_refs.saturating_sub(1),
495 (false, false) => refs.unspec_refs = refs.unspec_refs.saturating_sub(1),
496 }
497 }
498 }
499}
500
501#[cfg(test)]
502mod tests {
503 use super::*;
504
505 #[test]
506 fn asserts_node_have_either_external_data_or_root_id() {
508 let nodes_with_external_data: [gas_provider::node::GasNode<i32, i32, i32, i32>; 3] = [
509 GasNode::External {
510 id: Default::default(),
511 multiplier: GasMultiplier::ValuePerGas(100),
512 value: Default::default(),
513 lock: Default::default(),
514 system_reserve: Default::default(),
515 refs: Default::default(),
516 consumed: Default::default(),
517 deposit: Default::default(),
518 },
519 GasNode::Cut {
520 id: Default::default(),
521 multiplier: GasMultiplier::ValuePerGas(100),
522 value: Default::default(),
523 lock: Default::default(),
524 },
525 GasNode::Reserved {
526 id: Default::default(),
527 multiplier: GasMultiplier::ValuePerGas(100),
528 value: Default::default(),
529 lock: Default::default(),
530 refs: Default::default(),
531 consumed: Default::default(),
532 },
533 ];
534
535 for node in nodes_with_external_data {
536 assert!(node.external_data().is_some() || node.root_id().is_none());
537 }
538
539 let nodes_with_root_id: [gas_provider::node::GasNode<i32, i32, i32, i32>; 2] = [
540 GasNode::SpecifiedLocal {
541 parent: Default::default(),
542 root: Default::default(),
543 value: Default::default(),
544 lock: Default::default(),
545 system_reserve: Default::default(),
546 refs: Default::default(),
547 consumed: Default::default(),
548 },
549 GasNode::UnspecifiedLocal {
550 parent: Default::default(),
551 root: Default::default(),
552 lock: Default::default(),
553 system_reserve: Default::default(),
554 },
555 ];
556
557 for node in nodes_with_root_id {
558 assert!(node.external_data().is_none() || node.root_id().is_some());
559 }
560 }
561}