use std::collections::{BTreeMap, BTreeSet};
use std::error::Error;
use std::fmt::Debug;
use aluvm::library::{Lib, LibId};
use amplify::confinement;
use amplify::confinement::{Confined, MediumBlob, TinyOrdMap};
use bp::dbc::anchor::MergeError;
use bp::dbc::tapret::TapretCommitment;
use commit_verify::mpc;
use nonasync::persistence::{CloneNoPersistence, Persisting};
use rgb::validation::Scripts;
use rgb::{
AttachId, BundleId, ContractId, Extension, Genesis, GraphSeal, Identity, OpId, Operation,
Schema, SchemaId, TransitionBundle, XChain, XWitnessId,
};
use strict_encoding::{FieldName, TypeName};
use strict_types::typesys::UnknownType;
use strict_types::TypeSystem;
use crate::containers::{
BundledWitness, Consignment, ConsignmentExt, ContentId, ContentRef, ContentSigs, Kit,
SealWitness, SigBlob, Supplement, TrustLevel,
};
use crate::interface::{
ContractBuilder, Iface, IfaceClass, IfaceId, IfaceImpl, IfaceRef, TransitionBuilder,
};
use crate::persistence::{ContractIfaceError, StoreTransaction};
use crate::{MergeReveal, MergeRevealError, SecretSeal, LIB_NAME_RGB_STD};
#[derive(Debug, Display, Error, From)]
#[display(inner)]
pub enum StashError<P: StashProvider> {
ReadProvider(<P as StashReadProvider>::Error),
WriteProvider(<P as StashWriteProvider>::Error),
#[display(doc_comments)]
Inconsistency(StashInconsistency),
#[from]
#[from(UnknownType)]
#[from(MergeError)]
#[from(MergeRevealError)]
#[from(mpc::InvalidProof)]
Data(StashDataError),
}
#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
#[display(inner)]
pub enum ProviderError<E: Error> {
#[from]
Inconsistency(StashInconsistency),
#[from]
Iface(ContractIfaceError),
Connectivity(E),
}
impl<P: StashProvider> From<ProviderError<<P as StashReadProvider>::Error>> for StashError<P> {
fn from(err: ProviderError<<P as StashReadProvider>::Error>) -> Self {
match err {
ProviderError::Inconsistency(e) => StashError::Inconsistency(e),
ProviderError::Connectivity(e) => StashError::ReadProvider(e),
ProviderError::Iface(e) => StashError::Data(StashDataError::NoAbstractIface(e)),
}
}
}
#[derive(Clone, PartialEq, Eq, Debug, Display, Error, From)]
#[display(doc_comments)]
pub enum StashInconsistency {
LibAbsent(LibId),
IfaceAbsent(IfaceRef),
ContractAbsent(ContractId),
SchemaAbsent(SchemaId),
IfaceImplAbsent(IfaceId, SchemaId),
OperationAbsent(OpId),
WitnessAbsent(XWitnessId),
BundleAbsent(BundleId),
BundleMissedInAnchors(BundleId, ContractId),
}
#[derive(Clone, PartialEq, Eq, Debug, Display, Error, From)]
#[display(doc_comments)]
pub enum StashDataError {
TooManyLibs(SchemaId),
#[from]
#[display(inner)]
UnknownType(UnknownType),
#[from]
#[display(inner)]
Anchor(mpc::InvalidProof),
#[from]
#[display(inner)]
Merge(MergeError),
#[from]
#[display(inner)]
MergeReveal(MergeRevealError),
NoIfaceImpl(SchemaId, IfaceId),
#[from]
#[display(inner)]
NoAbstractIface(ContractIfaceError),
}
#[derive(Clone, Eq, PartialEq, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB_STD)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct SchemaIfaces {
pub schema: Schema,
pub iimpls: TinyOrdMap<TypeName, IfaceImpl>,
}
impl SchemaIfaces {
pub fn new(schema: Schema) -> Self {
SchemaIfaces {
schema,
iimpls: none!(),
}
}
pub fn get(&self, id: IfaceId) -> Option<&IfaceImpl> {
self.iimpls.values().find(|iimpl| iimpl.iface_id == id)
}
pub fn contains(&self, id: IfaceId) -> bool {
self.iimpls.values().any(|iimpl| iimpl.iface_id == id)
}
}
#[derive(Debug)]
pub struct Stash<P: StashProvider> {
provider: P,
}
impl<P: StashProvider> CloneNoPersistence for Stash<P> {
fn clone_no_persistence(&self) -> Self {
Self {
provider: self.provider.clone_no_persistence(),
}
}
}
impl<P: StashProvider> Default for Stash<P>
where P: Default
{
fn default() -> Self {
Self {
provider: default!(),
}
}
}
impl<P: StashProvider> Stash<P> {
pub(super) fn new(provider: P) -> Self { Self { provider } }
#[doc(hidden)]
pub fn as_provider(&self) -> &P { &self.provider }
#[doc(hidden)]
pub(super) fn as_provider_mut(&mut self) -> &mut P { &mut self.provider }
pub(super) fn ifaces(&self) -> Result<impl Iterator<Item = &Iface> + '_, StashError<P>> {
self.provider.ifaces().map_err(StashError::ReadProvider)
}
pub(super) fn iface(&self, iface: impl Into<IfaceRef>) -> Result<&Iface, StashError<P>> {
Ok(self.provider.iface(iface)?)
}
pub(super) fn schemata(
&self,
) -> Result<impl Iterator<Item = &SchemaIfaces> + '_, StashError<P>> {
self.provider.schemata().map_err(StashError::ReadProvider)
}
pub(super) fn schema(&self, schema_id: SchemaId) -> Result<&SchemaIfaces, StashError<P>> {
Ok(self.provider.schema(schema_id)?)
}
pub(super) fn impl_for<'a, C: IfaceClass + 'a>(
&'a self,
schema_ifaces: &'a SchemaIfaces,
) -> Result<&'a IfaceImpl, StashError<P>> {
Ok(self.provider.impl_for::<C>(schema_ifaces)?)
}
pub(super) fn geneses(&self) -> Result<impl Iterator<Item = &Genesis> + '_, StashError<P>> {
self.provider.geneses().map_err(StashError::ReadProvider)
}
pub(super) fn geneses_by<'a, C: IfaceClass + 'a>(
&'a self,
) -> Result<impl Iterator<Item = &'a Genesis> + 'a, StashError<P>> {
self.provider
.geneses_by::<C>()
.map_err(StashError::ReadProvider)
}
pub(super) fn genesis(&self, contract_id: ContractId) -> Result<&Genesis, StashError<P>> {
Ok(self.provider.genesis(contract_id)?)
}
pub(super) fn bundle(&self, bundle_id: BundleId) -> Result<&TransitionBundle, StashError<P>> {
Ok(self.provider.bundle(bundle_id)?)
}
pub(super) fn witness(&self, witness_id: XWitnessId) -> Result<&SealWitness, StashError<P>> {
Ok(self.provider.witness(witness_id)?)
}
pub(super) fn supplements(
&self,
content_ref: ContentRef,
) -> Result<impl Iterator<Item = Supplement> + '_, StashError<P>> {
self.provider
.supplements(content_ref)
.map_err(StashError::ReadProvider)
}
pub(super) fn sigs_for(
&self,
content_id: &ContentId,
) -> Result<Option<&ContentSigs>, StashError<P>> {
self.provider
.sigs_for(content_id)
.map_err(StashError::ReadProvider)
}
pub(super) fn supplement(
&self,
content_ref: ContentRef,
) -> Result<Option<&Supplement>, StashError<P>> {
self.provider
.supplement(content_ref)
.map_err(StashError::ReadProvider)
}
pub(super) fn extract<'a>(
&self,
schema: &Schema,
ifaces: impl IntoIterator<Item = &'a Iface>,
) -> Result<(TypeSystem, Scripts), StashError<P>> {
let type_iter = schema
.types()
.chain(ifaces.into_iter().flat_map(Iface::types));
let types = self
.provider
.type_system()
.map_err(StashError::ReadProvider)?
.extract(type_iter)?;
let mut scripts = BTreeMap::new();
for id in schema.libs() {
let lib = self.provider.lib(id)?;
scripts.insert(id, lib.clone());
}
let scripts = Scripts::try_from(scripts)
.map_err(|_| StashDataError::TooManyLibs(schema.schema_id()))?;
Ok((types, scripts))
}
pub(super) fn contract_builder(
&self,
issuer: Identity,
schema_id: SchemaId,
iface: impl Into<IfaceRef>,
) -> Result<ContractBuilder, StashError<P>> {
let schema_ifaces = self.schema(schema_id)?;
let iface = self.iface(iface)?;
let iface_id = iface.iface_id();
let iimpl = schema_ifaces
.get(iface_id)
.ok_or(StashDataError::NoIfaceImpl(schema_id, iface_id))?;
let (types, scripts) = self.extract(&schema_ifaces.schema, [iface])?;
let builder = ContractBuilder::with(
issuer,
iface.clone(),
schema_ifaces.schema.clone(),
iimpl.clone(),
types,
scripts,
);
Ok(builder)
}
pub(super) fn transition_builder(
&self,
contract_id: ContractId,
iface: impl Into<IfaceRef>,
transition_name: Option<impl Into<FieldName>>,
) -> Result<TransitionBuilder, StashError<P>> {
let schema_ifaces = self.provider.contract_schema(contract_id)?;
let iface = self.iface(iface)?;
let schema = &schema_ifaces.schema;
let iimpl = schema_ifaces
.get(iface.iface_id())
.ok_or(StashDataError::NoIfaceImpl(schema.schema_id(), iface.iface_id()))?;
let genesis = self.provider.genesis(contract_id)?;
let (types, _) = self.extract(&schema_ifaces.schema, [iface])?;
let mut builder = if let Some(transition_name) = transition_name {
TransitionBuilder::named_transition(
contract_id,
iface.clone(),
schema.clone(),
iimpl.clone(),
transition_name.into(),
types,
)
} else {
TransitionBuilder::default_transition(
contract_id,
iface.clone(),
schema.clone(),
iimpl.clone(),
types,
)
}
.expect("internal inconsistency");
for (assignment_type, asset_tag) in genesis.asset_tags.iter() {
builder = builder
.add_asset_tag_raw(*assignment_type, *asset_tag)
.expect("tags are in bset and must not repeat");
}
Ok(builder)
}
pub(super) fn blank_builder(
&self,
contract_id: ContractId,
iface: impl Into<IfaceRef>,
) -> Result<TransitionBuilder, StashError<P>> {
let schema_ifaces = self.provider.contract_schema(contract_id)?;
let iface = self.iface(iface)?;
let schema = &schema_ifaces.schema;
if schema_ifaces.iimpls.is_empty() {
return Err(StashDataError::NoIfaceImpl(schema.schema_id(), iface.iface_id()).into());
}
let genesis = self.provider.genesis(contract_id)?;
let (types, _) = self.extract(&schema_ifaces.schema, [iface])?;
let mut builder = if let Some(iimpl) = schema_ifaces.get(iface.iface_id()) {
TransitionBuilder::blank_transition(
contract_id,
iface.clone(),
schema.clone(),
iimpl.clone(),
types,
)
} else {
let (default_iface_name, default_iimpl) =
schema_ifaces.iimpls.first_key_value().unwrap();
let default_iface = self.iface(default_iface_name.clone())?;
TransitionBuilder::blank_transition(
contract_id,
default_iface.clone(),
schema.clone(),
default_iimpl.clone(),
types,
)
};
for (assignment_type, asset_tag) in genesis.asset_tags.iter() {
builder = builder
.add_asset_tag_raw(*assignment_type, *asset_tag)
.expect("tags are in bset and must not repeat");
}
Ok(builder)
}
pub(super) fn consume_kit(&mut self, kit: Kit) -> Result<(), StashError<P>> {
self.provider
.consume_types(kit.types)
.map_err(StashError::WriteProvider)?;
for lib in kit.scripts {
self.provider
.replace_lib(lib)
.map_err(StashError::WriteProvider)?;
}
for schema in kit.schemata {
self.provider
.replace_schema(schema)
.map_err(StashError::WriteProvider)?;
}
for iface in kit.ifaces {
self.provider
.replace_iface(iface)
.map_err(StashError::WriteProvider)?;
}
for iimpl in kit.iimpls {
self.provider
.replace_iimpl(iimpl)
.map_err(StashError::WriteProvider)?;
}
for suppl in kit.supplements {
self.provider
.add_supplement(suppl)
.map_err(StashError::WriteProvider)?;
}
for (content_id, sigs) in kit.signatures {
self.provider.import_sigs(content_id, sigs).ok();
}
Ok(())
}
pub(super) fn resolve_secrets<const TRANSFER: bool>(
&self,
mut consignment: Consignment<TRANSFER>,
) -> Result<Consignment<TRANSFER>, StashError<P>> {
consignment = consignment.reveal_terminal_seals(|secret| {
self.provider
.seal_secret(secret)
.map_err(StashError::ReadProvider)
})?;
Ok(consignment)
}
pub(super) fn consume_consignment<const TRANSFER: bool>(
&mut self,
consignment: Consignment<TRANSFER>,
) -> Result<(), StashError<P>> {
let contract_id = consignment.contract_id();
let genesis = match self.genesis(contract_id) {
Ok(g) => g.clone().merge_reveal(consignment.genesis)?,
Err(_) => consignment.genesis,
};
self.provider
.replace_genesis(genesis)
.map_err(StashError::WriteProvider)?;
for extension in consignment.extensions {
let opid = extension.id();
let extension = match self.provider.extension(opid) {
Ok(e) => e.clone().merge_reveal(extension)?,
Err(_) => extension,
};
self.provider
.replace_extension(extension)
.map_err(StashError::WriteProvider)?;
}
for bw in consignment.bundles {
self.consume_bundled_witness(contract_id, bw)?;
}
for (id, attach) in consignment.attachments {
self.provider
.replace_attachment(id, attach)
.map_err(StashError::WriteProvider)?;
}
let (ifaces, iimpls): (BTreeSet<_>, BTreeSet<_>) = consignment
.ifaces
.release()
.into_iter()
.fold((bset!(), bset!()), |(mut keys, mut values), (k, v)| {
keys.insert(k);
values.insert(v);
(keys, values)
});
self.consume_kit(Kit {
version: consignment.version,
ifaces: Confined::from_checked(ifaces),
schemata: tiny_bset![consignment.schema],
iimpls: Confined::from_checked(iimpls),
supplements: consignment.supplements,
types: consignment.types,
scripts: Confined::from_checked(consignment.scripts.release()),
signatures: consignment.signatures,
})
}
fn consume_bundled_witness(
&mut self,
contract_id: ContractId,
bundled_witness: BundledWitness,
) -> Result<(), StashError<P>> {
let BundledWitness {
pub_witness,
anchored_bundles,
} = bundled_witness;
for bundle in anchored_bundles.bundles().cloned() {
let bundle_id = bundle.bundle_id();
self.consume_bundle(bundle)?;
let anchors = anchored_bundles.to_anchor_set(contract_id, bundle_id)?;
let witness = SealWitness {
public: pub_witness.clone(),
anchors,
};
self.consume_witness(witness)?;
}
Ok(())
}
pub(crate) fn consume_witness(&mut self, witness: SealWitness) -> Result<bool, StashError<P>> {
let witness = match self.provider.witness(witness.witness_id()).cloned() {
Ok(mut w) => {
w.public = w.public.clone().merge_reveal(witness.public)?;
w.anchors = w.anchors.clone().merge_reveal(witness.anchors)?;
w
}
Err(_) => witness,
};
self.provider
.replace_witness(witness)
.map_err(StashError::WriteProvider)
}
pub(crate) fn consume_bundle(
&mut self,
bundle: TransitionBundle,
) -> Result<bool, StashError<P>> {
let bundle = match self.provider.bundle(bundle.bundle_id()).cloned() {
Ok(b) => b.merge_reveal(bundle)?,
Err(_) => bundle,
};
self.provider
.replace_bundle(bundle)
.map_err(StashError::WriteProvider)
}
pub(crate) fn store_secret_seal(
&mut self,
seal: XChain<GraphSeal>,
) -> Result<bool, StashError<P>> {
self.begin_transaction()?;
let seal = self
.provider
.add_secret_seal(seal)
.inspect_err(|_| self.rollback_transaction())
.map_err(StashError::WriteProvider)?;
self.commit_transaction()?;
Ok(seal)
}
}
impl<P: StashProvider> StoreTransaction for Stash<P> {
type TransactionErr = StashError<P>;
fn begin_transaction(&mut self) -> Result<(), Self::TransactionErr> {
self.provider
.begin_transaction()
.map_err(StashError::WriteProvider)
}
fn commit_transaction(&mut self) -> Result<(), Self::TransactionErr> {
self.provider
.commit_transaction()
.map_err(StashError::WriteProvider)
}
fn rollback_transaction(&mut self) { self.provider.rollback_transaction() }
}
pub trait StashProvider:
Debug + CloneNoPersistence + Persisting + StashReadProvider + StashWriteProvider
{
}
pub trait StashReadProvider {
type Error: Clone + Eq + Error;
fn type_system(&self) -> Result<&TypeSystem, Self::Error>;
fn lib(&self, id: LibId) -> Result<&Lib, ProviderError<Self::Error>>;
fn ifaces(&self) -> Result<impl Iterator<Item = &Iface>, Self::Error>;
fn iface(&self, iface: impl Into<IfaceRef>) -> Result<&Iface, ProviderError<Self::Error>>;
fn schemata(&self) -> Result<impl Iterator<Item = &SchemaIfaces>, Self::Error>;
fn schema(&self, schema_id: SchemaId) -> Result<&SchemaIfaces, ProviderError<Self::Error>>;
fn schemata_by<C: IfaceClass>(
&self,
) -> Result<impl Iterator<Item = &SchemaIfaces>, Self::Error>;
fn impl_for<'a, C: IfaceClass + 'a>(
&'a self,
schema_ifaces: &'a SchemaIfaces,
) -> Result<&'a IfaceImpl, ProviderError<Self::Error>>;
fn geneses(&self) -> Result<impl Iterator<Item = &Genesis>, Self::Error>;
fn geneses_by<C: IfaceClass>(&self) -> Result<impl Iterator<Item = &Genesis>, Self::Error>;
fn genesis(&self, contract_id: ContractId) -> Result<&Genesis, ProviderError<Self::Error>>;
fn contract_schema(
&self,
contract_id: ContractId,
) -> Result<&SchemaIfaces, ProviderError<Self::Error>> {
let genesis = self.genesis(contract_id)?;
self.schema(genesis.schema_id)
}
fn get_trust(&self, identity: &Identity) -> Result<TrustLevel, Self::Error>;
fn supplement(&self, content_ref: ContentRef) -> Result<Option<&Supplement>, Self::Error>;
fn supplements(
&self,
content_ref: ContentRef,
) -> Result<impl Iterator<Item = Supplement>, Self::Error>;
fn sigs_for(&self, content_id: &ContentId) -> Result<Option<&ContentSigs>, Self::Error>;
fn witness_ids(&self) -> Result<impl Iterator<Item = XWitnessId>, Self::Error>;
fn bundle_ids(&self) -> Result<impl Iterator<Item = BundleId>, Self::Error>;
fn bundle(&self, bundle_id: BundleId) -> Result<&TransitionBundle, ProviderError<Self::Error>>;
fn extension_ids(&self) -> Result<impl Iterator<Item = OpId>, Self::Error>;
fn extension(&self, op_id: OpId) -> Result<&Extension, ProviderError<Self::Error>>;
fn witness(&self, witness_id: XWitnessId) -> Result<&SealWitness, ProviderError<Self::Error>>;
fn taprets(&self) -> Result<impl Iterator<Item = (XWitnessId, TapretCommitment)>, Self::Error>;
fn seal_secret(
&self,
secret: XChain<SecretSeal>,
) -> Result<Option<XChain<GraphSeal>>, Self::Error>;
fn secret_seals(&self) -> Result<impl Iterator<Item = XChain<GraphSeal>>, Self::Error>;
}
pub trait StashWriteProvider: StoreTransaction<TransactionErr = Self::Error> {
type Error: Error;
fn replace_schema(&mut self, schema: Schema) -> Result<bool, Self::Error>;
fn replace_iface(&mut self, iface: Iface) -> Result<bool, Self::Error>;
fn replace_iimpl(&mut self, iimpl: IfaceImpl) -> Result<bool, Self::Error>;
fn replace_genesis(&mut self, genesis: Genesis) -> Result<bool, Self::Error>;
fn replace_extension(&mut self, extension: Extension) -> Result<bool, Self::Error>;
fn replace_bundle(&mut self, bundle: TransitionBundle) -> Result<bool, Self::Error>;
fn replace_witness(&mut self, witness: SealWitness) -> Result<bool, Self::Error>;
fn replace_attachment(&mut self, id: AttachId, attach: MediumBlob)
-> Result<bool, Self::Error>;
fn replace_lib(&mut self, lib: Lib) -> Result<bool, Self::Error>;
fn consume_types(&mut self, types: TypeSystem) -> Result<(), Self::Error>;
fn set_trust(
&mut self,
identity: Identity,
trust: TrustLevel,
) -> Result<(), confinement::Error>;
fn add_supplement(&mut self, suppl: Supplement) -> Result<(), Self::Error>;
fn import_sigs<I>(&mut self, content_id: ContentId, sigs: I) -> Result<(), Self::Error>
where I: IntoIterator<Item = (Identity, SigBlob)>;
fn add_secret_seal(&mut self, seal: XChain<GraphSeal>) -> Result<bool, Self::Error>;
}