diff --git a/src/validation/status.rs b/src/validation/status.rs index 5cb3bff4..d371e0d0 100644 --- a/src/validation/status.rs +++ b/src/validation/status.rs @@ -29,7 +29,7 @@ use strict_types::SemId; use crate::contract::Opout; use crate::schema::{self, SchemaId}; use crate::{ - AssignmentType, BundleId, ContractId, Layer1, OccurrencesMismatch, OpFullType, OpId, + AssignmentType, BundleId, ContractId, Layer1, OccurrencesMismatch, OpFullType, OpId, OpType, SecretSeal, StateType, Vin, XChain, XGraphSeal, XOutputSeal, XWitnessId, }; @@ -237,6 +237,12 @@ pub enum Failure { CyclicGraph(OpId), /// operation {0} is absent from the consignment. OperationAbsent(OpId), + /// {ty} data doesn't match operation id {expected} (actual id is {actual}). + OperationIdMismatch { + ty: OpType, + expected: OpId, + actual: OpId, + }, /// transition bundle {0} referenced in consignment terminals is absent from /// the consignment. TerminalBundleAbsent(BundleId), diff --git a/src/validation/validator.rs b/src/validation/validator.rs index c856d7f3..def5fc32 100644 --- a/src/validation/validator.rs +++ b/src/validation/validator.rs @@ -230,10 +230,13 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveWitness> fn validate_logic_on_route(&self, opid: OpId) { let schema = self.consignment.schema(); let Some(OpRef::Transition(transition)) = self.consignment.operation(opid) else { - panic!("provided {opid} is absent"); + self.status + .borrow_mut() + .add_failure(Failure::OperationAbsent(opid)); + return; }; - let mut queue: VecDeque = VecDeque::new(); + let mut queue: VecDeque<(OpId, OpRef)> = VecDeque::new(); // Instead of constructing complex graph structures or using a recursions we // utilize queue to keep the track of the upstream (ancestor) nodes and make @@ -244,9 +247,18 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveWitness> // change to a given operation is valid against the schema + committed // into bitcoin transaction graph with proper anchor. That is what we are // checking in the code below: - queue.push_back(OpRef::Transition(transition)); - while let Some(operation) = queue.pop_front() { - let opid = operation.id(); + queue.push_back((opid, OpRef::Transition(transition))); + while let Some((opid, operation)) = queue.pop_front() { + let actual_opid = operation.id(); + if opid != actual_opid { + self.status + .borrow_mut() + .add_failure(Failure::OperationIdMismatch { + ty: operation.op_type(), + expected: opid, + actual: actual_opid, + }); + } if operation.contract_id() != self.contract_id { self.status @@ -274,12 +286,15 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveWitness> OpRef::Transition(transition) => { // Now, we must collect all parent nodes and add them to the verification queue let parent_nodes = transition.inputs.iter().filter_map(|input| { - self.consignment.operation(input.prev_out.op).or_else(|| { - self.status - .borrow_mut() - .add_failure(Failure::OperationAbsent(input.prev_out.op)); - None - }) + self.consignment + .operation(input.prev_out.op) + .map(|op| (input.prev_out.op, op)) + .or_else(|| { + self.status + .borrow_mut() + .add_failure(Failure::OperationAbsent(input.prev_out.op)); + None + }) }); queue.extend(parent_nodes); @@ -308,7 +323,7 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveWitness> continue; } - queue.push_back(prev_op); + queue.push_back((*prev_id, prev_op)); } } }