Skip to content

Commit

Permalink
fix mismatch between anchored bundles dichotomies in persistence and …
Browse files Browse the repository at this point in the history
…containers
  • Loading branch information
dr-orlovsky committed Oct 12, 2024
1 parent f77e76f commit 8c5ebf9
Show file tree
Hide file tree
Showing 14 changed files with 450 additions and 194 deletions.
282 changes: 264 additions & 18 deletions src/containers/anchors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,36 @@
// limitations under the License.

use std::cmp::Ordering;
use std::vec;

use amplify::ByteArray;
use bp::dbc::opret::OpretProof;
use bp::dbc::tapret::TapretProof;
use bp::dbc::{anchor, Anchor};
use bp::{Tx, Txid};
use bp::{dbc, Tx, Txid};
use commit_verify::mpc;
use rgb::validation::DbcProof;
use rgb::{BundleId, DiscloseHash, TransitionBundle, XChain, XWitnessId};
use rgb::validation::{DbcProof, EAnchor};
use rgb::{
BundleId, DiscloseHash, OpId, Operation, Transition, TransitionBundle, XChain, XGraphSeal,
XWitnessId,
};
use strict_encoding::StrictDumb;

use crate::{MergeReveal, MergeRevealError, LIB_NAME_RGB_STD};
use crate::containers::Dichotomy;
use crate::{MergeReveal, MergeRevealError, TypedAssignsExt, LIB_NAME_RGB_STD};

#[derive(Clone, Eq, PartialEq, Debug, Display, Error)]
#[display("state transition {0} is not a part of the bundle.")]
pub struct UnrelatedTransition(OpId, Transition);

#[derive(Copy, Clone, Eq, PartialEq, Debug, Display, Error)]
#[display(doc_comments)]
pub enum AnchoredBundleMismatch {
/// witness bundle for witness id {0} already has both opret and tapret information.
AlreadyDouble(XWitnessId),
/// the combined anchored bundles for witness id {0} are of the same type.
SameBundleType(XWitnessId),
}

#[derive(Clone, Eq, PartialEq, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
Expand Down Expand Up @@ -159,36 +177,264 @@ impl PubWitness {
)]
#[derive(CommitEncode)]
#[commit_encode(strategy = strict, id = DiscloseHash)]
pub struct WitnessBundle<P: mpc::Proof + StrictDumb = mpc::MerkleProof> {
pub struct WitnessBundle {
pub pub_witness: XPubWitness,
pub anchor: Anchor<P, DbcProof>,
pub bundle: TransitionBundle,
pub anchored_bundles: AnchoredBundles,
}

impl<P: mpc::Proof + StrictDumb> PartialEq for WitnessBundle<P> {
impl PartialEq for WitnessBundle {
fn eq(&self, other: &Self) -> bool { self.pub_witness == other.pub_witness }
}

impl<P: mpc::Proof + StrictDumb> Ord for WitnessBundle<P> {
impl Ord for WitnessBundle {
fn cmp(&self, other: &Self) -> Ordering { self.pub_witness.cmp(&other.pub_witness) }
}

impl<P: mpc::Proof + StrictDumb> PartialOrd for WitnessBundle<P> {
impl PartialOrd for WitnessBundle {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
}

impl WitnessBundle<mpc::MerkleProof> {
impl WitnessBundle {
#[inline]
pub fn with(pub_witness: XPubWitness, anchored_bundle: AnchoredBundle) -> Self {
Self {
pub_witness,
anchored_bundles: AnchoredBundles::from(anchored_bundle),
}
}

Check warning on line 204 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L199-L204

Added lines #L199 - L204 were not covered by tests

pub fn into_double(mut self, other: AnchoredBundle) -> Result<Self, AnchoredBundleMismatch> {
match (self.anchored_bundles, other.anchor.dbc_proof) {

Check warning on line 207 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L206-L207

Added lines #L206 - L207 were not covered by tests
(AnchoredBundles::Double { .. }, _) => {
return Err(AnchoredBundleMismatch::AlreadyDouble(
self.pub_witness.to_witness_id(),
));

Check warning on line 211 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L209-L211

Added lines #L209 - L211 were not covered by tests
}
(AnchoredBundles::Opret(opret), DbcProof::Tapret(tapret)) => {
let anchor = Anchor::new(other.anchor.mpc_proof, tapret);
self.anchored_bundles = AnchoredBundles::Double {
tapret: AnchoredBundle::new(anchor, other.bundle),
opret,
}

Check warning on line 218 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L213-L218

Added lines #L213 - L218 were not covered by tests
}
(AnchoredBundles::Tapret(tapret), DbcProof::Opret(opret)) => {
let anchor = Anchor::new(other.anchor.mpc_proof, opret);
self.anchored_bundles = AnchoredBundles::Double {
opret: AnchoredBundle::new(anchor, other.bundle),
tapret,
}

Check warning on line 225 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L220-L225

Added lines #L220 - L225 were not covered by tests
}
_ => {
return Err(AnchoredBundleMismatch::SameBundleType(
self.pub_witness.to_witness_id(),
));

Check warning on line 230 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L228-L230

Added lines #L228 - L230 were not covered by tests
}
}
Ok(self)
}

Check warning on line 234 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L233-L234

Added lines #L233 - L234 were not covered by tests

pub fn witness_id(&self) -> XWitnessId { self.pub_witness.to_witness_id() }

pub fn reveal_seal(&mut self, bundle_id: BundleId, seal: XGraphSeal) -> bool {
let bundle = match &mut self.anchored_bundles {
AnchoredBundles::Tapret(tapret) | AnchoredBundles::Double { tapret, .. }
if tapret.bundle.bundle_id() == bundle_id =>

Check warning on line 241 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L238-L241

Added lines #L238 - L241 were not covered by tests
{
Some(&mut tapret.bundle)

Check warning on line 243 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L243

Added line #L243 was not covered by tests
}
AnchoredBundles::Opret(opret) | AnchoredBundles::Double { opret, .. }
if opret.bundle.bundle_id() == bundle_id =>

Check warning on line 246 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L245-L246

Added lines #L245 - L246 were not covered by tests
{
Some(&mut opret.bundle)

Check warning on line 248 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L248

Added line #L248 was not covered by tests
}
_ => None,

Check warning on line 250 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L250

Added line #L250 was not covered by tests
};
let Some(bundle) = bundle else {
return false;

Check warning on line 253 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L252-L253

Added lines #L252 - L253 were not covered by tests
};
bundle
.known_transitions
.values_mut()
.flat_map(|t| t.assignments.values_mut())
.for_each(|a| a.reveal_seal(seal));

true
}

Check warning on line 262 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L255-L262

Added lines #L255 - L262 were not covered by tests

pub fn anchored_bundles(&self) -> impl Iterator<Item = (EAnchor, &TransitionBundle)> {
self.anchored_bundles.iter()
}

Check warning on line 266 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L264-L266

Added lines #L264 - L266 were not covered by tests

#[inline]
pub fn known_transitions(&self) -> impl Iterator<Item = &Transition> {
self.anchored_bundles
.bundles()
.flat_map(|bundle| bundle.known_transitions.values())
}

Check warning on line 273 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L269-L273

Added lines #L269 - L273 were not covered by tests
}

impl WitnessBundle {
pub fn merge_reveal(mut self, other: Self) -> Result<Self, MergeRevealError> {
self.pub_witness = self.pub_witness.merge_reveal(other.pub_witness)?;
if self.anchor != other.anchor {
return Err(MergeRevealError::AnchorsNonEqual(self.bundle.bundle_id()));
/// Struct ensuring invariant between anchor and transition bundle methods.
#[derive(Clone, PartialEq, Eq, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB_STD)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),

Check warning on line 282 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L282

Added line #L282 was not covered by tests
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct AnchoredBundle<D: dbc::Proof = DbcProof> {
anchor: Anchor<mpc::MerkleProof, D>,
bundle: TransitionBundle,
}

impl<D: dbc::Proof> AnchoredBundle<D> {
/// # Panics
///
/// Panics if anchor and bundle have different closing methods
pub fn new(anchor: Anchor<mpc::MerkleProof, D>, bundle: TransitionBundle) -> Self {
assert_eq!(anchor.method, bundle.close_method);
Self { anchor, bundle }
}

Check warning on line 297 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L294-L297

Added lines #L294 - L297 were not covered by tests

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

Check warning on line 300 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L300

Added line #L300 was not covered by tests

pub fn reveal_transition(
&mut self,
transition: Transition,
) -> Result<bool, UnrelatedTransition> {
let opid = transition.id();
if self.bundle.input_map.values().all(|id| *id != opid) {
return Err(UnrelatedTransition(opid, transition));

Check warning on line 308 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L302-L308

Added lines #L302 - L308 were not covered by tests
}
self.bundle = self.bundle.merge_reveal(other.bundle)?;
Ok(self)
if self.bundle.known_transitions.contains_key(&opid) {
return Ok(false);
}
self.bundle
.known_transitions
.insert(opid, transition)
.expect("same size as input map");
Ok(true)
}

Check warning on line 318 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L310-L318

Added lines #L310 - L318 were not covered by tests
}

#[derive(Clone, PartialEq, Eq, Debug)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB_STD, tags = custom)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),

Check warning on line 326 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L326

Added line #L326 was not covered by tests
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub enum AnchoredBundles {
#[strict_type(tag = 0x01)]
Tapret(AnchoredBundle<TapretProof>),
#[strict_type(tag = 0x02)]
Opret(AnchoredBundle<OpretProof>),
#[strict_type(tag = 0x03)]
Double {
tapret: AnchoredBundle<TapretProof>,
opret: AnchoredBundle<OpretProof>,
},
}

impl StrictDumb for AnchoredBundles {
fn strict_dumb() -> Self { Self::Opret(strict_dumb!()) }
}

impl From<AnchoredBundle> for AnchoredBundles {
fn from(ab: AnchoredBundle) -> Self {
match ab.anchor.dbc_proof {
DbcProof::Opret(proof) => Self::Opret(AnchoredBundle::<OpretProof>::new(
Anchor::new(ab.anchor.mpc_proof, proof),
ab.bundle,
)),
DbcProof::Tapret(proof) => Self::Tapret(AnchoredBundle::<TapretProof>::new(
Anchor::new(ab.anchor.mpc_proof, proof),
ab.bundle,
)),

Check warning on line 355 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L346-L355

Added lines #L346 - L355 were not covered by tests
}
}

Check warning on line 357 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L357

Added line #L357 was not covered by tests
}

impl AnchoredBundles {
pub fn bundles(&self) -> impl Iterator<Item = &TransitionBundle> {
match self {
AnchoredBundles::Tapret(tapret) => Dichotomy::single(&tapret.bundle),
AnchoredBundles::Opret(opret) => Dichotomy::single(&opret.bundle),
AnchoredBundles::Double { tapret, opret } => {
Dichotomy::double(&tapret.bundle, &opret.bundle)

Check warning on line 366 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L361-L366

Added lines #L361 - L366 were not covered by tests
}
}
.into_iter()
}

Check warning on line 370 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L369-L370

Added lines #L369 - L370 were not covered by tests

pub fn into_bundles(self) -> impl Iterator<Item = TransitionBundle> {
match self {
AnchoredBundles::Tapret(tapret) => Dichotomy::single(tapret.bundle),
AnchoredBundles::Opret(opret) => Dichotomy::single(opret.bundle),
AnchoredBundles::Double { tapret, opret } => {
Dichotomy::double(tapret.bundle, opret.bundle)

Check warning on line 377 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L372-L377

Added lines #L372 - L377 were not covered by tests
}
}
.into_iter()
}

Check warning on line 381 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L380-L381

Added lines #L380 - L381 were not covered by tests

pub fn iter(&self) -> impl Iterator<Item = (EAnchor, &TransitionBundle)> {
match self {
AnchoredBundles::Tapret(tapret) => {
let anchor = EAnchor::new(
tapret.anchor.mpc_proof.clone(),
tapret.anchor.dbc_proof.clone().into(),
);
Dichotomy::single((anchor, &tapret.bundle))

Check warning on line 390 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L383-L390

Added lines #L383 - L390 were not covered by tests
}
AnchoredBundles::Opret(opret) => {
let anchor = EAnchor::new(
opret.anchor.mpc_proof.clone(),
opret.anchor.dbc_proof.clone().into(),
);
Dichotomy::single((anchor, &opret.bundle))

Check warning on line 397 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L392-L397

Added lines #L392 - L397 were not covered by tests
}
AnchoredBundles::Double { tapret, opret } => {
let tapret_anchor = EAnchor::new(
tapret.anchor.mpc_proof.clone(),
tapret.anchor.dbc_proof.clone().into(),
);
let opret_anchor = EAnchor::new(
opret.anchor.mpc_proof.clone(),
opret.anchor.dbc_proof.clone().into(),
);
Dichotomy::double((tapret_anchor, &tapret.bundle), (opret_anchor, &opret.bundle))

Check warning on line 408 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L399-L408

Added lines #L399 - L408 were not covered by tests
}
}
.into_iter()
}

Check warning on line 412 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L411-L412

Added lines #L411 - L412 were not covered by tests
}

impl IntoIterator for AnchoredBundles {
type Item = (EAnchor, TransitionBundle);
type IntoIter = vec::IntoIter<(EAnchor, TransitionBundle)>;

fn into_iter(self) -> Self::IntoIter {
match self {
AnchoredBundles::Tapret(tapret) => {
let anchor = EAnchor::new(tapret.anchor.mpc_proof, tapret.anchor.dbc_proof.into());
Dichotomy::single((anchor, tapret.bundle))

Check warning on line 423 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L419-L423

Added lines #L419 - L423 were not covered by tests
}
AnchoredBundles::Opret(opret) => {
let anchor = EAnchor::new(opret.anchor.mpc_proof, opret.anchor.dbc_proof.into());
Dichotomy::single((anchor, opret.bundle))

Check warning on line 427 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L425-L427

Added lines #L425 - L427 were not covered by tests
}
AnchoredBundles::Double { tapret, opret } => {
let tapret_anchor =
EAnchor::new(tapret.anchor.mpc_proof, tapret.anchor.dbc_proof.into());
let opret_anchor =
EAnchor::new(opret.anchor.mpc_proof, opret.anchor.dbc_proof.into());
Dichotomy::double((tapret_anchor, tapret.bundle), (opret_anchor, opret.bundle))

Check warning on line 434 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L429-L434

Added lines #L429 - L434 were not covered by tests
}
}
.into_iter()

Check warning on line 437 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L437

Added line #L437 was not covered by tests
}
}

Expand Down
5 changes: 2 additions & 3 deletions src/containers/consignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ use super::{
use crate::interface::{Iface, IfaceImpl};
use crate::persistence::{MemContract, MemContractState};
use crate::resolvers::ConsignmentResolver;
use crate::{BundleExt, SecretSeal, LIB_NAME_RGB_STD};
use crate::{SecretSeal, LIB_NAME_RGB_STD};

pub type Transfer = Consignment<true>;
pub type Contract = Consignment<false>;
Expand Down Expand Up @@ -296,8 +296,7 @@ impl<const TRANSFER: bool> Consignment<TRANSFER> {
for mut witness_bundle in self.bundles {
for (bundle_id, secret) in &self.terminals {
if let Some(seal) = f(*secret)? {
if witness_bundle.bundle.bundle_id() == *bundle_id {
witness_bundle.bundle.reveal_seal(seal);
if witness_bundle.reveal_seal(*bundle_id, seal) {

Check warning on line 299 in src/containers/consignment.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/consignment.rs#L299

Added line #L299 was not covered by tests
break;
}
}
Expand Down
19 changes: 10 additions & 9 deletions src/containers/indexed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use crate::containers::anchors::ToWitnessId;
pub struct IndexedConsignment<'c, const TRANSFER: bool> {
consignment: &'c Consignment<TRANSFER>,
scripts: Scripts,
anchor_idx: BTreeMap<BundleId, (XWitnessId, &'c EAnchor)>,
anchor_idx: BTreeMap<BundleId, (XWitnessId, EAnchor)>,
bundle_idx: BTreeMap<BundleId, &'c TransitionBundle>,
op_witness_idx: BTreeMap<OpId, XWitnessId>,
op_bundle_idx: BTreeMap<OpId, BundleId>,
Expand All @@ -61,14 +61,15 @@ impl<'c, const TRANSFER: bool> IndexedConsignment<'c, TRANSFER> {
for witness_bundle in &consignment.bundles {
witness_idx
.insert(witness_bundle.pub_witness.to_witness_id(), &witness_bundle.pub_witness);
let bundle = &witness_bundle.bundle;
let bundle_id = bundle.bundle_id();
let witness_id = witness_bundle.pub_witness.to_witness_id();
bundle_idx.insert(bundle_id, bundle);
anchor_idx.insert(bundle_id, (witness_id, &witness_bundle.anchor));
for opid in witness_bundle.bundle.known_transitions.keys() {
op_witness_idx.insert(*opid, witness_id);
op_bundle_idx.insert(*opid, bundle_id);
for (anchor, bundle) in witness_bundle.anchored_bundles() {
let bundle_id = bundle.bundle_id();
bundle_idx.insert(bundle_id, bundle);
anchor_idx.insert(bundle_id, (witness_id, anchor));
for opid in bundle.known_transitions.keys() {
op_witness_idx.insert(*opid, witness_id);
op_bundle_idx.insert(*opid, bundle_id);
}

Check warning on line 72 in src/containers/indexed.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/indexed.rs#L65-L72

Added lines #L65 - L72 were not covered by tests
}
}
for extension in &consignment.extensions {
Expand Down Expand Up @@ -137,7 +138,7 @@ impl<'c, const TRANSFER: bool> ConsignmentApi for IndexedConsignment<'c, TRANSFE
}

fn anchor(&self, bundle_id: BundleId) -> Option<(XWitnessId, &EAnchor)> {
self.anchor_idx.get(&bundle_id).map(|(id, set)| (*id, *set))
self.anchor_idx.get(&bundle_id).map(|(id, set)| (*id, set))

Check warning on line 141 in src/containers/indexed.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/indexed.rs#L141

Added line #L141 was not covered by tests
}

fn op_witness_id(&self, opid: OpId) -> Option<XWitnessId> {
Expand Down
5 changes: 4 additions & 1 deletion src/containers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ mod file;
mod kit;
mod suppl;

pub use anchors::{AnchorSet, PubWitness, SealWitness, ToWitnessId, WitnessBundle, XPubWitness};
pub use anchors::{
AnchorSet, AnchoredBundle, AnchoredBundleMismatch, AnchoredBundles, PubWitness, SealWitness,
ToWitnessId, UnrelatedTransition, WitnessBundle, XPubWitness,
};
pub use consignment::{
Consignment, ConsignmentExt, ConsignmentId, ConsignmentParseError, Contract, Transfer,
ValidConsignment, ValidContract, ValidTransfer,
Expand Down
Loading

0 comments on commit 8c5ebf9

Please sign in to comment.