From 46f1334a0a02c7ff3b195516331cbda9f5227762 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Thu, 14 Dec 2023 21:01:32 +0100 Subject: [PATCH 01/20] validation: wip on refactoring --- Cargo.lock | 8 ++-- Cargo.toml | 8 ++-- src/contract/anchor.rs | 34 +++++++++++++--- src/contract/bundle.rs | 73 +++-------------------------------- src/contract/mod.rs | 2 +- src/validation/consignment.rs | 34 +++------------- src/validation/status.rs | 13 ++----- src/validation/validator.rs | 42 +++++++++----------- 8 files changed, 69 insertions(+), 145 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 808de33c..3c17ef56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -195,7 +195,7 @@ dependencies = [ [[package]] name = "bp-consensus" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-core?branch=v0.11#ea4e7c753cecfeabe252916a3c9651719364e70c" +source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#fbdd7f30d67551af598381885d9005630015921f" dependencies = [ "amplify", "chrono", @@ -209,7 +209,7 @@ dependencies = [ [[package]] name = "bp-core" version = "0.11.0-beta.1" -source = "git+https://github.com/BP-WG/bp-core?branch=v0.11#ea4e7c753cecfeabe252916a3c9651719364e70c" +source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#fbdd7f30d67551af598381885d9005630015921f" dependencies = [ "amplify", "bp-consensus", @@ -225,7 +225,7 @@ dependencies = [ [[package]] name = "bp-dbc" version = "0.11.0-beta.1" -source = "git+https://github.com/BP-WG/bp-core?branch=v0.11#ea4e7c753cecfeabe252916a3c9651719364e70c" +source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#fbdd7f30d67551af598381885d9005630015921f" dependencies = [ "amplify", "base85", @@ -239,7 +239,7 @@ dependencies = [ [[package]] name = "bp-seals" version = "0.11.0-beta.1" -source = "git+https://github.com/BP-WG/bp-core?branch=v0.11#ea4e7c753cecfeabe252916a3c9651719364e70c" +source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#fbdd7f30d67551af598381885d9005630015921f" dependencies = [ "amplify", "baid58", diff --git a/Cargo.toml b/Cargo.toml index 7c9a757c..037f0adf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ wasm-bindgen-test = "0.3" features = [ "all" ] [patch.crates-io] -bp-consensus = { git = "https://github.com/BP-WG/bp-core", branch = "v0.11" } -bp-dbc = { git = "https://github.com/BP-WG/bp-core", branch = "v0.11" } -bp-seals = { git = "https://github.com/BP-WG/bp-core", branch = "v0.11" } -bp-core = { git = "https://github.com/BP-WG/bp-core", branch = "v0.11" } +bp-consensus = { git = "https://github.com/BP-WG/bp-core", branch = "doubleanchors" } +bp-dbc = { git = "https://github.com/BP-WG/bp-core", branch = "doubleanchors" } +bp-seals = { git = "https://github.com/BP-WG/bp-core", branch = "doubleanchors" } +bp-core = { git = "https://github.com/BP-WG/bp-core", branch = "doubleanchors" } diff --git a/src/contract/anchor.rs b/src/contract/anchor.rs index 5058356c..19be29ba 100644 --- a/src/contract/anchor.rs +++ b/src/contract/anchor.rs @@ -25,6 +25,8 @@ use std::ops::Deref; use bp::dbc; use bp::dbc::anchor::MergeError; +use bp::dbc::opret::OpretProof; +use bp::dbc::tapret::TapretProof; use commit_verify::mpc; use strict_encoding::StrictDumb; @@ -53,14 +55,14 @@ pub struct AnchoredBundle { )] pub enum Anchor { #[strict_type(tag = 0x00)] - Bitcoin(dbc::Anchor

), + Bitcoin(AnchorSet

), #[strict_type(tag = 0x01)] - Liquid(dbc::Anchor

), + Liquid(AnchorSet

), } impl Deref for Anchor

{ - type Target = dbc::Anchor

; + type Target = AnchorSet

; #[inline] fn deref(&self) -> &Self::Target { @@ -87,10 +89,10 @@ impl Anchor

{ } } - pub fn map( + pub fn map( self, - f: impl FnOnce(dbc::Anchor

) -> Result, E>, - ) -> Result, E> { + f: impl FnOnce(dbc::Anchor) -> Result, E>, + ) -> Result, E> { match self { Anchor::Bitcoin(anchor) => f(anchor).map(Anchor::Bitcoin), Anchor::Liquid(anchor) => f(anchor).map(Anchor::Liquid), @@ -112,6 +114,26 @@ impl Anchor { } } +#[derive(Clone, Eq, PartialEq, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB, tags = custom, dumb = Self::Taptet(strict_dumb!()))] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", rename_all = "camelCase") +)] +pub enum AnchorSet { + #[strict_type(tag = 0x01)] + Taptet(dbc::Anchor), + #[strict_type(tag = 0x02)] + Opret(dbc::Anchor), + #[strict_type(tag = 0x03)] + Dual { + tapret: dbc::Anchor, + opret: dbc::Anchor, + }, +} + /// Txid and height information ordered according to the RGB consensus rules. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] diff --git a/src/contract/bundle.rs b/src/contract/bundle.rs index c330ce2c..ce4f4094 100644 --- a/src/contract/bundle.rs +++ b/src/contract/bundle.rs @@ -20,28 +20,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -use amplify::confinement::{TinyOrdMap, TinyOrdSet}; +use std::collections::BTreeMap; + +use amplify::confinement::{Confined, TinyOrdMap, U16}; use amplify::{Bytes32, Wrapper}; use commit_verify::{mpc, CommitStrategy, CommitmentId, Conceal}; -use super::{OpId, Transition}; -use crate::{ContractId, LIB_NAME_RGB}; - -#[derive(Copy, Clone, Eq, PartialEq, Debug, Display, Error)] -#[display(doc_comments)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub enum BundleError { - /// state transitions created under different contracts. - DivergentContracts, - /// no state transitions. - EmptyBundle, - /// state transitions reference to the same input multiple times ({0}). - RepeatedInputs(usize), -} +use super::OpId; +use crate::LIB_NAME_RGB; /// Unique state transition bundle identifier equivalent to the bundle /// commitment hash @@ -68,33 +54,13 @@ impl From for BundleId { fn from(id: mpc::Message) -> Self { BundleId(id.into_inner()) } } -#[derive(Clone, PartialEq, Eq, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] -pub struct BundleItem { - pub inputs: TinyOrdSet, - pub transition: Option, -} - -impl Conceal for BundleItem { - type Concealed = Self; - - fn conceal(&self) -> Self::Concealed { - BundleItem { - inputs: self.inputs.clone(), - transition: None, - } - } -} - #[derive(Wrapper, WrapperMut, Clone, PartialEq, Eq, Debug, From)] #[wrapper(Deref)] #[wrapper_mut(DerefMut)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] -pub struct TransitionBundle(TinyOrdMap); +pub struct TransitionBundle(Confined, 1, U16>); impl Conceal for TransitionBundle { type Concealed = Self; @@ -118,30 +84,3 @@ impl CommitmentId for TransitionBundle { impl TransitionBundle { pub fn bundle_id(&self) -> BundleId { self.commitment_id() } } - -impl TransitionBundle { - pub fn validate(&self) -> Result { - let mut contract_id = None; - let mut used_inputs = bset! {}; - for item in self.values() { - if !contract_id - .and_then(|id| { - item.transition - .as_ref() - .map(|t| (id, t)) - .map(|(id, t)| id == t.contract_id) - }) - .unwrap_or_default() - { - return Err(BundleError::DivergentContracts); - } - contract_id = item.transition.as_ref().map(|t| t.contract_id); - let repeated_inputs = used_inputs.intersection(&item.inputs).count(); - if repeated_inputs > 0 { - return Err(BundleError::RepeatedInputs(repeated_inputs)); - } - used_inputs.extend(&item.inputs); - } - contract_id.ok_or(BundleError::EmptyBundle) - } -} diff --git a/src/contract/mod.rs b/src/contract/mod.rs index 0b7e6d9f..73db6f16 100644 --- a/src/contract/mod.rs +++ b/src/contract/mod.rs @@ -42,7 +42,7 @@ pub use assignments::{ TypedAssigns, }; pub use attachment::{AttachId, ConcealedAttach, RevealedAttach}; -pub use bundle::{BundleError, BundleId, BundleItem, TransitionBundle}; +pub use bundle::{BundleId, TransitionBundle}; use commit_verify::CommitEncode; pub use contract::{ AttachOutput, ContractHistory, ContractState, DataOutput, FungibleOutput, GlobalOrd, Opout, diff --git a/src/validation/consignment.rs b/src/validation/consignment.rs index c417c498..3cd356ca 100644 --- a/src/validation/consignment.rs +++ b/src/validation/consignment.rs @@ -27,8 +27,8 @@ use std::collections::{BTreeMap, BTreeSet}; use crate::{ - AnchoredBundle, AssetTag, AssignmentType, BundleId, Extension, Genesis, OpId, OpRef, - SecretSeal, SubSchema, Transition, TransitionBundle, + AnchoredBundle, AssetTag, AssignmentType, BundleId, Genesis, OpId, OpRef, SecretSeal, + SubSchema, Transition, }; /// Trait defining common data access API for all storage-related RGB structures @@ -44,6 +44,8 @@ use crate::{ pub trait ConsignmentApi { type BundleIter<'container>: Iterator where Self: 'container; + type TransitionIter<'container>: Iterator + where Self: 'container; fn schema(&self) -> &SubSchema; @@ -57,30 +59,6 @@ pub trait ConsignmentApi { /// Contract genesis. fn genesis(&self) -> &Genesis; - /// Returns reference to a state transition, if known, matching the provided - /// id. If id is unknown, or corresponds to other type of the operation - /// (genesis or state extensions) a error is returned. - /// - /// # Errors - /// - /// - [`Error::WrongNodeType`] when operation is present, but has some other - /// operation type - /// - [`Error::TransitionAbsent`] when operation with the given id is absent - /// from the storage/container - fn transition(&self, opid: OpId) -> Option<&Transition>; - - /// Returns reference to a state extension, if known, matching the provided - /// id. If id is unknown, or corresponds to other type of the operation - /// (genesis or state transition) a error is returned. - /// - /// # Errors - /// - /// - [`Error::WrongNodeType`] when operation is present, but has some other - /// operation type - /// - [`Error::ExtensionAbsent`] when operation with the given id is absent - /// from the storage/container - fn extension(&self, opid: OpId) -> Option<&Extension>; - /// The final state ("endpoints") provided by this consignment. /// /// There are two reasons for having endpoints: @@ -95,11 +73,9 @@ pub trait ConsignmentApi { /// Data on all anchored state transitions contained in the consignment fn anchored_bundles(&self) -> Self::BundleIter<'_>; - fn bundle_by_id(&self, bundle_id: BundleId) -> Option<&TransitionBundle>; - fn op_ids_except(&self, ids: &BTreeSet) -> BTreeSet; fn has_operation(&self, opid: OpId) -> bool; - fn known_transitions_by_bundle_id(&self, bundle_id: BundleId) -> Option>; + fn known_transitions_in_bundle(&self, bundle_id: BundleId) -> Self::TransitionIter<'_>; } diff --git a/src/validation/status.rs b/src/validation/status.rs index 8011423c..275d573a 100644 --- a/src/validation/status.rs +++ b/src/validation/status.rs @@ -32,8 +32,7 @@ use strict_types::SemId; use crate::contract::Opout; use crate::schema::{self, SchemaId}; use crate::{ - BundleError, BundleId, Layer1, OccurrencesMismatch, OpFullType, OpId, SecretSeal, StateType, - Xchain, + ContractId, Layer1, OccurrencesMismatch, OpFullType, OpId, SecretSeal, StateType, Xchain, }; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Display)] @@ -299,11 +298,8 @@ pub enum Failure { OperationAbsent(OpId), /// state transition {0} is absent from the consignment. TransitionAbsent(OpId), - /// bundle with id {0} {1} - BundleInvalid(BundleId, BundleError), - /// consignment misses bundle with id {0} which must contain one of - /// terminals. - BundleMissed(BundleId), + /// operation {0} is under a different contract {1}. + ContractMismatch(OpId, ContractId), // Errors checking seal closing /// transition {0} is not anchored. @@ -322,9 +318,6 @@ pub enum Failure { /// seal defined in the history as a part of operation output {0} is /// confidential and can't be validated. ConfidentialSeal(Opout), - /// transition {0} is not a part of multi-protocol commitment for witness - /// {1}; anchor is invalid. - MpcInvalid(OpId, Txid), /// witness transaction {0} is not known to the transaction resolver. SealNoWitnessTx(Txid), /// witness layer 1 {anchor} doesn't match seal definition {seal}. diff --git a/src/validation/validator.rs b/src/validation/validator.rs index 4b653786..da548270 100644 --- a/src/validation/validator.rs +++ b/src/validation/validator.rs @@ -32,8 +32,8 @@ use super::{ConsignmentApi, Status, Validity, VirtualMachine}; use crate::vm::AluRuntime; use crate::{ AltLayer1, Anchor, AnchoredBundle, BundleId, ContractId, GraphSeal, Layer1, OpId, OpRef, - Operation, Opout, Schema, SchemaId, SchemaRoot, Script, SubSchema, Transition, - TransitionBundle, TypedAssigns, Xchain, + Operation, Opout, Schema, SchemaId, SchemaRoot, Script, SubSchema, Transition, TypedAssigns, + Xchain, }; #[derive(Clone, Debug, Display, Error, From)] @@ -88,12 +88,8 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> ref bundle, } in consignment.anchored_bundles() { - if let Err(err) = TransitionBundle::validate(bundle) { - status.add_failure(Failure::BundleInvalid(bundle.bundle_id(), err)); - } - for transition in bundle.values().filter_map(|item| item.transition.as_ref()) { - let opid = transition.id(); - anchor_index.insert(opid, anchor); + for opid in bundle.values() { + anchor_index.insert(*opid, anchor); } } @@ -103,11 +99,7 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> // about them (in form of generated warnings) let mut end_transitions = Vec::<(&Transition, BundleId)>::new(); for (bundle_id, seal_endpoint) in consignment.terminals() { - let Some(transitions) = consignment.known_transitions_by_bundle_id(bundle_id) else { - status.add_failure(Failure::BundleMissed(bundle_id)); - continue; - }; - for transition in transitions { + for transition in consignment.known_transitions_in_bundle(bundle_id) { let opid = transition.id(); // Checking for endpoint definition duplicates if !transition @@ -365,6 +357,12 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> bundle_id: BundleId, anchor: &'consignment Anchor, ) { + if transition.contract_id != self.contract_id { + self.status + .add_failure(Failure::ContractMismatch(transition.id(), transition.contract_id)); + return; + } + let (layer1, anchor) = match anchor { Anchor::Bitcoin(a) | Anchor::Liquid(a) => (anchor.layer1(), a), }; @@ -495,9 +493,13 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> } let message = mpc::Message::from(bundle_id); - match anchor.convolve(self.contract_id, message) { - Err(_) => { - self.status.add_failure(Failure::MpcInvalid(opid, txid)); + // [VALIDATION]: Checking anchor deterministic bitcoin commitment + match anchor.verify(self.contract_id, message, &witness.tx) { + Err(err) => { + // The operation is not committed to bitcoin transaction graph! + // Ultimate failure. But continuing to detect the rest (after reporting it). + self.status + .add_failure(Failure::AnchorInvalid(opid, txid, err)); } Ok(commitment) => { // [VALIDATION]: CHECKING SINGLE-USE-SEALS @@ -510,13 +512,5 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> .ok(); } } - - // [VALIDATION]: Checking anchor deterministic bitcoin commitment - if let Err(err) = anchor.verify(self.contract_id, message, &witness.tx) { - // The operation is not committed to bitcoin transaction graph! - // Ultimate failure. But continuing to detect the rest (after reporting it). - self.status - .add_failure(Failure::AnchorInvalid(opid, txid, err)); - } } } From a21ed8ba15385757febaaf6bf0f834a0005d6581 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Thu, 14 Dec 2023 21:19:35 +0100 Subject: [PATCH 02/20] validation: re-arranging blocks of validation logic --- src/validation/validator.rs | 103 +++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 50 deletions(-) diff --git a/src/validation/validator.rs b/src/validation/validator.rs index da548270..20e3e425 100644 --- a/src/validation/validator.rs +++ b/src/validation/validator.rs @@ -156,10 +156,10 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> /// transaction id, and returns a validation object listing all detected /// failures, warnings and additional information. /// - /// When a failure detected, it not stopped; the failure is is logged into - /// the status object, but the validation continues for the rest of the - /// consignment data. This can help it debugging and detecting all problems - /// with the consignment. + /// When a failure detected, validation is not stopped; the failure is + /// logged into the status object, but the validation continues for the + /// rest of the consignment data. This can help it debugging and + /// detecting all problems with the consignment. pub fn validate(consignment: &'consignment C, resolver: &'resolver R, testnet: bool) -> Status { let mut validator = Validator::init(consignment, resolver); @@ -176,12 +176,13 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> } // We must return here, since if the schema is not valid there is no reason to - // validate contract nodes against it: it will produce a plenty of errors + // validate contract nodes against it: it will produce a plenty of errors. if validator.status.validity() == Validity::Invalid { return validator.status; } - validator.validate_contract(consignment.schema()); + validator.validate_logic(consignment.schema()); + validator.validate_commitments(); // Done. Returning status report with all possible failures, issues, warnings // and notifications about transactions we were unable to obtain. @@ -190,7 +191,7 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> fn validate_schema(&mut self, schema: &SubSchema) { self.status += schema.verify(); } - fn validate_contract(&mut self, schema: &Schema) { + fn validate_logic(&mut self, schema: &Schema) { // [VALIDATION]: Making sure that we were supplied with the schema // that corresponds to the schema of the contract genesis if schema.schema_id() != self.schema_id { @@ -217,13 +218,43 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> // NB: We are not aiming to validate the consignment as a whole, but instead // treat it as a superposition of subgraphs, one for each endpoint; and validate // them independently. - for (operation, bundle_id) in self.end_transitions.clone() { - self.validate_branch(schema, operation, bundle_id); + for (transition, _) in &self.end_transitions { + self.validate_branch(schema, transition); } + + // Generate warning if some of the transitions within the consignment were + // excessive (i.e. not part of validation_index). Nothing critical, but still + // good to report the user that the consignment is not perfect + for opid in self.consignment.op_ids_except(&self.validation_index) { + self.status.add_warning(Warning::ExcessiveOperation(opid)); + } + } + + fn validate_commitments(&mut self) { // Replace missed (not yet mined) endpoint witness transaction failures // with a dedicated type - for (operation, _) in &self.end_transitions { - if let Some(anchor) = self.anchor_index.get(&operation.id()) { + for (transition, bundle_id) in &self.end_transitions { + let opid = transition.id(); + + if let Some(anchor) = self.anchor_index.get(&opid) { + // Making sure we do have a corresponding anchor; otherwise reporting failure + // (see below) - with the except of genesis and extension nodes, which does not + // have a corresponding anchor + if !self.anchor_validation_index.contains(&opid) { + // Ok, now we have the `transition` and the `anchor`, let's do all + // required checks + + // [VALIDATION]: Check that transition is committed into the anchor. + // This must be done with deterministic bitcoin + // commitments & LNPBP-4. + if anchor.convolve(self.contract_id, bundle_id.into()).is_err() { + self.status.add_failure(Failure::NotInAnchor(opid)); + } + + self.validate_anchor(transition, *bundle_id, anchor); + self.anchor_validation_index.insert(opid); + } + let anchor = match anchor { Anchor::Bitcoin(anchor) | Anchor::Liquid(anchor) => anchor, }; @@ -243,22 +274,18 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> .warnings .push(Warning::TerminalWitnessNotMined(anchor.txid)); } + } else { + // If we've got here there is something broken with the consignment + // provider. + self.status.add_failure(Failure::NotAnchored(opid)); } } - - // Generate warning if some of the transitions within the consignment were - // excessive (i.e. not part of validation_index). Nothing critical, but still - // good to report the user that the consignment is not perfect - for opid in self.consignment.op_ids_except(&self.validation_index) { - self.status.add_warning(Warning::ExcessiveOperation(opid)); - } } fn validate_branch( &mut self, schema: &Schema, transition: &'consignment Transition, - bundle_id: BundleId, ) { let mut queue: VecDeque = VecDeque::new(); @@ -275,6 +302,12 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> while let Some(operation) = queue.pop_front() { let opid = operation.id(); + if operation.contract_id() != self.contract_id { + self.status + .add_failure(Failure::ContractMismatch(opid, operation.contract_id())); + continue; + } + // [VALIDATION]: Verify operation against the schema and scripts if !self.validation_index.contains(&opid) { self.status += schema.validate(self.consignment, operation, self.vm.as_ref()); @@ -286,30 +319,6 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> // nothing to add to the queue here } OpRef::Transition(transition) => { - // Making sure we do have a corresponding anchor; otherwise reporting failure - // (see below) - with the except of genesis and extension nodes, which does not - // have a corresponding anchor - if let Some(anchor) = self.anchor_index.get(&opid).cloned() { - if !self.anchor_validation_index.contains(&opid) { - // Ok, now we have the `operation` and the `anchor`, let's do all - // required checks - - // [VALIDATION]: Check that transition is committed into the anchor. - // This must be done with deterministic bitcoin - // commitments & LNPBP-4. - if anchor.convolve(self.contract_id, bundle_id.into()).is_err() { - self.status.add_failure(Failure::NotInAnchor(opid)); - } - - self.validate_transition(transition, bundle_id, anchor); - self.anchor_validation_index.insert(opid); - } - } else { - // If we've got here there is something broken with the consignment - // provider. - self.status.add_failure(Failure::NotAnchored(opid)); - } - // 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(|| { @@ -351,18 +360,12 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> } } - fn validate_transition( + fn validate_anchor( &mut self, transition: &'consignment Transition, bundle_id: BundleId, anchor: &'consignment Anchor, ) { - if transition.contract_id != self.contract_id { - self.status - .add_failure(Failure::ContractMismatch(transition.id(), transition.contract_id)); - return; - } - let (layer1, anchor) = match anchor { Anchor::Bitcoin(a) | Anchor::Liquid(a) => (anchor.layer1(), a), }; From 0c01bc32d8d03410ee0cce481a52781fb02f33e5 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 15 Dec 2023 10:42:40 +0100 Subject: [PATCH 03/20] validation: avoid double DBC validation in anchor --- src/validation/status.rs | 3 ++- src/validation/validator.rs | 24 ++++++++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/validation/status.rs b/src/validation/status.rs index 275d573a..256d7db3 100644 --- a/src/validation/status.rs +++ b/src/validation/status.rs @@ -27,6 +27,7 @@ use std::fmt::{self, Display, Formatter}; use bp::dbc::anchor; use bp::seals::txout::blind::ChainBlindSeal; use bp::{seals, Txid}; +use commit_verify::mpc::InvalidProof; use strict_types::SemId; use crate::contract::Opout; @@ -330,7 +331,7 @@ pub enum Failure { SealInvalid(OpId, Txid, seals::txout::VerifyError), /// transition {0} is not properly anchored to the witness transaction {1}. /// Details: {2} - AnchorInvalid(OpId, Txid, anchor::VerifyError), + MpcInvalid(OpId, Txid, InvalidProof), // State extensions errors /// valency {valency} redeemed by state extension {opid} references diff --git a/src/validation/validator.rs b/src/validation/validator.rs index 20e3e425..0761a3c9 100644 --- a/src/validation/validator.rs +++ b/src/validation/validator.rs @@ -231,6 +231,8 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> } fn validate_commitments(&mut self) { + for anchor in &self.anchor_index {} + // Replace missed (not yet mined) endpoint witness transaction failures // with a dedicated type for (transition, bundle_id) in &self.end_transitions { @@ -396,12 +398,22 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> } } - fn validate_witness( + /// Single-use-seal closing validation. + /// + /// Takes state transition, extracts all seals from its inputs. Checks that + /// the set of seals is closed over the message, which is multi-protocol + /// commitment, by utilizing witness, consisting of transaction with + /// deterministic bitcoin commitments (defined by generic type `Dbc`) and + /// extra-transaction data, which are taken from anchors DBC proof. + /// + /// Additionally checks that the provided message contains commitment to the + /// bundle under the current contract. + fn validate_witness( &mut self, transition: &'consignment Transition, - witness: Witness, + witness: Witness, bundle_id: BundleId, - anchor: &'consignment dbc::Anchor, + anchor: &'consignment dbc::Anchor, ) { let opid = transition.id(); let txid = witness.txid; @@ -496,13 +508,13 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> } let message = mpc::Message::from(bundle_id); - // [VALIDATION]: Checking anchor deterministic bitcoin commitment - match anchor.verify(self.contract_id, message, &witness.tx) { + // [VALIDATION]: Checking anchor MPC commitment + match anchor.convolve(self.contract_id, message) { Err(err) => { // The operation is not committed to bitcoin transaction graph! // Ultimate failure. But continuing to detect the rest (after reporting it). self.status - .add_failure(Failure::AnchorInvalid(opid, txid, err)); + .add_failure(Failure::MpcInvalid(opid, txid, err)); } Ok(commitment) => { // [VALIDATION]: CHECKING SINGLE-USE-SEALS From ffe7c81a34ea835f1a8e406ec5856db99cb07370 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 15 Dec 2023 11:15:08 +0100 Subject: [PATCH 04/20] contract: refactor TransitionBundle --- src/contract/bundle.rs | 41 +++++++++++++++++++++------------------- src/contract/mod.rs | 2 +- src/validation/status.rs | 1 - 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/contract/bundle.rs b/src/contract/bundle.rs index ce4f4094..25d64a3b 100644 --- a/src/contract/bundle.rs +++ b/src/contract/bundle.rs @@ -21,13 +21,18 @@ // limitations under the License. use std::collections::BTreeMap; +use std::io::Write; -use amplify::confinement::{Confined, TinyOrdMap, U16}; +use amplify::confinement::{Confined, U16}; use amplify::{Bytes32, Wrapper}; -use commit_verify::{mpc, CommitStrategy, CommitmentId, Conceal}; +use bp::Vout; +use commit_verify::{mpc, CommitEncode, CommitmentId}; +use strict_encoding::{StrictEncode, StrictWriter}; use super::OpId; -use crate::LIB_NAME_RGB; +use crate::{Transition, LIB_NAME_RGB}; + +pub type Vin = Vout; /// Unique state transition bundle identifier equivalent to the bundle /// commitment hash @@ -54,26 +59,24 @@ impl From for BundleId { fn from(id: mpc::Message) -> Self { BundleId(id.into_inner()) } } -#[derive(Wrapper, WrapperMut, Clone, PartialEq, Eq, Debug, From)] -#[wrapper(Deref)] -#[wrapper_mut(DerefMut)] +#[derive(Clone, PartialEq, Eq, Debug, From)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] -pub struct TransitionBundle(Confined, 1, U16>); - -impl Conceal for TransitionBundle { - type Concealed = Self; - - fn conceal(&self) -> Self::Concealed { - let concealed = self.iter().map(|(id, item)| (*id, item.conceal())); - TransitionBundle(TinyOrdMap::try_from_iter(concealed).expect("same size")) - } +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", rename_all = "camelCase") +)] +pub struct TransitionBundle { + input_map: Confined, 1, U16>, + known_transitions: Confined, 1, U16>, } -impl CommitStrategy for TransitionBundle { - // TODO: Use merklization strategy - type Strategy = commit_verify::strategies::ConcealStrict; +impl CommitEncode for TransitionBundle { + fn commit_encode(&self, e: &mut impl Write) { + let w = StrictWriter::with(u32::MAX as usize, e); + self.input_map.strict_encode(w).ok(); + } } impl CommitmentId for TransitionBundle { diff --git a/src/contract/mod.rs b/src/contract/mod.rs index 73db6f16..116fd805 100644 --- a/src/contract/mod.rs +++ b/src/contract/mod.rs @@ -42,7 +42,7 @@ pub use assignments::{ TypedAssigns, }; pub use attachment::{AttachId, ConcealedAttach, RevealedAttach}; -pub use bundle::{BundleId, TransitionBundle}; +pub use bundle::{BundleId, TransitionBundle, Vin}; use commit_verify::CommitEncode; pub use contract::{ AttachOutput, ContractHistory, ContractState, DataOutput, FungibleOutput, GlobalOrd, Opout, diff --git a/src/validation/status.rs b/src/validation/status.rs index 256d7db3..3d80bd2e 100644 --- a/src/validation/status.rs +++ b/src/validation/status.rs @@ -24,7 +24,6 @@ use core::iter::FromIterator; use core::ops::AddAssign; use std::fmt::{self, Display, Formatter}; -use bp::dbc::anchor; use bp::seals::txout::blind::ChainBlindSeal; use bp::{seals, Txid}; use commit_verify::mpc::InvalidProof; From 10d8436464acbc8da8bcbc891679400e9e38d214 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 15 Dec 2023 11:15:24 +0100 Subject: [PATCH 05/20] validation: move validation blocks into more logic sequence --- src/validation/validator.rs | 115 ++++++++++++++++++------------------ 1 file changed, 59 insertions(+), 56 deletions(-) diff --git a/src/validation/validator.rs b/src/validation/validator.rs index 0761a3c9..742c0d74 100644 --- a/src/validation/validator.rs +++ b/src/validation/validator.rs @@ -189,8 +189,10 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> validator.status } + // *** PART I: Schema validation fn validate_schema(&mut self, schema: &SubSchema) { self.status += schema.verify(); } + // *** PART II: Validating business logic fn validate_logic(&mut self, schema: &Schema) { // [VALIDATION]: Making sure that we were supplied with the schema // that corresponds to the schema of the contract genesis @@ -219,7 +221,7 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> // treat it as a superposition of subgraphs, one for each endpoint; and validate // them independently. for (transition, _) in &self.end_transitions { - self.validate_branch(schema, transition); + self.validate_logic_on_route(schema, transition); } // Generate warning if some of the transitions within the consignment were @@ -230,61 +232,7 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> } } - fn validate_commitments(&mut self) { - for anchor in &self.anchor_index {} - - // Replace missed (not yet mined) endpoint witness transaction failures - // with a dedicated type - for (transition, bundle_id) in &self.end_transitions { - let opid = transition.id(); - - if let Some(anchor) = self.anchor_index.get(&opid) { - // Making sure we do have a corresponding anchor; otherwise reporting failure - // (see below) - with the except of genesis and extension nodes, which does not - // have a corresponding anchor - if !self.anchor_validation_index.contains(&opid) { - // Ok, now we have the `transition` and the `anchor`, let's do all - // required checks - - // [VALIDATION]: Check that transition is committed into the anchor. - // This must be done with deterministic bitcoin - // commitments & LNPBP-4. - if anchor.convolve(self.contract_id, bundle_id.into()).is_err() { - self.status.add_failure(Failure::NotInAnchor(opid)); - } - - self.validate_anchor(transition, *bundle_id, anchor); - self.anchor_validation_index.insert(opid); - } - - let anchor = match anchor { - Anchor::Bitcoin(anchor) | Anchor::Liquid(anchor) => anchor, - }; - - if let Some(pos) = self - .status - .failures - .iter() - .position(|f| f == &Failure::SealNoWitnessTx(anchor.txid)) - { - self.status.failures.remove(pos); - self.status - .unresolved_txids - .retain(|txid| *txid != anchor.txid); - self.status.unmined_terminals.push(anchor.txid); - self.status - .warnings - .push(Warning::TerminalWitnessNotMined(anchor.txid)); - } - } else { - // If we've got here there is something broken with the consignment - // provider. - self.status.add_failure(Failure::NotAnchored(opid)); - } - } - } - - fn validate_branch( + fn validate_logic_on_route( &mut self, schema: &Schema, transition: &'consignment Transition, @@ -362,6 +310,61 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> } } + // *** PART III: Validating single-use-seals + fn validate_commitments(&mut self) { + for anchor in &self.anchor_index {} + + // Replace missed (not yet mined) endpoint witness transaction failures + // with a dedicated type + for (transition, bundle_id) in &self.end_transitions { + let opid = transition.id(); + + if let Some(anchor) = self.anchor_index.get(&opid) { + // Making sure we do have a corresponding anchor; otherwise reporting failure + // (see below) - with the except of genesis and extension nodes, which does not + // have a corresponding anchor + if !self.anchor_validation_index.contains(&opid) { + // Ok, now we have the `transition` and the `anchor`, let's do all + // required checks + + // [VALIDATION]: Check that transition is committed into the anchor. + // This must be done with deterministic bitcoin + // commitments & LNPBP-4. + if anchor.convolve(self.contract_id, bundle_id.into()).is_err() { + self.status.add_failure(Failure::NotInAnchor(opid)); + } + + self.validate_anchor(transition, *bundle_id, anchor); + self.anchor_validation_index.insert(opid); + } + + let anchor = match anchor { + Anchor::Bitcoin(anchor) | Anchor::Liquid(anchor) => anchor, + }; + + if let Some(pos) = self + .status + .failures + .iter() + .position(|f| f == &Failure::SealNoWitnessTx(anchor.txid)) + { + self.status.failures.remove(pos); + self.status + .unresolved_txids + .retain(|txid| *txid != anchor.txid); + self.status.unmined_terminals.push(anchor.txid); + self.status + .warnings + .push(Warning::TerminalWitnessNotMined(anchor.txid)); + } + } else { + // If we've got here there is something broken with the consignment + // provider. + self.status.add_failure(Failure::NotAnchored(opid)); + } + } + } + fn validate_anchor( &mut self, transition: &'consignment Transition, From ab68b792a457e01bca0bed0b37f4cb108a8d5662 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 15 Dec 2023 11:17:00 +0100 Subject: [PATCH 06/20] validation: enhance order of validation blocks --- src/validation/validator.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/validation/validator.rs b/src/validation/validator.rs index 742c0d74..cd8b97d1 100644 --- a/src/validation/validator.rs +++ b/src/validation/validator.rs @@ -162,12 +162,8 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> /// detecting all problems with the consignment. pub fn validate(consignment: &'consignment C, resolver: &'resolver R, testnet: bool) -> Status { let mut validator = Validator::init(consignment, resolver); - - validator.validate_schema(consignment.schema()); - // If the network mismatches there is no point in validating the contract since - // all witness transactions will be missed. Thus, we return early (however after - // schema validation, which is not network-specific). + // all witness transactions will be missed. if testnet != validator.consignment.genesis().testnet { validator .status @@ -175,15 +171,21 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> return validator.status; } + validator.validate_schema(consignment.schema()); // We must return here, since if the schema is not valid there is no reason to // validate contract nodes against it: it will produce a plenty of errors. if validator.status.validity() == Validity::Invalid { return validator.status; } - validator.validate_logic(consignment.schema()); validator.validate_commitments(); + // We must return here, since if there were no proper commitments, it is + // pointless to validate the contract state. + if validator.status.validity() == Validity::Invalid { + return validator.status; + } + validator.validate_logic(consignment.schema()); // Done. Returning status report with all possible failures, issues, warnings // and notifications about transactions we were unable to obtain. validator.status From afafbe40e2d86aa6f212c5276b69c4a3370af45e Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 15 Dec 2023 11:20:05 +0100 Subject: [PATCH 07/20] validation: fix valency validation --- src/validation/consignment.rs | 2 -- src/validation/model.rs | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/validation/consignment.rs b/src/validation/consignment.rs index 3cd356ca..b7973242 100644 --- a/src/validation/consignment.rs +++ b/src/validation/consignment.rs @@ -76,6 +76,4 @@ pub trait ConsignmentApi { fn op_ids_except(&self, ids: &BTreeSet) -> BTreeSet; fn has_operation(&self, opid: OpId) -> bool; - - fn known_transitions_in_bundle(&self, bundle_id: BundleId) -> Self::TransitionIter<'_>; } diff --git a/src/validation/model.rs b/src/validation/model.rs index c8f76375..0ca0fcdc 100644 --- a/src/validation/model.rs +++ b/src/validation/model.rs @@ -556,13 +556,13 @@ fn extract_redeemed_valencies( redeemed: &Redeemed, status: &mut validation::Status, ) -> Valencies { - let mut public_rights = Valencies::default(); + let mut valencies = Valencies::default(); for (valency, id) in redeemed.iter() { if consignment.has_operation(*id) { - status.add_failure(validation::Failure::OperationAbsent(*id)); + valencies.push(*valency).expect("same size"); } else { - public_rights.push(*valency).expect("same size"); + status.add_failure(validation::Failure::OperationAbsent(*id)); } } - public_rights + valencies } From cae2cd053f13a4e1daf03928dea14a4b21921c8b Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 15 Dec 2023 12:56:35 +0100 Subject: [PATCH 08/20] validation: refactor workflow for anchor/DBC/seals validation with bundling --- src/contract/anchor.rs | 13 +- src/contract/bundle.rs | 17 +- src/contract/mod.rs | 2 +- src/contract/seal.rs | 7 + src/validation/consignment.rs | 60 +++-- src/validation/{model.rs => logic.rs} | 10 +- src/validation/mod.rs | 6 +- src/validation/status.rs | 32 +-- src/validation/validator.rs | 342 +++++++++++--------------- 9 files changed, 241 insertions(+), 248 deletions(-) rename src/validation/{model.rs => logic.rs} (98%) diff --git a/src/contract/anchor.rs b/src/contract/anchor.rs index 19be29ba..b425ff98 100644 --- a/src/contract/anchor.rs +++ b/src/contract/anchor.rs @@ -23,10 +23,10 @@ use std::cmp::Ordering; use std::ops::Deref; -use bp::dbc; use bp::dbc::anchor::MergeError; use bp::dbc::opret::OpretProof; use bp::dbc::tapret::TapretProof; +use bp::{dbc, Txid}; use commit_verify::mpc; use strict_encoding::StrictDumb; @@ -134,6 +134,17 @@ pub enum AnchorSet { }, } +impl AnchorSet

{ + pub fn txid(&self) -> Option { + match self { + AnchorSet::Taptet(a) => Some(a.txid), + AnchorSet::Opret(a) => Some(a.txid), + AnchorSet::Dual { tapret, opret } if tapret.txid == opret.txid => Some(tapret.txid), + _ => None, + } + } +} + /// Txid and height information ordered according to the RGB consensus rules. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] diff --git a/src/contract/bundle.rs b/src/contract/bundle.rs index 25d64a3b..0da66199 100644 --- a/src/contract/bundle.rs +++ b/src/contract/bundle.rs @@ -27,7 +27,7 @@ use amplify::confinement::{Confined, U16}; use amplify::{Bytes32, Wrapper}; use bp::Vout; use commit_verify::{mpc, CommitEncode, CommitmentId}; -use strict_encoding::{StrictEncode, StrictWriter}; +use strict_encoding::{StrictDumb, StrictEncode, StrictWriter}; use super::OpId; use crate::{Transition, LIB_NAME_RGB}; @@ -60,7 +60,7 @@ impl From for BundleId { } #[derive(Clone, PartialEq, Eq, Debug, From)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[derive(StrictType, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB)] #[cfg_attr( feature = "serde", @@ -68,8 +68,8 @@ impl From for BundleId { serde(crate = "serde_crate", rename_all = "camelCase") )] pub struct TransitionBundle { - input_map: Confined, 1, U16>, - known_transitions: Confined, 1, U16>, + pub input_map: Confined, 1, U16>, + pub known_transitions: Confined, 1, U16>, } impl CommitEncode for TransitionBundle { @@ -84,6 +84,15 @@ impl CommitmentId for TransitionBundle { type Id = BundleId; } +impl StrictDumb for TransitionBundle { + fn strict_dumb() -> Self { + Self { + input_map: confined_bmap! { strict_dumb!() => strict_dumb!() }, + known_transitions: confined_bmap! { strict_dumb!() => strict_dumb!() }, + } + } +} + impl TransitionBundle { pub fn bundle_id(&self) -> BundleId { self.commitment_id() } } diff --git a/src/contract/mod.rs b/src/contract/mod.rs index 116fd805..b6879445 100644 --- a/src/contract/mod.rs +++ b/src/contract/mod.rs @@ -36,7 +36,7 @@ mod contract; use std::io::Write; use amplify::confinement::TinyOrdSet; -pub use anchor::{Anchor, AnchoredBundle, Layer1, WitnessAnchor}; +pub use anchor::{Anchor, AnchorSet, AnchoredBundle, Layer1, WitnessAnchor}; pub use assignments::{ Assign, AssignAttach, AssignData, AssignFungible, AssignRights, Assignments, AssignmentsRef, TypedAssigns, diff --git a/src/contract/seal.rs b/src/contract/seal.rs index e761d4d3..528aa8a9 100644 --- a/src/contract/seal.rs +++ b/src/contract/seal.rs @@ -132,6 +132,13 @@ impl Xchain { } } + pub fn reduce_to_bp(self) -> Option { + Some(match self { + Xchain::Bitcoin(seal) => seal, + Xchain::Liquid(seal) => seal, + }) + } + pub fn method(self) -> CloseMethod { match self { Xchain::Bitcoin(seal) => seal.method(), diff --git a/src/validation/consignment.rs b/src/validation/consignment.rs index b7973242..919b9768 100644 --- a/src/validation/consignment.rs +++ b/src/validation/consignment.rs @@ -25,27 +25,52 @@ //! single-use-seal data. use std::collections::{BTreeMap, BTreeSet}; +use std::rc::Rc; use crate::{ - AnchoredBundle, AssetTag, AssignmentType, BundleId, Genesis, OpId, OpRef, SecretSeal, - SubSchema, Transition, + AnchoredBundle, AssetTag, AssignmentType, BundleId, Genesis, OpId, OpRef, Operation, + SecretSeal, SubSchema, }; +pub struct CheckedConsignment<'consignment, C: ConsignmentApi>(&'consignment C); + +impl<'consignment, C: ConsignmentApi> CheckedConsignment<'consignment, C> { + pub fn new(consignment: &'consignment C) -> Self { Self(consignment) } +} + +impl<'consignment, C: ConsignmentApi> ConsignmentApi for CheckedConsignment<'consignment, C> { + type Iter = C::Iter; + + fn schema(&self) -> &SubSchema { self.0.schema() } + + fn asset_tags(&self) -> &BTreeMap { self.0.asset_tags() } + + fn operation(&self, opid: OpId) -> Option { + self.0.operation(opid).filter(|op| op.id() == opid) + } + + fn genesis(&self) -> &Genesis { self.0.genesis() } + + fn terminals(&self) -> BTreeSet<(BundleId, SecretSeal)> { self.0.terminals() } + + fn bundle_ids(&self) -> Self::Iter { self.0.bundle_ids() } + + fn anchored_bundle(&self, bundle_id: BundleId) -> Option> { + self.0 + .anchored_bundle(bundle_id) + .filter(|ab| ab.bundle.bundle_id() == bundle_id) + } +} + /// Trait defining common data access API for all storage-related RGB structures /// -/// # Verification -/// -/// The function does not verify the internal consistency, schema conformance or -/// validation status of the RGB contract data within the storage or container; -/// these checks must be performed as a separate step before calling any of the -/// [`ContainerApi`] methods. If the methods are called on -/// non-validated/unchecked data this may result in returned [`Error`] or -/// [`None`] values from the API methods. +/// The API provided for the consignment should not verify the internal +/// consistency, schema conformance or validation status of the RGB contract +/// data within the storage or container. If the methods are called on an +/// invalid or absent data, the API must always return [`None`] or empty +/// collections/iterators. pub trait ConsignmentApi { - type BundleIter<'container>: Iterator - where Self: 'container; - type TransitionIter<'container>: Iterator - where Self: 'container; + type Iter: Iterator; fn schema(&self) -> &SubSchema; @@ -70,10 +95,7 @@ pub trait ConsignmentApi { /// state transitions represent the final state fn terminals(&self) -> BTreeSet<(BundleId, SecretSeal)>; - /// Data on all anchored state transitions contained in the consignment - fn anchored_bundles(&self) -> Self::BundleIter<'_>; - - fn op_ids_except(&self, ids: &BTreeSet) -> BTreeSet; + fn bundle_ids(&self) -> Self::Iter; - fn has_operation(&self, opid: OpId) -> bool; + fn anchored_bundle(&self, bundle_id: BundleId) -> Option>; } diff --git a/src/validation/model.rs b/src/validation/logic.rs similarity index 98% rename from src/validation/model.rs rename to src/validation/logic.rs index 0ca0fcdc..780540ca 100644 --- a/src/validation/model.rs +++ b/src/validation/logic.rs @@ -27,7 +27,7 @@ use amplify::Wrapper; use strict_types::SemId; use crate::schema::{AssignmentsSchema, GlobalSchema, ValencySchema}; -use crate::validation::{ConsignmentApi, VirtualMachine}; +use crate::validation::{CheckedConsignment, ConsignmentApi, VirtualMachine}; use crate::{ validation, AssetTag, AssignmentType, Assignments, AssignmentsRef, ContractId, ExposedSeal, GlobalState, GlobalStateSchema, GlobalValues, GraphSeal, Inputs, OpFullType, OpId, OpRef, @@ -35,11 +35,11 @@ use crate::{ }; impl Schema { - pub fn validate( - &self, - consignment: &C, + pub fn validate_state<'validator, 'consignment, 'vm, C: ConsignmentApi>( + &'validator self, + consignment: &'validator CheckedConsignment<'consignment, C>, op: OpRef, - vm: &dyn VirtualMachine, + vm: &'vm dyn VirtualMachine, ) -> validation::Status { let id = op.id(); diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 72f1b0f0..b1f83600 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -22,14 +22,14 @@ mod script; mod schema; -mod model; +mod logic; mod state; mod validator; mod consignment; mod status; -pub use consignment::ConsignmentApi; -pub(crate) use model::OpInfo; +pub use consignment::{CheckedConsignment, ConsignmentApi}; +pub(crate) use logic::OpInfo; pub use script::VirtualMachine; pub use status::{Failure, Info, Status, Validity, Warning}; pub use validator::{ResolveTx, TxResolverError, Validator}; diff --git a/src/validation/status.rs b/src/validation/status.rs index 3d80bd2e..5d36ab0d 100644 --- a/src/validation/status.rs +++ b/src/validation/status.rs @@ -25,14 +25,15 @@ use core::ops::AddAssign; use std::fmt::{self, Display, Formatter}; use bp::seals::txout::blind::ChainBlindSeal; -use bp::{seals, Txid}; +use bp::Txid; use commit_verify::mpc::InvalidProof; use strict_types::SemId; use crate::contract::Opout; use crate::schema::{self, SchemaId}; use crate::{ - ContractId, Layer1, OccurrencesMismatch, OpFullType, OpId, SecretSeal, StateType, Xchain, + BundleId, ContractId, Layer1, OccurrencesMismatch, OpFullType, OpId, SecretSeal, StateType, + Xchain, }; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Display)] @@ -296,16 +297,12 @@ pub enum Failure { // Consignment consistency errors /// operation {0} is absent from the consignment. OperationAbsent(OpId), - /// state transition {0} is absent from the consignment. - TransitionAbsent(OpId), + /// transition bundle {0} is absent from the consignment. + BundleAbsent(BundleId), /// operation {0} is under a different contract {1}. ContractMismatch(OpId, ContractId), // Errors checking seal closing - /// transition {0} is not anchored. - NotAnchored(OpId), - /// anchor for transition {0} doesn't commit to the actual transition data. - NotInAnchor(OpId), /// transition {opid} references state type {state_type} absent in the /// outputs of previous state transition {prev_id}. NoPrevState { @@ -315,6 +312,8 @@ pub enum Failure { }, /// transition {0} references non-existing previous output {1}. NoPrevOut(OpId, Opout), + /// anchors used inside bundle {0} reference different transaction ids. + AnchorSetInvalid(BundleId), /// seal defined in the history as a part of operation output {0} is /// confidential and can't be validated. ConfidentialSeal(Opout), @@ -324,13 +323,13 @@ pub enum Failure { SealWitnessLayer1Mismatch { seal: Layer1, anchor: Layer1 }, /// seal {1:?} is defined on {0} which is not in the set of layers allowed /// by the contract genesis. - SealInvalidLayer1(Layer1, Xchain), - /// transition {0} doesn't close seal with the witness transaction {1}. - /// Details: {2} - SealInvalid(OpId, Txid, seals::txout::VerifyError), - /// transition {0} is not properly anchored to the witness transaction {1}. - /// Details: {2} - MpcInvalid(OpId, Txid, InvalidProof), + SealLayerMismatch(Layer1, Xchain), + /// transition bundle {0} doesn't close seal with the witness transaction + /// {1}. Details: {2} + SealsInvalid(BundleId, Txid, String), + /// transition bundle {0} is not properly anchored to the witness + /// transaction {1}. Details: {2} + MpcInvalid(BundleId, Txid, InvalidProof), // State extensions errors /// valency {valency} redeemed by state extension {opid} references @@ -399,6 +398,9 @@ pub enum Warning { ExcessiveOperation(OpId), /// terminal witness transaction {0} is not yet mined. TerminalWitnessNotMined(Txid), + /// transition bundle {0} doesn't close all the seals defined for its + /// inputs. + UnclosedSeals(BundleId), /// Custom warning by external services on top of RGB Core. #[display(inner)] diff --git a/src/validation/validator.rs b/src/validation/validator.rs index cd8b97d1..bff00ea1 100644 --- a/src/validation/validator.rs +++ b/src/validation/validator.rs @@ -20,20 +20,20 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::{BTreeMap, BTreeSet, VecDeque}; +use std::collections::{BTreeSet, VecDeque}; -use bp::seals::txout::{TxPtr, Witness}; +use bp::seals::txout::{CloseMethod, TxoSeal, Witness}; use bp::{dbc, Tx, Txid}; use commit_verify::mpc; use single_use_seals::SealWitness; use super::status::{Failure, Warning}; -use super::{ConsignmentApi, Status, Validity, VirtualMachine}; +use super::{CheckedConsignment, ConsignmentApi, Status, Validity, VirtualMachine}; use crate::vm::AluRuntime; use crate::{ - AltLayer1, Anchor, AnchoredBundle, BundleId, ContractId, GraphSeal, Layer1, OpId, OpRef, - Operation, Opout, Schema, SchemaId, SchemaRoot, Script, SubSchema, Transition, TypedAssigns, - Xchain, + AltLayer1, Anchor, AnchorSet, BundleId, ContractId, GenesisSeal, Layer1, OpId, OpRef, + Operation, Opout, Schema, SchemaId, SchemaRoot, Script, SubSchema, Transition, + TransitionBundle, TypedAssigns, }; #[derive(Clone, Debug, Display, Error, From)] @@ -46,11 +46,11 @@ pub enum TxResolverError { } pub trait ResolveTx { - fn resolve_tx(&self, layer1: Layer1, txid: Txid) -> Result; + fn resolve_bp_tx(&self, layer1: Layer1, txid: Txid) -> Result; } -pub struct Validator<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> { - consignment: &'consignment C, +pub struct Validator<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> { + consignment: CheckedConsignment<'consignment, C>, status: Status, @@ -58,22 +58,21 @@ pub struct Validator<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> { genesis_id: OpId, contract_id: ContractId, layers1: BTreeSet, - anchor_index: BTreeMap, end_transitions: Vec<(&'consignment Transition, BundleId)>, - validation_index: BTreeSet, - anchor_validation_index: BTreeSet, + validated_op_state: BTreeSet, - vm: Box, + vm: Box, resolver: &'resolver R, } -impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> - Validator<'consignment, 'resolver, C, R> +impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> + Validator<'consignment, 'vm, 'resolver, C, R> { fn init(consignment: &'consignment C, resolver: &'resolver R) -> Self { // We use validation status object to store all detected failures and // warnings let mut status = Status::default(); + let consignment = CheckedConsignment::new(consignment); // Frequently used computation-heavy data let genesis = consignment.genesis(); @@ -81,18 +80,6 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> let contract_id = genesis.contract_id(); let schema_id = genesis.schema_id; - // Create indexes - let mut anchor_index = BTreeMap::::new(); - for AnchoredBundle { - ref anchor, - ref bundle, - } in consignment.anchored_bundles() - { - for opid in bundle.values() { - anchor_index.insert(*opid, anchor); - } - } - // Collect all endpoint transitions. // This is pretty simple operation; it takes a lot of code because we would like // to detect any potential issues with the consignment structure and notify user @@ -121,10 +108,7 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> // Validation index is used to check that all transitions presented in the // consignment were validated. Also, we use it to avoid double schema // validations for transitions. - let validation_index = BTreeSet::::new(); - - // Index used to avoid repeated validations of the same anchor+transition pairs - let anchor_validation_index = BTreeSet::::new(); + let validated_op_state = BTreeSet::::new(); let mut layers1 = bset! { Layer1::Bitcoin }; layers1.extend(genesis.alt_layers1.iter().map(AltLayer1::layer1)); @@ -142,10 +126,8 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> genesis_id, contract_id, layers1, - anchor_index, end_transitions, - validation_index, - anchor_validation_index, + validated_op_state, vm, resolver, } @@ -210,12 +192,12 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> } // [VALIDATION]: Validate genesis - self.status += schema.validate( - self.consignment, + self.status += schema.validate_state( + &self.consignment, OpRef::Genesis(self.consignment.genesis()), self.vm.as_ref(), ); - self.validation_index.insert(self.genesis_id); + self.validated_op_state.insert(self.genesis_id); // [VALIDATION]: Iterating over each endpoint, reconstructing operation // graph up to genesis for each one of them. @@ -229,7 +211,7 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> // Generate warning if some of the transitions within the consignment were // excessive (i.e. not part of validation_index). Nothing critical, but still // good to report the user that the consignment is not perfect - for opid in self.consignment.op_ids_except(&self.validation_index) { + for opid in self.consignment.op_ids_except(&self.validated_op_state) { self.status.add_warning(Warning::ExcessiveOperation(opid)); } } @@ -261,9 +243,10 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> } // [VALIDATION]: Verify operation against the schema and scripts - if !self.validation_index.contains(&opid) { - self.status += schema.validate(self.consignment, operation, self.vm.as_ref()); - self.validation_index.insert(opid); + if !self.validated_op_state.contains(&opid) { + self.status += + schema.validate_state(&self.consignment, operation, self.vm.as_ref()); + self.validated_op_state.insert(opid); } match operation { @@ -274,11 +257,8 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> // 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(|| { - // This will not actually happen since we already checked that each - // ancestor reference has a corresponding operation in the code above. - // But lets double-check :) self.status - .add_failure(Failure::TransitionAbsent(input.prev_out.op)); + .add_failure(Failure::OperationAbsent(input.prev_out.op)); None }) }); @@ -314,74 +294,39 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> // *** PART III: Validating single-use-seals fn validate_commitments(&mut self) { - for anchor in &self.anchor_index {} - - // Replace missed (not yet mined) endpoint witness transaction failures - // with a dedicated type - for (transition, bundle_id) in &self.end_transitions { - let opid = transition.id(); - - if let Some(anchor) = self.anchor_index.get(&opid) { - // Making sure we do have a corresponding anchor; otherwise reporting failure - // (see below) - with the except of genesis and extension nodes, which does not - // have a corresponding anchor - if !self.anchor_validation_index.contains(&opid) { - // Ok, now we have the `transition` and the `anchor`, let's do all - // required checks - - // [VALIDATION]: Check that transition is committed into the anchor. - // This must be done with deterministic bitcoin - // commitments & LNPBP-4. - if anchor.convolve(self.contract_id, bundle_id.into()).is_err() { - self.status.add_failure(Failure::NotInAnchor(opid)); - } - - self.validate_anchor(transition, *bundle_id, anchor); - self.anchor_validation_index.insert(opid); - } + for bundle_id in self.consignment.bundle_ids() { + let Some(anchored_bundle) = self.consignment.anchored_bundle(bundle_id) else { + self.status.add_failure(Failure::BundleAbsent(bundle_id)); + continue; + }; - let anchor = match anchor { - Anchor::Bitcoin(anchor) | Anchor::Liquid(anchor) => anchor, - }; + let layer1 = anchored_bundle.anchor.layer1(); + let anchor = match &anchored_bundle.anchor { + Anchor::Bitcoin(anchor) | Anchor::Liquid(anchor) => anchor, + }; - if let Some(pos) = self - .status - .failures - .iter() - .position(|f| f == &Failure::SealNoWitnessTx(anchor.txid)) - { - self.status.failures.remove(pos); - self.status - .unresolved_txids - .retain(|txid| *txid != anchor.txid); - self.status.unmined_terminals.push(anchor.txid); - self.status - .warnings - .push(Warning::TerminalWitnessNotMined(anchor.txid)); - } - } else { - // If we've got here there is something broken with the consignment - // provider. - self.status.add_failure(Failure::NotAnchored(opid)); - } + self.validate_commitments_bp(layer1, bundle_id, &anchored_bundle.bundle, anchor); } } - fn validate_anchor( + fn validate_commitments_bp( &mut self, - transition: &'consignment Transition, + layer1: Layer1, bundle_id: BundleId, - anchor: &'consignment Anchor, + bundle: &'consignment TransitionBundle, + anchor: &'consignment AnchorSet, ) { - let (layer1, anchor) = match anchor { - Anchor::Bitcoin(a) | Anchor::Liquid(a) => (anchor.layer1(), a), + let Some(txid) = anchor.txid() else { + self.status + .add_failure(Failure::AnchorSetInvalid(bundle_id)); + return; }; - let txid = anchor.txid; + let (tapret_seals, opret_seals) = self.validate_seal_definitions(layer1, txid, bundle); // Check that the anchor is committed into a transaction spending all of the // transition inputs. - match self.resolver.resolve_tx(layer1, txid) { + match self.resolver.resolve_bp_tx(layer1, txid) { Err(_) => { // We wre unable to retrieve corresponding transaction, so can't check. // Reporting this incident and continuing further. Why this happens? No @@ -397,10 +342,96 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> self.status.add_failure(Failure::SealNoWitnessTx(txid)); } Ok(witness_tx) => { - let witness = Witness::with(witness_tx, anchor.clone()); - self.validate_witness(transition, witness, bundle_id, anchor) + let (tapret, opret) = match anchor { + AnchorSet::Taptet(tapret) => (Some(tapret), None), + AnchorSet::Opret(opret) => (None, Some(opret)), + AnchorSet::Dual { tapret, opret } => (Some(tapret), Some(opret)), + }; + + if let Some(tapret) = tapret { + let witness = Witness::with(witness_tx.clone(), tapret.clone()); + self.validate_seal_closing(&tapret_seals, witness, bundle_id, tapret) + } else if !tapret_seals.is_empty() { + self.status.add_warning(Warning::UnclosedSeals(bundle_id)); + } + if let Some(opret) = opret { + let witness = Witness::with(witness_tx, opret.clone()); + self.validate_seal_closing(&opret_seals, witness, bundle_id, opret) + } else if !opret_seals.is_empty() { + self.status.add_warning(Warning::UnclosedSeals(bundle_id)); + } + } + } + } + + fn validate_seal_definitions( + &mut self, + layer1: Layer1, + witness_txid: Txid, + bundle: &'consignment TransitionBundle, + ) -> (Vec, Vec) { + let (mut tapret_seals, mut opret_seals) = (vec![], vec![]); + for (opid, transition) in &bundle.known_transitions { + let opid = *opid; + + // Checking that witness transaction closes seals defined by transition previous + // outputs. + for input in &transition.inputs { + let Opout { op, ty, no } = input.prev_out; + + let Some(prev_op) = self.consignment.operation(op) else { + // Node, referenced as the ancestor, was not found in the consignment. + // Usually this means that the consignment data are broken + self.status.add_failure(Failure::OperationAbsent(op)); + continue; + }; + + let Some(variant) = prev_op.assignments_by_type(ty) else { + self.status.add_failure(Failure::NoPrevState { + opid, + prev_id: op, + state_type: ty, + }); + continue; + }; + + let Ok(seal) = variant.revealed_seal_at(no) else { + self.status + .add_failure(Failure::NoPrevOut(opid, input.prev_out)); + continue; + }; + let Some(seal) = seal else { + // Everything is ok, but we have incomplete data (confidential), thus can't do a + // full verification and have to report the failure + self.status + .add_failure(Failure::ConfidentialSeal(input.prev_out)); + continue; + }; + + if seal.layer1() != layer1 { + self.status.add_failure(Failure::SealWitnessLayer1Mismatch { + seal: seal.layer1(), + anchor: layer1, + }); + continue; + } + if !self.layers1.contains(&seal.layer1()) { + self.status + .add_failure(Failure::SealLayerMismatch(seal.layer1(), seal)); + continue; + } + + let seal = seal + .reduce_to_bp() + .expect("method must be called only on BP-compatible layer 1") + .resolve(witness_txid); + match seal.method() { + CloseMethod::OpretFirst => opret_seals.push(seal), + CloseMethod::TapretFirst => tapret_seals.push(seal), + } } } + (tapret_seals, opret_seals) } /// Single-use-seal closing validation. @@ -413,105 +444,13 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> /// /// Additionally checks that the provided message contains commitment to the /// bundle under the current contract. - fn validate_witness( + fn validate_seal_closing<'seal, Seal: TxoSeal + 'seal, Dbc: dbc::Proof>( &mut self, - transition: &'consignment Transition, + seals: impl IntoIterator, witness: Witness, bundle_id: BundleId, anchor: &'consignment dbc::Anchor, ) { - let opid = transition.id(); - let txid = witness.txid; - - // Checking that witness transaction closes seals defined by transition previous - // outputs. - let mut seals = vec![]; - for input in &transition.inputs { - let Opout { op, ty, no } = input.prev_out; - - let Some(prev_op) = self.consignment.operation(op) else { - // Node, referenced as the ancestor, was not found in the consignment. - // Usually this means that the consignment data are broken - self.status.add_failure(Failure::OperationAbsent(op)); - continue; - }; - - let Some(variant) = prev_op.assignments_by_type(ty) else { - self.status.add_failure(Failure::NoPrevState { - opid, - prev_id: op, - state_type: ty, - }); - continue; - }; - - let Ok(seal) = variant.revealed_seal_at(no) else { - self.status - .add_failure(Failure::NoPrevOut(opid, input.prev_out)); - continue; - }; - let Some(seal) = seal else { - // Everything is ok, but we have incomplete data (confidential), thus can't do a - // full verification and have to report the failure - self.status - .add_failure(Failure::ConfidentialSeal(input.prev_out)); - continue; - }; - - let Some(anchor) = self.anchor_index.get(&op) else { - panic!("anchor for the operation {op} was not indexed by the validator"); - }; - if seal.layer1() != anchor.layer1() { - self.status.add_failure(Failure::SealWitnessLayer1Mismatch { - seal: seal.layer1(), - anchor: anchor.layer1(), - }); - continue; - } - if !self.layers1.contains(&seal.layer1()) { - self.status - .add_failure(Failure::SealInvalidLayer1(seal.layer1(), seal)); - continue; - } - - let seal = match (seal, anchor) { - ( - Xchain::Bitcoin( - seal @ GraphSeal { - txid: TxPtr::WitnessTx, - .. - }, - ) | - Xchain::Liquid( - seal @ GraphSeal { - txid: TxPtr::WitnessTx, - .. - }, - ), - Anchor::Bitcoin(anchor) | Anchor::Liquid(anchor), - ) => { - let prev_witness_txid = anchor.txid; - seal.resolve(prev_witness_txid) - } - ( - Xchain::Bitcoin( - seal @ GraphSeal { - txid: TxPtr::Txid(txid), - .. - }, - ) | - Xchain::Liquid( - seal @ GraphSeal { - txid: TxPtr::Txid(txid), - .. - }, - ), - Anchor::Bitcoin(_) | Anchor::Liquid(_), - ) => seal.resolve(txid), - }; - seals.push(seal); - } - let message = mpc::Message::from(bundle_id); // [VALIDATION]: Checking anchor MPC commitment match anchor.convolve(self.contract_id, message) { @@ -519,15 +458,18 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> // The operation is not committed to bitcoin transaction graph! // Ultimate failure. But continuing to detect the rest (after reporting it). self.status - .add_failure(Failure::MpcInvalid(opid, txid, err)); + .add_failure(Failure::MpcInvalid(bundle_id, witness.txid, err)); } Ok(commitment) => { // [VALIDATION]: CHECKING SINGLE-USE-SEALS witness - .verify_many_seals(&seals, &commitment) + .verify_many_seals(seals, &commitment) .map_err(|err| { - self.status - .add_failure(Failure::SealInvalid(opid, txid, err)); + self.status.add_failure(Failure::SealsInvalid( + bundle_id, + witness.txid, + err.to_string(), + )); }) .ok(); } From 002ca5f7d6593a059cb62069a974181cb66451be Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 15 Dec 2023 13:04:07 +0100 Subject: [PATCH 09/20] validation: improve doc comments --- src/contract/anchor.rs | 6 +++--- src/validation/validator.rs | 21 +++++++++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/contract/anchor.rs b/src/contract/anchor.rs index b425ff98..2f9c963e 100644 --- a/src/contract/anchor.rs +++ b/src/contract/anchor.rs @@ -109,7 +109,7 @@ impl Anchor { (Anchor::Liquid(anchor), Anchor::Liquid(other)) => { anchor.merge_reveal(other).map(Anchor::Liquid) } - _ => Err(MergeError::ProofMismatch), + _ => Err(MergeError::TxidMismatch), } } } @@ -124,7 +124,7 @@ impl Anchor { )] pub enum AnchorSet { #[strict_type(tag = 0x01)] - Taptet(dbc::Anchor), + Tapret(dbc::Anchor), #[strict_type(tag = 0x02)] Opret(dbc::Anchor), #[strict_type(tag = 0x03)] @@ -137,7 +137,7 @@ pub enum AnchorSet { impl AnchorSet

{ pub fn txid(&self) -> Option { match self { - AnchorSet::Taptet(a) => Some(a.txid), + AnchorSet::Tapret(a) => Some(a.txid), AnchorSet::Opret(a) => Some(a.txid), AnchorSet::Dual { tapret, opret } if tapret.txid == opret.txid => Some(tapret.txid), _ => None, diff --git a/src/validation/validator.rs b/src/validation/validator.rs index bff00ea1..a2afe279 100644 --- a/src/validation/validator.rs +++ b/src/validation/validator.rs @@ -301,14 +301,19 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> }; let layer1 = anchored_bundle.anchor.layer1(); + + // For now we use just Bitcoin and Liquid as layer1, but in the + // future we may have more validation routes for other types of + // layer1 structure. let anchor = match &anchored_bundle.anchor { Anchor::Bitcoin(anchor) | Anchor::Liquid(anchor) => anchor, }; - self.validate_commitments_bp(layer1, bundle_id, &anchored_bundle.bundle, anchor); } } + /// Bitcoin- and liquid-specific commitment validation using deterministic + /// bitcoin commitments with opret and tapret schema. fn validate_commitments_bp( &mut self, layer1: Layer1, @@ -364,6 +369,10 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> } } + /// Single-use-seal definition validation. + /// + /// Takes state transition, extracts all seals from its inputs and makes + /// sure they are defined or a correct layer1. fn validate_seal_definitions( &mut self, layer1: Layer1, @@ -436,11 +445,11 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> /// Single-use-seal closing validation. /// - /// Takes state transition, extracts all seals from its inputs. Checks that - /// the set of seals is closed over the message, which is multi-protocol - /// commitment, by utilizing witness, consisting of transaction with - /// deterministic bitcoin commitments (defined by generic type `Dbc`) and - /// extra-transaction data, which are taken from anchors DBC proof. + /// Checks that the set of seals is closed over the message, which is + /// multi-protocol commitment, by utilizing witness, consisting of + /// transaction with deterministic bitcoin commitments (defined by + /// generic type `Dbc`) and extra-transaction data, which are taken from + /// anchors DBC proof. /// /// Additionally checks that the provided message contains commitment to the /// bundle under the current contract. From 7f0782ba00b3dc4681c2feec1c468cd6f3e53464 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 15 Dec 2023 13:14:24 +0100 Subject: [PATCH 10/20] validation: improve typing in validation method signatures --- src/contract/anchor.rs | 2 +- src/validation/validator.rs | 46 +++++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/contract/anchor.rs b/src/contract/anchor.rs index 2f9c963e..8627a5c1 100644 --- a/src/contract/anchor.rs +++ b/src/contract/anchor.rs @@ -116,7 +116,7 @@ impl Anchor { #[derive(Clone, Eq, PartialEq, Debug)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB, tags = custom, dumb = Self::Taptet(strict_dumb!()))] +#[strict_type(lib = LIB_NAME_RGB, tags = custom, dumb = Self::Tapret(strict_dumb!()))] #[cfg_attr( feature = "serde", derive(Serialize, Deserialize), diff --git a/src/validation/validator.rs b/src/validation/validator.rs index a2afe279..89e4bf8a 100644 --- a/src/validation/validator.rs +++ b/src/validation/validator.rs @@ -22,7 +22,7 @@ use std::collections::{BTreeSet, VecDeque}; -use bp::seals::txout::{CloseMethod, TxoSeal, Witness}; +use bp::seals::txout::{CloseMethod, ExplicitSeal, TxoSeal, Witness}; use bp::{dbc, Tx, Txid}; use commit_verify::mpc; use single_use_seals::SealWitness; @@ -31,9 +31,9 @@ use super::status::{Failure, Warning}; use super::{CheckedConsignment, ConsignmentApi, Status, Validity, VirtualMachine}; use crate::vm::AluRuntime; use crate::{ - AltLayer1, Anchor, AnchorSet, BundleId, ContractId, GenesisSeal, Layer1, OpId, OpRef, - Operation, Opout, Schema, SchemaId, SchemaRoot, Script, SubSchema, Transition, - TransitionBundle, TypedAssigns, + AltLayer1, Anchor, AnchorSet, BundleId, ContractId, Layer1, OpId, OpRef, Operation, Opout, + Schema, SchemaId, SchemaRoot, Script, SubSchema, Transition, TransitionBundle, TypedAssigns, + WitnessId, }; #[derive(Clone, Debug, Display, Error, From)] @@ -305,10 +305,16 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> // For now we use just Bitcoin and Liquid as layer1, but in the // future we may have more validation routes for other types of // layer1 structure. - let anchor = match &anchored_bundle.anchor { + let witness_id = anchored_bundle.anchor.witness_id(); + let anchors = match &anchored_bundle.anchor { Anchor::Bitcoin(anchor) | Anchor::Liquid(anchor) => anchor, }; - self.validate_commitments_bp(layer1, bundle_id, &anchored_bundle.bundle, anchor); + let bundle = &anchored_bundle.bundle; + + let (tapret_seals, opret_seals) = + self.validate_seal_definitions(layer1, witness_id, bundle); + + self.validate_commitments_bp(layer1, tapret_seals, opret_seals, bundle_id, anchors); } } @@ -317,18 +323,17 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> fn validate_commitments_bp( &mut self, layer1: Layer1, + tapret_seals: Vec>, + opret_seals: Vec>, bundle_id: BundleId, - bundle: &'consignment TransitionBundle, - anchor: &'consignment AnchorSet, + anchors: &'consignment AnchorSet, ) { - let Some(txid) = anchor.txid() else { + let Some(txid) = anchors.txid() else { self.status .add_failure(Failure::AnchorSetInvalid(bundle_id)); return; }; - let (tapret_seals, opret_seals) = self.validate_seal_definitions(layer1, txid, bundle); - // Check that the anchor is committed into a transaction spending all of the // transition inputs. match self.resolver.resolve_bp_tx(layer1, txid) { @@ -347,21 +352,21 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> self.status.add_failure(Failure::SealNoWitnessTx(txid)); } Ok(witness_tx) => { - let (tapret, opret) = match anchor { - AnchorSet::Taptet(tapret) => (Some(tapret), None), + let (tapret, opret) = match anchors { + AnchorSet::Tapret(tapret) => (Some(tapret), None), AnchorSet::Opret(opret) => (None, Some(opret)), AnchorSet::Dual { tapret, opret } => (Some(tapret), Some(opret)), }; if let Some(tapret) = tapret { let witness = Witness::with(witness_tx.clone(), tapret.clone()); - self.validate_seal_closing(&tapret_seals, witness, bundle_id, tapret) + self.validate_seal_closing_bp(&tapret_seals, witness, bundle_id, tapret) } else if !tapret_seals.is_empty() { self.status.add_warning(Warning::UnclosedSeals(bundle_id)); } if let Some(opret) = opret { let witness = Witness::with(witness_tx, opret.clone()); - self.validate_seal_closing(&opret_seals, witness, bundle_id, opret) + self.validate_seal_closing_bp(&opret_seals, witness, bundle_id, opret) } else if !opret_seals.is_empty() { self.status.add_warning(Warning::UnclosedSeals(bundle_id)); } @@ -376,9 +381,9 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> fn validate_seal_definitions( &mut self, layer1: Layer1, - witness_txid: Txid, + witness_id: WitnessId, bundle: &'consignment TransitionBundle, - ) -> (Vec, Vec) { + ) -> (Vec>, Vec>) { let (mut tapret_seals, mut opret_seals) = (vec![], vec![]); for (opid, transition) in &bundle.known_transitions { let opid = *opid; @@ -431,9 +436,10 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> } let seal = seal - .reduce_to_bp() + .try_to_output_seal(witness_id) .expect("method must be called only on BP-compatible layer 1") - .resolve(witness_txid); + .reduce_to_bp() + .expect("method must be called only on BP-compatible layer 1"); match seal.method() { CloseMethod::OpretFirst => opret_seals.push(seal), CloseMethod::TapretFirst => tapret_seals.push(seal), @@ -453,7 +459,7 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> /// /// Additionally checks that the provided message contains commitment to the /// bundle under the current contract. - fn validate_seal_closing<'seal, Seal: TxoSeal + 'seal, Dbc: dbc::Proof>( + fn validate_seal_closing_bp<'seal, Seal: TxoSeal + 'seal, Dbc: dbc::Proof>( &mut self, seals: impl IntoIterator, witness: Witness, From da4bb9e2a96528b74bb0cfd4606976a3b6f65d06 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 15 Dec 2023 14:06:58 +0100 Subject: [PATCH 11/20] validation: simplify valency redeeming validation logic --- src/validation/logic.rs | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/src/validation/logic.rs b/src/validation/logic.rs index 780540ca..0a61934b 100644 --- a/src/validation/logic.rs +++ b/src/validation/logic.rs @@ -31,7 +31,7 @@ use crate::validation::{CheckedConsignment, ConsignmentApi, VirtualMachine}; use crate::{ validation, AssetTag, AssignmentType, Assignments, AssignmentsRef, ContractId, ExposedSeal, GlobalState, GlobalStateSchema, GlobalValues, GraphSeal, Inputs, OpFullType, OpId, OpRef, - Operation, Opout, Redeemed, Schema, SchemaRoot, TransitionType, TypedAssigns, Valencies, + Operation, Opout, Schema, SchemaRoot, TransitionType, TypedAssigns, Valencies, }; impl Schema { @@ -146,14 +146,13 @@ impl Schema { } else { Assignments::default() }; - let redeemed = if let OpRef::Extension(extension) = op { - let redeemed = - extract_redeemed_valencies(consignment, &extension.redeemed, &mut status); + let mut redeemed = Valencies::default(); + if let OpRef::Extension(extension) = op { + for valency in extension.redeemed.keys() { + redeemed.push(*valency).expect("same size"); + } status += self.validate_redeemed(id, &redeemed, redeem_schema); - redeemed - } else { - Valencies::default() - }; + } status += match op.assignments() { AssignmentsRef::Genesis(assignments) => { self.validate_owned_state(id, assignments, assign_schema) @@ -550,19 +549,3 @@ fn extract_prev_state( .expect("collections is assembled from another collection with the same size requirements") .into() } - -fn extract_redeemed_valencies( - consignment: &C, - redeemed: &Redeemed, - status: &mut validation::Status, -) -> Valencies { - let mut valencies = Valencies::default(); - for (valency, id) in redeemed.iter() { - if consignment.has_operation(*id) { - valencies.push(*valency).expect("same size"); - } else { - status.add_failure(validation::Failure::OperationAbsent(*id)); - } - } - valencies -} From 36fe4b0f659487597506678fcd2c0f04bdc7f37e Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 15 Dec 2023 14:13:55 +0100 Subject: [PATCH 12/20] validation: add checks for bundles and DAG graph consistencies --- src/validation/status.rs | 27 +++++-- src/validation/validator.rs | 140 ++++++++++++++++++++++++++---------- 2 files changed, 124 insertions(+), 43 deletions(-) diff --git a/src/validation/status.rs b/src/validation/status.rs index 5d36ab0d..8b8fa47f 100644 --- a/src/validation/status.rs +++ b/src/validation/status.rs @@ -33,7 +33,7 @@ use crate::contract::Opout; use crate::schema::{self, SchemaId}; use crate::{ BundleId, ContractId, Layer1, OccurrencesMismatch, OpFullType, OpId, SecretSeal, StateType, - Xchain, + Vin, Xchain, }; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Display)] @@ -295,13 +295,30 @@ pub enum Failure { SchemaAssignmentOccurrences(OpId, schema::AssignmentType, OccurrencesMismatch), // Consignment consistency errors + /// operation {0} is referenced within the history multiple times. RGB + /// contracts allow only direct acyclic graphs. + CyclicGraph(OpId), /// operation {0} is absent from the consignment. OperationAbsent(OpId), + /// transition bundle {0} referenced in consignment terminals is absent from + /// the consignment. + TerminalBundleAbsent(BundleId), /// transition bundle {0} is absent from the consignment. BundleAbsent(BundleId), /// operation {0} is under a different contract {1}. ContractMismatch(OpId, ContractId), + // Errors checking bundle commitments + /// transition bundle {0} references state transition {1} which is not + /// included into the bundle input map. + BundleExtraTransition(BundleId, OpId), + /// transition bundle {0} references non-existing input in witness + /// transaction {2} for the state transition {1}. + BundleInvalidInput(BundleId, OpId, Txid), + /// transition bundle {0} doesn't commit to the input {1} in the witness + /// transaction {2} which is an input of the state transition {3}. + BundleInvalidCommitment(BundleId, Vin, Txid, OpId), + // Errors checking seal closing /// transition {opid} references state type {state_type} absent in the /// outputs of previous state transition {prev_id}. @@ -327,6 +344,9 @@ pub enum Failure { /// transition bundle {0} doesn't close seal with the witness transaction /// {1}. Details: {2} SealsInvalid(BundleId, Txid, String), + /// single-use seals for the operation {0} were not validated, which + /// probably indicateds unanchored state transition. + SealsUnvalidated(OpId), /// transition bundle {0} is not properly anchored to the witness /// transaction {1}. Details: {2} MpcInvalid(BundleId, Txid, InvalidProof), @@ -347,7 +367,7 @@ pub enum Failure { valency: schema::ValencyType, }, - // Data check errors + // State check errors /// state in {opid}/{state_type} is of {found} type, while schema requires /// it to be {expected}. StateTypeMismatch { @@ -393,9 +413,6 @@ pub enum Warning { /// terminal seal {1} referencing operation {0} is not present in operation /// assignments. TerminalSealAbsent(OpId, SecretSeal), - /// operation {0} present in the consignment is excessive and not a part of - /// the validated contract history. - ExcessiveOperation(OpId), /// terminal witness transaction {0} is not yet mined. TerminalWitnessNotMined(Txid), /// transition bundle {0} doesn't close all the seals defined for its diff --git a/src/validation/validator.rs b/src/validation/validator.rs index 89e4bf8a..f821f178 100644 --- a/src/validation/validator.rs +++ b/src/validation/validator.rs @@ -20,10 +20,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::{BTreeSet, VecDeque}; +use std::collections::{BTreeMap, BTreeSet, VecDeque}; use bp::seals::txout::{CloseMethod, ExplicitSeal, TxoSeal, Witness}; -use bp::{dbc, Tx, Txid}; +use bp::{dbc, Outpoint, Tx, Txid}; use commit_verify::mpc; use single_use_seals::SealWitness; @@ -58,7 +58,8 @@ pub struct Validator<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: Resolve genesis_id: OpId, contract_id: ContractId, layers1: BTreeSet, - end_transitions: Vec<(&'consignment Transition, BundleId)>, + + validated_op_seals: BTreeSet, validated_op_state: BTreeSet, vm: Box, @@ -84,10 +85,12 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> // This is pretty simple operation; it takes a lot of code because we would like // to detect any potential issues with the consignment structure and notify user // about them (in form of generated warnings) - let mut end_transitions = Vec::<(&Transition, BundleId)>::new(); for (bundle_id, seal_endpoint) in consignment.terminals() { - for transition in consignment.known_transitions_in_bundle(bundle_id) { - let opid = transition.id(); + let Some(anchored_bundle) = consignment.anchored_bundle(bundle_id) else { + status.add_failure(Failure::TerminalBundleAbsent(bundle_id)); + continue; + }; + for (opid, transition) in &anchored_bundle.bundle.known_transitions { // Checking for endpoint definition duplicates if !transition .assignments @@ -97,10 +100,7 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> { // We generate just a warning here because it's up to a user to decide whether // to accept consignment with wrong endpoint list - status.add_warning(Warning::TerminalSealAbsent(opid, seal_endpoint)); - } - if end_transitions.iter().all(|(n, _)| n.id() != opid) { - end_transitions.push((transition, bundle_id)); + status.add_warning(Warning::TerminalSealAbsent(*opid, seal_endpoint)); } } } @@ -109,6 +109,7 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> // consignment were validated. Also, we use it to avoid double schema // validations for transitions. let validated_op_state = BTreeSet::::new(); + let validated_op_seals = BTreeSet::::new(); let mut layers1 = bset! { Layer1::Bitcoin }; layers1.extend(genesis.alt_layers1.iter().map(AltLayer1::layer1)); @@ -126,8 +127,8 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> genesis_id, contract_id, layers1, - end_transitions, validated_op_state, + validated_op_seals, vm, resolver, } @@ -204,15 +205,15 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> // NB: We are not aiming to validate the consignment as a whole, but instead // treat it as a superposition of subgraphs, one for each endpoint; and validate // them independently. - for (transition, _) in &self.end_transitions { - self.validate_logic_on_route(schema, transition); - } - - // Generate warning if some of the transitions within the consignment were - // excessive (i.e. not part of validation_index). Nothing critical, but still - // good to report the user that the consignment is not perfect - for opid in self.consignment.op_ids_except(&self.validated_op_state) { - self.status.add_warning(Warning::ExcessiveOperation(opid)); + for (bundle_id, _) in self.consignment.terminals() { + let Some(anchored_bundle) = self.consignment.anchored_bundle(bundle_id) else { + // We already checked and errored here during the terminal validation, so just + // skipping. + continue; + }; + for transition in anchored_bundle.bundle.known_transitions.values() { + self.validate_logic_on_route(schema, transition); + } } } @@ -242,11 +243,13 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> continue; } + if !self.validated_op_seals.contains(&opid) { + self.status.add_failure(Failure::SealsUnvalidated(opid)); + } // [VALIDATION]: Verify operation against the schema and scripts - if !self.validated_op_state.contains(&opid) { - self.status += - schema.validate_state(&self.consignment, operation, self.vm.as_ref()); - self.validated_op_state.insert(opid); + self.status += schema.validate_state(&self.consignment, operation, self.vm.as_ref()); + if !self.validated_op_state.insert(opid) { + self.status.add_failure(Failure::CyclicGraph(opid)); } match operation { @@ -311,27 +314,76 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> }; let bundle = &anchored_bundle.bundle; - let (tapret_seals, opret_seals) = + // [VALIDATION]: We validate that the seals were properly defined on BP-type layers + let (tapret_seals, opret_seals, input_map) = self.validate_seal_definitions(layer1, witness_id, bundle); - self.validate_commitments_bp(layer1, tapret_seals, opret_seals, bundle_id, anchors); + // [VALIDATION]: We validate that the seals were properly closed on BP-type layers + let Some(witness_tx) = self.validate_commitments_bp( + layer1, + &tapret_seals, + &opret_seals, + bundle_id, + anchors, + ) else { + continue; + }; + + // [VALIDATION]: We validate bundle commitments to the input map + self.validate_bundle_commitments(bundle_id, bundle, witness_tx, input_map); + } + } + + /// Validates that the transition bundle is internally consistent: inputs of + /// its state transitions correspond to the way how they are committed + /// in the input map of the bundle; and these inputs are real inputs of + /// the transaction. + fn validate_bundle_commitments( + &mut self, + bundle_id: BundleId, + bundle: &'consignment TransitionBundle, + witness_tx: Tx, + input_map: BTreeMap>, + ) { + for (vin, opid) in bundle.input_map { + let Some(outpoints) = input_map.get(&opid) else { + self.status + .add_failure(Failure::BundleExtraTransition(bundle_id, opid)); + continue; + }; + let Some(input) = witness_tx.inputs.get(vin.to_usize()) else { + self.status.add_failure(Failure::BundleInvalidInput( + bundle_id, + opid, + witness_tx.txid(), + )); + continue; + }; + if !outpoints.contains(&input.prev_output) { + self.status.add_failure(Failure::BundleInvalidCommitment( + bundle_id, + vin, + witness_tx.txid(), + opid, + )); + } } } /// Bitcoin- and liquid-specific commitment validation using deterministic /// bitcoin commitments with opret and tapret schema. - fn validate_commitments_bp( + fn validate_commitments_bp<'seal>( &mut self, layer1: Layer1, - tapret_seals: Vec>, - opret_seals: Vec>, + tapret_seals: impl IntoIterator>, + opret_seals: impl IntoIterator>, bundle_id: BundleId, anchors: &'consignment AnchorSet, - ) { + ) -> Option { let Some(txid) = anchors.txid() else { self.status .add_failure(Failure::AnchorSetInvalid(bundle_id)); - return; + return None; }; // Check that the anchor is committed into a transaction spending all of the @@ -350,6 +402,7 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> // validation in order to detect the rest of problems (and reporting the // failure!) self.status.add_failure(Failure::SealNoWitnessTx(txid)); + None } Ok(witness_tx) => { let (tapret, opret) = match anchors { @@ -360,16 +413,17 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> if let Some(tapret) = tapret { let witness = Witness::with(witness_tx.clone(), tapret.clone()); - self.validate_seal_closing_bp(&tapret_seals, witness, bundle_id, tapret) - } else if !tapret_seals.is_empty() { + self.validate_seal_closing_bp(tapret_seals, witness, bundle_id, tapret) + } else if tapret_seals.into_iter().count() > 0 { self.status.add_warning(Warning::UnclosedSeals(bundle_id)); } if let Some(opret) = opret { - let witness = Witness::with(witness_tx, opret.clone()); - self.validate_seal_closing_bp(&opret_seals, witness, bundle_id, opret) - } else if !opret_seals.is_empty() { + let witness = Witness::with(witness_tx.clone(), opret.clone()); + self.validate_seal_closing_bp(opret_seals, witness, bundle_id, opret) + } else if opret_seals.into_iter().count() > 0 { self.status.add_warning(Warning::UnclosedSeals(bundle_id)); } + Some(witness_tx) } } } @@ -383,11 +437,17 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> layer1: Layer1, witness_id: WitnessId, bundle: &'consignment TransitionBundle, - ) -> (Vec>, Vec>) { + ) -> (Vec>, Vec>, BTreeMap>) + { + let mut input_map: BTreeMap> = bmap!(); let (mut tapret_seals, mut opret_seals) = (vec![], vec![]); for (opid, transition) in &bundle.known_transitions { let opid = *opid; + if !self.validated_op_seals.insert(opid) { + self.status.add_failure(Failure::CyclicGraph(opid)); + } + // Checking that witness transaction closes seals defined by transition previous // outputs. for input in &transition.inputs { @@ -444,9 +504,13 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> CloseMethod::OpretFirst => opret_seals.push(seal), CloseMethod::TapretFirst => tapret_seals.push(seal), } + input_map + .entry(opid) + .or_default() + .insert(Outpoint::new(seal.txid, seal.vout)); } } - (tapret_seals, opret_seals) + (tapret_seals, opret_seals, input_map) } /// Single-use-seal closing validation. From c975d558af7652c827dd6e51ac27abb5d9f2eda9 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 15 Dec 2023 14:48:08 +0100 Subject: [PATCH 13/20] validation: fix all lints and do final API improvements --- src/contract/anchor.rs | 64 +++- src/stl.rs | 2 +- src/validation/logic.rs | 4 +- src/validation/validator.rs | 93 +++--- stl/RGB@0.1.0.sta | 633 ++++++++++++++++++------------------ stl/RGB@0.1.0.stl | Bin 15362 -> 15725 bytes stl/RGB@0.1.0.sty | 49 +-- 7 files changed, 451 insertions(+), 394 deletions(-) diff --git a/src/contract/anchor.rs b/src/contract/anchor.rs index 8627a5c1..eff91260 100644 --- a/src/contract/anchor.rs +++ b/src/contract/anchor.rs @@ -84,15 +84,15 @@ impl Anchor

{ #[inline] pub fn witness_id(&self) -> WitnessId { match self { - Anchor::Bitcoin(anchor) => WitnessId::Bitcoin(anchor.txid), - Anchor::Liquid(anchor) => WitnessId::Liquid(anchor.txid), + Anchor::Bitcoin(anchor) => WitnessId::Bitcoin(anchor.txid_unchecked()), + Anchor::Liquid(anchor) => WitnessId::Liquid(anchor.txid_unchecked()), } } - pub fn map( + pub fn map( self, - f: impl FnOnce(dbc::Anchor) -> Result, E>, - ) -> Result, E> { + f: impl FnOnce(AnchorSet

) -> Result, E>, + ) -> Result, E> { match self { Anchor::Bitcoin(anchor) => f(anchor).map(Anchor::Bitcoin), Anchor::Liquid(anchor) => f(anchor).map(Anchor::Liquid), @@ -143,6 +143,60 @@ impl AnchorSet

{ _ => None, } } + + pub(crate) fn txid_unchecked(&self) -> Txid { + match self { + AnchorSet::Tapret(a) => a.txid, + AnchorSet::Opret(a) => a.txid, + AnchorSet::Dual { tapret, .. } => tapret.txid, + } + } + + #[allow(clippy::type_complexity)] + pub fn as_split( + &self, + ) -> (Option<&dbc::Anchor>, Option<&dbc::Anchor>) { + match self { + AnchorSet::Tapret(tapret) => (Some(tapret), None), + AnchorSet::Opret(opret) => (None, Some(opret)), + AnchorSet::Dual { tapret, opret } => (Some(tapret), Some(opret)), + } + } + + #[allow(clippy::type_complexity)] + pub fn into_split( + self, + ) -> (Option>, Option>) { + match self { + AnchorSet::Tapret(tapret) => (Some(tapret), None), + AnchorSet::Opret(opret) => (None, Some(opret)), + AnchorSet::Dual { tapret, opret } => (Some(tapret), Some(opret)), + } + } +} + +impl AnchorSet { + pub fn merge_reveal(self, other: Self) -> Result { + let (tapret1, opret1) = self.into_split(); + let (tapret2, opret2) = other.into_split(); + + let tapret = match (tapret1, tapret2) { + (Some(tr), None) | (None, Some(tr)) => Some(tr), + (Some(tapret1), Some(tapret2)) => Some(tapret1.merge_reveal(tapret2)?), + (None, None) => None, + }; + let opret = match (opret1, opret2) { + (Some(or), None) | (None, Some(or)) => Some(or), + (Some(opret1), Some(opret2)) => Some(opret1.merge_reveal(opret2)?), + (None, None) => None, + }; + Ok(match (tapret, opret) { + (Some(tapret), None) => Self::Tapret(tapret), + (None, Some(opret)) => Self::Opret(opret), + (Some(tapret), Some(opret)) => Self::Dual { tapret, opret }, + _ => unreachable!(), + }) + } } /// Txid and height information ordered according to the RGB consensus rules. diff --git a/src/stl.rs b/src/stl.rs index a560029e..8b681caa 100644 --- a/src/stl.rs +++ b/src/stl.rs @@ -32,7 +32,7 @@ use crate::{AnchoredBundle, ContractState, Extension, Genesis, SubSchema, LIB_NA /// Strict types id for the library providing data types for RGB consensus. pub const LIB_ID_RGB: &str = - "urn:ubideco:stl:9gBiimtSnE1Qxwa49rbdPpv2s5ggXfitb6aktnunpDjX#cosmos-gentle-goblin"; + "urn:ubideco:stl:Cx3Lxt83gdcuXnTxTvSXfxyZUeNi5fqaiibEYr6o7jxy#panic-genius-delphi"; fn _rgb_core_stl() -> Result { LibBuilder::new(libname!(LIB_NAME_RGB), tiny_bset! { diff --git a/src/validation/logic.rs b/src/validation/logic.rs index 0a61934b..b0e1007c 100644 --- a/src/validation/logic.rs +++ b/src/validation/logic.rs @@ -35,11 +35,11 @@ use crate::{ }; impl Schema { - pub fn validate_state<'validator, 'consignment, 'vm, C: ConsignmentApi>( + pub fn validate_state<'validator, 'consignment, C: ConsignmentApi>( &'validator self, consignment: &'validator CheckedConsignment<'consignment, C>, op: OpRef, - vm: &'vm dyn VirtualMachine, + vm: &'consignment dyn VirtualMachine, ) -> validation::Status { let id = op.id(); diff --git a/src/validation/validator.rs b/src/validation/validator.rs index f821f178..36948f9c 100644 --- a/src/validation/validator.rs +++ b/src/validation/validator.rs @@ -49,7 +49,7 @@ pub trait ResolveTx { fn resolve_bp_tx(&self, layer1: Layer1, txid: Txid) -> Result; } -pub struct Validator<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> { +pub struct Validator<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> { consignment: CheckedConsignment<'consignment, C>, status: Status, @@ -62,17 +62,22 @@ pub struct Validator<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: Resolve validated_op_seals: BTreeSet, validated_op_state: BTreeSet, - vm: Box, + vm: Box, resolver: &'resolver R, } -impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> - Validator<'consignment, 'vm, 'resolver, C, R> +impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> + Validator<'consignment, 'resolver, C, R> { fn init(consignment: &'consignment C, resolver: &'resolver R) -> Self { // We use validation status object to store all detected failures and // warnings let mut status = Status::default(); + let vm = match consignment.schema().script { + Script::AluVM(ref lib) => { + Box::new(AluRuntime::new(lib)) as Box + } + }; let consignment = CheckedConsignment::new(consignment); // Frequently used computation-heavy data @@ -114,12 +119,6 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> let mut layers1 = bset! { Layer1::Bitcoin }; layers1.extend(genesis.alt_layers1.iter().map(AltLayer1::layer1)); - let vm = match &consignment.schema().script { - Script::AluVM(lib) => { - Box::new(AluRuntime::new(lib)) as Box - } - }; - Self { consignment, status, @@ -178,7 +177,7 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> fn validate_schema(&mut self, schema: &SubSchema) { self.status += schema.verify(); } // *** PART II: Validating business logic - fn validate_logic(&mut self, schema: &Schema) { + fn validate_logic(&mut self, schema: &'consignment Schema) { // [VALIDATION]: Making sure that we were supplied with the schema // that corresponds to the schema of the contract genesis if schema.schema_id() != self.schema_id { @@ -220,7 +219,7 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> fn validate_logic_on_route( &mut self, schema: &Schema, - transition: &'consignment Transition, + transition: &Transition, ) { let mut queue: VecDeque = VecDeque::new(); @@ -315,17 +314,11 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> let bundle = &anchored_bundle.bundle; // [VALIDATION]: We validate that the seals were properly defined on BP-type layers - let (tapret_seals, opret_seals, input_map) = - self.validate_seal_definitions(layer1, witness_id, bundle); + let (seals, input_map) = self.validate_seal_definitions(layer1, witness_id, bundle); // [VALIDATION]: We validate that the seals were properly closed on BP-type layers - let Some(witness_tx) = self.validate_commitments_bp( - layer1, - &tapret_seals, - &opret_seals, - bundle_id, - anchors, - ) else { + let Some(witness_tx) = self.validate_commitments_bp(layer1, &seals, bundle_id, anchors) + else { continue; }; @@ -341,20 +334,20 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> fn validate_bundle_commitments( &mut self, bundle_id: BundleId, - bundle: &'consignment TransitionBundle, + bundle: &TransitionBundle, witness_tx: Tx, input_map: BTreeMap>, ) { - for (vin, opid) in bundle.input_map { - let Some(outpoints) = input_map.get(&opid) else { + for (vin, opid) in &bundle.input_map { + let Some(outpoints) = input_map.get(opid) else { self.status - .add_failure(Failure::BundleExtraTransition(bundle_id, opid)); + .add_failure(Failure::BundleExtraTransition(bundle_id, *opid)); continue; }; let Some(input) = witness_tx.inputs.get(vin.to_usize()) else { self.status.add_failure(Failure::BundleInvalidInput( bundle_id, - opid, + *opid, witness_tx.txid(), )); continue; @@ -362,9 +355,9 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> if !outpoints.contains(&input.prev_output) { self.status.add_failure(Failure::BundleInvalidCommitment( bundle_id, - vin, + *vin, witness_tx.txid(), - opid, + *opid, )); } } @@ -372,13 +365,12 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> /// Bitcoin- and liquid-specific commitment validation using deterministic /// bitcoin commitments with opret and tapret schema. - fn validate_commitments_bp<'seal>( + fn validate_commitments_bp( &mut self, layer1: Layer1, - tapret_seals: impl IntoIterator>, - opret_seals: impl IntoIterator>, + seals: impl AsRef<[ExplicitSeal]>, bundle_id: BundleId, - anchors: &'consignment AnchorSet, + anchors: &AnchorSet, ) -> Option { let Some(txid) = anchors.txid() else { self.status @@ -405,22 +397,27 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> None } Ok(witness_tx) => { - let (tapret, opret) = match anchors { - AnchorSet::Tapret(tapret) => (Some(tapret), None), - AnchorSet::Opret(opret) => (None, Some(opret)), - AnchorSet::Dual { tapret, opret } => (Some(tapret), Some(opret)), - }; + let (tapret, opret) = anchors.as_split(); + let tapret_seals = seals + .as_ref() + .iter() + .filter(|seal| seal.method() == CloseMethod::TapretFirst); if let Some(tapret) = tapret { let witness = Witness::with(witness_tx.clone(), tapret.clone()); self.validate_seal_closing_bp(tapret_seals, witness, bundle_id, tapret) - } else if tapret_seals.into_iter().count() > 0 { + } else if tapret_seals.count() > 0 { self.status.add_warning(Warning::UnclosedSeals(bundle_id)); } + + let opret_seals = seals + .as_ref() + .iter() + .filter(|seal| seal.method() == CloseMethod::OpretFirst); if let Some(opret) = opret { let witness = Witness::with(witness_tx.clone(), opret.clone()); self.validate_seal_closing_bp(opret_seals, witness, bundle_id, opret) - } else if opret_seals.into_iter().count() > 0 { + } else if opret_seals.count() > 0 { self.status.add_warning(Warning::UnclosedSeals(bundle_id)); } Some(witness_tx) @@ -436,11 +433,10 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> &mut self, layer1: Layer1, witness_id: WitnessId, - bundle: &'consignment TransitionBundle, - ) -> (Vec>, Vec>, BTreeMap>) - { + bundle: &TransitionBundle, + ) -> (Vec>, BTreeMap>) { let mut input_map: BTreeMap> = bmap!(); - let (mut tapret_seals, mut opret_seals) = (vec![], vec![]); + let mut seals = vec![]; for (opid, transition) in &bundle.known_transitions { let opid = *opid; @@ -500,17 +496,14 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> .expect("method must be called only on BP-compatible layer 1") .reduce_to_bp() .expect("method must be called only on BP-compatible layer 1"); - match seal.method() { - CloseMethod::OpretFirst => opret_seals.push(seal), - CloseMethod::TapretFirst => tapret_seals.push(seal), - } + seals.push(seal); input_map .entry(opid) .or_default() .insert(Outpoint::new(seal.txid, seal.vout)); } } - (tapret_seals, opret_seals, input_map) + (seals, input_map) } /// Single-use-seal closing validation. @@ -523,12 +516,12 @@ impl<'consignment, 'vm, 'resolver, C: ConsignmentApi, R: ResolveTx> /// /// Additionally checks that the provided message contains commitment to the /// bundle under the current contract. - fn validate_seal_closing_bp<'seal, Seal: TxoSeal + 'seal, Dbc: dbc::Proof>( + fn validate_seal_closing_bp<'seal, 'temp, Seal: TxoSeal + 'seal, Dbc: dbc::Proof>( &mut self, seals: impl IntoIterator, witness: Witness, bundle_id: BundleId, - anchor: &'consignment dbc::Anchor, + anchor: &'temp dbc::Anchor, ) { let message = mpc::Message::from(bundle_id); // [VALIDATION]: Checking anchor MPC commitment diff --git a/stl/RGB@0.1.0.sta b/stl/RGB@0.1.0.sta index 9c6509ab..c6a78326 100644 --- a/stl/RGB@0.1.0.sta +++ b/stl/RGB@0.1.0.sta @@ -1,335 +1,342 @@ -----BEGIN STRICT TYPE LIB----- -Id: urn:ubideco:stl:9gBiimtSnE1Qxwa49rbdPpv2s5ggXfitb6aktnunpDjX +Id: urn:ubideco:stl:Cx3Lxt83gdcuXnTxTvSXfxyZUeNi5fqaiibEYr6o7jxy Name: RGB Dependencies: urn:ubideco:stl:ZtHaBzu9ojbDahaGKEXe5v9DfSDxLERbLkEB23R6Q6V, + urn:ubideco:stl:5UnRmQChaU5Czu3AQHxDHPzW9JKARBWoMcVYamwoCLPP, urn:ubideco:stl:5XLKQ1sNryZm9bdFKU2kBY3MPYdZXhchVdQKBbHA3gby, urn:ubideco:stl:9KALDYR8Nyjq4FdMW6kYoL7vdkWnqPqNuFnmE9qHpNjZ, - urn:ubideco:stl:BrKg2wSDRFDFSGzn2RrQkbwFeuLw2yDAFtpQ7WRh3nrz, urn:ubideco:stl:DVtm25LRKU4TjbyZmVxPhvCmctZ6vKkPKqfpU2QsDNUo, urn:ubideco:stl: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 -uwERY29uZmlkZW50aWFsU3RhdGUABgIEc2VhbAHA4gov08gxGtaH+pfqjunbIJMh -b5y9o1wnOqguM8YczwVzdGF0ZQEusqX+F8AwRDCY4FybG1ps1h7tdg1h3suAJtNk -8xbhuwIQY29uZmlkZW50aWFsU2VhbAAGAgRzZWFsAqE3cUlXlgi1ItyqExAFO+4f -42FVjTZ4t8Qi5wv6dv23aBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2CplLO9Bk4F -c3RhdGUBLrKl/hfAMEQwmOBcmxtabNYe7XYNYd7LgCbTZPMW4bsDCHJldmVhbGVk -AAYCBHNlYWwBwOIKL9PIMRrWh/qX6o7p2yCTIW+cvaNcJzqoLjPGHM8Fc3RhdGUB -LrKl/hfAMEQwmOBcmxtabNYe7XYNYd7LgCbTZPMW4bsOQXNzaWdubWVudFR5cGUF -AQAAAhlBc3NpZ25tZW50c0JsaW5kU2VhbFR4UHRyBQEACgGH/uJlWIhs6By+hoSz -eWMnE2WDxbZoAV6LFBY2sUSHuQES0B/DKzjcoudQlinXZaJzuJP3Ng6XQYTyYbhR -3gJQ5wAAAAAAAAAA/wAAAAAAAAAYQXNzaWdubWVudHNCbGluZFNlYWxUeGlkBQEA -CgGH/uJlWIhs6By+hoSzeWMnE2WDxbZoAV6LFBY2sUSHuQGMwdNqHGGkrHkTbqPf -Yf8oiq8voLGVTcVv1Knx91k/WwAAAAAAAAAA/wAAAAAAAAAIQXR0YWNoSWQFAQAH -AABAIAAOQmxpbmRpbmdGYWN0b3IFAQAHAABAIAAKQnVuZGxlSXRlbQYCBmlucHV0 -cwAJAAACAAAAAAAAAAD/AAAAAAAAAAp0cmFuc2l0aW9uAAQCAARub25lAAAAAQRz -b21lAAUBAb61xaOXj7fsShcZqPMJYJ/2pmcSaP3VSOUTHyhQfchXD0NvbmNlYWxl -ZEF0dGFjaAUBAAcAAEAgAA1Db25jZWFsZWREYXRhBQEABwAAQCAAEUNvbmNlYWxl -ZEZ1bmdpYmxlBgIKY29tbWl0bWVudAFIvRpuF/uGOxZ8fiVeNWfbgkPvKl6666LF -Cs0Jrp5RGgpyYW5nZVByb29mAahYa/iRZdrCIxBtvYXEhsk35rBm6wxmQf8WL2io -d6WYD0NvbnRyYWN0SGlzdG9yeQYICHNjaGVtYUlkAZRS09sDq6uoUZ+n9j7QFOvU -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 +QmxpbmRTZWFsVHhQdHIEBAAMY29uZmlkZW50aWFsAAYCBHNlYWwCQozLRHshnceQ +CMzhrzgJsL4vdOKPb3Of+kgu7+Z/l5ZoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcD +YKmUs70GTgVzdGF0ZQH8NEXdX88NC/+sFaR6ugUi4FuLKxswZVKHg497LeuOPQER +Y29uZmlkZW50aWFsU3RhdGUABgIEc2VhbAEkvLMZiKNHP+ioRwLJfnX8PwOt6aqt +h5QAMFBriWteygVzdGF0ZQH8NEXdX88NC/+sFaR6ugUi4FuLKxswZVKHg497LeuO +PQIQY29uZmlkZW50aWFsU2VhbAAGAgRzZWFsAkKMy0R7IZ3HkAjM4a84CbC+L3Ti +j29zn/pILu/mf5eWaBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2CplLO9Bk4Fc3Rh +dGUBaFM0IAzcU8ZNduK2LqpmL9o62ZG/BqspDrzQ9noDSswDCHJldmVhbGVkAAYC +BHNlYWwBJLyzGYijRz/oqEcCyX51/D8DremqrYeUADBQa4lrXsoFc3RhdGUBaFM0 +IAzcU8ZNduK2LqpmL9o62ZG/BqspDrzQ9noDSswhQXNzaWduUmV2ZWFsZWRBdHRh +Y2hCbGluZFNlYWxUeGlkBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkKMy0R7IZ3H +kAjM4a84CbC+L3Tij29zn/pILu/mf5eWaBnrvNWzGKuXs5ilSzZl3dqnBm/o6STn +A2CplLO9Bk4Fc3RhdGUB/DRF3V/PDQv/rBWkeroFIuBbiysbMGVSh4OPey3rjj0B +EWNvbmZpZGVudGlhbFN0YXRlAAYCBHNlYWwBwOIKL9PIMRrWh/qX6o7p2yCTIW+c +vaNcJzqoLjPGHM8Fc3RhdGUB/DRF3V/PDQv/rBWkeroFIuBbiysbMGVSh4OPey3r +jj0CEGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAJCjMtEeyGdx5AIzOGvOAmwvi90 +4o9vc5/6SC7v5n+XlmgZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0 +YXRlAWhTNCAM3FPGTXbiti6qZi/aOtmRvwarKQ680PZ6A0rMAwhyZXZlYWxlZAAG +AgRzZWFsAcDiCi/TyDEa1of6l+qO6dsgkyFvnL2jXCc6qC4zxhzPBXN0YXRlAWhT +NCAM3FPGTXbiti6qZi/aOtmRvwarKQ680PZ6A0rMIEFzc2lnblJldmVhbGVkRGF0 +YUJsaW5kU2VhbFR4UHRyBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkKMy0R7IZ3H +kAjM4a84CbC+L3Tij29zn/pILu/mf5eWaBnrvNWzGKuXs5ilSzZl3dqnBm/o6STn +A2CplLO9Bk4Fc3RhdGUBcA1lELKCgW8ftz3lEmOTA1cIpVJhrQLg1L5axc+n6gkB +EWNvbmZpZGVudGlhbFN0YXRlAAYCBHNlYWwBJLyzGYijRz/oqEcCyX51/D8Dremq +rYeUADBQa4lrXsoFc3RhdGUBcA1lELKCgW8ftz3lEmOTA1cIpVJhrQLg1L5axc+n +6gkCEGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAJCjMtEeyGdx5AIzOGvOAmwvi90 +4o9vc5/6SC7v5n+XlmgZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0 +YXRlASDyUFYij2bO/JH66me8X8bwaVw9R3lzWT+s0TfKckhlAwhyZXZlYWxlZAAG +AgRzZWFsASS8sxmIo0c/6KhHAsl+dfw/A63pqq2HlAAwUGuJa17KBXN0YXRlASDy +UFYij2bO/JH66me8X8bwaVw9R3lzWT+s0TfKckhlH0Fzc2lnblJldmVhbGVkRGF0 +YUJsaW5kU2VhbFR4aWQEBAAMY29uZmlkZW50aWFsAAYCBHNlYWwCQozLRHshnceQ +CMzhrzgJsL4vdOKPb3Of+kgu7+Z/l5ZoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcD +YKmUs70GTgVzdGF0ZQFwDWUQsoKBbx+3PeUSY5MDVwilUmGtAuDUvlrFz6fqCQER +Y29uZmlkZW50aWFsU3RhdGUABgIEc2VhbAHA4gov08gxGtaH+pfqjunbIJMhb5y9 +o1wnOqguM8YczwVzdGF0ZQFwDWUQsoKBbx+3PeUSY5MDVwilUmGtAuDUvlrFz6fq +CQIQY29uZmlkZW50aWFsU2VhbAAGAgRzZWFsAkKMy0R7IZ3HkAjM4a84CbC+L3Ti +j29zn/pILu/mf5eWaBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2CplLO9Bk4Fc3Rh +dGUBIPJQViKPZs78kfrqZ7xfxvBpXD1HeXNZP6zRN8pySGUDCHJldmVhbGVkAAYC +BHNlYWwBwOIKL9PIMRrWh/qX6o7p2yCTIW+cvaNcJzqoLjPGHM8Fc3RhdGUBIPJQ +ViKPZs78kfrqZ7xfxvBpXD1HeXNZP6zRN8pySGUhQXNzaWduUmV2ZWFsZWRWYWx1 +ZUJsaW5kU2VhbFR4UHRyBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkKMy0R7IZ3H +kAjM4a84CbC+L3Tij29zn/pILu/mf5eWaBnrvNWzGKuXs5ilSzZl3dqnBm/o6STn +A2CplLO9Bk4Fc3RhdGUBwlGtZel1DayaElnMwIUkXNX3sW9S2HI2RizxdYyfTl0B +EWNvbmZpZGVudGlhbFN0YXRlAAYCBHNlYWwBJLyzGYijRz/oqEcCyX51/D8Dremq +rYeUADBQa4lrXsoFc3RhdGUBwlGtZel1DayaElnMwIUkXNX3sW9S2HI2RizxdYyf +Tl0CEGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAJCjMtEeyGdx5AIzOGvOAmwvi90 +4o9vc5/6SC7v5n+XlmgZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0 +YXRlAW1ExvQvagU6QxUPpubCLMToeuUslPgG1Ayl3If2dY2gAwhyZXZlYWxlZAAG +AgRzZWFsASS8sxmIo0c/6KhHAsl+dfw/A63pqq2HlAAwUGuJa17KBXN0YXRlAW1E +xvQvagU6QxUPpubCLMToeuUslPgG1Ayl3If2dY2gIEFzc2lnblJldmVhbGVkVmFs +dWVCbGluZFNlYWxUeGlkBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkKMy0R7IZ3H +kAjM4a84CbC+L3Tij29zn/pILu/mf5eWaBnrvNWzGKuXs5ilSzZl3dqnBm/o6STn +A2CplLO9Bk4Fc3RhdGUBwlGtZel1DayaElnMwIUkXNX3sW9S2HI2RizxdYyfTl0B +EWNvbmZpZGVudGlhbFN0YXRlAAYCBHNlYWwBwOIKL9PIMRrWh/qX6o7p2yCTIW+c +vaNcJzqoLjPGHM8Fc3RhdGUBwlGtZel1DayaElnMwIUkXNX3sW9S2HI2RizxdYyf +Tl0CEGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAJCjMtEeyGdx5AIzOGvOAmwvi90 +4o9vc5/6SC7v5n+XlmgZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0 +YXRlAW1ExvQvagU6QxUPpubCLMToeuUslPgG1Ayl3If2dY2gAwhyZXZlYWxlZAAG +AgRzZWFsAcDiCi/TyDEa1of6l+qO6dsgkyFvnL2jXCc6qC4zxhzPBXN0YXRlAW1E +xvQvagU6QxUPpubCLMToeuUslPgG1Ayl3If2dY2gHUFzc2lnblZvaWRTdGF0ZUJs +aW5kU2VhbFR4UHRyBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkKMy0R7IZ3HkAjM +4a84CbC+L3Tij29zn/pILu/mf5eWaBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2Cp +lLO9Bk4Fc3RhdGUBLrKl/hfAMEQwmOBcmxtabNYe7XYNYd7LgCbTZPMW4bsBEWNv +bmZpZGVudGlhbFN0YXRlAAYCBHNlYWwBJLyzGYijRz/oqEcCyX51/D8DremqrYeU +ADBQa4lrXsoFc3RhdGUBLrKl/hfAMEQwmOBcmxtabNYe7XYNYd7LgCbTZPMW4bsC +EGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAJCjMtEeyGdx5AIzOGvOAmwvi904o9v +c5/6SC7v5n+XlmgZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0YXRl +AS6ypf4XwDBEMJjgXJsbWmzWHu12DWHey4Am02TzFuG7AwhyZXZlYWxlZAAGAgRz +ZWFsASS8sxmIo0c/6KhHAsl+dfw/A63pqq2HlAAwUGuJa17KBXN0YXRlAS6ypf4X +wDBEMJjgXJsbWmzWHu12DWHey4Am02TzFuG7HEFzc2lnblZvaWRTdGF0ZUJsaW5k +U2VhbFR4aWQEBAAMY29uZmlkZW50aWFsAAYCBHNlYWwCQozLRHshnceQCMzhrzgJ +sL4vdOKPb3Of+kgu7+Z/l5ZoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcDYKmUs70G +TgVzdGF0ZQEusqX+F8AwRDCY4FybG1ps1h7tdg1h3suAJtNk8xbhuwERY29uZmlk +ZW50aWFsU3RhdGUABgIEc2VhbAHA4gov08gxGtaH+pfqjunbIJMhb5y9o1wnOqgu +M8YczwVzdGF0ZQEusqX+F8AwRDCY4FybG1ps1h7tdg1h3suAJtNk8xbhuwIQY29u +ZmlkZW50aWFsU2VhbAAGAgRzZWFsAkKMy0R7IZ3HkAjM4a84CbC+L3Tij29zn/pI +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 +ZXNzAAQCAARub25lAAAAAQRzb21lAAUBAcSgCp7hCQITdyIBFVk7g8NT4mD4gRDk +szbK42hGQScbHE91dHB1dEFzc2lnbm1lbnRSZXZlYWxlZERhdGEGBAVvcG91dAGT +ELyAsTRaiy/DWFLuD01o0B23+jXLm2SSq1YJmvSalwRzZWFsAQeL+m/TAkZzkq3S +wOCPOMBFZeR4uOTUtZR3Y2uJnI0GBXN0YXRlASDyUFYij2bO/JH66me8X8bwaVw9 +R3lzWT+s0TfKckhlB3dpdG5lc3MABAIABG5vbmUAAAABBHNvbWUABQEBxKAKnuEJ +AhN3IgEVWTuDw1PiYPiBEOSzNsrjaEZBJxsdT3V0cHV0QXNzaWdubWVudFJldmVh +bGVkVmFsdWUGBAVvcG91dAGTELyAsTRaiy/DWFLuD01o0B23+jXLm2SSq1YJmvSa +lwRzZWFsAQeL+m/TAkZzkq3SwOCPOMBFZeR4uOTUtZR3Y2uJnI0GBXN0YXRlAW1E +xvQvagU6QxUPpubCLMToeuUslPgG1Ayl3If2dY2gB3dpdG5lc3MABAIABG5vbmUA +AAABBHNvbWUABQEBxKAKnuEJAhN3IgEVWTuDw1PiYPiBEOSzNsrjaEZBJxsZT3V0 +cHV0QXNzaWdubWVudFZvaWRTdGF0ZQYEBW9wb3V0AZMQvICxNFqLL8NYUu4PTWjQ +Hbf6NcubZJKrVgma9JqXBHNlYWwBB4v6b9MCRnOSrdLA4I84wEVl5Hi45NS1lHdj +a4mcjQYFc3RhdGUBLrKl/hfAMEQwmOBcmxtabNYe7XYNYd7LgCbTZPMW4bsHd2l0 +bmVzcwAEAgAEbm9uZQAAAAEEc29tZQAFAQHEoAqe4QkCE3ciARVZO4PDU+Jg+IEQ +5LM2yuNoRkEnGxJQZWRlcnNlbkNvbW1pdG1lbnQFAQAHAABAIQAKUmFuZ2VQcm9v +ZgQB/wtwbGFjZWhvbGRlcgAFAQEedhfxJ33bPrvhag9yEbdt7VXfb0MNVRFfA3gn +pUJXJwhSZWRlZW1lZAUBAAoBRu3mwwgc/bjmV6QqXODGCIR//B++Xna5cXSpyvWJ +Wc4Blcjmeh51Yl/UllVCapHrfkKQoW9amPG+UPe2iiQS98UAAAAAAAAAAP8AAAAA +AAAADFJlc2VydmVkQnl0ZQUBAAABDlJldmVhbGVkQXR0YWNoBgMCaWQBhHENkyxO +9MO3CEtpi7CHcCl+OWQkf0WR2NqDbdF9ujgJbWVkaWFUeXBlAUIwYYWIyNSrFCZA +x/3JFyzN0P8Q/w2TgABEfIia3cx5BHNhbHQAAAgMUmV2ZWFsZWREYXRhBQEACAAA +QAAAAAAAAAAA//8AAAAAAAAQUmV2ZWFsZWRGdW5naWJsZQYDBXZhbHVlAaaMMJFH +S8o6wmKMx5VEjSzdqsUUnwUzlav2PFVhBxcmCGJsaW5kaW5nAYW4+Cu79KSmDbO/ +P0W4D5RueIPDrVJtk/RvowGobkfaA3RhZwHJj5qpwwZLGv39ZxuXvCr8/kxojx9z +yC3rcW/naZsirwZTY2hlbWEGCgNmZnYB2ptRE1gWVnaQh/uZ5VaUcjaA1zkMBqHM +JJgsBWT3zNUIc3Vic2V0T2YABAIABG5vbmUAAAABBHNvbWUABQEAAAALZ2xvYmFs +VHlwZXMACgHV7pIOSYizafFqU9EHSvu1I/jHUnEe+zY9VlkF4eQVywHHmKbYYzZ4 +RB7aUW4sPou/DqiwSoDBtx256XLmP3HmJAAAAAAAAAAA/wAAAAAAAAAKb3duZWRU +eXBlcwAKAYf+4mVYiGzoHL6GhLN5YycTZYPFtmgBXosUFjaxRIe5ATjKFOCFIsfj +OYJGlLLDmVh1U6boygwO4eiVibqJdxvzAAAAAAAAAAD/AAAAAAAAAAx2YWxlbmN5 +VHlwZXMACQFG7ebDCBz9uOZXpCpc4MYIhH/8H75edrlxdKnK9YlZzgAAAAAAAAAA +/wAAAAAAAAAHZ2VuZXNpcwGrQv8KxJBqRRD4VF8GJCpNCYyQVViz09s3LHCMWeBM +QwpleHRlbnNpb25zAAoBZHUeQqkVoTxDEYLV/4bVHNNEcKOQ4UrsoFDMOlNvSN4B +ZrSXhI/EeTlg3zSS63YSkLMOrBDRPTdULVJkEQiUUHEAAAAAAAAAAP8AAAAAAAAA +C3RyYW5zaXRpb25zAAoBNFIPrhOWGl69KfwRIz+FTvIQOAR/1AS+36FG2RalMmgB +vcdveMo95d9+PKqU9FGUnC0VNYeAXXvK6KRai0nxAX8AAAAAAAAAAP8AAAAAAAAA +CnR5cGVTeXN0ZW0CQzQDlNgbMOJSKJAmHvNv+fioOVGR9QtpXiMqHrO3QcguR1s+ +c8ngIm2OLCe6FLOqJb5tKPdHfiz9jE0oXhjsVgZzY3JpcHQBxhhje2dNDLS8qcBD +XX8yYoOYeHN0J0PRN+VE+7oS0EwIU2NoZW1hSWQFAQAHAABAIAAMU2NoZW1hU2No +ZW1hBgoDZmZ2AdqbURNYFlZ2kIf7meVWlHI2gNc5DAahzCSYLAVk98zVCHN1YnNl +dE9mAAQCAARub25lAAAAAQRzb21lAAUBAT2/VtLujbQw6i9r87tqbrHBuVFKvDll +p3WPgFgHnI+6C2dsb2JhbFR5cGVzAAoB1e6SDkmIs2nxalPRB0r7tSP4x1JxHvs2 +PVZZBeHkFcsBx5im2GM2eEQe2lFuLD6Lvw6osEqAwbcduely5j9x5iQAAAAAAAAA +AP8AAAAAAAAACm93bmVkVHlwZXMACgGH/uJlWIhs6By+hoSzeWMnE2WDxbZoAV6L +FBY2sUSHuQE4yhTghSLH4zmCRpSyw5lYdVOm6MoMDuHolYm6iXcb8wAAAAAAAAAA +/wAAAAAAAAAMdmFsZW5jeVR5cGVzAAkBRu3mwwgc/bjmV6QqXODGCIR//B++Xna5 +cXSpyvWJWc4AAAAAAAAAAP8AAAAAAAAAB2dlbmVzaXMBq0L/CsSQakUQ+FRfBiQq +TQmMkFVYs9PbNyxwjFngTEMKZXh0ZW5zaW9ucwAKAWR1HkKpFaE8QxGC1f+G1RzT +RHCjkOFK7KBQzDpTb0jeAWa0l4SPxHk5YN80kut2EpCzDqwQ0T03VC1SZBEIlFBx +AAAAAAAAAAD/AAAAAAAAAAt0cmFuc2l0aW9ucwAKATRSD64TlhpevSn8ESM/hU7y +EDgEf9QEvt+hRtkWpTJoAb3Hb3jKPeXffjyqlPRRlJwtFTWHgF17yuikWotJ8QF/ +AAAAAAAAAAD/AAAAAAAAAAp0eXBlU3lzdGVtAkM0A5TYGzDiUiiQJh7zb/n4qDlR +kfULaV4jKh6zt0HILkdbPnPJ4CJtjiwnuhSzqiW+bSj3R34s/YxNKF4Y7FYGc2Ny +aXB0AcYYY3tnTQy0vKnAQ11/MmKDmHhzdCdD0TflRPu6EtBMBlNjcmlwdAQBAAVh +bHVWbQAFAQGi+uqecFyckczb3Ubtj3DljvUUgz7IPlxktWpbw1OtsAtTdGF0ZVNj +aGVtYQQEAAtkZWNsYXJhdGl2ZQAAAAEIZnVuZ2libGUABQEB+fSsCGauQXdm1P0M +EX0EsdlU/Q5nXhI7YgTrcU/p5UYCCnN0cnVjdHVyZWQABQECQzQDlNgbMOJSKJAm +HvNv+fioOVGR9QtpXiMqHrO3QchrBKMUnqaVABZnn+8CtKsk9ea3imTI2dC9ZfzX +o1hOjQMKYXR0YWNobWVudAAFAQFCMGGFiMjUqxQmQMf9yRcszdD/EP8Nk4AARHyI +mt3MeQpUcmFuc2l0aW9uBggDZmZ2AdqbURNYFlZ2kIf7meVWlHI2gNc5DAahzCSY +LAVk98zVCmNvbnRyYWN0SWQBnwgsSTrIAqK6xd3cCyJ8IK+U1GjESM8aWiHgvcL1 +OjIOdHJhbnNpdGlvblR5cGUBNFIPrhOWGl69KfwRIz+FTvIQOAR/1AS+36FG2Ral +MmgIbWV0YWRhdGEACAAAQAAAAAAAAAAA//8AAAAAAAAHZ2xvYmFscwGiM8noKE7M +dZj8BjnnNNDLAoxLLNyEkT6Z2apGPiTWUgZpbnB1dHMB+XhNoLZD3jl8HtlXjTOM +N9Ecy+JKweinmkzk1fwYJ6sLYXNzaWdubWVudHMBUr+sEbRdpcKFNeoNxtKGPgeO +jWhBiEZSE6d5Rv2gOfYJdmFsZW5jaWVzAd2FZgBvYWQRFO6O8deq+AmzGiSwqiep +m7Iw6KrPKUdNEFRyYW5zaXRpb25CdW5kbGUGAghpbnB1dE1hcAAKAvVsE2Ij9jmn +SgmT3EdGyfmKq7iDWF212RY/GH7EKBoKIeM+Q8WqXPIpJ1OjOMFn7TtjnE3Zzr2p +jzRpF7rJQ3UBlcjmeh51Yl/UllVCapHrfkKQoW9amPG+UPe2iiQS98UBAAAAAAAA +AP//AAAAAAAAEGtub3duVHJhbnNpdGlvbnMACgGVyOZ6HnViX9SWVUJqket+QpCh +b1qY8b5Q97aKJBL3xQG+tcWjl4+37EoXGajzCWCf9qZnEmj91UjlEx8oUH3IVwEA +AAAAAAAA//8AAAAAAAAQVHJhbnNpdGlvblNjaGVtYQYFCG1ldGFkYXRhAkM0A5TY +GzDiUiiQJh7zb/n4qDlRkfULaV4jKh6zt0HIawSjFJ6mlQAWZ5/vArSrJPXmt4pk +yNnQvWX816NYTo0HZ2xvYmFscwAKAdXukg5JiLNp8WpT0QdK+7Uj+MdScR77Nj1W +WQXh5BXLATbBNKH6oIETp90wgDxesPLC/doUsNWj1pPDbp4PMqp5AAAAAAAAAAD/ +AAAAAAAAAAZpbnB1dHMACgGH/uJlWIhs6By+hoSzeWMnE2WDxbZoAV6LFBY2sUSH +uQE2wTSh+qCBE6fdMIA8XrDywv3aFLDVo9aTw26eDzKqeQAAAAAAAAAA/wAAAAAA AAALYXNzaWdubWVudHMACgGH/uJlWIhs6By+hoSzeWMnE2WDxbZoAV6LFBY2sUSH uQE2wTSh+qCBE6fdMIA8XrDywv3aFLDVo9aTw26eDzKqeQAAAAAAAAAA/wAAAAAA AAAJdmFsZW5jaWVzAAkBRu3mwwgc/bjmV6QqXODGCIR//B++Xna5cXSpyvWJWc4A -AAAAAAAAAP8AAAAAAAAADUV4dGVuc2lvblR5cGUFAQAAAgNGZnYFAQAAAg1GdW5n -aWJsZVN0YXRlBAEIBmJpdHM2NAAFAQAACAxGdW5naWJsZVR5cGUDAQ11bnNpZ25l -ZDY0Qml0CAdHZW5lc2lzBggDZmZ2AdqbURNYFlZ2kIf7meVWlHI2gNc5DAahzCSY -LAVk98zVCHNjaGVtYUlkAZRS09sDq6uoUZ+n9j7QFOvUTX0xP/z+APpdJHpUBJAQ -B3Rlc3RuZXQCe4SAPJ764hElp3wsObxw0v3o+UOuDf2c9OaC7cdmynBhhiLRe67w -ZgLf53XJgOCza2666AkNgHX3UTvsS5P2TQphbHRMYXllcnMxASRXUthlgPCcymoj -N1QSRCBgfKMsf+7M8RQQAG8KCSqMCG1ldGFkYXRhAAgAAEAAAAAAAAAAAP//AAAA -AAAAB2dsb2JhbHMBojPJ6ChOzHWY/AY55zTQywKMSyzchJE+mdmqRj4k1lILYXNz -aWdubWVudHMBX1ZZY2/LWdfVOFQgFBdf+G8jBkAmsSfSXbJRgoYLq6wJdmFsZW5j -aWVzAd2FZgBvYWQRFO6O8deq+AmzGiSwqiepm7Iw6KrPKUdNDUdlbmVzaXNTY2hl -bWEGBAhtZXRhZGF0YQJDNAOU2Bsw4lIokCYe82/5+Kg5UZH1C2leIyoes7dByGsE -oxSeppUAFmef7wK0qyT15reKZMjZ0L1l/NejWE6NB2dsb2JhbHMACgHV7pIOSYiz -afFqU9EHSvu1I/jHUnEe+zY9VlkF4eQVywE2wTSh+qCBE6fdMIA8XrDywv3aFLDV -o9aTw26eDzKqeQAAAAAAAAAA/wAAAAAAAAALYXNzaWdubWVudHMACgGH/uJlWIhs -6By+hoSzeWMnE2WDxbZoAV6LFBY2sUSHuQE2wTSh+qCBE6fdMIA8XrDywv3aFLDV -o9aTw26eDzKqeQAAAAAAAAAA/wAAAAAAAAAJdmFsZW5jaWVzAAkBRu3mwwgc/bjm -V6QqXODGCIR//B++Xna5cXSpyvWJWc4AAAAAAAAAAP8AAAAAAAAACUdsb2JhbE9y -ZAYCDXdpdG5lc3NBbmNob3IABAIABG5vbmUAAAABBHNvbWUABQEB6ta3unkK4FGp -fiwZ+PwhnMW+Kp3v3/3VY6uveGsbggsDaWR4AAACC0dsb2JhbFN0YXRlBQEACgHV -7pIOSYizafFqU9EHSvu1I/jHUnEe+zY9VlkF4eQVywFGNH2lHu1oDF77by+mxG/p -2cNS74mOKbKURqaNxqBepgAAAAAAAAAA/wAAAAAAAAARR2xvYmFsU3RhdGVTY2hl -bWEGAgVzZW1JZAJDNAOU2Bsw4lIokCYe82/5+Kg5UZH1C2leIyoes7dByGsEoxSe -ppUAFmef7wK0qyT15reKZMjZ0L1l/NejWE6NCG1heEl0ZW1zAAACD0dsb2JhbFN0 -YXRlVHlwZQUBAAACDEdsb2JhbFZhbHVlcwUBAAgBIPJQViKPZs78kfrqZ7xfxvBp -XD1HeXNZP6zRN8pySGUBAAAAAAAAAP//AAAAAAAABUlucHV0BgIHcHJldk91dAGT -ELyAsTRaiy/DWFLuD01o0B23+jXLm2SSq1YJmvSalwhyZXNlcnZlZAFFKqVffdYB -SouhbcRmMrYP8bVs3DpTLs+9a5PVZxmeiQZJbnB1dHMFAQAJAclCQiLtAr5Haf1P -Ix2zRU6nKLxDqBEO2zPLjy8KnkhGAAAAAAAAAAD/AAAAAAAAAAlNZWRpYVR5cGUD -AQNhbnn/CU5vaXNlRHVtYgUBAAcAAEAAAgtPY2N1cnJlbmNlcwYCA21pbgAAAgNt -YXgAAAIET3BJZAUBAAcAAEAgAAVPcG91dAYDAm9wAZXI5noedWJf1JZVQmqR635C -kKFvWpjxvlD3tookEvfFAnR5AYf+4mVYiGzoHL6GhLN5YycTZYPFtmgBXosUFjax -RIe5Am5vAAACHk91dHB1dEFzc2lnbm1lbnRSZXZlYWxlZEF0dGFjaAYEBW9wb3V0 -AZMQvICxNFqLL8NYUu4PTWjQHbf6NcubZJKrVgma9JqXBHNlYWwBB4v6b9MCRnOS -rdLA4I84wEVl5Hi45NS1lHdja4mcjQYFc3RhdGUBaFM0IAzcU8ZNduK2LqpmL9o6 -2ZG/BqspDrzQ9noDSswHd2l0bmVzcwAEAgAEbm9uZQAAAAEEc29tZQAFAQHEoAqe -4QkCE3ciARVZO4PDU+Jg+IEQ5LM2yuNoRkEnGxxPdXRwdXRBc3NpZ25tZW50UmV2 -ZWFsZWREYXRhBgQFb3BvdXQBkxC8gLE0Wosvw1hS7g9NaNAdt/o1y5tkkqtWCZr0 -mpcEc2VhbAEHi/pv0wJGc5Kt0sDgjzjARWXkeLjk1LWUd2NriZyNBgVzdGF0ZQEg -8lBWIo9mzvyR+upnvF/G8GlcPUd5c1k/rNE3ynJIZQd3aXRuZXNzAAQCAARub25l -AAAAAQRzb21lAAUBAcSgCp7hCQITdyIBFVk7g8NT4mD4gRDkszbK42hGQScbHU91 -dHB1dEFzc2lnbm1lbnRSZXZlYWxlZFZhbHVlBgQFb3BvdXQBkxC8gLE0Wosvw1hS -7g9NaNAdt/o1y5tkkqtWCZr0mpcEc2VhbAEHi/pv0wJGc5Kt0sDgjzjARWXkeLjk -1LWUd2NriZyNBgVzdGF0ZQFtRMb0L2oFOkMVD6bmwizE6HrlLJT4BtQMpdyH9nWN -oAd3aXRuZXNzAAQCAARub25lAAAAAQRzb21lAAUBAcSgCp7hCQITdyIBFVk7g8NT -4mD4gRDkszbK42hGQScbGU91dHB1dEFzc2lnbm1lbnRWb2lkU3RhdGUGBAVvcG91 -dAGTELyAsTRaiy/DWFLuD01o0B23+jXLm2SSq1YJmvSalwRzZWFsAQeL+m/TAkZz -kq3SwOCPOMBFZeR4uOTUtZR3Y2uJnI0GBXN0YXRlAS6ypf4XwDBEMJjgXJsbWmzW -Hu12DWHey4Am02TzFuG7B3dpdG5lc3MABAIABG5vbmUAAAABBHNvbWUABQEBxKAK -nuEJAhN3IgEVWTuDw1PiYPiBEOSzNsrjaEZBJxsSUGVkZXJzZW5Db21taXRtZW50 -BQEABwAAQCEAClJhbmdlUHJvb2YEAf8LcGxhY2Vob2xkZXIABQEBHnYX8Sd92z67 -4WoPchG3be1V329DDVURXwN4J6VCVycIUmVkZWVtZWQFAQAKAUbt5sMIHP245lek -KlzgxgiEf/wfvl52uXF0qcr1iVnOAZXI5noedWJf1JZVQmqR635CkKFvWpjxvlD3 -tookEvfFAAAAAAAAAAD/AAAAAAAAAAxSZXNlcnZlZEJ5dGUFAQAAAQ5SZXZlYWxl -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 -w26eDzKqeQAAAAAAAAAA/wAAAAAAAAAJdmFsZW5jaWVzAAkBRu3mwwgc/bjmV6Qq -XODGCIR//B++Xna5cXSpyvWJWc4AAAAAAAAAAP8AAAAAAAAADlRyYW5zaXRpb25U -eXBlBQEAAAIaVHlwZWRBc3NpZ25zQmxpbmRTZWFsVHhQdHIEBAALZGVjbGFyYXRp -dmUABQEACAHuDPbvcwu3Ix7KFJ/LQ3wQWNWsVySNqFCGuJENkk+72AAAAAAAAAAA -//8AAAAAAAABCGZ1bmdpYmxlAAUBAAgB7DjvvzrgeG3pSdVTCf/SpHSC5tcSnZEf -LCdbh9l1nUcAAAAAAAAAAP//AAAAAAAAAgpzdHJ1Y3R1cmVkAAUBAAgB4sRdTDlm -ehorcHwpTaTJpmq3gWAwlq8DekNtuKT8qEsAAAAAAAAAAP//AAAAAAAA/wphdHRh -Y2htZW50AAUBAAgBKZZ9nq4XJK6Z2/C/p/re+UZi6T2WjZ/83r85fZXtnQEAAAAA -AAAAAP//AAAAAAAAGVR5cGVkQXNzaWduc0JsaW5kU2VhbFR4aWQEBAALZGVjbGFy -YXRpdmUABQEACAEOL75mFCKjObsFQvYik9Cifpwx8YL+trq2rNsVdPBiVgAAAAAA -AAAA//8AAAAAAAABCGZ1bmdpYmxlAAUBAAgBP6Xk/XbnoyVMKK58A3Bboboij3LY -xcT6MOGKHPKwSRoAAAAAAAAAAP//AAAAAAAAAgpzdHJ1Y3R1cmVkAAUBAAgB68+w -s5pDFpcjx6rZxoXkIZKoOmYrA+RZXbO75vJsCloAAAAAAAAAAP//AAAAAAAA/wph -dHRhY2htZW50AAUBAAgB/BFPWxxNmu5c38QFiv/3oKNdkudg7RgZmD9xLwolBXwA -AAAAAAAAAP//AAAAAAAACVZhbGVuY2llcwUBAAkBRu3mwwgc/bjmV6QqXODGCIR/ -/B++Xna5cXSpyvWJWc4AAAAAAAAAAP8AAAAAAAAAC1ZhbGVuY3lUeXBlBQEAAAIJ -Vm9pZFN0YXRlBQEAAAANV2l0bmVzc0FuY2hvcgYCCndpdG5lc3NPcmQBwh7s3ADT -vuLrjwKbcjr7sRDANpfpzwNoGZQVpgQHacoJd2l0bmVzc0lkAcSgCp7hCQITdyIB -FVk7g8NT4mD4gRDkszbK42hGQScbCVdpdG5lc3NJZAQCAAdiaXRjb2luAAUBAvVs -E2Ij9jmnSgmT3EdGyfmKq7iDWF212RY/GH7EKBoKo4JC88vX0dChEtqN4WAvVtT4 -bw7ExHbFwGhZTEsEZVYBBmxpcXVpZAAFAQL1bBNiI/Y5p0oJk9xHRsn5iqu4g1hd -tdkWPxh+xCgaCqOCQvPL19HQoRLajeFgL1bU+G8OxMR2xcBoWUxLBGVWCldpdG5l -c3NPcmQEAgAHb25DaGFpbgAFAQEOv7jtOGJupIO6NPMU+VR16VbZRzUT3CcYFjxj -tuGJsQEIb2ZmQ2hhaW4AAAAKV2l0bmVzc1BvcwYCBmhlaWdodAAABAl0aW1lc3Rh -bXAAAEgUWGNoYWluQmxpbmRTZWFsVHhQdHIEAgAHYml0Y29pbgAFAQKhN3FJV5YI -tSLcqhMQBTvuH+NhVY02eLfEIucL+nb9t361+DN8kapvuL2Vu7vL9eHFJYlF0hF2 -2h7U3IzFMSVlAQZsaXF1aWQABQECoTdxSVeWCLUi3KoTEAU77h/jYVWNNni3xCLn -C/p2/bd+tfgzfJGqb7i9lbu7y/XhxSWJRdIRdtoe1NyMxTElZRNYY2hhaW5CbGlu -ZFNlYWxUeGlkBAIAB2JpdGNvaW4ABQECoTdxSVeWCLUi3KoTEAU77h/jYVWNNni3 -xCLnC/p2/bcMUGySSZxY8y8u7CxUuNcWa7yYFOw+mQJFPXEuumEJ4QEGbGlxdWlk -AAUBAqE3cUlXlgi1ItyqExAFO+4f42FVjTZ4t8Qi5wv6dv23DFBskkmcWPMvLuws -VLjXFmu8mBTsPpkCRT1xLrphCeESWGNoYWluRXhwbGljaXRTZWFsBAIAB2JpdGNv -aW4ABQECoTdxSVeWCLUi3KoTEAU77h/jYVWNNni3xCLnC/p2/bdYhY59BZJX7QUN -SLVm+wjlig7dqrMOFqHvGK5jmQRPsgEGbGlxdWlkAAUBAqE3cUlXlgi1ItyqExAF -O+4f42FVjTZ4t8Qi5wv6dv23WIWOfQWSV+0FDUi1ZvsI5YoO3aqzDhah7xiuY5kE -T7I= +AAAAAAAAAP8AAAAAAAAADlRyYW5zaXRpb25UeXBlBQEAAAIaVHlwZWRBc3NpZ25z +QmxpbmRTZWFsVHhQdHIEBAALZGVjbGFyYXRpdmUABQEACAHuDPbvcwu3Ix7KFJ/L +Q3wQWNWsVySNqFCGuJENkk+72AAAAAAAAAAA//8AAAAAAAABCGZ1bmdpYmxlAAUB +AAgB7DjvvzrgeG3pSdVTCf/SpHSC5tcSnZEfLCdbh9l1nUcAAAAAAAAAAP//AAAA +AAAAAgpzdHJ1Y3R1cmVkAAUBAAgB4sRdTDlmehorcHwpTaTJpmq3gWAwlq8DekNt +uKT8qEsAAAAAAAAAAP//AAAAAAAA/wphdHRhY2htZW50AAUBAAgBKZZ9nq4XJK6Z +2/C/p/re+UZi6T2WjZ/83r85fZXtnQEAAAAAAAAAAP//AAAAAAAAGVR5cGVkQXNz +aWduc0JsaW5kU2VhbFR4aWQEBAALZGVjbGFyYXRpdmUABQEACAEOL75mFCKjObsF +QvYik9Cifpwx8YL+trq2rNsVdPBiVgAAAAAAAAAA//8AAAAAAAABCGZ1bmdpYmxl +AAUBAAgBP6Xk/XbnoyVMKK58A3Bboboij3LYxcT6MOGKHPKwSRoAAAAAAAAAAP// +AAAAAAAAAgpzdHJ1Y3R1cmVkAAUBAAgB68+ws5pDFpcjx6rZxoXkIZKoOmYrA+RZ +XbO75vJsCloAAAAAAAAAAP//AAAAAAAA/wphdHRhY2htZW50AAUBAAgB/BFPWxxN +mu5c38QFiv/3oKNdkudg7RgZmD9xLwolBXwAAAAAAAAAAP//AAAAAAAACVZhbGVu +Y2llcwUBAAkBRu3mwwgc/bjmV6QqXODGCIR//B++Xna5cXSpyvWJWc4AAAAAAAAA +AP8AAAAAAAAAC1ZhbGVuY3lUeXBlBQEAAAIJVm9pZFN0YXRlBQEAAAANV2l0bmVz +c0FuY2hvcgYCCndpdG5lc3NPcmQBwh7s3ADTvuLrjwKbcjr7sRDANpfpzwNoGZQV +pgQHacoJd2l0bmVzc0lkAcSgCp7hCQITdyIBFVk7g8NT4mD4gRDkszbK42hGQScb +CVdpdG5lc3NJZAQCAAdiaXRjb2luAAUBAvVsE2Ij9jmnSgmT3EdGyfmKq7iDWF21 +2RY/GH7EKBoKo4JC88vX0dChEtqN4WAvVtT4bw7ExHbFwGhZTEsEZVYBBmxpcXVp +ZAAFAQL1bBNiI/Y5p0oJk9xHRsn5iqu4g1hdtdkWPxh+xCgaCqOCQvPL19HQoRLa +jeFgL1bU+G8OxMR2xcBoWUxLBGVWCldpdG5lc3NPcmQEAgAHb25DaGFpbgAFAQEO +v7jtOGJupIO6NPMU+VR16VbZRzUT3CcYFjxjtuGJsQEIb2ZmQ2hhaW4AAAAKV2l0 +bmVzc1BvcwYCBmhlaWdodAAABAl0aW1lc3RhbXAAAEgUWGNoYWluQmxpbmRTZWFs +VHhQdHIEAgAHYml0Y29pbgAFAQJCjMtEeyGdx5AIzOGvOAmwvi904o9vc5/6SC7v +5n+Xln61+DN8kapvuL2Vu7vL9eHFJYlF0hF22h7U3IzFMSVlAQZsaXF1aWQABQEC +QozLRHshnceQCMzhrzgJsL4vdOKPb3Of+kgu7+Z/l5Z+tfgzfJGqb7i9lbu7y/Xh +xSWJRdIRdtoe1NyMxTElZRNYY2hhaW5CbGluZFNlYWxUeGlkBAIAB2JpdGNvaW4A +BQECQozLRHshnceQCMzhrzgJsL4vdOKPb3Of+kgu7+Z/l5YMUGySSZxY8y8u7CxU +uNcWa7yYFOw+mQJFPXEuumEJ4QEGbGlxdWlkAAUBAkKMy0R7IZ3HkAjM4a84CbC+ +L3Tij29zn/pILu/mf5eWDFBskkmcWPMvLuwsVLjXFmu8mBTsPpkCRT1xLrphCeES +WGNoYWluRXhwbGljaXRTZWFsBAIAB2JpdGNvaW4ABQECQozLRHshnceQCMzhrzgJ +sL4vdOKPb3Of+kgu7+Z/l5ZYhY59BZJX7QUNSLVm+wjlig7dqrMOFqHvGK5jmQRP +sgEGbGlxdWlkAAUBAkKMy0R7IZ3HkAjM4a84CbC+L3Tij29zn/pILu/mf5eWWIWO +fQWSV+0FDUi1ZvsI5YoO3aqzDhah7xiuY5kET7I= -----END STRICT TYPE LIB----- diff --git a/stl/RGB@0.1.0.stl b/stl/RGB@0.1.0.stl index 36f40e3bec20969841b89d79f70f059dd2b15e26..d2a0e46169ef37298c80f9cc68dc40de940ab877 100644 GIT binary patch delta 1752 zcmZpwcw05WNZqODv`e+(+~X5C&OBUi!MS0de#xW${Nnk)JoMf_tDioN%_+b+zbJLG zfr-MzAcM*GITR+I>|x}c9LBiULR9u-FQa0`^S+N?j>f$1e(}uMq}e6%(V@jL@ATg8 zHT0En%uCM5FY-+-%Fam*D9X=I3rQ>}N-dep$7HVN?sq=s!V4d}idVmF>>}Rmh&)?v zzBxUGB}-1&Yqo{;bS{6e(jq1!o0K`vx2W{5{>G4UctxZ-!;*&ov+LNtUpd#~n3C{O zyw6q|!yd3+u#J-&c@!oeVd~jj#(a*op3$Je$Ewqi{q_y*S&QwKnH^d3nwQgQN>caf zFYMR1?C9Sa?hu~=lS1<3BYf(UwYaT`QLH|>m)Cgm0)F+$&v_Ll ztML$}=_s)#Zy~HOk&j5za``tCV_NcLSqb&Yrv!-di2CGQe&fjpc-1E#=U13~Oppjo zib6!XS67$_-!qC3X&SHSM#8>t9e5M-(hFl>{E)c_9H%?gsX zjGK>3eUsp0W8%onD=01TO)OyGVw!kfVR8er!sLU-N(u}R@c%!A7Rb)aFV71pO3W+H zEXmBzE1taHSY`5ZDFrS@xXR7ECOm9}U2>jDVREDu4{-{YS}7A&*vY6cnZue06F~|? Wtd$6xSk9!NgH$3DEOaIVH2?r0kt)6b delta 1513 zcmaD`)l@OTX!099g^860>I=;aJ;SGQY*o6mN?3r^`knma#L!-|itR^~o^$^y`@5aZ zDZn|uD0T7%4uy#)dl-2phcWJ*9Kd8`kJ^=KX^i)5-pxu* zIQ)(`VbkB)H-GGTpCO&NI-pUbbj#@$RrvCOUX%PV`57L`<79PEkx`(JeWG^1|$-cbN#A$fXJC8Vp zSNOINr|>*KktS{t*i5X6Hj}ma)F&GW5E%+v1dJy;NU2YLD@0@{^a~T|<`NMiLm^+3 zNE1WEHWCvG$>4ObUYw{9aG$IzB@9jn>%|o&ua+<)#)hNBD01JdD^<>D!Uf4Ho+YWd z!0eKlS5R6~%)rUOz{CIr|DiNjNl{{6ab`(oe%|DVI--+(O%*pUk^UyZ#L75X%Tz^6 zTV3Q!TcO%t4mP%X4_3Tt72C6Jm3(zvax?o^Rts*i=Sa9byzI00647Z(jfa diff --git a/stl/RGB@0.1.0.sty b/stl/RGB@0.1.0.sty index eaaaf5ee..a273b86f 100644 --- a/stl/RGB@0.1.0.sty +++ b/stl/RGB@0.1.0.sty @@ -1,5 +1,5 @@ {- - Id: urn:ubideco:stl:9gBiimtSnE1Qxwa49rbdPpv2s5ggXfitb6aktnunpDjX#cosmos-gentle-goblin + Id: urn:ubideco:stl:Cx3Lxt83gdcuXnTxTvSXfxyZUeNi5fqaiibEYr6o7jxy#panic-genius-delphi Name: RGB Version: 0.1.0 Description: Consensus layer for RGB smart contracts @@ -15,6 +15,22 @@ import urn:ubideco:stl:ZtHaBzu9ojbDahaGKEXe5v9DfSDxLERbLkEB23R6Q6V#rhino-cover-f -- MerkleProof := urn:ubideco:semid:4E7NDL8Nm1EXtcenS9idAx1LAXvTu2wRdYsxT8Q2hgRC#carol-alamo-denver -- MerkleNode := urn:ubideco:semid:6kxYeCatpncbA9UiTdsFbxbxJdU56x6MdmTRkEeGAv6R#iceberg-rocket-velvet +import urn:ubideco:stl:5UnRmQChaU5Czu3AQHxDHPzW9JKARBWoMcVYamwoCLPP#siren-stone-soda as BPCore +-- Imports: +-- TapretNodePartner := urn:ubideco:semid:6o6mGBNbDXJCcNgk5ohP6wgXcdXZvYd1ZWy1GMBy5q2#iceberg-poker-active +-- BlindSealTxid := urn:ubideco:semid:q529pAPHhD1aFgueAHy8QtfjUayszR85WgEg7s2a3KE#raymond-reply-phrase +-- TapretProof := urn:ubideco:semid:24LBor5ZVKjGk4uBbEb28w6D7rroV3mpVAntNAWQsYaP#forum-paint-tunnel +-- TapretPathProof := urn:ubideco:semid:2LANtvWZDRes61SHKFxtFPzSuTzaKQGCUvYsNowNf3n3#stage-element-update +-- AnchorMerkleProofTapretProof := urn:ubideco:semid:2RRuXmkGM51gpruJzNCL6mSFm7nQFrP6zXm6CmNr77uE#arcade-modest-fossil +-- TapretRightBranch := urn:ubideco:semid:4nZtVVw7QJaMDHYffkHBWhxXSkLXLcJ89qTLZH4Z3xck#basket-prelude-bridge +-- OpretProof := urn:ubideco:semid:5oMeVsXXeicrdvrCCEZQiKWcrDHy4nQPbaQGhtnwM3ug#segment-ambient-totem +-- ExplicitSeal := urn:ubideco:semid:6xYzjyQ958BPRsJgBXL4N2DhswvgHWLp769Uzo4ybbUV#turtle-phoenix-panda +-- AnchorMerkleProofOpretProof := urn:ubideco:semid:7mkYAw62Vi8BuyQ16qGg3bgFw8WVtTXUkJoYBv1qoNg4#tape-mental-legacy +-- SecretSeal := urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle +-- BlindSealTxPtr := urn:ubideco:semid:9XdJg1BFMpMXPfaiw4Te79W2qYgArsEye6XPJUtj31L8#metro-chris-olympic +-- TxPtr := urn:ubideco:semid:CyRtMpPJkKLX3AdhgY7ZyA7PnYAzCo7yFTeYwwGsUBhn#strange-source-father +-- CloseMethod := urn:ubideco:semid:FA1JhsEFKi2LLpuAjuvLA3qiBuEJrwpKyypB9J2aPicr#july-salmon-contact + import urn:ubideco:stl:5XLKQ1sNryZm9bdFKU2kBY3MPYdZXhchVdQKBbHA3gby#south-strong-welcome as StrictTypes -- Imports: -- Primitive := urn:ubideco:semid:3T3zMmQxuir7TsdjhBLaETJfLH4mr5amAseXDePnzhMT#hobby-cable-puzzle @@ -37,21 +53,6 @@ import urn:ubideco:stl:9KALDYR8Nyjq4FdMW6kYoL7vdkWnqPqNuFnmE9qHpNjZ#justice-rock -- Bool := urn:ubideco:semid:7ZhBHGSJm9ixmm8Z9vCX7i5Ga7j5xrW8t11nsb1Cgpnx#laser-madam-maxwell -- AlphaNumLodash := urn:ubideco:semid:8iBe2dh8beD1KUairdqCacEcxAr4h55XfUQN2PspWXjz#north-sound-salsa -import urn:ubideco:stl:BrKg2wSDRFDFSGzn2RrQkbwFeuLw2yDAFtpQ7WRh3nrz#dance-swim-liter as BPCore --- Imports: --- TapretNodePartner := urn:ubideco:semid:6o6mGBNbDXJCcNgk5ohP6wgXcdXZvYd1ZWy1GMBy5q2#iceberg-poker-active --- BlindSealTxid := urn:ubideco:semid:q529pAPHhD1aFgueAHy8QtfjUayszR85WgEg7s2a3KE#raymond-reply-phrase --- TapretProof := urn:ubideco:semid:24LBor5ZVKjGk4uBbEb28w6D7rroV3mpVAntNAWQsYaP#forum-paint-tunnel --- TapretPathProof := urn:ubideco:semid:2LANtvWZDRes61SHKFxtFPzSuTzaKQGCUvYsNowNf3n3#stage-element-update --- TapretRightBranch := urn:ubideco:semid:4nZtVVw7QJaMDHYffkHBWhxXSkLXLcJ89qTLZH4Z3xck#basket-prelude-bridge --- AnchorMerkleProof := urn:ubideco:semid:5YGZTLPUCHos5Wg8Gm5cLgMPZijqHQLp8CGTMoXTNPf1#coral-vienna-horizon --- ExplicitSeal := urn:ubideco:semid:6xYzjyQ958BPRsJgBXL4N2DhswvgHWLp769Uzo4ybbUV#turtle-phoenix-panda --- SecretSeal := urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle --- BlindSealTxPtr := urn:ubideco:semid:9XdJg1BFMpMXPfaiw4Te79W2qYgArsEye6XPJUtj31L8#metro-chris-olympic --- TxPtr := urn:ubideco:semid:CyRtMpPJkKLX3AdhgY7ZyA7PnYAzCo7yFTeYwwGsUBhn#strange-source-father --- CloseMethod := urn:ubideco:semid:FA1JhsEFKi2LLpuAjuvLA3qiBuEJrwpKyypB9J2aPicr#july-salmon-contact --- Proof := urn:ubideco:semid:FJrSpHdBSVvF5B8YAq3vY15Qh2k3NUXvdkt8ESb1hLW7#hello-america-origin - import urn:ubideco:stl:DVtm25LRKU4TjbyZmVxPhvCmctZ6vKkPKqfpU2QsDNUo#exodus-axiom-tommy as AluVM -- Imports: -- LibSite := urn:ubideco:semid:8Q9NNyK2PCcjZ7U7rDGUJBhk8q37hAnWLgSizGLmr56g#mission-papa-mercy @@ -77,9 +78,13 @@ data AltLayer1 :: liquid:1 data AltLayer1Set :: {AltLayer1 ^ ..0xff} -- urn:ubideco:semid:ByCxcZ2hYTTJ8yoUhpUuHaxceQoAjqsxSF9zJkED3JuM#soviet-arsenal-complex data AluScript :: libs {AluVM.LibId -> ^ ..0xff [Byte]}, entryPoints {[Byte ^ 3] -> AluVM.LibSite {- urn:ubideco:semid:8Q9NNyK2PCcjZ7U7rDGUJBhk8q37hAnWLgSizGLmr56g#mission-papa-mercy -}} --- urn:ubideco:semid:2kbCRxs1hpVo6C9XpXBysmxSyx4HhE6ounqggmvWA1wW#spring-felix-model -data Anchor :: bitcoin BPCore.AnchorMerkleProof {- urn:ubideco:semid:5YGZTLPUCHos5Wg8Gm5cLgMPZijqHQLp8CGTMoXTNPf1#coral-vienna-horizon -} - | liquid BPCore.AnchorMerkleProof {- urn:ubideco:semid:5YGZTLPUCHos5Wg8Gm5cLgMPZijqHQLp8CGTMoXTNPf1#coral-vienna-horizon -} +-- urn:ubideco:semid:5Q2bLHrWvtLLdB1egx6BDDpwnxbNw4wgyBoC7VL9PYmq#king-scorpio-thermos +data Anchor :: bitcoin AnchorSet + | liquid AnchorSet +-- urn:ubideco:semid:4GL38RVDhs4JkL5phqyF6pguwL6Av8qTUWCVNfGeWdUg#history-joel-ivory +data AnchorSet :: tapret:1 BPCore.AnchorMerkleProofTapretProof {- urn:ubideco:semid:2RRuXmkGM51gpruJzNCL6mSFm7nQFrP6zXm6CmNr77uE#arcade-modest-fossil -} + | opret BPCore.AnchorMerkleProofOpretProof {- urn:ubideco:semid:7mkYAw62Vi8BuyQ16qGg3bgFw8WVtTXUkJoYBv1qoNg4#tape-mental-legacy -} + | dual (tapret BPCore.AnchorMerkleProofTapretProof {- urn:ubideco:semid:2RRuXmkGM51gpruJzNCL6mSFm7nQFrP6zXm6CmNr77uE#arcade-modest-fossil -}, opret BPCore.AnchorMerkleProofOpretProof {- urn:ubideco:semid:7mkYAw62Vi8BuyQ16qGg3bgFw8WVtTXUkJoYBv1qoNg4#tape-mental-legacy -}) -- urn:ubideco:semid:3wnGtf3mZhoFh3zXkSppQCDFesMZzj8dxmXCcEf7EgPP#picture-service-index data AnchoredBundle :: anchor Anchor, bundle TransitionBundle -- urn:ubideco:semid:EZoxBpGenvb9UVze1zuwuEHqQJAqw2m3T8za5gbX1JZk#buzzer-pattern-craft @@ -134,8 +139,6 @@ data AssignmentsBlindSealTxid :: {AssignmentType -> ^ ..0xff TypedAssignsBlindSe data AttachId :: [Byte ^ 32] -- urn:ubideco:semid:9zzp5XyDaLvZSGhCEWtey1Y7xdD1soEYdGaimjyZexyf#agenda-ivory-blast data BlindingFactor :: [Byte ^ 32] --- urn:ubideco:semid:8Gjujg1Dk1CV23Pn7CWBdn7vq9rGUS7CZXQmh6MbR4B1#light-manual-neuron -data BundleItem :: inputs {U16 ^ ..0xff}, transition Transition? -- urn:ubideco:semid:HyVyGxhRswAZ3BHJqx6PKmcEGCUSHaL1Rc7qxxi811qE#pizza-natural-cyclone data ConcealedAttach :: [Byte ^ 32] -- urn:ubideco:semid:8YQWVpKJBaYsAwrXvuLDNEDEKRZxfoQJpu1G7X2ZN1tL#mirage-invite-newton @@ -298,8 +301,8 @@ data Transition :: ffv Ffv , inputs Inputs , assignments AssignmentsBlindSealTxPtr , valencies Valencies --- urn:ubideco:semid:6CpmR5xzubTM5JnF7jQxVd4e17s4iJKMXeshqeb2bHGj#malta-heavy-harris -data TransitionBundle :: {OpId -> ^ ..0xff BundleItem} +-- urn:ubideco:semid:LnohmzqG7bkCL7hAjWd2tUrvEP1Pe5C6oiv5r5KjF2G#boxer-nadia-alias +data TransitionBundle :: inputMap {Bitcoin.Vout -> ^ 1.. OpId}, knownTransitions {OpId -> ^ 1.. Transition} -- urn:ubideco:semid:4d7FmcvNey5X175gyggcn4wftcf7mtGyn6N4r6Ek4roX#fiction-caramel-fractal data TransitionSchema :: metadata StrictTypes.SemId {- urn:ubideco:semid:8Ckj2p3GLKina636pSKJkj7GB6ft8XeoP4jfGkRUNwtp#cargo-plasma-catalog -} , globals {GlobalStateType -> ^ ..0xff Occurrences} From 5cc5ef1daf6c92b883eba10eaef1eafaeb3e455e Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 15 Dec 2023 16:50:13 +0100 Subject: [PATCH 14/20] contract: add AnchoredBundle::bundle_id method --- src/contract/anchor.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/contract/anchor.rs b/src/contract/anchor.rs index eff91260..a1984d3e 100644 --- a/src/contract/anchor.rs +++ b/src/contract/anchor.rs @@ -30,7 +30,7 @@ use bp::{dbc, Txid}; use commit_verify::mpc; use strict_encoding::StrictDumb; -use crate::{TransitionBundle, WitnessId, WitnessOrd, LIB_NAME_RGB}; +use crate::{BundleId, TransitionBundle, WitnessId, WitnessOrd, LIB_NAME_RGB}; #[derive(Clone, Eq, PartialEq, Debug)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] @@ -45,6 +45,11 @@ pub struct AnchoredBundle { pub bundle: TransitionBundle, } +impl AnchoredBundle { + #[inline] + pub fn bundle_id(&self) -> BundleId { self.bundle.bundle_id() } +} + #[derive(Clone, Eq, PartialEq, Debug)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB, tags = custom, dumb = Self::Bitcoin(strict_dumb!()))] From 68517bfdfae1b934045d66bf617825e23a954614 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 15 Dec 2023 16:56:04 +0100 Subject: [PATCH 15/20] validation: add lifetimes to bundle iter --- src/validation/consignment.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/validation/consignment.rs b/src/validation/consignment.rs index 919b9768..2e142bce 100644 --- a/src/validation/consignment.rs +++ b/src/validation/consignment.rs @@ -39,7 +39,7 @@ impl<'consignment, C: ConsignmentApi> CheckedConsignment<'consignment, C> { } impl<'consignment, C: ConsignmentApi> ConsignmentApi for CheckedConsignment<'consignment, C> { - type Iter = C::Iter; + type Iter<'placeholder> = C::Iter<'placeholder>; fn schema(&self) -> &SubSchema { self.0.schema() } @@ -53,7 +53,7 @@ impl<'consignment, C: ConsignmentApi> ConsignmentApi for CheckedConsignment<'con fn terminals(&self) -> BTreeSet<(BundleId, SecretSeal)> { self.0.terminals() } - fn bundle_ids(&self) -> Self::Iter { self.0.bundle_ids() } + fn bundle_ids<'a>(&self) -> Self::Iter<'a> { self.0.bundle_ids() } fn anchored_bundle(&self, bundle_id: BundleId) -> Option> { self.0 @@ -70,7 +70,7 @@ impl<'consignment, C: ConsignmentApi> ConsignmentApi for CheckedConsignment<'con /// invalid or absent data, the API must always return [`None`] or empty /// collections/iterators. pub trait ConsignmentApi { - type Iter: Iterator; + type Iter<'a>: Iterator; fn schema(&self) -> &SubSchema; @@ -95,7 +95,7 @@ pub trait ConsignmentApi { /// state transitions represent the final state fn terminals(&self) -> BTreeSet<(BundleId, SecretSeal)>; - fn bundle_ids(&self) -> Self::Iter; + fn bundle_ids<'a>(&self) -> Self::Iter<'a>; fn anchored_bundle(&self, bundle_id: BundleId) -> Option>; } From 4aba65f969cfaa821e5d29297e2b5d596ba22982 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 15 Dec 2023 17:43:19 +0100 Subject: [PATCH 16/20] contract: remove AnchorId type --- Cargo.lock | 12 +++--- src/lib.rs | 1 - src/stl.rs | 2 +- stl/RGB@0.1.0.sta | 100 +++++++++++++++++++++++----------------------- stl/RGB@0.1.0.stl | Bin 15725 -> 15725 bytes stl/RGB@0.1.0.sty | 4 +- 6 files changed, 59 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3c17ef56..df5d0860 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -195,7 +195,7 @@ dependencies = [ [[package]] name = "bp-consensus" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#fbdd7f30d67551af598381885d9005630015921f" +source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#395931907ac8430d2cf6fe9bf8cbeb60b8ff8075" dependencies = [ "amplify", "chrono", @@ -209,7 +209,7 @@ dependencies = [ [[package]] name = "bp-core" version = "0.11.0-beta.1" -source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#fbdd7f30d67551af598381885d9005630015921f" +source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#395931907ac8430d2cf6fe9bf8cbeb60b8ff8075" dependencies = [ "amplify", "bp-consensus", @@ -225,7 +225,7 @@ dependencies = [ [[package]] name = "bp-dbc" version = "0.11.0-beta.1" -source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#fbdd7f30d67551af598381885d9005630015921f" +source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#395931907ac8430d2cf6fe9bf8cbeb60b8ff8075" dependencies = [ "amplify", "base85", @@ -239,7 +239,7 @@ dependencies = [ [[package]] name = "bp-seals" version = "0.11.0-beta.1" -source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#fbdd7f30d67551af598381885d9005630015921f" +source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#395931907ac8430d2cf6fe9bf8cbeb60b8ff8075" dependencies = [ "amplify", "baid58", @@ -924,9 +924,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unsafe-libyaml" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" +checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" [[package]] name = "version_check" diff --git a/src/lib.rs b/src/lib.rs index 75de8a5a..8062fdca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,7 +52,6 @@ pub mod vm; pub mod stl; pub mod prelude { - pub use bp::dbc::AnchorId; pub use contract::*; pub use schema::*; diff --git a/src/stl.rs b/src/stl.rs index 8b681caa..81b7ee5f 100644 --- a/src/stl.rs +++ b/src/stl.rs @@ -32,7 +32,7 @@ use crate::{AnchoredBundle, ContractState, Extension, Genesis, SubSchema, LIB_NA /// Strict types id for the library providing data types for RGB consensus. pub const LIB_ID_RGB: &str = - "urn:ubideco:stl:Cx3Lxt83gdcuXnTxTvSXfxyZUeNi5fqaiibEYr6o7jxy#panic-genius-delphi"; + "urn:ubideco:stl:CjHnUSMpAUVxemMGtj8hJtcCssguGxDghjBzfLrUfiMW#action-ribbon-diana"; fn _rgb_core_stl() -> Result { LibBuilder::new(libname!(LIB_NAME_RGB), tiny_bset! { diff --git a/stl/RGB@0.1.0.sta b/stl/RGB@0.1.0.sta index c6a78326..0301baa1 100644 --- a/stl/RGB@0.1.0.sta +++ b/stl/RGB@0.1.0.sta @@ -1,16 +1,16 @@ -----BEGIN STRICT TYPE LIB----- -Id: urn:ubideco:stl:Cx3Lxt83gdcuXnTxTvSXfxyZUeNi5fqaiibEYr6o7jxy +Id: urn:ubideco:stl:CjHnUSMpAUVxemMGtj8hJtcCssguGxDghjBzfLrUfiMW Name: RGB Dependencies: urn:ubideco:stl:ZtHaBzu9ojbDahaGKEXe5v9DfSDxLERbLkEB23R6Q6V, - urn:ubideco:stl:5UnRmQChaU5Czu3AQHxDHPzW9JKARBWoMcVYamwoCLPP, + urn:ubideco:stl:5Nyd3BhfDAEfc5th2httogefZGzMftfcJNh5Lo9guuTx, urn:ubideco:stl:5XLKQ1sNryZm9bdFKU2kBY3MPYdZXhchVdQKBbHA3gby, urn:ubideco:stl:9KALDYR8Nyjq4FdMW6kYoL7vdkWnqPqNuFnmE9qHpNjZ, urn:ubideco:stl:DVtm25LRKU4TjbyZmVxPhvCmctZ6vKkPKqfpU2QsDNUo, urn:ubideco:stl:HX2UBak8vPsTokug1DGMDvTpzns3xUdwZ7QJdyt4qBA9 A1JHQgYIbJMpP1Zo7NnfnUB1CNehMyMWREFWAosurAm/5d+NQgxDb21taXRWZXJp -ZnlCjMtEeyGdx5AIzOGvOAmwvi904o9vc5/6SC7v5n+XlgZCUENvcmVDNAOU2Bsw +ZnlBD/d2QIYSU77AjC52Xwu6XWojk0H9GfxAPSJfiKVy+wZCUENvcmVDNAOU2Bsw 4lIokCYe82/5+Kg5UZH1C2leIyoes7dByAtTdHJpY3RUeXBlc3uEgDye+uIRJad8 LDm8cNL96PlDrg39nPTmgu3HZspwA1N0ZLmzB6Bap1ZJhkNCbroWCz+PjGj56E/9 zS2FQAp57Q9gBUFsdVZN9WwTYiP2OadKCZPcR0bJ+YqruINYXbXZFj8YfsQoGgoH @@ -62,92 +62,92 @@ p1ZJhkNCbroWCz+PjGj56E/9zS2FQAp57Q9gbe+hJuG8deH/SEv7hcE00Qwy3Iwe QOBpSxzWp+vc3GEAAAAAAAAAAP//AAAAAAAABkFuY2hvcgQCAAdiaXRjb2luAAUB ATCATDqJMQfb2Cuaoz6mNsSk6w0JQpRii8v0B9e0v42NAQZsaXF1aWQABQEBMIBM OokxB9vYK5qjPqY2xKTrDQlClGKLy/QH17S/jY0JQW5jaG9yU2V0BAMBBnRhcHJl -dAAFAQJCjMtEeyGdx5AIzOGvOAmwvi904o9vc5/6SC7v5n+XlhUdyY0BIXjnjvH0 -xVzri+jmMzSDRFniwqNc7i7tvTFNAgVvcHJldAAFAQJCjMtEeyGdx5AIzOGvOAmw -vi904o9vc5/6SC7v5n+XlmSc57Qkj6v2AGTDqFknAKSA/5t+BvfUzoxBZGDxF449 -AwRkdWFsAAYCBnRhcHJldAJCjMtEeyGdx5AIzOGvOAmwvi904o9vc5/6SC7v5n+X -lhUdyY0BIXjnjvH0xVzri+jmMzSDRFniwqNc7i7tvTFNBW9wcmV0AkKMy0R7IZ3H -kAjM4a84CbC+L3Tij29zn/pILu/mf5eWZJzntCSPq/YAZMOoWScApID/m34G99TO +dAAFAQJBD/d2QIYSU77AjC52Xwu6XWojk0H9GfxAPSJfiKVy+xUdyY0BIXjnjvH0 +xVzri+jmMzSDRFniwqNc7i7tvTFNAgVvcHJldAAFAQJBD/d2QIYSU77AjC52Xwu6 +XWojk0H9GfxAPSJfiKVy+2Sc57Qkj6v2AGTDqFknAKSA/5t+BvfUzoxBZGDxF449 +AwRkdWFsAAYCBnRhcHJldAJBD/d2QIYSU77AjC52Xwu6XWojk0H9GfxAPSJfiKVy ++xUdyY0BIXjnjvH0xVzri+jmMzSDRFniwqNc7i7tvTFNBW9wcmV0AkEP93ZAhhJT +vsCMLnZfC7pdaiOTQf0Z/EA9Il+IpXL7ZJzntCSPq/YAZMOoWScApID/m34G99TO jEFkYPEXjj0OQW5jaG9yZWRCdW5kbGUGAgZhbmNob3IBAPuWr//RTDiPJjNA6aCv mWzkE3240iW/UCHWIiLtb7gGYnVuZGxlAdWvfrlv3zlwTsSxGUiB+mc6KXHAwEFi 16JO9AqmkJ/3CEFzc2V0VGFnBQEABwAAQCAAIkFzc2lnblJldmVhbGVkQXR0YWNo -QmxpbmRTZWFsVHhQdHIEBAAMY29uZmlkZW50aWFsAAYCBHNlYWwCQozLRHshnceQ -CMzhrzgJsL4vdOKPb3Of+kgu7+Z/l5ZoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcD +QmxpbmRTZWFsVHhQdHIEBAAMY29uZmlkZW50aWFsAAYCBHNlYWwCQQ/3dkCGElO+ +wIwudl8Lul1qI5NB/Rn8QD0iX4ilcvtoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcD YKmUs70GTgVzdGF0ZQH8NEXdX88NC/+sFaR6ugUi4FuLKxswZVKHg497LeuOPQER Y29uZmlkZW50aWFsU3RhdGUABgIEc2VhbAEkvLMZiKNHP+ioRwLJfnX8PwOt6aqt h5QAMFBriWteygVzdGF0ZQH8NEXdX88NC/+sFaR6ugUi4FuLKxswZVKHg497LeuO -PQIQY29uZmlkZW50aWFsU2VhbAAGAgRzZWFsAkKMy0R7IZ3HkAjM4a84CbC+L3Ti -j29zn/pILu/mf5eWaBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2CplLO9Bk4Fc3Rh +PQIQY29uZmlkZW50aWFsU2VhbAAGAgRzZWFsAkEP93ZAhhJTvsCMLnZfC7pdaiOT +Qf0Z/EA9Il+IpXL7aBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2CplLO9Bk4Fc3Rh dGUBaFM0IAzcU8ZNduK2LqpmL9o62ZG/BqspDrzQ9noDSswDCHJldmVhbGVkAAYC BHNlYWwBJLyzGYijRz/oqEcCyX51/D8DremqrYeUADBQa4lrXsoFc3RhdGUBaFM0 IAzcU8ZNduK2LqpmL9o62ZG/BqspDrzQ9noDSswhQXNzaWduUmV2ZWFsZWRBdHRh -Y2hCbGluZFNlYWxUeGlkBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkKMy0R7IZ3H -kAjM4a84CbC+L3Tij29zn/pILu/mf5eWaBnrvNWzGKuXs5ilSzZl3dqnBm/o6STn +Y2hCbGluZFNlYWxUeGlkBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkEP93ZAhhJT +vsCMLnZfC7pdaiOTQf0Z/EA9Il+IpXL7aBnrvNWzGKuXs5ilSzZl3dqnBm/o6STn A2CplLO9Bk4Fc3RhdGUB/DRF3V/PDQv/rBWkeroFIuBbiysbMGVSh4OPey3rjj0B EWNvbmZpZGVudGlhbFN0YXRlAAYCBHNlYWwBwOIKL9PIMRrWh/qX6o7p2yCTIW+c vaNcJzqoLjPGHM8Fc3RhdGUB/DRF3V/PDQv/rBWkeroFIuBbiysbMGVSh4OPey3r -jj0CEGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAJCjMtEeyGdx5AIzOGvOAmwvi90 -4o9vc5/6SC7v5n+XlmgZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0 +jj0CEGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAJBD/d2QIYSU77AjC52Xwu6XWoj +k0H9GfxAPSJfiKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0 YXRlAWhTNCAM3FPGTXbiti6qZi/aOtmRvwarKQ680PZ6A0rMAwhyZXZlYWxlZAAG AgRzZWFsAcDiCi/TyDEa1of6l+qO6dsgkyFvnL2jXCc6qC4zxhzPBXN0YXRlAWhT NCAM3FPGTXbiti6qZi/aOtmRvwarKQ680PZ6A0rMIEFzc2lnblJldmVhbGVkRGF0 -YUJsaW5kU2VhbFR4UHRyBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkKMy0R7IZ3H -kAjM4a84CbC+L3Tij29zn/pILu/mf5eWaBnrvNWzGKuXs5ilSzZl3dqnBm/o6STn +YUJsaW5kU2VhbFR4UHRyBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkEP93ZAhhJT +vsCMLnZfC7pdaiOTQf0Z/EA9Il+IpXL7aBnrvNWzGKuXs5ilSzZl3dqnBm/o6STn A2CplLO9Bk4Fc3RhdGUBcA1lELKCgW8ftz3lEmOTA1cIpVJhrQLg1L5axc+n6gkB EWNvbmZpZGVudGlhbFN0YXRlAAYCBHNlYWwBJLyzGYijRz/oqEcCyX51/D8Dremq rYeUADBQa4lrXsoFc3RhdGUBcA1lELKCgW8ftz3lEmOTA1cIpVJhrQLg1L5axc+n -6gkCEGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAJCjMtEeyGdx5AIzOGvOAmwvi90 -4o9vc5/6SC7v5n+XlmgZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0 +6gkCEGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAJBD/d2QIYSU77AjC52Xwu6XWoj +k0H9GfxAPSJfiKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0 YXRlASDyUFYij2bO/JH66me8X8bwaVw9R3lzWT+s0TfKckhlAwhyZXZlYWxlZAAG AgRzZWFsASS8sxmIo0c/6KhHAsl+dfw/A63pqq2HlAAwUGuJa17KBXN0YXRlASDy UFYij2bO/JH66me8X8bwaVw9R3lzWT+s0TfKckhlH0Fzc2lnblJldmVhbGVkRGF0 -YUJsaW5kU2VhbFR4aWQEBAAMY29uZmlkZW50aWFsAAYCBHNlYWwCQozLRHshnceQ -CMzhrzgJsL4vdOKPb3Of+kgu7+Z/l5ZoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcD +YUJsaW5kU2VhbFR4aWQEBAAMY29uZmlkZW50aWFsAAYCBHNlYWwCQQ/3dkCGElO+ +wIwudl8Lul1qI5NB/Rn8QD0iX4ilcvtoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcD YKmUs70GTgVzdGF0ZQFwDWUQsoKBbx+3PeUSY5MDVwilUmGtAuDUvlrFz6fqCQER Y29uZmlkZW50aWFsU3RhdGUABgIEc2VhbAHA4gov08gxGtaH+pfqjunbIJMhb5y9 o1wnOqguM8YczwVzdGF0ZQFwDWUQsoKBbx+3PeUSY5MDVwilUmGtAuDUvlrFz6fq -CQIQY29uZmlkZW50aWFsU2VhbAAGAgRzZWFsAkKMy0R7IZ3HkAjM4a84CbC+L3Ti -j29zn/pILu/mf5eWaBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2CplLO9Bk4Fc3Rh +CQIQY29uZmlkZW50aWFsU2VhbAAGAgRzZWFsAkEP93ZAhhJTvsCMLnZfC7pdaiOT +Qf0Z/EA9Il+IpXL7aBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2CplLO9Bk4Fc3Rh dGUBIPJQViKPZs78kfrqZ7xfxvBpXD1HeXNZP6zRN8pySGUDCHJldmVhbGVkAAYC BHNlYWwBwOIKL9PIMRrWh/qX6o7p2yCTIW+cvaNcJzqoLjPGHM8Fc3RhdGUBIPJQ ViKPZs78kfrqZ7xfxvBpXD1HeXNZP6zRN8pySGUhQXNzaWduUmV2ZWFsZWRWYWx1 -ZUJsaW5kU2VhbFR4UHRyBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkKMy0R7IZ3H -kAjM4a84CbC+L3Tij29zn/pILu/mf5eWaBnrvNWzGKuXs5ilSzZl3dqnBm/o6STn +ZUJsaW5kU2VhbFR4UHRyBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkEP93ZAhhJT +vsCMLnZfC7pdaiOTQf0Z/EA9Il+IpXL7aBnrvNWzGKuXs5ilSzZl3dqnBm/o6STn A2CplLO9Bk4Fc3RhdGUBwlGtZel1DayaElnMwIUkXNX3sW9S2HI2RizxdYyfTl0B EWNvbmZpZGVudGlhbFN0YXRlAAYCBHNlYWwBJLyzGYijRz/oqEcCyX51/D8Dremq rYeUADBQa4lrXsoFc3RhdGUBwlGtZel1DayaElnMwIUkXNX3sW9S2HI2RizxdYyf -Tl0CEGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAJCjMtEeyGdx5AIzOGvOAmwvi90 -4o9vc5/6SC7v5n+XlmgZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0 +Tl0CEGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAJBD/d2QIYSU77AjC52Xwu6XWoj +k0H9GfxAPSJfiKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0 YXRlAW1ExvQvagU6QxUPpubCLMToeuUslPgG1Ayl3If2dY2gAwhyZXZlYWxlZAAG AgRzZWFsASS8sxmIo0c/6KhHAsl+dfw/A63pqq2HlAAwUGuJa17KBXN0YXRlAW1E xvQvagU6QxUPpubCLMToeuUslPgG1Ayl3If2dY2gIEFzc2lnblJldmVhbGVkVmFs -dWVCbGluZFNlYWxUeGlkBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkKMy0R7IZ3H -kAjM4a84CbC+L3Tij29zn/pILu/mf5eWaBnrvNWzGKuXs5ilSzZl3dqnBm/o6STn +dWVCbGluZFNlYWxUeGlkBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkEP93ZAhhJT +vsCMLnZfC7pdaiOTQf0Z/EA9Il+IpXL7aBnrvNWzGKuXs5ilSzZl3dqnBm/o6STn A2CplLO9Bk4Fc3RhdGUBwlGtZel1DayaElnMwIUkXNX3sW9S2HI2RizxdYyfTl0B EWNvbmZpZGVudGlhbFN0YXRlAAYCBHNlYWwBwOIKL9PIMRrWh/qX6o7p2yCTIW+c vaNcJzqoLjPGHM8Fc3RhdGUBwlGtZel1DayaElnMwIUkXNX3sW9S2HI2RizxdYyf -Tl0CEGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAJCjMtEeyGdx5AIzOGvOAmwvi90 -4o9vc5/6SC7v5n+XlmgZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0 +Tl0CEGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAJBD/d2QIYSU77AjC52Xwu6XWoj +k0H9GfxAPSJfiKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0 YXRlAW1ExvQvagU6QxUPpubCLMToeuUslPgG1Ayl3If2dY2gAwhyZXZlYWxlZAAG AgRzZWFsAcDiCi/TyDEa1of6l+qO6dsgkyFvnL2jXCc6qC4zxhzPBXN0YXRlAW1E xvQvagU6QxUPpubCLMToeuUslPgG1Ayl3If2dY2gHUFzc2lnblZvaWRTdGF0ZUJs -aW5kU2VhbFR4UHRyBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkKMy0R7IZ3HkAjM -4a84CbC+L3Tij29zn/pILu/mf5eWaBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2Cp +aW5kU2VhbFR4UHRyBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkEP93ZAhhJTvsCM +LnZfC7pdaiOTQf0Z/EA9Il+IpXL7aBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2Cp lLO9Bk4Fc3RhdGUBLrKl/hfAMEQwmOBcmxtabNYe7XYNYd7LgCbTZPMW4bsBEWNv bmZpZGVudGlhbFN0YXRlAAYCBHNlYWwBJLyzGYijRz/oqEcCyX51/D8DremqrYeU ADBQa4lrXsoFc3RhdGUBLrKl/hfAMEQwmOBcmxtabNYe7XYNYd7LgCbTZPMW4bsC -EGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAJCjMtEeyGdx5AIzOGvOAmwvi904o9v -c5/6SC7v5n+XlmgZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0YXRl +EGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAJBD/d2QIYSU77AjC52Xwu6XWojk0H9 +GfxAPSJfiKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0YXRl AS6ypf4XwDBEMJjgXJsbWmzWHu12DWHey4Am02TzFuG7AwhyZXZlYWxlZAAGAgRz ZWFsASS8sxmIo0c/6KhHAsl+dfw/A63pqq2HlAAwUGuJa17KBXN0YXRlAS6ypf4X wDBEMJjgXJsbWmzWHu12DWHey4Am02TzFuG7HEFzc2lnblZvaWRTdGF0ZUJsaW5k -U2VhbFR4aWQEBAAMY29uZmlkZW50aWFsAAYCBHNlYWwCQozLRHshnceQCMzhrzgJ -sL4vdOKPb3Of+kgu7+Z/l5ZoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcDYKmUs70G +U2VhbFR4aWQEBAAMY29uZmlkZW50aWFsAAYCBHNlYWwCQQ/3dkCGElO+wIwudl8L +ul1qI5NB/Rn8QD0iX4ilcvtoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcDYKmUs70G TgVzdGF0ZQEusqX+F8AwRDCY4FybG1ps1h7tdg1h3suAJtNk8xbhuwERY29uZmlk ZW50aWFsU3RhdGUABgIEc2VhbAHA4gov08gxGtaH+pfqjunbIJMhb5y9o1wnOqgu M8YczwVzdGF0ZQEusqX+F8AwRDCY4FybG1ps1h7tdg1h3suAJtNk8xbhuwIQY29u -ZmlkZW50aWFsU2VhbAAGAgRzZWFsAkKMy0R7IZ3HkAjM4a84CbC+L3Tij29zn/pI -Lu/mf5eWaBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2CplLO9Bk4Fc3RhdGUBLrKl +ZmlkZW50aWFsU2VhbAAGAgRzZWFsAkEP93ZAhhJTvsCMLnZfC7pdaiOTQf0Z/EA9 +Il+IpXL7aBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2CplLO9Bk4Fc3RhdGUBLrKl /hfAMEQwmOBcmxtabNYe7XYNYd7LgCbTZPMW4bsDCHJldmVhbGVkAAYCBHNlYWwB wOIKL9PIMRrWh/qX6o7p2yCTIW+cvaNcJzqoLjPGHM8Fc3RhdGUBLrKl/hfAMEQw mOBcmxtabNYe7XYNYd7LgCbTZPMW4bsOQXNzaWdubWVudFR5cGUFAQAAAhlBc3Np @@ -326,16 +326,16 @@ ZAAFAQL1bBNiI/Y5p0oJk9xHRsn5iqu4g1hdtdkWPxh+xCgaCqOCQvPL19HQoRLa jeFgL1bU+G8OxMR2xcBoWUxLBGVWCldpdG5lc3NPcmQEAgAHb25DaGFpbgAFAQEO v7jtOGJupIO6NPMU+VR16VbZRzUT3CcYFjxjtuGJsQEIb2ZmQ2hhaW4AAAAKV2l0 bmVzc1BvcwYCBmhlaWdodAAABAl0aW1lc3RhbXAAAEgUWGNoYWluQmxpbmRTZWFs -VHhQdHIEAgAHYml0Y29pbgAFAQJCjMtEeyGdx5AIzOGvOAmwvi904o9vc5/6SC7v -5n+Xln61+DN8kapvuL2Vu7vL9eHFJYlF0hF22h7U3IzFMSVlAQZsaXF1aWQABQEC -QozLRHshnceQCMzhrzgJsL4vdOKPb3Of+kgu7+Z/l5Z+tfgzfJGqb7i9lbu7y/Xh +VHhQdHIEAgAHYml0Y29pbgAFAQJBD/d2QIYSU77AjC52Xwu6XWojk0H9GfxAPSJf +iKVy+361+DN8kapvuL2Vu7vL9eHFJYlF0hF22h7U3IzFMSVlAQZsaXF1aWQABQEC +QQ/3dkCGElO+wIwudl8Lul1qI5NB/Rn8QD0iX4ilcvt+tfgzfJGqb7i9lbu7y/Xh xSWJRdIRdtoe1NyMxTElZRNYY2hhaW5CbGluZFNlYWxUeGlkBAIAB2JpdGNvaW4A -BQECQozLRHshnceQCMzhrzgJsL4vdOKPb3Of+kgu7+Z/l5YMUGySSZxY8y8u7CxU -uNcWa7yYFOw+mQJFPXEuumEJ4QEGbGlxdWlkAAUBAkKMy0R7IZ3HkAjM4a84CbC+ -L3Tij29zn/pILu/mf5eWDFBskkmcWPMvLuwsVLjXFmu8mBTsPpkCRT1xLrphCeES -WGNoYWluRXhwbGljaXRTZWFsBAIAB2JpdGNvaW4ABQECQozLRHshnceQCMzhrzgJ -sL4vdOKPb3Of+kgu7+Z/l5ZYhY59BZJX7QUNSLVm+wjlig7dqrMOFqHvGK5jmQRP -sgEGbGlxdWlkAAUBAkKMy0R7IZ3HkAjM4a84CbC+L3Tij29zn/pILu/mf5eWWIWO +BQECQQ/3dkCGElO+wIwudl8Lul1qI5NB/Rn8QD0iX4ilcvsMUGySSZxY8y8u7CxU +uNcWa7yYFOw+mQJFPXEuumEJ4QEGbGlxdWlkAAUBAkEP93ZAhhJTvsCMLnZfC7pd +aiOTQf0Z/EA9Il+IpXL7DFBskkmcWPMvLuwsVLjXFmu8mBTsPpkCRT1xLrphCeES +WGNoYWluRXhwbGljaXRTZWFsBAIAB2JpdGNvaW4ABQECQQ/3dkCGElO+wIwudl8L +ul1qI5NB/Rn8QD0iX4ilcvtYhY59BZJX7QUNSLVm+wjlig7dqrMOFqHvGK5jmQRP +sgEGbGlxdWlkAAUBAkEP93ZAhhJTvsCMLnZfC7pdaiOTQf0Z/EA9Il+IpXL7WIWO fQWSV+0FDUi1ZvsI5YoO3aqzDhah7xiuY5kET7I= -----END STRICT TYPE LIB----- diff --git a/stl/RGB@0.1.0.stl b/stl/RGB@0.1.0.stl index d2a0e46169ef37298c80f9cc68dc40de940ab877..3d9ed9cf817f726404a6de0575b40e50d1a75ad7 100644 GIT binary patch delta 1168 zcmaD`^|oq)k-8)Q_cDhzq2PT7di2WTxp&28DNlC%EBVL4Rw=$?Y0+;srvT^tqSVQ6 z^b|H$)^I8iQa^bkmx4AEYkomdY6$}?BNIXO30#K67^+TKQ3993WC`x|gcUMMs!x8; zYcrW!Kz(u$pEYqBuJ9R8_To{W?8_TXoQC(j^N3S;g>MUS3eWQsY2p@v&BU5$Gg+HY zeX@}Nk)g0fz<9EQl=|eiLPUl_zc7(*E)gLz6!JxhG%-YMBQc?n3{D5@#fb_5_sP0a z!r*kUUR+`FY6&x9Y&c4cBKOU@QsIh(RXQ>&=rFS7WEPfYrVy3EfEp$X*eDY=dA<#a RCeOEVB5W$7tqw7Y6aY>)*Czk~ delta 1168 zcmaD`^|oq)k-AgQX_sonxyL7PoO!t3f^)+@{gOxh`Ni{pdFZ`=RzH0jn^S;ueo^Y= zH+l*iD{D9v2&tdEkxN0Fi8a5VD7A!vm63^{`UEaRVhmL$tSEs?VX_4Gdcq1BCDkXt z=e3#4EucO*h|iih4OjS#CwuXzPxj@FCQie9-g(3+yu!DIIECl=i8OJGz-D4iw3)2U zr#{(8fXGnTB49k(K}vn{TOlGtp^o! SM3d*+I1x6L(N>2TMG645MkPW3 diff --git a/stl/RGB@0.1.0.sty b/stl/RGB@0.1.0.sty index a273b86f..69f3a43b 100644 --- a/stl/RGB@0.1.0.sty +++ b/stl/RGB@0.1.0.sty @@ -1,5 +1,5 @@ {- - Id: urn:ubideco:stl:Cx3Lxt83gdcuXnTxTvSXfxyZUeNi5fqaiibEYr6o7jxy#panic-genius-delphi + Id: urn:ubideco:stl:CjHnUSMpAUVxemMGtj8hJtcCssguGxDghjBzfLrUfiMW#action-ribbon-diana Name: RGB Version: 0.1.0 Description: Consensus layer for RGB smart contracts @@ -15,7 +15,7 @@ import urn:ubideco:stl:ZtHaBzu9ojbDahaGKEXe5v9DfSDxLERbLkEB23R6Q6V#rhino-cover-f -- MerkleProof := urn:ubideco:semid:4E7NDL8Nm1EXtcenS9idAx1LAXvTu2wRdYsxT8Q2hgRC#carol-alamo-denver -- MerkleNode := urn:ubideco:semid:6kxYeCatpncbA9UiTdsFbxbxJdU56x6MdmTRkEeGAv6R#iceberg-rocket-velvet -import urn:ubideco:stl:5UnRmQChaU5Czu3AQHxDHPzW9JKARBWoMcVYamwoCLPP#siren-stone-soda as BPCore +import urn:ubideco:stl:5Nyd3BhfDAEfc5th2httogefZGzMftfcJNh5Lo9guuTx#father-soviet-mercy as BPCore -- Imports: -- TapretNodePartner := urn:ubideco:semid:6o6mGBNbDXJCcNgk5ohP6wgXcdXZvYd1ZWy1GMBy5q2#iceberg-poker-active -- BlindSealTxid := urn:ubideco:semid:q529pAPHhD1aFgueAHy8QtfjUayszR85WgEg7s2a3KE#raymond-reply-phrase From 5805b2871403af54eae9cfed271679fea6e681ff Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 15 Dec 2023 19:51:56 +0100 Subject: [PATCH 17/20] contract: add more AnchorSet methods --- Cargo.lock | 8 +-- src/contract/anchor.rs | 129 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 119 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index df5d0860..928a2429 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -195,7 +195,7 @@ dependencies = [ [[package]] name = "bp-consensus" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#395931907ac8430d2cf6fe9bf8cbeb60b8ff8075" +source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#2d3806295804fc3d50b4259f1d5da41f8cf88356" dependencies = [ "amplify", "chrono", @@ -209,7 +209,7 @@ dependencies = [ [[package]] name = "bp-core" version = "0.11.0-beta.1" -source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#395931907ac8430d2cf6fe9bf8cbeb60b8ff8075" +source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#2d3806295804fc3d50b4259f1d5da41f8cf88356" dependencies = [ "amplify", "bp-consensus", @@ -225,7 +225,7 @@ dependencies = [ [[package]] name = "bp-dbc" version = "0.11.0-beta.1" -source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#395931907ac8430d2cf6fe9bf8cbeb60b8ff8075" +source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#2d3806295804fc3d50b4259f1d5da41f8cf88356" dependencies = [ "amplify", "base85", @@ -239,7 +239,7 @@ dependencies = [ [[package]] name = "bp-seals" version = "0.11.0-beta.1" -source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#395931907ac8430d2cf6fe9bf8cbeb60b8ff8075" +source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#2d3806295804fc3d50b4259f1d5da41f8cf88356" dependencies = [ "amplify", "baid58", diff --git a/src/contract/anchor.rs b/src/contract/anchor.rs index a1984d3e..d09b350e 100644 --- a/src/contract/anchor.rs +++ b/src/contract/anchor.rs @@ -21,7 +21,6 @@ // limitations under the License. use std::cmp::Ordering; -use std::ops::Deref; use bp::dbc::anchor::MergeError; use bp::dbc::opret::OpretProof; @@ -30,7 +29,7 @@ use bp::{dbc, Txid}; use commit_verify::mpc; use strict_encoding::StrictDumb; -use crate::{BundleId, TransitionBundle, WitnessId, WitnessOrd, LIB_NAME_RGB}; +use crate::{BundleId, ContractId, TransitionBundle, WitnessId, WitnessOrd, LIB_NAME_RGB}; #[derive(Clone, Eq, PartialEq, Debug)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] @@ -66,17 +65,6 @@ pub enum Anchor { Liquid(AnchorSet

), } -impl Deref for Anchor

{ - type Target = AnchorSet

; - - #[inline] - fn deref(&self) -> &Self::Target { - match self { - Anchor::Bitcoin(anchor) | Anchor::Liquid(anchor) => anchor, - } - } -} - impl Anchor

{ #[inline] pub fn layer1(&self) -> Layer1 { @@ -94,7 +82,7 @@ impl Anchor

{ } } - pub fn map( + fn map( self, f: impl FnOnce(AnchorSet

) -> Result, E>, ) -> Result, E> { @@ -106,6 +94,26 @@ impl Anchor

{ } impl Anchor { + pub fn known_bundle_ids(&self) -> impl Iterator + '_ { + match self { + Anchor::Bitcoin(anchor) | Anchor::Liquid(anchor) => anchor.known_bundle_ids(), + } + } + + pub fn to_merkle_proof( + &self, + contract_id: ContractId, + ) -> Result, mpc::LeafNotKnown> { + self.clone().into_merkle_proof(contract_id) + } + + pub fn into_merkle_proof( + self, + contract_id: ContractId, + ) -> Result, mpc::LeafNotKnown> { + self.map(|a| a.into_merkle_proof(contract_id)) + } + pub fn merge_reveal(self, other: Self) -> Result { match (self, other) { (Anchor::Bitcoin(anchor), Anchor::Bitcoin(other)) => { @@ -119,6 +127,24 @@ impl Anchor { } } +impl Anchor { + pub fn to_merkle_block( + &self, + contract_id: ContractId, + bundle_id: BundleId, + ) -> Result, mpc::InvalidProof> { + self.clone().into_merkle_block(contract_id, bundle_id) + } + + pub fn into_merkle_block( + self, + contract_id: ContractId, + bundle_id: BundleId, + ) -> Result, mpc::InvalidProof> { + self.map(|a| a.into_merkle_block(contract_id, bundle_id)) + } +} + #[derive(Clone, Eq, PartialEq, Debug)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB, tags = custom, dumb = Self::Tapret(strict_dumb!()))] @@ -157,6 +183,18 @@ impl AnchorSet

{ } } + pub fn from_split( + tapret: Option>, + opret: Option>, + ) -> Option { + Some(match (tapret, opret) { + (Some(tapret), Some(opret)) => Self::Dual { tapret, opret }, + (Some(tapret), None) => Self::Tapret(tapret), + (None, Some(opret)) => Self::Opret(opret), + (None, None) => return None, + }) + } + #[allow(clippy::type_complexity)] pub fn as_split( &self, @@ -178,9 +216,72 @@ impl AnchorSet

{ AnchorSet::Dual { tapret, opret } => (Some(tapret), Some(opret)), } } + + pub fn mpc_proofs(&self) -> impl Iterator { + let (t, o) = self.as_split(); + t.map(|a| &a.mpc_proof) + .into_iter() + .chain(o.map(|a| &a.mpc_proof)) + } +} + +impl AnchorSet { + pub fn to_merkle_block( + &self, + contract_id: ContractId, + bundle_id: BundleId, + ) -> Result, mpc::InvalidProof> { + self.clone().into_merkle_block(contract_id, bundle_id) + } + + pub fn into_merkle_block( + self, + contract_id: ContractId, + bundle_id: BundleId, + ) -> Result, mpc::InvalidProof> { + let (tapret, opret) = self.into_split(); + let tapret = tapret + .map(|t| t.into_merkle_block(contract_id, bundle_id)) + .transpose()?; + let opret = opret + .map(|o| o.into_merkle_block(contract_id, bundle_id)) + .transpose()?; + Ok(AnchorSet::from_split(tapret, opret).expect("one must be non-None")) + } } impl AnchorSet { + pub fn known_bundle_ids(&self) -> impl Iterator + '_ { + self.mpc_proofs() + .map(|p| { + p.to_known_message_map() + .into_iter() + .map(|(p, m)| (m.into(), p.into())) + }) + .flatten() + } + + pub fn to_merkle_proof( + &self, + contract_id: ContractId, + ) -> Result, mpc::LeafNotKnown> { + self.clone().into_merkle_proof(contract_id) + } + + pub fn into_merkle_proof( + self, + contract_id: ContractId, + ) -> Result, mpc::LeafNotKnown> { + let (tapret, opret) = self.into_split(); + let tapret = tapret + .map(|t| t.into_merkle_proof(contract_id)) + .transpose()?; + let opret = opret + .map(|o| o.into_merkle_proof(contract_id)) + .transpose()?; + Ok(AnchorSet::from_split(tapret, opret).expect("one must be non-None")) + } + pub fn merge_reveal(self, other: Self) -> Result { let (tapret1, opret1) = self.into_split(); let (tapret2, opret2) = other.into_split(); From 00fdba729ee484584d2f9a1ca54ea5c374dfb933 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 15 Dec 2023 20:20:30 +0100 Subject: [PATCH 18/20] contract: move MergeReveal implementations to standard library --- src/contract/anchor.rs | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/src/contract/anchor.rs b/src/contract/anchor.rs index d09b350e..2b883fe1 100644 --- a/src/contract/anchor.rs +++ b/src/contract/anchor.rs @@ -22,7 +22,6 @@ use std::cmp::Ordering; -use bp::dbc::anchor::MergeError; use bp::dbc::opret::OpretProof; use bp::dbc::tapret::TapretProof; use bp::{dbc, Txid}; @@ -113,18 +112,6 @@ impl Anchor { ) -> Result, mpc::LeafNotKnown> { self.map(|a| a.into_merkle_proof(contract_id)) } - - pub fn merge_reveal(self, other: Self) -> Result { - match (self, other) { - (Anchor::Bitcoin(anchor), Anchor::Bitcoin(other)) => { - anchor.merge_reveal(other).map(Anchor::Bitcoin) - } - (Anchor::Liquid(anchor), Anchor::Liquid(other)) => { - anchor.merge_reveal(other).map(Anchor::Liquid) - } - _ => Err(MergeError::TxidMismatch), - } - } } impl Anchor { @@ -281,28 +268,6 @@ impl AnchorSet { .transpose()?; Ok(AnchorSet::from_split(tapret, opret).expect("one must be non-None")) } - - pub fn merge_reveal(self, other: Self) -> Result { - let (tapret1, opret1) = self.into_split(); - let (tapret2, opret2) = other.into_split(); - - let tapret = match (tapret1, tapret2) { - (Some(tr), None) | (None, Some(tr)) => Some(tr), - (Some(tapret1), Some(tapret2)) => Some(tapret1.merge_reveal(tapret2)?), - (None, None) => None, - }; - let opret = match (opret1, opret2) { - (Some(or), None) | (None, Some(or)) => Some(or), - (Some(opret1), Some(opret2)) => Some(opret1.merge_reveal(opret2)?), - (None, None) => None, - }; - Ok(match (tapret, opret) { - (Some(tapret), None) => Self::Tapret(tapret), - (None, Some(opret)) => Self::Opret(opret), - (Some(tapret), Some(opret)) => Self::Dual { tapret, opret }, - _ => unreachable!(), - }) - } } /// Txid and height information ordered according to the RGB consensus rules. From 234a16f7ada64aa942ec83d6cbc2fb4db74329e8 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 15 Dec 2023 20:27:41 +0100 Subject: [PATCH 19/20] contract: rename XSeal and XAnchor types --- src/contract/anchor.rs | 69 +++-- src/contract/assignments.rs | 20 +- src/contract/contract.rs | 6 +- src/contract/mod.rs | 4 +- src/contract/seal.rs | 71 +++-- src/stl.rs | 2 +- src/validation/status.rs | 4 +- src/validation/validator.rs | 11 +- stl/RGB@0.1.0.sta | 544 ++++++++++++++++++------------------ stl/RGB@0.1.0.stl | Bin 15725 -> 15723 bytes stl/RGB@0.1.0.sty | 88 +++--- 11 files changed, 406 insertions(+), 413 deletions(-) diff --git a/src/contract/anchor.rs b/src/contract/anchor.rs index 2b883fe1..70c89b56 100644 --- a/src/contract/anchor.rs +++ b/src/contract/anchor.rs @@ -24,7 +24,8 @@ use std::cmp::Ordering; use bp::dbc::opret::OpretProof; use bp::dbc::tapret::TapretProof; -use bp::{dbc, Txid}; +use bp::dbc::Anchor; +use bp::Txid; use commit_verify::mpc; use strict_encoding::StrictDumb; @@ -39,7 +40,7 @@ use crate::{BundleId, ContractId, TransitionBundle, WitnessId, WitnessOrd, LIB_N serde(crate = "serde_crate", rename_all = "camelCase") )] pub struct AnchoredBundle { - pub anchor: Anchor, + pub anchor: XAnchor, pub bundle: TransitionBundle, } @@ -56,7 +57,7 @@ impl AnchoredBundle { derive(Serialize, Deserialize), serde(crate = "serde_crate", rename_all = "camelCase") )] -pub enum Anchor { +pub enum XAnchor { #[strict_type(tag = 0x00)] Bitcoin(AnchorSet

), @@ -64,62 +65,62 @@ pub enum Anchor { Liquid(AnchorSet

), } -impl Anchor

{ +impl XAnchor

{ #[inline] pub fn layer1(&self) -> Layer1 { match self { - Anchor::Bitcoin(_) => Layer1::Bitcoin, - Anchor::Liquid(_) => Layer1::Liquid, + XAnchor::Bitcoin(_) => Layer1::Bitcoin, + XAnchor::Liquid(_) => Layer1::Liquid, } } #[inline] pub fn witness_id(&self) -> WitnessId { match self { - Anchor::Bitcoin(anchor) => WitnessId::Bitcoin(anchor.txid_unchecked()), - Anchor::Liquid(anchor) => WitnessId::Liquid(anchor.txid_unchecked()), + XAnchor::Bitcoin(anchor) => WitnessId::Bitcoin(anchor.txid_unchecked()), + XAnchor::Liquid(anchor) => WitnessId::Liquid(anchor.txid_unchecked()), } } fn map( self, f: impl FnOnce(AnchorSet

) -> Result, E>, - ) -> Result, E> { + ) -> Result, E> { match self { - Anchor::Bitcoin(anchor) => f(anchor).map(Anchor::Bitcoin), - Anchor::Liquid(anchor) => f(anchor).map(Anchor::Liquid), + XAnchor::Bitcoin(anchor) => f(anchor).map(XAnchor::Bitcoin), + XAnchor::Liquid(anchor) => f(anchor).map(XAnchor::Liquid), } } } -impl Anchor { +impl XAnchor { pub fn known_bundle_ids(&self) -> impl Iterator + '_ { match self { - Anchor::Bitcoin(anchor) | Anchor::Liquid(anchor) => anchor.known_bundle_ids(), + XAnchor::Bitcoin(anchor) | XAnchor::Liquid(anchor) => anchor.known_bundle_ids(), } } pub fn to_merkle_proof( &self, contract_id: ContractId, - ) -> Result, mpc::LeafNotKnown> { + ) -> Result, mpc::LeafNotKnown> { self.clone().into_merkle_proof(contract_id) } pub fn into_merkle_proof( self, contract_id: ContractId, - ) -> Result, mpc::LeafNotKnown> { + ) -> Result, mpc::LeafNotKnown> { self.map(|a| a.into_merkle_proof(contract_id)) } } -impl Anchor { +impl XAnchor { pub fn to_merkle_block( &self, contract_id: ContractId, bundle_id: BundleId, - ) -> Result, mpc::InvalidProof> { + ) -> Result, mpc::InvalidProof> { self.clone().into_merkle_block(contract_id, bundle_id) } @@ -127,7 +128,7 @@ impl Anchor { self, contract_id: ContractId, bundle_id: BundleId, - ) -> Result, mpc::InvalidProof> { + ) -> Result, mpc::InvalidProof> { self.map(|a| a.into_merkle_block(contract_id, bundle_id)) } } @@ -142,13 +143,13 @@ impl Anchor { )] pub enum AnchorSet { #[strict_type(tag = 0x01)] - Tapret(dbc::Anchor), + Tapret(Anchor), #[strict_type(tag = 0x02)] - Opret(dbc::Anchor), + Opret(Anchor), #[strict_type(tag = 0x03)] Dual { - tapret: dbc::Anchor, - opret: dbc::Anchor, + tapret: Anchor, + opret: Anchor, }, } @@ -171,8 +172,8 @@ impl AnchorSet

{ } pub fn from_split( - tapret: Option>, - opret: Option>, + tapret: Option>, + opret: Option>, ) -> Option { Some(match (tapret, opret) { (Some(tapret), Some(opret)) => Self::Dual { tapret, opret }, @@ -183,9 +184,7 @@ impl AnchorSet

{ } #[allow(clippy::type_complexity)] - pub fn as_split( - &self, - ) -> (Option<&dbc::Anchor>, Option<&dbc::Anchor>) { + pub fn as_split(&self) -> (Option<&Anchor>, Option<&Anchor>) { match self { AnchorSet::Tapret(tapret) => (Some(tapret), None), AnchorSet::Opret(opret) => (None, Some(opret)), @@ -194,9 +193,7 @@ impl AnchorSet

{ } #[allow(clippy::type_complexity)] - pub fn into_split( - self, - ) -> (Option>, Option>) { + pub fn into_split(self) -> (Option>, Option>) { match self { AnchorSet::Tapret(tapret) => (Some(tapret), None), AnchorSet::Opret(opret) => (None, Some(opret)), @@ -239,13 +236,11 @@ impl AnchorSet { impl AnchorSet { pub fn known_bundle_ids(&self) -> impl Iterator + '_ { - self.mpc_proofs() - .map(|p| { - p.to_known_message_map() - .into_iter() - .map(|(p, m)| (m.into(), p.into())) - }) - .flatten() + self.mpc_proofs().flat_map(|p| { + p.to_known_message_map() + .into_iter() + .map(|(p, m)| (m.into(), p.into())) + }) } pub fn to_merkle_proof( diff --git a/src/contract/assignments.rs b/src/contract/assignments.rs index 7baba590..ea05842a 100644 --- a/src/contract/assignments.rs +++ b/src/contract/assignments.rs @@ -35,7 +35,7 @@ use super::ExposedState; use crate::contract::seal::GenesisSeal; use crate::{ AssignmentType, ExposedSeal, GraphSeal, RevealedAttach, RevealedData, RevealedValue, - SecretSeal, StateType, VoidState, Xchain, LIB_NAME_RGB, + SecretSeal, StateType, VoidState, XSeal, LIB_NAME_RGB, }; #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Display, Error)] @@ -76,12 +76,12 @@ pub enum Assign { state: State::Confidential, }, #[strict_type(tag = 0x03)] - Revealed { seal: Xchain, state: State }, + Revealed { seal: XSeal, state: State }, #[strict_type(tag = 0x02)] ConfidentialSeal { seal: SecretSeal, state: State }, #[strict_type(tag = 0x01)] ConfidentialState { - seal: Xchain, + seal: XSeal, state: State::Confidential, }, } @@ -118,9 +118,9 @@ impl Hash for Assign { } impl Assign { - pub fn revealed(seal: Xchain, state: State) -> Self { Assign::Revealed { seal, state } } + pub fn revealed(seal: XSeal, state: State) -> Self { Assign::Revealed { seal, state } } - pub fn with_seal_replaced(assignment: &Self, seal: Xchain) -> Self { + pub fn with_seal_replaced(assignment: &Self, seal: XSeal) -> Self { match assignment { Assign::Confidential { seal: _, state } | Assign::ConfidentialState { seal: _, state } => Assign::ConfidentialState { @@ -145,7 +145,7 @@ impl Assign { } } - pub fn revealed_seal(&self) -> Option> { + pub fn revealed_seal(&self) -> Option> { match self { Assign::Revealed { seal, .. } | Assign::ConfidentialState { seal, .. } => Some(*seal), Assign::Confidential { .. } | Assign::ConfidentialSeal { .. } => None, @@ -182,21 +182,21 @@ impl Assign { } } - pub fn as_revealed(&self) -> Option<(&Xchain, &State)> { + pub fn as_revealed(&self) -> Option<(&XSeal, &State)> { match self { Assign::Revealed { seal, state } => Some((seal, state)), _ => None, } } - pub fn to_revealed(&self) -> Option<(Xchain, State)> { + pub fn to_revealed(&self) -> Option<(XSeal, State)> { match self { Assign::Revealed { seal, state } => Some((*seal, state.clone())), _ => None, } } - pub fn into_revealed(self) -> Option<(Xchain, State)> { + pub fn into_revealed(self) -> Option<(XSeal, State)> { match self { Assign::Revealed { seal, state } => Some((seal, state)), _ => None, @@ -445,7 +445,7 @@ impl TypedAssigns { /// If seal definition does not exist, returns [`UnknownDataError`]. If the /// seal is confidential, returns `Ok(None)`; otherwise returns revealed /// seal data packed as `Ok(Some(`[`Seal`]`))` - pub fn revealed_seal_at(&self, index: u16) -> Result>, UnknownDataError> { + pub fn revealed_seal_at(&self, index: u16) -> Result>, UnknownDataError> { Ok(match self { TypedAssigns::Declarative(vec) => vec .get(index as usize) diff --git a/src/contract/contract.rs b/src/contract/contract.rs index 854279ba..9cd9a1e2 100644 --- a/src/contract/contract.rs +++ b/src/contract/contract.rs @@ -37,7 +37,7 @@ use crate::{ Assign, AssignmentType, Assignments, AssignmentsRef, ContractId, ExposedSeal, ExposedState, Extension, Genesis, GlobalStateType, OpId, Operation, OutputSeal, RevealedAttach, RevealedData, RevealedValue, SchemaId, SubSchema, Transition, TypedAssigns, VoidState, WitnessAnchor, - WitnessId, Xchain, LIB_NAME_RGB, + WitnessId, XSeal, LIB_NAME_RGB, }; #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] @@ -146,7 +146,7 @@ impl OutputAssignment { /// If the processing is done on invalid stash data, the seal is /// witness-based and the anchor chain doesn't match the seal chain. pub fn with_witness( - seal: Xchain, + seal: XSeal, witness_id: WitnessId, state: State, opid: OpId, @@ -169,7 +169,7 @@ impl OutputAssignment { /// If the processing is done on invalid stash data, the seal is /// witness-based and the anchor chain doesn't match the seal chain. pub fn with_no_witness( - seal: Xchain, + seal: XSeal, state: State, opid: OpId, ty: AssignmentType, diff --git a/src/contract/mod.rs b/src/contract/mod.rs index b6879445..1c0e0f33 100644 --- a/src/contract/mod.rs +++ b/src/contract/mod.rs @@ -36,7 +36,7 @@ mod contract; use std::io::Write; use amplify::confinement::TinyOrdSet; -pub use anchor::{Anchor, AnchorSet, AnchoredBundle, Layer1, WitnessAnchor}; +pub use anchor::{AnchorSet, AnchoredBundle, Layer1, WitnessAnchor, XAnchor}; pub use assignments::{ Assign, AssignAttach, AssignData, AssignFungible, AssignRights, Assignments, AssignmentsRef, TypedAssigns, @@ -60,7 +60,7 @@ pub use operations::{ }; pub use seal::{ ExposedSeal, GenesisSeal, GraphSeal, OutputSeal, SecretSeal, TxoSeal, WitnessId, WitnessOrd, - WitnessPos, Xchain, XchainParseError, + WitnessPos, XSeal, XchainParseError, }; pub use state::{ConfidentialState, ExposedState, StateCommitment, StateData, StateType}; diff --git a/src/contract/seal.rs b/src/contract/seal.rs index 528aa8a9..77c93bde 100644 --- a/src/contract/seal.rs +++ b/src/contract/seal.rs @@ -49,7 +49,7 @@ impl ExposedSeal for GenesisSeal {} impl ExposedSeal for ExplicitSeal {} -pub type OutputSeal = Xchain>; +pub type OutputSeal = XSeal>; /* #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] @@ -71,7 +71,7 @@ pub struct SealPreimage(Bytes32); derive(Serialize, Deserialize), serde(crate = "serde_crate", rename_all = "camelCase") )] -pub enum Xchain { +pub enum XSeal { #[strict_type(tag = 0x00)] Bitcoin(U), #[strict_type(tag = 0x01)] @@ -84,15 +84,15 @@ pub enum Xchain { */ } -impl Conceal for Xchain { +impl Conceal for XSeal { type Concealed = SecretSeal; #[inline] fn conceal(&self) -> Self::Concealed { SecretSeal::commit(self) } } -impl CommitVerify, UntaggedProtocol> for SecretSeal { - fn commit(reveal: &Xchain) -> Self { +impl CommitVerify, UntaggedProtocol> for SecretSeal { + fn commit(reveal: &XSeal) -> Self { let mut engine = Sha256::default(); let w = StrictWriter::with(u32::MAX as usize, &mut engine); reveal.strict_encode(w).ok(); @@ -100,15 +100,15 @@ impl CommitVerify, UntaggedProtocol> for SecretSeal { } } -impl commit_verify::CommitStrategy for Xchain { +impl commit_verify::CommitStrategy for XSeal { type Strategy = strategies::ConcealStrict; } -impl Xchain { - pub fn transmutate(self) -> Xchain { +impl XSeal { + pub fn transmutate(self) -> XSeal { match self { - Xchain::Bitcoin(seal) => Xchain::Bitcoin(seal.transmutate()), - Xchain::Liquid(seal) => Xchain::Liquid(seal.transmutate()), + XSeal::Bitcoin(seal) => XSeal::Bitcoin(seal.transmutate()), + XSeal::Liquid(seal) => XSeal::Liquid(seal.transmutate()), /* SealDefinition::Abraxas(seal) => SealDefinition::Abraxas(seal), SealDefinition::Prime(seal) => SealDefinition::Prime(seal), @@ -117,67 +117,67 @@ impl Xchain { } } -impl Xchain { +impl XSeal { pub fn with(layer1: Layer1, seal: impl Into) -> Self { match layer1 { - Layer1::Bitcoin => Xchain::Bitcoin(seal.into()), - Layer1::Liquid => Xchain::Liquid(seal.into()), + Layer1::Bitcoin => XSeal::Bitcoin(seal.into()), + Layer1::Liquid => XSeal::Liquid(seal.into()), } } pub fn layer1(self) -> Layer1 { match self { - Xchain::Bitcoin(_) => Layer1::Bitcoin, - Xchain::Liquid(_) => Layer1::Liquid, + XSeal::Bitcoin(_) => Layer1::Bitcoin, + XSeal::Liquid(_) => Layer1::Liquid, } } pub fn reduce_to_bp(self) -> Option { Some(match self { - Xchain::Bitcoin(seal) => seal, - Xchain::Liquid(seal) => seal, + XSeal::Bitcoin(seal) => seal, + XSeal::Liquid(seal) => seal, }) } pub fn method(self) -> CloseMethod { match self { - Xchain::Bitcoin(seal) => seal.method(), - Xchain::Liquid(seal) => seal.method(), + XSeal::Bitcoin(seal) => seal.method(), + XSeal::Liquid(seal) => seal.method(), } } #[inline] pub fn to_output_seal(self) -> Option { Some(match self { - Xchain::Bitcoin(seal) => { + XSeal::Bitcoin(seal) => { let outpoint = seal.outpoint()?; - Xchain::Bitcoin(ExplicitSeal::new(seal.method(), outpoint)) + XSeal::Bitcoin(ExplicitSeal::new(seal.method(), outpoint)) } - Xchain::Liquid(seal) => { + XSeal::Liquid(seal) => { let outpoint = seal.outpoint()?; - Xchain::Liquid(ExplicitSeal::new(seal.method(), outpoint)) + XSeal::Liquid(ExplicitSeal::new(seal.method(), outpoint)) } }) } pub fn try_to_output_seal(self, witness_id: WitnessId) -> Result { match (self, witness_id) { - (Xchain::Bitcoin(seal), WitnessId::Bitcoin(txid)) => { - Ok(Xchain::Bitcoin(ExplicitSeal::new(seal.method(), seal.outpoint_or(txid)))) + (XSeal::Bitcoin(seal), WitnessId::Bitcoin(txid)) => { + Ok(XSeal::Bitcoin(ExplicitSeal::new(seal.method(), seal.outpoint_or(txid)))) } - (Xchain::Liquid(seal), WitnessId::Liquid(txid)) => { - Ok(Xchain::Liquid(ExplicitSeal::new(seal.method(), seal.outpoint_or(txid)))) + (XSeal::Liquid(seal), WitnessId::Liquid(txid)) => { + Ok(XSeal::Liquid(ExplicitSeal::new(seal.method(), seal.outpoint_or(txid)))) } (me, _) => Err(me), } } } -impl Display for Xchain { +impl Display for XSeal { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - Xchain::Bitcoin(seal) => write!(f, "bitcoin:{seal}"), - Xchain::Liquid(seal) => write!(f, "liquid:{seal}"), + XSeal::Bitcoin(seal) => write!(f, "bitcoin:{seal}"), + XSeal::Liquid(seal) => write!(f, "liquid:{seal}"), } } } @@ -192,7 +192,7 @@ pub enum XchainParseError { Seal(E), } -impl FromStr for Xchain +impl FromStr for XSeal where U::Err: Debug + Display { type Err = XchainParseError; @@ -202,17 +202,14 @@ where U::Err: Debug + Display match prefix { "bitcoin" => s .parse() - .map(Xchain::Bitcoin) - .map_err(XchainParseError::from), - "liquid" => s - .parse() - .map(Xchain::Liquid) + .map(XSeal::Bitcoin) .map_err(XchainParseError::from), + "liquid" => s.parse().map(XSeal::Liquid).map_err(XchainParseError::from), unknown => Err(XchainParseError::UnknownPrefix(unknown.to_owned())), } } else { s.parse() - .map(Xchain::Bitcoin) + .map(XSeal::Bitcoin) .map_err(XchainParseError::from) } } diff --git a/src/stl.rs b/src/stl.rs index 81b7ee5f..46bf1e79 100644 --- a/src/stl.rs +++ b/src/stl.rs @@ -32,7 +32,7 @@ use crate::{AnchoredBundle, ContractState, Extension, Genesis, SubSchema, LIB_NA /// Strict types id for the library providing data types for RGB consensus. pub const LIB_ID_RGB: &str = - "urn:ubideco:stl:CjHnUSMpAUVxemMGtj8hJtcCssguGxDghjBzfLrUfiMW#action-ribbon-diana"; + "urn:ubideco:stl:68HqdPT8Bwr2Y9im6kY28Z2JNABrRnxKeGuhrkYCAYqE#laptop-brush-baby"; fn _rgb_core_stl() -> Result { LibBuilder::new(libname!(LIB_NAME_RGB), tiny_bset! { diff --git a/src/validation/status.rs b/src/validation/status.rs index 8b8fa47f..33499a1c 100644 --- a/src/validation/status.rs +++ b/src/validation/status.rs @@ -33,7 +33,7 @@ use crate::contract::Opout; use crate::schema::{self, SchemaId}; use crate::{ BundleId, ContractId, Layer1, OccurrencesMismatch, OpFullType, OpId, SecretSeal, StateType, - Vin, Xchain, + Vin, XSeal, }; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Display)] @@ -340,7 +340,7 @@ pub enum Failure { SealWitnessLayer1Mismatch { seal: Layer1, anchor: Layer1 }, /// seal {1:?} is defined on {0} which is not in the set of layers allowed /// by the contract genesis. - SealLayerMismatch(Layer1, Xchain), + SealLayerMismatch(Layer1, XSeal), /// transition bundle {0} doesn't close seal with the witness transaction /// {1}. Details: {2} SealsInvalid(BundleId, Txid, String), diff --git a/src/validation/validator.rs b/src/validation/validator.rs index 36948f9c..2778b350 100644 --- a/src/validation/validator.rs +++ b/src/validation/validator.rs @@ -22,6 +22,7 @@ use std::collections::{BTreeMap, BTreeSet, VecDeque}; +use bp::dbc::Anchor; use bp::seals::txout::{CloseMethod, ExplicitSeal, TxoSeal, Witness}; use bp::{dbc, Outpoint, Tx, Txid}; use commit_verify::mpc; @@ -31,9 +32,9 @@ use super::status::{Failure, Warning}; use super::{CheckedConsignment, ConsignmentApi, Status, Validity, VirtualMachine}; use crate::vm::AluRuntime; use crate::{ - AltLayer1, Anchor, AnchorSet, BundleId, ContractId, Layer1, OpId, OpRef, Operation, Opout, - Schema, SchemaId, SchemaRoot, Script, SubSchema, Transition, TransitionBundle, TypedAssigns, - WitnessId, + AltLayer1, AnchorSet, BundleId, ContractId, Layer1, OpId, OpRef, Operation, Opout, Schema, + SchemaId, SchemaRoot, Script, SubSchema, Transition, TransitionBundle, TypedAssigns, WitnessId, + XAnchor, }; #[derive(Clone, Debug, Display, Error, From)] @@ -309,7 +310,7 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> // layer1 structure. let witness_id = anchored_bundle.anchor.witness_id(); let anchors = match &anchored_bundle.anchor { - Anchor::Bitcoin(anchor) | Anchor::Liquid(anchor) => anchor, + XAnchor::Bitcoin(anchor) | XAnchor::Liquid(anchor) => anchor, }; let bundle = &anchored_bundle.bundle; @@ -521,7 +522,7 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> seals: impl IntoIterator, witness: Witness, bundle_id: BundleId, - anchor: &'temp dbc::Anchor, + anchor: &'temp Anchor, ) { let message = mpc::Message::from(bundle_id); // [VALIDATION]: Checking anchor MPC commitment diff --git a/stl/RGB@0.1.0.sta b/stl/RGB@0.1.0.sta index 0301baa1..a13b7d4b 100644 --- a/stl/RGB@0.1.0.sta +++ b/stl/RGB@0.1.0.sta @@ -1,5 +1,5 @@ -----BEGIN STRICT TYPE LIB----- -Id: urn:ubideco:stl:CjHnUSMpAUVxemMGtj8hJtcCssguGxDghjBzfLrUfiMW +Id: urn:ubideco:stl:68HqdPT8Bwr2Y9im6kY28Z2JNABrRnxKeGuhrkYCAYqE Name: RGB Dependencies: urn:ubideco:stl:ZtHaBzu9ojbDahaGKEXe5v9DfSDxLERbLkEB23R6Q6V, @@ -59,284 +59,284 @@ U2NyaXB0BgIEbGlicwAKArmzB6Bap1ZJhkNCbroWCz+PjGj56E/9zS2FQAp57Q9g pzBVAi35XMjwiaNFoj+W3lEpwBO3DvEn2CGQQZX7UwoACAAAQAAAAAAAAAAA//8A AAAAAAAAAAAAAAAAAP8AAAAAAAAAC2VudHJ5UG9pbnRzAAoABwAAQAMAArmzB6Ba p1ZJhkNCbroWCz+PjGj56E/9zS2FQAp57Q9gbe+hJuG8deH/SEv7hcE00Qwy3Iwe -QOBpSxzWp+vc3GEAAAAAAAAAAP//AAAAAAAABkFuY2hvcgQCAAdiaXRjb2luAAUB -ATCATDqJMQfb2Cuaoz6mNsSk6w0JQpRii8v0B9e0v42NAQZsaXF1aWQABQEBMIBM -OokxB9vYK5qjPqY2xKTrDQlClGKLy/QH17S/jY0JQW5jaG9yU2V0BAMBBnRhcHJl -dAAFAQJBD/d2QIYSU77AjC52Xwu6XWojk0H9GfxAPSJfiKVy+xUdyY0BIXjnjvH0 -xVzri+jmMzSDRFniwqNc7i7tvTFNAgVvcHJldAAFAQJBD/d2QIYSU77AjC52Xwu6 -XWojk0H9GfxAPSJfiKVy+2Sc57Qkj6v2AGTDqFknAKSA/5t+BvfUzoxBZGDxF449 -AwRkdWFsAAYCBnRhcHJldAJBD/d2QIYSU77AjC52Xwu6XWojk0H9GfxAPSJfiKVy -+xUdyY0BIXjnjvH0xVzri+jmMzSDRFniwqNc7i7tvTFNBW9wcmV0AkEP93ZAhhJT -vsCMLnZfC7pdaiOTQf0Z/EA9Il+IpXL7ZJzntCSPq/YAZMOoWScApID/m34G99TO -jEFkYPEXjj0OQW5jaG9yZWRCdW5kbGUGAgZhbmNob3IBAPuWr//RTDiPJjNA6aCv -mWzkE3240iW/UCHWIiLtb7gGYnVuZGxlAdWvfrlv3zlwTsSxGUiB+mc6KXHAwEFi -16JO9AqmkJ/3CEFzc2V0VGFnBQEABwAAQCAAIkFzc2lnblJldmVhbGVkQXR0YWNo -QmxpbmRTZWFsVHhQdHIEBAAMY29uZmlkZW50aWFsAAYCBHNlYWwCQQ/3dkCGElO+ -wIwudl8Lul1qI5NB/Rn8QD0iX4ilcvtoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcD -YKmUs70GTgVzdGF0ZQH8NEXdX88NC/+sFaR6ugUi4FuLKxswZVKHg497LeuOPQER -Y29uZmlkZW50aWFsU3RhdGUABgIEc2VhbAEkvLMZiKNHP+ioRwLJfnX8PwOt6aqt -h5QAMFBriWteygVzdGF0ZQH8NEXdX88NC/+sFaR6ugUi4FuLKxswZVKHg497LeuO -PQIQY29uZmlkZW50aWFsU2VhbAAGAgRzZWFsAkEP93ZAhhJTvsCMLnZfC7pdaiOT -Qf0Z/EA9Il+IpXL7aBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2CplLO9Bk4Fc3Rh -dGUBaFM0IAzcU8ZNduK2LqpmL9o62ZG/BqspDrzQ9noDSswDCHJldmVhbGVkAAYC -BHNlYWwBJLyzGYijRz/oqEcCyX51/D8DremqrYeUADBQa4lrXsoFc3RhdGUBaFM0 -IAzcU8ZNduK2LqpmL9o62ZG/BqspDrzQ9noDSswhQXNzaWduUmV2ZWFsZWRBdHRh -Y2hCbGluZFNlYWxUeGlkBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkEP93ZAhhJT -vsCMLnZfC7pdaiOTQf0Z/EA9Il+IpXL7aBnrvNWzGKuXs5ilSzZl3dqnBm/o6STn -A2CplLO9Bk4Fc3RhdGUB/DRF3V/PDQv/rBWkeroFIuBbiysbMGVSh4OPey3rjj0B -EWNvbmZpZGVudGlhbFN0YXRlAAYCBHNlYWwBwOIKL9PIMRrWh/qX6o7p2yCTIW+c -vaNcJzqoLjPGHM8Fc3RhdGUB/DRF3V/PDQv/rBWkeroFIuBbiysbMGVSh4OPey3r -jj0CEGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAJBD/d2QIYSU77AjC52Xwu6XWoj -k0H9GfxAPSJfiKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0 -YXRlAWhTNCAM3FPGTXbiti6qZi/aOtmRvwarKQ680PZ6A0rMAwhyZXZlYWxlZAAG -AgRzZWFsAcDiCi/TyDEa1of6l+qO6dsgkyFvnL2jXCc6qC4zxhzPBXN0YXRlAWhT -NCAM3FPGTXbiti6qZi/aOtmRvwarKQ680PZ6A0rMIEFzc2lnblJldmVhbGVkRGF0 -YUJsaW5kU2VhbFR4UHRyBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkEP93ZAhhJT -vsCMLnZfC7pdaiOTQf0Z/EA9Il+IpXL7aBnrvNWzGKuXs5ilSzZl3dqnBm/o6STn -A2CplLO9Bk4Fc3RhdGUBcA1lELKCgW8ftz3lEmOTA1cIpVJhrQLg1L5axc+n6gkB -EWNvbmZpZGVudGlhbFN0YXRlAAYCBHNlYWwBJLyzGYijRz/oqEcCyX51/D8Dremq -rYeUADBQa4lrXsoFc3RhdGUBcA1lELKCgW8ftz3lEmOTA1cIpVJhrQLg1L5axc+n -6gkCEGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAJBD/d2QIYSU77AjC52Xwu6XWoj -k0H9GfxAPSJfiKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0 -YXRlASDyUFYij2bO/JH66me8X8bwaVw9R3lzWT+s0TfKckhlAwhyZXZlYWxlZAAG -AgRzZWFsASS8sxmIo0c/6KhHAsl+dfw/A63pqq2HlAAwUGuJa17KBXN0YXRlASDy -UFYij2bO/JH66me8X8bwaVw9R3lzWT+s0TfKckhlH0Fzc2lnblJldmVhbGVkRGF0 -YUJsaW5kU2VhbFR4aWQEBAAMY29uZmlkZW50aWFsAAYCBHNlYWwCQQ/3dkCGElO+ -wIwudl8Lul1qI5NB/Rn8QD0iX4ilcvtoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcD -YKmUs70GTgVzdGF0ZQFwDWUQsoKBbx+3PeUSY5MDVwilUmGtAuDUvlrFz6fqCQER -Y29uZmlkZW50aWFsU3RhdGUABgIEc2VhbAHA4gov08gxGtaH+pfqjunbIJMhb5y9 -o1wnOqguM8YczwVzdGF0ZQFwDWUQsoKBbx+3PeUSY5MDVwilUmGtAuDUvlrFz6fq -CQIQY29uZmlkZW50aWFsU2VhbAAGAgRzZWFsAkEP93ZAhhJTvsCMLnZfC7pdaiOT -Qf0Z/EA9Il+IpXL7aBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2CplLO9Bk4Fc3Rh -dGUBIPJQViKPZs78kfrqZ7xfxvBpXD1HeXNZP6zRN8pySGUDCHJldmVhbGVkAAYC -BHNlYWwBwOIKL9PIMRrWh/qX6o7p2yCTIW+cvaNcJzqoLjPGHM8Fc3RhdGUBIPJQ -ViKPZs78kfrqZ7xfxvBpXD1HeXNZP6zRN8pySGUhQXNzaWduUmV2ZWFsZWRWYWx1 -ZUJsaW5kU2VhbFR4UHRyBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkEP93ZAhhJT -vsCMLnZfC7pdaiOTQf0Z/EA9Il+IpXL7aBnrvNWzGKuXs5ilSzZl3dqnBm/o6STn -A2CplLO9Bk4Fc3RhdGUBwlGtZel1DayaElnMwIUkXNX3sW9S2HI2RizxdYyfTl0B -EWNvbmZpZGVudGlhbFN0YXRlAAYCBHNlYWwBJLyzGYijRz/oqEcCyX51/D8Dremq -rYeUADBQa4lrXsoFc3RhdGUBwlGtZel1DayaElnMwIUkXNX3sW9S2HI2RizxdYyf -Tl0CEGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAJBD/d2QIYSU77AjC52Xwu6XWoj -k0H9GfxAPSJfiKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0 -YXRlAW1ExvQvagU6QxUPpubCLMToeuUslPgG1Ayl3If2dY2gAwhyZXZlYWxlZAAG -AgRzZWFsASS8sxmIo0c/6KhHAsl+dfw/A63pqq2HlAAwUGuJa17KBXN0YXRlAW1E -xvQvagU6QxUPpubCLMToeuUslPgG1Ayl3If2dY2gIEFzc2lnblJldmVhbGVkVmFs -dWVCbGluZFNlYWxUeGlkBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkEP93ZAhhJT -vsCMLnZfC7pdaiOTQf0Z/EA9Il+IpXL7aBnrvNWzGKuXs5ilSzZl3dqnBm/o6STn -A2CplLO9Bk4Fc3RhdGUBwlGtZel1DayaElnMwIUkXNX3sW9S2HI2RizxdYyfTl0B -EWNvbmZpZGVudGlhbFN0YXRlAAYCBHNlYWwBwOIKL9PIMRrWh/qX6o7p2yCTIW+c -vaNcJzqoLjPGHM8Fc3RhdGUBwlGtZel1DayaElnMwIUkXNX3sW9S2HI2RizxdYyf -Tl0CEGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAJBD/d2QIYSU77AjC52Xwu6XWoj -k0H9GfxAPSJfiKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0 -YXRlAW1ExvQvagU6QxUPpubCLMToeuUslPgG1Ayl3If2dY2gAwhyZXZlYWxlZAAG -AgRzZWFsAcDiCi/TyDEa1of6l+qO6dsgkyFvnL2jXCc6qC4zxhzPBXN0YXRlAW1E -xvQvagU6QxUPpubCLMToeuUslPgG1Ayl3If2dY2gHUFzc2lnblZvaWRTdGF0ZUJs +QOBpSxzWp+vc3GEAAAAAAAAAAP//AAAAAAAACUFuY2hvclNldAQDAQZ0YXByZXQA +BQECQQ/3dkCGElO+wIwudl8Lul1qI5NB/Rn8QD0iX4ilcvsVHcmNASF4547x9MVc +64vo5jM0g0RZ4sKjXO4u7b0xTQIFb3ByZXQABQECQQ/3dkCGElO+wIwudl8Lul1q +I5NB/Rn8QD0iX4ilcvtknOe0JI+r9gBkw6hZJwCkgP+bfgb31M6MQWRg8ReOPQME +ZHVhbAAGAgZ0YXByZXQCQQ/3dkCGElO+wIwudl8Lul1qI5NB/Rn8QD0iX4ilcvsV +HcmNASF4547x9MVc64vo5jM0g0RZ4sKjXO4u7b0xTQVvcHJldAJBD/d2QIYSU77A +jC52Xwu6XWojk0H9GfxAPSJfiKVy+2Sc57Qkj6v2AGTDqFknAKSA/5t+BvfUzoxB +ZGDxF449DkFuY2hvcmVkQnVuZGxlBgIGYW5jaG9yAZnFUBdztq4RE9DZeugVGTjw +JheAW5lbTKe1zAiDMP52BmJ1bmRsZQESOEVVE20whsAVkkpbSoPln683cGGNaBwD ++6KqHPancAhBc3NldFRhZwUBAAcAAEAgACJBc3NpZ25SZXZlYWxlZEF0dGFjaEJs aW5kU2VhbFR4UHRyBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkEP93ZAhhJTvsCM LnZfC7pdaiOTQf0Z/EA9Il+IpXL7aBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2Cp -lLO9Bk4Fc3RhdGUBLrKl/hfAMEQwmOBcmxtabNYe7XYNYd7LgCbTZPMW4bsBEWNv -bmZpZGVudGlhbFN0YXRlAAYCBHNlYWwBJLyzGYijRz/oqEcCyX51/D8DremqrYeU -ADBQa4lrXsoFc3RhdGUBLrKl/hfAMEQwmOBcmxtabNYe7XYNYd7LgCbTZPMW4bsC +lLO9Bk4Fc3RhdGUB/DRF3V/PDQv/rBWkeroFIuBbiysbMGVSh4OPey3rjj0BEWNv +bmZpZGVudGlhbFN0YXRlAAYCBHNlYWwBJWFq3aw6xdsnG6nfvlxoTXofW/NHd9iL +hlXXpgfmuroFc3RhdGUB/DRF3V/PDQv/rBWkeroFIuBbiysbMGVSh4OPey3rjj0C EGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAJBD/d2QIYSU77AjC52Xwu6XWojk0H9 GfxAPSJfiKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0YXRl -AS6ypf4XwDBEMJjgXJsbWmzWHu12DWHey4Am02TzFuG7AwhyZXZlYWxlZAAGAgRz -ZWFsASS8sxmIo0c/6KhHAsl+dfw/A63pqq2HlAAwUGuJa17KBXN0YXRlAS6ypf4X -wDBEMJjgXJsbWmzWHu12DWHey4Am02TzFuG7HEFzc2lnblZvaWRTdGF0ZUJsaW5k -U2VhbFR4aWQEBAAMY29uZmlkZW50aWFsAAYCBHNlYWwCQQ/3dkCGElO+wIwudl8L -ul1qI5NB/Rn8QD0iX4ilcvtoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcDYKmUs70G -TgVzdGF0ZQEusqX+F8AwRDCY4FybG1ps1h7tdg1h3suAJtNk8xbhuwERY29uZmlk -ZW50aWFsU3RhdGUABgIEc2VhbAHA4gov08gxGtaH+pfqjunbIJMhb5y9o1wnOqgu -M8YczwVzdGF0ZQEusqX+F8AwRDCY4FybG1ps1h7tdg1h3suAJtNk8xbhuwIQY29u -ZmlkZW50aWFsU2VhbAAGAgRzZWFsAkEP93ZAhhJTvsCMLnZfC7pdaiOTQf0Z/EA9 -Il+IpXL7aBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2CplLO9Bk4Fc3RhdGUBLrKl -/hfAMEQwmOBcmxtabNYe7XYNYd7LgCbTZPMW4bsDCHJldmVhbGVkAAYCBHNlYWwB -wOIKL9PIMRrWh/qX6o7p2yCTIW+cvaNcJzqoLjPGHM8Fc3RhdGUBLrKl/hfAMEQw -mOBcmxtabNYe7XYNYd7LgCbTZPMW4bsOQXNzaWdubWVudFR5cGUFAQAAAhlBc3Np -Z25tZW50c0JsaW5kU2VhbFR4UHRyBQEACgGH/uJlWIhs6By+hoSzeWMnE2WDxbZo -AV6LFBY2sUSHuQES0B/DKzjcoudQlinXZaJzuJP3Ng6XQYTyYbhR3gJQ5wAAAAAA -AAAA/wAAAAAAAAAYQXNzaWdubWVudHNCbGluZFNlYWxUeGlkBQEACgGH/uJlWIhs -6By+hoSzeWMnE2WDxbZoAV6LFBY2sUSHuQGMwdNqHGGkrHkTbqPfYf8oiq8voLGV -TcVv1Knx91k/WwAAAAAAAAAA/wAAAAAAAAAIQXR0YWNoSWQFAQAHAABAIAAOQmxp -bmRpbmdGYWN0b3IFAQAHAABAIAAPQ29uY2VhbGVkQXR0YWNoBQEABwAAQCAADUNv -bmNlYWxlZERhdGEFAQAHAABAIAARQ29uY2VhbGVkRnVuZ2libGUGAgpjb21taXRt -ZW50AUi9Gm4X+4Y7Fnx+JV41Z9uCQ+8qXrrrosUKzQmunlEaCnJhbmdlUHJvb2YB -qFhr+JFl2sIjEG29hcSGyTfmsGbrDGZB/xYvaKh3pZgPQ29udHJhY3RIaXN0b3J5 -BggIc2NoZW1hSWQBlFLT2wOrq6hRn6f2PtAU69RNfTE//P4A+l0kelQEkBAMcm9v -dFNjaGVtYUlkAAQCAARub25lAAAAAQRzb21lAAUBAZRS09sDq6uoUZ+n9j7QFOvU -TX0xP/z+APpdJHpUBJAQCmNvbnRyYWN0SWQBnwgsSTrIAqK6xd3cCyJ8IK+U1GjE -SM8aWiHgvcL1OjIGZ2xvYmFsAAoB1e6SDkmIs2nxalPRB0r7tSP4x1JxHvs2PVZZ -BeHkFcsACgF69XkNjG4gtH3wH/XNhWn1y7zxEMGvZWy1kKKWtKv/AQEg8lBWIo9m -zvyR+upnvF/G8GlcPUd5c1k/rNE3ynJIZQAAAAAAAAAA/////wAAAAAAAAAAAAAA -AP8AAAAAAAAABnJpZ2h0cwAJAeFSWe0gp+bTva2FlYlvsu/JclNjeI53R081+Mq/ -S04LAAAAAAAAAAD/////AAAAAAlmdW5naWJsZXMACQG4bc6bJthz4eq3amq/K7GV -ybU+1VQP0ajbA0c1BPdlaAAAAAAAAAAA/////wAAAAAEZGF0YQAJAdMORAY4T48R -3R0BXA3IR5IWzoSUlLTuEucjjCbWzZfSAAAAAAAAAAD/////AAAAAAZhdHRhY2gA -CQH0C1LTLD31Nt6xbN1WdD8YRdL73cjyOxW5/lCRa55swQAAAAAAAAAA/////wAA -AAAKQ29udHJhY3RJZAUBAAcAAEAgAA1Db250cmFjdFN0YXRlBgIGc2NoZW1hARBB -gnI1FxzZgpWSppkX/gNxP61v4tMhTojB/hyjpwqdB2hpc3RvcnkBPFLnrq+zlEL1 -9wGQ1AoMvppMckAB/YH3oYJqXfsOYJ0JRXh0ZW5zaW9uBggDZmZ2AdqbURNYFlZ2 -kIf7meVWlHI2gNc5DAahzCSYLAVk98zVCmNvbnRyYWN0SWQBnwgsSTrIAqK6xd3c -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 -AAAA//8AAAAAAAAFSW5wdXQGAgdwcmV2T3V0AZMQvICxNFqLL8NYUu4PTWjQHbf6 -NcubZJKrVgma9JqXCHJlc2VydmVkAUUqpV991gFKi6FtxGYytg/xtWzcOlMuz71r -k9VnGZ6JBklucHV0cwUBAAkByUJCIu0Cvkdp/U8jHbNFTqcovEOoEQ7bM8uPLwqe -SEYAAAAAAAAAAP8AAAAAAAAACU1lZGlhVHlwZQMBA2Fuef8JTm9pc2VEdW1iBQEA -BwAAQAACC09jY3VycmVuY2VzBgIDbWluAAACA21heAAAAgRPcElkBQEABwAAQCAA -BU9wb3V0BgMCb3ABlcjmeh51Yl/UllVCapHrfkKQoW9amPG+UPe2iiQS98UCdHkB -h/7iZViIbOgcvoaEs3ljJxNlg8W2aAFeixQWNrFEh7kCbm8AAAIeT3V0cHV0QXNz -aWdubWVudFJldmVhbGVkQXR0YWNoBgQFb3BvdXQBkxC8gLE0Wosvw1hS7g9NaNAd -t/o1y5tkkqtWCZr0mpcEc2VhbAEHi/pv0wJGc5Kt0sDgjzjARWXkeLjk1LWUd2Nr -iZyNBgVzdGF0ZQFoUzQgDNxTxk124rYuqmYv2jrZkb8GqykOvND2egNKzAd3aXRu -ZXNzAAQCAARub25lAAAAAQRzb21lAAUBAcSgCp7hCQITdyIBFVk7g8NT4mD4gRDk -szbK42hGQScbHE91dHB1dEFzc2lnbm1lbnRSZXZlYWxlZERhdGEGBAVvcG91dAGT -ELyAsTRaiy/DWFLuD01o0B23+jXLm2SSq1YJmvSalwRzZWFsAQeL+m/TAkZzkq3S -wOCPOMBFZeR4uOTUtZR3Y2uJnI0GBXN0YXRlASDyUFYij2bO/JH66me8X8bwaVw9 -R3lzWT+s0TfKckhlB3dpdG5lc3MABAIABG5vbmUAAAABBHNvbWUABQEBxKAKnuEJ -AhN3IgEVWTuDw1PiYPiBEOSzNsrjaEZBJxsdT3V0cHV0QXNzaWdubWVudFJldmVh -bGVkVmFsdWUGBAVvcG91dAGTELyAsTRaiy/DWFLuD01o0B23+jXLm2SSq1YJmvSa -lwRzZWFsAQeL+m/TAkZzkq3SwOCPOMBFZeR4uOTUtZR3Y2uJnI0GBXN0YXRlAW1E -xvQvagU6QxUPpubCLMToeuUslPgG1Ayl3If2dY2gB3dpdG5lc3MABAIABG5vbmUA -AAABBHNvbWUABQEBxKAKnuEJAhN3IgEVWTuDw1PiYPiBEOSzNsrjaEZBJxsZT3V0 -cHV0QXNzaWdubWVudFZvaWRTdGF0ZQYEBW9wb3V0AZMQvICxNFqLL8NYUu4PTWjQ -Hbf6NcubZJKrVgma9JqXBHNlYWwBB4v6b9MCRnOSrdLA4I84wEVl5Hi45NS1lHdj -a4mcjQYFc3RhdGUBLrKl/hfAMEQwmOBcmxtabNYe7XYNYd7LgCbTZPMW4bsHd2l0 -bmVzcwAEAgAEbm9uZQAAAAEEc29tZQAFAQHEoAqe4QkCE3ciARVZO4PDU+Jg+IEQ -5LM2yuNoRkEnGxJQZWRlcnNlbkNvbW1pdG1lbnQFAQAHAABAIQAKUmFuZ2VQcm9v -ZgQB/wtwbGFjZWhvbGRlcgAFAQEedhfxJ33bPrvhag9yEbdt7VXfb0MNVRFfA3gn -pUJXJwhSZWRlZW1lZAUBAAoBRu3mwwgc/bjmV6QqXODGCIR//B++Xna5cXSpyvWJ -Wc4Blcjmeh51Yl/UllVCapHrfkKQoW9amPG+UPe2iiQS98UAAAAAAAAAAP8AAAAA -AAAADFJlc2VydmVkQnl0ZQUBAAABDlJldmVhbGVkQXR0YWNoBgMCaWQBhHENkyxO -9MO3CEtpi7CHcCl+OWQkf0WR2NqDbdF9ujgJbWVkaWFUeXBlAUIwYYWIyNSrFCZA -x/3JFyzN0P8Q/w2TgABEfIia3cx5BHNhbHQAAAgMUmV2ZWFsZWREYXRhBQEACAAA -QAAAAAAAAAAA//8AAAAAAAAQUmV2ZWFsZWRGdW5naWJsZQYDBXZhbHVlAaaMMJFH -S8o6wmKMx5VEjSzdqsUUnwUzlav2PFVhBxcmCGJsaW5kaW5nAYW4+Cu79KSmDbO/ -P0W4D5RueIPDrVJtk/RvowGobkfaA3RhZwHJj5qpwwZLGv39ZxuXvCr8/kxojx9z -yC3rcW/naZsirwZTY2hlbWEGCgNmZnYB2ptRE1gWVnaQh/uZ5VaUcjaA1zkMBqHM -JJgsBWT3zNUIc3Vic2V0T2YABAIABG5vbmUAAAABBHNvbWUABQEAAAALZ2xvYmFs -VHlwZXMACgHV7pIOSYizafFqU9EHSvu1I/jHUnEe+zY9VlkF4eQVywHHmKbYYzZ4 -RB7aUW4sPou/DqiwSoDBtx256XLmP3HmJAAAAAAAAAAA/wAAAAAAAAAKb3duZWRU -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 -uKT8qEsAAAAAAAAAAP//AAAAAAAA/wphdHRhY2htZW50AAUBAAgBKZZ9nq4XJK6Z -2/C/p/re+UZi6T2WjZ/83r85fZXtnQEAAAAAAAAAAP//AAAAAAAAGVR5cGVkQXNz -aWduc0JsaW5kU2VhbFR4aWQEBAALZGVjbGFyYXRpdmUABQEACAEOL75mFCKjObsF -QvYik9Cifpwx8YL+trq2rNsVdPBiVgAAAAAAAAAA//8AAAAAAAABCGZ1bmdpYmxl -AAUBAAgBP6Xk/XbnoyVMKK58A3Bboboij3LYxcT6MOGKHPKwSRoAAAAAAAAAAP// -AAAAAAAAAgpzdHJ1Y3R1cmVkAAUBAAgB68+ws5pDFpcjx6rZxoXkIZKoOmYrA+RZ -XbO75vJsCloAAAAAAAAAAP//AAAAAAAA/wphdHRhY2htZW50AAUBAAgB/BFPWxxN -mu5c38QFiv/3oKNdkudg7RgZmD9xLwolBXwAAAAAAAAAAP//AAAAAAAACVZhbGVu -Y2llcwUBAAkBRu3mwwgc/bjmV6QqXODGCIR//B++Xna5cXSpyvWJWc4AAAAAAAAA -AP8AAAAAAAAAC1ZhbGVuY3lUeXBlBQEAAAIJVm9pZFN0YXRlBQEAAAANV2l0bmVz -c0FuY2hvcgYCCndpdG5lc3NPcmQBwh7s3ADTvuLrjwKbcjr7sRDANpfpzwNoGZQV -pgQHacoJd2l0bmVzc0lkAcSgCp7hCQITdyIBFVk7g8NT4mD4gRDkszbK42hGQScb -CVdpdG5lc3NJZAQCAAdiaXRjb2luAAUBAvVsE2Ij9jmnSgmT3EdGyfmKq7iDWF21 -2RY/GH7EKBoKo4JC88vX0dChEtqN4WAvVtT4bw7ExHbFwGhZTEsEZVYBBmxpcXVp -ZAAFAQL1bBNiI/Y5p0oJk9xHRsn5iqu4g1hdtdkWPxh+xCgaCqOCQvPL19HQoRLa -jeFgL1bU+G8OxMR2xcBoWUxLBGVWCldpdG5lc3NPcmQEAgAHb25DaGFpbgAFAQEO -v7jtOGJupIO6NPMU+VR16VbZRzUT3CcYFjxjtuGJsQEIb2ZmQ2hhaW4AAAAKV2l0 -bmVzc1BvcwYCBmhlaWdodAAABAl0aW1lc3RhbXAAAEgUWGNoYWluQmxpbmRTZWFs +AWhTNCAM3FPGTXbiti6qZi/aOtmRvwarKQ680PZ6A0rMAwhyZXZlYWxlZAAGAgRz +ZWFsASVhat2sOsXbJxup375caE16H1vzR3fYi4ZV16YH5rq6BXN0YXRlAWhTNCAM +3FPGTXbiti6qZi/aOtmRvwarKQ680PZ6A0rMIUFzc2lnblJldmVhbGVkQXR0YWNo +QmxpbmRTZWFsVHhpZAQEAAxjb25maWRlbnRpYWwABgIEc2VhbAJBD/d2QIYSU77A +jC52Xwu6XWojk0H9GfxAPSJfiKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNg +qZSzvQZOBXN0YXRlAfw0Rd1fzw0L/6wVpHq6BSLgW4srGzBlUoeDj3st6449ARFj +b25maWRlbnRpYWxTdGF0ZQAGAgRzZWFsAVayoow+asLxLxx0B3i++PaPaQTQwx+y +j3rgpfn+kGD6BXN0YXRlAfw0Rd1fzw0L/6wVpHq6BSLgW4srGzBlUoeDj3st6449 +AhBjb25maWRlbnRpYWxTZWFsAAYCBHNlYWwCQQ/3dkCGElO+wIwudl8Lul1qI5NB +/Rn8QD0iX4ilcvtoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcDYKmUs70GTgVzdGF0 +ZQFoUzQgDNxTxk124rYuqmYv2jrZkb8GqykOvND2egNKzAMIcmV2ZWFsZWQABgIE +c2VhbAFWsqKMPmrC8S8cdAd4vvj2j2kE0MMfso964KX5/pBg+gVzdGF0ZQFoUzQg +DNxTxk124rYuqmYv2jrZkb8GqykOvND2egNKzCBBc3NpZ25SZXZlYWxlZERhdGFC +bGluZFNlYWxUeFB0cgQEAAxjb25maWRlbnRpYWwABgIEc2VhbAJBD/d2QIYSU77A +jC52Xwu6XWojk0H9GfxAPSJfiKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNg +qZSzvQZOBXN0YXRlAXANZRCygoFvH7c95RJjkwNXCKVSYa0C4NS+WsXPp+oJARFj +b25maWRlbnRpYWxTdGF0ZQAGAgRzZWFsASVhat2sOsXbJxup375caE16H1vzR3fY +i4ZV16YH5rq6BXN0YXRlAXANZRCygoFvH7c95RJjkwNXCKVSYa0C4NS+WsXPp+oJ +AhBjb25maWRlbnRpYWxTZWFsAAYCBHNlYWwCQQ/3dkCGElO+wIwudl8Lul1qI5NB +/Rn8QD0iX4ilcvtoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcDYKmUs70GTgVzdGF0 +ZQEg8lBWIo9mzvyR+upnvF/G8GlcPUd5c1k/rNE3ynJIZQMIcmV2ZWFsZWQABgIE +c2VhbAElYWrdrDrF2ycbqd++XGhNeh9b80d32IuGVdemB+a6ugVzdGF0ZQEg8lBW +Io9mzvyR+upnvF/G8GlcPUd5c1k/rNE3ynJIZR9Bc3NpZ25SZXZlYWxlZERhdGFC +bGluZFNlYWxUeGlkBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkEP93ZAhhJTvsCM +LnZfC7pdaiOTQf0Z/EA9Il+IpXL7aBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2Cp +lLO9Bk4Fc3RhdGUBcA1lELKCgW8ftz3lEmOTA1cIpVJhrQLg1L5axc+n6gkBEWNv +bmZpZGVudGlhbFN0YXRlAAYCBHNlYWwBVrKijD5qwvEvHHQHeL749o9pBNDDH7KP +euCl+f6QYPoFc3RhdGUBcA1lELKCgW8ftz3lEmOTA1cIpVJhrQLg1L5axc+n6gkC +EGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAJBD/d2QIYSU77AjC52Xwu6XWojk0H9 +GfxAPSJfiKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0YXRl +ASDyUFYij2bO/JH66me8X8bwaVw9R3lzWT+s0TfKckhlAwhyZXZlYWxlZAAGAgRz +ZWFsAVayoow+asLxLxx0B3i++PaPaQTQwx+yj3rgpfn+kGD6BXN0YXRlASDyUFYi +j2bO/JH66me8X8bwaVw9R3lzWT+s0TfKckhlIUFzc2lnblJldmVhbGVkVmFsdWVC +bGluZFNlYWxUeFB0cgQEAAxjb25maWRlbnRpYWwABgIEc2VhbAJBD/d2QIYSU77A +jC52Xwu6XWojk0H9GfxAPSJfiKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNg +qZSzvQZOBXN0YXRlAcJRrWXpdQ2smhJZzMCFJFzV97FvUthyNkYs8XWMn05dARFj +b25maWRlbnRpYWxTdGF0ZQAGAgRzZWFsASVhat2sOsXbJxup375caE16H1vzR3fY +i4ZV16YH5rq6BXN0YXRlAcJRrWXpdQ2smhJZzMCFJFzV97FvUthyNkYs8XWMn05d +AhBjb25maWRlbnRpYWxTZWFsAAYCBHNlYWwCQQ/3dkCGElO+wIwudl8Lul1qI5NB +/Rn8QD0iX4ilcvtoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcDYKmUs70GTgVzdGF0 +ZQFtRMb0L2oFOkMVD6bmwizE6HrlLJT4BtQMpdyH9nWNoAMIcmV2ZWFsZWQABgIE +c2VhbAElYWrdrDrF2ycbqd++XGhNeh9b80d32IuGVdemB+a6ugVzdGF0ZQFtRMb0 +L2oFOkMVD6bmwizE6HrlLJT4BtQMpdyH9nWNoCBBc3NpZ25SZXZlYWxlZFZhbHVl +QmxpbmRTZWFsVHhpZAQEAAxjb25maWRlbnRpYWwABgIEc2VhbAJBD/d2QIYSU77A +jC52Xwu6XWojk0H9GfxAPSJfiKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNg +qZSzvQZOBXN0YXRlAcJRrWXpdQ2smhJZzMCFJFzV97FvUthyNkYs8XWMn05dARFj +b25maWRlbnRpYWxTdGF0ZQAGAgRzZWFsAVayoow+asLxLxx0B3i++PaPaQTQwx+y +j3rgpfn+kGD6BXN0YXRlAcJRrWXpdQ2smhJZzMCFJFzV97FvUthyNkYs8XWMn05d +AhBjb25maWRlbnRpYWxTZWFsAAYCBHNlYWwCQQ/3dkCGElO+wIwudl8Lul1qI5NB +/Rn8QD0iX4ilcvtoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcDYKmUs70GTgVzdGF0 +ZQFtRMb0L2oFOkMVD6bmwizE6HrlLJT4BtQMpdyH9nWNoAMIcmV2ZWFsZWQABgIE +c2VhbAFWsqKMPmrC8S8cdAd4vvj2j2kE0MMfso964KX5/pBg+gVzdGF0ZQFtRMb0 +L2oFOkMVD6bmwizE6HrlLJT4BtQMpdyH9nWNoB1Bc3NpZ25Wb2lkU3RhdGVCbGlu +ZFNlYWxUeFB0cgQEAAxjb25maWRlbnRpYWwABgIEc2VhbAJBD/d2QIYSU77AjC52 +Xwu6XWojk0H9GfxAPSJfiKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSz +vQZOBXN0YXRlAS6ypf4XwDBEMJjgXJsbWmzWHu12DWHey4Am02TzFuG7ARFjb25m +aWRlbnRpYWxTdGF0ZQAGAgRzZWFsASVhat2sOsXbJxup375caE16H1vzR3fYi4ZV +16YH5rq6BXN0YXRlAS6ypf4XwDBEMJjgXJsbWmzWHu12DWHey4Am02TzFuG7AhBj +b25maWRlbnRpYWxTZWFsAAYCBHNlYWwCQQ/3dkCGElO+wIwudl8Lul1qI5NB/Rn8 +QD0iX4ilcvtoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcDYKmUs70GTgVzdGF0ZQEu +sqX+F8AwRDCY4FybG1ps1h7tdg1h3suAJtNk8xbhuwMIcmV2ZWFsZWQABgIEc2Vh +bAElYWrdrDrF2ycbqd++XGhNeh9b80d32IuGVdemB+a6ugVzdGF0ZQEusqX+F8Aw +RDCY4FybG1ps1h7tdg1h3suAJtNk8xbhuxxBc3NpZ25Wb2lkU3RhdGVCbGluZFNl +YWxUeGlkBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkEP93ZAhhJTvsCMLnZfC7pd +aiOTQf0Z/EA9Il+IpXL7aBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2CplLO9Bk4F +c3RhdGUBLrKl/hfAMEQwmOBcmxtabNYe7XYNYd7LgCbTZPMW4bsBEWNvbmZpZGVu +dGlhbFN0YXRlAAYCBHNlYWwBVrKijD5qwvEvHHQHeL749o9pBNDDH7KPeuCl+f6Q +YPoFc3RhdGUBLrKl/hfAMEQwmOBcmxtabNYe7XYNYd7LgCbTZPMW4bsCEGNvbmZp +ZGVudGlhbFNlYWwABgIEc2VhbAJBD/d2QIYSU77AjC52Xwu6XWojk0H9GfxAPSJf +iKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0YXRlAS6ypf4X +wDBEMJjgXJsbWmzWHu12DWHey4Am02TzFuG7AwhyZXZlYWxlZAAGAgRzZWFsAVay +oow+asLxLxx0B3i++PaPaQTQwx+yj3rgpfn+kGD6BXN0YXRlAS6ypf4XwDBEMJjg +XJsbWmzWHu12DWHey4Am02TzFuG7DkFzc2lnbm1lbnRUeXBlBQEAAAIZQXNzaWdu +bWVudHNCbGluZFNlYWxUeFB0cgUBAAoBh/7iZViIbOgcvoaEs3ljJxNlg8W2aAFe +ixQWNrFEh7kBa9EQPVH54dz3yE5Hlem8RTMgINYYRZEfl0KkVBueclwAAAAAAAAA +AP8AAAAAAAAAGEFzc2lnbm1lbnRzQmxpbmRTZWFsVHhpZAUBAAoBh/7iZViIbOgc +voaEs3ljJxNlg8W2aAFeixQWNrFEh7kBrReyc1pbStH15nyRZz0WkJ2UnKi9/yQ5 +BpeVy4koirEAAAAAAAAAAP8AAAAAAAAACEF0dGFjaElkBQEABwAAQCAADkJsaW5k +aW5nRmFjdG9yBQEABwAAQCAAD0NvbmNlYWxlZEF0dGFjaAUBAAcAAEAgAA1Db25j +ZWFsZWREYXRhBQEABwAAQCAAEUNvbmNlYWxlZEZ1bmdpYmxlBgIKY29tbWl0bWVu +dAFIvRpuF/uGOxZ8fiVeNWfbgkPvKl6666LFCs0Jrp5RGgpyYW5nZVByb29mAahY +a/iRZdrCIxBtvYXEhsk35rBm6wxmQf8WL2iod6WYD0NvbnRyYWN0SGlzdG9yeQYI +CHNjaGVtYUlkAZRS09sDq6uoUZ+n9j7QFOvUTX0xP/z+APpdJHpUBJAQDHJvb3RT +Y2hlbWFJZAAEAgAEbm9uZQAAAAEEc29tZQAFAQGUUtPbA6urqFGfp/Y+0BTr1E19 +MT/8/gD6XSR6VASQEApjb250cmFjdElkAZ8ILEk6yAKiusXd3AsifCCvlNRoxEjP +Gloh4L3C9ToyBmdsb2JhbAAKAdXukg5JiLNp8WpT0QdK+7Uj+MdScR77Nj1WWQXh +5BXLAAoBevV5DYxuILR98B/1zYVp9cu88RDBr2VstZCilrSr/wEBIPJQViKPZs78 +kfrqZ7xfxvBpXD1HeXNZP6zRN8pySGUAAAAAAAAAAP////8AAAAAAAAAAAAAAAD/ +AAAAAAAAAAZyaWdodHMACQHAmUPBk94MaIXS7scBxs9lR+5nJWQ8Qei18pR7TFAv +uQAAAAAAAAAA/////wAAAAAJZnVuZ2libGVzAAkBxuLkjvJxT+uNZSUyDoB7+GZU +SiL/Jt58VdmcZRaJ0F8AAAAAAAAAAP////8AAAAABGRhdGEACQFZ4ZDKG4w7ZJiy +vPGip/NUxlax6zgj4cO5klWrPr0OPgAAAAAAAAAA/////wAAAAAGYXR0YWNoAAkB +7S8juEPqDhUbGJ7zsNbHI+chYUuVrrerDOUBuOKIVZwAAAAAAAAAAP////8AAAAA +CkNvbnRyYWN0SWQFAQAHAABAIAANQ29udHJhY3RTdGF0ZQYCBnNjaGVtYQEQQYJy +NRcc2YKVkqaZF/4DcT+tb+LTIU6Iwf4co6cKnQdoaXN0b3J5AeAa0ySHXQuDI/9G +QWcmzHlRjcQEjmf6UtqE/M2hIMYVCUV4dGVuc2lvbgYIA2ZmdgHam1ETWBZWdpCH ++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 +L2oFOkMVD6bmwizE6HrlLJT4BtQMpdyH9nWNoAd3aXRuZXNzAAQCAARub25lAAAA +AQRzb21lAAUBAcSgCp7hCQITdyIBFVk7g8NT4mD4gRDkszbK42hGQScbGU91dHB1 +dEFzc2lnbm1lbnRWb2lkU3RhdGUGBAVvcG91dAGTELyAsTRaiy/DWFLuD01o0B23 ++jXLm2SSq1YJmvSalwRzZWFsAaDIP2bjJVuxHhWqE4mveGGWqVaXKVz0ZmKNY7H/ +IPiOBXN0YXRlAS6ypf4XwDBEMJjgXJsbWmzWHu12DWHey4Am02TzFuG7B3dpdG5l +c3MABAIABG5vbmUAAAABBHNvbWUABQEBxKAKnuEJAhN3IgEVWTuDw1PiYPiBEOSz +NsrjaEZBJxsSUGVkZXJzZW5Db21taXRtZW50BQEABwAAQCEAClJhbmdlUHJvb2YE +Af8LcGxhY2Vob2xkZXIABQEBHnYX8Sd92z674WoPchG3be1V329DDVURXwN4J6VC +VycIUmVkZWVtZWQFAQAKAUbt5sMIHP245lekKlzgxgiEf/wfvl52uXF0qcr1iVnO +AZXI5noedWJf1JZVQmqR635CkKFvWpjxvlD3tookEvfFAAAAAAAAAAD/AAAAAAAA +AAxSZXNlcnZlZEJ5dGUFAQAAAQ5SZXZlYWxlZEF0dGFjaAYDAmlkAYRxDZMsTvTD +twhLaYuwh3ApfjlkJH9Fkdjag23Rfbo4CW1lZGlhVHlwZQFCMGGFiMjUqxQmQMf9 +yRcszdD/EP8Nk4AARHyImt3MeQRzYWx0AAAIDFJldmVhbGVkRGF0YQUBAAgAAEAA +AAAAAAAAAP//AAAAAAAAEFJldmVhbGVkRnVuZ2libGUGAwV2YWx1ZQGmjDCRR0vK +OsJijMeVRI0s3arFFJ8FM5Wr9jxVYQcXJghibGluZGluZwGFuPgru/Skpg2zvz9F +uA+UbniDw61SbZP0b6MBqG5H2gN0YWcByY+aqcMGSxr9/Wcbl7wq/P5MaI8fc8gt +63Fv52mbIq8GU2NoZW1hBgoDZmZ2AdqbURNYFlZ2kIf7meVWlHI2gNc5DAahzCSY +LAVk98zVCHN1YnNldE9mAAQCAARub25lAAAAAQRzb21lAAUBAAAAC2dsb2JhbFR5 +cGVzAAoB1e6SDkmIs2nxalPRB0r7tSP4x1JxHvs2PVZZBeHkFcsBx5im2GM2eEQe +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 +AAIKc3RydWN0dXJlZAAFAQAIAZy+FvstUIIP6PDf+B50LCaNVOryk7Y/2AiA4zOv +1HJMAAAAAAAAAAD//wAAAAAAAP8KYXR0YWNobWVudAAFAQAIAUuSw8nD8/xv2t0Z +E1Gb98Y4Qi0LmS6zhNkIGh3BZKcmAAAAAAAAAAD//wAAAAAAABlUeXBlZEFzc2ln +bnNCbGluZFNlYWxUeGlkBAQAC2RlY2xhcmF0aXZlAAUBAAgB/bGlv7I+F1FCfW5Q +KpRakyPNH5MfJuKapnzpG4Aw3PEAAAAAAAAAAP//AAAAAAAAAQhmdW5naWJsZQAF +AQAIAcd49g41PYTHEt9wS+ro+eh6YJiSqeM0KCn/EjimhSkYAAAAAAAAAAD//wAA +AAAAAAIKc3RydWN0dXJlZAAFAQAIAV20SWpGjdA9H0T5J/x1RYz/RiVePFbZjDZN +eMftLnoQAAAAAAAAAAD//wAAAAAAAP8KYXR0YWNobWVudAAFAQAIATB8kryIvczP +SQkuBDneEEiOoh+UMm6oskBR/yeVMOzBAAAAAAAAAAD//wAAAAAAAAlWYWxlbmNp +ZXMFAQAJAUbt5sMIHP245lekKlzgxgiEf/wfvl52uXF0qcr1iVnOAAAAAAAAAAD/ +AAAAAAAAAAtWYWxlbmN5VHlwZQUBAAACCVZvaWRTdGF0ZQUBAAAADVdpdG5lc3NB +bmNob3IGAgp3aXRuZXNzT3JkAcIe7NwA077i648Cm3I6+7EQwDaX6c8DaBmUFaYE +B2nKCXdpdG5lc3NJZAHEoAqe4QkCE3ciARVZO4PDU+Jg+IEQ5LM2yuNoRkEnGwlX +aXRuZXNzSWQEAgAHYml0Y29pbgAFAQL1bBNiI/Y5p0oJk9xHRsn5iqu4g1hdtdkW +Pxh+xCgaCqOCQvPL19HQoRLajeFgL1bU+G8OxMR2xcBoWUxLBGVWAQZsaXF1aWQA +BQEC9WwTYiP2OadKCZPcR0bJ+YqruINYXbXZFj8YfsQoGgqjgkLzy9fR0KES2o3h +YC9W1PhvDsTEdsXAaFlMSwRlVgpXaXRuZXNzT3JkBAIAB29uQ2hhaW4ABQEBDr+4 +7ThibqSDujTzFPlUdelW2Uc1E9wnGBY8Y7bhibEBCG9mZkNoYWluAAAACldpdG5l +c3NQb3MGAgZoZWlnaHQAAAQJdGltZXN0YW1wAABIB1hBbmNob3IEAgAHYml0Y29p +bgAFAQEwgEw6iTEH29grmqM+pjbEpOsNCUKUYovL9AfXtL+NjQEGbGlxdWlkAAUB +ATCATDqJMQfb2Cuaoz6mNsSk6w0JQpRii8v0B9e0v42NE1hTZWFsQmxpbmRTZWFs VHhQdHIEAgAHYml0Y29pbgAFAQJBD/d2QIYSU77AjC52Xwu6XWojk0H9GfxAPSJf iKVy+361+DN8kapvuL2Vu7vL9eHFJYlF0hF22h7U3IzFMSVlAQZsaXF1aWQABQEC QQ/3dkCGElO+wIwudl8Lul1qI5NB/Rn8QD0iX4ilcvt+tfgzfJGqb7i9lbu7y/Xh -xSWJRdIRdtoe1NyMxTElZRNYY2hhaW5CbGluZFNlYWxUeGlkBAIAB2JpdGNvaW4A -BQECQQ/3dkCGElO+wIwudl8Lul1qI5NB/Rn8QD0iX4ilcvsMUGySSZxY8y8u7CxU -uNcWa7yYFOw+mQJFPXEuumEJ4QEGbGlxdWlkAAUBAkEP93ZAhhJTvsCMLnZfC7pd -aiOTQf0Z/EA9Il+IpXL7DFBskkmcWPMvLuwsVLjXFmu8mBTsPpkCRT1xLrphCeES -WGNoYWluRXhwbGljaXRTZWFsBAIAB2JpdGNvaW4ABQECQQ/3dkCGElO+wIwudl8L -ul1qI5NB/Rn8QD0iX4ilcvtYhY59BZJX7QUNSLVm+wjlig7dqrMOFqHvGK5jmQRP -sgEGbGlxdWlkAAUBAkEP93ZAhhJTvsCMLnZfC7pdaiOTQf0Z/EA9Il+IpXL7WIWO -fQWSV+0FDUi1ZvsI5YoO3aqzDhah7xiuY5kET7I= +xSWJRdIRdtoe1NyMxTElZRJYU2VhbEJsaW5kU2VhbFR4aWQEAgAHYml0Y29pbgAF +AQJBD/d2QIYSU77AjC52Xwu6XWojk0H9GfxAPSJfiKVy+wxQbJJJnFjzLy7sLFS4 +1xZrvJgU7D6ZAkU9cS66YQnhAQZsaXF1aWQABQECQQ/3dkCGElO+wIwudl8Lul1q +I5NB/Rn8QD0iX4ilcvsMUGySSZxY8y8u7CxUuNcWa7yYFOw+mQJFPXEuumEJ4RFY +U2VhbEV4cGxpY2l0U2VhbAQCAAdiaXRjb2luAAUBAkEP93ZAhhJTvsCMLnZfC7pd +aiOTQf0Z/EA9Il+IpXL7WIWOfQWSV+0FDUi1ZvsI5YoO3aqzDhah7xiuY5kET7IB +BmxpcXVpZAAFAQJBD/d2QIYSU77AjC52Xwu6XWojk0H9GfxAPSJfiKVy+1iFjn0F +klftBQ1ItWb7COWKDt2qsw4Woe8YrmOZBE+y -----END STRICT TYPE LIB----- diff --git a/stl/RGB@0.1.0.stl b/stl/RGB@0.1.0.stl index 3d9ed9cf817f726404a6de0575b40e50d1a75ad7..4eb1c2709fb7648f2ddf6e546e36df96ed7ad5e8 100644 GIT binary patch delta 1687 zcmaD`^}1?9HRtBLoFR-JGmi#{7jIi9D16~&)eBKciw|nz4bd~BeU@)M!_jQ;uZ%6J zG%qD5l~KsTHB>m)pzVO@B(G?%=BM-5n-?VZX2>xAUbIT)+wy|RmE0ErAuCY3L zTU~nP{e3YRzE$$kpWVxEbhm|GU&j7y*RILle0vBhI?Ja%IhS{zLfEE7J$6}#KI+Sq zuvhH+@vT3T<-%e4P5o65mj3)VA>kL;G=hpirup&jC2Xy>01<9mE1*7EPjC-mw}DI} z=r)jPyu$kkyY00w5pK&BQJ?%yWG`WBL8cLO8_2YCV!H^tO;J*P@_F&8#3@`Yv6HaX zFcS&75u~tPDnudsqJVAS&xd!upYU^^`f`t}v4X-i3D=48)18)tNY5*ZnfzPIUtz8I zrsAk*uZv%w)l5vc6`L@3%A6H@|EpNCO`m$YQ=@C+<}7JRrq}~BoexgF$CJ@|>D_V0 zW9L)d-=(Xj*f_q}`e{nFPk{bT1}OLs1Ry47T4`Q-W>QXSF#{*#u}4q(J{9`E?oCxS z;%lh>krv{m^k3~>P3X-zsbZZM;wL|lm57RbIN_9Zk9Eq7O?y5rTK+lYSlGtb7RnC~ z@0=96+HNnO9l8x{i6te8$r(W7-|8#xaDK%nDlIYZ^M-53m7gmndQV-qeKpTh#vPA3 zLg!2ll1o*1Aaz-#J(jy!`M;ZEy4sn_z}_P)ed)h~ZngY5yHMen=;Z5i4-{k#t;|ZA ze(XuriD@`gU-F@k@pr5Pi<8c7Zi|VV+PS?pUzd+(Ca7vMqk{J4M&)1@g#{<<(;llv zZh+$#Q);M{U35AI%>TkuRcxQW`BdD;j!`hD@8t&Ei9E3yeAz# zdHD06{9AV=g#%}QKW5>i%RN(XbIVN*DcOT5%he`_Ti7f7-MDoBCOh#!r`o&#ttnBH zmCwpgmREZ;Ygx@p=>~&4AHi-qUh$33)VAfg(ES4MS1*3Ps7jbIY2{-R4bA^T7Ry>S zCBSZq-Qt<$)_cKL-sPwIpHkPJ|8A;rHeok=%zP`3ztyV}0K3VcX40OHy=TsQa_X^I z-V^ZXTO>clC~w6khrs{pQw`o6++1b(jFE{wVsZ^J{=j>e^qi(fA&-j?kd9Y~&6@g59!?%~PwI}(BaGSS)`s6bL zdkDJ?WEw%YflOO1w2!dcI)#aF8@Gu1Oa(vnOH4}&7#DlC6? zdGFfRsh#FamK30Jsy_RaDsa$x-1_#{r+OD*g`lM0rO{1TAt)WjXtonp2uuu{ zwX`;IDg^CcBe*4Y>7iEBSG>nAwb`-v^=3GBxCIF>uXOvn!15c&JCoH-jwe1~t zOXs>zE;LtCcyuJz$1<%-O1q#&(|5_qWm(%B6AY%UXRdP2-Ld4)3h&9+ER+;9r`66| zC$6$?=Isyrm;bu=(=F+x?X=$cf9~zKteyIHF5~2{=JpDF`uoyElong=W_9|eH2K1! zx;cg)oBnOvwQbF9(UK2IVPH4eFMaa2?D=9nNJ%$ zrNC}_eSX8{S ^ ..0xff [Byte]}, entryPoints {[Byte ^ 3] -> AluVM.LibSite {- urn:ubideco:semid:8Q9NNyK2PCcjZ7U7rDGUJBhk8q37hAnWLgSizGLmr56g#mission-papa-mercy -}} --- urn:ubideco:semid:5Q2bLHrWvtLLdB1egx6BDDpwnxbNw4wgyBoC7VL9PYmq#king-scorpio-thermos -data Anchor :: bitcoin AnchorSet - | liquid AnchorSet -- urn:ubideco:semid:4GL38RVDhs4JkL5phqyF6pguwL6Av8qTUWCVNfGeWdUg#history-joel-ivory data AnchorSet :: tapret:1 BPCore.AnchorMerkleProofTapretProof {- urn:ubideco:semid:2RRuXmkGM51gpruJzNCL6mSFm7nQFrP6zXm6CmNr77uE#arcade-modest-fossil -} | opret BPCore.AnchorMerkleProofOpretProof {- urn:ubideco:semid:7mkYAw62Vi8BuyQ16qGg3bgFw8WVtTXUkJoYBv1qoNg4#tape-mental-legacy -} | dual (tapret BPCore.AnchorMerkleProofTapretProof {- urn:ubideco:semid:2RRuXmkGM51gpruJzNCL6mSFm7nQFrP6zXm6CmNr77uE#arcade-modest-fossil -}, opret BPCore.AnchorMerkleProofOpretProof {- urn:ubideco:semid:7mkYAw62Vi8BuyQ16qGg3bgFw8WVtTXUkJoYBv1qoNg4#tape-mental-legacy -}) --- urn:ubideco:semid:3wnGtf3mZhoFh3zXkSppQCDFesMZzj8dxmXCcEf7EgPP#picture-service-index -data AnchoredBundle :: anchor Anchor, bundle TransitionBundle +-- urn:ubideco:semid:Af6Yix94PXQE73AAR55UvXUsSZgARZEnAZGiJYeo987E#cowboy-gallop-miller +data AnchoredBundle :: anchor XAnchor, bundle TransitionBundle -- urn:ubideco:semid:EZoxBpGenvb9UVze1zuwuEHqQJAqw2m3T8za5gbX1JZk#buzzer-pattern-craft data AssetTag :: [Byte ^ 32] --- urn:ubideco:semid:5Q2gTbSdQZDn4BBabk7121ogbCdbsqVeiMnZiHKka4Zd#process-canal-lopez +-- urn:ubideco:semid:FoWWT72VexCqqTZe4BTUWd3rGFmdgmjgzG4BtEXAhrFG#clark-yoga-needle data AssignRevealedAttachBlindSealTxPtr :: confidential (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state ConcealedAttach) - | confidentialState (seal XchainBlindSealTxPtr, state ConcealedAttach) + | confidentialState (seal XSealBlindSealTxPtr, state ConcealedAttach) | confidentialSeal (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state RevealedAttach) - | revealed (seal XchainBlindSealTxPtr, state RevealedAttach) --- urn:ubideco:semid:EPQDTH5KJx4reXGKDUYdJkQU2qpCdHse4nokD8TrqP1H#danube-plate-drink + | revealed (seal XSealBlindSealTxPtr, state RevealedAttach) +-- urn:ubideco:semid:9sXC96VNRB6EjYqvNHf621Tc34As2ym7hPZYzGocZcjv#british-castro-cannon data AssignRevealedAttachBlindSealTxid :: confidential (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state ConcealedAttach) - | confidentialState (seal XchainBlindSealTxid, state ConcealedAttach) + | confidentialState (seal XSealBlindSealTxid, state ConcealedAttach) | confidentialSeal (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state RevealedAttach) - | revealed (seal XchainBlindSealTxid, state RevealedAttach) --- urn:ubideco:semid:7krjkF3hTEpEaVND3WMb6R82RJQXDJdQH5zYmwTrVB1y#climax-ballet-harvard + | revealed (seal XSealBlindSealTxid, state RevealedAttach) +-- urn:ubideco:semid:8TYdgvgzXVeSDtSx4N4NBFaZX6yseoN8wdu2UmjyG4kf#brain-echo-chant data AssignRevealedDataBlindSealTxPtr :: confidential (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state ConcealedData) - | confidentialState (seal XchainBlindSealTxPtr, state ConcealedData) + | confidentialState (seal XSealBlindSealTxPtr, state ConcealedData) | confidentialSeal (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state RevealedData) - | revealed (seal XchainBlindSealTxPtr, state RevealedData) --- urn:ubideco:semid:3JNyNJbwW9qYFRrt92gJ3g779psW6DCcZgv59hKn9p3R#local-drama-savage + | revealed (seal XSealBlindSealTxPtr, state RevealedData) +-- urn:ubideco:semid:GBLt8m4DjojpH2tLJQdSYjCpfmYEQzMY6MDptoUiqBsU#kilo-family-minimum data AssignRevealedDataBlindSealTxid :: confidential (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state ConcealedData) - | confidentialState (seal XchainBlindSealTxid, state ConcealedData) + | confidentialState (seal XSealBlindSealTxid, state ConcealedData) | confidentialSeal (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state RevealedData) - | revealed (seal XchainBlindSealTxid, state RevealedData) --- urn:ubideco:semid:EAQsdSBXLyP13AaLsfpWtgS3efUeEz9dtUiED3bonT76#shelf-channel-agent + | revealed (seal XSealBlindSealTxid, state RevealedData) +-- urn:ubideco:semid:9C9FFDS8HCuTpseRp8R4B8XULZsWdp9JTvrmXmJjWKLX#moment-rabbit-ambient data AssignRevealedValueBlindSealTxPtr :: confidential (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state ConcealedFungible) - | confidentialState (seal XchainBlindSealTxPtr, state ConcealedFungible) + | confidentialState (seal XSealBlindSealTxPtr, state ConcealedFungible) | confidentialSeal (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state RevealedFungible) - | revealed (seal XchainBlindSealTxPtr, state RevealedFungible) --- urn:ubideco:semid:AvLXeeVAMwVbV488VtWb2oX5TrCwDcDBFDFTEeu9Uvzu#exotic-kansas-venus + | revealed (seal XSealBlindSealTxPtr, state RevealedFungible) +-- urn:ubideco:semid:CZ6L13AWVT2QFdgjzWTca9pvLEFsm3wmszKrvJ3u4JjA#cabaret-asia-flipper data AssignRevealedValueBlindSealTxid :: confidential (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state ConcealedFungible) - | confidentialState (seal XchainBlindSealTxid, state ConcealedFungible) + | confidentialState (seal XSealBlindSealTxid, state ConcealedFungible) | confidentialSeal (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state RevealedFungible) - | revealed (seal XchainBlindSealTxid, state RevealedFungible) --- urn:ubideco:semid:D6VJLrKLhXibehWpntHuNSceXS8355ivWtF1xkCDJ3i1#western-neuron-harris + | revealed (seal XSealBlindSealTxid, state RevealedFungible) +-- urn:ubideco:semid:8V73NTKvhVbbofS9Ne2KmDKC4ZjnWxK5iGaavvmMQWkU#uncle-smoke-bishop data AssignVoidStateBlindSealTxPtr :: confidential (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state VoidState) - | confidentialState (seal XchainBlindSealTxPtr, state VoidState) + | confidentialState (seal XSealBlindSealTxPtr, state VoidState) | confidentialSeal (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state VoidState) - | revealed (seal XchainBlindSealTxPtr, state VoidState) --- urn:ubideco:semid:FRJRWe9x1NsTGTMnarxjr1zW8CgSQRWuXbZkhwdmVLZS#mambo-credit-sweden + | revealed (seal XSealBlindSealTxPtr, state VoidState) +-- urn:ubideco:semid:FSUnkjzFs2NyU91Q5mfqQ46eLdjmi3m4eh8afpc39ScM#blue-general-sting data AssignVoidStateBlindSealTxid :: confidential (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state VoidState) - | confidentialState (seal XchainBlindSealTxid, state VoidState) + | confidentialState (seal XSealBlindSealTxid, state VoidState) | confidentialSeal (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state VoidState) - | revealed (seal XchainBlindSealTxid, state VoidState) + | revealed (seal XSealBlindSealTxid, state VoidState) -- urn:ubideco:semid:A9sThAqgwKPfuJcR4GDfTQHUAbbS5sbEXG5XVk7FZHEg#hunter-hello-retro data AssignmentType :: U16 -- urn:ubideco:semid:Bu8iXz4MmJnuUMW6AFAepa8BAa4DXLVAMdrNrkLh3S7a#granite-history-canvas @@ -223,24 +220,24 @@ data OpId :: [Byte ^ 32] data Opout :: op OpId , ty AssignmentType , no U16 --- urn:ubideco:semid:CiYicmKTiHc9cFEQvpEY3zWDi13ydMznRmsEdfPa1e8x#potato-gate-liter +-- urn:ubideco:semid:GNcKtjWdcEiXtDX9RQuR8zwNB845PScyvgjzuCvy5Dsu#algebra-pardon-othello data OutputAssignmentRevealedAttach :: opout Opout - , seal XchainExplicitSeal + , seal XSealExplicitSeal , state RevealedAttach , witness WitnessId? --- urn:ubideco:semid:5ArcjNeF6iWBssN3oayXy9d8Z5iWH9juBhCAe28QMi29#safari-amazon-radar +-- urn:ubideco:semid:3PB4sX4TDJMyLTk5yzm7DQbWhpcMzoHDriV3dYZd9VcL#alibi-credit-spain data OutputAssignmentRevealedData :: opout Opout - , seal XchainExplicitSeal + , seal XSealExplicitSeal , state RevealedData , witness WitnessId? --- urn:ubideco:semid:51tRLWfJzEeYeDCVn8DdrgERPPNSaew2fKxb3zeBsZat#guide-organic-human +-- urn:ubideco:semid:J1nZUtrWPX18FyXxDcRvyZK8tv2BKLJN6G8riUETUKNm#mars-scholar-potato data OutputAssignmentRevealedValue :: opout Opout - , seal XchainExplicitSeal + , seal XSealExplicitSeal , state RevealedFungible , witness WitnessId? --- urn:ubideco:semid:6dhzNQ2P4zizD6DK9CMbR5Tejj1hYtxj85wPPPueCdFQ#door-donald-welcome +-- urn:ubideco:semid:9tNXvuRwNLhtMduizsdKtiUhLSewRmwRL7knBegcQ7o4#sunday-random-vincent data OutputAssignmentVoidState :: opout Opout - , seal XchainExplicitSeal + , seal XSealExplicitSeal , state VoidState , witness WitnessId? -- urn:ubideco:semid:5twbh2U5hyaowidwum1iRNCqebBLxTuZTuNPt3SaRT13#nepal-delta-earth @@ -337,13 +334,16 @@ data WitnessOrd :: onChain WitnessPos | offChain () -- urn:ubideco:semid:zaGYZruf2pxiZqk1bjmoivxK8DkFycoyjQB52uHYhGL#robert-average-artist data WitnessPos :: height U32, timestamp I64 --- urn:ubideco:semid:3UQZiL3kChuaakzDdyt8Y2sFAVcxFVaJyTtjNC49D4Df#diamond-order-martin -data XchainBlindSealTxPtr :: bitcoin BPCore.BlindSealTxPtr {- urn:ubideco:semid:9XdJg1BFMpMXPfaiw4Te79W2qYgArsEye6XPJUtj31L8#metro-chris-olympic -} +-- urn:ubideco:semid:6WtKj1L8L7zpomz5ExJnV6wBrsbtLnshuJLrkCids7ky#camera-kilo-morning +data XAnchor :: bitcoin AnchorSet + | liquid AnchorSet +-- urn:ubideco:semid:3WvEzjPwR7ka5PD6vCQR5ZJ9hc3K763GsaXJUD9TLFjf#pandora-finance-tomato +data XSealBlindSealTxPtr :: bitcoin BPCore.BlindSealTxPtr {- urn:ubideco:semid:9XdJg1BFMpMXPfaiw4Te79W2qYgArsEye6XPJUtj31L8#metro-chris-olympic -} | liquid BPCore.BlindSealTxPtr {- urn:ubideco:semid:9XdJg1BFMpMXPfaiw4Te79W2qYgArsEye6XPJUtj31L8#metro-chris-olympic -} --- urn:ubideco:semid:DywBwpGAdBpDeSvLqEP2Pvt5mb84r5baE1xDKkcEujBL#portal-kimono-jungle -data XchainBlindSealTxid :: bitcoin BPCore.BlindSealTxid {- urn:ubideco:semid:q529pAPHhD1aFgueAHy8QtfjUayszR85WgEg7s2a3KE#raymond-reply-phrase -} +-- urn:ubideco:semid:6qS3mM1bNePt2RZ8nR2Fzhj76sv3prarRThHcZDHD67B#nebula-civil-filter +data XSealBlindSealTxid :: bitcoin BPCore.BlindSealTxid {- urn:ubideco:semid:q529pAPHhD1aFgueAHy8QtfjUayszR85WgEg7s2a3KE#raymond-reply-phrase -} | liquid BPCore.BlindSealTxid {- urn:ubideco:semid:q529pAPHhD1aFgueAHy8QtfjUayszR85WgEg7s2a3KE#raymond-reply-phrase -} --- urn:ubideco:semid:WTenE41RzjcDoZ6DUhdrVV9yYoiVRrG72prS64ePDqP#prefix-halt-service -data XchainExplicitSeal :: bitcoin BPCore.ExplicitSeal {- urn:ubideco:semid:6xYzjyQ958BPRsJgBXL4N2DhswvgHWLp769Uzo4ybbUV#turtle-phoenix-panda -} +-- urn:ubideco:semid:BpdLEoqRHDNSVP2Xe3PkYQfQoJAKfua19i2YUg9RYxdK#photo-english-format +data XSealExplicitSeal :: bitcoin BPCore.ExplicitSeal {- urn:ubideco:semid:6xYzjyQ958BPRsJgBXL4N2DhswvgHWLp769Uzo4ybbUV#turtle-phoenix-panda -} | liquid BPCore.ExplicitSeal {- urn:ubideco:semid:6xYzjyQ958BPRsJgBXL4N2DhswvgHWLp769Uzo4ybbUV#turtle-phoenix-panda -} From 1b4fbcd3746aa9873ef59b7d189d03a85f88a17a Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Mon, 25 Dec 2023 20:05:31 +0100 Subject: [PATCH 20/20] validation: fix excessive validation errors --- src/validation/status.rs | 2 +- src/validation/validator.rs | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/validation/status.rs b/src/validation/status.rs index 33499a1c..9978a1c9 100644 --- a/src/validation/status.rs +++ b/src/validation/status.rs @@ -345,7 +345,7 @@ pub enum Failure { /// {1}. Details: {2} SealsInvalid(BundleId, Txid, String), /// single-use seals for the operation {0} were not validated, which - /// probably indicateds unanchored state transition. + /// probably indicates unanchored state transition. SealsUnvalidated(OpId), /// transition bundle {0} is not properly anchored to the witness /// transaction {1}. Details: {2} diff --git a/src/validation/validator.rs b/src/validation/validator.rs index 2778b350..a228352e 100644 --- a/src/validation/validator.rs +++ b/src/validation/validator.rs @@ -32,9 +32,9 @@ use super::status::{Failure, Warning}; use super::{CheckedConsignment, ConsignmentApi, Status, Validity, VirtualMachine}; use crate::vm::AluRuntime; use crate::{ - AltLayer1, AnchorSet, BundleId, ContractId, Layer1, OpId, OpRef, Operation, Opout, Schema, - SchemaId, SchemaRoot, Script, SubSchema, Transition, TransitionBundle, TypedAssigns, WitnessId, - XAnchor, + AltLayer1, AnchorSet, BundleId, ContractId, Layer1, OpId, OpRef, OpType, Operation, Opout, + Schema, SchemaId, SchemaRoot, Script, SubSchema, Transition, TransitionBundle, TypedAssigns, + WitnessId, XAnchor, }; #[derive(Clone, Debug, Display, Error, From)] @@ -243,13 +243,15 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> continue; } - if !self.validated_op_seals.contains(&opid) { + if !self.validated_op_seals.contains(&opid) && + operation.op_type() == OpType::StateTransition + { self.status.add_failure(Failure::SealsUnvalidated(opid)); } // [VALIDATION]: Verify operation against the schema and scripts - self.status += schema.validate_state(&self.consignment, operation, self.vm.as_ref()); - if !self.validated_op_state.insert(opid) { - self.status.add_failure(Failure::CyclicGraph(opid)); + if self.validated_op_state.insert(opid) { + self.status += + schema.validate_state(&self.consignment, operation, self.vm.as_ref()); } match operation {