Skip to content
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
4 changes: 2 additions & 2 deletions src/contract/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,6 @@ pub use seal::{
};
pub use state::{ConcealedState, ConfidentialState, ExposedState, RevealedState, StateType};
pub use xchain::{
AltLayer1, AltLayer1Set, XChain, XChainParseError, XOutpoint, XCHAIN_BITCOIN_PREFIX,
XCHAIN_LIQUID_PREFIX, Impossible
AltLayer1, AltLayer1Set, Impossible, XChain, XChainParseError, XOutpoint,
XCHAIN_BITCOIN_PREFIX, XCHAIN_LIQUID_PREFIX,
};
4 changes: 2 additions & 2 deletions src/validation/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ impl OwnedStateSchema {
}
}
(OwnedStateSchema::Structured(_), ConcealedState::Structured(_)) => {
status.add_info(validation::Info::UncheckableConfidentialState(
status.add_warning(validation::Warning::UncheckableConfidentialState(
opid, state_type,
));
}
(OwnedStateSchema::Attachment(_), ConcealedState::Attachment(_)) => {
status.add_info(validation::Info::UncheckableConfidentialState(
status.add_warning(validation::Warning::UncheckableConfidentialState(
opid, state_type,
));
}
Expand Down
62 changes: 18 additions & 44 deletions src/validation/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,13 @@
use core::ops::AddAssign;
use std::fmt::{self, Display, Formatter};

use bp::Txid;
use commit_verify::mpc::InvalidProof;
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,
};

Expand All @@ -40,11 +39,8 @@ pub enum Validity {
#[display("is valid")]
Valid,

#[display("has non-mined terminal(s)")]
UnminedTerminals,

#[display("contains unknown witness transactions")]
UnresolvedTransactions,
#[display("valid, with warnings")]
Warnings,

#[display("is NOT valid")]
Invalid,
Expand All @@ -57,8 +53,6 @@ pub enum Validity {
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct Status {
pub absent_pub_witnesses: Vec<XWitnessId>,
pub unmined_terminals: Vec<Txid>,
pub failures: Vec<Failure>,
pub warnings: Vec<Warning>,
pub info: Vec<Info>,
Expand All @@ -70,20 +64,6 @@ impl Display for Status {
writeln!(f, "Consignment {}", self.validity())?;
}

if !self.absent_pub_witnesses.is_empty() {
f.write_str("Unknown public witnesses:\n")?;
for txid in &self.absent_pub_witnesses {
writeln!(f, "- {txid}")?;
}
}

if !self.unmined_terminals.is_empty() {
f.write_str("Non-mined terminals:\n")?;
for txid in &self.unmined_terminals {
writeln!(f, "- {txid}")?;
}
}

if !self.failures.is_empty() {
f.write_str("Validation failures:\n")?;
for fail in &self.failures {
Expand Down Expand Up @@ -111,8 +91,6 @@ impl Display for Status {

impl AddAssign for Status {
fn add_assign(&mut self, rhs: Self) {
self.absent_pub_witnesses.extend(rhs.absent_pub_witnesses);
self.unmined_terminals.extend(rhs.unmined_terminals);
self.failures.extend(rhs.failures);
self.warnings.extend(rhs.warnings);
self.info.extend(rhs.info);
Expand All @@ -122,8 +100,6 @@ impl AddAssign for Status {
impl Status {
pub fn from_error(v: Failure) -> Self {
Status {
absent_pub_witnesses: vec![],
unmined_terminals: vec![],
failures: vec![v],
warnings: vec![],
info: vec![],
Expand Down Expand Up @@ -166,16 +142,12 @@ impl Status {
}

pub fn validity(&self) -> Validity {
if self.failures.is_empty() {
if self.unmined_terminals.is_empty() {
Validity::Valid
} else {
Validity::UnminedTerminals
}
} else if self.absent_pub_witnesses.is_empty() {
if !self.failures.is_empty() {
Validity::Invalid
} else if !self.warnings.is_empty() {
Validity::Warnings
} else {
Validity::UnresolvedTransactions
Validity::Valid
}
}
}
Expand Down Expand Up @@ -265,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),
Expand Down Expand Up @@ -308,8 +286,8 @@ pub enum Failure {
/// seal defined in the history as a part of operation output {0} is
/// confidential and can't be validated.
ConfidentialSeal(Opout),
/// witness {0} is not known to the transaction resolver.
SealNoWitnessTx(XWitnessId),
/// public witness {0} is not known to the resolver.
SealNoPubWitness(XWitnessId),
/// witness layer 1 {anchor} doesn't match seal definition {seal}.
SealWitnessLayer1Mismatch { seal: Layer1, anchor: Layer1 },
/// seal {1} is defined on {0} which is not in the set of layers allowed
Expand Down Expand Up @@ -390,12 +368,12 @@ pub enum Failure {
)]
#[display(doc_comments)]
pub enum Warning {
// TODO: Replace debug with display
/// terminal seal {1:?} referencing operation {0} is not present in
/// operation assignments.
TerminalSealAbsent(OpId, XChain<SecretSeal>),
/// terminal witness transaction {0} is not yet mined.
TerminalWitnessNotMined(Txid),
/// operation {0} contains state in assignment {1} which is confidential and
/// thus was not validated.
UncheckableConfidentialState(OpId, schema::AssignmentType),

/// Custom warning by external services on top of RGB Core.
#[display(inner)]
Expand All @@ -410,10 +388,6 @@ pub enum Warning {
)]
#[display(doc_comments)]
pub enum Info {
/// operation {0} contains state in assignment {1} which is confidential and
/// thus was not validated.
UncheckableConfidentialState(OpId, schema::AssignmentType),

/// Custom info by external services on top of RGB Core.
#[display(inner)]
Custom(String),
Expand Down
48 changes: 29 additions & 19 deletions src/validation/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<OpRef> = 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
Expand All @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -308,7 +323,7 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveWitness>
continue;
}

queue.push_back(prev_op);
queue.push_back((*prev_id, prev_op));
}
}
}
Expand Down Expand Up @@ -407,19 +422,14 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveWitness>
// Reporting this incident and continuing further. Why this happens? No
// connection to Bitcoin Core, Electrum or other backend etc. So this is not a
// failure in a strict sense, however we can't be sure that the consignment is
// valid. That's why we keep the track of such information in a separate place
// (`unresolved_txids` field of the validation status object).
self.status
.borrow_mut()
.absent_pub_witnesses
.push(witness_id);
// valid.
// This also can mean that there is no known transaction with the id provided by
// the anchor, i.e. consignment is invalid. We are proceeding with further
// validation in order to detect the rest of problems (and reporting the
// failure!)
self.status
.borrow_mut()
.add_failure(Failure::SealNoWitnessTx(witness_id));
.add_failure(Failure::SealNoPubWitness(witness_id));
None
}
Ok(pub_witness) => {
Expand Down