diff --git a/Cargo.lock b/Cargo.lock index 1678a204..dda3e820 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,9 +28,9 @@ dependencies = [ [[package]] name = "amplify" -version = "3.12.0" +version = "3.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47cfa6a69eeb140125a63e4f5ae1156a530f8744ea1d35aa1b8b5613e4d8b41d" +checksum = "e45c604e86700ffea68a7d90c3ffb1508dad1120902f31dbb08afc942cf8acf3" dependencies = [ "amplify_apfloat", "amplify_derive", @@ -1144,9 +1144,9 @@ dependencies = [ [[package]] name = "stens" -version = "0.3.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e56c50a9ee6529486f13b8ca5682c4f23260f5900e4f2206cd7be689bb4f095" +checksum = "dc1e031ab71b85047628043fc99cbd286277b956534fd4f6762cce7cdb26b150" dependencies = [ "amplify", "serde", @@ -1157,9 +1157,9 @@ dependencies = [ [[package]] name = "strict_encoding" -version = "1.8.9" +version = "1.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3379d91cf1a6dda3ad54629ae868f0dd65a8fb42fae697c94777253e795a12b3" +checksum = "796bf3e8abb485e9194d03dfc534221e382f8cf058f417bbd66d5f4dd3b8a0cc" dependencies = [ "amplify", "bitcoin", diff --git a/Cargo.toml b/Cargo.toml index 747922d9..8642ea3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ path = "src/lib.rs" amplify = { version = "~3.12.0", features = ["apfloat"] } bp-core = { version = "~0.7.0" } lnpbp = { version = "~0.7.0", features = ["zip"] } -stens = "~0.3.2" +stens = "~0.4.0" strict_encoding = { version = "~1.8.8", features = ["crypto", "chrono", "bitcoin", "float"] } commit_verify = { version = "~0.7.0", features = ["rand", "bulletproofs"] } descriptor-wallet = "~0.7.1" diff --git a/src/schema/schema.rs b/src/schema/schema.rs index 0de0f97c..0ba288a4 100644 --- a/src/schema/schema.rs +++ b/src/schema/schema.rs @@ -188,6 +188,7 @@ mod _validation { use commit_verify::CommitConceal; use super::*; + use crate::schema::state::StenValidate; use crate::schema::{ MetadataStructure, OwnedRightsStructure, PublicRightsStructure, SchemaVerify, }; @@ -446,14 +447,16 @@ mod _validation { for data in set { let schema_type = data.schema_type(); if &schema_type != field - && !matches!((data, field), (data::Revealed::Bytes(_), TypeRef::Named(_))) + && !matches!( + (&data, field), + (data::Revealed::Bytes(_), TypeRef::Named(_)) + ) { status.add_failure(validation::Failure::SchemaMismatchedDataType( *field_type_id, )); } - // TODO: [validation] validate type serialization for structured types - // status += field.validate(&data); + status += field.validate(&self.type_system, node_id, *field_type_id, &data); } } @@ -562,16 +565,20 @@ mod _validation { match owned_rights.get(owned_type_id) { None => {} Some(AssignmentVec::Declarative(set)) => set.iter().for_each(|data| { - status += assignment.validate(&node_id, *owned_type_id, data) + status += + assignment.validate(&self.type_system, &node_id, *owned_type_id, data) }), Some(AssignmentVec::Fungible(set)) => set.iter().for_each(|data| { - status += assignment.validate(&node_id, *owned_type_id, data) + status += + assignment.validate(&self.type_system, &node_id, *owned_type_id, data) }), Some(AssignmentVec::NonFungible(set)) => set.iter().for_each(|data| { - status += assignment.validate(&node_id, *owned_type_id, data) + status += + assignment.validate(&self.type_system, &node_id, *owned_type_id, data) }), Some(AssignmentVec::Attachment(set)) => set.iter().for_each(|data| { - status += assignment.validate(&node_id, *owned_type_id, data) + status += + assignment.validate(&self.type_system, &node_id, *owned_type_id, data) }), }; } diff --git a/src/schema/state.rs b/src/schema/state.rs index 8474ed8a..a7005fe9 100644 --- a/src/schema/state.rs +++ b/src/schema/state.rs @@ -64,20 +64,118 @@ pub enum DiscreteFiniteFieldFormat { mod _validation { use core::any::Any; + use std::io; use amplify::AsAny; use commit_verify::CommitConceal; + use stens::{PrimitiveType, TypeConstr, TypeSystem, Validate}; use super::*; use crate::contract::AttachmentStrategy; use crate::schema::OwnedRightType; use crate::{ - validation, Assignment, DeclarativeStrategy, HashStrategy, NodeId, PedersenStrategy, State, + data, validation, Assignment, DeclarativeStrategy, HashStrategy, NodeId, PedersenStrategy, + State, }; + pub trait StenValidate { + fn validate( + &self, + type_system: &TypeSystem, + node_id: NodeId, + schema_type_id: u16, + data: &data::Revealed, + ) -> validation::Status; + } + + impl StenValidate for PrimitiveType { + fn validate( + &self, + _: &TypeSystem, + node_id: NodeId, + schema_type_id: u16, + data: &data::Revealed, + ) -> validation::Status { + let mut status = validation::Status::new(); + match (self, data) { + (PrimitiveType::U8, data::Revealed::U8(_)) + | (PrimitiveType::U16, data::Revealed::U16(_)) + | (PrimitiveType::U32, data::Revealed::U32(_)) + | (PrimitiveType::U64, data::Revealed::U64(_)) + | (PrimitiveType::U128, data::Revealed::U128(_)) + | (PrimitiveType::U256, data::Revealed::U256(_)) + | (PrimitiveType::U512, data::Revealed::U512(_)) + | (PrimitiveType::U1024, data::Revealed::U1024(_)) + | (PrimitiveType::I8, data::Revealed::I8(_)) + | (PrimitiveType::I16, data::Revealed::I16(_)) + | (PrimitiveType::I32, data::Revealed::I32(_)) + | (PrimitiveType::I64, data::Revealed::I64(_)) + | (PrimitiveType::I128, data::Revealed::I128(_)) + | (PrimitiveType::I256, data::Revealed::I256(_)) + | (PrimitiveType::I512, data::Revealed::I512(_)) + | (PrimitiveType::I1024, data::Revealed::I1024(_)) + | (PrimitiveType::F16b, data::Revealed::F16B(_)) + | (PrimitiveType::F16, data::Revealed::F16(_)) + | (PrimitiveType::F32, data::Revealed::F32(_)) + | (PrimitiveType::F64, data::Revealed::F64(_)) + | (PrimitiveType::F80, data::Revealed::F80(_)) + | (PrimitiveType::F128, data::Revealed::F128(_)) + | (PrimitiveType::F256, data::Revealed::F256(_)) => {} + _ => { + status.add_failure(validation::Failure::InvalidStateDataType( + node_id, + schema_type_id, + TypeRef::Primitive(TypeConstr::Plain(self.clone())), + data.clone(), + )); + } + } + status + } + } + + impl StenValidate for TypeRef { + fn validate( + &self, + type_system: &TypeSystem, + node_id: NodeId, + schema_type_id: u16, + data: &data::Revealed, + ) -> validation::Status { + let mut status = validation::Status::new(); + match (self, data) { + (TypeRef::Primitive(TypeConstr::Plain(ty)), _) => { + status += + StenValidate::validate(ty, type_system, node_id, schema_type_id, data); + } + (TypeRef::Named(ty), data::Revealed::Bytes(bytes)) => { + let mut cursor = io::Cursor::new(bytes.as_slice()); + if !ty.validate(type_system, &mut cursor) { + status.add_failure(validation::Failure::InvalidStateDataValue( + node_id, + schema_type_id, + self.clone(), + bytes.clone(), + )); + } + } + _ => { + status.add_failure(validation::Failure::InvalidStateDataType( + node_id, + schema_type_id, + self.clone(), + data.clone(), + )); + } + } + status + } + } + impl StateSchema { pub fn validate( &self, + type_system: &TypeSystem, node_id: &NodeId, assignment_id: OwnedRightType, data: &Assignment, @@ -188,8 +286,13 @@ mod _validation { ); } Some(data) => { - // TODO: [validation] validate type schema - // status += format.validate(assignment_id, data); + status += StenValidate::validate( + format, + type_system, + *node_id, + assignment_id, + data, + ); } } } @@ -209,6 +312,7 @@ mod _validation { } } } +pub(super) use _validation::StenValidate; #[cfg(test)] mod test { @@ -217,6 +321,7 @@ mod test { use bitcoin::hashes::sha256; use commit_verify::{CommitConceal, TaggedHash}; use secp256k1zkp::rand::thread_rng; + use stens::TypeSystem; use strict_encoding::StrictDecode; use super::*; @@ -307,77 +412,78 @@ mod test { let hash_format = StateSchema::CustomData(TypeRef::bytes()); // Assert different failure combinations + let ts = TypeSystem::default(); assert_eq!( dec_format - .validate(&node_id, 3u16, &assignment_ped_rev) + .validate(&ts, &node_id, 3u16, &assignment_ped_rev) .failures[0], Failure::SchemaMismatchedStateType(3) ); assert_eq!( dec_format - .validate(&node_id, 3u16, &assignment_ped_conf) + .validate(&ts, &node_id, 3u16, &assignment_ped_conf) .failures[0], Failure::SchemaMismatchedStateType(3) ); assert_eq!( dec_format - .validate(&node_id, 3u16, &assignment_hash_rev) + .validate(&ts, &node_id, 3u16, &assignment_hash_rev) .failures[0], Failure::SchemaMismatchedStateType(3) ); assert_eq!( dec_format - .validate(&node_id, 3u16, &assignment_hash_conf) + .validate(&ts, &node_id, 3u16, &assignment_hash_conf) .failures[0], Failure::SchemaMismatchedStateType(3) ); assert_eq!( ped_format - .validate(&node_id, 3u16, &assignment_dec_rev) + .validate(&ts, &node_id, 3u16, &assignment_dec_rev) .failures[0], Failure::SchemaMismatchedStateType(3) ); assert_eq!( ped_format - .validate(&node_id, 3u16, &assignment_dec_conf) + .validate(&ts, &node_id, 3u16, &assignment_dec_conf) .failures[0], Failure::SchemaMismatchedStateType(3) ); assert_eq!( ped_format - .validate(&node_id, 3u16, &assignment_hash_rev) + .validate(&ts, &node_id, 3u16, &assignment_hash_rev) .failures[0], Failure::SchemaMismatchedStateType(3) ); assert_eq!( ped_format - .validate(&node_id, 3u16, &assignment_hash_conf) + .validate(&ts, &node_id, 3u16, &assignment_hash_conf) .failures[0], Failure::SchemaMismatchedStateType(3) ); assert_eq!( hash_format - .validate(&node_id, 3u16, &assignment_dec_rev) + .validate(&ts, &node_id, 3u16, &assignment_dec_rev) .failures[0], Failure::SchemaMismatchedStateType(3) ); assert_eq!( hash_format - .validate(&node_id, 3u16, &assignment_dec_conf) + .validate(&ts, &node_id, 3u16, &assignment_dec_conf) .failures[0], Failure::SchemaMismatchedStateType(3) ); assert_eq!( hash_format - .validate(&node_id, 3u16, &assignment_ped_rev) + .validate(&ts, &node_id, 3u16, &assignment_ped_rev) .failures[0], Failure::SchemaMismatchedStateType(3) ); assert_eq!( hash_format - .validate(&node_id, 3u16, &assignment_ped_conf) + .validate(&ts, &node_id, 3u16, &assignment_ped_conf) .failures[0], Failure::SchemaMismatchedStateType(3) ); diff --git a/src/validation.rs b/src/validation.rs index 7e9a2766..18de6ab6 100644 --- a/src/validation.rs +++ b/src/validation.rs @@ -17,13 +17,14 @@ use bitcoin::{Transaction, Txid}; use bp::dbc::Anchor; use bp::seals::txout::TxoSeal; use commit_verify::{lnpbp4, CommitConceal}; +use stens::TypeRef; use wallet::onchain::ResolveTx; use super::schema::{NodeType, OccurrencesError}; use super::{schema, seal, AssignmentVec, ContractId, Node, NodeId, Schema, SchemaId}; use crate::schema::SchemaVerify; use crate::stash::Consignment; -use crate::{BundleId, Extension, SealEndpoint, TransitionBundle}; +use crate::{data, BundleId, Extension, SealEndpoint, TransitionBundle}; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Display)] #[display(Debug)] @@ -207,6 +208,9 @@ pub enum Failure { EndpointTransitionNotFound(NodeId), + InvalidStateDataType(NodeId, u16, TypeRef, data::Revealed), + InvalidStateDataValue(NodeId, u16, TypeRef, Vec), + /// invalid bulletproofs in {0}:{1}: {2} InvalidBulletproofs(NodeId, u16, secp256k1zkp::Error),