From 7eb443b87745d2a05030f556d8a33ba8dbed0f0a Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Thu, 28 Dec 2023 11:34:10 +0100 Subject: [PATCH 1/2] validation: fix issue with invalid witness id on previous seals --- src/validation/consignment.rs | 16 ++++++++++++---- src/validation/validator.rs | 12 ++++++++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/validation/consignment.rs b/src/validation/consignment.rs index 2e142bce..8e3075b8 100644 --- a/src/validation/consignment.rs +++ b/src/validation/consignment.rs @@ -27,10 +27,7 @@ use std::collections::{BTreeMap, BTreeSet}; use std::rc::Rc; -use crate::{ - AnchoredBundle, AssetTag, AssignmentType, BundleId, Genesis, OpId, OpRef, Operation, - SecretSeal, SubSchema, -}; +use crate::{AnchoredBundle, AssetTag, AssignmentType, BundleId, Genesis, OpId, OpRef, Operation, SecretSeal, SubSchema, WitnessId}; pub struct CheckedConsignment<'consignment, C: ConsignmentApi>(&'consignment C); @@ -60,6 +57,10 @@ impl<'consignment, C: ConsignmentApi> ConsignmentApi for CheckedConsignment<'con .anchored_bundle(bundle_id) .filter(|ab| ab.bundle.bundle_id() == bundle_id) } + + fn op_witness_id(&self, opid: OpId) -> Option { + self.0.op_witness_id(opid) + } } /// Trait defining common data access API for all storage-related RGB structures @@ -70,8 +71,10 @@ impl<'consignment, C: ConsignmentApi> ConsignmentApi for CheckedConsignment<'con /// invalid or absent data, the API must always return [`None`] or empty /// collections/iterators. pub trait ConsignmentApi { + /// Iterator for all bundle ids present in the consignment. type Iter<'a>: Iterator; + /// Returns reference to the schema object used by the consignment. fn schema(&self) -> &SubSchema; /// Asset tags uses in the confidential asset validation. @@ -95,7 +98,12 @@ pub trait ConsignmentApi { /// state transitions represent the final state fn terminals(&self) -> BTreeSet<(BundleId, SecretSeal)>; + /// 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>; + + /// Returns witness id for a given operaiton. + fn op_witness_id(&self, opid: OpId) -> Option; } diff --git a/src/validation/validator.rs b/src/validation/validator.rs index 7539b808..97c5bc79 100644 --- a/src/validation/validator.rs +++ b/src/validation/validator.rs @@ -34,7 +34,7 @@ use crate::vm::AluRuntime; use crate::{ AltLayer1, AnchorSet, BundleId, ContractId, Layer1, OpId, OpRef, OpType, Operation, Opout, Schema, SchemaId, SchemaRoot, Script, SubSchema, Transition, TransitionBundle, TypedAssigns, - WitnessId, XAnchor, + XAnchor, }; #[derive(Clone, Debug, Display, Error, From)] @@ -310,14 +310,13 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> // For now we use just Bitcoin and Liquid as layer1, but in the // future we may have more validation routes for other types of // layer1 structure. - let witness_id = anchored_bundle.anchor.witness_id(); let anchors = match &anchored_bundle.anchor { XAnchor::Bitcoin(anchor) | XAnchor::Liquid(anchor) => anchor, }; let bundle = &anchored_bundle.bundle; // [VALIDATION]: We validate that the seals were properly defined on BP-type layers - let (seals, input_map) = self.validate_seal_definitions(layer1, witness_id, bundle); + let (seals, input_map) = self.validate_seal_definitions(layer1, bundle); // [VALIDATION]: We validate that the seals were properly closed on BP-type layers let Some(witness_tx) = self.validate_commitments_bp(layer1, &seals, bundle_id, anchors) @@ -435,7 +434,6 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> fn validate_seal_definitions( &mut self, layer1: Layer1, - witness_id: WitnessId, bundle: &TransitionBundle, ) -> (Vec>, BTreeMap>) { let mut input_map: BTreeMap> = bmap!(); @@ -494,6 +492,12 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> continue; } + let Some(witness_id) = self.consignment.op_witness_id(op) else { + self.status + .add_failure(Failure::OperationAbsent(op)); + continue; + }; + let seal = seal .try_to_output_seal(witness_id) .expect("method must be called only on BP-compatible layer 1") From 67fefef855cecc2eb5aa1f5145267b672de71f3c Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Thu, 28 Dec 2023 12:54:41 +0100 Subject: [PATCH 2/2] validation: fix seal resolution for genesis and extensions --- src/validation/validator.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/validation/validator.rs b/src/validation/validator.rs index 97c5bc79..583b5539 100644 --- a/src/validation/validator.rs +++ b/src/validation/validator.rs @@ -492,17 +492,21 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> continue; } - let Some(witness_id) = self.consignment.op_witness_id(op) else { - self.status - .add_failure(Failure::OperationAbsent(op)); - continue; - }; + let seal = if prev_op.op_type() == OpType::StateTransition { + let Some(witness_id) = self.consignment.op_witness_id(op) else { + self.status.add_failure(Failure::OperationAbsent(op)); + continue; + }; + + seal.try_to_output_seal(witness_id) + .expect("method must be called only on BP-compatible layer 1") + } else { + seal.to_output_seal() + .expect("genesis and state extensions must have explicit seals") + } + .reduce_to_bp() + .expect("method must be called only on BP-compatible layer 1"); - let seal = seal - .try_to_output_seal(witness_id) - .expect("method must be called only on BP-compatible layer 1") - .reduce_to_bp() - .expect("method must be called only on BP-compatible layer 1"); seals.push(seal); input_map .entry(opid)