1use std::collections::{BTreeMap, BTreeSet};
23use std::convert::Infallible;
24
25use amplify::confinement;
26use amplify::confinement::{Confined, LargeOrdMap, SmallOrdMap, TinyOrdMap, TinyOrdSet};
27use bp::dbc::anchor::MergeError;
28use commit_verify::mpc;
29use commit_verify::mpc::MerkleBlock;
30use rgb::{
31 Anchor, AnchorId, AnchoredBundle, BundleId, ContractId, Extension, Genesis, OpId, Operation,
32 SchemaId, TransitionBundle,
33};
34use strict_encoding::TypeName;
35
36use crate::accessors::{MergeReveal, MergeRevealError};
37use crate::containers::{Cert, Consignment, ContentId, ContentSigs};
38use crate::interface::{rgb20, ContractSuppl, Iface, IfaceId, IfacePair, SchemaIfaces};
39use crate::persistence::{InventoryError, Stash, StashError, StashInconsistency};
40use crate::LIB_NAME_RGB_STD;
41
42#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
43#[display(inner)]
44pub enum ConsumeError {
45 #[from]
46 Confinement(confinement::Error),
47
48 #[from]
49 Anchor(mpc::InvalidProof),
50
51 #[from]
52 Merge(MergeError),
53
54 #[from]
55 MergeReveal(MergeRevealError),
56}
57
58impl From<Infallible> for InventoryError<Infallible> {
59 fn from(_: Infallible) -> Self { unreachable!() }
60}
61
62#[derive(Clone, Debug)]
64#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
65#[strict_type(lib = LIB_NAME_RGB_STD, dumb = Hoard::preset())]
66pub struct Hoard {
67 pub(super) schemata: TinyOrdMap<SchemaId, SchemaIfaces>,
68 pub(super) ifaces: TinyOrdMap<IfaceId, Iface>,
69 pub(super) geneses: TinyOrdMap<ContractId, Genesis>,
70 pub(super) suppl: TinyOrdMap<ContractId, TinyOrdSet<ContractSuppl>>,
71 pub(super) bundles: LargeOrdMap<BundleId, TransitionBundle>,
72 pub(super) extensions: LargeOrdMap<OpId, Extension>,
73 pub(super) anchors: LargeOrdMap<AnchorId, Anchor<mpc::MerkleBlock>>,
74 pub(super) sigs: SmallOrdMap<ContentId, ContentSigs>,
75}
76
77impl Hoard {
78 pub fn preset() -> Self {
79 let rgb20 = rgb20();
80 let rgb20_id = rgb20.iface_id();
81 Hoard {
82 schemata: none!(),
83 ifaces: tiny_bmap! {
84 rgb20_id => rgb20,
85 },
86 geneses: none!(),
87 suppl: none!(),
88 bundles: none!(),
89 extensions: none!(),
90 anchors: none!(),
91 sigs: none!(),
92 }
93 }
94
95 pub(super) fn import_sigs_internal<I>(
96 &mut self,
97 content_id: ContentId,
98 sigs: I,
99 ) -> Result<(), confinement::Error>
100 where
101 I: IntoIterator<Item = Cert>,
102 I::IntoIter: ExactSizeIterator<Item = Cert>,
103 {
104 let sigs = sigs.into_iter();
105 if sigs.len() > 0 {
106 if let Some(prev_sigs) = self.sigs.get_mut(&content_id) {
107 prev_sigs.extend(sigs)?;
108 } else {
109 let sigs = Confined::try_from_iter(sigs)?;
110 self.sigs.insert(content_id, ContentSigs::from(sigs)).ok();
111 }
112 }
113 Ok(())
114 }
115
116 pub fn consume_consignment<const TYPE: bool>(
118 &mut self,
119 consignment: Consignment<TYPE>,
120 ) -> Result<(), ConsumeError> {
121 let contract_id = consignment.contract_id();
122 let schema_id = consignment.schema_id();
123
124 let iimpls = match self.schemata.get_mut(&schema_id) {
125 Some(si) => &mut si.iimpls,
126 None => {
127 self.schemata
128 .insert(schema_id, SchemaIfaces::new(consignment.schema))?;
129 &mut self
130 .schemata
131 .get_mut(&schema_id)
132 .expect("just inserted")
133 .iimpls
134 }
135 };
136
137 for (iface_id, IfacePair { iface, iimpl }) in consignment.ifaces {
138 if !self.ifaces.contains_key(&iface_id) {
139 self.ifaces.insert(iface_id, iface)?;
140 };
141 if !iimpls.contains_key(&iface_id) {
143 iimpls.insert(iface_id, iimpl)?;
144 };
145 }
146
147 match self.suppl.get_mut(&contract_id) {
149 Some(entry) => {
150 entry.extend(consignment.supplements).ok();
151 }
152 None => {
153 self.suppl.insert(contract_id, consignment.supplements).ok();
154 }
155 }
156
157 match self.geneses.get_mut(&contract_id) {
158 Some(genesis) => *genesis = genesis.clone().merge_reveal(consignment.genesis)?,
159 None => {
160 self.geneses.insert(contract_id, consignment.genesis)?;
161 }
162 }
163
164 for extension in consignment.extensions {
165 let opid = extension.id();
166 match self.extensions.get_mut(&opid) {
167 Some(e) => *e = e.clone().merge_reveal(extension)?,
168 None => {
169 self.extensions.insert(opid, extension)?;
170 }
171 }
172 }
173
174 for AnchoredBundle { anchor, bundle } in consignment.bundles {
175 let bundle_id = bundle.bundle_id();
176 let anchor = anchor.into_merkle_block(contract_id, bundle_id.into())?;
177 self.consume_anchor(anchor)?;
178 self.consume_bundle(bundle)?;
179 }
180
181 for (content_id, sigs) in consignment.signatures {
182 self.import_sigs_internal(content_id, sigs).ok();
184 }
185
186 Ok(())
187 }
188
189 pub fn consume_bundle(&mut self, bundle: TransitionBundle) -> Result<(), ConsumeError> {
191 let bundle_id = bundle.bundle_id();
192 match self.bundles.get_mut(&bundle_id) {
193 Some(b) => *b = b.clone().merge_reveal(bundle)?,
194 None => {
195 self.bundles.insert(bundle_id, bundle)?;
196 }
197 }
198 Ok(())
199 }
200
201 pub fn consume_anchor(&mut self, anchor: Anchor<MerkleBlock>) -> Result<(), ConsumeError> {
203 let anchor_id = anchor.anchor_id();
204 match self.anchors.get_mut(&anchor_id) {
205 Some(a) => *a = a.clone().merge_reveal(anchor)?,
206 None => {
207 self.anchors.insert(anchor_id, anchor)?;
208 }
209 }
210 Ok(())
211 }
212}
213
214impl Stash for Hoard {
215 type Error = Infallible;
217
218 fn ifaces(&self) -> Result<BTreeMap<IfaceId, TypeName>, Self::Error> {
219 Ok(self
220 .ifaces
221 .iter()
222 .map(|(id, iface)| (*id, iface.name.clone()))
223 .collect())
224 }
225
226 fn iface_by_name(&self, name: &TypeName) -> Result<&Iface, StashError<Self::Error>> {
227 self.ifaces
228 .values()
229 .find(|iface| &iface.name == name)
230 .ok_or_else(|| StashInconsistency::IfaceNameAbsent(name.clone()).into())
231 }
232 fn iface_by_id(&self, id: IfaceId) -> Result<&Iface, StashError<Self::Error>> {
233 self.ifaces
234 .get(&id)
235 .ok_or_else(|| StashInconsistency::IfaceAbsent(id).into())
236 }
237
238 fn schema_ids(&self) -> Result<BTreeSet<SchemaId>, Self::Error> {
239 Ok(self.schemata.keys().copied().collect())
240 }
241
242 fn schema(&self, schema_id: SchemaId) -> Result<&SchemaIfaces, StashError<Self::Error>> {
243 self.schemata
244 .get(&schema_id)
245 .ok_or_else(|| StashInconsistency::SchemaAbsent(schema_id).into())
246 }
247
248 fn contract_ids_by_iface(&self, name: &TypeName) -> Result<BTreeSet<ContractId>, Self::Error> {
249 let iface = self.iface_by_name(name).unwrap();
250 let iface_id = iface.iface_id();
251 let schemata = self
252 .schemata
253 .iter()
254 .filter(|(_, iface)| iface.iimpls.contains_key(&iface_id))
255 .map(|(schema_id, _)| schema_id)
256 .collect::<BTreeSet<_>>();
257 Ok(self
258 .geneses
259 .iter()
260 .filter(|(_, genesis)| schemata.contains(&genesis.schema_id))
261 .map(|(contract_id, _)| contract_id)
262 .copied()
263 .collect())
264 }
265
266 fn contract_ids(&self) -> Result<BTreeSet<ContractId>, Self::Error> {
267 Ok(self.geneses.keys().copied().collect())
268 }
269
270 fn contract_suppl(&self, contract_id: ContractId) -> Option<&TinyOrdSet<ContractSuppl>> {
271 self.suppl.get(&contract_id)
272 }
273
274 fn genesis(&self, contract_id: ContractId) -> Result<&Genesis, StashError<Self::Error>> {
275 self.geneses
276 .get(&contract_id)
277 .ok_or(StashInconsistency::ContractAbsent(contract_id).into())
278 }
279
280 fn bundle_ids(&self) -> Result<BTreeSet<BundleId>, Self::Error> {
281 Ok(self.bundles.keys().copied().collect())
282 }
283
284 fn bundle(&self, bundle_id: BundleId) -> Result<&TransitionBundle, StashError<Self::Error>> {
285 self.bundles
286 .get(&bundle_id)
287 .ok_or(StashInconsistency::BundleAbsent(bundle_id).into())
288 }
289
290 fn extension_ids(&self) -> Result<BTreeSet<OpId>, Self::Error> {
291 Ok(self.extensions.keys().copied().collect())
292 }
293
294 fn extension(&self, op_id: OpId) -> Result<&Extension, StashError<Self::Error>> {
295 self.extensions
296 .get(&op_id)
297 .ok_or(StashInconsistency::OperationAbsent(op_id).into())
298 }
299
300 fn anchor_ids(&self) -> Result<BTreeSet<AnchorId>, Self::Error> {
301 Ok(self.anchors.keys().copied().collect())
302 }
303
304 fn anchor(&self, anchor_id: AnchorId) -> Result<&Anchor<MerkleBlock>, StashError<Self::Error>> {
305 self.anchors
306 .get(&anchor_id)
307 .ok_or(StashInconsistency::AnchorAbsent(anchor_id).into())
308 }
309}