rgbstd/interface/
contract.rs1use std::collections::{BTreeSet, HashSet};
23
24use amplify::confinement::{LargeOrdMap, LargeVec, SmallVec};
25use bp::Outpoint;
26use rgb::{
27 AssignmentType, AttachId, ContractId, ContractState, FungibleOutput, MediaType, RevealedAttach,
28 RevealedData, SealWitness,
29};
30use strict_encoding::FieldName;
31use strict_types::typify::TypedVal;
32use strict_types::{decode, StrictVal};
33
34use crate::interface::IfaceImpl;
35
36#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
37#[display(doc_comments)]
38pub enum ContractError {
39 FieldNameUnknown(FieldName),
41
42 #[from]
43 #[display(inner)]
44 Reify(decode::Error),
45}
46
47#[derive(Clone, Eq, PartialEq, Debug, Display, From)]
48#[display(inner)]
49pub enum TypedState {
50 #[display("")]
51 Void,
52 #[from]
53 Amount(u64),
54 #[from]
55 Data(RevealedData),
56 #[from]
57 Attachment(AttachedState),
58}
59
60#[derive(Clone, Eq, PartialEq, Debug, Display)]
61#[display("{id}:{media_type}")]
62pub struct AttachedState {
63 pub id: AttachId,
64 pub media_type: MediaType,
65}
66
67impl From<RevealedAttach> for AttachedState {
68 fn from(attach: RevealedAttach) -> Self {
69 AttachedState {
70 id: attach.id,
71 media_type: attach.media_type,
72 }
73 }
74}
75
76#[derive(Copy, Clone, Eq, PartialEq, Debug)]
77pub struct FungibleAllocation {
78 pub owner: Outpoint,
79 pub witness: SealWitness,
80 pub value: u64,
81}
82
83impl From<FungibleOutput> for FungibleAllocation {
84 fn from(out: FungibleOutput) -> Self { Self::from(&out) }
85}
86
87impl From<&FungibleOutput> for FungibleAllocation {
88 fn from(out: &FungibleOutput) -> Self {
89 FungibleAllocation {
90 owner: out.seal,
91 witness: out.witness,
92 value: out.state.value.as_u64(),
93 }
94 }
95}
96
97pub trait OutpointFilter {
98 fn include_outpoint(&self, outpoint: Outpoint) -> bool;
99}
100
101impl OutpointFilter for Option<&[Outpoint]> {
102 fn include_outpoint(&self, outpoint: Outpoint) -> bool {
103 self.map(|filter| filter.include_outpoint(outpoint))
104 .unwrap_or(true)
105 }
106}
107
108impl OutpointFilter for &[Outpoint] {
109 fn include_outpoint(&self, outpoint: Outpoint) -> bool { self.contains(&outpoint) }
110}
111
112impl OutpointFilter for Vec<Outpoint> {
113 fn include_outpoint(&self, outpoint: Outpoint) -> bool { self.contains(&outpoint) }
114}
115
116impl OutpointFilter for HashSet<Outpoint> {
117 fn include_outpoint(&self, outpoint: Outpoint) -> bool { self.contains(&outpoint) }
118}
119
120impl OutpointFilter for BTreeSet<Outpoint> {
121 fn include_outpoint(&self, outpoint: Outpoint) -> bool { self.contains(&outpoint) }
122}
123
124#[derive(Clone, Eq, PartialEq, Debug)]
127pub struct ContractIface {
128 pub state: ContractState,
129 pub iface: IfaceImpl,
130}
131
132impl ContractIface {
133 pub fn contract_id(&self) -> ContractId { self.state.contract_id() }
134
135 pub fn global(&self, name: impl Into<FieldName>) -> Result<SmallVec<StrictVal>, ContractError> {
140 let name = name.into();
141 let type_system = &self.state.schema.type_system;
142 let type_id = self
143 .iface
144 .global_type(&name)
145 .ok_or(ContractError::FieldNameUnknown(name))?;
146 let type_schema = self
147 .state
148 .schema
149 .global_types
150 .get(&type_id)
151 .expect("schema doesn't match interface");
152 let state = unsafe { self.state.global_unchecked(type_id) };
153 let state = state
154 .into_iter()
155 .map(|revealed| {
156 type_system
157 .strict_deserialize_type(type_schema.sem_id, revealed.as_ref())
158 .map(TypedVal::unbox)
159 })
160 .take(type_schema.max_items as usize)
161 .collect::<Result<Vec<_>, _>>()?;
162 Ok(SmallVec::try_from_iter(state).expect("same or smaller collection size"))
163 }
164
165 pub fn fungible(
166 &self,
167 name: impl Into<FieldName>,
168 filter: &impl OutpointFilter,
169 ) -> Result<LargeVec<FungibleAllocation>, ContractError> {
170 let name = name.into();
171 let type_id = self
172 .iface
173 .assignments_type(&name)
174 .ok_or(ContractError::FieldNameUnknown(name))?;
175 let state = self
176 .state
177 .fungibles()
178 .iter()
179 .filter(|outp| outp.opout.ty == type_id)
180 .filter(|outp| filter.include_outpoint(outp.seal))
181 .map(FungibleAllocation::from);
182 Ok(LargeVec::try_from_iter(state).expect("same or smaller collection size"))
183 }
184
185 pub fn outpoint(
187 &self,
188 _outpoint: Outpoint,
189 ) -> LargeOrdMap<AssignmentType, LargeVec<TypedState>> {
190 todo!()
191 }
192}