use std::collections::{BTreeMap, BTreeSet};
use std::convert::Infallible;
use amplify::confinement;
use amplify::confinement::{Confined, LargeOrdMap, SmallOrdMap, TinyOrdMap, TinyOrdSet};
use bp::dbc::anchor::MergeError;
use commit_verify::mpc;
use commit_verify::mpc::{MerkleBlock, UnrelatedProof};
use rgb::{
Anchor, AnchorId, AnchoredBundle, BundleId, ContractId, Extension, Genesis, OpId, Operation,
SchemaId, TransitionBundle,
};
use strict_encoding::TypeName;
use crate::accessors::{MergeReveal, MergeRevealError};
use crate::containers::{Cert, Consignment, ContentId, ContentSigs};
use crate::interface::{rgb20, ContractSuppl, Iface, IfaceId, IfacePair, SchemaIfaces};
use crate::persistence::{Stash, StashError, StashInconsistency};
use crate::LIB_NAME_RGB_STD;
#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
#[display(inner)]
pub enum ConsumeError {
#[from]
Confinement(confinement::Error),
#[from]
Anchor(UnrelatedProof),
#[from]
Merge(MergeError),
#[from]
MergeReveal(MergeRevealError),
}
#[derive(Clone, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB_STD, dumb = Hoard::preset())]
pub struct Hoard {
pub(super) schemata: TinyOrdMap<SchemaId, SchemaIfaces>,
pub(super) ifaces: TinyOrdMap<IfaceId, Iface>,
pub(super) geneses: TinyOrdMap<ContractId, Genesis>,
pub(super) suppl: TinyOrdMap<ContractId, TinyOrdSet<ContractSuppl>>,
pub(super) bundles: LargeOrdMap<BundleId, TransitionBundle>,
pub(super) extensions: LargeOrdMap<OpId, Extension>,
pub(super) anchors: LargeOrdMap<AnchorId, Anchor<mpc::MerkleBlock>>,
pub(super) sigs: SmallOrdMap<ContentId, ContentSigs>,
}
impl Hoard {
pub fn preset() -> Self {
let rgb20 = rgb20();
let rgb20_id = rgb20.iface_id();
Hoard {
schemata: none!(),
ifaces: tiny_bmap! {
rgb20_id => rgb20,
},
geneses: none!(),
suppl: none!(),
bundles: none!(),
extensions: none!(),
anchors: none!(),
sigs: none!(),
}
}
pub(super) fn import_sigs_internal<I>(
&mut self,
content_id: ContentId,
sigs: I,
) -> Result<(), confinement::Error>
where
I: IntoIterator<Item = Cert>,
I::IntoIter: ExactSizeIterator<Item = Cert>,
{
let sigs = sigs.into_iter();
if sigs.len() > 0 {
if let Some(prev_sigs) = self.sigs.get_mut(&content_id) {
prev_sigs.extend(sigs)?;
} else {
let sigs = Confined::try_from_iter(sigs)?;
self.sigs.insert(content_id, ContentSigs::from(sigs)).ok();
}
}
Ok(())
}
pub fn consume_consignment<const TYPE: bool>(
&mut self,
consignment: Consignment<TYPE>,
) -> Result<(), ConsumeError> {
let contract_id = consignment.contract_id();
let schema_id = consignment.schema_id();
let iimpls = match self.schemata.get_mut(&schema_id) {
Some(si) => &mut si.iimpls,
None => {
self.schemata
.insert(schema_id, SchemaIfaces::new(consignment.schema))?;
&mut self
.schemata
.get_mut(&schema_id)
.expect("just inserted")
.iimpls
}
};
for (iface_id, IfacePair { iface, iimpl }) in consignment.ifaces {
if !self.ifaces.contains_key(&iface_id) {
self.ifaces.insert(iface_id, iface)?;
};
if !iimpls.contains_key(&iface_id) {
iimpls.insert(iface_id, iimpl)?;
};
}
match self.suppl.get_mut(&contract_id) {
Some(entry) => {
entry.extend(consignment.supplements).ok();
}
None => {
self.suppl.insert(contract_id, consignment.supplements).ok();
}
}
match self.geneses.get_mut(&contract_id) {
Some(genesis) => *genesis = genesis.clone().merge_reveal(consignment.genesis)?,
None => {
self.geneses.insert(contract_id, consignment.genesis)?;
}
}
for extension in consignment.extensions {
let opid = extension.id();
match self.extensions.get_mut(&opid) {
Some(e) => *e = e.clone().merge_reveal(extension)?,
None => {
self.extensions.insert(opid, extension)?;
}
}
}
for AnchoredBundle { anchor, bundle } in consignment.bundles {
let bundle_id = bundle.bundle_id();
let anchor = anchor.into_merkle_block(contract_id, bundle_id.into())?;
self.consume_anchor(anchor)?;
self.consume_bundle(bundle)?;
}
for (content_id, sigs) in consignment.signatures {
self.import_sigs_internal(content_id, sigs).ok();
}
Ok(())
}
pub fn consume_bundle(&mut self, bundle: TransitionBundle) -> Result<(), ConsumeError> {
let bundle_id = bundle.bundle_id();
match self.bundles.get_mut(&bundle_id) {
Some(b) => *b = b.clone().merge_reveal(bundle)?,
None => {
self.bundles.insert(bundle_id, bundle)?;
}
}
Ok(())
}
pub fn consume_anchor(&mut self, anchor: Anchor<MerkleBlock>) -> Result<(), ConsumeError> {
let anchor_id = anchor.anchor_id();
match self.anchors.get_mut(&anchor_id) {
Some(a) => *a = a.clone().merge_reveal(anchor)?,
None => {
self.anchors.insert(anchor_id, anchor)?;
}
}
Ok(())
}
}
impl Stash for Hoard {
type Error = Infallible;
fn ifaces(&self) -> Result<BTreeMap<IfaceId, TypeName>, Self::Error> {
Ok(self
.ifaces
.iter()
.map(|(id, iface)| (*id, iface.name.clone()))
.collect())
}
fn iface_by_name(&self, name: &TypeName) -> Result<&Iface, StashError<Self::Error>> {
self.ifaces
.values()
.find(|iface| &iface.name == name)
.ok_or_else(|| StashInconsistency::IfaceNameAbsent(name.clone()).into())
}
fn iface_by_id(&self, id: IfaceId) -> Result<&Iface, StashError<Self::Error>> {
self.ifaces
.get(&id)
.ok_or_else(|| StashInconsistency::IfaceAbsent(id).into())
}
fn schema_ids(&self) -> Result<BTreeSet<SchemaId>, Self::Error> {
Ok(self.schemata.keys().copied().collect())
}
fn schema(&self, schema_id: SchemaId) -> Result<&SchemaIfaces, StashError<Self::Error>> {
self.schemata
.get(&schema_id)
.ok_or_else(|| StashInconsistency::SchemaAbsent(schema_id).into())
}
fn contract_ids(&self) -> Result<BTreeSet<ContractId>, Self::Error> {
Ok(self.geneses.keys().copied().collect())
}
fn contract_suppl(&self, contract_id: ContractId) -> Option<&TinyOrdSet<ContractSuppl>> {
self.suppl.get(&contract_id)
}
fn genesis(&self, contract_id: ContractId) -> Result<&Genesis, StashError<Self::Error>> {
self.geneses
.get(&contract_id)
.ok_or(StashInconsistency::ContractAbsent(contract_id).into())
}
fn bundle_ids(&self) -> Result<BTreeSet<BundleId>, Self::Error> {
Ok(self.bundles.keys().copied().collect())
}
fn bundle(&self, bundle_id: BundleId) -> Result<&TransitionBundle, StashError<Self::Error>> {
self.bundles
.get(&bundle_id)
.ok_or(StashInconsistency::BundleAbsent(bundle_id).into())
}
fn extension_ids(&self) -> Result<BTreeSet<OpId>, Self::Error> {
Ok(self.extensions.keys().copied().collect())
}
fn extension(&self, op_id: OpId) -> Result<&Extension, StashError<Self::Error>> {
self.extensions
.get(&op_id)
.ok_or(StashInconsistency::OperationAbsent(op_id).into())
}
fn anchor_ids(&self) -> Result<BTreeSet<AnchorId>, Self::Error> {
Ok(self.anchors.keys().copied().collect())
}
fn anchor(&self, anchor_id: AnchorId) -> Result<&Anchor<MerkleBlock>, StashError<Self::Error>> {
self.anchors
.get(&anchor_id)
.ok_or(StashInconsistency::AnchorAbsent(anchor_id).into())
}
}