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