Skip to content

Commit

Permalink
RCP-240313A: support future SPV proofs by refactoring anchors
Browse files Browse the repository at this point in the history
All non-consensus data has been removed from anchors and consignment API into resolver API, which can now return SPV info etc without consensus-breaking changes
  • Loading branch information
dr-orlovsky committed Mar 30, 2024
1 parent e04cfff commit bde1287
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 102 deletions.
12 changes: 4 additions & 8 deletions Cargo.lock

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

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,9 @@ 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 = "deanchor" }
bp-dbc = { git = "https://github.com/BP-WG/bp-core", branch = "deanchor" }
bp-seals = { git = "https://github.com/BP-WG/bp-core", branch = "deanchor" }
bp-core = { git = "https://github.com/BP-WG/bp-core", branch = "deanchor" }
54 changes: 1 addition & 53 deletions src/contract/anchor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,48 +25,13 @@ use std::cmp::Ordering;
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::{BundleId, ContractId, TransitionBundle, WitnessId, WitnessOrd, XChain, LIB_NAME_RGB};

#[derive(Clone, Eq, PartialEq, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct AnchoredBundle {
pub anchor: XAnchor,
pub bundle: TransitionBundle,
}

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

impl Ord for AnchoredBundle {
fn cmp(&self, other: &Self) -> Ordering { self.bundle_id().cmp(&other.bundle_id()) }
}

impl PartialOrd for AnchoredBundle {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
}
use crate::{BundleId, ContractId, WitnessId, WitnessOrd, XChain, LIB_NAME_RGB};

pub type XAnchor<P = mpc::MerkleProof> = XChain<AnchorSet<P>>;

impl<P: mpc::Proof + StrictDumb> XAnchor<P> {
#[inline]
pub fn witness_id(&self) -> Option<WitnessId> { self.maybe_map_ref(|set| set.txid()) }

#[inline]
pub fn witness_id_unchecked(&self) -> WitnessId { self.map_ref(|set| set.txid_unchecked()) }
}

impl XAnchor<mpc::MerkleBlock> {
pub fn known_bundle_ids(&self) -> impl Iterator<Item = (BundleId, ContractId)> + '_ {
match self {
Expand Down Expand Up @@ -129,23 +94,6 @@ pub enum AnchorSet<P: mpc::Proof + StrictDumb = mpc::MerkleProof> {
}

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 fn txid_unchecked(&self) -> Txid {
match self {
AnchorSet::Tapret(a) => a.txid,
AnchorSet::Opret(a) => a.txid,
AnchorSet::Dual { tapret, opret: _ } => tapret.txid,
}
}

pub fn from_split(
tapret: Option<Anchor<P, TapretProof>>,
opret: Option<Anchor<P, OpretProof>>,
Expand Down
2 changes: 1 addition & 1 deletion src/contract/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ mod contract;
mod xchain;
mod commit;

pub use anchor::{AnchorSet, AnchoredBundle, Layer1, WitnessAnchor, XAnchor};
pub use anchor::{AnchorSet, Layer1, WitnessAnchor, XAnchor};
pub use assignments::{
Assign, AssignAttach, AssignData, AssignFungible, AssignRights, Assignments, AssignmentsRef,
TypedAssigns,
Expand Down
6 changes: 2 additions & 4 deletions src/stl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ use strict_types::stl::{std_stl, strict_types_stl};
use strict_types::typelib::LibBuilder;
use strict_types::{CompileError, TypeLib};

use crate::{
AnchoredBundle, ContractState, Extension, Genesis, OpCommitment, SubSchema, LIB_NAME_RGB,
};
use crate::{ContractState, Extension, Genesis, OpCommitment, SubSchema, XAnchor, LIB_NAME_RGB};

/// Strict types id for the library providing data types for RGB consensus.
pub const LIB_ID_RGB: &str =
Expand All @@ -47,7 +45,7 @@ fn _rgb_core_stl() -> Result<TypeLib, CompileError> {
})
.transpile::<SubSchema>()
.transpile::<Genesis>()
.transpile::<AnchoredBundle>()
.transpile::<XAnchor>()
.transpile::<Extension>()
.transpile::<ContractState>()
.transpile::<OpCommitment>()
Expand Down
28 changes: 20 additions & 8 deletions src/validation/consignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ use std::collections::{BTreeMap, BTreeSet};
use std::rc::Rc;

use crate::{
AnchoredBundle, AssetTag, AssignmentType, BundleId, Genesis, OpId, OpRef, Operation,
SecretSeal, SubSchema, WitnessId, XChain,
AssetTag, AssignmentType, BundleId, Genesis, OpId, OpRef, Operation, SecretSeal, SubSchema,
TransitionBundle, WitnessId, XAnchor, XChain,
};

pub struct CheckedConsignment<'consignment, C: ConsignmentApi>(&'consignment C);
Expand All @@ -55,10 +55,16 @@ impl<'consignment, C: ConsignmentApi> ConsignmentApi for CheckedConsignment<'con

fn bundle_ids<'a>(&self) -> Self::Iter<'a> { self.0.bundle_ids() }

fn anchored_bundle(&self, bundle_id: BundleId) -> Option<Rc<AnchoredBundle>> {
fn bundle(&self, bundle_id: BundleId) -> Option<Rc<TransitionBundle>> {
self.0
.anchored_bundle(bundle_id)
.filter(|ab| ab.bundle.bundle_id() == bundle_id)
.bundle(bundle_id)
.filter(|b| b.bundle_id() == bundle_id)
}

fn anchor(&self, bundle_id: BundleId) -> Option<Rc<XAnchor>> { self.0.anchor(bundle_id) }

fn bundle_witness_id(&self, bundle_id: BundleId) -> Option<WitnessId> {
self.0.bundle_witness_id(bundle_id)
}

fn op_witness_id(&self, opid: OpId) -> Option<WitnessId> { self.0.op_witness_id(opid) }
Expand All @@ -81,7 +87,7 @@ pub trait ConsignmentApi {
/// Asset tags uses in the confidential asset validation.
fn asset_tags(&self) -> &BTreeMap<AssignmentType, AssetTag>;

/// Retrieves reference to a operation (genesis, state transition or state
/// Retrieves reference to an operation (genesis, state transition or state
/// extension) matching the provided id, or `None` otherwise
fn operation(&self, opid: OpId) -> Option<OpRef>;

Expand All @@ -102,8 +108,14 @@ pub trait ConsignmentApi {
/// Returns iterator over all bundle ids present in the consignment.
fn bundle_ids<'a>(&self) -> Self::Iter<'a>;

/// Returns reference to an anchored bundle given a bundle id.
fn anchored_bundle(&self, bundle_id: BundleId) -> Option<Rc<AnchoredBundle>>;
/// Returns reference to a bundle given a bundle id.
fn bundle(&self, bundle_id: BundleId) -> Option<Rc<TransitionBundle>>;

/// Returns reference to an anchor given a bundle id.
fn anchor(&self, bundle_id: BundleId) -> Option<Rc<XAnchor>>;

/// Returns witness id for a given transition bundle.
fn bundle_witness_id(&self, bundle_id: BundleId) -> Option<WitnessId>;

/// Returns witness id for a given operation.
fn op_witness_id(&self, opid: OpId) -> Option<WitnessId>;
Expand Down
8 changes: 5 additions & 3 deletions src/validation/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,8 +301,12 @@ pub enum Failure {
/// transition bundle {0} referenced in consignment terminals is absent from
/// the consignment.
TerminalBundleAbsent(BundleId),
/// transition bundle {0} is absent from the consignment.
/// transition bundle {0} is absent in the consignment.
BundleAbsent(BundleId),
/// anchor for transitio bundle {0} is absent in the consignment.
AnchorAbsent(BundleId),
/// witness id for transition bundle {0} is absent in the consignment.
WitnessIdAbsent(BundleId),
/// operation {0} is under a different contract {1}.
ContractMismatch(OpId, ContractId),

Expand All @@ -327,8 +331,6 @@ pub enum Failure {
},
/// transition {0} references non-existing previous output {1}.
NoPrevOut(OpId, Opout),
/// anchors used inside bundle {0} reference different public witness ids.
AnchorSetInvalid(BundleId),
/// seal defined in the history as a part of operation output {0} is
/// confidential and can't be validated.
ConfidentialSeal(Opout),
Expand Down
53 changes: 28 additions & 25 deletions src/validation/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,14 @@ use crate::{
#[derive(Clone, Debug, Display, Error, From)]
#[display(doc_comments)]
pub enum WitnessResolverError {
/// witness {0} does not exists.
/// witness {0} does not exist.
Unknown(WitnessId),
/// unable to retrieve witness {0}, {1}
Other(WitnessId, String),
}

pub trait ResolveWitness {
// TODO: Return with SPV proof data
fn resolve_pub_witness(
&self,
witness_id: WitnessId,
Expand Down Expand Up @@ -95,11 +96,11 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveWitness>
// to detect any potential issues with the consignment structure and notify user
// about them (in form of generated warnings)
for (bundle_id, seal_endpoint) in consignment.terminals() {
let Some(anchored_bundle) = consignment.anchored_bundle(bundle_id) else {
let Some(bundle) = consignment.bundle(bundle_id) else {
status.add_failure(Failure::TerminalBundleAbsent(bundle_id));
continue;
};
for (opid, transition) in &anchored_bundle.bundle.known_transitions {
for (opid, transition) in &bundle.known_transitions {
// Checking for endpoint definition duplicates
if !transition
.assignments
Expand Down Expand Up @@ -209,12 +210,12 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveWitness>
// treat it as a superposition of subgraphs, one for each endpoint; and validate
// them independently.
for (bundle_id, _) in self.consignment.terminals() {
let Some(anchored_bundle) = self.consignment.anchored_bundle(bundle_id) else {
let Some(bundle) = self.consignment.bundle(bundle_id) else {
// We already checked and errored here during the terminal validation, so just
// skipping.
continue;
};
for transition in anchored_bundle.bundle.known_transitions.values() {
for transition in bundle.known_transitions.values() {
self.validate_logic_on_route(schema, transition);
}
}
Expand Down Expand Up @@ -303,27 +304,32 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveWitness>
// *** PART III: Validating single-use-seals
fn validate_commitments(&mut self) {
for bundle_id in self.consignment.bundle_ids() {
let Some(anchored_bundle) = self.consignment.anchored_bundle(bundle_id) else {
let Some(bundle) = self.consignment.bundle(bundle_id) else {
self.status.add_failure(Failure::BundleAbsent(bundle_id));
continue;
};

let layer1 = anchored_bundle.anchor.layer1();

let anchors = &anchored_bundle.anchor;
let bundle = &anchored_bundle.bundle;
let Some(anchor) = self.consignment.anchor(bundle_id) else {
self.status.add_failure(Failure::AnchorAbsent(bundle_id));
continue;
};
let Some(witness_id) = self.consignment.bundle_witness_id(bundle_id) else {
self.status.add_failure(Failure::WitnessIdAbsent(bundle_id));
continue;
};

// [VALIDATION]: We validate that the seals were properly defined on BP-type layers
let (seals, input_map) = self.validate_seal_definitions(layer1, bundle);
let (seals, input_map) =
self.validate_seal_definitions(witness_id.layer1(), bundle.as_ref());

// [VALIDATION]: We validate that the seals were properly closed on BP-type layers
let Some(witness_tx) = self.validate_seal_commitments(&seals, bundle_id, anchors)
let Some(witness_tx) =
self.validate_seal_commitments(&seals, bundle_id, anchor.as_ref(), witness_id)
else {
continue;
};

// [VALIDATION]: We validate bundle commitments to the input map
self.validate_bundle_commitments(bundle_id, bundle, witness_tx, input_map);
self.validate_bundle_commitments(bundle_id, bundle.as_ref(), witness_tx, input_map);
}
}

Expand Down Expand Up @@ -367,15 +373,12 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveWitness>
seals: impl AsRef<[XOutputSeal]>,
bundle_id: BundleId,
anchors: &XAnchor,
witness_id: WitnessId,
) -> Option<XPubWitness> {
let Some(witness_id) = anchors.witness_id() else {
self.status
.add_failure(Failure::AnchorSetInvalid(bundle_id));
return None;
};

// Check that the anchor is committed into a transaction spending all of the
// Check that the anchor is committed into a transaction spending all the
// transition inputs.
// Here the method can do SPV proof instead of querying the indexer. The SPV
// proofs can be part of the consignments, but do not require .
match self.resolver.resolve_pub_witness(witness_id) {
Err(_) => {
// We wre unable to retrieve corresponding transaction, so can't check.
Expand Down Expand Up @@ -403,7 +406,7 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveWitness>
if let Some(tapret) = tapret {
let witness = pub_witness
.clone()
.map(|tx| Witness::with(tx, tapret.clone()));
.map(|tx| Witness::with(tx, tapret.dbc_proof.clone()));
self.validate_seal_closing(tapret_seals, witness, bundle_id, tapret)
} else if tapret_seals.count() > 0 {
self.status.add_warning(Warning::UnclosedSeals(bundle_id));
Expand All @@ -416,7 +419,7 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveWitness>
if let Some(opret) = opret {
let witness = pub_witness
.clone()
.map(|tx| Witness::with(tx, opret.clone()));
.map(|tx| Witness::with(tx, opret.dbc_proof));
self.validate_seal_closing(opret_seals, witness, bundle_id, opret)
} else if opret_seals.count() > 0 {
self.status.add_warning(Warning::UnclosedSeals(bundle_id));
Expand Down Expand Up @@ -530,8 +533,8 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveWitness>
/// generic type `Dbc`) and extra-transaction data, which are taken from
/// anchors DBC proof.
///
/// Additionally checks that the provided message contains commitment to the
/// bundle under the current contract.
/// Additionally, checks that the provided message contains commitment to
/// the bundle under the current contract.
fn validate_seal_closing<'seal, 'temp, Seal: 'seal, Dbc: dbc::Proof>(
&mut self,
seals: impl IntoIterator<Item = &'seal Seal>,
Expand Down

0 comments on commit bde1287

Please sign in to comment.