use std::cmp::Ordering;
use std::collections::{BTreeSet, HashMap};
use std::fmt::Debug;
use std::hash::Hash;
use invoice::Amount;
use rgb::vm::WitnessOrd;
use rgb::{
AssignmentType, BundleId, ExposedSeal, OpId, Opout, OutputSeal, RevealedData, RevealedValue,
Txid, VoidState,
};
use strict_encoding::{StrictDecode, StrictDumb, StrictEncode};
use crate::LIB_NAME_RGB_OPS;
pub trait KnownState: Debug + StrictDumb + StrictEncode + StrictDecode + Eq + Clone + Hash {
const IS_FUNGIBLE: bool;
}
impl KnownState for () {
const IS_FUNGIBLE: bool = false;
}
impl KnownState for VoidState {
const IS_FUNGIBLE: bool = false;
}
impl KnownState for Amount {
const IS_FUNGIBLE: bool = true;
}
impl KnownState for RevealedValue {
const IS_FUNGIBLE: bool = true;
}
impl KnownState for RevealedData {
const IS_FUNGIBLE: bool = false;
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB_OPS)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct WitnessInfo {
pub id: Txid,
pub ord: WitnessOrd,
}
#[allow(clippy::derived_hash_with_manual_eq)]
#[derive(Copy, Clone, Eq, Hash, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB_OPS)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct OutputAssignment<State: KnownState> {
pub opout: Opout,
pub seal: OutputSeal,
pub state: State,
pub witness: Option<Txid>,
pub bundle_id: Option<BundleId>,
}
impl<State: KnownState> PartialEq for OutputAssignment<State> {
fn eq(&self, other: &Self) -> bool {
let res = self.opout == other.opout && self.seal == other.seal;
#[cfg(debug_assertions)]
if res {
debug_assert_eq!(self.state, other.state);
}
res
}
}
impl<State: KnownState> PartialOrd for OutputAssignment<State> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
}
impl<State: KnownState> Ord for OutputAssignment<State> {
fn cmp(&self, other: &Self) -> Ordering {
if self == other {
return Ordering::Equal;
}
match self.opout.cmp(&other.opout) {
Ordering::Equal => self.seal.cmp(&other.seal),
ordering => ordering,
}
}
}
impl<State: KnownState> OutputAssignment<State> {
pub fn with_witness<Seal: ExposedSeal>(
seal: Seal,
witness_id: Txid,
state: State,
bundle_id: Option<BundleId>,
opid: OpId,
ty: AssignmentType,
no: u16,
) -> Self {
OutputAssignment {
opout: Opout::new(opid, ty, no),
seal: seal.to_output_seal_or_default(witness_id),
state,
bundle_id,
witness: witness_id.into(),
}
}
pub fn with_no_witness<Seal: ExposedSeal>(
seal: Seal,
state: State,
bundle_id: Option<BundleId>,
opid: OpId,
ty: AssignmentType,
no: u16,
) -> Self {
OutputAssignment {
opout: Opout::new(opid, ty, no),
seal: seal.to_output_seal().expect(
"processing contract from unverified/invalid stash: seal must have txid \
information since it comes from genesis",
),
state,
bundle_id,
witness: None,
}
}
pub fn transmute<S: KnownState + From<State>>(self) -> OutputAssignment<S> {
OutputAssignment {
opout: self.opout,
seal: self.seal,
state: self.state.into(),
bundle_id: self.bundle_id,
witness: self.witness,
}
}
pub fn check_witness(&self, filter: &HashMap<Txid, WitnessOrd>) -> bool {
match self.witness {
None => true,
Some(witness_id) => {
!matches!(filter.get(&witness_id), None | Some(WitnessOrd::Archived))
}
}
}
pub fn check_bundle(&self, invalid_bundles: &BTreeSet<BundleId>) -> bool {
match self.bundle_id {
Some(bundle_id) => !invalid_bundles.contains(&bundle_id),
None => true,
}
}
}