diff --git a/src/validation/logic.rs b/src/validation/logic.rs index 1f030ce3..ed7c3732 100644 --- a/src/validation/logic.rs +++ b/src/validation/logic.rs @@ -98,7 +98,7 @@ impl Schema { Transition { transition_type, .. }, - _, + .., ) => { // Right now we do not have actions to implement; but later // we may have embedded procedures which must be verified @@ -133,7 +133,7 @@ impl Schema { Some(transition_type.into_inner()), ) } - OrdOpRef::Extension(Extension { extension_type, .. }, _) => { + OrdOpRef::Extension(Extension { extension_type, .. }, ..) => { // Right now we do not have actions to implement; but later // we may have embedded procedures which must be verified // here @@ -170,7 +170,7 @@ impl Schema { status += self.validate_metadata(opid, op.metadata(), metadata_schema, consignment.types()); status += self.validate_global_state(opid, op.globals(), global_schema, consignment.types()); - let prev_state = if let OrdOpRef::Transition(transition, _) = op { + let prev_state = if let OrdOpRef::Transition(transition, ..) = op { let prev_state = extract_prev_state(consignment, opid, &transition.inputs, &mut status); status += self.validate_prev_state(opid, &prev_state, owned_schema); prev_state @@ -178,7 +178,7 @@ impl Schema { Assignments::default() }; let mut redeemed = Valencies::default(); - if let OrdOpRef::Extension(extension, _) = op { + if let OrdOpRef::Extension(extension, ..) = op { for valency in extension.redeemed.keys() { redeemed.push(*valency).expect("same size"); } diff --git a/src/validation/validator.rs b/src/validation/validator.rs index 49dfd988..65953cd2 100644 --- a/src/validation/validator.rs +++ b/src/validation/validator.rs @@ -33,7 +33,7 @@ use single_use_seals::SealWitness; use super::status::Failure; use super::{CheckedConsignment, ConsignmentApi, DbcProof, EAnchor, OpRef, Status, Validity}; use crate::vm::{ - ContractStateAccess, ContractStateEvolve, OpOrd, OrdOpRef, WitnessOrd, XWitnessId, XWitnessTx, + ContractStateAccess, ContractStateEvolve, OrdOpRef, WitnessOrd, XWitnessId, XWitnessTx, }; use crate::{ validation, AltLayer1, BundleId, ContractId, Layer1, OpId, OpType, Operation, Opout, Schema, @@ -258,7 +258,7 @@ impl< // [VALIDATION]: Iterating over all consignment operations, ordering them according to the // consensus ordering rules. - let mut ops = BTreeMap::::new(); + let mut ops = BTreeSet::::new(); for bundle_id in self.consignment.bundle_ids() { let bundle = self .consignment @@ -279,41 +279,34 @@ impl< return; } }; - for (opid, op) in &bundle.known_transitions { - let mut ord = OpOrd::Transition { - witness: witness_ord, - nonce: op.nonce, - opid: *opid, - }; - ops.insert(ord, OrdOpRef::Transition(op, witness_id)); + for op in bundle.known_transitions.values() { + ops.insert(OrdOpRef::Transition(op, witness_id, witness_ord, op.nonce)); for input in &op.inputs { // We will error in `validate_operations` below on the absent extension from the // consignment. if let Some(OpRef::Extension(extension)) = self.consignment.operation(input.prev_out.op) { - ord = OpOrd::Extension { - witness: witness_ord, - nonce: extension.nonce, - opid: *opid, - }; + let ext = OrdOpRef::Extension(extension, witness_id, witness_ord, op.nonce); // Account only for the first time when extension seal was closed - let prev = ops.iter().find(|(_, r)| matches!(r, OrdOpRef::Extension(ext, _) if ext.id() == extension.id())).map(|(a, _)| *a); - let ext = OrdOpRef::Extension(extension, witness_id); - if let Some(old) = prev { - if old > ord { + let prev = ops.iter().find(|r| matches!(r, OrdOpRef::Extension(ext, ..) if ext.id() == extension.id())).copied(); + match prev { + Some(old) if old > ext => { ops.remove(&old); - ops.insert(ord, ext); + ops.insert(ext) } - } - if prev.is_none() { - ops.insert(ord, ext); - } + None => ops.insert(ext), + _ => { + /* the extension is already present in the queue and properly + * ordered, so we have nothing to add or change */ + true + } + }; } } } } - for op in ops.into_values() { + for op in ops { self.validate_operation(op); } } @@ -343,7 +336,7 @@ impl< OrdOpRef::Genesis(_) => { unreachable!("genesis is not a part of the operation history") } - OrdOpRef::Transition(transition, _) => { + OrdOpRef::Transition(transition, ..) => { for input in &transition.inputs { if self.consignment.operation(input.prev_out.op).is_none() { self.status @@ -352,7 +345,7 @@ impl< } } } - OrdOpRef::Extension(extension, _) => { + OrdOpRef::Extension(extension, ..) => { for (valency, prev_id) in &extension.redeemed { let Some(prev_op) = self.consignment.operation(*prev_id) else { self.status diff --git a/src/vm/contract.rs b/src/vm/contract.rs index d483e787..fc0ff7aa 100644 --- a/src/vm/contract.rs +++ b/src/vm/contract.rs @@ -135,127 +135,155 @@ impl XChain { } } +/// The type is used during validation and computing a contract state. It +/// combines both the operation with the information required for its ordering +/// in the contract history (via construction of [`OpOrd`]) according to the +/// consensus rules. #[derive(Copy, Clone, PartialEq, Eq, Debug, From)] pub enum OrdOpRef<'op> { #[from] Genesis(&'op Genesis), - Transition(&'op Transition, XWitnessId), - Extension(&'op Extension, XWitnessId), + Transition(&'op Transition, XWitnessId, WitnessOrd, /** Nonce value, used in ordering */ u8), + Extension(&'op Extension, XWitnessId, WitnessOrd, /** Nonce value, used in ordering */ u8), +} + +impl<'op> PartialOrd for OrdOpRef<'op> { + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } +} + +impl<'op> Ord for OrdOpRef<'op> { + fn cmp(&self, other: &Self) -> Ordering { self.op_ord().cmp(&other.op_ord()) } } impl<'op> OrdOpRef<'op> { pub fn witness_id(&self) -> Option { match self { OrdOpRef::Genesis(_) => None, - OrdOpRef::Transition(_, witness_id) | OrdOpRef::Extension(_, witness_id) => { + OrdOpRef::Transition(_, witness_id, ..) | OrdOpRef::Extension(_, witness_id, ..) => { Some(*witness_id) } } } + + pub fn op_ord(&self) -> OpOrd { + match self { + OrdOpRef::Genesis(_) => OpOrd::Genesis, + OrdOpRef::Transition(op, _, witness_ord, nonce) => OpOrd::Transition { + witness: *witness_ord, + nonce: *nonce, + opid: op.id(), + }, + OrdOpRef::Extension(op, _, witness_ord, nonce) => OpOrd::Extension { + witness: *witness_ord, + nonce: *nonce, + opid: op.id(), + }, + } + } } impl<'op> Operation for OrdOpRef<'op> { fn op_type(&self) -> OpType { match self { OrdOpRef::Genesis(op) => op.op_type(), - OrdOpRef::Transition(op, _) => op.op_type(), - OrdOpRef::Extension(op, _) => op.op_type(), + OrdOpRef::Transition(op, ..) => op.op_type(), + OrdOpRef::Extension(op, ..) => op.op_type(), } } fn full_type(&self) -> OpFullType { match self { OrdOpRef::Genesis(op) => op.full_type(), - OrdOpRef::Transition(op, _) => op.full_type(), - OrdOpRef::Extension(op, _) => op.full_type(), + OrdOpRef::Transition(op, ..) => op.full_type(), + OrdOpRef::Extension(op, ..) => op.full_type(), } } fn id(&self) -> OpId { match self { OrdOpRef::Genesis(op) => op.id(), - OrdOpRef::Transition(op, _) => op.id(), - OrdOpRef::Extension(op, _) => op.id(), + OrdOpRef::Transition(op, ..) => op.id(), + OrdOpRef::Extension(op, ..) => op.id(), } } fn contract_id(&self) -> ContractId { match self { OrdOpRef::Genesis(op) => op.contract_id(), - OrdOpRef::Transition(op, _) => op.contract_id(), - OrdOpRef::Extension(op, _) => op.contract_id(), + OrdOpRef::Transition(op, ..) => op.contract_id(), + OrdOpRef::Extension(op, ..) => op.contract_id(), } } fn nonce(&self) -> u8 { match self { OrdOpRef::Genesis(op) => op.nonce(), - OrdOpRef::Transition(op, _) => op.nonce(), - OrdOpRef::Extension(op, _) => op.nonce(), + OrdOpRef::Transition(op, ..) => op.nonce(), + OrdOpRef::Extension(op, ..) => op.nonce(), } } fn transition_type(&self) -> Option { match self { OrdOpRef::Genesis(op) => op.transition_type(), - OrdOpRef::Transition(op, _) => op.transition_type(), - OrdOpRef::Extension(op, _) => op.transition_type(), + OrdOpRef::Transition(op, ..) => op.transition_type(), + OrdOpRef::Extension(op, ..) => op.transition_type(), } } fn extension_type(&self) -> Option { match self { OrdOpRef::Genesis(op) => op.extension_type(), - OrdOpRef::Transition(op, _) => op.extension_type(), - OrdOpRef::Extension(op, _) => op.extension_type(), + OrdOpRef::Transition(op, ..) => op.extension_type(), + OrdOpRef::Extension(op, ..) => op.extension_type(), } } fn metadata(&self) -> &Metadata { match self { OrdOpRef::Genesis(op) => op.metadata(), - OrdOpRef::Transition(op, _) => op.metadata(), - OrdOpRef::Extension(op, _) => op.metadata(), + OrdOpRef::Transition(op, ..) => op.metadata(), + OrdOpRef::Extension(op, ..) => op.metadata(), } } fn globals(&self) -> &GlobalState { match self { OrdOpRef::Genesis(op) => op.globals(), - OrdOpRef::Transition(op, _) => op.globals(), - OrdOpRef::Extension(op, _) => op.globals(), + OrdOpRef::Transition(op, ..) => op.globals(), + OrdOpRef::Extension(op, ..) => op.globals(), } } fn valencies(&self) -> &Valencies { match self { OrdOpRef::Genesis(op) => op.valencies(), - OrdOpRef::Transition(op, _) => op.valencies(), - OrdOpRef::Extension(op, _) => op.valencies(), + OrdOpRef::Transition(op, ..) => op.valencies(), + OrdOpRef::Extension(op, ..) => op.valencies(), } } fn assignments(&self) -> AssignmentsRef<'op> { match self { OrdOpRef::Genesis(op) => (&op.assignments).into(), - OrdOpRef::Transition(op, _) => (&op.assignments).into(), - OrdOpRef::Extension(op, _) => (&op.assignments).into(), + OrdOpRef::Transition(op, ..) => (&op.assignments).into(), + OrdOpRef::Extension(op, ..) => (&op.assignments).into(), } } fn assignments_by_type(&self, t: AssignmentType) -> Option> { match self { OrdOpRef::Genesis(op) => op.assignments_by_type(t), - OrdOpRef::Transition(op, _) => op.assignments_by_type(t), - OrdOpRef::Extension(op, _) => op.assignments_by_type(t), + OrdOpRef::Transition(op, ..) => op.assignments_by_type(t), + OrdOpRef::Extension(op, ..) => op.assignments_by_type(t), } } fn inputs(&self) -> Inputs { match self { OrdOpRef::Genesis(op) => op.inputs(), - OrdOpRef::Transition(op, _) => op.inputs(), - OrdOpRef::Extension(op, _) => op.inputs(), + OrdOpRef::Transition(op, ..) => op.inputs(), + OrdOpRef::Extension(op, ..) => op.inputs(), } } }