Skip to content

Commit

Permalink
Merge pull request #194 from RGB-WG/validation
Browse files Browse the repository at this point in the history
Validation revision
  • Loading branch information
dr-orlovsky authored Dec 25, 2023
2 parents 709363e + 81c58f2 commit ea12425
Show file tree
Hide file tree
Showing 16 changed files with 1,041 additions and 902 deletions.
229 changes: 191 additions & 38 deletions src/contract/anchor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@
// limitations under the License.

use std::cmp::Ordering;
use std::ops::Deref;

use bp::dbc;
use bp::dbc::anchor::MergeError;
use bp::dbc::opret::OpretProof;
use bp::dbc::tapret::TapretProof;
use bp::dbc::Anchor;
use bp::Txid;
use commit_verify::mpc;
use strict_encoding::StrictDumb;

use crate::{TransitionBundle, WitnessId, WitnessOrd, LIB_NAME_RGB};
use crate::{BundleId, ContractId, TransitionBundle, WitnessId, WitnessOrd, LIB_NAME_RGB};

#[derive(Clone, Eq, PartialEq, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
Expand All @@ -39,10 +40,15 @@ use crate::{TransitionBundle, WitnessId, WitnessOrd, LIB_NAME_RGB};
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct AnchoredBundle {
pub anchor: Anchor,
pub anchor: XAnchor,
pub bundle: TransitionBundle,
}

impl AnchoredBundle {
#[inline]
pub fn bundle_id(&self) -> BundleId { self.bundle.bundle_id() }
}

#[derive(Clone, Eq, PartialEq, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB, tags = custom, dumb = Self::Bitcoin(strict_dumb!()))]
Expand All @@ -51,65 +57,212 @@ pub struct AnchoredBundle {
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub enum Anchor<P: mpc::Proof + StrictDumb = mpc::MerkleProof> {
pub enum XAnchor<P: mpc::Proof + StrictDumb = mpc::MerkleProof> {
#[strict_type(tag = 0x00)]
Bitcoin(dbc::Anchor<P>),
Bitcoin(AnchorSet<P>),

#[strict_type(tag = 0x01)]
Liquid(dbc::Anchor<P>),
Liquid(AnchorSet<P>),
}

impl<P: mpc::Proof + StrictDumb> Deref for Anchor<P> {
type Target = dbc::Anchor<P>;

impl<P: mpc::Proof + StrictDumb> XAnchor<P> {
#[inline]
fn deref(&self) -> &Self::Target {
pub fn layer1(&self) -> Layer1 {
match self {
Anchor::Bitcoin(anchor) | Anchor::Liquid(anchor) => anchor,
XAnchor::Bitcoin(_) => Layer1::Bitcoin,
XAnchor::Liquid(_) => Layer1::Liquid,
}
}
}

impl<P: mpc::Proof + StrictDumb> Anchor<P> {
#[inline]
pub fn layer1(&self) -> Layer1 {
pub fn witness_id(&self) -> WitnessId {
match self {
Anchor::Bitcoin(_) => Layer1::Bitcoin,
Anchor::Liquid(_) => Layer1::Liquid,
XAnchor::Bitcoin(anchor) => WitnessId::Bitcoin(anchor.txid_unchecked()),
XAnchor::Liquid(anchor) => WitnessId::Liquid(anchor.txid_unchecked()),
}
}

#[inline]
pub fn witness_id(&self) -> WitnessId {
fn map<Q: mpc::Proof + StrictDumb, E>(
self,
f: impl FnOnce(AnchorSet<P>) -> Result<AnchorSet<Q>, E>,
) -> Result<XAnchor<Q>, E> {
match self {
Anchor::Bitcoin(anchor) => WitnessId::Bitcoin(anchor.txid),
Anchor::Liquid(anchor) => WitnessId::Liquid(anchor.txid),
XAnchor::Bitcoin(anchor) => f(anchor).map(XAnchor::Bitcoin),
XAnchor::Liquid(anchor) => f(anchor).map(XAnchor::Liquid),
}
}
}

pub fn map<Q: mpc::Proof + StrictDumb, E>(
self,
f: impl FnOnce(dbc::Anchor<P>) -> Result<dbc::Anchor<Q>, E>,
) -> Result<Anchor<Q>, E> {
impl XAnchor<mpc::MerkleBlock> {
pub fn known_bundle_ids(&self) -> impl Iterator<Item = (BundleId, ContractId)> + '_ {
match self {
Anchor::Bitcoin(anchor) => f(anchor).map(Anchor::Bitcoin),
Anchor::Liquid(anchor) => f(anchor).map(Anchor::Liquid),
XAnchor::Bitcoin(anchor) | XAnchor::Liquid(anchor) => anchor.known_bundle_ids(),
}
}

pub fn to_merkle_proof(
&self,
contract_id: ContractId,
) -> Result<XAnchor<mpc::MerkleProof>, mpc::LeafNotKnown> {
self.clone().into_merkle_proof(contract_id)
}

pub fn into_merkle_proof(
self,
contract_id: ContractId,
) -> Result<XAnchor<mpc::MerkleProof>, mpc::LeafNotKnown> {
self.map(|a| a.into_merkle_proof(contract_id))
}
}

impl XAnchor<mpc::MerkleProof> {
pub fn to_merkle_block(
&self,
contract_id: ContractId,
bundle_id: BundleId,
) -> Result<XAnchor<mpc::MerkleBlock>, mpc::InvalidProof> {
self.clone().into_merkle_block(contract_id, bundle_id)
}

pub fn into_merkle_block(
self,
contract_id: ContractId,
bundle_id: BundleId,
) -> Result<XAnchor<mpc::MerkleBlock>, mpc::InvalidProof> {
self.map(|a| a.into_merkle_block(contract_id, bundle_id))
}
}

impl Anchor<mpc::MerkleBlock> {
pub fn merge_reveal(self, other: Self) -> Result<Self, MergeError> {
match (self, other) {
(Anchor::Bitcoin(anchor), Anchor::Bitcoin(other)) => {
anchor.merge_reveal(other).map(Anchor::Bitcoin)
}
(Anchor::Liquid(anchor), Anchor::Liquid(other)) => {
anchor.merge_reveal(other).map(Anchor::Liquid)
}
_ => Err(MergeError::ProofMismatch),
#[derive(Clone, Eq, PartialEq, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB, tags = custom, dumb = Self::Tapret(strict_dumb!()))]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub enum AnchorSet<P: mpc::Proof + StrictDumb = mpc::MerkleProof> {
#[strict_type(tag = 0x01)]
Tapret(Anchor<P, TapretProof>),
#[strict_type(tag = 0x02)]
Opret(Anchor<P, OpretProof>),
#[strict_type(tag = 0x03)]
Dual {
tapret: Anchor<P, TapretProof>,
opret: Anchor<P, OpretProof>,
},
}

impl<P: mpc::Proof + StrictDumb> AnchorSet<P> {
pub fn txid(&self) -> Option<Txid> {
match self {
AnchorSet::Tapret(a) => Some(a.txid),
AnchorSet::Opret(a) => Some(a.txid),
AnchorSet::Dual { tapret, opret } if tapret.txid == opret.txid => Some(tapret.txid),
_ => None,
}
}

pub(crate) fn txid_unchecked(&self) -> Txid {
match self {
AnchorSet::Tapret(a) => a.txid,
AnchorSet::Opret(a) => a.txid,
AnchorSet::Dual { tapret, .. } => tapret.txid,
}
}

pub fn from_split(
tapret: Option<Anchor<P, TapretProof>>,
opret: Option<Anchor<P, OpretProof>>,
) -> Option<Self> {
Some(match (tapret, opret) {
(Some(tapret), Some(opret)) => Self::Dual { tapret, opret },
(Some(tapret), None) => Self::Tapret(tapret),
(None, Some(opret)) => Self::Opret(opret),
(None, None) => return None,
})
}

#[allow(clippy::type_complexity)]
pub fn as_split(&self) -> (Option<&Anchor<P, TapretProof>>, Option<&Anchor<P, OpretProof>>) {
match self {
AnchorSet::Tapret(tapret) => (Some(tapret), None),
AnchorSet::Opret(opret) => (None, Some(opret)),
AnchorSet::Dual { tapret, opret } => (Some(tapret), Some(opret)),
}
}

#[allow(clippy::type_complexity)]
pub fn into_split(self) -> (Option<Anchor<P, TapretProof>>, Option<Anchor<P, OpretProof>>) {
match self {
AnchorSet::Tapret(tapret) => (Some(tapret), None),
AnchorSet::Opret(opret) => (None, Some(opret)),
AnchorSet::Dual { tapret, opret } => (Some(tapret), Some(opret)),
}
}

pub fn mpc_proofs(&self) -> impl Iterator<Item = &P> {
let (t, o) = self.as_split();
t.map(|a| &a.mpc_proof)
.into_iter()
.chain(o.map(|a| &a.mpc_proof))
}
}

impl AnchorSet<mpc::MerkleProof> {
pub fn to_merkle_block(
&self,
contract_id: ContractId,
bundle_id: BundleId,
) -> Result<AnchorSet<mpc::MerkleBlock>, mpc::InvalidProof> {
self.clone().into_merkle_block(contract_id, bundle_id)
}

pub fn into_merkle_block(
self,
contract_id: ContractId,
bundle_id: BundleId,
) -> Result<AnchorSet<mpc::MerkleBlock>, mpc::InvalidProof> {
let (tapret, opret) = self.into_split();
let tapret = tapret
.map(|t| t.into_merkle_block(contract_id, bundle_id))
.transpose()?;
let opret = opret
.map(|o| o.into_merkle_block(contract_id, bundle_id))
.transpose()?;
Ok(AnchorSet::from_split(tapret, opret).expect("one must be non-None"))
}
}

impl AnchorSet<mpc::MerkleBlock> {
pub fn known_bundle_ids(&self) -> impl Iterator<Item = (BundleId, ContractId)> + '_ {
self.mpc_proofs().flat_map(|p| {
p.to_known_message_map()
.into_iter()
.map(|(p, m)| (m.into(), p.into()))
})
}

pub fn to_merkle_proof(
&self,
contract_id: ContractId,
) -> Result<AnchorSet<mpc::MerkleProof>, mpc::LeafNotKnown> {
self.clone().into_merkle_proof(contract_id)
}

pub fn into_merkle_proof(
self,
contract_id: ContractId,
) -> Result<AnchorSet<mpc::MerkleProof>, mpc::LeafNotKnown> {
let (tapret, opret) = self.into_split();
let tapret = tapret
.map(|t| t.into_merkle_proof(contract_id))
.transpose()?;
let opret = opret
.map(|o| o.into_merkle_proof(contract_id))
.transpose()?;
Ok(AnchorSet::from_split(tapret, opret).expect("one must be non-None"))
}
}

/// Txid and height information ordered according to the RGB consensus rules.
Expand Down
20 changes: 10 additions & 10 deletions src/contract/assignments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use super::ExposedState;
use crate::contract::seal::GenesisSeal;
use crate::{
AssignmentType, ExposedSeal, GraphSeal, RevealedAttach, RevealedData, RevealedValue,
SecretSeal, StateType, VoidState, Xchain, LIB_NAME_RGB,
SecretSeal, StateType, VoidState, XSeal, LIB_NAME_RGB,
};

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Display, Error)]
Expand Down Expand Up @@ -76,12 +76,12 @@ pub enum Assign<State: ExposedState, Seal: ExposedSeal> {
state: State::Confidential,
},
#[strict_type(tag = 0x03)]
Revealed { seal: Xchain<Seal>, state: State },
Revealed { seal: XSeal<Seal>, state: State },
#[strict_type(tag = 0x02)]
ConfidentialSeal { seal: SecretSeal, state: State },
#[strict_type(tag = 0x01)]
ConfidentialState {
seal: Xchain<Seal>,
seal: XSeal<Seal>,
state: State::Confidential,
},
}
Expand Down Expand Up @@ -118,9 +118,9 @@ impl<State: ExposedState, Seal: ExposedSeal> Hash for Assign<State, Seal> {
}

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

pub fn with_seal_replaced(assignment: &Self, seal: Xchain<Seal>) -> Self {
pub fn with_seal_replaced(assignment: &Self, seal: XSeal<Seal>) -> Self {
match assignment {
Assign::Confidential { seal: _, state } |
Assign::ConfidentialState { seal: _, state } => Assign::ConfidentialState {
Expand All @@ -145,7 +145,7 @@ impl<State: ExposedState, Seal: ExposedSeal> Assign<State, Seal> {
}
}

pub fn revealed_seal(&self) -> Option<Xchain<Seal>> {
pub fn revealed_seal(&self) -> Option<XSeal<Seal>> {
match self {
Assign::Revealed { seal, .. } | Assign::ConfidentialState { seal, .. } => Some(*seal),
Assign::Confidential { .. } | Assign::ConfidentialSeal { .. } => None,
Expand Down Expand Up @@ -182,21 +182,21 @@ impl<State: ExposedState, Seal: ExposedSeal> Assign<State, Seal> {
}
}

pub fn as_revealed(&self) -> Option<(&Xchain<Seal>, &State)> {
pub fn as_revealed(&self) -> Option<(&XSeal<Seal>, &State)> {
match self {
Assign::Revealed { seal, state } => Some((seal, state)),
_ => None,
}
}

pub fn to_revealed(&self) -> Option<(Xchain<Seal>, State)> {
pub fn to_revealed(&self) -> Option<(XSeal<Seal>, State)> {
match self {
Assign::Revealed { seal, state } => Some((*seal, state.clone())),
_ => None,
}
}

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

0 comments on commit ea12425

Please sign in to comment.