rgbp/
utxoset.rs

1// Wallet Library for RGB smart contracts
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Designed in 2019-2025 by Dr Maxim Orlovsky <orlovsky@lnp-bp.org>
6// Written in 2024-2025 by Dr Maxim Orlovsky <orlovsky@lnp-bp.org>
7//
8// Copyright (C) 2019-2024 LNP/BP Standards Association, Switzerland.
9// Copyright (C) 2024-2025 LNP/BP Laboratories,
10//                         Institute for Distributed and Cognitive Systems (InDCS), Switzerland.
11// Copyright (C) 2025 RGB Consortium, Switzerland.
12// Copyright (C) 2019-2025 Dr Maxim Orlovsky.
13// All rights under the above copyrights are reserved.
14//
15// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
16// in compliance with the License. You may obtain a copy of the License at
17//
18//        http://www.apache.org/licenses/LICENSE-2.0
19//
20// Unless required by applicable law or agreed to in writing, software distributed under the License
21// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
22// or implied. See the License for the specific language governing permissions and limitations under
23// the License.
24
25use bpstd::psbt::Utxo;
26use bpstd::{Idx, Keychain, NormalIndex, Outpoint, Sats, Terminal};
27use indexmap::IndexMap;
28
29#[allow(clippy::len_without_is_empty)]
30pub trait UtxoSet {
31    fn len(&self) -> usize;
32    fn has(&self, outpoint: Outpoint) -> bool;
33    fn get(&self, outpoint: Outpoint) -> Option<(Sats, Terminal)>;
34
35    #[cfg(not(feature = "async"))]
36    fn insert(&mut self, outpoint: Outpoint, value: Sats, terminal: Terminal);
37    #[cfg(feature = "async")]
38    async fn insert_async(&mut self, outpoint: Outpoint, value: Sats, terminal: Terminal);
39
40    #[cfg(not(feature = "async"))]
41    fn insert_all(&mut self, utxos: impl IntoIterator<Item = Utxo>);
42    #[cfg(feature = "async")]
43    async fn insert_all_async(&mut self, utxos: impl IntoIterator<Item = Utxo>);
44
45    #[cfg(not(feature = "async"))]
46    fn clear(&mut self);
47    #[cfg(feature = "async")]
48    async fn clear_async(&mut self);
49
50    #[cfg(not(feature = "async"))]
51    fn remove(&mut self, outpoint: Outpoint) -> Option<(Sats, Terminal)>;
52    #[cfg(feature = "async")]
53    async fn remove_async(&mut self, outpoint: Outpoint) -> Option<(Sats, Terminal)>;
54
55    #[cfg(not(feature = "async"))]
56    fn remove_all(&mut self, outpoints: impl IntoIterator<Item = Outpoint>);
57    #[cfg(feature = "async")]
58    async fn remove_all_async(&mut self, outpoints: impl IntoIterator<Item = Outpoint>);
59
60    fn outpoints(&self) -> impl Iterator<Item = Outpoint>;
61
62    fn next_index_noshift(&self, keychain: impl Into<Keychain>) -> NormalIndex;
63
64    #[cfg(not(feature = "async"))]
65    fn next_index(&mut self, keychain: impl Into<Keychain>, shift: bool) -> NormalIndex;
66    #[cfg(feature = "async")]
67    async fn next_index_async(&mut self, keychain: impl Into<Keychain>, shift: bool)
68        -> NormalIndex;
69}
70
71#[derive(Clone, PartialEq, Eq, Debug, Default)]
72#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
73pub struct MemUtxos {
74    set: IndexMap<Outpoint, (Sats, Terminal)>,
75    next_index: IndexMap<Keychain, NormalIndex>,
76}
77
78impl UtxoSet for MemUtxos {
79    #[inline]
80    fn len(&self) -> usize { self.set.len() }
81    #[inline]
82    fn has(&self, outpoint: Outpoint) -> bool { self.set.contains_key(&outpoint) }
83    #[inline]
84    fn get(&self, outpoint: Outpoint) -> Option<(Sats, Terminal)> {
85        self.set.get(&outpoint).copied()
86    }
87
88    #[inline]
89    #[cfg(not(feature = "async"))]
90    fn insert(&mut self, outpoint: Outpoint, value: Sats, terminal: Terminal) {
91        self.set.insert(outpoint, (value, terminal));
92    }
93    #[inline]
94    #[cfg(feature = "async")]
95    async fn insert_async(&mut self, outpoint: Outpoint, value: Sats, terminal: Terminal) {
96        self.set.insert(outpoint, (value, terminal));
97    }
98
99    #[inline]
100    #[cfg(not(feature = "async"))]
101    fn insert_all(&mut self, utxos: impl IntoIterator<Item = Utxo>) {
102        for utxo in utxos {
103            self.set.insert(utxo.outpoint, (utxo.value, utxo.terminal));
104        }
105    }
106    #[inline]
107    #[cfg(feature = "async")]
108    async fn insert_all_async(&mut self, utxos: impl IntoIterator<Item = Utxo>) {
109        for utxo in utxos {
110            self.set.insert(utxo.outpoint, (utxo.value, utxo.terminal));
111        }
112    }
113
114    #[inline]
115    #[cfg(not(feature = "async"))]
116    fn clear(&mut self) { self.set.clear() }
117    #[inline]
118    #[cfg(feature = "async")]
119    async fn clear_async(&mut self) { self.set.clear() }
120
121    #[inline]
122    #[cfg(not(feature = "async"))]
123    fn remove(&mut self, outpoint: Outpoint) -> Option<(Sats, Terminal)> {
124        self.set.shift_remove(&outpoint)
125    }
126    #[inline]
127    #[cfg(feature = "async")]
128    async fn remove_async(&mut self, outpoint: Outpoint) -> Option<(Sats, Terminal)> {
129        self.set.shift_remove(&outpoint)
130    }
131
132    #[inline]
133    #[cfg(not(feature = "async"))]
134    fn remove_all(&mut self, outpoints: impl IntoIterator<Item = Outpoint>) {
135        for outpoint in outpoints {
136            self.set.shift_remove(&outpoint);
137        }
138    }
139    #[inline]
140    #[cfg(feature = "async")]
141    async fn remove_all_async(&mut self, outpoints: impl IntoIterator<Item = Outpoint>) {
142        for outpoint in outpoints {
143            self.set.shift_remove(&outpoint);
144        }
145    }
146
147    #[inline]
148    fn outpoints(&self) -> impl Iterator<Item = Outpoint> { self.set.keys().copied() }
149
150    fn next_index_noshift(&self, keychain: impl Into<Keychain>) -> NormalIndex {
151        self.next_index
152            .get(&keychain.into())
153            .copied()
154            .unwrap_or_default()
155    }
156
157    #[cfg(not(feature = "async"))]
158    fn next_index(&mut self, keychain: impl Into<Keychain>, shift: bool) -> NormalIndex {
159        let index = self.next_index.entry(keychain.into()).or_default();
160        let next = *index;
161        if shift {
162            index.saturating_inc_assign();
163        }
164        next
165    }
166    #[inline]
167    #[cfg(feature = "async")]
168    async fn next_index_async(
169        &mut self,
170        keychain: impl Into<Keychain>,
171        shift: bool,
172    ) -> NormalIndex {
173        let index = self.next_index.entry(keychain.into()).or_default();
174        let next = *index;
175        if shift {
176            index.saturating_inc_assign();
177        }
178        next
179    }
180}