use std::borrow::Borrow;
use std::error::Error;
use std::fmt::Debug;
use amplify::confinement::{LargeOrdMap, LargeOrdSet};
use nonasync::persistence::{CloneNoPersistence, Persisting};
use rgb::validation::{ResolveWitness, WitnessOrdProvider, WitnessResolverError};
use rgb::vm::{ContractStateAccess, WitnessOrd};
use rgb::{
BundleId, ContractId, Genesis, KnownTransition, RevealedData, RevealedValue, Schema, SchemaId,
Transition, TransitionBundle, Txid, VoidState,
};
use crate::containers::{ConsignmentExt, ToWitnessId};
use crate::contract::OutputAssignment;
use crate::persistence::StoreTransaction;
#[derive(Debug, Display, Error, From)]
#[display(inner)]
pub enum StateError<P: StateProvider> {
ReadProvider(<P as StateReadProvider>::Error),
WriteProvider(<P as StateWriteProvider>::Error),
#[display(doc_comments)]
Resolver(Txid, WitnessResolverError),
AbsentValidWitness,
#[from]
#[display(doc_comments)]
Inconsistency(StateInconsistency),
}
#[derive(Clone, PartialEq, Eq, Debug, Display, Error)]
#[display(doc_comments)]
pub enum StateInconsistency {
UnknownContract(ContractId),
AbsentWitness(Txid),
}
#[derive(Debug)]
pub struct State<P: StateProvider> {
provider: P,
}
impl<P: StateProvider> CloneNoPersistence for State<P> {
fn clone_no_persistence(&self) -> Self {
Self {
provider: self.provider.clone_no_persistence(),
}
}
}
impl<P: StateProvider> Default for State<P>
where P: Default
{
fn default() -> Self {
Self {
provider: default!(),
}
}
}
impl<P: StateProvider> State<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 }
#[inline]
pub fn contract_state(
&self,
contract_id: ContractId,
) -> Result<P::ContractRead<'_>, StateError<P>> {
self.provider
.contract_state(contract_id)
.map_err(StateError::ReadProvider)
}
pub fn select_valid_witness(
&self,
witness_ids: impl IntoIterator<Item = impl Borrow<Txid>>,
) -> Result<(Txid, WitnessOrd), StateError<P>> {
let witnesses = self.as_provider().witnesses();
let mut best_candidate = None;
for id in witness_ids {
let id = *id.borrow();
let Some(&ord) = witnesses.get(&id) else {
return Err(StateError::Inconsistency(StateInconsistency::AbsentWitness(id)));
};
best_candidate = match best_candidate {
Some((_, curr_ord)) if ord < curr_ord => Some((id, ord)),
None => Some((id, ord)),
_ => best_candidate,
};
}
let (best_id, best_ord) = best_candidate.expect("one witness ID should always be there");
if best_ord == WitnessOrd::Archived {
Err(StateError::AbsentValidWitness)
} else {
Ok((best_id, best_ord))
}
}
pub fn update_from_bundle<WP: WitnessOrdProvider>(
&mut self,
contract_id: ContractId,
bundle: &TransitionBundle,
witness_id: Txid,
witness_ord_provider: &WP,
) -> Result<(), StateError<P>> {
let mut updater = self
.as_provider_mut()
.update_contract(contract_id)
.map_err(StateError::WriteProvider)?
.ok_or(StateInconsistency::UnknownContract(contract_id))?;
let bundle_id = bundle.bundle_id();
for KnownTransition { transition, .. } in &bundle.known_transitions {
let ord = witness_ord_provider
.witness_ord(witness_id)
.map_err(|e| StateError::Resolver(witness_id, e))?;
updater
.add_transition(transition, witness_id, ord, bundle_id)
.map_err(StateError::WriteProvider)?;
}
Ok(())
}
pub fn update_from_consignment<R: ResolveWitness>(
&mut self,
consignment: impl ConsignmentExt,
resolver: R,
) -> Result<(), StateError<P>> {
let mut state = self
.as_provider_mut()
.register_contract(consignment.schema(), consignment.genesis())
.map_err(StateError::WriteProvider)?;
for witness_bundle in consignment.bundled_witnesses() {
let bundle = witness_bundle.bundle();
let bundle_id = bundle.bundle_id();
for KnownTransition { transition, .. } in &bundle.known_transitions {
let witness_id = witness_bundle.pub_witness.to_witness_id();
let witness_ord = resolver
.resolve_witness(witness_id)
.map_err(|e| StateError::Resolver(witness_id, e))?
.witness_ord();
state
.add_transition(transition, witness_id, witness_ord, bundle_id)
.map_err(StateError::WriteProvider)?;
}
}
Ok(())
}
pub fn upsert_witness(
&mut self,
witness_id: Txid,
witness_ord: WitnessOrd,
) -> Result<(), StateError<P>> {
self.provider
.upsert_witness(witness_id, witness_ord)
.map_err(StateError::WriteProvider)
}
pub fn update_bundle(&mut self, bundle_id: BundleId, valid: bool) -> Result<(), StateError<P>> {
self.provider
.update_bundle(bundle_id, valid)
.map_err(StateError::WriteProvider)
}
}
impl<P: StateProvider> StoreTransaction for State<P> {
type TransactionErr = StateError<P>;
fn begin_transaction(&mut self) -> Result<(), Self::TransactionErr> {
self.provider
.begin_transaction()
.map_err(StateError::WriteProvider)
}
fn commit_transaction(&mut self) -> Result<(), Self::TransactionErr> {
self.provider
.commit_transaction()
.map_err(StateError::WriteProvider)
}
fn rollback_transaction(&mut self) { self.provider.rollback_transaction() }
}
pub trait StateProvider:
Debug + CloneNoPersistence + Persisting + StateReadProvider + StateWriteProvider
{
}
pub trait StateReadProvider {
type ContractRead<'a>: ContractStateRead
where Self: 'a;
type Error: Clone + Eq + Error;
fn contract_state(
&self,
contract_id: ContractId,
) -> Result<Self::ContractRead<'_>, Self::Error>;
fn witnesses(&self) -> LargeOrdMap<Txid, WitnessOrd>;
fn invalid_bundles(&self) -> LargeOrdSet<BundleId>;
}
pub trait StateWriteProvider: StoreTransaction<TransactionErr = Self::Error> {
type ContractWrite<'a>: ContractStateWrite<Error = Self::Error>
where Self: 'a;
type Error: Error;
fn register_contract(
&mut self,
schema: &Schema,
genesis: &Genesis,
) -> Result<Self::ContractWrite<'_>, Self::Error>;
fn update_contract(
&mut self,
contract_id: ContractId,
) -> Result<Option<Self::ContractWrite<'_>>, Self::Error>;
fn upsert_witness(
&mut self,
witness_id: Txid,
witness_ord: WitnessOrd,
) -> Result<(), Self::Error>;
fn update_bundle(&mut self, bundle_id: BundleId, valid: bool) -> Result<(), Self::Error>;
}
pub trait ContractStateRead: ContractStateAccess {
fn contract_id(&self) -> ContractId;
fn schema_id(&self) -> SchemaId;
fn witness_ord(&self, witness_id: Txid) -> Option<WitnessOrd>;
fn rights_all(&self) -> impl Iterator<Item = &OutputAssignment<VoidState>>;
fn fungible_all(&self) -> impl Iterator<Item = &OutputAssignment<RevealedValue>>;
fn data_all(&self) -> impl Iterator<Item = &OutputAssignment<RevealedData>>;
}
pub trait ContractStateWrite {
type Error: Error;
fn add_genesis(&mut self, genesis: &Genesis) -> Result<(), Self::Error>;
fn add_transition(
&mut self,
transition: &Transition,
witness_id: Txid,
witness_ord: WitnessOrd,
bundle_id: BundleId,
) -> Result<(), Self::Error>;
}