From 959497c382097f711b2cba0e87963cd8fcc77212 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 29 Dec 2023 14:39:38 +0100 Subject: [PATCH 01/11] chore: update BP Core with support for multi-method seals/anchors --- Cargo.lock | 40 ++++++++++++++++------------------------ Cargo.toml | 8 ++++++++ src/contract/seal.rs | 6 +++--- src/stl.rs | 2 +- src/validation/status.rs | 12 ++++++------ 5 files changed, 34 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index da993cf9..b479b6d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -196,8 +196,7 @@ dependencies = [ [[package]] name = "bp-consensus" version = "0.11.0-beta.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190ac89a2a3c79d5bfb677f48e5393691832c540973341831572edaffdce0881" +source = "git+https://github.com/BP-WG/bp-core?branch=v0.11#e30a05fae8774d722da70673d7428aee07410abc" dependencies = [ "amplify", "chrono", @@ -211,8 +210,7 @@ dependencies = [ [[package]] name = "bp-core" version = "0.11.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0143f6c7399cb6d0003407e6de197a03b3b42b34380fab02c8fecaf4431b061" +source = "git+https://github.com/BP-WG/bp-core?branch=v0.11#e30a05fae8774d722da70673d7428aee07410abc" dependencies = [ "amplify", "bp-consensus", @@ -228,8 +226,7 @@ dependencies = [ [[package]] name = "bp-dbc" version = "0.11.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e3e04649c77079cfd1466ba14a62c83585053bb61d294578a39fcfe38f402db" +source = "git+https://github.com/BP-WG/bp-core?branch=v0.11#e30a05fae8774d722da70673d7428aee07410abc" dependencies = [ "amplify", "base85", @@ -243,8 +240,7 @@ dependencies = [ [[package]] name = "bp-seals" version = "0.11.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e4955315fad858472320ee5a7fba8a35c61c3928bdacb581931e23d4f25c7b" +source = "git+https://github.com/BP-WG/bp-core?branch=v0.11#e30a05fae8774d722da70673d7428aee07410abc" dependencies = [ "amplify", "baid58", @@ -301,8 +297,7 @@ dependencies = [ [[package]] name = "commit_encoding_derive" version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00033f14d67c4169d588f085ea2faeb7b610cf03a74d42ea09eeba31abef2047" +source = "git+https://github.com/LNP-BP/client_side_validation?branch=v0.11#2d3a0a2981409c493067edfc94338088884b1aa7" dependencies = [ "amplify", "amplify_syn", @@ -314,8 +309,7 @@ dependencies = [ [[package]] name = "commit_verify" version = "0.11.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5598661b1d90b149f0b944faef8c1d1d131f847678a6c450b9540b629ac11291" +source = "git+https://github.com/LNP-BP/client_side_validation?branch=v0.11#2d3a0a2981409c493067edfc94338088884b1aa7" dependencies = [ "amplify", "commit_encoding_derive", @@ -502,9 +496,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "mime" @@ -788,8 +782,7 @@ dependencies = [ [[package]] name = "strict_encoding" version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab7b75b4af0aff9dd97b68df262bf0e807b7d007cc860fa217943f898a05a5ab" +source = "git+https://github.com/strict-types/strict-encoding?branch=phantom#2123237a512bbe28e8e419e7d4f899dfedfa758c" dependencies = [ "amplify", "half", @@ -800,8 +793,7 @@ dependencies = [ [[package]] name = "strict_encoding_derive" version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37064ec285e2a633465eb525c8698eea51373dee889fe310e0d32df8343e7f4f" +source = "git+https://github.com/strict-types/strict-encoding?branch=phantom#2123237a512bbe28e8e419e7d4f899dfedfa758c" dependencies = [ "amplify_syn", "heck", @@ -863,18 +855,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.51" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +checksum = "83a48fd946b02c0a526b2e9481c8e2a17755e47039164a86c4070446e3a4614d" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.51" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +checksum = "e7fbe9b594d6568a6a1443250a7e67d80b74e1e96f6d1715e1e21cc1888291d3" dependencies = [ "proc-macro2", "quote", @@ -1114,9 +1106,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.5.30" +version = "0.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b5c3db89721d50d0e2a673f5043fc4722f76dcc352d7b1ab8b8288bed4ed2c5" +checksum = "97a4882e6b134d6c28953a387571f1acdd3496830d5e36c5e3a1075580ea641c" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index e2116a13..408c05f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,3 +59,11 @@ wasm-bindgen-test = "0.3" [package.metadata.docs.rs] features = [ "all" ] + +[patch.crates-io] +strict_encoding = { git = "https://github.com/strict-types/strict-encoding", branch = "phantom" } +commit_verify = { git = "https://github.com/LNP-BP/client_side_validation", branch = "v0.11" } +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" } diff --git a/src/contract/seal.rs b/src/contract/seal.rs index 77c93bde..df9327eb 100644 --- a/src/contract/seal.rs +++ b/src/contract/seal.rs @@ -27,11 +27,11 @@ use std::hash::Hash; use std::num::NonZeroU32; use std::str::FromStr; -pub use bp::seals::txout::blind::{ - ChainBlindSeal as GraphSeal, ParseError, SecretSeal, SingleBlindSeal as GenesisSeal, -}; +use bp::dbc::Method; +pub use bp::seals::txout::blind::{ChainBlindSeal, ParseError, SingleBlindSeal}; pub use bp::seals::txout::TxoSeal; use bp::seals::txout::{CloseMethod, ExplicitSeal, SealTxid}; +pub use bp::seals::SecretSeal; use bp::Txid; use commit_verify::{strategies, CommitVerify, Conceal, DigestExt, Sha256, UntaggedProtocol}; use strict_encoding::{StrictDecode, StrictDumb, StrictEncode, StrictType, StrictWriter}; diff --git a/src/stl.rs b/src/stl.rs index 2b68ec18..ce1d5edf 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:EZiZRCHpyqJakmTU1zkwczq5YKMbapfMhVLhS8DbkpwC#premium-honey-scarlet"; + "urn:ubideco:stl:5d7k62t7JyjhbVvTNJ9xbSoQWG1xkXPoiYViK69TdMxe#karate-arsenal-spirit"; 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 9978a1c9..19403dc5 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::seals::txout::blind::ChainBlindSeal; use bp::Txid; use commit_verify::mpc::InvalidProof; use strict_types::SemId; @@ -32,8 +31,8 @@ use strict_types::SemId; use crate::contract::Opout; use crate::schema::{self, SchemaId}; use crate::{ - BundleId, ContractId, Layer1, OccurrencesMismatch, OpFullType, OpId, SecretSeal, StateType, - Vin, XSeal, + BundleId, ContractId, GraphSeal, Layer1, OccurrencesMismatch, OpFullType, OpId, SecretSeal, + StateType, Vin, XSeal, }; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Display)] @@ -340,7 +339,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, XSeal), + SealLayerMismatch(Layer1, XSeal), /// transition bundle {0} doesn't close seal with the witness transaction /// {1}. Details: {2} SealsInvalid(BundleId, Txid, String), @@ -410,8 +409,9 @@ pub enum Failure { )] #[display(doc_comments)] pub enum Warning { - /// terminal seal {1} referencing operation {0} is not present in operation - /// assignments. + // TODO: Replace debug with display + /// terminal seal {1:?} referencing operation {0} is not present in + /// operation assignments. TerminalSealAbsent(OpId, SecretSeal), /// terminal witness transaction {0} is not yet mined. TerminalWitnessNotMined(Txid), From 35c22ab5b3489de98fe34dd9cc57683083a72eb4 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 29 Dec 2023 14:43:20 +0100 Subject: [PATCH 02/11] contract: introduce XChain generic wrapper --- src/contract/mod.rs | 40 +++++++++++++++++++++ src/contract/seal.rs | 86 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) diff --git a/src/contract/mod.rs b/src/contract/mod.rs index 1c0e0f33..02f07812 100644 --- a/src/contract/mod.rs +++ b/src/contract/mod.rs @@ -42,6 +42,7 @@ pub use assignments::{ TypedAssigns, }; pub use attachment::{AttachId, ConcealedAttach, RevealedAttach}; +use bp::Bp; pub use bundle::{BundleId, TransitionBundle, Vin}; use commit_verify::CommitEncode; pub use contract::{ @@ -63,6 +64,7 @@ pub use seal::{ WitnessPos, XSeal, XchainParseError, }; pub use state::{ConfidentialState, ExposedState, StateCommitment, StateData, StateType}; +use strict_encoding::{StrictDecode, StrictDumb, StrictEncode}; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] #[display(lowercase)] @@ -108,3 +110,41 @@ impl CommitEncode for AltLayer1Set { } } } + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = super::LIB_NAME_RGB, tags = custom, dumb = Self::Bitcoin(strict_dumb!()))] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", rename_all = "camelCase") +)] +pub enum Xchain +where T: StrictDumb + StrictEncode + StrictDecode +{ + #[strict_type(tag = 0x00)] + Bitcoin(T), + + #[strict_type(tag = 0x01)] + Liquid(T), +} + +impl Xchain { + pub fn is_bitcoin(&self) -> bool { matches!(self, Xchain::Bitcoin(_)) } + pub fn is_liquid(&self) -> bool { matches!(self, Xchain::Liquid(_)) } + pub fn is_bp(&self) -> bool { + match self { + Xchain::Bitcoin(_) | Xchain::Liquid(_) => true, + } + } + pub fn into_bp(self) -> Option> { + Some(match self { + Xchain::Bitcoin(t) => Bp::Bitcoin(t), + Xchain::Liquid(t) => Bp::Liquid(t), + }) + } + pub fn unwrap_into_bp(self) -> Bp { + self.into_bp() + .expect("only Bitcoin and Liquid chains are supported at this moment") + } +} diff --git a/src/contract/seal.rs b/src/contract/seal.rs index df9327eb..50450c3a 100644 --- a/src/contract/seal.rs +++ b/src/contract/seal.rs @@ -38,6 +38,92 @@ use strict_encoding::{StrictDecode, StrictDumb, StrictEncode, StrictType, Strict use crate::{Layer1, LIB_NAME_RGB}; +pub type GenesisSeal = SingleBlindSeal; +pub type GraphSeal = ChainBlindSeal; + +/* +impl ToBaid58<32> for SecretSeal { + const HRI: &'static str = "utxob"; + const CHUNKING: Option = CHUNKING_32CHECKSUM; + fn to_baid58_payload(&self) -> [u8; 32] { self.0.into_inner() } + fn to_baid58_string(&self) -> String { self.to_string() } +} +impl FromBaid58<32> for SecretSeal {} +impl FromStr for SecretSeal { + type Err = Baid58ParseError; + fn from_str(s: &str) -> Result { + SecretSeal::from_baid58_maybe_chunked_str(s, ':', ' ') + } +} +impl Display for SecretSeal { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if f.alternate() { + write!(f, "{::^}", self.to_baid58()) + } else { + write!(f, "{::^.3}", self.to_baid58()) + } + } +} + +impl CommitVerify, TapretFirst> for SecretSeal { + fn commit(reveal: &BlindSeal) -> Self { Bytes32::commit(reveal).into() } +} + +impl Conceal for BlindSeal { + type Concealed = SecretSeal; + + #[inline] + fn conceal(&self) -> Self::Concealed { SecretSeal::commit(self) } +} + +impl BlindSeal { + /// Converts revealed seal into concealed. + #[inline] + pub fn to_concealed_seal(&self) -> SecretSeal { self.conceal() } +} + +mod test { + use super::*; + + #[test] + fn secret_seal_is_sha256d() { + let reveal = BlindSeal { + method: CloseMethod::TapretFirst, + blinding: 54683213134637, + txid: TxPtr::Txid( + Txid::from_hex("646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839") + .unwrap(), + ), + vout: Vout::from(2), + }; + assert_eq!(reveal.to_concealed_seal(), reveal.conceal()) + } + + #[test] + fn secret_seal_baid58() { + let seal = BlindSeal { + method: CloseMethod::TapretFirst, + blinding: 54683213134637, + txid: TxPtr::Txid( + Txid::from_hex("646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839") + .unwrap(), + ), + vout: Vout::from(2), + } + .to_concealed_seal(); + + let baid58 = "utxob:2eFrirU-RjqLnqR74-AKRfdnc9M-DpvSRjmZG-mFPrw7nvu-Te1wy83"; + assert_eq!(baid58, seal.to_string()); + assert_eq!(baid58.replace('-', ""), format!("{seal:#}")); + assert_eq!(seal.to_string(), seal.to_baid58_string()); + let reconstructed = SecretSeal::from_str(baid58).unwrap(); + assert_eq!(reconstructed, seal); + let reconstructed = SecretSeal::from_str(&baid58.replace('-', "")).unwrap(); + assert_eq!(reconstructed, seal); + } +} + */ + pub trait ExposedSeal: Debug + StrictDumb + StrictEncode + StrictDecode + Eq + Ord + Copy + Hash + TxoSeal { From 77da743989d75aee246d3abde21a3da18f221604 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 29 Dec 2023 14:48:08 +0100 Subject: [PATCH 03/11] contract: replace XSeal with XChain --- src/contract/mod.rs | 70 ++++++++++++++++++++++++++++++++++++++------ src/contract/seal.rs | 67 ++---------------------------------------- 2 files changed, 63 insertions(+), 74 deletions(-) diff --git a/src/contract/mod.rs b/src/contract/mod.rs index 02f07812..9a8eee6d 100644 --- a/src/contract/mod.rs +++ b/src/contract/mod.rs @@ -33,7 +33,10 @@ mod bundle; #[allow(clippy::module_inception)] mod contract; +use std::fmt; +use std::fmt::{Debug, Display, Formatter}; use std::io::Write; +use std::str::FromStr; use amplify::confinement::TinyOrdSet; pub use anchor::{AnchorSet, AnchoredBundle, Layer1, WitnessAnchor, XAnchor}; @@ -61,7 +64,7 @@ pub use operations::{ }; pub use seal::{ ExposedSeal, GenesisSeal, GraphSeal, OutputSeal, SecretSeal, TxoSeal, WitnessId, WitnessOrd, - WitnessPos, XSeal, XchainParseError, + WitnessPos, XSeal, }; pub use state::{ConfidentialState, ExposedState, StateCommitment, StateData, StateType}; use strict_encoding::{StrictDecode, StrictDumb, StrictEncode}; @@ -111,7 +114,7 @@ impl CommitEncode for AltLayer1Set { } } -#[derive(Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = super::LIB_NAME_RGB, tags = custom, dumb = Self::Bitcoin(strict_dumb!()))] #[cfg_attr( @@ -119,7 +122,7 @@ impl CommitEncode for AltLayer1Set { derive(Serialize, Deserialize), serde(crate = "serde_crate", rename_all = "camelCase") )] -pub enum Xchain +pub enum XChain where T: StrictDumb + StrictEncode + StrictDecode { #[strict_type(tag = 0x00)] @@ -129,18 +132,18 @@ where T: StrictDumb + StrictEncode + StrictDecode Liquid(T), } -impl Xchain { - pub fn is_bitcoin(&self) -> bool { matches!(self, Xchain::Bitcoin(_)) } - pub fn is_liquid(&self) -> bool { matches!(self, Xchain::Liquid(_)) } +impl XChain { + pub fn is_bitcoin(&self) -> bool { matches!(self, XChain::Bitcoin(_)) } + pub fn is_liquid(&self) -> bool { matches!(self, XChain::Liquid(_)) } pub fn is_bp(&self) -> bool { match self { - Xchain::Bitcoin(_) | Xchain::Liquid(_) => true, + XChain::Bitcoin(_) | XChain::Liquid(_) => true, } } pub fn into_bp(self) -> Option> { Some(match self { - Xchain::Bitcoin(t) => Bp::Bitcoin(t), - Xchain::Liquid(t) => Bp::Liquid(t), + XChain::Bitcoin(t) => Bp::Bitcoin(t), + XChain::Liquid(t) => Bp::Liquid(t), }) } pub fn unwrap_into_bp(self) -> Bp { @@ -148,3 +151,52 @@ impl Xchain { .expect("only Bitcoin and Liquid chains are supported at this moment") } } + +#[derive(Clone, Debug, Display, Error, From)] +pub enum XChainParseError { + #[display("unknown chain prefix '{0}'; only 'bitcoin:' and 'liquid:' are currently supported")] + UnknownPrefix(String), + + #[from] + #[display(inner)] + Inner(E), +} + +impl FromStr for XChain +where + T: StrictDumb + StrictEncode + StrictDecode, + T::Err: Debug + Display, +{ + type Err = XChainParseError; + + fn from_str(s: &str) -> Result { + if let Some((prefix, s)) = s.split_once(':') { + match prefix { + "bitcoin" => s + .parse() + .map(XChain::Bitcoin) + .map_err(XChainParseError::from), + "liquid" => s + .parse() + .map(XChain::Liquid) + .map_err(XChainParseError::from), + unknown => Err(XChainParseError::UnknownPrefix(unknown.to_owned())), + } + } else { + s.parse() + .map(XChain::Bitcoin) + .map_err(XChainParseError::from) + } + } +} + +impl Display for XChain +where T: StrictDumb + StrictEncode + StrictDecode +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + XChain::Bitcoin(t) => write!(f, "bitcoin:{t}"), + XChain::Liquid(t) => write!(f, "liquid:{t}"), + } + } +} diff --git a/src/contract/seal.rs b/src/contract/seal.rs index 50450c3a..643770e7 100644 --- a/src/contract/seal.rs +++ b/src/contract/seal.rs @@ -22,10 +22,8 @@ use core::fmt::Debug; use std::cmp::Ordering; -use std::fmt::{self, Display, Formatter}; use std::hash::Hash; use std::num::NonZeroU32; -use std::str::FromStr; use bp::dbc::Method; pub use bp::seals::txout::blind::{ChainBlindSeal, ParseError, SingleBlindSeal}; @@ -36,7 +34,7 @@ use bp::Txid; use commit_verify::{strategies, CommitVerify, Conceal, DigestExt, Sha256, UntaggedProtocol}; use strict_encoding::{StrictDecode, StrictDumb, StrictEncode, StrictType, StrictWriter}; -use crate::{Layer1, LIB_NAME_RGB}; +use crate::{Layer1, XChain, LIB_NAME_RGB}; pub type GenesisSeal = SingleBlindSeal; pub type GraphSeal = ChainBlindSeal; @@ -149,26 +147,7 @@ pub type OutputSeal = XSeal>; pub struct SealPreimage(Bytes32); */ -#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB, tags = custom, dumb = Self::Bitcoin(strict_dumb!()))] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub enum XSeal { - #[strict_type(tag = 0x00)] - Bitcoin(U), - #[strict_type(tag = 0x01)] - Liquid(U), - /* - #[strict_type(tag = 0x10)] - Abraxas(SealPreimage), - #[strict_type(tag = 0x11)] - Prime(SealPreimage), - */ -} +pub type XSeal = XChain; impl Conceal for XSeal { type Concealed = SecretSeal; @@ -259,48 +238,6 @@ impl XSeal { } } -impl Display for XSeal { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - XSeal::Bitcoin(seal) => write!(f, "bitcoin:{seal}"), - XSeal::Liquid(seal) => write!(f, "liquid:{seal}"), - } - } -} - -#[derive(Clone, Debug, Display, Error, From)] -pub enum XchainParseError { - #[display("unknown seal prefix '{0}'; only 'bitcoin:' and 'liquid:' are currently supported")] - UnknownPrefix(String), - - #[from] - #[display(inner)] - Seal(E), -} - -impl FromStr for XSeal -where U::Err: Debug + Display -{ - type Err = XchainParseError; - - fn from_str(s: &str) -> Result { - if let Some((prefix, s)) = s.split_once(':') { - match prefix { - "bitcoin" => s - .parse() - .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(XSeal::Bitcoin) - .map_err(XchainParseError::from) - } - } -} - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Display)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB)] From 26f356099be54158233353b98b55bd41d89a4693 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 29 Dec 2023 19:51:30 +0100 Subject: [PATCH 04/11] contract: refactor cross-chain data structures - unification --- src/contract/anchor.rs | 54 +------ src/contract/contract.rs | 6 +- src/contract/mod.rs | 169 +++++++++++++++++--- src/contract/seal.rs | 306 ++++++++++++++++++++---------------- src/validation/mod.rs | 2 +- src/validation/status.rs | 50 +++--- src/validation/validator.rs | 134 ++++++++-------- 7 files changed, 424 insertions(+), 297 deletions(-) diff --git a/src/contract/anchor.rs b/src/contract/anchor.rs index 70c89b56..d70e0b08 100644 --- a/src/contract/anchor.rs +++ b/src/contract/anchor.rs @@ -29,7 +29,7 @@ use bp::Txid; use commit_verify::mpc; use strict_encoding::StrictDumb; -use crate::{BundleId, ContractId, TransitionBundle, WitnessId, WitnessOrd, LIB_NAME_RGB}; +use crate::{BundleId, ContractId, TransitionBundle, WitnessId, WitnessOrd, XChain, LIB_NAME_RGB}; #[derive(Clone, Eq, PartialEq, Debug)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] @@ -49,46 +49,14 @@ impl AnchoredBundle { 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!()))] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub enum XAnchor { - #[strict_type(tag = 0x00)] - Bitcoin(AnchorSet

), - - #[strict_type(tag = 0x01)] - Liquid(AnchorSet

), -} +pub type XAnchor

= XChain>; impl XAnchor

{ #[inline] - pub fn layer1(&self) -> Layer1 { - match self { - XAnchor::Bitcoin(_) => Layer1::Bitcoin, - XAnchor::Liquid(_) => Layer1::Liquid, - } - } - - #[inline] - pub fn witness_id(&self) -> WitnessId { - match self { - 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> { + pub fn witness_id(&self) -> Option { match self { - XAnchor::Bitcoin(anchor) => f(anchor).map(XAnchor::Bitcoin), - XAnchor::Liquid(anchor) => f(anchor).map(XAnchor::Liquid), + XAnchor::Bitcoin(anchor) => anchor.txid().map(WitnessId::Bitcoin), + XAnchor::Liquid(anchor) => anchor.txid().map(WitnessId::Liquid), } } } @@ -111,7 +79,7 @@ impl XAnchor { self, contract_id: ContractId, ) -> Result, mpc::LeafNotKnown> { - self.map(|a| a.into_merkle_proof(contract_id)) + self.try_map(|a| a.into_merkle_proof(contract_id)) } } @@ -129,7 +97,7 @@ impl XAnchor { contract_id: ContractId, bundle_id: BundleId, ) -> Result, mpc::InvalidProof> { - self.map(|a| a.into_merkle_block(contract_id, bundle_id)) + self.try_map(|a| a.into_merkle_block(contract_id, bundle_id)) } } @@ -163,14 +131,6 @@ impl AnchorSet

{ } } - pub(crate) fn txid_unchecked(&self) -> Txid { - match self { - AnchorSet::Tapret(a) => a.txid, - AnchorSet::Opret(a) => a.txid, - AnchorSet::Dual { tapret, .. } => tapret.txid, - } - } - pub fn from_split( tapret: Option>, opret: Option>, diff --git a/src/contract/contract.rs b/src/contract/contract.rs index 9cd9a1e2..d8711398 100644 --- a/src/contract/contract.rs +++ b/src/contract/contract.rs @@ -35,9 +35,9 @@ use strict_encoding::{StrictDecode, StrictDumb, StrictEncode}; use crate::{ Assign, AssignmentType, Assignments, AssignmentsRef, ContractId, ExposedSeal, ExposedState, - Extension, Genesis, GlobalStateType, OpId, Operation, OutputSeal, RevealedAttach, RevealedData, + Extension, Genesis, GlobalStateType, OpId, Operation, RevealedAttach, RevealedData, RevealedValue, SchemaId, SubSchema, Transition, TypedAssigns, VoidState, WitnessAnchor, - WitnessId, XSeal, LIB_NAME_RGB, + WitnessId, XOutputSeal, XSeal, LIB_NAME_RGB, }; #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] @@ -102,7 +102,7 @@ impl FromStr for Opout { )] pub struct OutputAssignment { pub opout: Opout, - pub seal: OutputSeal, + pub seal: XOutputSeal, pub state: State, pub witness: Option, } diff --git a/src/contract/mod.rs b/src/contract/mod.rs index 9a8eee6d..58776cb8 100644 --- a/src/contract/mod.rs +++ b/src/contract/mod.rs @@ -33,10 +33,11 @@ mod bundle; #[allow(clippy::module_inception)] mod contract; -use std::fmt; +use std::cmp::Ordering; use std::fmt::{Debug, Display, Formatter}; use std::io::Write; use std::str::FromStr; +use std::{fmt, io}; use amplify::confinement::TinyOrdSet; pub use anchor::{AnchorSet, AnchoredBundle, Layer1, WitnessAnchor, XAnchor}; @@ -64,10 +65,13 @@ pub use operations::{ }; pub use seal::{ ExposedSeal, GenesisSeal, GraphSeal, OutputSeal, SecretSeal, TxoSeal, WitnessId, WitnessOrd, - WitnessPos, XSeal, + WitnessPos, XGenesisSeal, XGraphSeal, XOutputSeal, XPubWitness, XSeal, XWitness, }; pub use state::{ConfidentialState, ExposedState, StateCommitment, StateData, StateType}; -use strict_encoding::{StrictDecode, StrictDumb, StrictEncode}; +use strict_encoding::{ + DecodeError, DefineUnion, ReadTuple, ReadUnion, StrictDecode, StrictDumb, StrictEncode, + StrictSum, StrictType, StrictUnion, TypedRead, TypedWrite, WriteUnion, +}; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] #[display(lowercase)] @@ -114,25 +118,99 @@ impl CommitEncode for AltLayer1Set { } } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = super::LIB_NAME_RGB, tags = custom, dumb = Self::Bitcoin(strict_dumb!()))] +pub const XCHAIN_BITCOIN_PREFIX: &str = "bc"; +pub const XCHAIN_LIQUID_PREFIX: &str = "lq"; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[cfg_attr( feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate", rename_all = "camelCase") )] -pub enum XChain -where T: StrictDumb + StrictEncode + StrictDecode -{ - #[strict_type(tag = 0x00)] +pub enum XChain { Bitcoin(T), - #[strict_type(tag = 0x01)] Liquid(T), } -impl XChain { +impl PartialOrd for XChain { + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } +} + +impl Ord for XChain { + fn cmp(&self, other: &Self) -> Ordering { + match (self, other) { + (Self::Bitcoin(_), Self::Liquid(_)) => Ordering::Greater, + (Self::Liquid(_), Self::Bitcoin(_)) => Ordering::Less, + (Self::Bitcoin(t1) | Self::Liquid(t1), Self::Bitcoin(t2) | Self::Liquid(t2)) => { + t1.cmp(t2) + } + } + } +} + +impl StrictType for XChain +where T: StrictDumb + StrictType +{ + const STRICT_LIB_NAME: &'static str = super::LIB_NAME_RGB; +} +impl StrictSum for XChain +where T: StrictDumb + StrictType +{ + const ALL_VARIANTS: &'static [(u8, &'static str)] = &[(0x00, "bitcoin"), (0x01, "liquid")]; + + fn variant_name(&self) -> &'static str { + match self { + XChain::Bitcoin(_) => Self::ALL_VARIANTS[0].1, + XChain::Liquid(_) => Self::ALL_VARIANTS[1].1, + } + } +} +impl StrictUnion for XChain where T: StrictDumb + StrictType {} +impl StrictDumb for XChain +where T: StrictDumb +{ + fn strict_dumb() -> Self { XChain::Bitcoin(strict_dumb!()) } +} +impl StrictEncode for XChain +where T: StrictDumb + StrictEncode +{ + fn strict_encode(&self, writer: W) -> io::Result { + writer.write_union::(|w| { + let w = w + .define_newtype::(fname!(Self::ALL_VARIANTS[0].1)) + .define_newtype::(fname!(Self::ALL_VARIANTS[1].1)) + .complete(); + Ok(match self { + XChain::Bitcoin(t) => w.write_newtype(fname!(Self::ALL_VARIANTS[0].1), t)?, + XChain::Liquid(t) => w.write_newtype(fname!(Self::ALL_VARIANTS[1].1), t)?, + } + .complete()) + }) + } +} +impl StrictDecode for XChain +where T: StrictDumb + StrictDecode +{ + fn strict_decode(reader: &mut impl TypedRead) -> Result { + reader.read_union(|field, r| match field.as_str() { + x if x == Self::ALL_VARIANTS[0].1 => { + r.read_tuple(|r| r.read_field().map(Self::Bitcoin)) + } + x if x == Self::ALL_VARIANTS[1].1 => r.read_tuple(|r| r.read_field().map(Self::Liquid)), + _ => unreachable!(), + }) + } +} + +impl XChain { + pub fn with(layer1: Layer1, data: impl Into) -> Self { + match layer1 { + Layer1::Bitcoin => XChain::Bitcoin(data.into()), + Layer1::Liquid => XChain::Liquid(data.into()), + } + } + pub fn is_bitcoin(&self) -> bool { matches!(self, XChain::Bitcoin(_)) } pub fn is_liquid(&self) -> bool { matches!(self, XChain::Liquid(_)) } pub fn is_bp(&self) -> bool { @@ -140,21 +218,66 @@ impl XChain { XChain::Bitcoin(_) | XChain::Liquid(_) => true, } } - pub fn into_bp(self) -> Option> { - Some(match self { + + pub fn layer1(&self) -> Layer1 { + match self { + XChain::Bitcoin(_) => Layer1::Bitcoin, + XChain::Liquid(_) => Layer1::Liquid, + } + } + + pub fn as_bp(&self) -> Bp<&T> + where for<'a> &'a T: StrictDumb + StrictEncode + StrictDecode { + match self { XChain::Bitcoin(t) => Bp::Bitcoin(t), XChain::Liquid(t) => Bp::Liquid(t), - }) + } } - pub fn unwrap_into_bp(self) -> Bp { - self.into_bp() - .expect("only Bitcoin and Liquid chains are supported at this moment") + + pub fn into_bp(self) -> Bp + where T: StrictDumb + StrictEncode + StrictDecode { + match self { + XChain::Bitcoin(t) => Bp::Bitcoin(t), + XChain::Liquid(t) => Bp::Liquid(t), + } + } + + pub fn as_reduced_unsafe(&self) -> &T { + match self { + XChain::Bitcoin(t) | XChain::Liquid(t) => t, + } + } + + /// Maps the value from one internal type into another. + pub fn map(self, f: impl FnOnce(T) -> U) -> XChain { + match self { + Self::Bitcoin(t) => XChain::Bitcoin(f(t)), + Self::Liquid(t) => XChain::Liquid(f(t)), + } + } + + /// Maps the value from one internal type into another, covering cases which + /// may error. + pub fn try_map(self, f: impl FnOnce(T) -> Result) -> Result, E> { + match self { + Self::Bitcoin(t) => f(t).map(XChain::Bitcoin), + Self::Liquid(t) => f(t).map(XChain::Liquid), + } + } + + /// Maps the value from one internal type into another, covering cases which + /// may result in an optional value. + pub fn maybe_map(self, f: impl FnOnce(T) -> Option) -> Option> { + match self { + Self::Bitcoin(t) => f(t).map(XChain::Bitcoin), + Self::Liquid(t) => f(t).map(XChain::Liquid), + } } } #[derive(Clone, Debug, Display, Error, From)] pub enum XChainParseError { - #[display("unknown chain prefix '{0}'; only 'bitcoin:' and 'liquid:' are currently supported")] + #[display("unknown chain prefix '{0}'; only 'bc:' and 'lq:' are currently supported")] UnknownPrefix(String), #[from] @@ -172,11 +295,11 @@ where fn from_str(s: &str) -> Result { if let Some((prefix, s)) = s.split_once(':') { match prefix { - "bitcoin" => s + XCHAIN_BITCOIN_PREFIX => s .parse() .map(XChain::Bitcoin) .map_err(XChainParseError::from), - "liquid" => s + XCHAIN_LIQUID_PREFIX => s .parse() .map(XChain::Liquid) .map_err(XChainParseError::from), @@ -195,8 +318,8 @@ where T: StrictDumb + StrictEncode + StrictDecode { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - XChain::Bitcoin(t) => write!(f, "bitcoin:{t}"), - XChain::Liquid(t) => write!(f, "liquid:{t}"), + XChain::Bitcoin(t) => write!(f, "{XCHAIN_BITCOIN_PREFIX}:{t}"), + XChain::Liquid(t) => write!(f, "{XCHAIN_LIQUID_PREFIX}:{t}"), } } } diff --git a/src/contract/seal.rs b/src/contract/seal.rs index 643770e7..874e2d9c 100644 --- a/src/contract/seal.rs +++ b/src/contract/seal.rs @@ -28,113 +28,75 @@ use std::num::NonZeroU32; use bp::dbc::Method; pub use bp::seals::txout::blind::{ChainBlindSeal, ParseError, SingleBlindSeal}; pub use bp::seals::txout::TxoSeal; -use bp::seals::txout::{CloseMethod, ExplicitSeal, SealTxid}; +use bp::seals::txout::{CloseMethod, ExplicitSeal, SealTxid, VerifyError, Witness}; pub use bp::seals::SecretSeal; -use bp::Txid; -use commit_verify::{strategies, CommitVerify, Conceal, DigestExt, Sha256, UntaggedProtocol}; +use bp::{dbc, Outpoint, Tx, Txid, Vout}; +use commit_verify::{mpc, strategies, CommitVerify, Conceal, DigestExt, Sha256, UntaggedProtocol}; +use single_use_seals::SealWitness; use strict_encoding::{StrictDecode, StrictDumb, StrictEncode, StrictType, StrictWriter}; -use crate::{Layer1, XChain, LIB_NAME_RGB}; +use crate::{XChain, LIB_NAME_RGB}; pub type GenesisSeal = SingleBlindSeal; pub type GraphSeal = ChainBlindSeal; -/* -impl ToBaid58<32> for SecretSeal { - const HRI: &'static str = "utxob"; - const CHUNKING: Option = CHUNKING_32CHECKSUM; - fn to_baid58_payload(&self) -> [u8; 32] { self.0.into_inner() } - fn to_baid58_string(&self) -> String { self.to_string() } -} -impl FromBaid58<32> for SecretSeal {} -impl FromStr for SecretSeal { - type Err = Baid58ParseError; - fn from_str(s: &str) -> Result { - SecretSeal::from_baid58_maybe_chunked_str(s, ':', ' ') - } -} -impl Display for SecretSeal { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if f.alternate() { - write!(f, "{::^}", self.to_baid58()) - } else { - write!(f, "{::^.3}", self.to_baid58()) - } - } -} +pub type OutputSeal = ExplicitSeal; -impl CommitVerify, TapretFirst> for SecretSeal { - fn commit(reveal: &BlindSeal) -> Self { Bytes32::commit(reveal).into() } -} +pub type WitnessId = XChain; -impl Conceal for BlindSeal { - type Concealed = SecretSeal; +pub type XGenesisSeal = XChain; +pub type XGraphSeal = XChain; +pub type XOutputSeal = XChain; - #[inline] - fn conceal(&self) -> Self::Concealed { SecretSeal::commit(self) } +pub trait ExposedSeal: + Debug + StrictDumb + StrictEncode + StrictDecode + Eq + Ord + Copy + Hash + TxoSeal +{ } -impl BlindSeal { - /// Converts revealed seal into concealed. - #[inline] - pub fn to_concealed_seal(&self) -> SecretSeal { self.conceal() } -} +impl ExposedSeal for GraphSeal {} -mod test { - use super::*; +impl ExposedSeal for GenesisSeal {} - #[test] - fn secret_seal_is_sha256d() { - let reveal = BlindSeal { - method: CloseMethod::TapretFirst, - blinding: 54683213134637, - txid: TxPtr::Txid( - Txid::from_hex("646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839") - .unwrap(), - ), - vout: Vout::from(2), - }; - assert_eq!(reveal.to_concealed_seal(), reveal.conceal()) +impl TxoSeal for XChain { + fn method(&self) -> CloseMethod { + match self { + XChain::Bitcoin(seal) | XChain::Liquid(seal) => seal.method(), + } } - #[test] - fn secret_seal_baid58() { - let seal = BlindSeal { - method: CloseMethod::TapretFirst, - blinding: 54683213134637, - txid: TxPtr::Txid( - Txid::from_hex("646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839") - .unwrap(), - ), - vout: Vout::from(2), + fn txid(&self) -> Option { + match self { + XChain::Bitcoin(seal) | XChain::Liquid(seal) => seal.txid(), } - .to_concealed_seal(); + } - let baid58 = "utxob:2eFrirU-RjqLnqR74-AKRfdnc9M-DpvSRjmZG-mFPrw7nvu-Te1wy83"; - assert_eq!(baid58, seal.to_string()); - assert_eq!(baid58.replace('-', ""), format!("{seal:#}")); - assert_eq!(seal.to_string(), seal.to_baid58_string()); - let reconstructed = SecretSeal::from_str(baid58).unwrap(); - assert_eq!(reconstructed, seal); - let reconstructed = SecretSeal::from_str(&baid58.replace('-', "")).unwrap(); - assert_eq!(reconstructed, seal); + fn vout(&self) -> Vout { + match self { + XChain::Bitcoin(seal) | XChain::Liquid(seal) => seal.vout(), + } } -} - */ -pub trait ExposedSeal: - Debug + StrictDumb + StrictEncode + StrictDecode + Eq + Ord + Copy + Hash + TxoSeal -{ -} + fn outpoint(&self) -> Option { + match self { + XChain::Bitcoin(seal) | XChain::Liquid(seal) => seal.outpoint(), + } + } -impl ExposedSeal for GraphSeal {} + fn txid_or(&self, default_txid: Txid) -> Txid { + match self { + XChain::Bitcoin(seal) | XChain::Liquid(seal) => seal.txid_or(default_txid), + } + } -impl ExposedSeal for GenesisSeal {} + fn outpoint_or(&self, default_txid: Txid) -> Outpoint { + match self { + XChain::Bitcoin(seal) | XChain::Liquid(seal) => seal.outpoint_or(default_txid), + } + } +} impl ExposedSeal for ExplicitSeal {} -pub type OutputSeal = XSeal>; - /* #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] @@ -183,28 +145,8 @@ impl XSeal { } impl XSeal { - pub fn with(layer1: Layer1, seal: impl Into) -> Self { - match layer1 { - Layer1::Bitcoin => XSeal::Bitcoin(seal.into()), - Layer1::Liquid => XSeal::Liquid(seal.into()), - } - } - - pub fn layer1(self) -> Layer1 { - match self { - XSeal::Bitcoin(_) => Layer1::Bitcoin, - XSeal::Liquid(_) => Layer1::Liquid, - } - } - - pub fn reduce_to_bp(self) -> Option { - Some(match self { - XSeal::Bitcoin(seal) => seal, - XSeal::Liquid(seal) => seal, - }) - } - - pub fn method(self) -> CloseMethod { + pub fn method(self) -> CloseMethod + where U: TxoSeal { match self { XSeal::Bitcoin(seal) => seal.method(), XSeal::Liquid(seal) => seal.method(), @@ -212,7 +154,8 @@ impl XSeal { } #[inline] - pub fn to_output_seal(self) -> Option { + pub fn to_output_seal(self) -> Option + where U: TxoSeal { Some(match self { XSeal::Bitcoin(seal) => { let outpoint = seal.outpoint()?; @@ -225,7 +168,8 @@ impl XSeal { }) } - pub fn try_to_output_seal(self, witness_id: WitnessId) -> Result { + pub fn try_to_output_seal(self, witness_id: WitnessId) -> Result + where U: TxoSeal { match (self, witness_id) { (XSeal::Bitcoin(seal), WitnessId::Bitcoin(txid)) => { Ok(XSeal::Bitcoin(ExplicitSeal::new(seal.method(), seal.outpoint_or(txid)))) @@ -299,39 +243,131 @@ impl WitnessOrd { } } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Display)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB, tags = custom, dumb = WitnessId::Bitcoin(strict_dumb!()))] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub enum WitnessId { - #[strict_type(tag = 0x00)] - #[display("bitcoin:{0}")] - Bitcoin(Txid), - - #[strict_type(tag = 0x01)] - #[display("liquid:{0}")] - Liquid(Txid), - // Prime, - // Abraxas, +pub type XPubWitness = XChain; + +pub type XWitness = XChain>; + +impl XPubWitness { + pub fn witness_id(&self) -> WitnessId { + match self { + Self::Bitcoin(tx) => WitnessId::Bitcoin(tx.txid()), + Self::Liquid(tx) => WitnessId::Liquid(tx.txid()), + } + } } -impl PartialOrd for WitnessId { - fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } +impl XWitness { + pub fn witness_id(&self) -> WitnessId { + match self { + Self::Bitcoin(w) => WitnessId::Bitcoin(w.txid), + Self::Liquid(w) => WitnessId::Liquid(w.txid), + } + } +} + +impl SealWitness for XWitness { + type Message = mpc::Commitment; + type Error = VerifyError; + + fn verify_seal(&self, seal: &Seal, msg: &Self::Message) -> Result<(), Self::Error> { + match self { + Self::Bitcoin(witness) | Self::Liquid(witness) => witness.verify_seal(seal, msg), + } + } + + fn verify_many_seals<'seal>( + &self, + seals: impl IntoIterator, + msg: &Self::Message, + ) -> Result<(), Self::Error> + where + Seal: 'seal, + { + match self { + Self::Bitcoin(witness) | Self::Liquid(witness) => witness.verify_many_seals(seals, msg), + } + } } -impl Ord for WitnessId { - fn cmp(&self, other: &Self) -> Ordering { - match (self, other) { - (WitnessId::Bitcoin(_), WitnessId::Liquid(_)) => Ordering::Greater, - (WitnessId::Liquid(_), WitnessId::Bitcoin(_)) => Ordering::Less, - ( - WitnessId::Bitcoin(txid1) | WitnessId::Liquid(txid1), - WitnessId::Bitcoin(txid2) | WitnessId::Liquid(txid2), - ) => txid1.cmp(txid2), +/* +impl ToBaid58<32> for SecretSeal { + const HRI: &'static str = "utxob"; + const CHUNKING: Option = CHUNKING_32CHECKSUM; + fn to_baid58_payload(&self) -> [u8; 32] { self.0.into_inner() } + fn to_baid58_string(&self) -> String { self.to_string() } +} +impl FromBaid58<32> for SecretSeal {} +impl FromStr for SecretSeal { + type Err = Baid58ParseError; + fn from_str(s: &str) -> Result { + SecretSeal::from_baid58_maybe_chunked_str(s, ':', ' ') + } +} +impl Display for SecretSeal { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if f.alternate() { + write!(f, "{::^}", self.to_baid58()) + } else { + write!(f, "{::^.3}", self.to_baid58()) } } } + +impl CommitVerify, TapretFirst> for SecretSeal { + fn commit(reveal: &BlindSeal) -> Self { Bytes32::commit(reveal).into() } +} + +impl Conceal for BlindSeal { + type Concealed = SecretSeal; + + #[inline] + fn conceal(&self) -> Self::Concealed { SecretSeal::commit(self) } +} + +impl BlindSeal { + /// Converts revealed seal into concealed. + #[inline] + pub fn to_concealed_seal(&self) -> SecretSeal { self.conceal() } +} + +mod test { + use super::*; + + #[test] + fn secret_seal_is_sha256d() { + let reveal = BlindSeal { + method: CloseMethod::TapretFirst, + blinding: 54683213134637, + txid: TxPtr::Txid( + Txid::from_hex("646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839") + .unwrap(), + ), + vout: Vout::from(2), + }; + assert_eq!(reveal.to_concealed_seal(), reveal.conceal()) + } + + #[test] + fn secret_seal_baid58() { + let seal = BlindSeal { + method: CloseMethod::TapretFirst, + blinding: 54683213134637, + txid: TxPtr::Txid( + Txid::from_hex("646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839") + .unwrap(), + ), + vout: Vout::from(2), + } + .to_concealed_seal(); + + let baid58 = "utxob:2eFrirU-RjqLnqR74-AKRfdnc9M-DpvSRjmZG-mFPrw7nvu-Te1wy83"; + assert_eq!(baid58, seal.to_string()); + assert_eq!(baid58.replace('-', ""), format!("{seal:#}")); + assert_eq!(seal.to_string(), seal.to_baid58_string()); + let reconstructed = SecretSeal::from_str(baid58).unwrap(); + assert_eq!(reconstructed, seal); + let reconstructed = SecretSeal::from_str(&baid58.replace('-', "")).unwrap(); + assert_eq!(reconstructed, seal); + } +} + */ diff --git a/src/validation/mod.rs b/src/validation/mod.rs index b1f83600..6043beb9 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -32,4 +32,4 @@ 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}; +pub use validator::{ResolveWitness, Validator, WitnessResolverError}; diff --git a/src/validation/status.rs b/src/validation/status.rs index 19403dc5..3988066a 100644 --- a/src/validation/status.rs +++ b/src/validation/status.rs @@ -31,8 +31,8 @@ use strict_types::SemId; use crate::contract::Opout; use crate::schema::{self, SchemaId}; use crate::{ - BundleId, ContractId, GraphSeal, Layer1, OccurrencesMismatch, OpFullType, OpId, SecretSeal, - StateType, Vin, XSeal, + BundleId, ContractId, Layer1, OccurrencesMismatch, OpFullType, OpId, SecretSeal, StateType, + Vin, WitnessId, XGraphSeal, }; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Display)] @@ -58,7 +58,7 @@ pub enum Validity { serde(crate = "serde_crate", rename_all = "camelCase") )] pub struct Status { - pub unresolved_txids: Vec, + pub absent_pub_witnesses: Vec, pub unmined_terminals: Vec, pub failures: Vec, pub warnings: Vec, @@ -71,9 +71,9 @@ impl Display for Status { writeln!(f, "Consignment {}", self.validity())?; } - if !self.unresolved_txids.is_empty() { - f.write_str("Unknown witness transactions:\n")?; - for txid in &self.unresolved_txids { + if !self.absent_pub_witnesses.is_empty() { + f.write_str("Unknown public witnesses:\n")?; + for txid in &self.absent_pub_witnesses { writeln!(f, "- {txid}")?; } } @@ -112,7 +112,7 @@ impl Display for Status { impl AddAssign for Status { fn add_assign(&mut self, rhs: Self) { - self.unresolved_txids.extend(rhs.unresolved_txids); + self.absent_pub_witnesses.extend(rhs.absent_pub_witnesses); self.unmined_terminals.extend(rhs.unmined_terminals); self.failures.extend(rhs.failures); self.warnings.extend(rhs.warnings); @@ -123,7 +123,7 @@ impl AddAssign for Status { impl Status { pub fn from_error(v: Failure) -> Self { Status { - unresolved_txids: vec![], + absent_pub_witnesses: vec![], unmined_terminals: vec![], failures: vec![v], warnings: vec![], @@ -173,7 +173,7 @@ impl Status { } else { Validity::UnminedTerminals } - } else if self.unresolved_txids.is_empty() { + } else if self.absent_pub_witnesses.is_empty() { Validity::Invalid } else { Validity::UnresolvedTransactions @@ -311,12 +311,12 @@ pub enum Failure { /// 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), + /// transition bundle {0} references non-existing input in witness {2} for + /// the state transition {1}. + BundleInvalidInput(BundleId, OpId, WitnessId), + /// transition bundle {0} doesn't commit to the input {1} in the witness {2} + /// which is an input of the state transition {3}. + BundleInvalidCommitment(BundleId, Vin, WitnessId, OpId), // Errors checking seal closing /// transition {opid} references state type {state_type} absent in the @@ -328,27 +328,27 @@ pub enum Failure { }, /// transition {0} references non-existing previous output {1}. NoPrevOut(OpId, Opout), - /// anchors used inside bundle {0} reference different transaction ids. + /// anchors used inside bundle {0} reference different public witness ids. AnchorSetInvalid(BundleId), /// seal defined in the history as a part of operation output {0} is /// confidential and can't be validated. ConfidentialSeal(Opout), - /// witness transaction {0} is not known to the transaction resolver. - SealNoWitnessTx(Txid), + /// witness {0} is not known to the transaction resolver. + SealNoWitnessTx(WitnessId), /// witness layer 1 {anchor} doesn't match seal definition {seal}. SealWitnessLayer1Mismatch { seal: Layer1, anchor: Layer1 }, /// seal {1:?} is defined on {0} which is not in the set of layers allowed /// by the contract genesis. - SealLayerMismatch(Layer1, XSeal), - /// transition bundle {0} doesn't close seal with the witness transaction - /// {1}. Details: {2} - SealsInvalid(BundleId, Txid, String), + SealLayerMismatch(Layer1, XGraphSeal), + /// transition bundle {0} doesn't close seal with the witness {1}. Details: + /// {2} + SealsInvalid(BundleId, WitnessId, String), /// single-use seals for the operation {0} were not validated, which /// probably indicates unanchored state transition. SealsUnvalidated(OpId), - /// transition bundle {0} is not properly anchored to the witness - /// transaction {1}. Details: {2} - MpcInvalid(BundleId, Txid, InvalidProof), + /// transition bundle {0} is not properly anchored to the witness {1}. + /// Details: {2} + MpcInvalid(BundleId, WitnessId, 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 583b5539..3cea55ae 100644 --- a/src/validation/validator.rs +++ b/src/validation/validator.rs @@ -23,8 +23,8 @@ 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 bp::seals::txout::{CloseMethod, ExplicitSeal, Witness}; +use bp::{dbc, Outpoint, Txid}; use commit_verify::mpc; use single_use_seals::SealWitness; @@ -32,25 +32,28 @@ 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, OpType, Operation, Opout, - Schema, SchemaId, SchemaRoot, Script, SubSchema, Transition, TransitionBundle, TypedAssigns, - XAnchor, + AltLayer1, BundleId, ContractId, Layer1, OpId, OpRef, OpType, Operation, Opout, Schema, + SchemaId, SchemaRoot, Script, SubSchema, Transition, TransitionBundle, TypedAssigns, WitnessId, + XAnchor, XChain, XOutputSeal, XPubWitness, XWitness, }; #[derive(Clone, Debug, Display, Error, From)] #[display(doc_comments)] -pub enum TxResolverError { - /// transaction {0} is not mined - Unknown(Txid), - /// unable to retrieve transaction {0}, {1} - Other(Txid, String), +pub enum WitnessResolverError { + /// witness {0} does not exists. + Unknown(WitnessId), + /// unable to retrieve witness {0}, {1} + Other(WitnessId, String), } -pub trait ResolveTx { - fn resolve_bp_tx(&self, layer1: Layer1, txid: Txid) -> Result; +pub trait ResolveWitness { + fn resolve_pub_witness( + &self, + witness_id: WitnessId, + ) -> Result; } -pub struct Validator<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> { +pub struct Validator<'consignment, 'resolver, C: ConsignmentApi, R: ResolveWitness> { consignment: CheckedConsignment<'consignment, C>, status: Status, @@ -67,7 +70,7 @@ pub struct Validator<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> { resolver: &'resolver R, } -impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> +impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveWitness> Validator<'consignment, 'resolver, C, R> { fn init(consignment: &'consignment C, resolver: &'resolver R) -> Self { @@ -307,19 +310,14 @@ impl<'consignment, '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 anchors = match &anchored_bundle.anchor { - XAnchor::Bitcoin(anchor) | XAnchor::Liquid(anchor) => anchor, - }; + let anchors = &anchored_bundle.anchor; let bundle = &anchored_bundle.bundle; // [VALIDATION]: We validate that the seals were properly defined on BP-type layers let (seals, input_map) = self.validate_seal_definitions(layer1, bundle); // [VALIDATION]: We validate that the seals were properly closed on BP-type layers - let Some(witness_tx) = self.validate_commitments_bp(layer1, &seals, bundle_id, anchors) + let Some(witness_tx) = self.validate_seal_commitments(&seals, bundle_id, anchors) else { continue; }; @@ -337,29 +335,26 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> &mut self, bundle_id: BundleId, bundle: &TransitionBundle, - witness_tx: Tx, - input_map: BTreeMap>, + pub_witness: XPubWitness, + input_map: BTreeMap>>, ) { + let witness_id = pub_witness.witness_id(); 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(), - )); + let layer1 = pub_witness.layer1(); + let pub_witness = pub_witness.as_reduced_unsafe(); + let Some(input) = pub_witness.inputs.get(vin.to_usize()) else { + self.status + .add_failure(Failure::BundleInvalidInput(bundle_id, *opid, witness_id)); continue; }; - if !outpoints.contains(&input.prev_output) { + if !outpoints.contains(&XChain::with(layer1, input.prev_output)) { self.status.add_failure(Failure::BundleInvalidCommitment( - bundle_id, - *vin, - witness_tx.txid(), - *opid, + bundle_id, *vin, witness_id, *opid, )); } } @@ -367,14 +362,13 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> /// Bitcoin- and liquid-specific commitment validation using deterministic /// bitcoin commitments with opret and tapret schema. - fn validate_commitments_bp( + fn validate_seal_commitments( &mut self, - layer1: Layer1, - seals: impl AsRef<[ExplicitSeal]>, + seals: impl AsRef<[XOutputSeal]>, bundle_id: BundleId, - anchors: &AnchorSet, - ) -> Option { - let Some(txid) = anchors.txid() else { + anchors: &XAnchor, + ) -> Option { + let Some(witness_id) = anchors.witness_id() else { self.status .add_failure(Failure::AnchorSetInvalid(bundle_id)); return None; @@ -382,7 +376,7 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> // Check that the anchor is committed into a transaction spending all of the // transition inputs. - match self.resolver.resolve_bp_tx(layer1, txid) { + match self.resolver.resolve_pub_witness(witness_id) { Err(_) => { // We wre unable to retrieve corresponding transaction, so can't check. // Reporting this incident and continuing further. Why this happens? No @@ -390,24 +384,27 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> // failure in a strict sense, however we can't be sure that the consignment is // valid. That's why we keep the track of such information in a separate place // (`unresolved_txids` field of the validation status object). - self.status.unresolved_txids.push(txid); + self.status.absent_pub_witnesses.push(witness_id); // This also can mean that there is no known transaction with the id provided by // the anchor, i.e. consignment is invalid. We are proceeding with further // validation in order to detect the rest of problems (and reporting the // failure!) - self.status.add_failure(Failure::SealNoWitnessTx(txid)); + self.status + .add_failure(Failure::SealNoWitnessTx(witness_id)); None } - Ok(witness_tx) => { - let (tapret, opret) = anchors.as_split(); + Ok(pub_witness) => { + let (tapret, opret) = anchors.as_reduced_unsafe().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) + let witness = pub_witness + .clone() + .map(|tx| Witness::with(tx, tapret.clone())); + self.validate_seal_closing(tapret_seals, witness, bundle_id, tapret) } else if tapret_seals.count() > 0 { self.status.add_warning(Warning::UnclosedSeals(bundle_id)); } @@ -417,12 +414,14 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> .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) + let witness = pub_witness + .clone() + .map(|tx| Witness::with(tx, opret.clone())); + self.validate_seal_closing(opret_seals, witness, bundle_id, opret) } else if opret_seals.count() > 0 { self.status.add_warning(Warning::UnclosedSeals(bundle_id)); } - Some(witness_tx) + Some(pub_witness) } } } @@ -435,8 +434,8 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> &mut self, layer1: Layer1, bundle: &TransitionBundle, - ) -> (Vec>, BTreeMap>) { - let mut input_map: BTreeMap> = bmap!(); + ) -> (Vec>>, BTreeMap>>) { + let mut input_map: BTreeMap>> = bmap!(); let mut seals = vec![]; for (opid, transition) in &bundle.known_transitions { let opid = *opid; @@ -498,20 +497,26 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> continue; }; - seal.try_to_output_seal(witness_id) - .expect("method must be called only on BP-compatible layer 1") + match seal.try_to_output_seal(witness_id) { + Ok(seal) => seal, + Err(_) => { + self.status.add_failure(Failure::SealWitnessLayer1Mismatch { + seal: seal.layer1(), + anchor: witness_id.layer1(), + }); + continue; + } + } } else { seal.to_output_seal() .expect("genesis and state extensions must have explicit seals") - } - .reduce_to_bp() - .expect("method must be called only on BP-compatible layer 1"); + }; seals.push(seal); input_map .entry(opid) .or_default() - .insert(Outpoint::new(seal.txid, seal.vout)); + .insert(seal.map(|seal| Outpoint::new(seal.txid, seal.vout))); } } (seals, input_map) @@ -527,21 +532,24 @@ impl<'consignment, '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, 'temp, Seal: TxoSeal + 'seal, Dbc: dbc::Proof>( + fn validate_seal_closing<'seal, 'temp, Seal: 'seal, Dbc: dbc::Proof>( &mut self, seals: impl IntoIterator, - witness: Witness, + witness: XWitness, bundle_id: BundleId, anchor: &'temp Anchor, - ) { + ) where + XWitness: SealWitness, + { let message = mpc::Message::from(bundle_id); + let witness_id = witness.witness_id(); // [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::MpcInvalid(bundle_id, witness.txid, err)); + .add_failure(Failure::MpcInvalid(bundle_id, witness_id, err)); } Ok(commitment) => { // [VALIDATION]: CHECKING SINGLE-USE-SEALS @@ -550,7 +558,7 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> .map_err(|err| { self.status.add_failure(Failure::SealsInvalid( bundle_id, - witness.txid, + witness_id, err.to_string(), )); }) From c1485191deb2b08473d102c2a5977d2ee9fc8040 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 29 Dec 2023 20:41:58 +0100 Subject: [PATCH 05/11] contract: wrap SecretSeal into XChain type --- Cargo.lock | 8 +- src/contract/assignments.rs | 35 ++++---- src/contract/contract.rs | 6 +- src/contract/mod.rs | 19 +++- src/contract/seal.rs | 160 +++++++++++----------------------- src/stl.rs | 2 +- src/validation/consignment.rs | 13 +-- src/validation/status.rs | 4 +- src/validation/validator.rs | 2 +- 9 files changed, 103 insertions(+), 146 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b479b6d6..6863848f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -196,7 +196,7 @@ dependencies = [ [[package]] name = "bp-consensus" version = "0.11.0-beta.3" -source = "git+https://github.com/BP-WG/bp-core?branch=v0.11#e30a05fae8774d722da70673d7428aee07410abc" +source = "git+https://github.com/BP-WG/bp-core?branch=v0.11#cbc896d075772a7b4eb620cd8c76cae86ff331c1" dependencies = [ "amplify", "chrono", @@ -210,7 +210,7 @@ dependencies = [ [[package]] name = "bp-core" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-core?branch=v0.11#e30a05fae8774d722da70673d7428aee07410abc" +source = "git+https://github.com/BP-WG/bp-core?branch=v0.11#cbc896d075772a7b4eb620cd8c76cae86ff331c1" dependencies = [ "amplify", "bp-consensus", @@ -226,7 +226,7 @@ dependencies = [ [[package]] name = "bp-dbc" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-core?branch=v0.11#e30a05fae8774d722da70673d7428aee07410abc" +source = "git+https://github.com/BP-WG/bp-core?branch=v0.11#cbc896d075772a7b4eb620cd8c76cae86ff331c1" dependencies = [ "amplify", "base85", @@ -240,7 +240,7 @@ dependencies = [ [[package]] name = "bp-seals" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-core?branch=v0.11#e30a05fae8774d722da70673d7428aee07410abc" +source = "git+https://github.com/BP-WG/bp-core?branch=v0.11#cbc896d075772a7b4eb620cd8c76cae86ff331c1" dependencies = [ "amplify", "baid58", diff --git a/src/contract/assignments.rs b/src/contract/assignments.rs index ea05842a..77587c45 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, XSeal, LIB_NAME_RGB, + SecretSeal, StateType, VoidState, XChain, LIB_NAME_RGB, }; #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Display, Error)] @@ -72,16 +72,19 @@ pub type AssignAttach = Assign; pub enum Assign { #[strict_type(tag = 0x00)] Confidential { - seal: SecretSeal, + seal: XChain, state: State::Confidential, }, #[strict_type(tag = 0x03)] - Revealed { seal: XSeal, state: State }, + Revealed { seal: XChain, state: State }, #[strict_type(tag = 0x02)] - ConfidentialSeal { seal: SecretSeal, state: State }, + ConfidentialSeal { + seal: XChain, + state: State, + }, #[strict_type(tag = 0x01)] ConfidentialState { - seal: XSeal, + seal: XChain, state: State::Confidential, }, } @@ -118,9 +121,9 @@ impl Hash for Assign { } impl Assign { - pub fn revealed(seal: XSeal, state: State) -> Self { Assign::Revealed { seal, state } } + pub fn revealed(seal: XChain, state: State) -> Self { Assign::Revealed { seal, state } } - pub fn with_seal_replaced(assignment: &Self, seal: XSeal) -> Self { + pub fn with_seal_replaced(assignment: &Self, seal: XChain) -> Self { match assignment { Assign::Confidential { seal: _, state } | Assign::ConfidentialState { seal: _, state } => Assign::ConfidentialState { @@ -136,7 +139,7 @@ impl Assign { } } - pub fn to_confidential_seal(&self) -> SecretSeal { + pub fn to_confidential_seal(&self) -> XChain { match self { Assign::Revealed { seal, .. } | Assign::ConfidentialState { seal, .. } => { seal.conceal() @@ -145,7 +148,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 +185,21 @@ impl Assign { } } - pub fn as_revealed(&self) -> Option<(&XSeal, &State)> { + pub fn as_revealed(&self) -> Option<(&XChain, &State)> { match self { Assign::Revealed { seal, state } => Some((seal, state)), _ => None, } } - pub fn to_revealed(&self) -> Option<(XSeal, State)> { + pub fn to_revealed(&self) -> Option<(XChain, State)> { match self { Assign::Revealed { seal, state } => Some((*seal, state.clone())), _ => None, } } - pub fn into_revealed(self) -> Option<(XSeal, State)> { + pub fn into_revealed(self) -> Option<(XChain, State)> { match self { Assign::Revealed { seal, state } => Some((seal, state)), _ => None, @@ -240,11 +243,11 @@ where Self: Clone state.commit_encode(e); } Assign::ConfidentialState { seal, state } => { - seal.commit_encode(e); + seal.conceal().commit_encode(e); state.commit_encode(e); } Assign::Revealed { seal, state } => { - seal.commit_encode(e); + seal.conceal().commit_encode(e); state.commit_encode(e); } Assign::ConfidentialSeal { seal, state } => { @@ -445,7 +448,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) @@ -466,7 +469,7 @@ impl TypedAssigns { }) } - pub fn to_confidential_seals(&self) -> Vec { + pub fn to_confidential_seals(&self) -> Vec> { match self { TypedAssigns::Declarative(s) => s .iter() diff --git a/src/contract/contract.rs b/src/contract/contract.rs index d8711398..6eeba235 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, RevealedAttach, RevealedData, RevealedValue, SchemaId, SubSchema, Transition, TypedAssigns, VoidState, WitnessAnchor, - WitnessId, XOutputSeal, XSeal, LIB_NAME_RGB, + WitnessId, XChain, XOutputSeal, 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: XSeal, + seal: XChain, 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: XSeal, + seal: XChain, state: State, opid: OpId, ty: AssignmentType, diff --git a/src/contract/mod.rs b/src/contract/mod.rs index 58776cb8..f48022c4 100644 --- a/src/contract/mod.rs +++ b/src/contract/mod.rs @@ -48,7 +48,7 @@ pub use assignments::{ pub use attachment::{AttachId, ConcealedAttach, RevealedAttach}; use bp::Bp; pub use bundle::{BundleId, TransitionBundle, Vin}; -use commit_verify::CommitEncode; +use commit_verify::{CommitEncode, Conceal}; pub use contract::{ AttachOutput, ContractHistory, ContractState, DataOutput, FungibleOutput, GlobalOrd, Opout, OpoutParseError, OutputAssignment, RightsOutput, @@ -65,7 +65,7 @@ pub use operations::{ }; pub use seal::{ ExposedSeal, GenesisSeal, GraphSeal, OutputSeal, SecretSeal, TxoSeal, WitnessId, WitnessOrd, - WitnessPos, XGenesisSeal, XGraphSeal, XOutputSeal, XPubWitness, XSeal, XWitness, + WitnessPos, XGenesisSeal, XGraphSeal, XOutputSeal, XPubWitness, XWitness, }; pub use state::{ConfidentialState, ExposedState, StateCommitment, StateData, StateType}; use strict_encoding::{ @@ -149,6 +149,13 @@ impl Ord for XChain { } } +impl Conceal for XChain { + type Concealed = XChain; + + #[inline] + fn conceal(&self) -> Self::Concealed { self.map_ref(|t| t.conceal()) } +} + impl StrictType for XChain where T: StrictDumb + StrictType { @@ -256,6 +263,14 @@ impl XChain { } } + /// Maps the value from a reference on internal type into another. + pub fn map_ref(&self, f: impl FnOnce(&T) -> U) -> XChain { + match self { + Self::Bitcoin(t) => XChain::Bitcoin(f(t)), + Self::Liquid(t) => XChain::Liquid(f(t)), + } + } + /// Maps the value from one internal type into another, covering cases which /// may error. pub fn try_map(self, f: impl FnOnce(T) -> Result) -> Result, E> { diff --git a/src/contract/seal.rs b/src/contract/seal.rs index 874e2d9c..887fb921 100644 --- a/src/contract/seal.rs +++ b/src/contract/seal.rs @@ -23,17 +23,18 @@ use core::fmt::Debug; use std::cmp::Ordering; use std::hash::Hash; +use std::io::Write; use std::num::NonZeroU32; use bp::dbc::Method; pub use bp::seals::txout::blind::{ChainBlindSeal, ParseError, SingleBlindSeal}; pub use bp::seals::txout::TxoSeal; -use bp::seals::txout::{CloseMethod, ExplicitSeal, SealTxid, VerifyError, Witness}; +use bp::seals::txout::{BlindSeal, CloseMethod, ExplicitSeal, SealTxid, VerifyError, Witness}; pub use bp::seals::SecretSeal; use bp::{dbc, Outpoint, Tx, Txid, Vout}; -use commit_verify::{mpc, strategies, CommitVerify, Conceal, DigestExt, Sha256, UntaggedProtocol}; +use commit_verify::{mpc, strategies, CommitEncode, CommitStrategy, Conceal}; use single_use_seals::SealWitness; -use strict_encoding::{StrictDecode, StrictDumb, StrictEncode, StrictType, StrictWriter}; +use strict_encoding::{StrictDecode, StrictDumb, StrictEncode, StrictType}; use crate::{XChain, LIB_NAME_RGB}; @@ -49,7 +50,16 @@ pub type XGraphSeal = XChain; pub type XOutputSeal = XChain; pub trait ExposedSeal: - Debug + StrictDumb + StrictEncode + StrictDecode + Eq + Ord + Copy + Hash + TxoSeal + Debug + + StrictDumb + + StrictEncode + + StrictDecode + + Eq + + Ord + + Copy + + Hash + + TxoSeal + + Conceal { } @@ -95,8 +105,6 @@ impl TxoSeal for XChain { } } -impl ExposedSeal for ExplicitSeal {} - /* #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] @@ -109,47 +117,16 @@ impl ExposedSeal for ExplicitSeal {} pub struct SealPreimage(Bytes32); */ -pub type XSeal = 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: &XSeal) -> Self { - let mut engine = Sha256::default(); - let w = StrictWriter::with(u32::MAX as usize, &mut engine); - reveal.strict_encode(w).ok(); - engine.finish().into() - } -} - -impl commit_verify::CommitStrategy for XSeal { - type Strategy = strategies::ConcealStrict; -} - -impl XSeal { - pub fn transmutate(self) -> XSeal { - match self { - 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), - */ - } - } +impl XChain { + pub fn transmutate(self) -> XChain { self.map(|seal| seal.transmutate()) } } -impl XSeal { +impl XChain { pub fn method(self) -> CloseMethod where U: TxoSeal { match self { - XSeal::Bitcoin(seal) => seal.method(), - XSeal::Liquid(seal) => seal.method(), + XChain::Bitcoin(seal) => seal.method(), + XChain::Liquid(seal) => seal.method(), } } @@ -157,13 +134,13 @@ impl XSeal { pub fn to_output_seal(self) -> Option where U: TxoSeal { Some(match self { - XSeal::Bitcoin(seal) => { + XChain::Bitcoin(seal) => { let outpoint = seal.outpoint()?; - XSeal::Bitcoin(ExplicitSeal::new(seal.method(), outpoint)) + XChain::Bitcoin(ExplicitSeal::new(seal.method(), outpoint)) } - XSeal::Liquid(seal) => { + XChain::Liquid(seal) => { let outpoint = seal.outpoint()?; - XSeal::Liquid(ExplicitSeal::new(seal.method(), outpoint)) + XChain::Liquid(ExplicitSeal::new(seal.method(), outpoint)) } }) } @@ -171,11 +148,11 @@ impl XSeal { pub fn try_to_output_seal(self, witness_id: WitnessId) -> Result where U: TxoSeal { match (self, witness_id) { - (XSeal::Bitcoin(seal), WitnessId::Bitcoin(txid)) => { - Ok(XSeal::Bitcoin(ExplicitSeal::new(seal.method(), seal.outpoint_or(txid)))) + (XChain::Bitcoin(seal), WitnessId::Bitcoin(txid)) => { + Ok(XChain::Bitcoin(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)))) + (XChain::Liquid(seal), WitnessId::Liquid(txid)) => { + Ok(XChain::Liquid(ExplicitSeal::new(seal.method(), seal.outpoint_or(txid)))) } (me, _) => Err(me), } @@ -289,67 +266,33 @@ impl SealWitness for XWitness { } } -/* -impl ToBaid58<32> for SecretSeal { - const HRI: &'static str = "utxob"; - const CHUNKING: Option = CHUNKING_32CHECKSUM; - fn to_baid58_payload(&self) -> [u8; 32] { self.0.into_inner() } - fn to_baid58_string(&self) -> String { self.to_string() } -} -impl FromBaid58<32> for SecretSeal {} -impl FromStr for SecretSeal { - type Err = Baid58ParseError; - fn from_str(s: &str) -> Result { - SecretSeal::from_baid58_maybe_chunked_str(s, ':', ' ') - } -} -impl Display for SecretSeal { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if f.alternate() { - write!(f, "{::^}", self.to_baid58()) - } else { - write!(f, "{::^.3}", self.to_baid58()) - } - } -} - -impl CommitVerify, TapretFirst> for SecretSeal { - fn commit(reveal: &BlindSeal) -> Self { Bytes32::commit(reveal).into() } +impl CommitStrategy for XChain> { + type Strategy = strategies::Strict; } -impl Conceal for BlindSeal { - type Concealed = SecretSeal; - +impl XChain> { + /// Converts revealed seal into concealed. #[inline] - fn conceal(&self) -> Self::Concealed { SecretSeal::commit(self) } + pub fn to_secret_seal(&self) -> XChain { self.conceal() } } -impl BlindSeal { - /// Converts revealed seal into concealed. - #[inline] - pub fn to_concealed_seal(&self) -> SecretSeal { self.conceal() } +impl CommitEncode for XChain { + fn commit_encode(&self, e: &mut impl Write) { + e.write_all(&[self.layer1() as u8]).ok(); + self.as_reduced_unsafe().commit_encode(e); + } } +#[cfg(test)] mod test { + use amplify::hex::FromHex; + use bp::seals::txout::TxPtr; + use super::*; #[test] fn secret_seal_is_sha256d() { - let reveal = BlindSeal { - method: CloseMethod::TapretFirst, - blinding: 54683213134637, - txid: TxPtr::Txid( - Txid::from_hex("646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839") - .unwrap(), - ), - vout: Vout::from(2), - }; - assert_eq!(reveal.to_concealed_seal(), reveal.conceal()) - } - - #[test] - fn secret_seal_baid58() { - let seal = BlindSeal { + let reveal = XChain::Bitcoin(BlindSeal { method: CloseMethod::TapretFirst, blinding: 54683213134637, txid: TxPtr::Txid( @@ -357,17 +300,12 @@ mod test { .unwrap(), ), vout: Vout::from(2), - } - .to_concealed_seal(); - - let baid58 = "utxob:2eFrirU-RjqLnqR74-AKRfdnc9M-DpvSRjmZG-mFPrw7nvu-Te1wy83"; - assert_eq!(baid58, seal.to_string()); - assert_eq!(baid58.replace('-', ""), format!("{seal:#}")); - assert_eq!(seal.to_string(), seal.to_baid58_string()); - let reconstructed = SecretSeal::from_str(baid58).unwrap(); - assert_eq!(reconstructed, seal); - let reconstructed = SecretSeal::from_str(&baid58.replace('-', "")).unwrap(); - assert_eq!(reconstructed, seal); + }); + let secret = reveal.to_secret_seal(); + assert_eq!( + secret.to_string(), + "bc:utxob:6JZb8te-bSUsZzCJk-op9E4D8zf-SHTDu2t4W-T21NaPNnb-58DFM9" + ); + assert_eq!(reveal.to_secret_seal(), reveal.conceal()) } } - */ diff --git a/src/stl.rs b/src/stl.rs index ce1d5edf..81ca5f38 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:5d7k62t7JyjhbVvTNJ9xbSoQWG1xkXPoiYViK69TdMxe#karate-arsenal-spirit"; + "urn:ubideco:stl:2PcZtrPrfQCu27qw8b4Wz4cEqUn2PpgSkDHwF4qVyyrq#russian-child-member"; fn _rgb_core_stl() -> Result { LibBuilder::new(libname!(LIB_NAME_RGB), tiny_bset! { diff --git a/src/validation/consignment.rs b/src/validation/consignment.rs index 8e3075b8..b3429003 100644 --- a/src/validation/consignment.rs +++ b/src/validation/consignment.rs @@ -27,7 +27,10 @@ use std::collections::{BTreeMap, BTreeSet}; use std::rc::Rc; -use crate::{AnchoredBundle, AssetTag, AssignmentType, BundleId, Genesis, OpId, OpRef, Operation, SecretSeal, SubSchema, WitnessId}; +use crate::{ + AnchoredBundle, AssetTag, AssignmentType, BundleId, Genesis, OpId, OpRef, Operation, + SecretSeal, SubSchema, WitnessId, XChain, +}; pub struct CheckedConsignment<'consignment, C: ConsignmentApi>(&'consignment C); @@ -48,7 +51,7 @@ impl<'consignment, C: ConsignmentApi> ConsignmentApi for CheckedConsignment<'con fn genesis(&self) -> &Genesis { self.0.genesis() } - fn terminals(&self) -> BTreeSet<(BundleId, SecretSeal)> { self.0.terminals() } + fn terminals(&self) -> BTreeSet<(BundleId, XChain)> { self.0.terminals() } fn bundle_ids<'a>(&self) -> Self::Iter<'a> { self.0.bundle_ids() } @@ -58,9 +61,7 @@ impl<'consignment, C: ConsignmentApi> ConsignmentApi for CheckedConsignment<'con .filter(|ab| ab.bundle.bundle_id() == bundle_id) } - fn op_witness_id(&self, opid: OpId) -> Option { - self.0.op_witness_id(opid) - } + fn op_witness_id(&self, opid: OpId) -> Option { self.0.op_witness_id(opid) } } /// Trait defining common data access API for all storage-related RGB structures @@ -96,7 +97,7 @@ pub trait ConsignmentApi { /// - if the consignment contains concealed state (known by the receiver), /// it will be computationally inefficient to understand which of the /// state transitions represent the final state - fn terminals(&self) -> BTreeSet<(BundleId, SecretSeal)>; + fn terminals(&self) -> BTreeSet<(BundleId, XChain)>; /// Returns iterator over all bundle ids present in the consignment. fn bundle_ids<'a>(&self) -> Self::Iter<'a>; diff --git a/src/validation/status.rs b/src/validation/status.rs index 3988066a..115963eb 100644 --- a/src/validation/status.rs +++ b/src/validation/status.rs @@ -32,7 +32,7 @@ use crate::contract::Opout; use crate::schema::{self, SchemaId}; use crate::{ BundleId, ContractId, Layer1, OccurrencesMismatch, OpFullType, OpId, SecretSeal, StateType, - Vin, WitnessId, XGraphSeal, + Vin, WitnessId, XChain, XGraphSeal, }; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Display)] @@ -412,7 +412,7 @@ pub enum Warning { // TODO: Replace debug with display /// terminal seal {1:?} referencing operation {0} is not present in /// operation assignments. - TerminalSealAbsent(OpId, SecretSeal), + TerminalSealAbsent(OpId, XChain), /// 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 3cea55ae..c1d6adb8 100644 --- a/src/validation/validator.rs +++ b/src/validation/validator.rs @@ -23,7 +23,7 @@ use std::collections::{BTreeMap, BTreeSet, VecDeque}; use bp::dbc::Anchor; -use bp::seals::txout::{CloseMethod, ExplicitSeal, Witness}; +use bp::seals::txout::{CloseMethod, ExplicitSeal, TxoSeal, Witness}; use bp::{dbc, Outpoint, Txid}; use commit_verify::mpc; use single_use_seals::SealWitness; From ff83af2f3f6fea425e365c86c290fca5229dff67 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 29 Dec 2023 21:44:44 +0100 Subject: [PATCH 06/11] contract: add XAnchor::witness_id_unchecked --- src/contract/anchor.rs | 18 ++++++++++++------ src/contract/mod.rs | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/contract/anchor.rs b/src/contract/anchor.rs index d70e0b08..266c0a72 100644 --- a/src/contract/anchor.rs +++ b/src/contract/anchor.rs @@ -53,12 +53,10 @@ pub type XAnchor

= XChain>; impl XAnchor

{ #[inline] - pub fn witness_id(&self) -> Option { - match self { - XAnchor::Bitcoin(anchor) => anchor.txid().map(WitnessId::Bitcoin), - XAnchor::Liquid(anchor) => anchor.txid().map(WitnessId::Liquid), - } - } + pub fn witness_id(&self) -> Option { self.maybe_map_ref(|set| set.txid()) } + + #[inline] + pub fn witness_id_unchecked(&self) -> WitnessId { self.map_ref(|set| set.txid_unchecked()) } } impl XAnchor { @@ -131,6 +129,14 @@ impl AnchorSet

{ } } + pub fn txid_unchecked(&self) -> Txid { + match self { + AnchorSet::Tapret(a) => a.txid, + AnchorSet::Opret(a) => a.txid, + AnchorSet::Dual { tapret, opret: _ } => tapret.txid, + } + } + pub fn from_split( tapret: Option>, opret: Option>, diff --git a/src/contract/mod.rs b/src/contract/mod.rs index f48022c4..86fd30f2 100644 --- a/src/contract/mod.rs +++ b/src/contract/mod.rs @@ -280,6 +280,15 @@ impl XChain { } } + /// Maps the value from one internal type into another, covering cases which + /// may error. + pub fn try_map_ref(&self, f: impl FnOnce(&T) -> Result) -> Result, E> { + match self { + Self::Bitcoin(t) => f(t).map(XChain::Bitcoin), + Self::Liquid(t) => f(t).map(XChain::Liquid), + } + } + /// Maps the value from one internal type into another, covering cases which /// may result in an optional value. pub fn maybe_map(self, f: impl FnOnce(T) -> Option) -> Option> { @@ -288,6 +297,15 @@ impl XChain { Self::Liquid(t) => f(t).map(XChain::Liquid), } } + + /// Maps the value from one internal type into another, covering cases which + /// may result in an optional value. + pub fn maybe_map_ref(&self, f: impl FnOnce(&T) -> Option) -> Option> { + match self { + Self::Bitcoin(t) => f(t).map(XChain::Bitcoin), + Self::Liquid(t) => f(t).map(XChain::Liquid), + } + } } #[derive(Clone, Debug, Display, Error, From)] From 50cd4191f2b92a86e8d79b1a2d15aa94bd50c367 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 29 Dec 2023 22:21:53 +0100 Subject: [PATCH 07/11] contract: make XChain iterator --- src/contract/mod.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/contract/mod.rs b/src/contract/mod.rs index 86fd30f2..b85c4fe7 100644 --- a/src/contract/mod.rs +++ b/src/contract/mod.rs @@ -306,6 +306,28 @@ impl XChain { Self::Liquid(t) => f(t).map(XChain::Liquid), } } + + /// Returns iterator over elements + pub fn iter<'i>( + &'i self, + ) -> Box::Item>> + 'i> + where &'i T: IntoIterator { + match self { + XChain::Bitcoin(t) => Box::new(t.into_iter().map(XChain::Bitcoin)), + XChain::Liquid(t) => Box::new(t.into_iter().map(XChain::Liquid)), + } + } +} + +impl Iterator for XChain { + type Item = XChain<::Item>; + + fn next(&mut self) -> Option { + match self { + XChain::Bitcoin(t) => t.next().map(XChain::Bitcoin), + XChain::Liquid(t) => t.next().map(XChain::Liquid), + } + } } #[derive(Clone, Debug, Display, Error, From)] From b6ff0051af4a5ba43fa913a298d289ec750a7efa Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sat, 30 Dec 2023 01:25:03 +0100 Subject: [PATCH 08/11] contract: refactor chain-related types into a dedicated module --- src/contract/mod.rs | 321 +------------------------------------- src/contract/xchain.rs | 343 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 346 insertions(+), 318 deletions(-) create mode 100644 src/contract/xchain.rs diff --git a/src/contract/mod.rs b/src/contract/mod.rs index b85c4fe7..d82f52f3 100644 --- a/src/contract/mod.rs +++ b/src/contract/mod.rs @@ -32,23 +32,15 @@ mod operations; mod bundle; #[allow(clippy::module_inception)] mod contract; +mod xchain; -use std::cmp::Ordering; -use std::fmt::{Debug, Display, Formatter}; -use std::io::Write; -use std::str::FromStr; -use std::{fmt, io}; - -use amplify::confinement::TinyOrdSet; pub use anchor::{AnchorSet, AnchoredBundle, Layer1, WitnessAnchor, XAnchor}; pub use assignments::{ Assign, AssignAttach, AssignData, AssignFungible, AssignRights, Assignments, AssignmentsRef, TypedAssigns, }; pub use attachment::{AttachId, ConcealedAttach, RevealedAttach}; -use bp::Bp; pub use bundle::{BundleId, TransitionBundle, Vin}; -use commit_verify::{CommitEncode, Conceal}; pub use contract::{ AttachOutput, ContractHistory, ContractState, DataOutput, FungibleOutput, GlobalOrd, Opout, OpoutParseError, OutputAssignment, RightsOutput, @@ -68,313 +60,6 @@ pub use seal::{ WitnessPos, XGenesisSeal, XGraphSeal, XOutputSeal, XPubWitness, XWitness, }; pub use state::{ConfidentialState, ExposedState, StateCommitment, StateData, StateType}; -use strict_encoding::{ - DecodeError, DefineUnion, ReadTuple, ReadUnion, StrictDecode, StrictDumb, StrictEncode, - StrictSum, StrictType, StrictUnion, TypedRead, TypedWrite, WriteUnion, +pub use xchain::{ + AltLayer1, AltLayer1Set, XChain, XChainParseError, XCHAIN_BITCOIN_PREFIX, XCHAIN_LIQUID_PREFIX, }; - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -#[display(lowercase)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = super::LIB_NAME_RGB, tags = repr, into_u8, try_from_u8)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -#[repr(u8)] -pub enum AltLayer1 { - #[strict_type(dumb)] - Liquid = 1, - // Abraxas = 0x10, - // Prime = 0x11, -} - -impl AltLayer1 { - pub fn layer1(&self) -> Layer1 { - match self { - AltLayer1::Liquid => Layer1::Liquid, - } - } -} - -#[derive(Wrapper, WrapperMut, Clone, PartialEq, Eq, Hash, Debug, Default, From)] -#[wrapper(Deref)] -#[wrapper_mut(DerefMut)] -#[derive(StrictType, StrictEncode, StrictDecode)] -#[strict_type(lib = super::LIB_NAME_RGB)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", transparent) -)] -pub struct AltLayer1Set(TinyOrdSet); - -impl CommitEncode for AltLayer1Set { - fn commit_encode(&self, e: &mut impl Write) { - for c in self.iter() { - e.write_all(&[*c as u8]).ok(); - } - } -} - -pub const XCHAIN_BITCOIN_PREFIX: &str = "bc"; -pub const XCHAIN_LIQUID_PREFIX: &str = "lq"; - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub enum XChain { - Bitcoin(T), - - Liquid(T), -} - -impl PartialOrd for XChain { - fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } -} - -impl Ord for XChain { - fn cmp(&self, other: &Self) -> Ordering { - match (self, other) { - (Self::Bitcoin(_), Self::Liquid(_)) => Ordering::Greater, - (Self::Liquid(_), Self::Bitcoin(_)) => Ordering::Less, - (Self::Bitcoin(t1) | Self::Liquid(t1), Self::Bitcoin(t2) | Self::Liquid(t2)) => { - t1.cmp(t2) - } - } - } -} - -impl Conceal for XChain { - type Concealed = XChain; - - #[inline] - fn conceal(&self) -> Self::Concealed { self.map_ref(|t| t.conceal()) } -} - -impl StrictType for XChain -where T: StrictDumb + StrictType -{ - const STRICT_LIB_NAME: &'static str = super::LIB_NAME_RGB; -} -impl StrictSum for XChain -where T: StrictDumb + StrictType -{ - const ALL_VARIANTS: &'static [(u8, &'static str)] = &[(0x00, "bitcoin"), (0x01, "liquid")]; - - fn variant_name(&self) -> &'static str { - match self { - XChain::Bitcoin(_) => Self::ALL_VARIANTS[0].1, - XChain::Liquid(_) => Self::ALL_VARIANTS[1].1, - } - } -} -impl StrictUnion for XChain where T: StrictDumb + StrictType {} -impl StrictDumb for XChain -where T: StrictDumb -{ - fn strict_dumb() -> Self { XChain::Bitcoin(strict_dumb!()) } -} -impl StrictEncode for XChain -where T: StrictDumb + StrictEncode -{ - fn strict_encode(&self, writer: W) -> io::Result { - writer.write_union::(|w| { - let w = w - .define_newtype::(fname!(Self::ALL_VARIANTS[0].1)) - .define_newtype::(fname!(Self::ALL_VARIANTS[1].1)) - .complete(); - Ok(match self { - XChain::Bitcoin(t) => w.write_newtype(fname!(Self::ALL_VARIANTS[0].1), t)?, - XChain::Liquid(t) => w.write_newtype(fname!(Self::ALL_VARIANTS[1].1), t)?, - } - .complete()) - }) - } -} -impl StrictDecode for XChain -where T: StrictDumb + StrictDecode -{ - fn strict_decode(reader: &mut impl TypedRead) -> Result { - reader.read_union(|field, r| match field.as_str() { - x if x == Self::ALL_VARIANTS[0].1 => { - r.read_tuple(|r| r.read_field().map(Self::Bitcoin)) - } - x if x == Self::ALL_VARIANTS[1].1 => r.read_tuple(|r| r.read_field().map(Self::Liquid)), - _ => unreachable!(), - }) - } -} - -impl XChain { - pub fn with(layer1: Layer1, data: impl Into) -> Self { - match layer1 { - Layer1::Bitcoin => XChain::Bitcoin(data.into()), - Layer1::Liquid => XChain::Liquid(data.into()), - } - } - - pub fn is_bitcoin(&self) -> bool { matches!(self, XChain::Bitcoin(_)) } - pub fn is_liquid(&self) -> bool { matches!(self, XChain::Liquid(_)) } - pub fn is_bp(&self) -> bool { - match self { - XChain::Bitcoin(_) | XChain::Liquid(_) => true, - } - } - - pub fn layer1(&self) -> Layer1 { - match self { - XChain::Bitcoin(_) => Layer1::Bitcoin, - XChain::Liquid(_) => Layer1::Liquid, - } - } - - pub fn as_bp(&self) -> Bp<&T> - where for<'a> &'a T: StrictDumb + StrictEncode + StrictDecode { - match self { - XChain::Bitcoin(t) => Bp::Bitcoin(t), - XChain::Liquid(t) => Bp::Liquid(t), - } - } - - pub fn into_bp(self) -> Bp - where T: StrictDumb + StrictEncode + StrictDecode { - match self { - XChain::Bitcoin(t) => Bp::Bitcoin(t), - XChain::Liquid(t) => Bp::Liquid(t), - } - } - - pub fn as_reduced_unsafe(&self) -> &T { - match self { - XChain::Bitcoin(t) | XChain::Liquid(t) => t, - } - } - - /// Maps the value from one internal type into another. - pub fn map(self, f: impl FnOnce(T) -> U) -> XChain { - match self { - Self::Bitcoin(t) => XChain::Bitcoin(f(t)), - Self::Liquid(t) => XChain::Liquid(f(t)), - } - } - - /// Maps the value from a reference on internal type into another. - pub fn map_ref(&self, f: impl FnOnce(&T) -> U) -> XChain { - match self { - Self::Bitcoin(t) => XChain::Bitcoin(f(t)), - Self::Liquid(t) => XChain::Liquid(f(t)), - } - } - - /// Maps the value from one internal type into another, covering cases which - /// may error. - pub fn try_map(self, f: impl FnOnce(T) -> Result) -> Result, E> { - match self { - Self::Bitcoin(t) => f(t).map(XChain::Bitcoin), - Self::Liquid(t) => f(t).map(XChain::Liquid), - } - } - - /// Maps the value from one internal type into another, covering cases which - /// may error. - pub fn try_map_ref(&self, f: impl FnOnce(&T) -> Result) -> Result, E> { - match self { - Self::Bitcoin(t) => f(t).map(XChain::Bitcoin), - Self::Liquid(t) => f(t).map(XChain::Liquid), - } - } - - /// Maps the value from one internal type into another, covering cases which - /// may result in an optional value. - pub fn maybe_map(self, f: impl FnOnce(T) -> Option) -> Option> { - match self { - Self::Bitcoin(t) => f(t).map(XChain::Bitcoin), - Self::Liquid(t) => f(t).map(XChain::Liquid), - } - } - - /// Maps the value from one internal type into another, covering cases which - /// may result in an optional value. - pub fn maybe_map_ref(&self, f: impl FnOnce(&T) -> Option) -> Option> { - match self { - Self::Bitcoin(t) => f(t).map(XChain::Bitcoin), - Self::Liquid(t) => f(t).map(XChain::Liquid), - } - } - - /// Returns iterator over elements - pub fn iter<'i>( - &'i self, - ) -> Box::Item>> + 'i> - where &'i T: IntoIterator { - match self { - XChain::Bitcoin(t) => Box::new(t.into_iter().map(XChain::Bitcoin)), - XChain::Liquid(t) => Box::new(t.into_iter().map(XChain::Liquid)), - } - } -} - -impl Iterator for XChain { - type Item = XChain<::Item>; - - fn next(&mut self) -> Option { - match self { - XChain::Bitcoin(t) => t.next().map(XChain::Bitcoin), - XChain::Liquid(t) => t.next().map(XChain::Liquid), - } - } -} - -#[derive(Clone, Debug, Display, Error, From)] -pub enum XChainParseError { - #[display("unknown chain prefix '{0}'; only 'bc:' and 'lq:' are currently supported")] - UnknownPrefix(String), - - #[from] - #[display(inner)] - Inner(E), -} - -impl FromStr for XChain -where - T: StrictDumb + StrictEncode + StrictDecode, - T::Err: Debug + Display, -{ - type Err = XChainParseError; - - fn from_str(s: &str) -> Result { - if let Some((prefix, s)) = s.split_once(':') { - match prefix { - XCHAIN_BITCOIN_PREFIX => s - .parse() - .map(XChain::Bitcoin) - .map_err(XChainParseError::from), - XCHAIN_LIQUID_PREFIX => s - .parse() - .map(XChain::Liquid) - .map_err(XChainParseError::from), - unknown => Err(XChainParseError::UnknownPrefix(unknown.to_owned())), - } - } else { - s.parse() - .map(XChain::Bitcoin) - .map_err(XChainParseError::from) - } - } -} - -impl Display for XChain -where T: StrictDumb + StrictEncode + StrictDecode -{ - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - XChain::Bitcoin(t) => write!(f, "{XCHAIN_BITCOIN_PREFIX}:{t}"), - XChain::Liquid(t) => write!(f, "{XCHAIN_LIQUID_PREFIX}:{t}"), - } - } -} diff --git a/src/contract/xchain.rs b/src/contract/xchain.rs new file mode 100644 index 00000000..646ce9a4 --- /dev/null +++ b/src/contract/xchain.rs @@ -0,0 +1,343 @@ +// RGB Core Library: consensus layer for RGB smart contracts. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2019-2023 by +// Dr Maxim Orlovsky +// +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// Copyright (C) 2019-2023 Dr Maxim Orlovsky. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::cmp::Ordering; +use std::fmt::{Debug, Display, Formatter}; +use std::io::Write; +use std::str::FromStr; +use std::{fmt, io}; + +use amplify::confinement::TinyOrdSet; +use bp::Bp; +use commit_verify::{CommitEncode, Conceal}; +use strict_encoding::{ + DecodeError, DefineUnion, ReadTuple, ReadUnion, StrictDecode, StrictDumb, StrictEncode, + StrictSum, StrictType, StrictUnion, TypedRead, TypedWrite, WriteUnion, +}; + +use crate::{Layer1, LIB_NAME_RGB}; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] +#[display(lowercase)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB, tags = repr, into_u8, try_from_u8)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", rename_all = "camelCase") +)] +#[repr(u8)] +pub enum AltLayer1 { + #[strict_type(dumb)] + Liquid = 1, + // Abraxas = 0x10, + // Prime = 0x11, +} + +impl AltLayer1 { + pub fn layer1(&self) -> Layer1 { + match self { + AltLayer1::Liquid => Layer1::Liquid, + } + } +} + +#[derive(Wrapper, WrapperMut, Clone, PartialEq, Eq, Hash, Debug, Default, From)] +#[wrapper(Deref)] +#[wrapper_mut(DerefMut)] +#[derive(StrictType, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", transparent) +)] +pub struct AltLayer1Set(TinyOrdSet); + +impl CommitEncode for AltLayer1Set { + fn commit_encode(&self, e: &mut impl Write) { + for c in self.iter() { + e.write_all(&[*c as u8]).ok(); + } + } +} + +pub const XCHAIN_BITCOIN_PREFIX: &str = "bc"; +pub const XCHAIN_LIQUID_PREFIX: &str = "lq"; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", rename_all = "camelCase") +)] +pub enum XChain { + Bitcoin(T), + + Liquid(T), +} + +impl PartialOrd for XChain { + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } +} + +impl Ord for XChain { + fn cmp(&self, other: &Self) -> Ordering { + match (self, other) { + (Self::Bitcoin(_), Self::Liquid(_)) => Ordering::Greater, + (Self::Liquid(_), Self::Bitcoin(_)) => Ordering::Less, + (Self::Bitcoin(t1) | Self::Liquid(t1), Self::Bitcoin(t2) | Self::Liquid(t2)) => { + t1.cmp(t2) + } + } + } +} + +impl Conceal for XChain { + type Concealed = XChain; + + #[inline] + fn conceal(&self) -> Self::Concealed { self.map_ref(|t| t.conceal()) } +} + +impl StrictType for XChain +where T: StrictDumb + StrictType +{ + const STRICT_LIB_NAME: &'static str = crate::LIB_NAME_RGB; +} +impl StrictSum for XChain +where T: StrictDumb + StrictType +{ + const ALL_VARIANTS: &'static [(u8, &'static str)] = &[(0x00, "bitcoin"), (0x01, "liquid")]; + + fn variant_name(&self) -> &'static str { + match self { + XChain::Bitcoin(_) => Self::ALL_VARIANTS[0].1, + XChain::Liquid(_) => Self::ALL_VARIANTS[1].1, + } + } +} +impl StrictUnion for XChain where T: StrictDumb + StrictType {} +impl StrictDumb for XChain +where T: StrictDumb +{ + fn strict_dumb() -> Self { XChain::Bitcoin(strict_dumb!()) } +} +impl StrictEncode for XChain +where T: StrictDumb + StrictEncode +{ + fn strict_encode(&self, writer: W) -> io::Result { + writer.write_union::(|w| { + let w = w + .define_newtype::(fname!(Self::ALL_VARIANTS[0].1)) + .define_newtype::(fname!(Self::ALL_VARIANTS[1].1)) + .complete(); + Ok(match self { + XChain::Bitcoin(t) => w.write_newtype(fname!(Self::ALL_VARIANTS[0].1), t)?, + XChain::Liquid(t) => w.write_newtype(fname!(Self::ALL_VARIANTS[1].1), t)?, + } + .complete()) + }) + } +} +impl StrictDecode for XChain +where T: StrictDumb + StrictDecode +{ + fn strict_decode(reader: &mut impl TypedRead) -> Result { + reader.read_union(|field, r| match field.as_str() { + x if x == Self::ALL_VARIANTS[0].1 => { + r.read_tuple(|r| r.read_field().map(Self::Bitcoin)) + } + x if x == Self::ALL_VARIANTS[1].1 => r.read_tuple(|r| r.read_field().map(Self::Liquid)), + _ => unreachable!(), + }) + } +} + +impl XChain { + pub fn with(layer1: Layer1, data: impl Into) -> Self { + match layer1 { + Layer1::Bitcoin => XChain::Bitcoin(data.into()), + Layer1::Liquid => XChain::Liquid(data.into()), + } + } + + pub fn is_bitcoin(&self) -> bool { matches!(self, XChain::Bitcoin(_)) } + pub fn is_liquid(&self) -> bool { matches!(self, XChain::Liquid(_)) } + pub fn is_bp(&self) -> bool { + match self { + XChain::Bitcoin(_) | XChain::Liquid(_) => true, + } + } + + pub fn layer1(&self) -> Layer1 { + match self { + XChain::Bitcoin(_) => Layer1::Bitcoin, + XChain::Liquid(_) => Layer1::Liquid, + } + } + + pub fn as_bp(&self) -> Bp<&T> + where for<'a> &'a T: StrictDumb + StrictEncode + StrictDecode { + match self { + XChain::Bitcoin(t) => Bp::Bitcoin(t), + XChain::Liquid(t) => Bp::Liquid(t), + } + } + + pub fn into_bp(self) -> Bp + where T: StrictDumb + StrictEncode + StrictDecode { + match self { + XChain::Bitcoin(t) => Bp::Bitcoin(t), + XChain::Liquid(t) => Bp::Liquid(t), + } + } + + pub fn as_reduced_unsafe(&self) -> &T { + match self { + XChain::Bitcoin(t) | XChain::Liquid(t) => t, + } + } + + /// Maps the value from one internal type into another. + pub fn map(self, f: impl FnOnce(T) -> U) -> XChain { + match self { + Self::Bitcoin(t) => XChain::Bitcoin(f(t)), + Self::Liquid(t) => XChain::Liquid(f(t)), + } + } + + /// Maps the value from a reference on internal type into another. + pub fn map_ref(&self, f: impl FnOnce(&T) -> U) -> XChain { + match self { + Self::Bitcoin(t) => XChain::Bitcoin(f(t)), + Self::Liquid(t) => XChain::Liquid(f(t)), + } + } + + /// Maps the value from one internal type into another, covering cases which + /// may error. + pub fn try_map(self, f: impl FnOnce(T) -> Result) -> Result, E> { + match self { + Self::Bitcoin(t) => f(t).map(XChain::Bitcoin), + Self::Liquid(t) => f(t).map(XChain::Liquid), + } + } + + /// Maps the value from one internal type into another, covering cases which + /// may error. + pub fn try_map_ref(&self, f: impl FnOnce(&T) -> Result) -> Result, E> { + match self { + Self::Bitcoin(t) => f(t).map(XChain::Bitcoin), + Self::Liquid(t) => f(t).map(XChain::Liquid), + } + } + + /// Maps the value from one internal type into another, covering cases which + /// may result in an optional value. + pub fn maybe_map(self, f: impl FnOnce(T) -> Option) -> Option> { + match self { + Self::Bitcoin(t) => f(t).map(XChain::Bitcoin), + Self::Liquid(t) => f(t).map(XChain::Liquid), + } + } + + /// Maps the value from one internal type into another, covering cases which + /// may result in an optional value. + pub fn maybe_map_ref(&self, f: impl FnOnce(&T) -> Option) -> Option> { + match self { + Self::Bitcoin(t) => f(t).map(XChain::Bitcoin), + Self::Liquid(t) => f(t).map(XChain::Liquid), + } + } + + /// Returns iterator over elements + pub fn iter<'i>( + &'i self, + ) -> Box::Item>> + 'i> + where &'i T: IntoIterator { + match self { + XChain::Bitcoin(t) => Box::new(t.into_iter().map(XChain::Bitcoin)), + XChain::Liquid(t) => Box::new(t.into_iter().map(XChain::Liquid)), + } + } +} + +impl Iterator for XChain { + type Item = XChain<::Item>; + + fn next(&mut self) -> Option { + match self { + XChain::Bitcoin(t) => t.next().map(XChain::Bitcoin), + XChain::Liquid(t) => t.next().map(XChain::Liquid), + } + } +} + +#[derive(Clone, Debug, Display, Error, From)] +pub enum XChainParseError { + #[display("unknown chain prefix '{0}'; only 'bc:' and 'lq:' are currently supported")] + UnknownPrefix(String), + + #[from] + #[display(inner)] + Inner(E), +} + +impl FromStr for XChain +where + T: StrictDumb + StrictEncode + StrictDecode, + T::Err: Debug + Display, +{ + type Err = XChainParseError; + + fn from_str(s: &str) -> Result { + if let Some((prefix, s)) = s.split_once(':') { + match prefix { + XCHAIN_BITCOIN_PREFIX => s + .parse() + .map(XChain::Bitcoin) + .map_err(XChainParseError::from), + XCHAIN_LIQUID_PREFIX => s + .parse() + .map(XChain::Liquid) + .map_err(XChainParseError::from), + unknown => Err(XChainParseError::UnknownPrefix(unknown.to_owned())), + } + } else { + s.parse() + .map(XChain::Bitcoin) + .map_err(XChainParseError::from) + } + } +} + +impl Display for XChain +where T: StrictDumb + StrictEncode + StrictDecode +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + XChain::Bitcoin(t) => write!(f, "{XCHAIN_BITCOIN_PREFIX}:{t}"), + XChain::Liquid(t) => write!(f, "{XCHAIN_LIQUID_PREFIX}:{t}"), + } + } +} From d598eee90dfa21ef9a34a8376342cffa43dbd4e9 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sat, 30 Dec 2023 01:30:54 +0100 Subject: [PATCH 09/11] contract: add cross-chain output seal to outpoint convertor --- src/contract/xchain.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/contract/xchain.rs b/src/contract/xchain.rs index 646ce9a4..fc9a88ae 100644 --- a/src/contract/xchain.rs +++ b/src/contract/xchain.rs @@ -27,14 +27,14 @@ use std::str::FromStr; use std::{fmt, io}; use amplify::confinement::TinyOrdSet; -use bp::Bp; +use bp::{Bp, Outpoint}; use commit_verify::{CommitEncode, Conceal}; use strict_encoding::{ DecodeError, DefineUnion, ReadTuple, ReadUnion, StrictDecode, StrictDumb, StrictEncode, StrictSum, StrictType, StrictUnion, TypedRead, TypedWrite, WriteUnion, }; -use crate::{Layer1, LIB_NAME_RGB}; +use crate::{Layer1, OutputSeal, LIB_NAME_RGB}; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] #[display(lowercase)] @@ -341,3 +341,7 @@ where T: StrictDumb + StrictEncode + StrictDecode } } } + +impl From> for XChain { + fn from(seal: XChain) -> Self { seal.map(Outpoint::from) } +} From 744560f68518b19eab7ae8135571910282e72e58 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sat, 30 Dec 2023 08:22:55 +0100 Subject: [PATCH 10/11] chore: fix clippy lints --- Cargo.lock | 38 +- Cargo.toml | 8 - src/contract/mod.rs | 3 +- src/contract/xchain.rs | 18 +- src/validation/consignment.rs | 2 +- src/validation/validator.rs | 10 +- stl/RGB@0.1.0.sta | 645 +++++++++++++++++----------------- stl/RGB@0.1.0.stl | Bin 15718 -> 15398 bytes stl/RGB@0.1.0.sty | 173 ++++----- 9 files changed, 448 insertions(+), 449 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6863848f..1e13c7bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -196,7 +196,8 @@ dependencies = [ [[package]] name = "bp-consensus" version = "0.11.0-beta.3" -source = "git+https://github.com/BP-WG/bp-core?branch=v0.11#cbc896d075772a7b4eb620cd8c76cae86ff331c1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190ac89a2a3c79d5bfb677f48e5393691832c540973341831572edaffdce0881" dependencies = [ "amplify", "chrono", @@ -209,8 +210,9 @@ dependencies = [ [[package]] name = "bp-core" -version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-core?branch=v0.11#cbc896d075772a7b4eb620cd8c76cae86ff331c1" +version = "0.11.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69269d27e32d784e37f7ca7cdf964fe5e30c5bff1ca2e229a118c84c8efdd179" dependencies = [ "amplify", "bp-consensus", @@ -225,8 +227,9 @@ dependencies = [ [[package]] name = "bp-dbc" -version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-core?branch=v0.11#cbc896d075772a7b4eb620cd8c76cae86ff331c1" +version = "0.11.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cef9a98c7ae4f1bb333ad8e55c9d8a91f071c347d35b62e19959faa07572b11" dependencies = [ "amplify", "base85", @@ -239,8 +242,9 @@ dependencies = [ [[package]] name = "bp-seals" -version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-core?branch=v0.11#cbc896d075772a7b4eb620cd8c76cae86ff331c1" +version = "0.11.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d211bb77e320abf4bba6c272fc85d73e5203140ca39e299271f4714d8b6029fe" dependencies = [ "amplify", "baid58", @@ -297,7 +301,8 @@ dependencies = [ [[package]] name = "commit_encoding_derive" version = "0.10.0" -source = "git+https://github.com/LNP-BP/client_side_validation?branch=v0.11#2d3a0a2981409c493067edfc94338088884b1aa7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00033f14d67c4169d588f085ea2faeb7b610cf03a74d42ea09eeba31abef2047" dependencies = [ "amplify", "amplify_syn", @@ -308,8 +313,9 @@ dependencies = [ [[package]] name = "commit_verify" -version = "0.11.0-beta.2" -source = "git+https://github.com/LNP-BP/client_side_validation?branch=v0.11#2d3a0a2981409c493067edfc94338088884b1aa7" +version = "0.11.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e001679b9be6a5df24facdae179e6ba1cffb503c875d691eac024db8d0f8d1" dependencies = [ "amplify", "commit_encoding_derive", @@ -772,17 +778,18 @@ dependencies = [ [[package]] name = "single_use_seals" -version = "0.11.0-beta.2" +version = "0.11.0-beta.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c30647a1342641c45ca7c1dcd5ae7db16533b86744e827c84cfed875db2de3fe" +checksum = "1b14ebe6be1e12070208a6f2ceb49f946d835b1f7dfb809a4db025de8f5ffe0a" dependencies = [ "amplify_derive", ] [[package]] name = "strict_encoding" -version = "2.6.1" -source = "git+https://github.com/strict-types/strict-encoding?branch=phantom#2123237a512bbe28e8e419e7d4f899dfedfa758c" +version = "2.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa76decc8ac190a56ba7857c023b69ed52b781ed974c5a181eac62cdbfc99521" dependencies = [ "amplify", "half", @@ -793,7 +800,8 @@ dependencies = [ [[package]] name = "strict_encoding_derive" version = "2.0.1" -source = "git+https://github.com/strict-types/strict-encoding?branch=phantom#2123237a512bbe28e8e419e7d4f899dfedfa758c" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37064ec285e2a633465eb525c8698eea51373dee889fe310e0d32df8343e7f4f" dependencies = [ "amplify_syn", "heck", diff --git a/Cargo.toml b/Cargo.toml index 408c05f5..e2116a13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,11 +59,3 @@ wasm-bindgen-test = "0.3" [package.metadata.docs.rs] features = [ "all" ] - -[patch.crates-io] -strict_encoding = { git = "https://github.com/strict-types/strict-encoding", branch = "phantom" } -commit_verify = { git = "https://github.com/LNP-BP/client_side_validation", branch = "v0.11" } -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" } diff --git a/src/contract/mod.rs b/src/contract/mod.rs index d82f52f3..dd37a503 100644 --- a/src/contract/mod.rs +++ b/src/contract/mod.rs @@ -61,5 +61,6 @@ pub use seal::{ }; pub use state::{ConfidentialState, ExposedState, StateCommitment, StateData, StateType}; pub use xchain::{ - AltLayer1, AltLayer1Set, XChain, XChainParseError, XCHAIN_BITCOIN_PREFIX, XCHAIN_LIQUID_PREFIX, + AltLayer1, AltLayer1Set, XChain, XChainParseError, XOutpoint, XCHAIN_BITCOIN_PREFIX, + XCHAIN_LIQUID_PREFIX, }; diff --git a/src/contract/xchain.rs b/src/contract/xchain.rs index fc9a88ae..1025a847 100644 --- a/src/contract/xchain.rs +++ b/src/contract/xchain.rs @@ -34,7 +34,16 @@ use strict_encoding::{ StrictSum, StrictType, StrictUnion, TypedRead, TypedWrite, WriteUnion, }; -use crate::{Layer1, OutputSeal, LIB_NAME_RGB}; +use crate::{Layer1, OutputSeal, XOutputSeal, LIB_NAME_RGB}; + +pub const XCHAIN_BITCOIN_PREFIX: &str = "bc"; +pub const XCHAIN_LIQUID_PREFIX: &str = "lq"; + +pub type XOutpoint = XChain; + +impl From for XOutpoint { + fn from(seal: XChain) -> Self { seal.map(Outpoint::from) } +} #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] #[display(lowercase)] @@ -81,9 +90,6 @@ impl CommitEncode for AltLayer1Set { } } -pub const XCHAIN_BITCOIN_PREFIX: &str = "bc"; -pub const XCHAIN_LIQUID_PREFIX: &str = "lq"; - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[cfg_attr( feature = "serde", @@ -341,7 +347,3 @@ where T: StrictDumb + StrictEncode + StrictDecode } } } - -impl From> for XChain { - fn from(seal: XChain) -> Self { seal.map(Outpoint::from) } -} diff --git a/src/validation/consignment.rs b/src/validation/consignment.rs index b3429003..805b94f0 100644 --- a/src/validation/consignment.rs +++ b/src/validation/consignment.rs @@ -105,6 +105,6 @@ pub trait ConsignmentApi { /// Returns reference to an anchored bundle given a bundle id. fn anchored_bundle(&self, bundle_id: BundleId) -> Option>; - /// Returns witness id for a given operaiton. + /// Returns witness id for a given operation. fn op_witness_id(&self, opid: OpId) -> Option; } diff --git a/src/validation/validator.rs b/src/validation/validator.rs index c1d6adb8..4fb39ddd 100644 --- a/src/validation/validator.rs +++ b/src/validation/validator.rs @@ -23,8 +23,8 @@ use std::collections::{BTreeMap, BTreeSet, VecDeque}; use bp::dbc::Anchor; -use bp::seals::txout::{CloseMethod, ExplicitSeal, TxoSeal, Witness}; -use bp::{dbc, Outpoint, Txid}; +use bp::seals::txout::{CloseMethod, TxoSeal, Witness}; +use bp::{dbc, Outpoint}; use commit_verify::mpc; use single_use_seals::SealWitness; @@ -34,7 +34,7 @@ use crate::vm::AluRuntime; use crate::{ AltLayer1, BundleId, ContractId, Layer1, OpId, OpRef, OpType, Operation, Opout, Schema, SchemaId, SchemaRoot, Script, SubSchema, Transition, TransitionBundle, TypedAssigns, WitnessId, - XAnchor, XChain, XOutputSeal, XPubWitness, XWitness, + XAnchor, XChain, XOutpoint, XOutputSeal, XPubWitness, XWitness, }; #[derive(Clone, Debug, Display, Error, From)] @@ -434,8 +434,8 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveWitness> &mut self, layer1: Layer1, bundle: &TransitionBundle, - ) -> (Vec>>, BTreeMap>>) { - let mut input_map: BTreeMap>> = bmap!(); + ) -> (Vec, BTreeMap>) { + let mut input_map: BTreeMap> = bmap!(); let mut seals = vec![]; for (opid, transition) in &bundle.known_transitions { let opid = *opid; diff --git a/stl/RGB@0.1.0.sta b/stl/RGB@0.1.0.sta index 731dbcc6..58498c6d 100644 --- a/stl/RGB@0.1.0.sta +++ b/stl/RGB@0.1.0.sta @@ -1,342 +1,335 @@ -----BEGIN STRICT TYPE LIB----- -Id: urn:ubideco:stl:EZiZRCHpyqJakmTU1zkwczq5YKMbapfMhVLhS8DbkpwC +Id: urn:ubideco:stl:2PcZtrPrfQCu27qw8b4Wz4cEqUn2PpgSkDHwF4qVyyrq Name: RGB Dependencies: urn:ubideco:stl:ZtHaBzu9ojbDahaGKEXe5v9DfSDxLERbLkEB23R6Q6V, urn:ubideco:stl:5XLKQ1sNryZm9bdFKU2kBY3MPYdZXhchVdQKBbHA3gby, + urn:ubideco:stl:8wHziC7Bxa3BwctUh6EVBanosrvRHecCTPUjkZT4btNd, urn:ubideco:stl:9KALDYR8Nyjq4FdMW6kYoL7vdkWnqPqNuFnmE9qHpNjZ, urn:ubideco:stl:DVtm25LRKU4TjbyZmVxPhvCmctZ6vKkPKqfpU2QsDNUo, - urn:ubideco:stl:HX2UBak8vPsTokug1DGMDvTpzns3xUdwZ7QJdyt4qBA9, - urn:ubideco:stl:JAeer3PsTTQRrGttsnPXJKdBTJqQwQ5ZwWHiABEDoCyd + urn:ubideco:stl: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 -epZqWm9BJxEOtc1oXMW3SGaN60s2wVyB8KN9nJBwFR3JjQEheOeO8fTFXOuL6OYz -NINEWeLCo1zuLu29MU0CBW9wcmV0AAUBAv8PepZqWm9BJxEOtc1oXMW3SGaN60s2 -wVyB8KN9nJBwZJzntCSPq/YAZMOoWScApID/m34G99TOjEFkYPEXjj0DBGR1YWwA -BgIGdGFwcmV0Av8PepZqWm9BJxEOtc1oXMW3SGaN60s2wVyB8KN9nJBwFR3JjQEh -eOeO8fTFXOuL6OYzNINEWeLCo1zuLu29MU0Fb3ByZXQC/w96lmpab0EnEQ61zWhc -xbdIZo3rSzbBXIHwo32ckHBknOe0JI+r9gBkw6hZJwCkgP+bfgb31M6MQWRg8ReO -PQ5BbmNob3JlZEJ1bmRsZQYCBmFuY2hvcgGZxVAXc7auERPQ2XroFRk48CYXgFuZ -W0yntcwIgzD+dgZidW5kbGUBl8rZ69p5F2EoIh9NRdrLnReYtssM+iHdEznvI1wQ -ThcIQXNzZXRUYWcFAQAHAABAIAAiQXNzaWduUmV2ZWFsZWRBdHRhY2hCbGluZFNl -YWxUeFB0cgQEAAxjb25maWRlbnRpYWwABgIEc2VhbAL/D3qWalpvQScRDrXNaFzF -t0hmjetLNsFcgfCjfZyQcGgZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZO -BXN0YXRlAfw0Rd1fzw0L/6wVpHq6BSLgW4srGzBlUoeDj3st6449ARFjb25maWRl -bnRpYWxTdGF0ZQAGAgRzZWFsATIrudR/Md1UndJjpZtlySca7+QWqSY9uSPt20ym -GJN8BXN0YXRlAfw0Rd1fzw0L/6wVpHq6BSLgW4srGzBlUoeDj3st6449AhBjb25m -aWRlbnRpYWxTZWFsAAYCBHNlYWwC/w96lmpab0EnEQ61zWhcxbdIZo3rSzbBXIHw -o32ckHBoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcDYKmUs70GTgVzdGF0ZQFoUzQg -DNxTxk124rYuqmYv2jrZkb8GqykOvND2egNKzAMIcmV2ZWFsZWQABgIEc2VhbAEy -K7nUfzHdVJ3SY6WbZcknGu/kFqkmPbkj7dtMphiTfAVzdGF0ZQFoUzQgDNxTxk12 -4rYuqmYv2jrZkb8GqykOvND2egNKzCFBc3NpZ25SZXZlYWxlZEF0dGFjaEJsaW5k -U2VhbFR4aWQEBAAMY29uZmlkZW50aWFsAAYCBHNlYWwC/w96lmpab0EnEQ61zWhc -xbdIZo3rSzbBXIHwo32ckHBoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcDYKmUs70G -TgVzdGF0ZQH8NEXdX88NC/+sFaR6ugUi4FuLKxswZVKHg497LeuOPQERY29uZmlk -ZW50aWFsU3RhdGUABgIEc2VhbAGP4NWGbqW8deLy8vTpcvBcoEg1ZB1bagMOtqr4 -EV3W/wVzdGF0ZQH8NEXdX88NC/+sFaR6ugUi4FuLKxswZVKHg497LeuOPQIQY29u -ZmlkZW50aWFsU2VhbAAGAgRzZWFsAv8PepZqWm9BJxEOtc1oXMW3SGaN60s2wVyB -8KN9nJBwaBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2CplLO9Bk4Fc3RhdGUBaFM0 -IAzcU8ZNduK2LqpmL9o62ZG/BqspDrzQ9noDSswDCHJldmVhbGVkAAYCBHNlYWwB -j+DVhm6lvHXi8vL06XLwXKBINWQdW2oDDraq+BFd1v8Fc3RhdGUBaFM0IAzcU8ZN -duK2LqpmL9o62ZG/BqspDrzQ9noDSswgQXNzaWduUmV2ZWFsZWREYXRhQmxpbmRT -ZWFsVHhQdHIEBAAMY29uZmlkZW50aWFsAAYCBHNlYWwC/w96lmpab0EnEQ61zWhc -xbdIZo3rSzbBXIHwo32ckHBoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcDYKmUs70G -TgVzdGF0ZQFwDWUQsoKBbx+3PeUSY5MDVwilUmGtAuDUvlrFz6fqCQERY29uZmlk -ZW50aWFsU3RhdGUABgIEc2VhbAEyK7nUfzHdVJ3SY6WbZcknGu/kFqkmPbkj7dtM -phiTfAVzdGF0ZQFwDWUQsoKBbx+3PeUSY5MDVwilUmGtAuDUvlrFz6fqCQIQY29u -ZmlkZW50aWFsU2VhbAAGAgRzZWFsAv8PepZqWm9BJxEOtc1oXMW3SGaN60s2wVyB -8KN9nJBwaBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2CplLO9Bk4Fc3RhdGUBIPJQ -ViKPZs78kfrqZ7xfxvBpXD1HeXNZP6zRN8pySGUDCHJldmVhbGVkAAYCBHNlYWwB -Miu51H8x3VSd0mOlm2XJJxrv5BapJj25I+3bTKYYk3wFc3RhdGUBIPJQViKPZs78 -kfrqZ7xfxvBpXD1HeXNZP6zRN8pySGUfQXNzaWduUmV2ZWFsZWREYXRhQmxpbmRT -ZWFsVHhpZAQEAAxjb25maWRlbnRpYWwABgIEc2VhbAL/D3qWalpvQScRDrXNaFzF -t0hmjetLNsFcgfCjfZyQcGgZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZO -BXN0YXRlAXANZRCygoFvH7c95RJjkwNXCKVSYa0C4NS+WsXPp+oJARFjb25maWRl -bnRpYWxTdGF0ZQAGAgRzZWFsAY/g1YZupbx14vLy9Oly8FygSDVkHVtqAw62qvgR -Xdb/BXN0YXRlAXANZRCygoFvH7c95RJjkwNXCKVSYa0C4NS+WsXPp+oJAhBjb25m -aWRlbnRpYWxTZWFsAAYCBHNlYWwC/w96lmpab0EnEQ61zWhcxbdIZo3rSzbBXIHw -o32ckHBoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcDYKmUs70GTgVzdGF0ZQEg8lBW -Io9mzvyR+upnvF/G8GlcPUd5c1k/rNE3ynJIZQMIcmV2ZWFsZWQABgIEc2VhbAGP -4NWGbqW8deLy8vTpcvBcoEg1ZB1bagMOtqr4EV3W/wVzdGF0ZQEg8lBWIo9mzvyR -+upnvF/G8GlcPUd5c1k/rNE3ynJIZSFBc3NpZ25SZXZlYWxlZFZhbHVlQmxpbmRT -ZWFsVHhQdHIEBAAMY29uZmlkZW50aWFsAAYCBHNlYWwC/w96lmpab0EnEQ61zWhc -xbdIZo3rSzbBXIHwo32ckHBoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcDYKmUs70G -TgVzdGF0ZQHCUa1l6XUNrJoSWczAhSRc1fexb1LYcjZGLPF1jJ9OXQERY29uZmlk -ZW50aWFsU3RhdGUABgIEc2VhbAEyK7nUfzHdVJ3SY6WbZcknGu/kFqkmPbkj7dtM -phiTfAVzdGF0ZQHCUa1l6XUNrJoSWczAhSRc1fexb1LYcjZGLPF1jJ9OXQIQY29u -ZmlkZW50aWFsU2VhbAAGAgRzZWFsAv8PepZqWm9BJxEOtc1oXMW3SGaN60s2wVyB -8KN9nJBwaBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2CplLO9Bk4Fc3RhdGUBbUTG -9C9qBTpDFQ+m5sIsxOh65SyU+AbUDKXch/Z1jaADCHJldmVhbGVkAAYCBHNlYWwB -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 +AQJ16mNz0/gjFWtGcwnmUPs563h9MyuQVVTYC3BAVUgBUhUdyY0BIXjnjvH0xVzr +i+jmMzSDRFniwqNc7i7tvTFNAgVvcHJldAAFAQJ16mNz0/gjFWtGcwnmUPs563h9 +MyuQVVTYC3BAVUgBUmSc57Qkj6v2AGTDqFknAKSA/5t+BvfUzoxBZGDxF449AwRk +dWFsAAYCBnRhcHJldAJ16mNz0/gjFWtGcwnmUPs563h9MyuQVVTYC3BAVUgBUhUd +yY0BIXjnjvH0xVzri+jmMzSDRFniwqNc7i7tvTFNBW9wcmV0AnXqY3PT+CMVa0Zz +CeZQ+znreH0zK5BVVNgLcEBVSAFSZJzntCSPq/YAZMOoWScApID/m34G99TOjEFk +YPEXjj0OQW5jaG9yZWRCdW5kbGUGAgZhbmNob3IB788ASS9fnMrBefV9mYPcaKsK +UAgMD4v7j4JDc4mrMpYGYnVuZGxlAW4ba6tKdf/9D1V9TiZ8CDv9KXLk8niNIw+o +xgXYfZXDCEFzc2V0VGFnBQEABwAAQCAAIkFzc2lnblJldmVhbGVkQXR0YWNoQmxp +bmRTZWFsVHhQdHIEBAAMY29uZmlkZW50aWFsAAYCBHNlYWwB24paGoIUEHUo8viM +QENqxEdddHiJx9s6XhHGvtZptQQFc3RhdGUB/DRF3V/PDQv/rBWkeroFIuBbiysb +MGVSh4OPey3rjj0BEWNvbmZpZGVudGlhbFN0YXRlAAYCBHNlYWwBj7vC6EeaTuRN +4l1xf736E7jU0ZG0bZHBACIGG+GKcTUFc3RhdGUB/DRF3V/PDQv/rBWkeroFIuBb +iysbMGVSh4OPey3rjj0CEGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAHbiloaghQQ +dSjy+IxAQ2rER110eInH2zpeEca+1mm1BAVzdGF0ZQFoUzQgDNxTxk124rYuqmYv +2jrZkb8GqykOvND2egNKzAMIcmV2ZWFsZWQABgIEc2VhbAGPu8LoR5pO5E3iXXF/ +vfoTuNTRkbRtkcEAIgYb4YpxNQVzdGF0ZQFoUzQgDNxTxk124rYuqmYv2jrZkb8G +qykOvND2egNKzCFBc3NpZ25SZXZlYWxlZEF0dGFjaEJsaW5kU2VhbFR4aWQEBAAM +Y29uZmlkZW50aWFsAAYCBHNlYWwB24paGoIUEHUo8viMQENqxEdddHiJx9s6XhHG +vtZptQQFc3RhdGUB/DRF3V/PDQv/rBWkeroFIuBbiysbMGVSh4OPey3rjj0BEWNv +bmZpZGVudGlhbFN0YXRlAAYCBHNlYWwBggwNr2v9NAF3KKi+UnCYed/Skak6e5lC +h7SXv6GnqdsFc3RhdGUB/DRF3V/PDQv/rBWkeroFIuBbiysbMGVSh4OPey3rjj0C +EGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAHbiloaghQQdSjy+IxAQ2rER110eInH +2zpeEca+1mm1BAVzdGF0ZQFoUzQgDNxTxk124rYuqmYv2jrZkb8GqykOvND2egNK +zAMIcmV2ZWFsZWQABgIEc2VhbAGCDA2va/00AXcoqL5ScJh539KRqTp7mUKHtJe/ +oaep2wVzdGF0ZQFoUzQgDNxTxk124rYuqmYv2jrZkb8GqykOvND2egNKzCBBc3Np +Z25SZXZlYWxlZERhdGFCbGluZFNlYWxUeFB0cgQEAAxjb25maWRlbnRpYWwABgIE +c2VhbAHbiloaghQQdSjy+IxAQ2rER110eInH2zpeEca+1mm1BAVzdGF0ZQFwDWUQ +soKBbx+3PeUSY5MDVwilUmGtAuDUvlrFz6fqCQERY29uZmlkZW50aWFsU3RhdGUA +BgIEc2VhbAGPu8LoR5pO5E3iXXF/vfoTuNTRkbRtkcEAIgYb4YpxNQVzdGF0ZQFw +DWUQsoKBbx+3PeUSY5MDVwilUmGtAuDUvlrFz6fqCQIQY29uZmlkZW50aWFsU2Vh +bAAGAgRzZWFsAduKWhqCFBB1KPL4jEBDasRHXXR4icfbOl4Rxr7WabUEBXN0YXRl +ASDyUFYij2bO/JH66me8X8bwaVw9R3lzWT+s0TfKckhlAwhyZXZlYWxlZAAGAgRz +ZWFsAY+7wuhHmk7kTeJdcX+9+hO41NGRtG2RwQAiBhvhinE1BXN0YXRlASDyUFYi +j2bO/JH66me8X8bwaVw9R3lzWT+s0TfKckhlH0Fzc2lnblJldmVhbGVkRGF0YUJs +aW5kU2VhbFR4aWQEBAAMY29uZmlkZW50aWFsAAYCBHNlYWwB24paGoIUEHUo8viM +QENqxEdddHiJx9s6XhHGvtZptQQFc3RhdGUBcA1lELKCgW8ftz3lEmOTA1cIpVJh +rQLg1L5axc+n6gkBEWNvbmZpZGVudGlhbFN0YXRlAAYCBHNlYWwBggwNr2v9NAF3 +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 -MEQwmOBcmxtabNYe7XYNYd7LgCbTZPMW4bsDCHJldmVhbGVkAAYCBHNlYWwBMiu5 -1H8x3VSd0mOlm2XJJxrv5BapJj25I+3bTKYYk3wFc3RhdGUBLrKl/hfAMEQwmOBc -mxtabNYe7XYNYd7LgCbTZPMW4bscQXNzaWduVm9pZFN0YXRlQmxpbmRTZWFsVHhp -ZAQEAAxjb25maWRlbnRpYWwABgIEc2VhbAL/D3qWalpvQScRDrXNaFzFt0hmjetL -NsFcgfCjfZyQcGgZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0YXRl -AS6ypf4XwDBEMJjgXJsbWmzWHu12DWHey4Am02TzFuG7ARFjb25maWRlbnRpYWxT -dGF0ZQAGAgRzZWFsAY/g1YZupbx14vLy9Oly8FygSDVkHVtqAw62qvgRXdb/BXN0 -YXRlAS6ypf4XwDBEMJjgXJsbWmzWHu12DWHey4Am02TzFuG7AhBjb25maWRlbnRp -YWxTZWFsAAYCBHNlYWwC/w96lmpab0EnEQ61zWhcxbdIZo3rSzbBXIHwo32ckHBo -Geu81bMYq5ezmKVLNmXd2qcGb+jpJOcDYKmUs70GTgVzdGF0ZQEusqX+F8AwRDCY -4FybG1ps1h7tdg1h3suAJtNk8xbhuwMIcmV2ZWFsZWQABgIEc2VhbAGP4NWGbqW8 -deLy8vTpcvBcoEg1ZB1bagMOtqr4EV3W/wVzdGF0ZQEusqX+F8AwRDCY4FybG1ps -1h7tdg1h3suAJtNk8xbhuw5Bc3NpZ25tZW50VHlwZQUBAAACGUFzc2lnbm1lbnRz -QmxpbmRTZWFsVHhQdHIFAQAKAYf+4mVYiGzoHL6GhLN5YycTZYPFtmgBXosUFjax -RIe5AQmnOaYlL9ZQ37XLgQ1GorvK79uuf9SfV8DW9KaNppFYAAAAAAAAAAD/AAAA -AAAAABhBc3NpZ25tZW50c0JsaW5kU2VhbFR4aWQFAQAKAYf+4mVYiGzoHL6GhLN5 -YycTZYPFtmgBXosUFjaxRIe5AdhU/cMbJJ9h8MMhrTxjRDQZFphQCv+KQVmH5LAh -xQqeAAAAAAAAAAD/AAAAAAAAAAhBdHRhY2hJZAUBAAcAAEAgAA5CbGluZGluZ0Zh -Y3RvcgUBAAcAAEAgAA9Db25jZWFsZWRBdHRhY2gFAQAHAABAIAANQ29uY2VhbGVk -RGF0YQUBAAcAAEAgABFDb25jZWFsZWRGdW5naWJsZQYCCmNvbW1pdG1lbnQBSL0a -bhf7hjsWfH4lXjVn24JD7ypeuuuixQrNCa6eURoKcmFuZ2VQcm9vZgGoWGv4kWXa -wiMQbb2FxIbJN+awZusMZkH/Fi9oqHelmA9Db250cmFjdEhpc3RvcnkGCAhzY2hl -bWFJZAGUUtPbA6urqFGfp/Y+0BTr1E19MT/8/gD6XSR6VASQEAxyb290U2NoZW1h -SWQABAIABG5vbmUAAAABBHNvbWUABQEBlFLT2wOrq6hRn6f2PtAU69RNfTE//P4A -+l0kelQEkBAKY29udHJhY3RJZAGfCCxJOsgCorrF3dwLInwgr5TUaMRIzxpaIeC9 -wvU6MgZnbG9iYWwACgHV7pIOSYizafFqU9EHSvu1I/jHUnEe+zY9VlkF4eQVywAK -AXr1eQ2MbiC0ffAf9c2FafXLvPEQwa9lbLWQopa0q/8BASDyUFYij2bO/JH66me8 -X8bwaVw9R3lzWT+s0TfKckhlAAAAAAAAAAD/////AAAAAAAAAAAAAAAA/wAAAAAA -AAAGcmlnaHRzAAkB/SJAruTdGn92qsPo+HThsMkM0mwRo9V6Ej5aXe6NI90AAAAA -AAAAAP////8AAAAACWZ1bmdpYmxlcwAJAf7HiZQ9NFdMrqCyVD9H4N6WYikvspMC -4D7liTeGXC/vAAAAAAAAAAD/////AAAAAARkYXRhAAkB1eytlOVewGsiTXqlefK0 -IRLGCijlAhqirarpHuVdkxEAAAAAAAAAAP////8AAAAABmF0dGFjaAAJAea+zg49 -PscHc9XEEdXZLc6Zl6HkvAi4GQlTuCISqYhAAAAAAAAAAAD/////AAAAAApDb250 -cmFjdElkBQEABwAAQCAADUNvbnRyYWN0U3RhdGUGAgZzY2hlbWEBEEGCcjUXHNmC -lZKmmRf+A3E/rW/i0yFOiMH+HKOnCp0HaGlzdG9yeQEFcDTTk5Ku4B/HHwwpnmv0 -zHMHjLTnCydUsvvgw3kA9QlFeHRlbnNpb24GCANmZnYB2ptRE1gWVnaQh/uZ5VaU -cjaA1zkMBqHMJJgsBWT3zNUKY29udHJhY3RJZAGfCCxJOsgCorrF3dwLInwgr5TU -aMRIzxpaIeC9wvU6Mg1leHRlbnNpb25UeXBlAWR1HkKpFaE8QxGC1f+G1RzTRHCj -kOFK7KBQzDpTb0jeCG1ldGFkYXRhAAgAAEAAAAAAAAAAAP//AAAAAAAAB2dsb2Jh -bHMBojPJ6ChOzHWY/AY55zTQywKMSyzchJE+mdmqRj4k1lILYXNzaWdubWVudHMB -kOSKpR1G+zzkmwX42/lUPNr4/CAesMqkxeXT2AOpR/YIcmVkZWVtZWQB38+pkfWH -5U1EtwEVXAAp/JVrm/HNqUGOYziWqsAqg+gJdmFsZW5jaWVzAd2FZgBvYWQRFO6O -8deq+AmzGiSwqiepm7Iw6KrPKUdND0V4dGVuc2lvblNjaGVtYQYFCG1ldGFkYXRh +aWFsU3RhdGUABgIEc2VhbAGCDA2va/00AXcoqL5ScJh539KRqTp7mUKHtJe/oaep +2wVzdGF0ZQEusqX+F8AwRDCY4FybG1ps1h7tdg1h3suAJtNk8xbhuwIQY29uZmlk +ZW50aWFsU2VhbAAGAgRzZWFsAduKWhqCFBB1KPL4jEBDasRHXXR4icfbOl4Rxr7W +abUEBXN0YXRlAS6ypf4XwDBEMJjgXJsbWmzWHu12DWHey4Am02TzFuG7AwhyZXZl +YWxlZAAGAgRzZWFsAYIMDa9r/TQBdyiovlJwmHnf0pGpOnuZQoe0l7+hp6nbBXN0 +YXRlAS6ypf4XwDBEMJjgXJsbWmzWHu12DWHey4Am02TzFuG7DkFzc2lnbm1lbnRU +eXBlBQEAAAIZQXNzaWdubWVudHNCbGluZFNlYWxUeFB0cgUBAAoBh/7iZViIbOgc +voaEs3ljJxNlg8W2aAFeixQWNrFEh7kBpbFI1A98HeTRGo3JsVJtuiNYUchQHz7F +F50oj2E2m4gAAAAAAAAAAP8AAAAAAAAAGEFzc2lnbm1lbnRzQmxpbmRTZWFsVHhp +ZAUBAAoBh/7iZViIbOgcvoaEs3ljJxNlg8W2aAFeixQWNrFEh7kBTIdke4cooXWf +mtNng98ms5VTvLhnSA9pjsBvEiBKzE8AAAAAAAAAAP8AAAAAAAAACEF0dGFjaElk +BQEABwAAQCAADkJsaW5kaW5nRmFjdG9yBQEABwAAQCAAD0NvbmNlYWxlZEF0dGFj +aAUBAAcAAEAgAA1Db25jZWFsZWREYXRhBQEABwAAQCAAEUNvbmNlYWxlZEZ1bmdp +YmxlBgIKY29tbWl0bWVudAFIvRpuF/uGOxZ8fiVeNWfbgkPvKl6666LFCs0Jrp5R +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 +KLxDqBEO2zPLjy8KnkhGAAAAAAAAAAD/AAAAAAAAAAlNZWRpYVR5cGUDAQNhbnn/ +CU5vaXNlRHVtYgUBAAcAAEAAAgtPY2N1cnJlbmNlcwYCA21pbgAAAgNtYXgAAAIE +T3BJZAUBAAcAAEAgAAVPcG91dAYDAm9wAZXI5noedWJf1JZVQmqR635CkKFvWpjx +vlD3tookEvfFAnR5AYf+4mVYiGzoHL6GhLN5YycTZYPFtmgBXosUFjaxRIe5Am5v +AAACHk91dHB1dEFzc2lnbm1lbnRSZXZlYWxlZEF0dGFjaAYEBW9wb3V0AZMQvICx +NFqLL8NYUu4PTWjQHbf6NcubZJKrVgma9JqXBHNlYWwB0WcXvUMS8PYJhO7+bk0e +CwMDJ/lOyLsFkizEyTEnlqoFc3RhdGUBaFM0IAzcU8ZNduK2LqpmL9o62ZG/Bqsp +DrzQ9noDSswHd2l0bmVzcwAEAgAEbm9uZQAAAAEEc29tZQAFAQGJa1glzcaQX1ha +x2CoUBexF5DFX+SZQXrRAyDwI0cb8RxPdXRwdXRBc3NpZ25tZW50UmV2ZWFsZWRE +YXRhBgQFb3BvdXQBkxC8gLE0Wosvw1hS7g9NaNAdt/o1y5tkkqtWCZr0mpcEc2Vh +bAHRZxe9QxLw9gmE7v5uTR4LAwMn+U7IuwWSLMTJMSeWqgVzdGF0ZQEg8lBWIo9m +zvyR+upnvF/G8GlcPUd5c1k/rNE3ynJIZQd3aXRuZXNzAAQCAARub25lAAAAAQRz +b21lAAUBAYlrWCXNxpBfWFrHYKhQF7EXkMVf5JlBetEDIPAjRxvxHU91dHB1dEFz +c2lnbm1lbnRSZXZlYWxlZFZhbHVlBgQFb3BvdXQBkxC8gLE0Wosvw1hS7g9NaNAd +t/o1y5tkkqtWCZr0mpcEc2VhbAHRZxe9QxLw9gmE7v5uTR4LAwMn+U7IuwWSLMTJ +MSeWqgVzdGF0ZQFtRMb0L2oFOkMVD6bmwizE6HrlLJT4BtQMpdyH9nWNoAd3aXRu +ZXNzAAQCAARub25lAAAAAQRzb21lAAUBAYlrWCXNxpBfWFrHYKhQF7EXkMVf5JlB +etEDIPAjRxvxGU91dHB1dEFzc2lnbm1lbnRWb2lkU3RhdGUGBAVvcG91dAGTELyA +sTRaiy/DWFLuD01o0B23+jXLm2SSq1YJmvSalwRzZWFsAdFnF71DEvD2CYTu/m5N +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 -AQAIASDyUFYij2bO/JH66me8X8bwaVw9R3lzWT+s0TfKckhlAQAAAAAAAAD//wAA -AAAAAAVJbnB1dAYCB3ByZXZPdXQBkxC8gLE0Wosvw1hS7g9NaNAdt/o1y5tkkqtW -CZr0mpcIcmVzZXJ2ZWQBRSqlX33WAUqLoW3EZjK2D/G1bNw6Uy7PvWuT1WcZnokG -SW5wdXRzBQEACQHJQkIi7QK+R2n9TyMds0VOpyi8Q6gRDtszy48vCp5IRgAAAAAA -AAAA/wAAAAAAAAAJTWVkaWFUeXBlAwEDYW55/wlOb2lzZUR1bWIFAQAHAABAAAIL -T2NjdXJyZW5jZXMGAgNtaW4AAAIDbWF4AAACBE9wSWQFAQAHAABAIAAFT3BvdXQG -AwJvcAGVyOZ6HnViX9SWVUJqket+QpChb1qY8b5Q97aKJBL3xQJ0eQGH/uJlWIhs -6By+hoSzeWMnE2WDxbZoAV6LFBY2sUSHuQJubwAAAh5PdXRwdXRBc3NpZ25tZW50 -UmV2ZWFsZWRBdHRhY2gGBAVvcG91dAGTELyAsTRaiy/DWFLuD01o0B23+jXLm2SS -q1YJmvSalwRzZWFsAUoFt5mdCYe5AtLJVDszmfUiviG8NKXomi1VSZUrFsZcBXN0 -YXRlAWhTNCAM3FPGTXbiti6qZi/aOtmRvwarKQ680PZ6A0rMB3dpdG5lc3MABAIA -BG5vbmUAAAABBHNvbWUABQEBxKAKnuEJAhN3IgEVWTuDw1PiYPiBEOSzNsrjaEZB -JxscT3V0cHV0QXNzaWdubWVudFJldmVhbGVkRGF0YQYEBW9wb3V0AZMQvICxNFqL -L8NYUu4PTWjQHbf6NcubZJKrVgma9JqXBHNlYWwBSgW3mZ0Jh7kC0slUOzOZ9SK+ -Ibw0peiaLVVJlSsWxlwFc3RhdGUBIPJQViKPZs78kfrqZ7xfxvBpXD1HeXNZP6zR -N8pySGUHd2l0bmVzcwAEAgAEbm9uZQAAAAEEc29tZQAFAQHEoAqe4QkCE3ciARVZ -O4PDU+Jg+IEQ5LM2yuNoRkEnGx1PdXRwdXRBc3NpZ25tZW50UmV2ZWFsZWRWYWx1 -ZQYEBW9wb3V0AZMQvICxNFqLL8NYUu4PTWjQHbf6NcubZJKrVgma9JqXBHNlYWwB -SgW3mZ0Jh7kC0slUOzOZ9SK+Ibw0peiaLVVJlSsWxlwFc3RhdGUBbUTG9C9qBTpD -FQ+m5sIsxOh65SyU+AbUDKXch/Z1jaAHd2l0bmVzcwAEAgAEbm9uZQAAAAEEc29t -ZQAFAQHEoAqe4QkCE3ciARVZO4PDU+Jg+IEQ5LM2yuNoRkEnGxlPdXRwdXRBc3Np -Z25tZW50Vm9pZFN0YXRlBgQFb3BvdXQBkxC8gLE0Wosvw1hS7g9NaNAdt/o1y5tk -kqtWCZr0mpcEc2VhbAFKBbeZnQmHuQLSyVQ7M5n1Ir4hvDSl6JotVUmVKxbGXAVz -dGF0ZQEusqX+F8AwRDCY4FybG1ps1h7tdg1h3suAJtNk8xbhuwd3aXRuZXNzAAQC -AARub25lAAAAAQRzb21lAAUBAcSgCp7hCQITdyIBFVk7g8NT4mD4gRDkszbK42hG -QScbElBlZGVyc2VuQ29tbWl0bWVudAUBAAcAAEAhAApSYW5nZVByb29mBAH/C3Bs -YWNlaG9sZGVyAAUBAR52F/Enfds+u+FqD3IRt23tVd9vQw1VEV8DeCelQlcnCFJl -ZGVlbWVkBQEACgFG7ebDCBz9uOZXpCpc4MYIhH/8H75edrlxdKnK9YlZzgGVyOZ6 -HnViX9SWVUJqket+QpChb1qY8b5Q97aKJBL3xQAAAAAAAAAA/wAAAAAAAAAMUmVz -ZXJ2ZWRCeXRlBQEAAAEOUmV2ZWFsZWRBdHRhY2gGAwJpZAGEcQ2TLE70w7cIS2mL -sIdwKX45ZCR/RZHY2oNt0X26OAltZWRpYVR5cGUBQjBhhYjI1KsUJkDH/ckXLM3Q -/xD/DZOAAER8iJrdzHkEc2FsdAAACAxSZXZlYWxlZERhdGEFAQAIAABAAAAAAAAA -AAD//wAAAAAAABBSZXZlYWxlZEZ1bmdpYmxlBgMFdmFsdWUBpowwkUdLyjrCYozH -lUSNLN2qxRSfBTOVq/Y8VWEHFyYIYmxpbmRpbmcBhbj4K7v0pKYNs78/RbgPlG54 -g8OtUm2T9G+jAahuR9oDdGFnAcmPmqnDBksa/f1nG5e8Kvz+TGiPH3PILetxb+dp -myKvBlNjaGVtYQYKA2ZmdgHam1ETWBZWdpCH+5nlVpRyNoDXOQwGocwkmCwFZPfM -1QhzdWJzZXRPZgAEAgAEbm9uZQAAAAEEc29tZQAFAQAAAAtnbG9iYWxUeXBlcwAK -AdXukg5JiLNp8WpT0QdK+7Uj+MdScR77Nj1WWQXh5BXLAceYpthjNnhEHtpRbiw+ -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 -EZkTOiWK/88iLMnUXpE3GwaRg/WqBA== +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 -----END STRICT TYPE LIB----- diff --git a/stl/RGB@0.1.0.stl b/stl/RGB@0.1.0.stl index fc7723e03939f7519da215925e72c29d66fc01df..f3df00c2259448db12c64fc60389a2c334b9017e 100644 GIT binary patch delta 3339 zcmb7{dpJ~S7{F)9WiVzKjJq?Y9JgxAP%af2xn)esW|Par#H>3rYa-;99TNJ;wbHZE zMJdgYP34l2Y?qyMdrT`NPn#}CC9>AAf9^)+pRpa~rdU}N`tEqD9P8-ohW3%)Om~inX9AotRFvvd^!dmd*+-PZ zQ7R{wh0(rTb83V37)iw{{(OIh|Cb!UV`Op)-h~?*$qHeyw=)@$JGiV+z(Y@f6ZR0l zTP9Hs?ag;oq@f|CaX~%C0y(Hi2?9DwYRG~07zmb&iH&EnWe`U$QnGb>e;?((I;g6r zec#8F^2}%9bxL$a6{qm(HH#mf z;O3KYXS(E`rj*dIauv^aN{ZiWV)V>&T)$kF-7PS*f7mv>8S7pT^C<1wpo8A!)c zjaoWhxmM7Iy@mqf(ymVvnjeaa1W`rpC}VlO!Q43O`4|`|tLjQYYg2_mzN!WyQ4%MQ zwUdIFj&+7IRInE#W+~`I(=#`GV$1eDe^7MRF1dVjj-a&lEQFsC}vKk*Gr5$4KmTrfLluV;mU7FQJYa&-4i2#E-f;8z1 z)_?(43%UA&HKbWv5 z8`_!^nw(>Ll5?V>H!SnH3BP3fwdOE49P4mfj5_3T$D1#!SC57i1X(4gVtUjxBStCu zhj@vz$zCY|LyB)-t{Lfgk&u=LMEai4RQ7iyCP?t;E)v~abdunaXc z-o0u!a4;~~*}F@(v67xE1DWzu#F@0LsLK3{!UxM}?4}>i&G@#)q5iq%<}b1T! zb!ZP}O5Vgq+}b(pKy2q9)Y}uV$M8>MzIKS-xoYd2 zH)*MWL9F2bM1DD}6>fv?$IS9~!p&=HCb_{q2Ra{#F_gZ~ij>=P#I-CxmO zhRU+C@E2Nli`)^*&f9C?qvwF+k0F2!NxTTZ6?eM|gZy^g3O++8HV_MiLBr)vNg`Qj zjO?m6t`8ts8z`GvAtNQ*RJJKH(qIAk%#?zR{Lah;LNWv;X70!&$#=HG+z^q*4>4zB zAm60A;t$6^Bpd0_(tGM(_g$I2yNOzraYx*dlfJu3Fb&=tTL5E<5TbvfC1#IpxZsRy z@-bBBtswnLIrxFd@pQk}bo?mAyw^jJ1#_cK*nUsAX>MOdyjQ?2j01|&Za5R9b7Q=^>&lalpkTc*)V{ae zW~GPc2#@^R^Es~KVdJqr0g0Xd*pu)i5LTc~yWdW|87LPTM)^KqpstVee{T zqvog5{c+t}{OD9Bm%8AjaL~0nxxZxarbFsQlK+jNRddA`^?RD9T6H4VMZ0~~0R-D7 z#Ki%J?IQ3vKj&};E7~bKBs?a5JChC2_Wo3j#fO_CS<#`3-71|Ozf1Au-;5ur~ z(%Fyhlvp$QJN`Ysgy=%cS-MBH=zA1i;Ha-ukSVSEMr}(rChi!i7F z{$`Z`6a<$D1gJvhXiP$45{svRL7`#e7qK=-MCtHBi&zM>s_Ocb2m;B;ZH;*1{gJen z+PY4&CgdMNN<#c0d zs&&Azq_k_9w6NK_-jOUeUjkWD}4&DI_3pQn;LVI}vHW|Dl9;ay5|fPRbdl*Ek^Tb|i@o z;+FDrnTy@}#up-IyD1H>(VOjbNyUMxOF5o^N|UCl=f*=V>9dB?yC>tM{hfblbzNa`Tk|UmTP`o5K)xmgEat+}1*I3caboUmI6-Jp^H3PJG z<3zUOsZgsKkgl(@SM<8!>_qr24%I)cDs`@fLb$DJI)T=!Y^Zy+ZXyie6~3;ec!0g4Y!TG%E_W0ylBL7prq(GNvFy+UF^k1UzURzIw?jbzZ41;>p;|tU zMH7!uQQCVLncX{|?UNnXlJFgb&)Hi&>H1)S<_E7N|Ab|mtbZx8#Z5ffP3L(qrdi&^i&eY^6dg8iAvQorUeMKtwj>6IG~rmDAb zG1ef_o(k!0VwBuXD9vF^a#E=G?C2^??|sZ+n+Q{D!Jh=TpKk$?0~MMwQKH_!bze0e zCPn4uaW8Rb7P0KQ_7hRg_U_QwGpoMxBT9a45(Ds;NkNL6lxjk=N5-K!8x8=F7Fx`-$aN|?BF;$v@adCQo+CsYe=5mMb4@-sL zo6o&C*8s?lZDw%S5E~rFi)SS#`+^)sDR|?^QBmIQ86V9~NCIr95GCSn`DhD-P6qIU zWjz|QKq+zK4jzh&K{V4YAHWe-5^Une#m4{}7czX7?-W;=W%+ ^ ..0xff [Byte]}, entryPoints {[By 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:Af6Yix94PXQE73AAR55UvXUsSZgARZEnAZGiJYeo987E#cowboy-gallop-miller -data AnchoredBundle :: anchor XAnchor, bundle TransitionBundle +-- urn:ubideco:semid:8N8sQGRwEmmxKLwUiE4GQ89ziPzgBi7BfSjaNz9ozRKv#verbal-slow-lemon +data AnchoredBundle :: anchor XChainAnchorSet, bundle TransitionBundle -- urn:ubideco:semid:EZoxBpGenvb9UVze1zuwuEHqQJAqw2m3T8za5gbX1JZk#buzzer-pattern-craft data AssetTag :: [Byte ^ 32] --- urn:ubideco:semid:FoWWT72VexCqqTZe4BTUWd3rGFmdgmjgzG4BtEXAhrFG#clark-yoga-needle -data AssignRevealedAttachBlindSealTxPtr :: confidential (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state ConcealedAttach) - | confidentialState (seal XSealBlindSealTxPtr, state ConcealedAttach) - | confidentialSeal (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state RevealedAttach) - | 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 XSealBlindSealTxid, state ConcealedAttach) - | confidentialSeal (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state RevealedAttach) - | 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 XSealBlindSealTxPtr, state ConcealedData) - | confidentialSeal (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state RevealedData) - | 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 XSealBlindSealTxid, state ConcealedData) - | confidentialSeal (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state RevealedData) - | 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 XSealBlindSealTxPtr, state ConcealedFungible) - | confidentialSeal (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state RevealedFungible) - | 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 XSealBlindSealTxid, state ConcealedFungible) - | confidentialSeal (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state RevealedFungible) - | 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 XSealBlindSealTxPtr, state VoidState) - | confidentialSeal (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state VoidState) - | 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 XSealBlindSealTxid, state VoidState) - | confidentialSeal (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state VoidState) - | revealed (seal XSealBlindSealTxid, state VoidState) +-- urn:ubideco:semid:86oEReUQtpkBF5ag57qt8QYeRoADcnsYVWUhd1hZYF9B#police-castle-fractal +data AssignRevealedAttachBlindSealTxPtr :: confidential (seal XChainSecretSeal, state ConcealedAttach) + | confidentialState (seal XChainBlindSealTxPtr, state ConcealedAttach) + | confidentialSeal (seal XChainSecretSeal, state RevealedAttach) + | revealed (seal XChainBlindSealTxPtr, state RevealedAttach) +-- urn:ubideco:semid:GMetkMHmE6pvWF7v4cVkh5FQW1cBWSb4F38oge4Le8wG#parker-locate-madrid +data AssignRevealedAttachBlindSealTxid :: confidential (seal XChainSecretSeal, state ConcealedAttach) + | confidentialState (seal XChainBlindSealTxid, state ConcealedAttach) + | confidentialSeal (seal XChainSecretSeal, state RevealedAttach) + | revealed (seal XChainBlindSealTxid, state RevealedAttach) +-- urn:ubideco:semid:AbqXzWbeC4QTe3RGDg1tgZoq8Cg79wPfgxC418mbLZWc#fabric-exotic-road +data AssignRevealedDataBlindSealTxPtr :: confidential (seal XChainSecretSeal, state ConcealedData) + | confidentialState (seal XChainBlindSealTxPtr, state ConcealedData) + | confidentialSeal (seal XChainSecretSeal, state RevealedData) + | revealed (seal XChainBlindSealTxPtr, state RevealedData) +-- urn:ubideco:semid:8DRTcwcPXjALBUhs8QSutcQj4KyxejP6nVGpmSPcma5s#iron-design-king +data AssignRevealedDataBlindSealTxid :: confidential (seal XChainSecretSeal, state ConcealedData) + | confidentialState (seal XChainBlindSealTxid, state ConcealedData) + | confidentialSeal (seal XChainSecretSeal, state RevealedData) + | revealed (seal XChainBlindSealTxid, state RevealedData) +-- urn:ubideco:semid:HpDAZ3iWVWNFP5oUrXNSifNop1wHGmfK7csUXkWbf3jE#avatar-house-ammonia +data AssignRevealedValueBlindSealTxPtr :: confidential (seal XChainSecretSeal, state ConcealedFungible) + | confidentialState (seal XChainBlindSealTxPtr, state ConcealedFungible) + | confidentialSeal (seal XChainSecretSeal, state RevealedFungible) + | revealed (seal XChainBlindSealTxPtr, state RevealedFungible) +-- urn:ubideco:semid:DFdcr9izr4NmVJJojKFXWPPYnWVM3RNU2K6oxMhDZmYx#popular-vista-urgent +data AssignRevealedValueBlindSealTxid :: confidential (seal XChainSecretSeal, state ConcealedFungible) + | confidentialState (seal XChainBlindSealTxid, state ConcealedFungible) + | confidentialSeal (seal XChainSecretSeal, state RevealedFungible) + | revealed (seal XChainBlindSealTxid, state RevealedFungible) +-- urn:ubideco:semid:6Nr3ZJAAV8ukSmVJHChmUkbGFzdAbCjzXbmoxrDNcmwc#slogan-change-green +data AssignVoidStateBlindSealTxPtr :: confidential (seal XChainSecretSeal, state VoidState) + | confidentialState (seal XChainBlindSealTxPtr, state VoidState) + | confidentialSeal (seal XChainSecretSeal, state VoidState) + | revealed (seal XChainBlindSealTxPtr, state VoidState) +-- urn:ubideco:semid:BqQbSTu2tdDVLMnTx2rpKFYF47oEwrc2fGrTZGzDrueV#classic-cubic-ingrid +data AssignVoidStateBlindSealTxid :: confidential (seal XChainSecretSeal, state VoidState) + | confidentialState (seal XChainBlindSealTxid, state VoidState) + | confidentialSeal (seal XChainSecretSeal, state VoidState) + | revealed (seal XChainBlindSealTxid, state VoidState) -- urn:ubideco:semid:A9sThAqgwKPfuJcR4GDfTQHUAbbS5sbEXG5XVk7FZHEg#hunter-hello-retro data AssignmentType :: U16 -- urn:ubideco:semid:Bu8iXz4MmJnuUMW6AFAepa8BAa4DXLVAMdrNrkLh3S7a#granite-history-canvas @@ -220,26 +220,26 @@ data OpId :: [Byte ^ 32] data Opout :: op OpId , ty AssignmentType , no U16 --- urn:ubideco:semid:GNcKtjWdcEiXtDX9RQuR8zwNB845PScyvgjzuCvy5Dsu#algebra-pardon-othello +-- urn:ubideco:semid:6D7oEZGP4eHYSgfJeP1Dyc6Qgc4ph4budoDSuFmtJ5zj#jungle-quota-front data OutputAssignmentRevealedAttach :: opout Opout - , seal XSealExplicitSeal + , seal XChainExplicitSealTxid , state RevealedAttach - , witness WitnessId? --- urn:ubideco:semid:3PB4sX4TDJMyLTk5yzm7DQbWhpcMzoHDriV3dYZd9VcL#alibi-credit-spain + , witness XChainTxid? +-- urn:ubideco:semid:7eRgyX7MZuLw9bdqGZRMJezC36u53CQ8vUsrwpuU3ATz#shake-vega-snow data OutputAssignmentRevealedData :: opout Opout - , seal XSealExplicitSeal + , seal XChainExplicitSealTxid , state RevealedData - , witness WitnessId? --- urn:ubideco:semid:J1nZUtrWPX18FyXxDcRvyZK8tv2BKLJN6G8riUETUKNm#mars-scholar-potato + , witness XChainTxid? +-- urn:ubideco:semid:4SNmowDSb3dv7WASKKpiRRWsmr9t5VjULSmYRPV3CcyC#burma-plate-degree data OutputAssignmentRevealedValue :: opout Opout - , seal XSealExplicitSeal + , seal XChainExplicitSealTxid , state RevealedFungible - , witness WitnessId? --- urn:ubideco:semid:9tNXvuRwNLhtMduizsdKtiUhLSewRmwRL7knBegcQ7o4#sunday-random-vincent + , witness XChainTxid? +-- urn:ubideco:semid:3EAtrfNu9ptxdaoZkCzj2UvzUMSwDci52kkgJoYBWuz3#garden-eagle-bagel data OutputAssignmentVoidState :: opout Opout - , seal XSealExplicitSeal + , seal XChainExplicitSealTxid , state VoidState - , witness WitnessId? + , witness XChainTxid? -- urn:ubideco:semid:5twbh2U5hyaowidwum1iRNCqebBLxTuZTuNPt3SaRT13#nepal-delta-earth data PedersenCommitment :: [Byte ^ 33] -- urn:ubideco:semid:73TpZuiyRWAuV9GRSj9H7tDf3JXwVvmT4teTXhKxdue1#invent-stock-result @@ -324,26 +324,29 @@ data Valencies :: {ValencyType ^ ..0xff} data ValencyType :: U16 -- urn:ubideco:semid:49HkbZvGaJE3phHjLBMQCR3NK1sGA462HJr5BkqQ6YQr#nectar-ceramic-driver data VoidState :: () --- urn:ubideco:semid:6tBUyNPX3Y24WzHhEXjr4E3wASpLTWqoUhEWSuvsjWEq#carrot-iris-wheel -data WitnessAnchor :: witnessOrd WitnessOrd, witnessId WitnessId --- urn:ubideco:semid:EEYT7goTNgX2nNFoKosg6FKx1CDSyFWHKNK1TRySs6gr#axiom-gyro-album -data WitnessId :: bitcoin Bitcoin.Txid {- urn:ubideco:semid:C1GfCrG7AXu2sFhRBspd7KpJK2YgyTkVy6pty5rZynRs#cowboy-diego-betty -} - | liquid Bitcoin.Txid {- urn:ubideco:semid:C1GfCrG7AXu2sFhRBspd7KpJK2YgyTkVy6pty5rZynRs#cowboy-diego-betty -} +-- urn:ubideco:semid:HtyS3FW8duiA8o1TV13jiK5BUJtebY1JvghTkz9sVuNM#henry-prepare-company +data WitnessAnchor :: witnessOrd WitnessOrd, witnessId XChainTxid -- urn:ubideco:semid:JAhqqYxn4oDEDefAeAHFcAtNQJqQBHo6q5xqbviV3L3q#jacket-fiction-sound data WitnessOrd :: onChain WitnessPos | offChain () -- urn:ubideco:semid:zaGYZruf2pxiZqk1bjmoivxK8DkFycoyjQB52uHYhGL#robert-average-artist data WitnessPos :: height U32, timestamp I64 --- urn:ubideco:semid:6WtKj1L8L7zpomz5ExJnV6wBrsbtLnshuJLrkCids7ky#camera-kilo-morning -data XAnchor :: bitcoin AnchorSet +-- urn:ubideco:semid:7ypgzk2JFQpQQbtUFua1uyGu6N7aUxkiXokmjrJDNQnq#floor-emotion-riviera +data XChainAnchorSet :: bitcoin AnchorSet | liquid AnchorSet --- urn:ubideco:semid:4Nr4GNjqYM4KpeLkWEFjy8FxYs1jVV6CCum7CMftHCm9#studio-evening-camera -data XSealBlindSealTxPtr :: bitcoin BPCore.BlindSealTxPtr {- urn:ubideco:semid:5ircUcbD1UjEKsAKMcvdfjS9UcVCEcs47BWAZ5UDqYdr#austria-icon-waiter -} +-- urn:ubideco:semid:Ag5MktqPQsAWFoJEaUfNw6AvjYtLFXMwupw9chdEwEKE#prime-loyal-escort +data XChainBlindSealTxPtr :: bitcoin BPCore.BlindSealTxPtr {- urn:ubideco:semid:5ircUcbD1UjEKsAKMcvdfjS9UcVCEcs47BWAZ5UDqYdr#austria-icon-waiter -} | liquid BPCore.BlindSealTxPtr {- urn:ubideco:semid:5ircUcbD1UjEKsAKMcvdfjS9UcVCEcs47BWAZ5UDqYdr#austria-icon-waiter -} --- urn:ubideco:semid:Age9RPnuptagg4d8Q6wixptyT6y6yRFvTSWEkrT6H6Vc#ford-guitar-tonight -data XSealBlindSealTxid :: bitcoin BPCore.BlindSealTxid {- urn:ubideco:semid:Cu4G4njDjd2mgmz4FsvFAwRKwBpksmZ9CB9AJhSNx5dk#kimono-golf-price -} +-- urn:ubideco:semid:9kefL6S8Tr5a7hMTPg6M8Ruh9Da7s3LV6L4HJDQH69vJ#biology-domain-domain +data XChainBlindSealTxid :: bitcoin BPCore.BlindSealTxid {- urn:ubideco:semid:Cu4G4njDjd2mgmz4FsvFAwRKwBpksmZ9CB9AJhSNx5dk#kimono-golf-price -} | liquid BPCore.BlindSealTxid {- urn:ubideco:semid:Cu4G4njDjd2mgmz4FsvFAwRKwBpksmZ9CB9AJhSNx5dk#kimono-golf-price -} --- urn:ubideco:semid:5yxE75Qk7ScEuNdG4NXGASsGkpyUVQB3FVaxm3q8w1zK#carbon-network-hilton -data XSealExplicitSeal :: bitcoin BPCore.ExplicitSeal {- urn:ubideco:semid:4rfbAaHqg7T4ukXDQRtFZzXipcf9FJwr5eUUXsj6YWJo#table-liberal-hope -} - | liquid BPCore.ExplicitSeal {- urn:ubideco:semid:4rfbAaHqg7T4ukXDQRtFZzXipcf9FJwr5eUUXsj6YWJo#table-liberal-hope -} +-- urn:ubideco:semid:F6RPJgZMGop2K5QBdWK1bP57ifXo5g76Jet1fEtbi3Wh#ozone-quick-hippie +data XChainExplicitSealTxid :: bitcoin BPCore.ExplicitSealTxid {- urn:ubideco:semid:FxnQvXfa6koZmm6uyZCPwnGrMi3UzY588kKN9ipN72L#antenna-darwin-emotion -} + | liquid BPCore.ExplicitSealTxid {- urn:ubideco:semid:FxnQvXfa6koZmm6uyZCPwnGrMi3UzY588kKN9ipN72L#antenna-darwin-emotion -} +-- urn:ubideco:semid:FmzeQkRM31bz3UNNTZTV2DWDyCMVSabzQWcdb1PpL2Ao#palace-random-taxi +data XChainSecretSeal :: bitcoin BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -} + | liquid BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -} +-- urn:ubideco:semid:AFRnr7FmAC7DtKDQZj9eCzuf9Tp8E6UckpVouwDmwq5S#cubic-museum-lemon +data XChainTxid :: bitcoin Bitcoin.Txid {- urn:ubideco:semid:C1GfCrG7AXu2sFhRBspd7KpJK2YgyTkVy6pty5rZynRs#cowboy-diego-betty -} + | liquid Bitcoin.Txid {- urn:ubideco:semid:C1GfCrG7AXu2sFhRBspd7KpJK2YgyTkVy6pty5rZynRs#cowboy-diego-betty -} From fc582efb35f7077b43f119c0feec6c225732e347 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sat, 30 Dec 2023 08:55:52 +0100 Subject: [PATCH 11/11] release v0.11.0-beta.4 --- Cargo.lock | 2 +- Cargo.toml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1e13c7bd..274138f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -599,7 +599,7 @@ dependencies = [ [[package]] name = "rgb-core" -version = "0.11.0-beta.3" +version = "0.11.0-beta.4" dependencies = [ "aluvm", "amplify", diff --git a/Cargo.toml b/Cargo.toml index e2116a13..158a3d67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rgb-core" -version = "0.11.0-beta.3" +version = "0.11.0-beta.4" authors = ["Dr Maxim Orlovsky "] description = "RGB Core Library: confidential & scalable smart contracts on Bitcoin & Lightning (consensus layer)" repository = "https://github.com/RGB-WG/rgb-core" @@ -23,12 +23,12 @@ required-features = ["stl"] [dependencies] amplify = { version = "~4.5.0", features = ["rand"] } -strict_encoding = "~2.6.1" +strict_encoding = "~2.6.2" strict_types = "~1.6.3" aluvm = { version = "~0.11.0-beta.2", features = ["std"] } -commit_verify = { version = "~0.11.0-beta.2", features = ["rand", "derive"] } -single_use_seals = "~0.11.0-beta.2" -bp-core = { version = "~0.11.0-beta.2" } +commit_verify = { version = "~0.11.0-beta.3", features = ["rand", "derive"] } +single_use_seals = "~0.11.0-beta.3" +bp-core = { version = "~0.11.0-beta.3" } secp256k1-zkp = { version = "0.9.2", features = ["rand", "rand-std", "global-context"] } # TODO: Update version before the release baid58 = "~0.4.4" mime = "~0.3.17"