sonicapi/state/
aggregators.rs

1// SONIC: Standard library for formally-verifiable distributed contracts
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Designed in 2019-2025 by Dr Maxim Orlovsky <orlovsky@ubideco.org>
6// Written in 2024-2025 by Dr Maxim Orlovsky <orlovsky@ubideco.org>
7//
8// Copyright (C) 2019-2024 LNP/BP Standards Association, Switzerland.
9// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO),
10//                         Institute for Distributed and Cognitive Systems (InDCS), Switzerland.
11// Copyright (C) 2019-2025 Dr Maxim Orlovsky.
12// All rights under the above copyrights are reserved.
13//
14// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
15// in compliance with the License. You may obtain a copy of the License at
16//
17//        http://www.apache.org/licenses/LICENSE-2.0
18//
19// Unless required by applicable law or agreed to in writing, software distributed under the License
20// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
21// or implied. See the License for the specific language governing permissions and limitations under
22// the License.
23
24use alloc::collections::BTreeMap;
25
26use aluvm::{Lib, LibId, LibSite};
27use amplify::confinement::TinyBlob;
28use indexmap::IndexMap;
29use sonic_callreq::StateName;
30use strict_encoding::StrictDumb;
31use strict_types::value::{EnumTag, StrictNum};
32use strict_types::{SemId, StrictVal, TypeSystem};
33use ultrasonic::CellAddr;
34
35use crate::{StateAtom, LIB_NAME_SONIC};
36
37/// Structure which allows applying aggregators either to a global or a different aggregated
38/// state.
39#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
40#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
41#[strict_type(lib = LIB_NAME_SONIC, tags = custom, dumb = Self::Aggregated(strict_dumb!()))]
42#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
43pub enum StateSelector {
44    #[strict_type(tag = 0)]
45    Global(
46        StateName,
47        /** Flag indicating that if multiple state elements are known, only the first one should
48         * be used. */
49        bool,
50    ),
51    #[strict_type(tag = 1)]
52    Aggregated(StateName),
53}
54
55/// A set of pre-defined top-level state aggregators (see [`crate::Api::aggregators`].
56#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
57#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
58#[strict_type(lib = LIB_NAME_SONIC, tags = custom, dumb = Self::Some(strict_dumb!()))]
59#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
60pub enum Aggregator {
61    /// Produces constant value of `Option::None` type.
62    #[strict_type(tag = 0)]
63    None,
64
65    /// Wrap into an optional value.
66    ///
67    /// If the underlying aggregated state fails, sets the aggregated state to `None`.
68    #[strict_type(tag = 1)]
69    #[cfg_attr(feature = "serde", serde(with = "serde_yaml::with::singleton_map"))]
70    Some(SubAggregator),
71
72    /// Takes the underlying aggregated state and applies nothing on top.
73    ///
74    /// If the underlying aggregator fails, the aggregated state is not produced.
75    #[strict_type(tag = 2)]
76    // https://github.com/dtolnay/serde-yaml/issues/363
77    // We should repeat this if we encounter any other nested enums.
78    #[cfg_attr(feature = "serde", serde(with = "serde_yaml::with::singleton_map"))]
79    Take(SubAggregator),
80
81    /// If the underlying aggregated state fails, returns the other state.
82    ///
83    /// If the other state fails, the aggregated state is not produced.
84    #[strict_type(tag = 3)]
85    Or(
86        #[cfg_attr(feature = "serde", serde(with = "serde_yaml::with::singleton_map"))] SubAggregator,
87        #[cfg_attr(feature = "serde", serde(with = "serde_yaml::with::singleton_map"))] SubAggregator,
88    ),
89
90    /// Execute a custom function on the state.
91    #[strict_type(tag = 0xFF)]
92    AluVM(
93        /// The entry point to the script (virtual machine uses libraries from
94        /// [`crate::Semantics`]).
95        LibSite,
96    ),
97}
98
99impl Aggregator {
100    /// Returns names of the other computed state which this aggregator depends on
101    /// and which needs to be computed before running this aggregator.
102    pub fn depends_on(&self) -> impl Iterator<Item = &StateName> {
103        match self {
104            Self::Some(sub) | Self::Take(sub) => sub.depends_on(),
105            Self::Or(some, other) => {
106                let mut deps = some.depends_on();
107                deps.append(&mut other.depends_on());
108                deps
109            }
110            Self::None | Self::AluVM(_) => vec![],
111        }
112        .into_iter()
113    }
114
115    /// Compute state via applying some aggregator function.
116    ///
117    /// # Returns
118    ///
119    /// Aggregated state value. If the computing fails due to any exception, `None`.
120    pub fn aggregate<'libs>(
121        &self,
122        global: &BTreeMap<StateName, BTreeMap<CellAddr, StateAtom>>,
123        aggregated: &BTreeMap<StateName, StrictVal>,
124        libs: impl IntoIterator<Item = &'libs Lib>,
125        types: &TypeSystem,
126    ) -> Option<StrictVal> {
127        match self {
128            Self::None => Some(StrictVal::none()),
129
130            Self::Take(sub) => sub.aggregate(global, aggregated, types),
131
132            Self::Some(sub) => Some(match sub.aggregate(global, aggregated, types) {
133                Some(val) => StrictVal::some(val),
134                None => StrictVal::none(),
135            }),
136
137            Self::Or(some, other) => some
138                .aggregate(global, aggregated, types)
139                .or_else(|| other.aggregate(global, aggregated, types)),
140
141            Self::AluVM(entry) => {
142                let libs = libs
143                    .into_iter()
144                    .map(|lib| (lib.lib_id(), lib))
145                    .collect::<IndexMap<_, _>>();
146                let mut vm = aluvm::Vm::<aluvm::isa::Instr<LibId>>::new();
147                // For now, we ignore all computations and return `None` anyway.
148                // This leaves a way to add proper VM computing in the future
149                // in a backward-compatible way.
150                let _status = vm.exec(*entry, &(), |id| libs.get(&id));
151                None
152            }
153        }
154    }
155}
156
157/// A set of pre-defined state sub-aggregators (see [`crate::Api::aggregators`].
158#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
159#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
160#[strict_type(lib = LIB_NAME_SONIC, tags = custom, dumb = Self::Neg(strict_dumb!()))]
161#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
162pub enum SubAggregator {
163    /// The aggregated state is generated with a predefined constant strict-encoded value.
164    ///
165    /// To produce a state with a unit value, use `Self::Const(SemId::unit(), none!())`.
166    #[strict_type(tag = 0)]
167    Const(SemId, TinyBlob),
168
169    /// Takes the only element of the global state.
170    ///
171    /// Fails if the state is not defined or has more than one defined element.
172    #[strict_type(tag = 1)]
173    TheOnly(StateName),
174
175    /// Takes some other aggregated and copies it.
176    ///
177    /// Fails if the other aggregated state is not defined.
178    #[strict_type(tag = 2)]
179    Copy(StateName),
180
181    /// Unwraps an optional value.
182    ///
183    /// Fails if the value is `None`, is not defined, multiple, or not an optional.
184    #[strict_type(tag = 3)]
185    Unwrap(StateName),
186
187    /// Takes the first element of the global state.
188    ///
189    /// Fails if the global state is not defined, i.e., has zero elements.
190    ///
191    /// # Nota bene
192    ///
193    /// The global state does not have only a partial ordering (it is a lattice).
194    ///
195    /// It is only in the case when one operation depends on outputs of another
196    /// (via global or owned state) there is a guarantee that the global state
197    /// defined by the second operation will always follow the first one.
198    ///
199    /// It is the responsibility of the codex developer
200    /// to ensure non-ambiguity when this aggregator is used.
201    #[strict_type(tag = 4)]
202    First(StateName),
203
204    /// Takes the nth element of the global state.
205    ///
206    /// Fails if the global state is not defined, i.e., has zero elements,
207    /// or if the nth-element is empty.
208    ///
209    /// # Nota bene
210    ///
211    /// The global state does not have only a partial ordering (it is a lattice).
212    ///
213    /// It is only in the case when one operation depends on outputs of another
214    /// (via global or owned state) there is a guarantee that the global state
215    /// defined by the second operation will always follow the first one.
216    ///
217    /// It is the responsibility of the codex developer
218    /// to ensure non-ambiguity when this aggregator is used.
219    #[strict_type(tag = 5)]
220    Nth(StateName, u16),
221
222    /// Takes the last element of the global state.
223    ///
224    /// Fails if the global state is not defined, i.e., has zero elements.
225    ///
226    /// # Nota bene
227    ///
228    /// The global state does not have only a partial ordering (it is a lattice).
229    ///
230    /// It is only in the case when one operation depends on outputs of another
231    /// (via global or owned state) there is a guarantee that the global state
232    /// defined by the second operation will always follow the first one.
233    ///
234    /// It is the responsibility of the codex developer
235    /// to ensure non-ambiguity when this aggregator is used.
236    #[strict_type(tag = 6)]
237    Last(StateName),
238
239    /// Takes the nth element of the global state, counting from the end of the list.
240    ///
241    /// Fails if the global state is not defined, i.e., has zero elements,
242    /// or if the nth-element is empty.
243    ///
244    /// # Nota bene
245    ///
246    /// The global state does not have only a partial ordering (it is a lattice).
247    ///
248    /// It is only in the case when one operation depends on outputs of another
249    /// (via global or owned state) there is a guarantee that the global state
250    /// defined by the second operation will always follow the first one.
251    ///
252    /// It is the responsibility of the codex developer
253    /// to ensure non-ambiguity when this aggregator is used.
254    #[strict_type(tag = 7)]
255    NthBack(StateName, u16),
256
257    /// Integer-negate state.
258    ///
259    /// Fails if the state is not defined or contains multiple elements.
260    /// Also fails if the state is not an unsigned 64-bit integer or is greater than `i64::MAX`.
261    #[strict_type(tag = 0x10)]
262    Neg(StateSelector),
263
264    /// Sum two states of different types, expecting them to be integers.
265    ///
266    /// Fails if any of the state is not defined or contains multiple elements.
267    /// Also fails if the state is not an unsigned 64-bit integer or there is an overflow.
268    #[strict_type(tag = 0x11)]
269    Add(StateSelector, StateSelector),
270
271    /// Substracts the second state from the first state, expecting both to be integers.
272    ///
273    /// Fails if any of the state is not defined or contains multiple elements.
274    /// Also fails if the state is not an unsigned 64-bit integer or there is an overflow.
275    #[strict_type(tag = 0x12)]
276    Sub(StateSelector, StateSelector),
277
278    /// Product two states of different types, expecting them to be integers.
279    ///
280    /// Fails if any of the state is not defined or contains multiple elements.
281    /// Also fails if the state is not an unsigned 64-bit integer or there is an overflow.
282    #[strict_type(tag = 0x13)]
283    Mul(StateSelector, StateSelector),
284
285    /// Divide the first state on the second state, expecting them to be integers.
286    /// The resulting value is rounded towards zero.
287    ///
288    /// Fails if any of the state is not defined or contains multiple elements.
289    /// Also fails if the state is not an unsigned 64-bit integer, or the second state is zero.
290    #[strict_type(tag = 0x14)]
291    Div(StateSelector, StateSelector),
292
293    /// Modulo-divide the first state on the second state, expecting them to be integers.
294    ///
295    /// Fails if any of the state is not defined or contains multiple elements.
296    /// Also fails if the state is not an unsigned 64-bit integer, or the second state is zero.
297    #[strict_type(tag = 0x15)]
298    Rem(StateSelector, StateSelector),
299
300    /// Exponentiates the first state with the second state, expecting them to be integers.
301    /// The resulting value is rounded towards zero.
302    ///
303    /// Fails if any of the state is not defined or contains multiple elements.
304    /// Also fails if the first state is not an unsigned 64-bit integer,
305    /// the second state is not an unsigned 32-bit integer, or there is an overflow.
306    #[strict_type(tag = 0x16)]
307    Exp(StateSelector, StateSelector),
308
309    /// Count the number of elements of the global state of a certain type.
310    #[strict_type(tag = 0x20)]
311    Count(StateName),
312
313    /// Count the number of unique elements of the global state of a certain type.
314    #[strict_type(tag = 0x21)]
315    CountUnique(StateName),
316
317    /// Convert a verified state under the same state type into an ordered set.
318    ///
319    /// Acts only on a global state; doesn't recognize aggregated state.
320    ///
321    /// If the global state with the name is absent returns an empty set.
322    #[strict_type(tag = 0x22)]
323    SetV(StateName),
324
325    /// Map from a field-based element state to a non-verifiable structured state;
326    /// when the field-based element state repeats, it is ignored and only the initial state is
327    /// kept.
328    ///
329    /// The map is sorted by its values, lexicographically.
330    ///
331    /// Acts only on a global state; doesn't recognize aggregated state.
332    ///
333    /// If the global state with the name is absent returns an empty map.
334    ///
335    /// # Nota bene
336    ///
337    /// The global state does not have only a partial ordering (it is a lattice).
338    ///
339    /// It is only in the case when one operation depends on outputs of another
340    /// (via global or owned state) there is a guarantee that the global state
341    /// defined by the second operation will always follow the first one.
342    ///
343    /// It is the responsibility of the codex developer
344    /// to ensure non-ambiguity when this aggregator is used.
345    #[strict_type(tag = 0x23)]
346    MapV2U(StateName),
347
348    /// Map from a field-based element state to a list of non-verifiable structured state;
349    /// when the field-based element state repeats, the list is extended with the non-verifiable
350    /// state.
351    ///
352    /// The map is ordered according to the global state element order.
353    ///
354    /// Acts only on a global state; doesn't recognize aggregated state.
355    ///
356    /// If the global state with the name is absent returns an empty map.
357    ///
358    /// # Nota bene
359    ///
360    /// The global state does not have only a partial ordering (it is a lattice).
361    ///
362    /// It is only in the case when one operation depends on outputs of another
363    /// (via global or owned state) there is a guarantee that the global state
364    /// defined by the second operation will always follow the first one.
365    ///
366    /// It is the responsibility of the codex developer
367    /// to ensure non-ambiguity when this aggregator is used.
368    #[strict_type(tag = 0x24)]
369    MapV2ListU(StateName),
370
371    /// Map from a field-based element state to a set of non-verifiable structured state;
372    /// when the field-based element state repeats, the set is extended with the non-verifiable
373    /// state.
374    ///
375    /// The map is ordered according to the global state element order.
376    ///
377    /// Acts only on a global state; doesn't recognize aggregated state.
378    ///
379    /// If the global state with the name is absent returns an empty map.
380    ///
381    /// # Nota bene
382    ///
383    /// The global state does not have only a partial ordering (it is a lattice).
384    ///
385    /// It is only in the case when one operation depends on outputs of another
386    /// (via global or owned state) there is a guarantee that the global state
387    /// defined by the second operation will always follow the first one.
388    ///
389    /// It is the responsibility of the codex developer
390    /// to ensure non-ambiguity when this aggregator is used.
391    #[strict_type(tag = 0x25)]
392    MapV2SetU(StateName),
393
394    /// Sums over verifiable part of a global state.
395    ///
396    /// Acts only on a global state; doesn't recognize aggregated state.
397    ///
398    /// Fails if the global state doesn't have any elements, or if there is an overflow,
399    /// or the state type is not an unsigned integer.
400    #[strict_type(tag = 0x30)]
401    SumUnwrap(StateName),
402
403    /// Sums over verifiable part of a global state.
404    ///
405    /// Acts only on a global state; doesn't recognize aggregated state.
406    ///
407    /// Produces zero f the global state doesn't have any elements, or if there is an overflow.
408    ///
409    /// If any of the elements of the global state are not an unsigner integer, treats them as zero.
410    #[strict_type(tag = 0x31)]
411    SumOrDefault(StateName),
412
413    /// Takes a product of the elements of a global state, taking their verifiable part.
414    ///
415    /// Acts only on a global state; doesn't recognize aggregated state.
416    ///
417    /// Fails if the global state doesn't have any elements, or if there is an overflow,
418    /// or the state type is not an unsigned integer.
419    #[strict_type(tag = 0x32)]
420    ProdUnwrap(StateName),
421
422    /// Takes a product of the elements of a global state, taking their verifiable part.
423    ///
424    /// Acts only on a global state; doesn't recognize aggregated state.
425    ///
426    /// Produces zero f the global state doesn't have any elements, or if there is an overflow.
427    ///
428    /// If any of the elements of the global state are not an unsigner integer, treats them as one.
429    #[strict_type(tag = 0x33)]
430    ProdOrDefault(StateName),
431}
432
433impl SubAggregator {
434    /// Returns names of the other computed state which this aggregator depends on
435    /// and which needs to be computed before running this aggregator.
436    pub fn depends_on(&self) -> Vec<&StateName> {
437        match self {
438            Self::Neg(StateSelector::Aggregated(state))
439            | Self::Add(StateSelector::Global(_, _), StateSelector::Aggregated(state))
440            | Self::Sub(StateSelector::Global(_, _), StateSelector::Aggregated(state))
441            | Self::Mul(StateSelector::Global(_, _), StateSelector::Aggregated(state))
442            | Self::Div(StateSelector::Global(_, _), StateSelector::Aggregated(state))
443            | Self::Rem(StateSelector::Global(_, _), StateSelector::Aggregated(state))
444            | Self::Exp(StateSelector::Global(_, _), StateSelector::Aggregated(state))
445            | Self::Add(StateSelector::Aggregated(state), StateSelector::Global(_, _))
446            | Self::Sub(StateSelector::Aggregated(state), StateSelector::Global(_, _))
447            | Self::Mul(StateSelector::Aggregated(state), StateSelector::Global(_, _))
448            | Self::Div(StateSelector::Aggregated(state), StateSelector::Global(_, _))
449            | Self::Rem(StateSelector::Aggregated(state), StateSelector::Global(_, _))
450            | Self::Exp(StateSelector::Aggregated(state), StateSelector::Global(_, _)) => vec![state],
451
452            Self::Add(StateSelector::Aggregated(a), StateSelector::Aggregated(b))
453            | Self::Sub(StateSelector::Aggregated(a), StateSelector::Aggregated(b))
454            | Self::Mul(StateSelector::Aggregated(a), StateSelector::Aggregated(b))
455            | Self::Div(StateSelector::Aggregated(a), StateSelector::Aggregated(b))
456            | Self::Rem(StateSelector::Aggregated(a), StateSelector::Aggregated(b))
457            | Self::Exp(StateSelector::Aggregated(a), StateSelector::Aggregated(b)) => vec![a, b],
458
459            Self::Const(_, _)
460            | Self::TheOnly(_)
461            | Self::Count(_)
462            | Self::CountUnique(_)
463            | Self::Copy(_)
464            | Self::Unwrap(_)
465            | Self::First(_)
466            | Self::Nth(_, _)
467            | Self::Last(_)
468            | Self::NthBack(_, _)
469            | Self::Neg(_)
470            | Self::Add(_, _)
471            | Self::Sub(_, _)
472            | Self::Mul(_, _)
473            | Self::Div(_, _)
474            | Self::Rem(_, _)
475            | Self::Exp(_, _)
476            | Self::SetV(_)
477            | Self::MapV2U(_)
478            | Self::MapV2ListU(_)
479            | Self::MapV2SetU(_)
480            | Self::SumUnwrap(_)
481            | Self::SumOrDefault(_)
482            | Self::ProdUnwrap(_)
483            | Self::ProdOrDefault(_) => vec![],
484        }
485    }
486
487    /// Compute state via applying some aggregator function.
488    ///
489    /// # Returns
490    ///
491    /// Aggregated state value.
492    /// If the computing fails due to any exception, `None`.
493    pub fn aggregate(
494        &self,
495        global: &BTreeMap<StateName, BTreeMap<CellAddr, StateAtom>>,
496        aggregated: &BTreeMap<StateName, StrictVal>,
497        types: &TypeSystem,
498    ) -> Option<StrictVal> {
499        let get_u64 = |sel: &StateSelector| -> Option<u64> {
500            let state = match sel {
501                StateSelector::Global(name, first) => {
502                    let map = global.get(name)?;
503                    if map.len() != 1 && !*first {
504                        return None;
505                    }
506                    let (_, atom) = map.first_key_value()?;
507                    &atom.verified
508                }
509                StateSelector::Aggregated(name) => aggregated.get(name)?,
510            };
511            match state {
512                StrictVal::Number(StrictNum::Uint(val)) => Some(*val),
513                _ => None,
514            }
515        };
516
517        match self {
518            Self::Const(sem_id, val) => deserialize(*sem_id, val, types),
519
520            Self::TheOnly(name) => {
521                let state = global.get(name)?;
522                if state.len() != 1 {
523                    return None;
524                }
525                Some(state.first_key_value()?.1.verified.clone())
526            }
527
528            Self::Copy(name) => aggregated.get(name).cloned(),
529
530            Self::Unwrap(name) => {
531                let state = global.get(name)?;
532                if state.len() != 1 {
533                    return None;
534                }
535                let (_, atom) = state.first_key_value()?;
536                let StrictVal::Union(tag, sv) = &atom.verified else {
537                    return None;
538                };
539                Some(match tag {
540                    EnumTag::Name(name) if name.as_str() == "some" => sv.as_ref().clone(),
541                    EnumTag::Ord(1) => sv.as_ref().clone(),
542                    _ => return None,
543                })
544            }
545
546            Self::First(name) => {
547                let state = global.get(name)?;
548                Some(state.first_key_value()?.1.verified.clone())
549            }
550
551            Self::Nth(name, pos) => {
552                let state = global.get(name)?;
553                Some(state.iter().nth(*pos as usize)?.1.verified.clone())
554            }
555
556            Self::Last(name) => {
557                let state = global.get(name)?;
558                Some(state.last_key_value()?.1.verified.clone())
559            }
560
561            Self::NthBack(name, pos) => {
562                let state = global.get(name)?;
563                Some(state.iter().nth_back(*pos as usize)?.1.verified.clone())
564            }
565
566            Self::Neg(name) => {
567                let val = get_u64(name)?;
568                let neg = (val as i64).checked_neg()?;
569                Some(svnum!(neg))
570            }
571            Self::Add(a, b) => {
572                let a = get_u64(a)?;
573                let b = get_u64(b)?;
574                let sum = a.checked_add(b)?;
575                Some(svnum!(sum))
576            }
577            Self::Sub(a, b) => {
578                let a = get_u64(a)?;
579                let b = get_u64(b)?;
580                let sub = a.checked_sub(b)?;
581                Some(svnum!(sub))
582            }
583            Self::Mul(a, b) => {
584                let a = get_u64(a)?;
585                let b = get_u64(b)?;
586                let sub = a.checked_mul(b)?;
587                Some(svnum!(sub))
588            }
589            Self::Div(a, b) => {
590                let a = get_u64(a)?;
591                let b = get_u64(b)?;
592                let sub = a.checked_div(b)?;
593                Some(svnum!(sub))
594            }
595            Self::Rem(a, b) => {
596                let a = get_u64(a)?;
597                let b = get_u64(b)?;
598                let sub = a.checked_rem(b)?;
599                Some(svnum!(sub))
600            }
601            Self::Exp(a, b) => {
602                let a = get_u64(a)?;
603                let b = get_u64(b)?;
604                let sub = a.checked_pow(b.try_into().ok()?)?;
605                Some(svnum!(sub))
606            }
607
608            Self::Count(name) => {
609                let count = global
610                    .get(name)
611                    .into_iter()
612                    .flat_map(BTreeMap::values)
613                    .count();
614                Some(svnum!(count as u64))
615            }
616
617            Self::CountUnique(name) => {
618                let mut unique = Vec::new();
619                for item in global.get(name)?.values() {
620                    if !unique.contains(&item) {
621                        unique.push(item);
622                    }
623                }
624                Some(svnum!(unique.len() as u64))
625            }
626
627            Self::SetV(name) => {
628                let mut set = Vec::new();
629                for state in global.get(name).into_iter().flat_map(BTreeMap::values) {
630                    if !set.contains(&state.verified) {
631                        set.push(state.verified.clone());
632                    }
633                }
634                Some(StrictVal::Set(set))
635            }
636
637            Self::MapV2U(name) => {
638                let mut map = Vec::new();
639                for atom in global.get(name)?.values() {
640                    let Some(val) = &atom.unverified else { continue };
641                    if map.iter().any(|(key, _)| &atom.verified == key) {
642                        continue;
643                    }
644                    map.push((atom.verified.clone(), val.clone()));
645                }
646                Some(StrictVal::Map(map))
647            }
648
649            Self::MapV2ListU(name) => {
650                let mut map = Vec::<(StrictVal, StrictVal)>::new();
651                for atom in global.get(name)?.values() {
652                    let Some(val) = &atom.unverified else { continue };
653                    if let Some((_key, list)) = map.iter_mut().find(|(key, _)| &atom.verified == key) {
654                        let StrictVal::List(list) = list else {
655                            unreachable!();
656                        };
657                        list.push(val.clone());
658                    } else {
659                        map.push((atom.verified.clone(), StrictVal::List(vec![val.clone()])));
660                    }
661                }
662                Some(StrictVal::Map(map))
663            }
664
665            Self::MapV2SetU(name) => {
666                let mut map = Vec::<(StrictVal, StrictVal)>::new();
667                for atom in global.get(name)?.values() {
668                    let Some(val) = &atom.unverified else { continue };
669                    if let Some((_key, list)) = map.iter_mut().find(|(key, _)| &atom.verified == key) {
670                        let StrictVal::Set(list) = list else {
671                            unreachable!();
672                        };
673                        if !list.contains(val) {
674                            list.push(val.clone());
675                        }
676                    } else {
677                        map.push((atom.verified.clone(), StrictVal::Set(vec![val.clone()])));
678                    }
679                }
680                Some(StrictVal::Map(map))
681            }
682
683            Self::SumUnwrap(name) => {
684                let sum = global
685                    .get(name)
686                    .into_iter()
687                    .flat_map(BTreeMap::values)
688                    .try_fold(0u64, |sum, val| match &val.verified {
689                        StrictVal::Number(StrictNum::Uint(val)) => sum.checked_add(*val),
690                        _ => None,
691                    })?;
692                Some(svnum!(sum))
693            }
694
695            Self::SumOrDefault(name) => {
696                let sum = global
697                    .get(name)
698                    .into_iter()
699                    .flat_map(BTreeMap::values)
700                    .try_fold(0u64, |sum, val| match &val.verified {
701                        StrictVal::Number(StrictNum::Uint(val)) => sum.checked_add(*val),
702                        _ => Some(sum),
703                    })?;
704                Some(svnum!(sum))
705            }
706
707            Self::ProdUnwrap(name) => {
708                let sum = global
709                    .get(name)
710                    .into_iter()
711                    .flat_map(BTreeMap::values)
712                    .try_fold(1u64, |prod, val| match &val.verified {
713                        StrictVal::Number(StrictNum::Uint(val)) => prod.checked_mul(*val),
714                        _ => None,
715                    })?;
716                Some(svnum!(sum))
717            }
718
719            Self::ProdOrDefault(name) => {
720                let sum = global
721                    .get(name)
722                    .into_iter()
723                    .flat_map(BTreeMap::values)
724                    .try_fold(1u64, |prod, val| match &val.verified {
725                        StrictVal::Number(StrictNum::Uint(val)) => prod.checked_mul(*val),
726                        _ => Some(prod),
727                    })?;
728                Some(svnum!(sum))
729            }
730        }
731    }
732}
733
734fn deserialize(sem_id: SemId, val: &TinyBlob, types: &TypeSystem) -> Option<StrictVal> {
735    let ty = types
736        .strict_deserialize_type(sem_id, val.as_slice())
737        .unwrap();
738    Some(ty.unbox())
739}
740
741#[cfg(test)]
742mod test {
743    #![cfg_attr(coverage_nightly, coverage(off))]
744
745    use aluvm::aluasm;
746    use strict_types::stl::std_stl;
747    use strict_types::SystemBuilder;
748
749    use super::*;
750
751    fn addr(no: u16) -> CellAddr { CellAddr::new(strict_dumb!(), no) }
752    fn state() -> BTreeMap<StateName, BTreeMap<CellAddr, StateAtom>> {
753        bmap! {
754            vname!("pairs") => bmap! {
755                addr(0) => StateAtom::with(5u64, "state 1"),
756                addr(1) => StateAtom::with(1u64, "state 2"),
757                addr(2) => StateAtom::with(2u64, "state 3"),
758                addr(3) => StateAtom::with(3u64, "state 4"),
759                addr(4) => StateAtom::with(4u64, "state 5"),
760                addr(5) => StateAtom::with(5u64, "state 6"),
761            },
762            vname!("verified") => bmap! {
763                addr(0) => StateAtom::new_verified(5u64),
764                addr(1) => StateAtom::new_verified(1u64),
765                addr(2) => StateAtom::new_verified(2u64),
766                addr(3) => StateAtom::new_verified(3u64),
767                addr(4) => StateAtom::new_verified(4u64),
768                addr(5) => StateAtom::new_verified(5u64),
769            },
770            vname!("unverified") => bmap! {
771                addr(0) => StateAtom::new_unverified("state 1"),
772                addr(1) => StateAtom::new_unverified("state 2"),
773                addr(2) => StateAtom::new_unverified("state 3"),
774                addr(3) => StateAtom::new_unverified("state 4"),
775                addr(4) => StateAtom::new_unverified("state 5"),
776                addr(5) => StateAtom::new_unverified("state 6"),
777            },
778        }
779    }
780    fn types() -> TypeSystem {
781        let sys = SystemBuilder::new()
782            .import(std_stl())
783            .unwrap()
784            .finalize()
785            .unwrap();
786        let types = std_stl()
787            .types
788            .into_iter()
789            .map(|(tn, ty)| ty.sem_id_named(&tn));
790        sys.as_types().extract(types).unwrap()
791    }
792    fn success_lib() -> Lib {
793        let code = aluasm! { ret; };
794        Lib::assemble(&code).unwrap()
795    }
796    fn call(aggregator: &Aggregator) -> StrictVal {
797        aggregator
798            .aggregate(&state(), &none!(), &[success_lib()], &types())
799            .unwrap()
800    }
801    fn call2(aggregator: &Aggregator) -> StrictVal {
802        let aggregated = bmap! {
803            vname!("zero") => svnum!(0u64),
804            vname!("two") => svnum!(2u64),
805            vname!("three") => svnum!(3u64),
806            vname!("str") => svstr!("Hi"),
807        };
808        aggregator
809            .aggregate(&state(), &aggregated, &[success_lib()], &types())
810            .unwrap()
811    }
812
813    #[test]
814    fn none() {
815        let agg = Aggregator::None;
816        assert_eq!(call(&agg), svnone!());
817        assert_eq!(agg.depends_on().count(), 0);
818    }
819
820    #[test]
821    fn some() {
822        let agg = Aggregator::Some(SubAggregator::Count(vname!("verified")));
823        assert_eq!(call(&agg), svsome!(6u64));
824        assert_eq!(agg.depends_on().count(), 0);
825    }
826
827    #[test]
828    fn or() {
829        let agg =
830            Aggregator::Or(SubAggregator::Unwrap(vname!("nonExisting")), SubAggregator::Count(vname!("verified")));
831        assert_eq!(call(&agg), svnum!(6u64));
832        assert_eq!(agg.depends_on().count(), 0);
833    }
834
835    #[test]
836    #[should_panic]
837    fn non_existing() { call(&Aggregator::Take(SubAggregator::Unwrap(vname!("nonExisting")))); }
838
839    #[test]
840    fn sum() {
841        #![allow(clippy::identity_op)]
842        let agg = Aggregator::Take(SubAggregator::SumUnwrap(vname!("verified")));
843        assert_eq!(call(&agg), svnum!(5u64 + 1 + 2 + 3 + 4 + 5));
844        assert_eq!(agg.depends_on().count(), 0);
845
846        let agg = Aggregator::Take(SubAggregator::SumOrDefault(vname!("verified")));
847        assert_eq!(call(&agg), svnum!(5u64 + 1 + 2 + 3 + 4 + 5));
848        assert_eq!(agg.depends_on().count(), 0);
849
850        let agg = Aggregator::Take(SubAggregator::SumOrDefault(vname!("unverified")));
851        assert_eq!(call(&agg), svnum!(0u64));
852        assert_eq!(agg.depends_on().count(), 0);
853    }
854
855    #[test]
856    fn prod() {
857        #![allow(clippy::identity_op)]
858        let agg = Aggregator::Take(SubAggregator::ProdUnwrap(vname!("verified")));
859        assert_eq!(call(&agg), svnum!(5u64 * 1 * 2 * 3 * 4 * 5));
860        assert_eq!(agg.depends_on().count(), 0);
861
862        let agg = Aggregator::Take(SubAggregator::ProdOrDefault(vname!("verified")));
863        assert_eq!(call(&agg), svnum!(5u64 * 1 * 2 * 3 * 4 * 5));
864        assert_eq!(agg.depends_on().count(), 0);
865
866        let agg = Aggregator::Take(SubAggregator::ProdOrDefault(vname!("unverified")));
867        assert_eq!(call(&agg), svnum!(1u64));
868        assert_eq!(agg.depends_on().count(), 0);
869    }
870
871    #[test]
872    fn add() {
873        let agg = Aggregator::Take(SubAggregator::Add(
874            StateSelector::Aggregated(vname!("two")),
875            StateSelector::Aggregated(vname!("three")),
876        ));
877        assert_eq!(agg.depends_on().collect::<Vec<_>>(), vec![&vname!("two"), &vname!("three")]);
878        assert_eq!(call2(&agg), svnum!(5u64));
879    }
880
881    #[test]
882    fn meg() {
883        let agg = Aggregator::Take(SubAggregator::Neg(StateSelector::Aggregated(vname!("two"))));
884        assert_eq!(agg.depends_on().collect::<Vec<_>>(), vec![&vname!("two")]);
885        assert_eq!(call2(&agg), svnum!(-2i64));
886    }
887
888    #[test]
889    fn sub() {
890        let agg = Aggregator::Take(SubAggregator::Sub(
891            StateSelector::Aggregated(vname!("three")),
892            StateSelector::Aggregated(vname!("two")),
893        ));
894        assert_eq!(agg.depends_on().collect::<Vec<_>>(), vec![&vname!("three"), &vname!("two")]);
895        assert_eq!(call2(&agg), svnum!(1u64));
896    }
897
898    #[test]
899    #[should_panic]
900    fn sub_overflow() {
901        let agg = Aggregator::Take(SubAggregator::Sub(
902            StateSelector::Aggregated(vname!("two")),
903            StateSelector::Aggregated(vname!("three")),
904        ));
905        call2(&agg);
906    }
907
908    #[test]
909    fn mul() {
910        let agg = Aggregator::Take(SubAggregator::Mul(
911            StateSelector::Aggregated(vname!("two")),
912            StateSelector::Aggregated(vname!("three")),
913        ));
914        assert_eq!(agg.depends_on().collect::<Vec<_>>(), vec![&vname!("two"), &vname!("three")]);
915        assert_eq!(call2(&agg), svnum!(6u64));
916    }
917
918    #[test]
919    fn div() {
920        let agg = Aggregator::Take(SubAggregator::Div(
921            StateSelector::Aggregated(vname!("two")),
922            StateSelector::Aggregated(vname!("three")),
923        ));
924        assert_eq!(agg.depends_on().collect::<Vec<_>>(), vec![&vname!("two"), &vname!("three")]);
925        assert_eq!(call2(&agg), svnum!(0u64));
926    }
927
928    #[test]
929    #[should_panic]
930    fn div_zero() {
931        let agg = Aggregator::Take(SubAggregator::Div(
932            StateSelector::Aggregated(vname!("two")),
933            StateSelector::Aggregated(vname!("zero")),
934        ));
935        call2(&agg);
936    }
937
938    #[test]
939    fn rem() {
940        let agg = Aggregator::Take(SubAggregator::Rem(
941            StateSelector::Aggregated(vname!("two")),
942            StateSelector::Aggregated(vname!("three")),
943        ));
944        assert_eq!(agg.depends_on().collect::<Vec<_>>(), vec![&vname!("two"), &vname!("three")]);
945        assert_eq!(call2(&agg), svnum!(2u64));
946    }
947
948    #[test]
949    fn exp() {
950        let agg = Aggregator::Take(SubAggregator::Exp(
951            StateSelector::Aggregated(vname!("two")),
952            StateSelector::Aggregated(vname!("three")),
953        ));
954        assert_eq!(agg.depends_on().collect::<Vec<_>>(), vec![&vname!("two"), &vname!("three")]);
955        assert_eq!(call2(&agg), svnum!(8u64));
956    }
957
958    #[test]
959    #[should_panic]
960    fn math_sum_fail() { call(&Aggregator::Take(SubAggregator::SumUnwrap(vname!("unverified")))); }
961
962    #[test]
963    #[should_panic]
964    fn math_prod_fail() { call(&Aggregator::Take(SubAggregator::ProdUnwrap(vname!("unverified")))); }
965
966    fn independent(agg: Aggregator, val: StrictVal) {
967        assert_eq!(call(&agg), val);
968        assert_eq!(agg.depends_on().count(), 0);
969    }
970
971    #[test]
972    fn verified_readers() {
973        independent(Aggregator::Take(SubAggregator::First(vname!("verified"))), svnum!(5u64));
974        independent(Aggregator::Take(SubAggregator::Nth(vname!("verified"), 0)), svnum!(5u64));
975        independent(Aggregator::Take(SubAggregator::Nth(vname!("verified"), 1)), svnum!(1u64));
976        independent(Aggregator::Take(SubAggregator::Last(vname!("verified"))), svnum!(5u64));
977        independent(Aggregator::Take(SubAggregator::NthBack(vname!("verified"), 0)), svnum!(5u64));
978        independent(Aggregator::Take(SubAggregator::NthBack(vname!("verified"), 1)), svnum!(4u64));
979        independent(Aggregator::Take(SubAggregator::Count(vname!("verified"))), svnum!(6u64));
980        independent(Aggregator::Take(SubAggregator::CountUnique(vname!("verified"))), svnum!(5u64));
981        independent(Aggregator::Take(SubAggregator::SetV(vname!("verified"))), svset!([5u64, 1u64, 2u64, 3u64, 4u64]));
982        independent(Aggregator::Take(SubAggregator::MapV2U(vname!("verified"))), StrictVal::Map(none!()));
983        independent(Aggregator::Take(SubAggregator::MapV2ListU(vname!("verified"))), StrictVal::Map(none!()));
984        independent(Aggregator::Take(SubAggregator::MapV2SetU(vname!("verified"))), StrictVal::Map(none!()));
985    }
986
987    #[test]
988    fn unverified_readers() {
989        independent(Aggregator::Take(SubAggregator::Count(vname!("verified"))), svnum!(6u64));
990        independent(Aggregator::Take(SubAggregator::SetV(vname!("unverified"))), svset!([()]));
991        independent(
992            Aggregator::Take(SubAggregator::MapV2U(vname!("unverified"))),
993            StrictVal::Map(vec![(StrictVal::Unit, svstr!("state 1"))]),
994        );
995        independent(
996            Aggregator::Take(SubAggregator::MapV2ListU(vname!("unverified"))),
997            StrictVal::Map(vec![(StrictVal::Unit, svlist![[
998                svstr!("state 1"),
999                svstr!("state 2"),
1000                svstr!("state 3"),
1001                svstr!("state 4"),
1002                svstr!("state 5"),
1003                svstr!("state 6"),
1004            ]])]),
1005        );
1006        independent(
1007            Aggregator::Take(SubAggregator::MapV2SetU(vname!("unverified"))),
1008            StrictVal::Map(vec![(StrictVal::Unit, svset![[
1009                svstr!("state 1"),
1010                svstr!("state 2"),
1011                svstr!("state 3"),
1012                svstr!("state 4"),
1013                svstr!("state 5"),
1014                svstr!("state 6"),
1015            ]])]),
1016        );
1017    }
1018
1019    #[test]
1020    #[should_panic]
1021    fn unverified_sum() { call(&Aggregator::Take(SubAggregator::SumUnwrap(vname!("unverified")))); }
1022
1023    #[test]
1024    fn unverified_sum_default() {
1025        independent(Aggregator::Take(SubAggregator::SumOrDefault(vname!("unverified"))), svnum!(0u64));
1026    }
1027
1028    #[test]
1029    fn pair_readers() {
1030        independent(Aggregator::Take(SubAggregator::Count(vname!("verified"))), svnum!(6u64));
1031        independent(Aggregator::Take(SubAggregator::SumUnwrap(vname!("pairs"))), svnum!(5u64 + 1 + 2 + 3 + 4 + 5));
1032        independent(Aggregator::Take(SubAggregator::SetV(vname!("pairs"))), svset!([5u64, 1u64, 2u64, 3u64, 4u64]));
1033        independent(
1034            Aggregator::Take(SubAggregator::MapV2U(vname!("pairs"))),
1035            StrictVal::Map(vec![
1036                (svnum!(5u64), svstr!("state 1")),
1037                (svnum!(1u64), svstr!("state 2")),
1038                (svnum!(2u64), svstr!("state 3")),
1039                (svnum!(3u64), svstr!("state 4")),
1040                (svnum!(4u64), svstr!("state 5")),
1041            ]),
1042        );
1043        independent(
1044            Aggregator::Take(SubAggregator::MapV2ListU(vname!("pairs"))),
1045            StrictVal::Map(vec![
1046                (svnum!(5u64), svlist![[svstr!("state 1"), svstr!("state 6")]]),
1047                (svnum!(1u64), svlist![[svstr!("state 2")]]),
1048                (svnum!(2u64), svlist![[svstr!("state 3")]]),
1049                (svnum!(3u64), svlist![[svstr!("state 4")]]),
1050                (svnum!(4u64), svlist![[svstr!("state 5")]]),
1051            ]),
1052        );
1053    }
1054
1055    #[test]
1056    #[should_panic]
1057    // For now, the fail here indicates forward compatibility with when we allow AluVM
1058    fn aluvm() { call(&Aggregator::AluVM(LibSite::new(success_lib().lib_id(), 0))); }
1059}