Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix use of anchor/bundle opret/tapret dichotomies #277

Merged
merged 6 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 5 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,10 @@ wasm-bindgen-test = "0.3"

[package.metadata.docs.rs]
features = ["all"]

[patch.crates-io]
bp-consensus = { git = "https://github.com/BP-WG/bp-core", branch = "methods" }
bp-dbc = { git = "https://github.com/BP-WG/bp-core", branch = "methods" }
bp-seals = { git = "https://github.com/BP-WG/bp-core", branch = "methods" }
bp-core = { git = "https://github.com/BP-WG/bp-core", branch = "methods" }
rgb-core = { git = "https://github.com/RGB-WG/rgb-core", branch = "methods" }
274 changes: 256 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,256 @@
)]
#[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: ClientBundle) -> 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: ClientBundle) -> Result<Self, AnchoredBundleMismatch> {
match (self.anchored_bundles, other.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)) => {
self.anchored_bundles = AnchoredBundles::Double {
tapret: ClientBundle::new(other.mpc_proof, tapret, other.bundle),
opret,
}

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L213-L217

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

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L219-L223

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

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L226-L228

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

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L231-L232

Added lines #L231 - L232 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 239 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L236-L239

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

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L241

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

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L243-L244

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

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L246

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

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
};
let Some(bundle) = bundle else {
return false;

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L250-L251

Added lines #L250 - L251 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 260 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L253-L260

Added lines #L253 - L260 were not covered by tests

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

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L262-L264

Added lines #L262 - L264 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 271 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L267-L271

Added lines #L267 - L271 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()));
/// Keeps client-side data - a combination of client-side witness (anchor) and state (transition
/// bundle). Ensures that transition bundle uses the same DBC close method as used by the
/// client-side witness (anchor).
#[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 ClientBundle<D: dbc::Proof = DbcProof> {
mpc_proof: mpc::MerkleProof,
dbc_proof: D,
bundle: TransitionBundle,
}

impl<D: dbc::Proof> ClientBundle<D> {
/// # Panics
///
/// Panics if DBC proof and bundle have different closing methods
pub fn new(mpc_proof: mpc::MerkleProof, dbc_proof: D, bundle: TransitionBundle) -> Self {
assert_eq!(dbc_proof.method(), bundle.close_method);
Self {
mpc_proof,
dbc_proof,
bundle,

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L295-L300

Added lines #L295 - L300 were not covered by tests
}
self.bundle = self.bundle.merge_reveal(other.bundle)?;
Ok(self)
}

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L302

Added line #L302 was not covered by tests

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

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L305

Added line #L305 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));
}
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 323 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L307-L323

Added lines #L307 - L323 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 331 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L331

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

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

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

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L351-L354

Added lines #L351 - L354 were not covered by tests
}
DbcProof::Tapret(proof) => {
Self::Tapret(ClientBundle::<TapretProof>::new(ab.mpc_proof, proof, ab.bundle))

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L356-L357

Added lines #L356 - L357 were not covered by tests
}
}
}

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L360

Added line #L360 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 369 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L364-L369

Added lines #L364 - L369 were not covered by tests
}
}
.into_iter()
}

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L372-L373

Added lines #L372 - L373 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 380 in src/containers/anchors.rs

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L375-L380

Added lines #L375 - L380 were not covered by tests
}
}
.into_iter()
}

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L383-L384

Added lines #L383 - L384 were not covered by tests

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

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L386-L391

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

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L393-L395

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

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L397-L402

Added lines #L397 - L402 were not covered by tests
}
}
.into_iter()
}

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L405-L406

Added lines #L405 - L406 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.mpc_proof, tapret.dbc_proof.into());
Dichotomy::single((anchor, tapret.bundle))

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L413-L417

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

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L419-L421

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

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L423-L426

Added lines #L423 - L426 were not covered by tests
}
}
.into_iter()

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

View check run for this annotation

Codecov / codecov/patch

src/containers/anchors.rs#L429

Added line #L429 was not covered by tests
}
}

Expand Down
7 changes: 2 additions & 5 deletions src/containers/consignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
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,10 +296,7 @@
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);
break;
}
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
}
}
bundles.push(witness_bundle).ok();
Expand Down
Loading
Loading