Skip to content

Commit ea12425

Browse files
authored
Merge pull request #194 from RGB-WG/validation
Validation revision
2 parents 709363e + 81c58f2 commit ea12425

File tree

16 files changed

+1041
-902
lines changed

16 files changed

+1041
-902
lines changed

src/contract/anchor.rs

Lines changed: 191 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,15 @@
2121
// limitations under the License.
2222

2323
use std::cmp::Ordering;
24-
use std::ops::Deref;
2524

26-
use bp::dbc;
27-
use bp::dbc::anchor::MergeError;
25+
use bp::dbc::opret::OpretProof;
26+
use bp::dbc::tapret::TapretProof;
27+
use bp::dbc::Anchor;
28+
use bp::Txid;
2829
use commit_verify::mpc;
2930
use strict_encoding::StrictDumb;
3031

31-
use crate::{TransitionBundle, WitnessId, WitnessOrd, LIB_NAME_RGB};
32+
use crate::{BundleId, ContractId, TransitionBundle, WitnessId, WitnessOrd, LIB_NAME_RGB};
3233

3334
#[derive(Clone, Eq, PartialEq, Debug)]
3435
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
@@ -39,10 +40,15 @@ use crate::{TransitionBundle, WitnessId, WitnessOrd, LIB_NAME_RGB};
3940
serde(crate = "serde_crate", rename_all = "camelCase")
4041
)]
4142
pub struct AnchoredBundle {
42-
pub anchor: Anchor,
43+
pub anchor: XAnchor,
4344
pub bundle: TransitionBundle,
4445
}
4546

47+
impl AnchoredBundle {
48+
#[inline]
49+
pub fn bundle_id(&self) -> BundleId { self.bundle.bundle_id() }
50+
}
51+
4652
#[derive(Clone, Eq, PartialEq, Debug)]
4753
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
4854
#[strict_type(lib = LIB_NAME_RGB, tags = custom, dumb = Self::Bitcoin(strict_dumb!()))]
@@ -51,65 +57,212 @@ pub struct AnchoredBundle {
5157
derive(Serialize, Deserialize),
5258
serde(crate = "serde_crate", rename_all = "camelCase")
5359
)]
54-
pub enum Anchor<P: mpc::Proof + StrictDumb = mpc::MerkleProof> {
60+
pub enum XAnchor<P: mpc::Proof + StrictDumb = mpc::MerkleProof> {
5561
#[strict_type(tag = 0x00)]
56-
Bitcoin(dbc::Anchor<P>),
62+
Bitcoin(AnchorSet<P>),
5763

5864
#[strict_type(tag = 0x01)]
59-
Liquid(dbc::Anchor<P>),
65+
Liquid(AnchorSet<P>),
6066
}
6167

62-
impl<P: mpc::Proof + StrictDumb> Deref for Anchor<P> {
63-
type Target = dbc::Anchor<P>;
64-
68+
impl<P: mpc::Proof + StrictDumb> XAnchor<P> {
6569
#[inline]
66-
fn deref(&self) -> &Self::Target {
70+
pub fn layer1(&self) -> Layer1 {
6771
match self {
68-
Anchor::Bitcoin(anchor) | Anchor::Liquid(anchor) => anchor,
72+
XAnchor::Bitcoin(_) => Layer1::Bitcoin,
73+
XAnchor::Liquid(_) => Layer1::Liquid,
6974
}
7075
}
71-
}
7276

73-
impl<P: mpc::Proof + StrictDumb> Anchor<P> {
7477
#[inline]
75-
pub fn layer1(&self) -> Layer1 {
78+
pub fn witness_id(&self) -> WitnessId {
7679
match self {
77-
Anchor::Bitcoin(_) => Layer1::Bitcoin,
78-
Anchor::Liquid(_) => Layer1::Liquid,
80+
XAnchor::Bitcoin(anchor) => WitnessId::Bitcoin(anchor.txid_unchecked()),
81+
XAnchor::Liquid(anchor) => WitnessId::Liquid(anchor.txid_unchecked()),
7982
}
8083
}
8184

82-
#[inline]
83-
pub fn witness_id(&self) -> WitnessId {
85+
fn map<Q: mpc::Proof + StrictDumb, E>(
86+
self,
87+
f: impl FnOnce(AnchorSet<P>) -> Result<AnchorSet<Q>, E>,
88+
) -> Result<XAnchor<Q>, E> {
8489
match self {
85-
Anchor::Bitcoin(anchor) => WitnessId::Bitcoin(anchor.txid),
86-
Anchor::Liquid(anchor) => WitnessId::Liquid(anchor.txid),
90+
XAnchor::Bitcoin(anchor) => f(anchor).map(XAnchor::Bitcoin),
91+
XAnchor::Liquid(anchor) => f(anchor).map(XAnchor::Liquid),
8792
}
8893
}
94+
}
8995

90-
pub fn map<Q: mpc::Proof + StrictDumb, E>(
91-
self,
92-
f: impl FnOnce(dbc::Anchor<P>) -> Result<dbc::Anchor<Q>, E>,
93-
) -> Result<Anchor<Q>, E> {
96+
impl XAnchor<mpc::MerkleBlock> {
97+
pub fn known_bundle_ids(&self) -> impl Iterator<Item = (BundleId, ContractId)> + '_ {
9498
match self {
95-
Anchor::Bitcoin(anchor) => f(anchor).map(Anchor::Bitcoin),
96-
Anchor::Liquid(anchor) => f(anchor).map(Anchor::Liquid),
99+
XAnchor::Bitcoin(anchor) | XAnchor::Liquid(anchor) => anchor.known_bundle_ids(),
97100
}
98101
}
102+
103+
pub fn to_merkle_proof(
104+
&self,
105+
contract_id: ContractId,
106+
) -> Result<XAnchor<mpc::MerkleProof>, mpc::LeafNotKnown> {
107+
self.clone().into_merkle_proof(contract_id)
108+
}
109+
110+
pub fn into_merkle_proof(
111+
self,
112+
contract_id: ContractId,
113+
) -> Result<XAnchor<mpc::MerkleProof>, mpc::LeafNotKnown> {
114+
self.map(|a| a.into_merkle_proof(contract_id))
115+
}
116+
}
117+
118+
impl XAnchor<mpc::MerkleProof> {
119+
pub fn to_merkle_block(
120+
&self,
121+
contract_id: ContractId,
122+
bundle_id: BundleId,
123+
) -> Result<XAnchor<mpc::MerkleBlock>, mpc::InvalidProof> {
124+
self.clone().into_merkle_block(contract_id, bundle_id)
125+
}
126+
127+
pub fn into_merkle_block(
128+
self,
129+
contract_id: ContractId,
130+
bundle_id: BundleId,
131+
) -> Result<XAnchor<mpc::MerkleBlock>, mpc::InvalidProof> {
132+
self.map(|a| a.into_merkle_block(contract_id, bundle_id))
133+
}
99134
}
100135

101-
impl Anchor<mpc::MerkleBlock> {
102-
pub fn merge_reveal(self, other: Self) -> Result<Self, MergeError> {
103-
match (self, other) {
104-
(Anchor::Bitcoin(anchor), Anchor::Bitcoin(other)) => {
105-
anchor.merge_reveal(other).map(Anchor::Bitcoin)
106-
}
107-
(Anchor::Liquid(anchor), Anchor::Liquid(other)) => {
108-
anchor.merge_reveal(other).map(Anchor::Liquid)
109-
}
110-
_ => Err(MergeError::ProofMismatch),
136+
#[derive(Clone, Eq, PartialEq, Debug)]
137+
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
138+
#[strict_type(lib = LIB_NAME_RGB, tags = custom, dumb = Self::Tapret(strict_dumb!()))]
139+
#[cfg_attr(
140+
feature = "serde",
141+
derive(Serialize, Deserialize),
142+
serde(crate = "serde_crate", rename_all = "camelCase")
143+
)]
144+
pub enum AnchorSet<P: mpc::Proof + StrictDumb = mpc::MerkleProof> {
145+
#[strict_type(tag = 0x01)]
146+
Tapret(Anchor<P, TapretProof>),
147+
#[strict_type(tag = 0x02)]
148+
Opret(Anchor<P, OpretProof>),
149+
#[strict_type(tag = 0x03)]
150+
Dual {
151+
tapret: Anchor<P, TapretProof>,
152+
opret: Anchor<P, OpretProof>,
153+
},
154+
}
155+
156+
impl<P: mpc::Proof + StrictDumb> AnchorSet<P> {
157+
pub fn txid(&self) -> Option<Txid> {
158+
match self {
159+
AnchorSet::Tapret(a) => Some(a.txid),
160+
AnchorSet::Opret(a) => Some(a.txid),
161+
AnchorSet::Dual { tapret, opret } if tapret.txid == opret.txid => Some(tapret.txid),
162+
_ => None,
163+
}
164+
}
165+
166+
pub(crate) fn txid_unchecked(&self) -> Txid {
167+
match self {
168+
AnchorSet::Tapret(a) => a.txid,
169+
AnchorSet::Opret(a) => a.txid,
170+
AnchorSet::Dual { tapret, .. } => tapret.txid,
171+
}
172+
}
173+
174+
pub fn from_split(
175+
tapret: Option<Anchor<P, TapretProof>>,
176+
opret: Option<Anchor<P, OpretProof>>,
177+
) -> Option<Self> {
178+
Some(match (tapret, opret) {
179+
(Some(tapret), Some(opret)) => Self::Dual { tapret, opret },
180+
(Some(tapret), None) => Self::Tapret(tapret),
181+
(None, Some(opret)) => Self::Opret(opret),
182+
(None, None) => return None,
183+
})
184+
}
185+
186+
#[allow(clippy::type_complexity)]
187+
pub fn as_split(&self) -> (Option<&Anchor<P, TapretProof>>, Option<&Anchor<P, OpretProof>>) {
188+
match self {
189+
AnchorSet::Tapret(tapret) => (Some(tapret), None),
190+
AnchorSet::Opret(opret) => (None, Some(opret)),
191+
AnchorSet::Dual { tapret, opret } => (Some(tapret), Some(opret)),
192+
}
193+
}
194+
195+
#[allow(clippy::type_complexity)]
196+
pub fn into_split(self) -> (Option<Anchor<P, TapretProof>>, Option<Anchor<P, OpretProof>>) {
197+
match self {
198+
AnchorSet::Tapret(tapret) => (Some(tapret), None),
199+
AnchorSet::Opret(opret) => (None, Some(opret)),
200+
AnchorSet::Dual { tapret, opret } => (Some(tapret), Some(opret)),
111201
}
112202
}
203+
204+
pub fn mpc_proofs(&self) -> impl Iterator<Item = &P> {
205+
let (t, o) = self.as_split();
206+
t.map(|a| &a.mpc_proof)
207+
.into_iter()
208+
.chain(o.map(|a| &a.mpc_proof))
209+
}
210+
}
211+
212+
impl AnchorSet<mpc::MerkleProof> {
213+
pub fn to_merkle_block(
214+
&self,
215+
contract_id: ContractId,
216+
bundle_id: BundleId,
217+
) -> Result<AnchorSet<mpc::MerkleBlock>, mpc::InvalidProof> {
218+
self.clone().into_merkle_block(contract_id, bundle_id)
219+
}
220+
221+
pub fn into_merkle_block(
222+
self,
223+
contract_id: ContractId,
224+
bundle_id: BundleId,
225+
) -> Result<AnchorSet<mpc::MerkleBlock>, mpc::InvalidProof> {
226+
let (tapret, opret) = self.into_split();
227+
let tapret = tapret
228+
.map(|t| t.into_merkle_block(contract_id, bundle_id))
229+
.transpose()?;
230+
let opret = opret
231+
.map(|o| o.into_merkle_block(contract_id, bundle_id))
232+
.transpose()?;
233+
Ok(AnchorSet::from_split(tapret, opret).expect("one must be non-None"))
234+
}
235+
}
236+
237+
impl AnchorSet<mpc::MerkleBlock> {
238+
pub fn known_bundle_ids(&self) -> impl Iterator<Item = (BundleId, ContractId)> + '_ {
239+
self.mpc_proofs().flat_map(|p| {
240+
p.to_known_message_map()
241+
.into_iter()
242+
.map(|(p, m)| (m.into(), p.into()))
243+
})
244+
}
245+
246+
pub fn to_merkle_proof(
247+
&self,
248+
contract_id: ContractId,
249+
) -> Result<AnchorSet<mpc::MerkleProof>, mpc::LeafNotKnown> {
250+
self.clone().into_merkle_proof(contract_id)
251+
}
252+
253+
pub fn into_merkle_proof(
254+
self,
255+
contract_id: ContractId,
256+
) -> Result<AnchorSet<mpc::MerkleProof>, mpc::LeafNotKnown> {
257+
let (tapret, opret) = self.into_split();
258+
let tapret = tapret
259+
.map(|t| t.into_merkle_proof(contract_id))
260+
.transpose()?;
261+
let opret = opret
262+
.map(|o| o.into_merkle_proof(contract_id))
263+
.transpose()?;
264+
Ok(AnchorSet::from_split(tapret, opret).expect("one must be non-None"))
265+
}
113266
}
114267

115268
/// Txid and height information ordered according to the RGB consensus rules.

src/contract/assignments.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use super::ExposedState;
3535
use crate::contract::seal::GenesisSeal;
3636
use crate::{
3737
AssignmentType, ExposedSeal, GraphSeal, RevealedAttach, RevealedData, RevealedValue,
38-
SecretSeal, StateType, VoidState, Xchain, LIB_NAME_RGB,
38+
SecretSeal, StateType, VoidState, XSeal, LIB_NAME_RGB,
3939
};
4040

4141
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Display, Error)]
@@ -76,12 +76,12 @@ pub enum Assign<State: ExposedState, Seal: ExposedSeal> {
7676
state: State::Confidential,
7777
},
7878
#[strict_type(tag = 0x03)]
79-
Revealed { seal: Xchain<Seal>, state: State },
79+
Revealed { seal: XSeal<Seal>, state: State },
8080
#[strict_type(tag = 0x02)]
8181
ConfidentialSeal { seal: SecretSeal, state: State },
8282
#[strict_type(tag = 0x01)]
8383
ConfidentialState {
84-
seal: Xchain<Seal>,
84+
seal: XSeal<Seal>,
8585
state: State::Confidential,
8686
},
8787
}
@@ -118,9 +118,9 @@ impl<State: ExposedState, Seal: ExposedSeal> Hash for Assign<State, Seal> {
118118
}
119119

120120
impl<State: ExposedState, Seal: ExposedSeal> Assign<State, Seal> {
121-
pub fn revealed(seal: Xchain<Seal>, state: State) -> Self { Assign::Revealed { seal, state } }
121+
pub fn revealed(seal: XSeal<Seal>, state: State) -> Self { Assign::Revealed { seal, state } }
122122

123-
pub fn with_seal_replaced(assignment: &Self, seal: Xchain<Seal>) -> Self {
123+
pub fn with_seal_replaced(assignment: &Self, seal: XSeal<Seal>) -> Self {
124124
match assignment {
125125
Assign::Confidential { seal: _, state } |
126126
Assign::ConfidentialState { seal: _, state } => Assign::ConfidentialState {
@@ -145,7 +145,7 @@ impl<State: ExposedState, Seal: ExposedSeal> Assign<State, Seal> {
145145
}
146146
}
147147

148-
pub fn revealed_seal(&self) -> Option<Xchain<Seal>> {
148+
pub fn revealed_seal(&self) -> Option<XSeal<Seal>> {
149149
match self {
150150
Assign::Revealed { seal, .. } | Assign::ConfidentialState { seal, .. } => Some(*seal),
151151
Assign::Confidential { .. } | Assign::ConfidentialSeal { .. } => None,
@@ -182,21 +182,21 @@ impl<State: ExposedState, Seal: ExposedSeal> Assign<State, Seal> {
182182
}
183183
}
184184

185-
pub fn as_revealed(&self) -> Option<(&Xchain<Seal>, &State)> {
185+
pub fn as_revealed(&self) -> Option<(&XSeal<Seal>, &State)> {
186186
match self {
187187
Assign::Revealed { seal, state } => Some((seal, state)),
188188
_ => None,
189189
}
190190
}
191191

192-
pub fn to_revealed(&self) -> Option<(Xchain<Seal>, State)> {
192+
pub fn to_revealed(&self) -> Option<(XSeal<Seal>, State)> {
193193
match self {
194194
Assign::Revealed { seal, state } => Some((*seal, state.clone())),
195195
_ => None,
196196
}
197197
}
198198

199-
pub fn into_revealed(self) -> Option<(Xchain<Seal>, State)> {
199+
pub fn into_revealed(self) -> Option<(XSeal<Seal>, State)> {
200200
match self {
201201
Assign::Revealed { seal, state } => Some((seal, state)),
202202
_ => None,
@@ -445,7 +445,7 @@ impl<Seal: ExposedSeal> TypedAssigns<Seal> {
445445
/// If seal definition does not exist, returns [`UnknownDataError`]. If the
446446
/// seal is confidential, returns `Ok(None)`; otherwise returns revealed
447447
/// seal data packed as `Ok(Some(`[`Seal`]`))`
448-
pub fn revealed_seal_at(&self, index: u16) -> Result<Option<Xchain<Seal>>, UnknownDataError> {
448+
pub fn revealed_seal_at(&self, index: u16) -> Result<Option<XSeal<Seal>>, UnknownDataError> {
449449
Ok(match self {
450450
TypedAssigns::Declarative(vec) => vec
451451
.get(index as usize)

0 commit comments

Comments
 (0)