use std::collections::BTreeSet;
use std::error::Error;
use std::fmt::Debug;
use amplify::confinement::{self, SmallOrdSet};
use nonasync::persistence::{CloneNoPersistence, Persisting};
use rgb::bitcoin::{OutPoint as Outpoint, Txid};
use rgb::{
Assign, AssignmentType, BundleId, ContractId, ExposedState, Genesis, GenesisSeal, GraphSeal,
KnownTransition, OpId, Operation, Opout, TransitionBundle, TypedAssigns,
};
use crate::containers::{ConsignmentExt, ToWitnessId, WitnessBundle};
use crate::persistence::{MemError, StoreTransaction};
use crate::SecretSeal;
#[derive(Debug, Display, Error, From)]
#[display(inner)]
pub enum IndexError<P: IndexProvider> {
ReadProvider(<P as IndexReadProvider>::Error),
WriteProvider(<P as IndexWriteProvider>::Error),
Inconsistency(IndexInconsistency),
}
#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
#[display(inner)]
pub enum IndexReadError<E: Error> {
#[from]
Inconsistency(IndexInconsistency),
Connectivity(E),
}
#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
#[display(inner)]
pub enum IndexWriteError<E: Error> {
#[from]
Inconsistency(IndexInconsistency),
Connectivity(E),
}
impl<P: IndexProvider> From<IndexReadError<<P as IndexReadProvider>::Error>> for IndexError<P> {
fn from(err: IndexReadError<<P as IndexReadProvider>::Error>) -> Self {
match err {
IndexReadError::Inconsistency(e) => IndexError::Inconsistency(e),
IndexReadError::Connectivity(e) => IndexError::ReadProvider(e),
}
}
}
impl<P: IndexProvider> From<IndexWriteError<<P as IndexWriteProvider>::Error>> for IndexError<P> {
fn from(err: IndexWriteError<<P as IndexWriteProvider>::Error>) -> Self {
match err {
IndexWriteError::Inconsistency(e) => IndexError::Inconsistency(e),
IndexWriteError::Connectivity(e) => IndexError::WriteProvider(e),
}
}
}
impl From<confinement::Error> for IndexWriteError<MemError> {
fn from(err: confinement::Error) -> Self { IndexWriteError::Connectivity(err.into()) }
}
#[derive(Clone, PartialEq, Eq, Debug, Display, Error, From)]
#[display(doc_comments)]
pub enum IndexInconsistency {
ContractAbsent(ContractId),
BundleAbsent(OpId),
OutpointUnknown(Outpoint, ContractId),
DistinctBundleContract {
bundle_id: BundleId,
present: ContractId,
expected: ContractId,
},
DistinctBundleOp {
opid: OpId,
present: BundleId,
expected: BundleId,
},
BundleContractUnknown(BundleId),
BundleWitnessUnknown(BundleId),
}
#[derive(Debug)]
pub struct Index<P: IndexProvider> {
provider: P,
}
impl<P: IndexProvider> CloneNoPersistence for Index<P> {
fn clone_no_persistence(&self) -> Self {
Self {
provider: self.provider.clone_no_persistence(),
}
}
}
impl<P: IndexProvider> Default for Index<P>
where P: Default
{
fn default() -> Self {
Self {
provider: default!(),
}
}
}
impl<P: IndexProvider> Index<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 index_consignment(
&mut self,
consignment: impl ConsignmentExt,
) -> Result<(), IndexError<P>> {
let contract_id = consignment.contract_id();
self.provider
.register_contract(contract_id)
.map_err(IndexError::WriteProvider)?;
self.index_genesis(contract_id, consignment.genesis())?;
for WitnessBundle {
pub_witness,
anchor: _,
bundle,
} in consignment.bundled_witnesses()
{
let witness_id = pub_witness.to_witness_id();
self.index_bundle(contract_id, bundle, witness_id)?;
}
Ok(())
}
fn index_genesis(&mut self, id: ContractId, genesis: &Genesis) -> Result<(), IndexError<P>> {
let opid = genesis.id();
for (type_id, assign) in genesis.assignments.iter() {
match assign {
TypedAssigns::Declarative(vec) => {
self.provider
.index_genesis_assignments(id, vec, opid, *type_id)?;
}
TypedAssigns::Fungible(vec) => {
self.provider
.index_genesis_assignments(id, vec, opid, *type_id)?;
}
TypedAssigns::Structured(vec) => {
self.provider
.index_genesis_assignments(id, vec, opid, *type_id)?;
}
}
}
Ok(())
}
pub(crate) fn index_bundle(
&mut self,
contract_id: ContractId,
bundle: &TransitionBundle,
witness_id: Txid,
) -> Result<(), IndexError<P>> {
let bundle_id = bundle.bundle_id();
self.provider
.register_bundle(bundle_id, witness_id, contract_id)?;
for KnownTransition { opid, transition } in &bundle.known_transitions {
self.provider.register_operation(*opid, bundle_id)?;
for input in &transition.inputs {
self.provider.register_spending(input.op, bundle_id)?;
}
for (type_id, assign) in transition.assignments.iter() {
match assign {
TypedAssigns::Declarative(vec) => {
self.provider.index_transition_assignments(
contract_id,
vec,
*opid,
*type_id,
witness_id,
)?;
}
TypedAssigns::Fungible(vec) => {
self.provider.index_transition_assignments(
contract_id,
vec,
*opid,
*type_id,
witness_id,
)?;
}
TypedAssigns::Structured(vec) => {
self.provider.index_transition_assignments(
contract_id,
vec,
*opid,
*type_id,
witness_id,
)?;
}
}
}
}
Ok(())
}
pub(super) fn contracts_assigning(
&self,
outputs: BTreeSet<Outpoint>,
) -> Result<impl Iterator<Item = ContractId> + '_, IndexError<P>> {
self.provider
.contracts_assigning(outputs)
.map_err(IndexError::ReadProvider)
}
pub(super) fn public_opouts(
&self,
contract_id: ContractId,
) -> Result<BTreeSet<Opout>, IndexError<P>> {
Ok(self.provider.public_opouts(contract_id)?)
}
pub(super) fn opouts_by_outputs(
&self,
contract_id: ContractId,
outputs: impl IntoIterator<Item = impl Into<Outpoint>>,
) -> Result<BTreeSet<Opout>, IndexError<P>> {
Ok(self.provider.opouts_by_outputs(contract_id, outputs)?)
}
pub(super) fn opouts_by_terminals(
&self,
terminals: impl IntoIterator<Item = SecretSeal>,
) -> Result<BTreeSet<Opout>, IndexError<P>> {
self.provider
.opouts_by_terminals(terminals)
.map_err(IndexError::ReadProvider)
}
pub(super) fn bundle_id_for_op(&self, opid: OpId) -> Result<BundleId, IndexError<P>> {
Ok(self.provider.bundle_id_for_op(opid)?)
}
pub(super) fn bundle_ids_children_of_op(
&self,
opid: OpId,
) -> Result<SmallOrdSet<BundleId>, IndexError<P>> {
Ok(self.provider.bundle_ids_children_of_op(opid)?)
}
pub(super) fn bundle_info(
&self,
bundle_id: BundleId,
) -> Result<(impl Iterator<Item = Txid> + '_, ContractId), IndexError<P>> {
Ok(self.provider.bundle_info(bundle_id)?)
}
}
impl<P: IndexProvider> StoreTransaction for Index<P> {
type TransactionErr = IndexError<P>;
fn begin_transaction(&mut self) -> Result<(), Self::TransactionErr> {
self.provider
.begin_transaction()
.map_err(IndexError::WriteProvider)
}
fn commit_transaction(&mut self) -> Result<(), Self::TransactionErr> {
self.provider
.commit_transaction()
.map_err(IndexError::WriteProvider)
}
fn rollback_transaction(&mut self) { self.provider.rollback_transaction() }
}
pub trait IndexProvider:
Debug + CloneNoPersistence + Persisting + IndexReadProvider + IndexWriteProvider
{
}
pub trait IndexReadProvider {
type Error: Clone + Eq + Error;
fn contracts_assigning(
&self,
outputs: BTreeSet<Outpoint>,
) -> Result<impl Iterator<Item = ContractId> + '_, Self::Error>;
fn public_opouts(
&self,
contract_id: ContractId,
) -> Result<BTreeSet<Opout>, IndexReadError<Self::Error>>;
fn opouts_by_outputs(
&self,
contract_id: ContractId,
outputs: impl IntoIterator<Item = impl Into<Outpoint>>,
) -> Result<BTreeSet<Opout>, IndexReadError<Self::Error>>;
fn opouts_by_terminals(
&self,
terminals: impl IntoIterator<Item = SecretSeal>,
) -> Result<BTreeSet<Opout>, Self::Error>;
fn bundle_id_for_op(&self, opid: OpId) -> Result<BundleId, IndexReadError<Self::Error>>;
fn bundle_ids_children_of_op(
&self,
opid: OpId,
) -> Result<SmallOrdSet<BundleId>, IndexReadError<Self::Error>>;
fn bundle_info(
&self,
bundle_id: BundleId,
) -> Result<(impl Iterator<Item = Txid>, ContractId), IndexReadError<Self::Error>>;
}
pub trait IndexWriteProvider: StoreTransaction<TransactionErr = Self::Error> {
type Error: Error;
fn register_contract(&mut self, contract_id: ContractId) -> Result<bool, Self::Error>;
fn register_bundle(
&mut self,
bundle_id: BundleId,
witness_id: Txid,
contract_id: ContractId,
) -> Result<bool, IndexWriteError<Self::Error>>;
fn register_operation(
&mut self,
opid: OpId,
bundle_id: BundleId,
) -> Result<bool, IndexWriteError<Self::Error>>;
fn register_spending(
&mut self,
opid: OpId,
bundle_id: BundleId,
) -> Result<bool, IndexWriteError<Self::Error>>;
fn index_genesis_assignments<State: ExposedState>(
&mut self,
contract_id: ContractId,
vec: &[Assign<State, GenesisSeal>],
opid: OpId,
type_id: AssignmentType,
) -> Result<(), IndexWriteError<Self::Error>>;
fn index_transition_assignments<State: ExposedState>(
&mut self,
contract_id: ContractId,
vec: &[Assign<State, GraphSeal>],
opid: OpId,
type_id: AssignmentType,
witness_id: Txid,
) -> Result<(), IndexWriteError<Self::Error>>;
}