use crate::validation::Status;
use crate::{
validation, OpFullType, OpSchema, Schema, StateSchema, SubSchema, BLANK_TRANSITION_ID,
};
impl SubSchema {
pub fn verify(&self) -> validation::Status {
let mut status = validation::Status::new();
if let Some(ref root) = self.subset_of {
status += self.verify_subschema(root);
}
status += self.verify_consistency();
status
}
fn verify_consistency(&self) -> validation::Status {
let mut status = validation::Status::new();
status += self.verify_operation(OpFullType::Genesis, &self.genesis);
for (type_id, schema) in &self.transitions {
status += self.verify_operation(OpFullType::StateTransition(*type_id), schema);
}
for (type_id, schema) in &self.extensions {
status += self.verify_operation(OpFullType::StateExtension(*type_id), schema);
}
if self.transitions.contains_key(&BLANK_TRANSITION_ID) {
status.add_failure(validation::Failure::SchemaBlankTransitionRedefined);
}
for (type_id, schema) in &self.global_types {
if !self.type_system.contains_key(&schema.sem_id) {
status.add_failure(validation::Failure::SchemaGlobalSemIdUnknown(
*type_id,
schema.sem_id,
));
}
}
for (type_id, schema) in &self.owned_types {
if let StateSchema::Structured(sem_id) = schema {
if !self.type_system.contains_key(sem_id) {
status.add_failure(validation::Failure::SchemaOwnedSemIdUnknown(
*type_id, *sem_id,
));
}
}
}
status
}
fn verify_operation(&self, op_type: OpFullType, schema: &impl OpSchema) -> Status {
let mut status = validation::Status::new();
if !self.type_system.contains_key(&schema.metadata()) {
status.add_failure(validation::Failure::SchemaOpMetaSemIdUnknown(
op_type,
schema.metadata(),
));
}
if matches!(schema.inputs(), Some(inputs) if inputs.is_empty()) {
status.add_failure(validation::Failure::SchemaOpEmptyInputs(op_type));
}
if matches!(schema.redeems(), Some(inputs) if inputs.is_empty()) {
status.add_failure(validation::Failure::SchemaOpEmptyInputs(op_type));
}
for type_id in schema.globals().keys() {
if !self.global_types.contains_key(type_id) {
status
.add_failure(validation::Failure::SchemaOpGlobalTypeUnknown(op_type, *type_id));
}
}
for type_id in schema.assignments().keys() {
if !self.owned_types.contains_key(type_id) {
status.add_failure(validation::Failure::SchemaOpAssignmentTypeUnknown(
op_type, *type_id,
));
}
}
for type_id in schema.valencies() {
if !self.valency_types.contains(type_id) {
status.add_failure(validation::Failure::SchemaOpValencyTypeUnknown(
op_type, *type_id,
));
}
}
status
}
fn verify_subschema(&self, root: &Schema<()>) -> validation::Status {
let mut status = validation::Status::new();
if self.subset_of.as_ref() != Some(root) {
panic!("SubSchema::schema_verify called with a root schema not matching subset_of");
}
for (global_type, data_format) in &self.global_types {
match root.global_types.get(global_type) {
None => status
.add_failure(validation::Failure::SubschemaGlobalStateMismatch(*global_type)),
Some(root_data_format) if root_data_format != data_format => status
.add_failure(validation::Failure::SubschemaGlobalStateMismatch(*global_type)),
_ => &status,
};
}
for (assignments_type, state_schema) in &self.owned_types {
match root.owned_types.get(assignments_type) {
None => status.add_failure(validation::Failure::SubschemaAssignmentTypeMismatch(
*assignments_type,
)),
Some(root_state_schema) if root_state_schema != state_schema => status.add_failure(
validation::Failure::SubschemaAssignmentTypeMismatch(*assignments_type),
),
_ => &status,
};
}
for valencies_type in &self.valency_types {
match root.valency_types.contains(valencies_type) {
false => status.add_failure(validation::Failure::SubschemaValencyTypeMismatch(
*valencies_type,
)),
_ => &status,
};
}
status += self
.genesis
.verify_subschema(OpFullType::Genesis, &root.genesis);
for (type_id, transition_schema) in &self.transitions {
if let Some(root_transition_schema) = root.transitions.get(type_id) {
status += transition_schema.verify_subschema(
OpFullType::StateTransition(*type_id),
root_transition_schema,
);
} else {
status.add_failure(validation::Failure::SubschemaTransitionTypeMismatch(*type_id));
}
}
for (type_id, extension_schema) in &self.extensions {
if let Some(root_extension_schema) = root.extensions.get(type_id) {
status += extension_schema
.verify_subschema(OpFullType::StateExtension(*type_id), root_extension_schema);
} else {
status.add_failure(validation::Failure::SubschemaExtensionTypeMismatch(*type_id));
}
}
status
}
}
pub(crate) trait SchemaVerify {
type Root;
fn verify_subschema(&self, op_type: OpFullType, root: &Self::Root) -> validation::Status;
}
impl<T> SchemaVerify for T
where T: OpSchema
{
type Root = T;
fn verify_subschema(&self, op_type: OpFullType, root: &Self) -> validation::Status {
let mut status = validation::Status::new();
if self.metadata() != root.metadata() {
status.add_failure(validation::Failure::SubschemaOpMetaMismatch {
op_type,
expected: root.metadata(),
actual: self.metadata(),
});
}
for (type_id, occ) in self.globals() {
match root.globals().get(type_id) {
None => status.add_failure(validation::Failure::SubschemaOpGlobalStateMismatch(
op_type, *type_id,
)),
Some(root_occ) if occ != root_occ => status.add_failure(
validation::Failure::SubschemaOpGlobalStateMismatch(op_type, *type_id),
),
_ => &status,
};
}
if let Some(inputs) = self.inputs() {
let root_inputs = root.inputs().expect("generic guarantees");
for (type_id, occ) in inputs {
match root_inputs.get(type_id) {
None => status.add_failure(validation::Failure::SubschemaOpInputMismatch(
op_type, *type_id,
)),
Some(root_occ) if occ != root_occ => status.add_failure(
validation::Failure::SubschemaOpInputMismatch(op_type, *type_id),
),
_ => &status,
};
}
}
for (type_id, occ) in self.assignments() {
match root.assignments().get(type_id) {
None => status.add_failure(validation::Failure::SubschemaOpAssignmentsMismatch(
op_type, *type_id,
)),
Some(root_occ) if occ != root_occ => status.add_failure(
validation::Failure::SubschemaOpAssignmentsMismatch(op_type, *type_id),
),
_ => &status,
};
}
if let Some(redeems) = self.redeems() {
let root_redeems = root.redeems().expect("generic guarantees");
for type_id in redeems {
if !root_redeems.contains(type_id) {
status.add_failure(validation::Failure::SubschemaOpRedeemMismatch(
op_type, *type_id,
));
}
}
}
for type_id in self.valencies() {
if !root.valencies().contains(type_id) {
status.add_failure(validation::Failure::SubschemaOpValencyMismatch(
op_type, *type_id,
));
}
}
status
}
}