diff --git a/src/contract/anchor.rs b/src/contract/anchor.rs index 5058356c..70c89b56 100644 --- a/src/contract/anchor.rs +++ b/src/contract/anchor.rs @@ -21,14 +21,15 @@ // limitations under the License. use std::cmp::Ordering; -use std::ops::Deref; -use bp::dbc; -use bp::dbc::anchor::MergeError; +use bp::dbc::opret::OpretProof; +use bp::dbc::tapret::TapretProof; +use bp::dbc::Anchor; +use bp::Txid; use commit_verify::mpc; use strict_encoding::StrictDumb; -use crate::{TransitionBundle, WitnessId, WitnessOrd, LIB_NAME_RGB}; +use crate::{BundleId, ContractId, TransitionBundle, WitnessId, WitnessOrd, LIB_NAME_RGB}; #[derive(Clone, Eq, PartialEq, Debug)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] @@ -39,10 +40,15 @@ use crate::{TransitionBundle, WitnessId, WitnessOrd, LIB_NAME_RGB}; serde(crate = "serde_crate", rename_all = "camelCase") )] pub struct AnchoredBundle { - pub anchor: Anchor, + pub anchor: XAnchor, pub bundle: TransitionBundle, } +impl AnchoredBundle { + #[inline] + pub fn bundle_id(&self) -> BundleId { self.bundle.bundle_id() } +} + #[derive(Clone, Eq, PartialEq, Debug)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB, tags = custom, dumb = Self::Bitcoin(strict_dumb!()))] @@ -51,65 +57,212 @@ pub struct AnchoredBundle { derive(Serialize, Deserialize), serde(crate = "serde_crate", rename_all = "camelCase") )] -pub enum Anchor { +pub enum XAnchor { #[strict_type(tag = 0x00)] - Bitcoin(dbc::Anchor

), + Bitcoin(AnchorSet

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

), + Liquid(AnchorSet

), } -impl Deref for Anchor

{ - type Target = dbc::Anchor

; - +impl XAnchor

{ #[inline] - fn deref(&self) -> &Self::Target { + pub fn layer1(&self) -> Layer1 { match self { - Anchor::Bitcoin(anchor) | Anchor::Liquid(anchor) => anchor, + XAnchor::Bitcoin(_) => Layer1::Bitcoin, + XAnchor::Liquid(_) => Layer1::Liquid, } } -} -impl Anchor

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

) -> Result, E>, + ) -> Result, E> { match self { - Anchor::Bitcoin(anchor) => WitnessId::Bitcoin(anchor.txid), - Anchor::Liquid(anchor) => WitnessId::Liquid(anchor.txid), + XAnchor::Bitcoin(anchor) => f(anchor).map(XAnchor::Bitcoin), + XAnchor::Liquid(anchor) => f(anchor).map(XAnchor::Liquid), } } +} - pub fn map( - self, - f: impl FnOnce(dbc::Anchor

) -> Result, E>, - ) -> Result, E> { +impl XAnchor { + pub fn known_bundle_ids(&self) -> impl Iterator + '_ { match self { - Anchor::Bitcoin(anchor) => f(anchor).map(Anchor::Bitcoin), - Anchor::Liquid(anchor) => f(anchor).map(Anchor::Liquid), + XAnchor::Bitcoin(anchor) | XAnchor::Liquid(anchor) => anchor.known_bundle_ids(), } } + + pub fn to_merkle_proof( + &self, + contract_id: ContractId, + ) -> Result, mpc::LeafNotKnown> { + self.clone().into_merkle_proof(contract_id) + } + + pub fn into_merkle_proof( + self, + contract_id: ContractId, + ) -> Result, mpc::LeafNotKnown> { + self.map(|a| a.into_merkle_proof(contract_id)) + } +} + +impl XAnchor { + pub fn to_merkle_block( + &self, + contract_id: ContractId, + bundle_id: BundleId, + ) -> Result, mpc::InvalidProof> { + self.clone().into_merkle_block(contract_id, bundle_id) + } + + pub fn into_merkle_block( + self, + contract_id: ContractId, + bundle_id: BundleId, + ) -> Result, mpc::InvalidProof> { + self.map(|a| a.into_merkle_block(contract_id, bundle_id)) + } } -impl Anchor { - pub fn merge_reveal(self, other: Self) -> Result { - match (self, other) { - (Anchor::Bitcoin(anchor), Anchor::Bitcoin(other)) => { - anchor.merge_reveal(other).map(Anchor::Bitcoin) - } - (Anchor::Liquid(anchor), Anchor::Liquid(other)) => { - anchor.merge_reveal(other).map(Anchor::Liquid) - } - _ => Err(MergeError::ProofMismatch), +#[derive(Clone, Eq, PartialEq, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB, tags = custom, dumb = Self::Tapret(strict_dumb!()))] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", rename_all = "camelCase") +)] +pub enum AnchorSet { + #[strict_type(tag = 0x01)] + Tapret(Anchor), + #[strict_type(tag = 0x02)] + Opret(Anchor), + #[strict_type(tag = 0x03)] + Dual { + tapret: Anchor, + opret: Anchor, + }, +} + +impl AnchorSet

{ + pub fn txid(&self) -> Option { + match self { + AnchorSet::Tapret(a) => Some(a.txid), + AnchorSet::Opret(a) => Some(a.txid), + AnchorSet::Dual { tapret, opret } if tapret.txid == opret.txid => Some(tapret.txid), + _ => None, + } + } + + 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>, + ) -> Option { + Some(match (tapret, opret) { + (Some(tapret), Some(opret)) => Self::Dual { tapret, opret }, + (Some(tapret), None) => Self::Tapret(tapret), + (None, Some(opret)) => Self::Opret(opret), + (None, None) => return None, + }) + } + + #[allow(clippy::type_complexity)] + pub fn as_split(&self) -> (Option<&Anchor>, Option<&Anchor>) { + match self { + AnchorSet::Tapret(tapret) => (Some(tapret), None), + AnchorSet::Opret(opret) => (None, Some(opret)), + AnchorSet::Dual { tapret, opret } => (Some(tapret), Some(opret)), + } + } + + #[allow(clippy::type_complexity)] + pub fn into_split(self) -> (Option>, Option>) { + match self { + AnchorSet::Tapret(tapret) => (Some(tapret), None), + AnchorSet::Opret(opret) => (None, Some(opret)), + AnchorSet::Dual { tapret, opret } => (Some(tapret), Some(opret)), } } + + pub fn mpc_proofs(&self) -> impl Iterator { + let (t, o) = self.as_split(); + t.map(|a| &a.mpc_proof) + .into_iter() + .chain(o.map(|a| &a.mpc_proof)) + } +} + +impl AnchorSet { + pub fn to_merkle_block( + &self, + contract_id: ContractId, + bundle_id: BundleId, + ) -> Result, mpc::InvalidProof> { + self.clone().into_merkle_block(contract_id, bundle_id) + } + + pub fn into_merkle_block( + self, + contract_id: ContractId, + bundle_id: BundleId, + ) -> Result, mpc::InvalidProof> { + let (tapret, opret) = self.into_split(); + let tapret = tapret + .map(|t| t.into_merkle_block(contract_id, bundle_id)) + .transpose()?; + let opret = opret + .map(|o| o.into_merkle_block(contract_id, bundle_id)) + .transpose()?; + Ok(AnchorSet::from_split(tapret, opret).expect("one must be non-None")) + } +} + +impl AnchorSet { + pub fn known_bundle_ids(&self) -> impl Iterator + '_ { + self.mpc_proofs().flat_map(|p| { + p.to_known_message_map() + .into_iter() + .map(|(p, m)| (m.into(), p.into())) + }) + } + + pub fn to_merkle_proof( + &self, + contract_id: ContractId, + ) -> Result, mpc::LeafNotKnown> { + self.clone().into_merkle_proof(contract_id) + } + + pub fn into_merkle_proof( + self, + contract_id: ContractId, + ) -> Result, mpc::LeafNotKnown> { + let (tapret, opret) = self.into_split(); + let tapret = tapret + .map(|t| t.into_merkle_proof(contract_id)) + .transpose()?; + let opret = opret + .map(|o| o.into_merkle_proof(contract_id)) + .transpose()?; + Ok(AnchorSet::from_split(tapret, opret).expect("one must be non-None")) + } } /// Txid and height information ordered according to the RGB consensus rules. diff --git a/src/contract/assignments.rs b/src/contract/assignments.rs index 7baba590..ea05842a 100644 --- a/src/contract/assignments.rs +++ b/src/contract/assignments.rs @@ -35,7 +35,7 @@ use super::ExposedState; use crate::contract::seal::GenesisSeal; use crate::{ AssignmentType, ExposedSeal, GraphSeal, RevealedAttach, RevealedData, RevealedValue, - SecretSeal, StateType, VoidState, Xchain, LIB_NAME_RGB, + SecretSeal, StateType, VoidState, XSeal, LIB_NAME_RGB, }; #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Display, Error)] @@ -76,12 +76,12 @@ pub enum Assign { state: State::Confidential, }, #[strict_type(tag = 0x03)] - Revealed { seal: Xchain, state: State }, + Revealed { seal: XSeal, state: State }, #[strict_type(tag = 0x02)] ConfidentialSeal { seal: SecretSeal, state: State }, #[strict_type(tag = 0x01)] ConfidentialState { - seal: Xchain, + seal: XSeal, state: State::Confidential, }, } @@ -118,9 +118,9 @@ impl Hash for Assign { } impl Assign { - pub fn revealed(seal: Xchain, state: State) -> Self { Assign::Revealed { seal, state } } + pub fn revealed(seal: XSeal, state: State) -> Self { Assign::Revealed { seal, state } } - pub fn with_seal_replaced(assignment: &Self, seal: Xchain) -> Self { + pub fn with_seal_replaced(assignment: &Self, seal: XSeal) -> Self { match assignment { Assign::Confidential { seal: _, state } | Assign::ConfidentialState { seal: _, state } => Assign::ConfidentialState { @@ -145,7 +145,7 @@ impl Assign { } } - pub fn revealed_seal(&self) -> Option> { + pub fn revealed_seal(&self) -> Option> { match self { Assign::Revealed { seal, .. } | Assign::ConfidentialState { seal, .. } => Some(*seal), Assign::Confidential { .. } | Assign::ConfidentialSeal { .. } => None, @@ -182,21 +182,21 @@ impl Assign { } } - pub fn as_revealed(&self) -> Option<(&Xchain, &State)> { + pub fn as_revealed(&self) -> Option<(&XSeal, &State)> { match self { Assign::Revealed { seal, state } => Some((seal, state)), _ => None, } } - pub fn to_revealed(&self) -> Option<(Xchain, State)> { + pub fn to_revealed(&self) -> Option<(XSeal, State)> { match self { Assign::Revealed { seal, state } => Some((*seal, state.clone())), _ => None, } } - pub fn into_revealed(self) -> Option<(Xchain, State)> { + pub fn into_revealed(self) -> Option<(XSeal, State)> { match self { Assign::Revealed { seal, state } => Some((seal, state)), _ => None, @@ -445,7 +445,7 @@ impl TypedAssigns { /// If seal definition does not exist, returns [`UnknownDataError`]. If the /// seal is confidential, returns `Ok(None)`; otherwise returns revealed /// seal data packed as `Ok(Some(`[`Seal`]`))` - pub fn revealed_seal_at(&self, index: u16) -> Result>, UnknownDataError> { + pub fn revealed_seal_at(&self, index: u16) -> Result>, UnknownDataError> { Ok(match self { TypedAssigns::Declarative(vec) => vec .get(index as usize) diff --git a/src/contract/bundle.rs b/src/contract/bundle.rs index c330ce2c..0da66199 100644 --- a/src/contract/bundle.rs +++ b/src/contract/bundle.rs @@ -20,28 +20,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -use amplify::confinement::{TinyOrdMap, TinyOrdSet}; +use std::collections::BTreeMap; +use std::io::Write; + +use amplify::confinement::{Confined, U16}; use amplify::{Bytes32, Wrapper}; -use commit_verify::{mpc, CommitStrategy, CommitmentId, Conceal}; +use bp::Vout; +use commit_verify::{mpc, CommitEncode, CommitmentId}; +use strict_encoding::{StrictDumb, StrictEncode, StrictWriter}; -use super::{OpId, Transition}; -use crate::{ContractId, LIB_NAME_RGB}; +use super::OpId; +use crate::{Transition, LIB_NAME_RGB}; -#[derive(Copy, Clone, Eq, PartialEq, Debug, Display, Error)] -#[display(doc_comments)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub enum BundleError { - /// state transitions created under different contracts. - DivergentContracts, - /// no state transitions. - EmptyBundle, - /// state transitions reference to the same input multiple times ({0}). - RepeatedInputs(usize), -} +pub type Vin = Vout; /// Unique state transition bundle identifier equivalent to the bundle /// commitment hash @@ -68,80 +59,40 @@ impl From for BundleId { fn from(id: mpc::Message) -> Self { BundleId(id.into_inner()) } } -#[derive(Clone, PartialEq, Eq, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[derive(Clone, PartialEq, Eq, Debug, From)] +#[derive(StrictType, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] -pub struct BundleItem { - pub inputs: TinyOrdSet, - pub transition: Option, -} - -impl Conceal for BundleItem { - type Concealed = Self; - - fn conceal(&self) -> Self::Concealed { - BundleItem { - inputs: self.inputs.clone(), - transition: None, - } - } +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", rename_all = "camelCase") +)] +pub struct TransitionBundle { + pub input_map: Confined, 1, U16>, + pub known_transitions: Confined, 1, U16>, } -#[derive(Wrapper, WrapperMut, Clone, PartialEq, Eq, Debug, From)] -#[wrapper(Deref)] -#[wrapper_mut(DerefMut)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] -pub struct TransitionBundle(TinyOrdMap); - -impl Conceal for TransitionBundle { - type Concealed = Self; - - fn conceal(&self) -> Self::Concealed { - let concealed = self.iter().map(|(id, item)| (*id, item.conceal())); - TransitionBundle(TinyOrdMap::try_from_iter(concealed).expect("same size")) +impl CommitEncode for TransitionBundle { + fn commit_encode(&self, e: &mut impl Write) { + let w = StrictWriter::with(u32::MAX as usize, e); + self.input_map.strict_encode(w).ok(); } } -impl CommitStrategy for TransitionBundle { - // TODO: Use merklization strategy - type Strategy = commit_verify::strategies::ConcealStrict; -} - impl CommitmentId for TransitionBundle { const TAG: [u8; 32] = *b"urn:lnpbp:rgb:bundle:v1#20230306"; type Id = BundleId; } -impl TransitionBundle { - pub fn bundle_id(&self) -> BundleId { self.commitment_id() } +impl StrictDumb for TransitionBundle { + fn strict_dumb() -> Self { + Self { + input_map: confined_bmap! { strict_dumb!() => strict_dumb!() }, + known_transitions: confined_bmap! { strict_dumb!() => strict_dumb!() }, + } + } } impl TransitionBundle { - pub fn validate(&self) -> Result { - let mut contract_id = None; - let mut used_inputs = bset! {}; - for item in self.values() { - if !contract_id - .and_then(|id| { - item.transition - .as_ref() - .map(|t| (id, t)) - .map(|(id, t)| id == t.contract_id) - }) - .unwrap_or_default() - { - return Err(BundleError::DivergentContracts); - } - contract_id = item.transition.as_ref().map(|t| t.contract_id); - let repeated_inputs = used_inputs.intersection(&item.inputs).count(); - if repeated_inputs > 0 { - return Err(BundleError::RepeatedInputs(repeated_inputs)); - } - used_inputs.extend(&item.inputs); - } - contract_id.ok_or(BundleError::EmptyBundle) - } + pub fn bundle_id(&self) -> BundleId { self.commitment_id() } } diff --git a/src/contract/contract.rs b/src/contract/contract.rs index 854279ba..9cd9a1e2 100644 --- a/src/contract/contract.rs +++ b/src/contract/contract.rs @@ -37,7 +37,7 @@ use crate::{ Assign, AssignmentType, Assignments, AssignmentsRef, ContractId, ExposedSeal, ExposedState, Extension, Genesis, GlobalStateType, OpId, Operation, OutputSeal, RevealedAttach, RevealedData, RevealedValue, SchemaId, SubSchema, Transition, TypedAssigns, VoidState, WitnessAnchor, - WitnessId, Xchain, LIB_NAME_RGB, + WitnessId, XSeal, LIB_NAME_RGB, }; #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] @@ -146,7 +146,7 @@ impl OutputAssignment { /// If the processing is done on invalid stash data, the seal is /// witness-based and the anchor chain doesn't match the seal chain. pub fn with_witness( - seal: Xchain, + seal: XSeal, witness_id: WitnessId, state: State, opid: OpId, @@ -169,7 +169,7 @@ impl OutputAssignment { /// If the processing is done on invalid stash data, the seal is /// witness-based and the anchor chain doesn't match the seal chain. pub fn with_no_witness( - seal: Xchain, + seal: XSeal, state: State, opid: OpId, ty: AssignmentType, diff --git a/src/contract/mod.rs b/src/contract/mod.rs index 0b7e6d9f..1c0e0f33 100644 --- a/src/contract/mod.rs +++ b/src/contract/mod.rs @@ -36,13 +36,13 @@ mod contract; use std::io::Write; use amplify::confinement::TinyOrdSet; -pub use anchor::{Anchor, AnchoredBundle, Layer1, WitnessAnchor}; +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}; -pub use bundle::{BundleError, BundleId, BundleItem, TransitionBundle}; +pub use bundle::{BundleId, TransitionBundle, Vin}; use commit_verify::CommitEncode; pub use contract::{ AttachOutput, ContractHistory, ContractState, DataOutput, FungibleOutput, GlobalOrd, Opout, @@ -60,7 +60,7 @@ pub use operations::{ }; pub use seal::{ ExposedSeal, GenesisSeal, GraphSeal, OutputSeal, SecretSeal, TxoSeal, WitnessId, WitnessOrd, - WitnessPos, Xchain, XchainParseError, + WitnessPos, XSeal, XchainParseError, }; pub use state::{ConfidentialState, ExposedState, StateCommitment, StateData, StateType}; diff --git a/src/contract/seal.rs b/src/contract/seal.rs index e761d4d3..77c93bde 100644 --- a/src/contract/seal.rs +++ b/src/contract/seal.rs @@ -49,7 +49,7 @@ impl ExposedSeal for GenesisSeal {} impl ExposedSeal for ExplicitSeal {} -pub type OutputSeal = Xchain>; +pub type OutputSeal = XSeal>; /* #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] @@ -71,7 +71,7 @@ pub struct SealPreimage(Bytes32); derive(Serialize, Deserialize), serde(crate = "serde_crate", rename_all = "camelCase") )] -pub enum Xchain { +pub enum XSeal { #[strict_type(tag = 0x00)] Bitcoin(U), #[strict_type(tag = 0x01)] @@ -84,15 +84,15 @@ pub enum Xchain { */ } -impl Conceal for Xchain { +impl Conceal for XSeal { type Concealed = SecretSeal; #[inline] fn conceal(&self) -> Self::Concealed { SecretSeal::commit(self) } } -impl CommitVerify, UntaggedProtocol> for SecretSeal { - fn commit(reveal: &Xchain) -> Self { +impl CommitVerify, UntaggedProtocol> for SecretSeal { + fn commit(reveal: &XSeal) -> Self { let mut engine = Sha256::default(); let w = StrictWriter::with(u32::MAX as usize, &mut engine); reveal.strict_encode(w).ok(); @@ -100,15 +100,15 @@ impl CommitVerify, UntaggedProtocol> for SecretSeal { } } -impl commit_verify::CommitStrategy for Xchain { +impl commit_verify::CommitStrategy for XSeal { type Strategy = strategies::ConcealStrict; } -impl Xchain { - pub fn transmutate(self) -> Xchain { +impl XSeal { + pub fn transmutate(self) -> XSeal { match self { - Xchain::Bitcoin(seal) => Xchain::Bitcoin(seal.transmutate()), - Xchain::Liquid(seal) => Xchain::Liquid(seal.transmutate()), + XSeal::Bitcoin(seal) => XSeal::Bitcoin(seal.transmutate()), + XSeal::Liquid(seal) => XSeal::Liquid(seal.transmutate()), /* SealDefinition::Abraxas(seal) => SealDefinition::Abraxas(seal), SealDefinition::Prime(seal) => SealDefinition::Prime(seal), @@ -117,60 +117,67 @@ impl Xchain { } } -impl Xchain { +impl XSeal { pub fn with(layer1: Layer1, seal: impl Into) -> Self { match layer1 { - Layer1::Bitcoin => Xchain::Bitcoin(seal.into()), - Layer1::Liquid => Xchain::Liquid(seal.into()), + Layer1::Bitcoin => XSeal::Bitcoin(seal.into()), + Layer1::Liquid => XSeal::Liquid(seal.into()), } } pub fn layer1(self) -> Layer1 { match self { - Xchain::Bitcoin(_) => Layer1::Bitcoin, - Xchain::Liquid(_) => Layer1::Liquid, + XSeal::Bitcoin(_) => Layer1::Bitcoin, + XSeal::Liquid(_) => Layer1::Liquid, } } + pub fn reduce_to_bp(self) -> Option { + Some(match self { + XSeal::Bitcoin(seal) => seal, + XSeal::Liquid(seal) => seal, + }) + } + pub fn method(self) -> CloseMethod { match self { - Xchain::Bitcoin(seal) => seal.method(), - Xchain::Liquid(seal) => seal.method(), + XSeal::Bitcoin(seal) => seal.method(), + XSeal::Liquid(seal) => seal.method(), } } #[inline] pub fn to_output_seal(self) -> Option { Some(match self { - Xchain::Bitcoin(seal) => { + XSeal::Bitcoin(seal) => { let outpoint = seal.outpoint()?; - Xchain::Bitcoin(ExplicitSeal::new(seal.method(), outpoint)) + XSeal::Bitcoin(ExplicitSeal::new(seal.method(), outpoint)) } - Xchain::Liquid(seal) => { + XSeal::Liquid(seal) => { let outpoint = seal.outpoint()?; - Xchain::Liquid(ExplicitSeal::new(seal.method(), outpoint)) + XSeal::Liquid(ExplicitSeal::new(seal.method(), outpoint)) } }) } pub fn try_to_output_seal(self, witness_id: WitnessId) -> Result { match (self, witness_id) { - (Xchain::Bitcoin(seal), WitnessId::Bitcoin(txid)) => { - Ok(Xchain::Bitcoin(ExplicitSeal::new(seal.method(), seal.outpoint_or(txid)))) + (XSeal::Bitcoin(seal), WitnessId::Bitcoin(txid)) => { + Ok(XSeal::Bitcoin(ExplicitSeal::new(seal.method(), seal.outpoint_or(txid)))) } - (Xchain::Liquid(seal), WitnessId::Liquid(txid)) => { - Ok(Xchain::Liquid(ExplicitSeal::new(seal.method(), seal.outpoint_or(txid)))) + (XSeal::Liquid(seal), WitnessId::Liquid(txid)) => { + Ok(XSeal::Liquid(ExplicitSeal::new(seal.method(), seal.outpoint_or(txid)))) } (me, _) => Err(me), } } } -impl Display for Xchain { +impl Display for XSeal { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - Xchain::Bitcoin(seal) => write!(f, "bitcoin:{seal}"), - Xchain::Liquid(seal) => write!(f, "liquid:{seal}"), + XSeal::Bitcoin(seal) => write!(f, "bitcoin:{seal}"), + XSeal::Liquid(seal) => write!(f, "liquid:{seal}"), } } } @@ -185,7 +192,7 @@ pub enum XchainParseError { Seal(E), } -impl FromStr for Xchain +impl FromStr for XSeal where U::Err: Debug + Display { type Err = XchainParseError; @@ -195,17 +202,14 @@ where U::Err: Debug + Display match prefix { "bitcoin" => s .parse() - .map(Xchain::Bitcoin) - .map_err(XchainParseError::from), - "liquid" => s - .parse() - .map(Xchain::Liquid) + .map(XSeal::Bitcoin) .map_err(XchainParseError::from), + "liquid" => s.parse().map(XSeal::Liquid).map_err(XchainParseError::from), unknown => Err(XchainParseError::UnknownPrefix(unknown.to_owned())), } } else { s.parse() - .map(Xchain::Bitcoin) + .map(XSeal::Bitcoin) .map_err(XchainParseError::from) } } diff --git a/src/lib.rs b/src/lib.rs index 75de8a5a..8062fdca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,7 +52,6 @@ pub mod vm; pub mod stl; pub mod prelude { - pub use bp::dbc::AnchorId; pub use contract::*; pub use schema::*; diff --git a/src/stl.rs b/src/stl.rs index a560029e..46bf1e79 100644 --- a/src/stl.rs +++ b/src/stl.rs @@ -32,7 +32,7 @@ use crate::{AnchoredBundle, ContractState, Extension, Genesis, SubSchema, LIB_NA /// Strict types id for the library providing data types for RGB consensus. pub const LIB_ID_RGB: &str = - "urn:ubideco:stl:9gBiimtSnE1Qxwa49rbdPpv2s5ggXfitb6aktnunpDjX#cosmos-gentle-goblin"; + "urn:ubideco:stl:68HqdPT8Bwr2Y9im6kY28Z2JNABrRnxKeGuhrkYCAYqE#laptop-brush-baby"; fn _rgb_core_stl() -> Result { LibBuilder::new(libname!(LIB_NAME_RGB), tiny_bset! { diff --git a/src/validation/consignment.rs b/src/validation/consignment.rs index c417c498..2e142bce 100644 --- a/src/validation/consignment.rs +++ b/src/validation/consignment.rs @@ -25,25 +25,52 @@ //! single-use-seal data. use std::collections::{BTreeMap, BTreeSet}; +use std::rc::Rc; use crate::{ - AnchoredBundle, AssetTag, AssignmentType, BundleId, Extension, Genesis, OpId, OpRef, - SecretSeal, SubSchema, Transition, TransitionBundle, + AnchoredBundle, AssetTag, AssignmentType, BundleId, Genesis, OpId, OpRef, Operation, + SecretSeal, SubSchema, }; +pub struct CheckedConsignment<'consignment, C: ConsignmentApi>(&'consignment C); + +impl<'consignment, C: ConsignmentApi> CheckedConsignment<'consignment, C> { + pub fn new(consignment: &'consignment C) -> Self { Self(consignment) } +} + +impl<'consignment, C: ConsignmentApi> ConsignmentApi for CheckedConsignment<'consignment, C> { + type Iter<'placeholder> = C::Iter<'placeholder>; + + fn schema(&self) -> &SubSchema { self.0.schema() } + + fn asset_tags(&self) -> &BTreeMap { self.0.asset_tags() } + + fn operation(&self, opid: OpId) -> Option { + self.0.operation(opid).filter(|op| op.id() == opid) + } + + fn genesis(&self) -> &Genesis { self.0.genesis() } + + fn terminals(&self) -> BTreeSet<(BundleId, SecretSeal)> { self.0.terminals() } + + fn bundle_ids<'a>(&self) -> Self::Iter<'a> { self.0.bundle_ids() } + + fn anchored_bundle(&self, bundle_id: BundleId) -> Option> { + self.0 + .anchored_bundle(bundle_id) + .filter(|ab| ab.bundle.bundle_id() == bundle_id) + } +} + /// Trait defining common data access API for all storage-related RGB structures /// -/// # Verification -/// -/// The function does not verify the internal consistency, schema conformance or -/// validation status of the RGB contract data within the storage or container; -/// these checks must be performed as a separate step before calling any of the -/// [`ContainerApi`] methods. If the methods are called on -/// non-validated/unchecked data this may result in returned [`Error`] or -/// [`None`] values from the API methods. +/// The API provided for the consignment should not verify the internal +/// consistency, schema conformance or validation status of the RGB contract +/// data within the storage or container. If the methods are called on an +/// invalid or absent data, the API must always return [`None`] or empty +/// collections/iterators. pub trait ConsignmentApi { - type BundleIter<'container>: Iterator - where Self: 'container; + type Iter<'a>: Iterator; fn schema(&self) -> &SubSchema; @@ -57,30 +84,6 @@ pub trait ConsignmentApi { /// Contract genesis. fn genesis(&self) -> &Genesis; - /// Returns reference to a state transition, if known, matching the provided - /// id. If id is unknown, or corresponds to other type of the operation - /// (genesis or state extensions) a error is returned. - /// - /// # Errors - /// - /// - [`Error::WrongNodeType`] when operation is present, but has some other - /// operation type - /// - [`Error::TransitionAbsent`] when operation with the given id is absent - /// from the storage/container - fn transition(&self, opid: OpId) -> Option<&Transition>; - - /// Returns reference to a state extension, if known, matching the provided - /// id. If id is unknown, or corresponds to other type of the operation - /// (genesis or state transition) a error is returned. - /// - /// # Errors - /// - /// - [`Error::WrongNodeType`] when operation is present, but has some other - /// operation type - /// - [`Error::ExtensionAbsent`] when operation with the given id is absent - /// from the storage/container - fn extension(&self, opid: OpId) -> Option<&Extension>; - /// The final state ("endpoints") provided by this consignment. /// /// There are two reasons for having endpoints: @@ -92,14 +95,7 @@ pub trait ConsignmentApi { /// state transitions represent the final state fn terminals(&self) -> BTreeSet<(BundleId, SecretSeal)>; - /// Data on all anchored state transitions contained in the consignment - fn anchored_bundles(&self) -> Self::BundleIter<'_>; - - fn bundle_by_id(&self, bundle_id: BundleId) -> Option<&TransitionBundle>; - - fn op_ids_except(&self, ids: &BTreeSet) -> BTreeSet; - - fn has_operation(&self, opid: OpId) -> bool; + fn bundle_ids<'a>(&self) -> Self::Iter<'a>; - fn known_transitions_by_bundle_id(&self, bundle_id: BundleId) -> Option>; + fn anchored_bundle(&self, bundle_id: BundleId) -> Option>; } diff --git a/src/validation/model.rs b/src/validation/logic.rs similarity index 95% rename from src/validation/model.rs rename to src/validation/logic.rs index c8f76375..b0e1007c 100644 --- a/src/validation/model.rs +++ b/src/validation/logic.rs @@ -27,19 +27,19 @@ use amplify::Wrapper; use strict_types::SemId; use crate::schema::{AssignmentsSchema, GlobalSchema, ValencySchema}; -use crate::validation::{ConsignmentApi, VirtualMachine}; +use crate::validation::{CheckedConsignment, ConsignmentApi, VirtualMachine}; use crate::{ validation, AssetTag, AssignmentType, Assignments, AssignmentsRef, ContractId, ExposedSeal, GlobalState, GlobalStateSchema, GlobalValues, GraphSeal, Inputs, OpFullType, OpId, OpRef, - Operation, Opout, Redeemed, Schema, SchemaRoot, TransitionType, TypedAssigns, Valencies, + Operation, Opout, Schema, SchemaRoot, TransitionType, TypedAssigns, Valencies, }; impl Schema { - pub fn validate( - &self, - consignment: &C, + pub fn validate_state<'validator, 'consignment, C: ConsignmentApi>( + &'validator self, + consignment: &'validator CheckedConsignment<'consignment, C>, op: OpRef, - vm: &dyn VirtualMachine, + vm: &'consignment dyn VirtualMachine, ) -> validation::Status { let id = op.id(); @@ -146,14 +146,13 @@ impl Schema { } else { Assignments::default() }; - let redeemed = if let OpRef::Extension(extension) = op { - let redeemed = - extract_redeemed_valencies(consignment, &extension.redeemed, &mut status); + let mut redeemed = Valencies::default(); + if let OpRef::Extension(extension) = op { + for valency in extension.redeemed.keys() { + redeemed.push(*valency).expect("same size"); + } status += self.validate_redeemed(id, &redeemed, redeem_schema); - redeemed - } else { - Valencies::default() - }; + } status += match op.assignments() { AssignmentsRef::Genesis(assignments) => { self.validate_owned_state(id, assignments, assign_schema) @@ -550,19 +549,3 @@ fn extract_prev_state( .expect("collections is assembled from another collection with the same size requirements") .into() } - -fn extract_redeemed_valencies( - consignment: &C, - redeemed: &Redeemed, - status: &mut validation::Status, -) -> Valencies { - let mut public_rights = Valencies::default(); - for (valency, id) in redeemed.iter() { - if consignment.has_operation(*id) { - status.add_failure(validation::Failure::OperationAbsent(*id)); - } else { - public_rights.push(*valency).expect("same size"); - } - } - public_rights -} diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 72f1b0f0..b1f83600 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -22,14 +22,14 @@ mod script; mod schema; -mod model; +mod logic; mod state; mod validator; mod consignment; mod status; -pub use consignment::ConsignmentApi; -pub(crate) use model::OpInfo; +pub use consignment::{CheckedConsignment, ConsignmentApi}; +pub(crate) use logic::OpInfo; pub use script::VirtualMachine; pub use status::{Failure, Info, Status, Validity, Warning}; pub use validator::{ResolveTx, TxResolverError, Validator}; diff --git a/src/validation/status.rs b/src/validation/status.rs index 8011423c..9978a1c9 100644 --- a/src/validation/status.rs +++ b/src/validation/status.rs @@ -24,16 +24,16 @@ use core::iter::FromIterator; use core::ops::AddAssign; use std::fmt::{self, Display, Formatter}; -use bp::dbc::anchor; use bp::seals::txout::blind::ChainBlindSeal; -use bp::{seals, Txid}; +use bp::Txid; +use commit_verify::mpc::InvalidProof; use strict_types::SemId; use crate::contract::Opout; use crate::schema::{self, SchemaId}; use crate::{ - BundleError, BundleId, Layer1, OccurrencesMismatch, OpFullType, OpId, SecretSeal, StateType, - Xchain, + BundleId, ContractId, Layer1, OccurrencesMismatch, OpFullType, OpId, SecretSeal, StateType, + Vin, XSeal, }; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Display)] @@ -295,21 +295,31 @@ pub enum Failure { SchemaAssignmentOccurrences(OpId, schema::AssignmentType, OccurrencesMismatch), // Consignment consistency errors + /// operation {0} is referenced within the history multiple times. RGB + /// contracts allow only direct acyclic graphs. + CyclicGraph(OpId), /// operation {0} is absent from the consignment. OperationAbsent(OpId), - /// state transition {0} is absent from the consignment. - TransitionAbsent(OpId), - /// bundle with id {0} {1} - BundleInvalid(BundleId, BundleError), - /// consignment misses bundle with id {0} which must contain one of - /// terminals. - BundleMissed(BundleId), + /// transition bundle {0} referenced in consignment terminals is absent from + /// the consignment. + TerminalBundleAbsent(BundleId), + /// transition bundle {0} is absent from the consignment. + BundleAbsent(BundleId), + /// operation {0} is under a different contract {1}. + ContractMismatch(OpId, ContractId), + + // Errors checking bundle commitments + /// transition bundle {0} references state transition {1} which is not + /// included into the bundle input map. + BundleExtraTransition(BundleId, OpId), + /// transition bundle {0} references non-existing input in witness + /// transaction {2} for the state transition {1}. + BundleInvalidInput(BundleId, OpId, Txid), + /// transition bundle {0} doesn't commit to the input {1} in the witness + /// transaction {2} which is an input of the state transition {3}. + BundleInvalidCommitment(BundleId, Vin, Txid, OpId), // Errors checking seal closing - /// transition {0} is not anchored. - NotAnchored(OpId), - /// anchor for transition {0} doesn't commit to the actual transition data. - NotInAnchor(OpId), /// transition {opid} references state type {state_type} absent in the /// outputs of previous state transition {prev_id}. NoPrevState { @@ -319,25 +329,27 @@ pub enum Failure { }, /// transition {0} references non-existing previous output {1}. NoPrevOut(OpId, Opout), + /// anchors used inside bundle {0} reference different transaction ids. + AnchorSetInvalid(BundleId), /// seal defined in the history as a part of operation output {0} is /// confidential and can't be validated. ConfidentialSeal(Opout), - /// transition {0} is not a part of multi-protocol commitment for witness - /// {1}; anchor is invalid. - MpcInvalid(OpId, Txid), /// witness transaction {0} is not known to the transaction resolver. SealNoWitnessTx(Txid), /// witness layer 1 {anchor} doesn't match seal definition {seal}. SealWitnessLayer1Mismatch { seal: Layer1, anchor: Layer1 }, /// seal {1:?} is defined on {0} which is not in the set of layers allowed /// by the contract genesis. - SealInvalidLayer1(Layer1, Xchain), - /// transition {0} doesn't close seal with the witness transaction {1}. - /// Details: {2} - SealInvalid(OpId, Txid, seals::txout::VerifyError), - /// transition {0} is not properly anchored to the witness transaction {1}. - /// Details: {2} - AnchorInvalid(OpId, Txid, anchor::VerifyError), + SealLayerMismatch(Layer1, XSeal), + /// transition bundle {0} doesn't close seal with the witness transaction + /// {1}. Details: {2} + SealsInvalid(BundleId, Txid, String), + /// single-use seals for the operation {0} were not validated, which + /// probably indicates unanchored state transition. + SealsUnvalidated(OpId), + /// transition bundle {0} is not properly anchored to the witness + /// transaction {1}. Details: {2} + MpcInvalid(BundleId, Txid, InvalidProof), // State extensions errors /// valency {valency} redeemed by state extension {opid} references @@ -355,7 +367,7 @@ pub enum Failure { valency: schema::ValencyType, }, - // Data check errors + // State check errors /// state in {opid}/{state_type} is of {found} type, while schema requires /// it to be {expected}. StateTypeMismatch { @@ -401,11 +413,11 @@ pub enum Warning { /// terminal seal {1} referencing operation {0} is not present in operation /// assignments. TerminalSealAbsent(OpId, SecretSeal), - /// operation {0} present in the consignment is excessive and not a part of - /// the validated contract history. - ExcessiveOperation(OpId), /// terminal witness transaction {0} is not yet mined. TerminalWitnessNotMined(Txid), + /// transition bundle {0} doesn't close all the seals defined for its + /// inputs. + UnclosedSeals(BundleId), /// Custom warning by external services on top of RGB Core. #[display(inner)] diff --git a/src/validation/validator.rs b/src/validation/validator.rs index 4b653786..a228352e 100644 --- a/src/validation/validator.rs +++ b/src/validation/validator.rs @@ -22,18 +22,19 @@ use std::collections::{BTreeMap, BTreeSet, VecDeque}; -use bp::seals::txout::{TxPtr, Witness}; -use bp::{dbc, Tx, Txid}; +use bp::dbc::Anchor; +use bp::seals::txout::{CloseMethod, ExplicitSeal, TxoSeal, Witness}; +use bp::{dbc, Outpoint, Tx, Txid}; use commit_verify::mpc; use single_use_seals::SealWitness; use super::status::{Failure, Warning}; -use super::{ConsignmentApi, Status, Validity, VirtualMachine}; +use super::{CheckedConsignment, ConsignmentApi, Status, Validity, VirtualMachine}; use crate::vm::AluRuntime; use crate::{ - AltLayer1, Anchor, AnchoredBundle, BundleId, ContractId, GraphSeal, Layer1, OpId, OpRef, - Operation, Opout, Schema, SchemaId, SchemaRoot, Script, SubSchema, Transition, - TransitionBundle, TypedAssigns, Xchain, + AltLayer1, AnchorSet, BundleId, ContractId, Layer1, OpId, OpRef, OpType, Operation, Opout, + Schema, SchemaId, SchemaRoot, Script, SubSchema, Transition, TransitionBundle, TypedAssigns, + WitnessId, XAnchor, }; #[derive(Clone, Debug, Display, Error, From)] @@ -46,11 +47,11 @@ pub enum TxResolverError { } pub trait ResolveTx { - fn resolve_tx(&self, layer1: Layer1, txid: Txid) -> Result; + fn resolve_bp_tx(&self, layer1: Layer1, txid: Txid) -> Result; } pub struct Validator<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> { - consignment: &'consignment C, + consignment: CheckedConsignment<'consignment, C>, status: Status, @@ -58,10 +59,9 @@ pub struct Validator<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> { genesis_id: OpId, contract_id: ContractId, layers1: BTreeSet, - anchor_index: BTreeMap, - end_transitions: Vec<(&'consignment Transition, BundleId)>, - validation_index: BTreeSet, - anchor_validation_index: BTreeSet, + + validated_op_seals: BTreeSet, + validated_op_state: BTreeSet, vm: Box, resolver: &'resolver R, @@ -74,6 +74,12 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> // We use validation status object to store all detected failures and // warnings let mut status = Status::default(); + let vm = match consignment.schema().script { + Script::AluVM(ref lib) => { + Box::new(AluRuntime::new(lib)) as Box + } + }; + let consignment = CheckedConsignment::new(consignment); // Frequently used computation-heavy data let genesis = consignment.genesis(); @@ -81,34 +87,16 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> let contract_id = genesis.contract_id(); let schema_id = genesis.schema_id; - // Create indexes - let mut anchor_index = BTreeMap::::new(); - for AnchoredBundle { - ref anchor, - ref bundle, - } in consignment.anchored_bundles() - { - if let Err(err) = TransitionBundle::validate(bundle) { - status.add_failure(Failure::BundleInvalid(bundle.bundle_id(), err)); - } - for transition in bundle.values().filter_map(|item| item.transition.as_ref()) { - let opid = transition.id(); - anchor_index.insert(opid, anchor); - } - } - // Collect all endpoint transitions. // This is pretty simple operation; it takes a lot of code because we would like // to detect any potential issues with the consignment structure and notify user // about them (in form of generated warnings) - let mut end_transitions = Vec::<(&Transition, BundleId)>::new(); for (bundle_id, seal_endpoint) in consignment.terminals() { - let Some(transitions) = consignment.known_transitions_by_bundle_id(bundle_id) else { - status.add_failure(Failure::BundleMissed(bundle_id)); + let Some(anchored_bundle) = consignment.anchored_bundle(bundle_id) else { + status.add_failure(Failure::TerminalBundleAbsent(bundle_id)); continue; }; - for transition in transitions { - let opid = transition.id(); + for (opid, transition) in &anchored_bundle.bundle.known_transitions { // Checking for endpoint definition duplicates if !transition .assignments @@ -118,10 +106,7 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> { // We generate just a warning here because it's up to a user to decide whether // to accept consignment with wrong endpoint list - status.add_warning(Warning::TerminalSealAbsent(opid, seal_endpoint)); - } - if end_transitions.iter().all(|(n, _)| n.id() != opid) { - end_transitions.push((transition, bundle_id)); + status.add_warning(Warning::TerminalSealAbsent(*opid, seal_endpoint)); } } } @@ -129,20 +114,12 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> // Validation index is used to check that all transitions presented in the // consignment were validated. Also, we use it to avoid double schema // validations for transitions. - let validation_index = BTreeSet::::new(); - - // Index used to avoid repeated validations of the same anchor+transition pairs - let anchor_validation_index = BTreeSet::::new(); + let validated_op_state = BTreeSet::::new(); + let validated_op_seals = BTreeSet::::new(); let mut layers1 = bset! { Layer1::Bitcoin }; layers1.extend(genesis.alt_layers1.iter().map(AltLayer1::layer1)); - let vm = match &consignment.schema().script { - Script::AluVM(lib) => { - Box::new(AluRuntime::new(lib)) as Box - } - }; - Self { consignment, status, @@ -150,10 +127,8 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> genesis_id, contract_id, layers1, - anchor_index, - end_transitions, - validation_index, - anchor_validation_index, + validated_op_state, + validated_op_seals, vm, resolver, } @@ -164,18 +139,14 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> /// transaction id, and returns a validation object listing all detected /// failures, warnings and additional information. /// - /// When a failure detected, it not stopped; the failure is is logged into - /// the status object, but the validation continues for the rest of the - /// consignment data. This can help it debugging and detecting all problems - /// with the consignment. + /// When a failure detected, validation is not stopped; the failure is + /// logged into the status object, but the validation continues for the + /// rest of the consignment data. This can help it debugging and + /// detecting all problems with the consignment. pub fn validate(consignment: &'consignment C, resolver: &'resolver R, testnet: bool) -> Status { let mut validator = Validator::init(consignment, resolver); - - validator.validate_schema(consignment.schema()); - // If the network mismatches there is no point in validating the contract since - // all witness transactions will be missed. Thus, we return early (however after - // schema validation, which is not network-specific). + // all witness transactions will be missed. if testnet != validator.consignment.genesis().testnet { validator .status @@ -183,22 +154,31 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> return validator.status; } + validator.validate_schema(consignment.schema()); // We must return here, since if the schema is not valid there is no reason to - // validate contract nodes against it: it will produce a plenty of errors + // validate contract nodes against it: it will produce a plenty of errors. if validator.status.validity() == Validity::Invalid { return validator.status; } - validator.validate_contract(consignment.schema()); + validator.validate_commitments(); + // We must return here, since if there were no proper commitments, it is + // pointless to validate the contract state. + if validator.status.validity() == Validity::Invalid { + return validator.status; + } + validator.validate_logic(consignment.schema()); // Done. Returning status report with all possible failures, issues, warnings // and notifications about transactions we were unable to obtain. validator.status } + // *** PART I: Schema validation fn validate_schema(&mut self, schema: &SubSchema) { self.status += schema.verify(); } - fn validate_contract(&mut self, schema: &Schema) { + // *** PART II: Validating business logic + fn validate_logic(&mut self, schema: &'consignment Schema) { // [VALIDATION]: Making sure that we were supplied with the schema // that corresponds to the schema of the contract genesis if schema.schema_id() != self.schema_id { @@ -213,60 +193,34 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> } // [VALIDATION]: Validate genesis - self.status += schema.validate( - self.consignment, + self.status += schema.validate_state( + &self.consignment, OpRef::Genesis(self.consignment.genesis()), self.vm.as_ref(), ); - self.validation_index.insert(self.genesis_id); + self.validated_op_state.insert(self.genesis_id); // [VALIDATION]: Iterating over each endpoint, reconstructing operation // graph up to genesis for each one of them. // NB: We are not aiming to validate the consignment as a whole, but instead // treat it as a superposition of subgraphs, one for each endpoint; and validate // them independently. - for (operation, bundle_id) in self.end_transitions.clone() { - self.validate_branch(schema, operation, bundle_id); - } - // Replace missed (not yet mined) endpoint witness transaction failures - // with a dedicated type - for (operation, _) in &self.end_transitions { - if let Some(anchor) = self.anchor_index.get(&operation.id()) { - let anchor = match anchor { - Anchor::Bitcoin(anchor) | Anchor::Liquid(anchor) => anchor, - }; - - if let Some(pos) = self - .status - .failures - .iter() - .position(|f| f == &Failure::SealNoWitnessTx(anchor.txid)) - { - self.status.failures.remove(pos); - self.status - .unresolved_txids - .retain(|txid| *txid != anchor.txid); - self.status.unmined_terminals.push(anchor.txid); - self.status - .warnings - .push(Warning::TerminalWitnessNotMined(anchor.txid)); - } + for (bundle_id, _) in self.consignment.terminals() { + let Some(anchored_bundle) = self.consignment.anchored_bundle(bundle_id) else { + // We already checked and errored here during the terminal validation, so just + // skipping. + continue; + }; + for transition in anchored_bundle.bundle.known_transitions.values() { + self.validate_logic_on_route(schema, transition); } } - - // Generate warning if some of the transitions within the consignment were - // excessive (i.e. not part of validation_index). Nothing critical, but still - // good to report the user that the consignment is not perfect - for opid in self.consignment.op_ids_except(&self.validation_index) { - self.status.add_warning(Warning::ExcessiveOperation(opid)); - } } - fn validate_branch( + fn validate_logic_on_route( &mut self, schema: &Schema, - transition: &'consignment Transition, - bundle_id: BundleId, + transition: &Transition, ) { let mut queue: VecDeque = VecDeque::new(); @@ -283,10 +237,21 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> while let Some(operation) = queue.pop_front() { let opid = operation.id(); + if operation.contract_id() != self.contract_id { + self.status + .add_failure(Failure::ContractMismatch(opid, operation.contract_id())); + continue; + } + + if !self.validated_op_seals.contains(&opid) && + operation.op_type() == OpType::StateTransition + { + self.status.add_failure(Failure::SealsUnvalidated(opid)); + } // [VALIDATION]: Verify operation against the schema and scripts - if !self.validation_index.contains(&opid) { - self.status += schema.validate(self.consignment, operation, self.vm.as_ref()); - self.validation_index.insert(opid); + if self.validated_op_state.insert(opid) { + self.status += + schema.validate_state(&self.consignment, operation, self.vm.as_ref()); } match operation { @@ -294,38 +259,11 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> // nothing to add to the queue here } OpRef::Transition(transition) => { - // Making sure we do have a corresponding anchor; otherwise reporting failure - // (see below) - with the except of genesis and extension nodes, which does not - // have a corresponding anchor - if let Some(anchor) = self.anchor_index.get(&opid).cloned() { - if !self.anchor_validation_index.contains(&opid) { - // Ok, now we have the `operation` and the `anchor`, let's do all - // required checks - - // [VALIDATION]: Check that transition is committed into the anchor. - // This must be done with deterministic bitcoin - // commitments & LNPBP-4. - if anchor.convolve(self.contract_id, bundle_id.into()).is_err() { - self.status.add_failure(Failure::NotInAnchor(opid)); - } - - self.validate_transition(transition, bundle_id, anchor); - self.anchor_validation_index.insert(opid); - } - } else { - // If we've got here there is something broken with the consignment - // provider. - self.status.add_failure(Failure::NotAnchored(opid)); - } - // Now, we must collect all parent nodes and add them to the verification queue let parent_nodes = transition.inputs.iter().filter_map(|input| { self.consignment.operation(input.prev_out.op).or_else(|| { - // This will not actually happen since we already checked that each - // ancestor reference has a corresponding operation in the code above. - // But lets double-check :) self.status - .add_failure(Failure::TransitionAbsent(input.prev_out.op)); + .add_failure(Failure::OperationAbsent(input.prev_out.op)); None }) }); @@ -359,21 +297,93 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> } } - fn validate_transition( + // *** PART III: Validating single-use-seals + fn validate_commitments(&mut self) { + for bundle_id in self.consignment.bundle_ids() { + let Some(anchored_bundle) = self.consignment.anchored_bundle(bundle_id) else { + self.status.add_failure(Failure::BundleAbsent(bundle_id)); + continue; + }; + + let 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 witness_id = anchored_bundle.anchor.witness_id(); + let anchors = match &anchored_bundle.anchor { + XAnchor::Bitcoin(anchor) | XAnchor::Liquid(anchor) => 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, witness_id, bundle); + + // [VALIDATION]: We validate that the seals were properly closed on BP-type layers + let Some(witness_tx) = self.validate_commitments_bp(layer1, &seals, bundle_id, anchors) + else { + continue; + }; + + // [VALIDATION]: We validate bundle commitments to the input map + self.validate_bundle_commitments(bundle_id, bundle, witness_tx, input_map); + } + } + + /// Validates that the transition bundle is internally consistent: inputs of + /// its state transitions correspond to the way how they are committed + /// in the input map of the bundle; and these inputs are real inputs of + /// the transaction. + fn validate_bundle_commitments( &mut self, - transition: &'consignment Transition, bundle_id: BundleId, - anchor: &'consignment Anchor, + bundle: &TransitionBundle, + witness_tx: Tx, + input_map: BTreeMap>, ) { - let (layer1, anchor) = match anchor { - Anchor::Bitcoin(a) | Anchor::Liquid(a) => (anchor.layer1(), a), - }; + for (vin, opid) in &bundle.input_map { + let Some(outpoints) = input_map.get(opid) else { + self.status + .add_failure(Failure::BundleExtraTransition(bundle_id, *opid)); + continue; + }; + let Some(input) = witness_tx.inputs.get(vin.to_usize()) else { + self.status.add_failure(Failure::BundleInvalidInput( + bundle_id, + *opid, + witness_tx.txid(), + )); + continue; + }; + if !outpoints.contains(&input.prev_output) { + self.status.add_failure(Failure::BundleInvalidCommitment( + bundle_id, + *vin, + witness_tx.txid(), + *opid, + )); + } + } + } - let txid = anchor.txid; + /// Bitcoin- and liquid-specific commitment validation using deterministic + /// bitcoin commitments with opret and tapret schema. + fn validate_commitments_bp( + &mut self, + layer1: Layer1, + seals: impl AsRef<[ExplicitSeal]>, + bundle_id: BundleId, + anchors: &AnchorSet, + ) -> Option { + let Some(txid) = anchors.txid() else { + self.status + .add_failure(Failure::AnchorSetInvalid(bundle_id)); + return None; + }; // Check that the anchor is committed into a transaction spending all of the // transition inputs. - match self.resolver.resolve_tx(layer1, txid) { + match self.resolver.resolve_bp_tx(layer1, txid) { Err(_) => { // We wre unable to retrieve corresponding transaction, so can't check. // Reporting this incident and continuing further. Why this happens? No @@ -387,136 +397,157 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> // validation in order to detect the rest of problems (and reporting the // failure!) self.status.add_failure(Failure::SealNoWitnessTx(txid)); + None } Ok(witness_tx) => { - let witness = Witness::with(witness_tx, anchor.clone()); - self.validate_witness(transition, witness, bundle_id, anchor) + let (tapret, opret) = anchors.as_split(); + + let tapret_seals = seals + .as_ref() + .iter() + .filter(|seal| seal.method() == CloseMethod::TapretFirst); + if let Some(tapret) = tapret { + let witness = Witness::with(witness_tx.clone(), tapret.clone()); + self.validate_seal_closing_bp(tapret_seals, witness, bundle_id, tapret) + } else if tapret_seals.count() > 0 { + self.status.add_warning(Warning::UnclosedSeals(bundle_id)); + } + + let opret_seals = seals + .as_ref() + .iter() + .filter(|seal| seal.method() == CloseMethod::OpretFirst); + if let Some(opret) = opret { + let witness = Witness::with(witness_tx.clone(), opret.clone()); + self.validate_seal_closing_bp(opret_seals, witness, bundle_id, opret) + } else if opret_seals.count() > 0 { + self.status.add_warning(Warning::UnclosedSeals(bundle_id)); + } + Some(witness_tx) } } } - fn validate_witness( + /// Single-use-seal definition validation. + /// + /// Takes state transition, extracts all seals from its inputs and makes + /// sure they are defined or a correct layer1. + fn validate_seal_definitions( &mut self, - transition: &'consignment Transition, - witness: Witness, - bundle_id: BundleId, - anchor: &'consignment dbc::Anchor, - ) { - let opid = transition.id(); - let txid = witness.txid; - - // Checking that witness transaction closes seals defined by transition previous - // outputs. + layer1: Layer1, + witness_id: WitnessId, + bundle: &TransitionBundle, + ) -> (Vec>, BTreeMap>) { + let mut input_map: BTreeMap> = bmap!(); let mut seals = vec![]; - for input in &transition.inputs { - let Opout { op, ty, no } = input.prev_out; + for (opid, transition) in &bundle.known_transitions { + let opid = *opid; - let Some(prev_op) = self.consignment.operation(op) else { - // Node, referenced as the ancestor, was not found in the consignment. - // Usually this means that the consignment data are broken - self.status.add_failure(Failure::OperationAbsent(op)); - continue; - }; + if !self.validated_op_seals.insert(opid) { + self.status.add_failure(Failure::CyclicGraph(opid)); + } - let Some(variant) = prev_op.assignments_by_type(ty) else { - self.status.add_failure(Failure::NoPrevState { - opid, - prev_id: op, - state_type: ty, - }); - continue; - }; + // Checking that witness transaction closes seals defined by transition previous + // outputs. + for input in &transition.inputs { + let Opout { op, ty, no } = input.prev_out; - let Ok(seal) = variant.revealed_seal_at(no) else { - self.status - .add_failure(Failure::NoPrevOut(opid, input.prev_out)); - continue; - }; - let Some(seal) = seal else { - // Everything is ok, but we have incomplete data (confidential), thus can't do a - // full verification and have to report the failure - self.status - .add_failure(Failure::ConfidentialSeal(input.prev_out)); - continue; - }; + let Some(prev_op) = self.consignment.operation(op) else { + // Node, referenced as the ancestor, was not found in the consignment. + // Usually this means that the consignment data are broken + self.status.add_failure(Failure::OperationAbsent(op)); + continue; + }; - let Some(anchor) = self.anchor_index.get(&op) else { - panic!("anchor for the operation {op} was not indexed by the validator"); - }; - if seal.layer1() != anchor.layer1() { - self.status.add_failure(Failure::SealWitnessLayer1Mismatch { - seal: seal.layer1(), - anchor: anchor.layer1(), - }); - continue; - } - if !self.layers1.contains(&seal.layer1()) { - self.status - .add_failure(Failure::SealInvalidLayer1(seal.layer1(), seal)); - continue; - } + let Some(variant) = prev_op.assignments_by_type(ty) else { + self.status.add_failure(Failure::NoPrevState { + opid, + prev_id: op, + state_type: ty, + }); + continue; + }; - let seal = match (seal, anchor) { - ( - Xchain::Bitcoin( - seal @ GraphSeal { - txid: TxPtr::WitnessTx, - .. - }, - ) | - Xchain::Liquid( - seal @ GraphSeal { - txid: TxPtr::WitnessTx, - .. - }, - ), - Anchor::Bitcoin(anchor) | Anchor::Liquid(anchor), - ) => { - let prev_witness_txid = anchor.txid; - seal.resolve(prev_witness_txid) + let Ok(seal) = variant.revealed_seal_at(no) else { + self.status + .add_failure(Failure::NoPrevOut(opid, input.prev_out)); + continue; + }; + let Some(seal) = seal else { + // Everything is ok, but we have incomplete data (confidential), thus can't do a + // full verification and have to report the failure + self.status + .add_failure(Failure::ConfidentialSeal(input.prev_out)); + continue; + }; + + if seal.layer1() != layer1 { + self.status.add_failure(Failure::SealWitnessLayer1Mismatch { + seal: seal.layer1(), + anchor: layer1, + }); + continue; } - ( - Xchain::Bitcoin( - seal @ GraphSeal { - txid: TxPtr::Txid(txid), - .. - }, - ) | - Xchain::Liquid( - seal @ GraphSeal { - txid: TxPtr::Txid(txid), - .. - }, - ), - Anchor::Bitcoin(_) | Anchor::Liquid(_), - ) => seal.resolve(txid), - }; - seals.push(seal); + if !self.layers1.contains(&seal.layer1()) { + self.status + .add_failure(Failure::SealLayerMismatch(seal.layer1(), seal)); + continue; + } + + let seal = seal + .try_to_output_seal(witness_id) + .expect("method must be called only on BP-compatible layer 1") + .reduce_to_bp() + .expect("method must be called only on BP-compatible layer 1"); + seals.push(seal); + input_map + .entry(opid) + .or_default() + .insert(Outpoint::new(seal.txid, seal.vout)); + } } + (seals, input_map) + } + /// Single-use-seal closing validation. + /// + /// Checks that the set of seals is closed over the message, which is + /// multi-protocol commitment, by utilizing witness, consisting of + /// transaction with deterministic bitcoin commitments (defined by + /// generic type `Dbc`) and extra-transaction data, which are taken from + /// anchors DBC proof. + /// + /// Additionally checks that the provided message contains commitment to the + /// bundle under the current contract. + fn validate_seal_closing_bp<'seal, 'temp, Seal: TxoSeal + 'seal, Dbc: dbc::Proof>( + &mut self, + seals: impl IntoIterator, + witness: Witness, + bundle_id: BundleId, + anchor: &'temp Anchor, + ) { let message = mpc::Message::from(bundle_id); + // [VALIDATION]: Checking anchor MPC commitment match anchor.convolve(self.contract_id, message) { - Err(_) => { - self.status.add_failure(Failure::MpcInvalid(opid, txid)); + 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)); } Ok(commitment) => { // [VALIDATION]: CHECKING SINGLE-USE-SEALS witness - .verify_many_seals(&seals, &commitment) + .verify_many_seals(seals, &commitment) .map_err(|err| { - self.status - .add_failure(Failure::SealInvalid(opid, txid, err)); + self.status.add_failure(Failure::SealsInvalid( + bundle_id, + witness.txid, + err.to_string(), + )); }) .ok(); } } - - // [VALIDATION]: Checking anchor deterministic bitcoin commitment - if let Err(err) = anchor.verify(self.contract_id, message, &witness.tx) { - // The operation is not committed to bitcoin transaction graph! - // Ultimate failure. But continuing to detect the rest (after reporting it). - self.status - .add_failure(Failure::AnchorInvalid(opid, txid, err)); - } } } diff --git a/stl/RGB@0.1.0.sta b/stl/RGB@0.1.0.sta index 9c6509ab..a13b7d4b 100644 --- a/stl/RGB@0.1.0.sta +++ b/stl/RGB@0.1.0.sta @@ -1,335 +1,342 @@ -----BEGIN STRICT TYPE LIB----- -Id: urn:ubideco:stl:9gBiimtSnE1Qxwa49rbdPpv2s5ggXfitb6aktnunpDjX +Id: urn:ubideco:stl:68HqdPT8Bwr2Y9im6kY28Z2JNABrRnxKeGuhrkYCAYqE Name: RGB Dependencies: urn:ubideco:stl:ZtHaBzu9ojbDahaGKEXe5v9DfSDxLERbLkEB23R6Q6V, + urn:ubideco:stl:5Nyd3BhfDAEfc5th2httogefZGzMftfcJNh5Lo9guuTx, urn:ubideco:stl:5XLKQ1sNryZm9bdFKU2kBY3MPYdZXhchVdQKBbHA3gby, urn:ubideco:stl:9KALDYR8Nyjq4FdMW6kYoL7vdkWnqPqNuFnmE9qHpNjZ, - urn:ubideco:stl:BrKg2wSDRFDFSGzn2RrQkbwFeuLw2yDAFtpQ7WRh3nrz, urn:ubideco:stl:DVtm25LRKU4TjbyZmVxPhvCmctZ6vKkPKqfpU2QsDNUo, urn:ubideco:stl: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 -uwERY29uZmlkZW50aWFsU3RhdGUABgIEc2VhbAHA4gov08gxGtaH+pfqjunbIJMh -b5y9o1wnOqguM8YczwVzdGF0ZQEusqX+F8AwRDCY4FybG1ps1h7tdg1h3suAJtNk -8xbhuwIQY29uZmlkZW50aWFsU2VhbAAGAgRzZWFsAqE3cUlXlgi1ItyqExAFO+4f -42FVjTZ4t8Qi5wv6dv23aBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2CplLO9Bk4F -c3RhdGUBLrKl/hfAMEQwmOBcmxtabNYe7XYNYd7LgCbTZPMW4bsDCHJldmVhbGVk -AAYCBHNlYWwBwOIKL9PIMRrWh/qX6o7p2yCTIW+cvaNcJzqoLjPGHM8Fc3RhdGUB -LrKl/hfAMEQwmOBcmxtabNYe7XYNYd7LgCbTZPMW4bsOQXNzaWdubWVudFR5cGUF -AQAAAhlBc3NpZ25tZW50c0JsaW5kU2VhbFR4UHRyBQEACgGH/uJlWIhs6By+hoSz -eWMnE2WDxbZoAV6LFBY2sUSHuQES0B/DKzjcoudQlinXZaJzuJP3Ng6XQYTyYbhR -3gJQ5wAAAAAAAAAA/wAAAAAAAAAYQXNzaWdubWVudHNCbGluZFNlYWxUeGlkBQEA -CgGH/uJlWIhs6By+hoSzeWMnE2WDxbZoAV6LFBY2sUSHuQGMwdNqHGGkrHkTbqPf -Yf8oiq8voLGVTcVv1Knx91k/WwAAAAAAAAAA/wAAAAAAAAAIQXR0YWNoSWQFAQAH -AABAIAAOQmxpbmRpbmdGYWN0b3IFAQAHAABAIAAKQnVuZGxlSXRlbQYCBmlucHV0 -cwAJAAACAAAAAAAAAAD/AAAAAAAAAAp0cmFuc2l0aW9uAAQCAARub25lAAAAAQRz -b21lAAUBAb61xaOXj7fsShcZqPMJYJ/2pmcSaP3VSOUTHyhQfchXD0NvbmNlYWxl -ZEF0dGFjaAUBAAcAAEAgAA1Db25jZWFsZWREYXRhBQEABwAAQCAAEUNvbmNlYWxl -ZEZ1bmdpYmxlBgIKY29tbWl0bWVudAFIvRpuF/uGOxZ8fiVeNWfbgkPvKl6666LF -Cs0Jrp5RGgpyYW5nZVByb29mAahYa/iRZdrCIxBtvYXEhsk35rBm6wxmQf8WL2io -d6WYD0NvbnRyYWN0SGlzdG9yeQYICHNjaGVtYUlkAZRS09sDq6uoUZ+n9j7QFOvU -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 -YXgAAAIET3BJZAUBAAcAAEAgAAVPcG91dAYDAm9wAZXI5noedWJf1JZVQmqR635C -kKFvWpjxvlD3tookEvfFAnR5AYf+4mVYiGzoHL6GhLN5YycTZYPFtmgBXosUFjax -RIe5Am5vAAACHk91dHB1dEFzc2lnbm1lbnRSZXZlYWxlZEF0dGFjaAYEBW9wb3V0 -AZMQvICxNFqLL8NYUu4PTWjQHbf6NcubZJKrVgma9JqXBHNlYWwBB4v6b9MCRnOS -rdLA4I84wEVl5Hi45NS1lHdja4mcjQYFc3RhdGUBaFM0IAzcU8ZNduK2LqpmL9o6 -2ZG/BqspDrzQ9noDSswHd2l0bmVzcwAEAgAEbm9uZQAAAAEEc29tZQAFAQHEoAqe -4QkCE3ciARVZO4PDU+Jg+IEQ5LM2yuNoRkEnGxxPdXRwdXRBc3NpZ25tZW50UmV2 -ZWFsZWREYXRhBgQFb3BvdXQBkxC8gLE0Wosvw1hS7g9NaNAdt/o1y5tkkqtWCZr0 -mpcEc2VhbAEHi/pv0wJGc5Kt0sDgjzjARWXkeLjk1LWUd2NriZyNBgVzdGF0ZQEg -8lBWIo9mzvyR+upnvF/G8GlcPUd5c1k/rNE3ynJIZQd3aXRuZXNzAAQCAARub25l -AAAAAQRzb21lAAUBAcSgCp7hCQITdyIBFVk7g8NT4mD4gRDkszbK42hGQScbHU91 -dHB1dEFzc2lnbm1lbnRSZXZlYWxlZFZhbHVlBgQFb3BvdXQBkxC8gLE0Wosvw1hS -7g9NaNAdt/o1y5tkkqtWCZr0mpcEc2VhbAEHi/pv0wJGc5Kt0sDgjzjARWXkeLjk -1LWUd2NriZyNBgVzdGF0ZQFtRMb0L2oFOkMVD6bmwizE6HrlLJT4BtQMpdyH9nWN -oAd3aXRuZXNzAAQCAARub25lAAAAAQRzb21lAAUBAcSgCp7hCQITdyIBFVk7g8NT -4mD4gRDkszbK42hGQScbGU91dHB1dEFzc2lnbm1lbnRWb2lkU3RhdGUGBAVvcG91 -dAGTELyAsTRaiy/DWFLuD01o0B23+jXLm2SSq1YJmvSalwRzZWFsAQeL+m/TAkZz -kq3SwOCPOMBFZeR4uOTUtZR3Y2uJnI0GBXN0YXRlAS6ypf4XwDBEMJjgXJsbWmzW -Hu12DWHey4Am02TzFuG7B3dpdG5lc3MABAIABG5vbmUAAAABBHNvbWUABQEBxKAK -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 -T7I= +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 +p1ZJhkNCbroWCz+PjGj56E/9zS2FQAp57Q9gbe+hJuG8deH/SEv7hcE00Qwy3Iwe +QOBpSxzWp+vc3GEAAAAAAAAAAP//AAAAAAAACUFuY2hvclNldAQDAQZ0YXByZXQA +BQECQQ/3dkCGElO+wIwudl8Lul1qI5NB/Rn8QD0iX4ilcvsVHcmNASF4547x9MVc +64vo5jM0g0RZ4sKjXO4u7b0xTQIFb3ByZXQABQECQQ/3dkCGElO+wIwudl8Lul1q +I5NB/Rn8QD0iX4ilcvtknOe0JI+r9gBkw6hZJwCkgP+bfgb31M6MQWRg8ReOPQME +ZHVhbAAGAgZ0YXByZXQCQQ/3dkCGElO+wIwudl8Lul1qI5NB/Rn8QD0iX4ilcvsV +HcmNASF4547x9MVc64vo5jM0g0RZ4sKjXO4u7b0xTQVvcHJldAJBD/d2QIYSU77A +jC52Xwu6XWojk0H9GfxAPSJfiKVy+2Sc57Qkj6v2AGTDqFknAKSA/5t+BvfUzoxB +ZGDxF449DkFuY2hvcmVkQnVuZGxlBgIGYW5jaG9yAZnFUBdztq4RE9DZeugVGTjw +JheAW5lbTKe1zAiDMP52BmJ1bmRsZQESOEVVE20whsAVkkpbSoPln683cGGNaBwD ++6KqHPancAhBc3NldFRhZwUBAAcAAEAgACJBc3NpZ25SZXZlYWxlZEF0dGFjaEJs +aW5kU2VhbFR4UHRyBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkEP93ZAhhJTvsCM +LnZfC7pdaiOTQf0Z/EA9Il+IpXL7aBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2Cp +lLO9Bk4Fc3RhdGUB/DRF3V/PDQv/rBWkeroFIuBbiysbMGVSh4OPey3rjj0BEWNv +bmZpZGVudGlhbFN0YXRlAAYCBHNlYWwBJWFq3aw6xdsnG6nfvlxoTXofW/NHd9iL +hlXXpgfmuroFc3RhdGUB/DRF3V/PDQv/rBWkeroFIuBbiysbMGVSh4OPey3rjj0C +EGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAJBD/d2QIYSU77AjC52Xwu6XWojk0H9 +GfxAPSJfiKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0YXRl +AWhTNCAM3FPGTXbiti6qZi/aOtmRvwarKQ680PZ6A0rMAwhyZXZlYWxlZAAGAgRz +ZWFsASVhat2sOsXbJxup375caE16H1vzR3fYi4ZV16YH5rq6BXN0YXRlAWhTNCAM +3FPGTXbiti6qZi/aOtmRvwarKQ680PZ6A0rMIUFzc2lnblJldmVhbGVkQXR0YWNo +QmxpbmRTZWFsVHhpZAQEAAxjb25maWRlbnRpYWwABgIEc2VhbAJBD/d2QIYSU77A +jC52Xwu6XWojk0H9GfxAPSJfiKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNg +qZSzvQZOBXN0YXRlAfw0Rd1fzw0L/6wVpHq6BSLgW4srGzBlUoeDj3st6449ARFj +b25maWRlbnRpYWxTdGF0ZQAGAgRzZWFsAVayoow+asLxLxx0B3i++PaPaQTQwx+y +j3rgpfn+kGD6BXN0YXRlAfw0Rd1fzw0L/6wVpHq6BSLgW4srGzBlUoeDj3st6449 +AhBjb25maWRlbnRpYWxTZWFsAAYCBHNlYWwCQQ/3dkCGElO+wIwudl8Lul1qI5NB +/Rn8QD0iX4ilcvtoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcDYKmUs70GTgVzdGF0 +ZQFoUzQgDNxTxk124rYuqmYv2jrZkb8GqykOvND2egNKzAMIcmV2ZWFsZWQABgIE +c2VhbAFWsqKMPmrC8S8cdAd4vvj2j2kE0MMfso964KX5/pBg+gVzdGF0ZQFoUzQg +DNxTxk124rYuqmYv2jrZkb8GqykOvND2egNKzCBBc3NpZ25SZXZlYWxlZERhdGFC +bGluZFNlYWxUeFB0cgQEAAxjb25maWRlbnRpYWwABgIEc2VhbAJBD/d2QIYSU77A +jC52Xwu6XWojk0H9GfxAPSJfiKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNg +qZSzvQZOBXN0YXRlAXANZRCygoFvH7c95RJjkwNXCKVSYa0C4NS+WsXPp+oJARFj +b25maWRlbnRpYWxTdGF0ZQAGAgRzZWFsASVhat2sOsXbJxup375caE16H1vzR3fY +i4ZV16YH5rq6BXN0YXRlAXANZRCygoFvH7c95RJjkwNXCKVSYa0C4NS+WsXPp+oJ +AhBjb25maWRlbnRpYWxTZWFsAAYCBHNlYWwCQQ/3dkCGElO+wIwudl8Lul1qI5NB +/Rn8QD0iX4ilcvtoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcDYKmUs70GTgVzdGF0 +ZQEg8lBWIo9mzvyR+upnvF/G8GlcPUd5c1k/rNE3ynJIZQMIcmV2ZWFsZWQABgIE +c2VhbAElYWrdrDrF2ycbqd++XGhNeh9b80d32IuGVdemB+a6ugVzdGF0ZQEg8lBW +Io9mzvyR+upnvF/G8GlcPUd5c1k/rNE3ynJIZR9Bc3NpZ25SZXZlYWxlZERhdGFC +bGluZFNlYWxUeGlkBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkEP93ZAhhJTvsCM +LnZfC7pdaiOTQf0Z/EA9Il+IpXL7aBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2Cp +lLO9Bk4Fc3RhdGUBcA1lELKCgW8ftz3lEmOTA1cIpVJhrQLg1L5axc+n6gkBEWNv +bmZpZGVudGlhbFN0YXRlAAYCBHNlYWwBVrKijD5qwvEvHHQHeL749o9pBNDDH7KP +euCl+f6QYPoFc3RhdGUBcA1lELKCgW8ftz3lEmOTA1cIpVJhrQLg1L5axc+n6gkC +EGNvbmZpZGVudGlhbFNlYWwABgIEc2VhbAJBD/d2QIYSU77AjC52Xwu6XWojk0H9 +GfxAPSJfiKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0YXRl +ASDyUFYij2bO/JH66me8X8bwaVw9R3lzWT+s0TfKckhlAwhyZXZlYWxlZAAGAgRz +ZWFsAVayoow+asLxLxx0B3i++PaPaQTQwx+yj3rgpfn+kGD6BXN0YXRlASDyUFYi +j2bO/JH66me8X8bwaVw9R3lzWT+s0TfKckhlIUFzc2lnblJldmVhbGVkVmFsdWVC +bGluZFNlYWxUeFB0cgQEAAxjb25maWRlbnRpYWwABgIEc2VhbAJBD/d2QIYSU77A +jC52Xwu6XWojk0H9GfxAPSJfiKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNg +qZSzvQZOBXN0YXRlAcJRrWXpdQ2smhJZzMCFJFzV97FvUthyNkYs8XWMn05dARFj +b25maWRlbnRpYWxTdGF0ZQAGAgRzZWFsASVhat2sOsXbJxup375caE16H1vzR3fY +i4ZV16YH5rq6BXN0YXRlAcJRrWXpdQ2smhJZzMCFJFzV97FvUthyNkYs8XWMn05d +AhBjb25maWRlbnRpYWxTZWFsAAYCBHNlYWwCQQ/3dkCGElO+wIwudl8Lul1qI5NB +/Rn8QD0iX4ilcvtoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcDYKmUs70GTgVzdGF0 +ZQFtRMb0L2oFOkMVD6bmwizE6HrlLJT4BtQMpdyH9nWNoAMIcmV2ZWFsZWQABgIE +c2VhbAElYWrdrDrF2ycbqd++XGhNeh9b80d32IuGVdemB+a6ugVzdGF0ZQFtRMb0 +L2oFOkMVD6bmwizE6HrlLJT4BtQMpdyH9nWNoCBBc3NpZ25SZXZlYWxlZFZhbHVl +QmxpbmRTZWFsVHhpZAQEAAxjb25maWRlbnRpYWwABgIEc2VhbAJBD/d2QIYSU77A +jC52Xwu6XWojk0H9GfxAPSJfiKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNg +qZSzvQZOBXN0YXRlAcJRrWXpdQ2smhJZzMCFJFzV97FvUthyNkYs8XWMn05dARFj +b25maWRlbnRpYWxTdGF0ZQAGAgRzZWFsAVayoow+asLxLxx0B3i++PaPaQTQwx+y +j3rgpfn+kGD6BXN0YXRlAcJRrWXpdQ2smhJZzMCFJFzV97FvUthyNkYs8XWMn05d +AhBjb25maWRlbnRpYWxTZWFsAAYCBHNlYWwCQQ/3dkCGElO+wIwudl8Lul1qI5NB +/Rn8QD0iX4ilcvtoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcDYKmUs70GTgVzdGF0 +ZQFtRMb0L2oFOkMVD6bmwizE6HrlLJT4BtQMpdyH9nWNoAMIcmV2ZWFsZWQABgIE +c2VhbAFWsqKMPmrC8S8cdAd4vvj2j2kE0MMfso964KX5/pBg+gVzdGF0ZQFtRMb0 +L2oFOkMVD6bmwizE6HrlLJT4BtQMpdyH9nWNoB1Bc3NpZ25Wb2lkU3RhdGVCbGlu +ZFNlYWxUeFB0cgQEAAxjb25maWRlbnRpYWwABgIEc2VhbAJBD/d2QIYSU77AjC52 +Xwu6XWojk0H9GfxAPSJfiKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSz +vQZOBXN0YXRlAS6ypf4XwDBEMJjgXJsbWmzWHu12DWHey4Am02TzFuG7ARFjb25m +aWRlbnRpYWxTdGF0ZQAGAgRzZWFsASVhat2sOsXbJxup375caE16H1vzR3fYi4ZV +16YH5rq6BXN0YXRlAS6ypf4XwDBEMJjgXJsbWmzWHu12DWHey4Am02TzFuG7AhBj +b25maWRlbnRpYWxTZWFsAAYCBHNlYWwCQQ/3dkCGElO+wIwudl8Lul1qI5NB/Rn8 +QD0iX4ilcvtoGeu81bMYq5ezmKVLNmXd2qcGb+jpJOcDYKmUs70GTgVzdGF0ZQEu +sqX+F8AwRDCY4FybG1ps1h7tdg1h3suAJtNk8xbhuwMIcmV2ZWFsZWQABgIEc2Vh +bAElYWrdrDrF2ycbqd++XGhNeh9b80d32IuGVdemB+a6ugVzdGF0ZQEusqX+F8Aw +RDCY4FybG1ps1h7tdg1h3suAJtNk8xbhuxxBc3NpZ25Wb2lkU3RhdGVCbGluZFNl +YWxUeGlkBAQADGNvbmZpZGVudGlhbAAGAgRzZWFsAkEP93ZAhhJTvsCMLnZfC7pd +aiOTQf0Z/EA9Il+IpXL7aBnrvNWzGKuXs5ilSzZl3dqnBm/o6STnA2CplLO9Bk4F +c3RhdGUBLrKl/hfAMEQwmOBcmxtabNYe7XYNYd7LgCbTZPMW4bsBEWNvbmZpZGVu +dGlhbFN0YXRlAAYCBHNlYWwBVrKijD5qwvEvHHQHeL749o9pBNDDH7KPeuCl+f6Q +YPoFc3RhdGUBLrKl/hfAMEQwmOBcmxtabNYe7XYNYd7LgCbTZPMW4bsCEGNvbmZp +ZGVudGlhbFNlYWwABgIEc2VhbAJBD/d2QIYSU77AjC52Xwu6XWojk0H9GfxAPSJf +iKVy+2gZ67zVsxirl7OYpUs2Zd3apwZv6Okk5wNgqZSzvQZOBXN0YXRlAS6ypf4X +wDBEMJjgXJsbWmzWHu12DWHey4Am02TzFuG7AwhyZXZlYWxlZAAGAgRzZWFsAVay +oow+asLxLxx0B3i++PaPaQTQwx+yj3rgpfn+kGD6BXN0YXRlAS6ypf4XwDBEMJjg +XJsbWmzWHu12DWHey4Am02TzFuG7DkFzc2lnbm1lbnRUeXBlBQEAAAIZQXNzaWdu +bWVudHNCbGluZFNlYWxUeFB0cgUBAAoBh/7iZViIbOgcvoaEs3ljJxNlg8W2aAFe +ixQWNrFEh7kBa9EQPVH54dz3yE5Hlem8RTMgINYYRZEfl0KkVBueclwAAAAAAAAA +AP8AAAAAAAAAGEFzc2lnbm1lbnRzQmxpbmRTZWFsVHhpZAUBAAoBh/7iZViIbOgc +voaEs3ljJxNlg8W2aAFeixQWNrFEh7kBrReyc1pbStH15nyRZz0WkJ2UnKi9/yQ5 +BpeVy4koirEAAAAAAAAAAP8AAAAAAAAACEF0dGFjaElkBQEABwAAQCAADkJsaW5k +aW5nRmFjdG9yBQEABwAAQCAAD0NvbmNlYWxlZEF0dGFjaAUBAAcAAEAgAA1Db25j +ZWFsZWREYXRhBQEABwAAQCAAEUNvbmNlYWxlZEZ1bmdpYmxlBgIKY29tbWl0bWVu +dAFIvRpuF/uGOxZ8fiVeNWfbgkPvKl6666LFCs0Jrp5RGgpyYW5nZVByb29mAahY +a/iRZdrCIxBtvYXEhsk35rBm6wxmQf8WL2iod6WYD0NvbnRyYWN0SGlzdG9yeQYI +CHNjaGVtYUlkAZRS09sDq6uoUZ+n9j7QFOvUTX0xP/z+APpdJHpUBJAQDHJvb3RT +Y2hlbWFJZAAEAgAEbm9uZQAAAAEEc29tZQAFAQGUUtPbA6urqFGfp/Y+0BTr1E19 +MT/8/gD6XSR6VASQEApjb250cmFjdElkAZ8ILEk6yAKiusXd3AsifCCvlNRoxEjP +Gloh4L3C9ToyBmdsb2JhbAAKAdXukg5JiLNp8WpT0QdK+7Uj+MdScR77Nj1WWQXh +5BXLAAoBevV5DYxuILR98B/1zYVp9cu88RDBr2VstZCilrSr/wEBIPJQViKPZs78 +kfrqZ7xfxvBpXD1HeXNZP6zRN8pySGUAAAAAAAAAAP////8AAAAAAAAAAAAAAAD/ +AAAAAAAAAAZyaWdodHMACQHAmUPBk94MaIXS7scBxs9lR+5nJWQ8Qei18pR7TFAv +uQAAAAAAAAAA/////wAAAAAJZnVuZ2libGVzAAkBxuLkjvJxT+uNZSUyDoB7+GZU +SiL/Jt58VdmcZRaJ0F8AAAAAAAAAAP////8AAAAABGRhdGEACQFZ4ZDKG4w7ZJiy +vPGip/NUxlax6zgj4cO5klWrPr0OPgAAAAAAAAAA/////wAAAAAGYXR0YWNoAAkB +7S8juEPqDhUbGJ7zsNbHI+chYUuVrrerDOUBuOKIVZwAAAAAAAAAAP////8AAAAA +CkNvbnRyYWN0SWQFAQAHAABAIAANQ29udHJhY3RTdGF0ZQYCBnNjaGVtYQEQQYJy +NRcc2YKVkqaZF/4DcT+tb+LTIU6Iwf4co6cKnQdoaXN0b3J5AeAa0ySHXQuDI/9G +QWcmzHlRjcQEjmf6UtqE/M2hIMYVCUV4dGVuc2lvbgYIA2ZmdgHam1ETWBZWdpCH ++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 +L2oFOkMVD6bmwizE6HrlLJT4BtQMpdyH9nWNoAd3aXRuZXNzAAQCAARub25lAAAA +AQRzb21lAAUBAcSgCp7hCQITdyIBFVk7g8NT4mD4gRDkszbK42hGQScbGU91dHB1 +dEFzc2lnbm1lbnRWb2lkU3RhdGUGBAVvcG91dAGTELyAsTRaiy/DWFLuD01o0B23 ++jXLm2SSq1YJmvSalwRzZWFsAaDIP2bjJVuxHhWqE4mveGGWqVaXKVz0ZmKNY7H/ +IPiOBXN0YXRlAS6ypf4XwDBEMJjgXJsbWmzWHu12DWHey4Am02TzFuG7B3dpdG5l +c3MABAIABG5vbmUAAAABBHNvbWUABQEBxKAKnuEJAhN3IgEVWTuDw1PiYPiBEOSz +NsrjaEZBJxsSUGVkZXJzZW5Db21taXRtZW50BQEABwAAQCEAClJhbmdlUHJvb2YE +Af8LcGxhY2Vob2xkZXIABQEBHnYX8Sd92z674WoPchG3be1V329DDVURXwN4J6VC +VycIUmVkZWVtZWQFAQAKAUbt5sMIHP245lekKlzgxgiEf/wfvl52uXF0qcr1iVnO +AZXI5noedWJf1JZVQmqR635CkKFvWpjxvlD3tookEvfFAAAAAAAAAAD/AAAAAAAA +AAxSZXNlcnZlZEJ5dGUFAQAAAQ5SZXZlYWxlZEF0dGFjaAYDAmlkAYRxDZMsTvTD +twhLaYuwh3ApfjlkJH9Fkdjag23Rfbo4CW1lZGlhVHlwZQFCMGGFiMjUqxQmQMf9 +yRcszdD/EP8Nk4AARHyImt3MeQRzYWx0AAAIDFJldmVhbGVkRGF0YQUBAAgAAEAA +AAAAAAAAAP//AAAAAAAAEFJldmVhbGVkRnVuZ2libGUGAwV2YWx1ZQGmjDCRR0vK +OsJijMeVRI0s3arFFJ8FM5Wr9jxVYQcXJghibGluZGluZwGFuPgru/Skpg2zvz9F +uA+UbniDw61SbZP0b6MBqG5H2gN0YWcByY+aqcMGSxr9/Wcbl7wq/P5MaI8fc8gt +63Fv52mbIq8GU2NoZW1hBgoDZmZ2AdqbURNYFlZ2kIf7meVWlHI2gNc5DAahzCSY +LAVk98zVCHN1YnNldE9mAAQCAARub25lAAAAAQRzb21lAAUBAAAAC2dsb2JhbFR5 +cGVzAAoB1e6SDkmIs2nxalPRB0r7tSP4x1JxHvs2PVZZBeHkFcsBx5im2GM2eEQe +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 +AAIKc3RydWN0dXJlZAAFAQAIAZy+FvstUIIP6PDf+B50LCaNVOryk7Y/2AiA4zOv +1HJMAAAAAAAAAAD//wAAAAAAAP8KYXR0YWNobWVudAAFAQAIAUuSw8nD8/xv2t0Z +E1Gb98Y4Qi0LmS6zhNkIGh3BZKcmAAAAAAAAAAD//wAAAAAAABlUeXBlZEFzc2ln +bnNCbGluZFNlYWxUeGlkBAQAC2RlY2xhcmF0aXZlAAUBAAgB/bGlv7I+F1FCfW5Q +KpRakyPNH5MfJuKapnzpG4Aw3PEAAAAAAAAAAP//AAAAAAAAAQhmdW5naWJsZQAF +AQAIAcd49g41PYTHEt9wS+ro+eh6YJiSqeM0KCn/EjimhSkYAAAAAAAAAAD//wAA +AAAAAAIKc3RydWN0dXJlZAAFAQAIAV20SWpGjdA9H0T5J/x1RYz/RiVePFbZjDZN +eMftLnoQAAAAAAAAAAD//wAAAAAAAP8KYXR0YWNobWVudAAFAQAIATB8kryIvczP +SQkuBDneEEiOoh+UMm6oskBR/yeVMOzBAAAAAAAAAAD//wAAAAAAAAlWYWxlbmNp +ZXMFAQAJAUbt5sMIHP245lekKlzgxgiEf/wfvl52uXF0qcr1iVnOAAAAAAAAAAD/ +AAAAAAAAAAtWYWxlbmN5VHlwZQUBAAACCVZvaWRTdGF0ZQUBAAAADVdpdG5lc3NB +bmNob3IGAgp3aXRuZXNzT3JkAcIe7NwA077i648Cm3I6+7EQwDaX6c8DaBmUFaYE +B2nKCXdpdG5lc3NJZAHEoAqe4QkCE3ciARVZO4PDU+Jg+IEQ5LM2yuNoRkEnGwlX +aXRuZXNzSWQEAgAHYml0Y29pbgAFAQL1bBNiI/Y5p0oJk9xHRsn5iqu4g1hdtdkW +Pxh+xCgaCqOCQvPL19HQoRLajeFgL1bU+G8OxMR2xcBoWUxLBGVWAQZsaXF1aWQA +BQEC9WwTYiP2OadKCZPcR0bJ+YqruINYXbXZFj8YfsQoGgqjgkLzy9fR0KES2o3h +YC9W1PhvDsTEdsXAaFlMSwRlVgpXaXRuZXNzT3JkBAIAB29uQ2hhaW4ABQEBDr+4 +7ThibqSDujTzFPlUdelW2Uc1E9wnGBY8Y7bhibEBCG9mZkNoYWluAAAACldpdG5l +c3NQb3MGAgZoZWlnaHQAAAQJdGltZXN0YW1wAABIB1hBbmNob3IEAgAHYml0Y29p +bgAFAQEwgEw6iTEH29grmqM+pjbEpOsNCUKUYovL9AfXtL+NjQEGbGlxdWlkAAUB +ATCATDqJMQfb2Cuaoz6mNsSk6w0JQpRii8v0B9e0v42NE1hTZWFsQmxpbmRTZWFs +VHhQdHIEAgAHYml0Y29pbgAFAQJBD/d2QIYSU77AjC52Xwu6XWojk0H9GfxAPSJf +iKVy+361+DN8kapvuL2Vu7vL9eHFJYlF0hF22h7U3IzFMSVlAQZsaXF1aWQABQEC +QQ/3dkCGElO+wIwudl8Lul1qI5NB/Rn8QD0iX4ilcvt+tfgzfJGqb7i9lbu7y/Xh +xSWJRdIRdtoe1NyMxTElZRJYU2VhbEJsaW5kU2VhbFR4aWQEAgAHYml0Y29pbgAF +AQJBD/d2QIYSU77AjC52Xwu6XWojk0H9GfxAPSJfiKVy+wxQbJJJnFjzLy7sLFS4 +1xZrvJgU7D6ZAkU9cS66YQnhAQZsaXF1aWQABQECQQ/3dkCGElO+wIwudl8Lul1q +I5NB/Rn8QD0iX4ilcvsMUGySSZxY8y8u7CxUuNcWa7yYFOw+mQJFPXEuumEJ4RFY +U2VhbEV4cGxpY2l0U2VhbAQCAAdiaXRjb2luAAUBAkEP93ZAhhJTvsCMLnZfC7pd +aiOTQf0Z/EA9Il+IpXL7WIWOfQWSV+0FDUi1ZvsI5YoO3aqzDhah7xiuY5kET7IB +BmxpcXVpZAAFAQJBD/d2QIYSU77AjC52Xwu6XWojk0H9GfxAPSJfiKVy+1iFjn0F +klftBQ1ItWb7COWKDt2qsw4Woe8YrmOZBE+y -----END STRICT TYPE LIB----- diff --git a/stl/RGB@0.1.0.stl b/stl/RGB@0.1.0.stl index 36f40e3b..4eb1c270 100644 Binary files a/stl/RGB@0.1.0.stl and b/stl/RGB@0.1.0.stl differ diff --git a/stl/RGB@0.1.0.sty b/stl/RGB@0.1.0.sty index eaaaf5ee..feb42604 100644 --- a/stl/RGB@0.1.0.sty +++ b/stl/RGB@0.1.0.sty @@ -1,5 +1,5 @@ {- - Id: urn:ubideco:stl:9gBiimtSnE1Qxwa49rbdPpv2s5ggXfitb6aktnunpDjX#cosmos-gentle-goblin + Id: urn:ubideco:stl:68HqdPT8Bwr2Y9im6kY28Z2JNABrRnxKeGuhrkYCAYqE#laptop-brush-baby Name: RGB Version: 0.1.0 Description: Consensus layer for RGB smart contracts @@ -15,6 +15,22 @@ import urn:ubideco:stl:ZtHaBzu9ojbDahaGKEXe5v9DfSDxLERbLkEB23R6Q6V#rhino-cover-f -- MerkleProof := urn:ubideco:semid:4E7NDL8Nm1EXtcenS9idAx1LAXvTu2wRdYsxT8Q2hgRC#carol-alamo-denver -- MerkleNode := urn:ubideco:semid:6kxYeCatpncbA9UiTdsFbxbxJdU56x6MdmTRkEeGAv6R#iceberg-rocket-velvet +import urn:ubideco:stl:5Nyd3BhfDAEfc5th2httogefZGzMftfcJNh5Lo9guuTx#father-soviet-mercy as BPCore +-- Imports: +-- TapretNodePartner := urn:ubideco:semid:6o6mGBNbDXJCcNgk5ohP6wgXcdXZvYd1ZWy1GMBy5q2#iceberg-poker-active +-- BlindSealTxid := urn:ubideco:semid:q529pAPHhD1aFgueAHy8QtfjUayszR85WgEg7s2a3KE#raymond-reply-phrase +-- TapretProof := urn:ubideco:semid:24LBor5ZVKjGk4uBbEb28w6D7rroV3mpVAntNAWQsYaP#forum-paint-tunnel +-- TapretPathProof := urn:ubideco:semid:2LANtvWZDRes61SHKFxtFPzSuTzaKQGCUvYsNowNf3n3#stage-element-update +-- AnchorMerkleProofTapretProof := urn:ubideco:semid:2RRuXmkGM51gpruJzNCL6mSFm7nQFrP6zXm6CmNr77uE#arcade-modest-fossil +-- TapretRightBranch := urn:ubideco:semid:4nZtVVw7QJaMDHYffkHBWhxXSkLXLcJ89qTLZH4Z3xck#basket-prelude-bridge +-- OpretProof := urn:ubideco:semid:5oMeVsXXeicrdvrCCEZQiKWcrDHy4nQPbaQGhtnwM3ug#segment-ambient-totem +-- ExplicitSeal := urn:ubideco:semid:6xYzjyQ958BPRsJgBXL4N2DhswvgHWLp769Uzo4ybbUV#turtle-phoenix-panda +-- AnchorMerkleProofOpretProof := urn:ubideco:semid:7mkYAw62Vi8BuyQ16qGg3bgFw8WVtTXUkJoYBv1qoNg4#tape-mental-legacy +-- SecretSeal := urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle +-- BlindSealTxPtr := urn:ubideco:semid:9XdJg1BFMpMXPfaiw4Te79W2qYgArsEye6XPJUtj31L8#metro-chris-olympic +-- TxPtr := urn:ubideco:semid:CyRtMpPJkKLX3AdhgY7ZyA7PnYAzCo7yFTeYwwGsUBhn#strange-source-father +-- CloseMethod := urn:ubideco:semid:FA1JhsEFKi2LLpuAjuvLA3qiBuEJrwpKyypB9J2aPicr#july-salmon-contact + import urn:ubideco:stl:5XLKQ1sNryZm9bdFKU2kBY3MPYdZXhchVdQKBbHA3gby#south-strong-welcome as StrictTypes -- Imports: -- Primitive := urn:ubideco:semid:3T3zMmQxuir7TsdjhBLaETJfLH4mr5amAseXDePnzhMT#hobby-cable-puzzle @@ -37,21 +53,6 @@ import urn:ubideco:stl:9KALDYR8Nyjq4FdMW6kYoL7vdkWnqPqNuFnmE9qHpNjZ#justice-rock -- Bool := urn:ubideco:semid:7ZhBHGSJm9ixmm8Z9vCX7i5Ga7j5xrW8t11nsb1Cgpnx#laser-madam-maxwell -- AlphaNumLodash := urn:ubideco:semid:8iBe2dh8beD1KUairdqCacEcxAr4h55XfUQN2PspWXjz#north-sound-salsa -import urn:ubideco:stl:BrKg2wSDRFDFSGzn2RrQkbwFeuLw2yDAFtpQ7WRh3nrz#dance-swim-liter as BPCore --- Imports: --- TapretNodePartner := urn:ubideco:semid:6o6mGBNbDXJCcNgk5ohP6wgXcdXZvYd1ZWy1GMBy5q2#iceberg-poker-active --- BlindSealTxid := urn:ubideco:semid:q529pAPHhD1aFgueAHy8QtfjUayszR85WgEg7s2a3KE#raymond-reply-phrase --- TapretProof := urn:ubideco:semid:24LBor5ZVKjGk4uBbEb28w6D7rroV3mpVAntNAWQsYaP#forum-paint-tunnel --- TapretPathProof := urn:ubideco:semid:2LANtvWZDRes61SHKFxtFPzSuTzaKQGCUvYsNowNf3n3#stage-element-update --- TapretRightBranch := urn:ubideco:semid:4nZtVVw7QJaMDHYffkHBWhxXSkLXLcJ89qTLZH4Z3xck#basket-prelude-bridge --- AnchorMerkleProof := urn:ubideco:semid:5YGZTLPUCHos5Wg8Gm5cLgMPZijqHQLp8CGTMoXTNPf1#coral-vienna-horizon --- ExplicitSeal := urn:ubideco:semid:6xYzjyQ958BPRsJgBXL4N2DhswvgHWLp769Uzo4ybbUV#turtle-phoenix-panda --- SecretSeal := urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle --- BlindSealTxPtr := urn:ubideco:semid:9XdJg1BFMpMXPfaiw4Te79W2qYgArsEye6XPJUtj31L8#metro-chris-olympic --- TxPtr := urn:ubideco:semid:CyRtMpPJkKLX3AdhgY7ZyA7PnYAzCo7yFTeYwwGsUBhn#strange-source-father --- CloseMethod := urn:ubideco:semid:FA1JhsEFKi2LLpuAjuvLA3qiBuEJrwpKyypB9J2aPicr#july-salmon-contact --- Proof := urn:ubideco:semid:FJrSpHdBSVvF5B8YAq3vY15Qh2k3NUXvdkt8ESb1hLW7#hello-america-origin - import urn:ubideco:stl:DVtm25LRKU4TjbyZmVxPhvCmctZ6vKkPKqfpU2QsDNUo#exodus-axiom-tommy as AluVM -- Imports: -- LibSite := urn:ubideco:semid:8Q9NNyK2PCcjZ7U7rDGUJBhk8q37hAnWLgSizGLmr56g#mission-papa-mercy @@ -77,53 +78,54 @@ data AltLayer1 :: liquid:1 data AltLayer1Set :: {AltLayer1 ^ ..0xff} -- urn:ubideco:semid:ByCxcZ2hYTTJ8yoUhpUuHaxceQoAjqsxSF9zJkED3JuM#soviet-arsenal-complex data AluScript :: libs {AluVM.LibId -> ^ ..0xff [Byte]}, entryPoints {[Byte ^ 3] -> AluVM.LibSite {- urn:ubideco:semid:8Q9NNyK2PCcjZ7U7rDGUJBhk8q37hAnWLgSizGLmr56g#mission-papa-mercy -}} --- urn:ubideco:semid:2kbCRxs1hpVo6C9XpXBysmxSyx4HhE6ounqggmvWA1wW#spring-felix-model -data Anchor :: bitcoin BPCore.AnchorMerkleProof {- urn:ubideco:semid:5YGZTLPUCHos5Wg8Gm5cLgMPZijqHQLp8CGTMoXTNPf1#coral-vienna-horizon -} - | liquid BPCore.AnchorMerkleProof {- urn:ubideco:semid:5YGZTLPUCHos5Wg8Gm5cLgMPZijqHQLp8CGTMoXTNPf1#coral-vienna-horizon -} --- urn:ubideco:semid:3wnGtf3mZhoFh3zXkSppQCDFesMZzj8dxmXCcEf7EgPP#picture-service-index -data AnchoredBundle :: anchor Anchor, bundle TransitionBundle +-- urn:ubideco:semid:4GL38RVDhs4JkL5phqyF6pguwL6Av8qTUWCVNfGeWdUg#history-joel-ivory +data AnchorSet :: tapret:1 BPCore.AnchorMerkleProofTapretProof {- urn:ubideco:semid:2RRuXmkGM51gpruJzNCL6mSFm7nQFrP6zXm6CmNr77uE#arcade-modest-fossil -} + | opret BPCore.AnchorMerkleProofOpretProof {- urn:ubideco:semid:7mkYAw62Vi8BuyQ16qGg3bgFw8WVtTXUkJoYBv1qoNg4#tape-mental-legacy -} + | dual (tapret BPCore.AnchorMerkleProofTapretProof {- urn:ubideco:semid:2RRuXmkGM51gpruJzNCL6mSFm7nQFrP6zXm6CmNr77uE#arcade-modest-fossil -}, opret BPCore.AnchorMerkleProofOpretProof {- urn:ubideco:semid:7mkYAw62Vi8BuyQ16qGg3bgFw8WVtTXUkJoYBv1qoNg4#tape-mental-legacy -}) +-- urn:ubideco:semid:Af6Yix94PXQE73AAR55UvXUsSZgARZEnAZGiJYeo987E#cowboy-gallop-miller +data AnchoredBundle :: anchor XAnchor, bundle TransitionBundle -- urn:ubideco:semid:EZoxBpGenvb9UVze1zuwuEHqQJAqw2m3T8za5gbX1JZk#buzzer-pattern-craft data AssetTag :: [Byte ^ 32] --- urn:ubideco:semid:5Q2gTbSdQZDn4BBabk7121ogbCdbsqVeiMnZiHKka4Zd#process-canal-lopez +-- urn:ubideco:semid:FoWWT72VexCqqTZe4BTUWd3rGFmdgmjgzG4BtEXAhrFG#clark-yoga-needle data AssignRevealedAttachBlindSealTxPtr :: confidential (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state ConcealedAttach) - | confidentialState (seal XchainBlindSealTxPtr, state ConcealedAttach) + | confidentialState (seal XSealBlindSealTxPtr, state ConcealedAttach) | confidentialSeal (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state RevealedAttach) - | revealed (seal XchainBlindSealTxPtr, state RevealedAttach) --- urn:ubideco:semid:EPQDTH5KJx4reXGKDUYdJkQU2qpCdHse4nokD8TrqP1H#danube-plate-drink + | revealed (seal XSealBlindSealTxPtr, state RevealedAttach) +-- urn:ubideco:semid:9sXC96VNRB6EjYqvNHf621Tc34As2ym7hPZYzGocZcjv#british-castro-cannon data AssignRevealedAttachBlindSealTxid :: confidential (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state ConcealedAttach) - | confidentialState (seal XchainBlindSealTxid, state ConcealedAttach) + | confidentialState (seal XSealBlindSealTxid, state ConcealedAttach) | confidentialSeal (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state RevealedAttach) - | revealed (seal XchainBlindSealTxid, state RevealedAttach) --- urn:ubideco:semid:7krjkF3hTEpEaVND3WMb6R82RJQXDJdQH5zYmwTrVB1y#climax-ballet-harvard + | revealed (seal XSealBlindSealTxid, state RevealedAttach) +-- urn:ubideco:semid:8TYdgvgzXVeSDtSx4N4NBFaZX6yseoN8wdu2UmjyG4kf#brain-echo-chant data AssignRevealedDataBlindSealTxPtr :: confidential (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state ConcealedData) - | confidentialState (seal XchainBlindSealTxPtr, state ConcealedData) + | confidentialState (seal XSealBlindSealTxPtr, state ConcealedData) | confidentialSeal (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state RevealedData) - | revealed (seal XchainBlindSealTxPtr, state RevealedData) --- urn:ubideco:semid:3JNyNJbwW9qYFRrt92gJ3g779psW6DCcZgv59hKn9p3R#local-drama-savage + | revealed (seal XSealBlindSealTxPtr, state RevealedData) +-- urn:ubideco:semid:GBLt8m4DjojpH2tLJQdSYjCpfmYEQzMY6MDptoUiqBsU#kilo-family-minimum data AssignRevealedDataBlindSealTxid :: confidential (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state ConcealedData) - | confidentialState (seal XchainBlindSealTxid, state ConcealedData) + | confidentialState (seal XSealBlindSealTxid, state ConcealedData) | confidentialSeal (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state RevealedData) - | revealed (seal XchainBlindSealTxid, state RevealedData) --- urn:ubideco:semid:EAQsdSBXLyP13AaLsfpWtgS3efUeEz9dtUiED3bonT76#shelf-channel-agent + | revealed (seal XSealBlindSealTxid, state RevealedData) +-- urn:ubideco:semid:9C9FFDS8HCuTpseRp8R4B8XULZsWdp9JTvrmXmJjWKLX#moment-rabbit-ambient data AssignRevealedValueBlindSealTxPtr :: confidential (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state ConcealedFungible) - | confidentialState (seal XchainBlindSealTxPtr, state ConcealedFungible) + | confidentialState (seal XSealBlindSealTxPtr, state ConcealedFungible) | confidentialSeal (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state RevealedFungible) - | revealed (seal XchainBlindSealTxPtr, state RevealedFungible) --- urn:ubideco:semid:AvLXeeVAMwVbV488VtWb2oX5TrCwDcDBFDFTEeu9Uvzu#exotic-kansas-venus + | revealed (seal XSealBlindSealTxPtr, state RevealedFungible) +-- urn:ubideco:semid:CZ6L13AWVT2QFdgjzWTca9pvLEFsm3wmszKrvJ3u4JjA#cabaret-asia-flipper data AssignRevealedValueBlindSealTxid :: confidential (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state ConcealedFungible) - | confidentialState (seal XchainBlindSealTxid, state ConcealedFungible) + | confidentialState (seal XSealBlindSealTxid, state ConcealedFungible) | confidentialSeal (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state RevealedFungible) - | revealed (seal XchainBlindSealTxid, state RevealedFungible) --- urn:ubideco:semid:D6VJLrKLhXibehWpntHuNSceXS8355ivWtF1xkCDJ3i1#western-neuron-harris + | revealed (seal XSealBlindSealTxid, state RevealedFungible) +-- urn:ubideco:semid:8V73NTKvhVbbofS9Ne2KmDKC4ZjnWxK5iGaavvmMQWkU#uncle-smoke-bishop data AssignVoidStateBlindSealTxPtr :: confidential (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state VoidState) - | confidentialState (seal XchainBlindSealTxPtr, state VoidState) + | confidentialState (seal XSealBlindSealTxPtr, state VoidState) | confidentialSeal (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state VoidState) - | revealed (seal XchainBlindSealTxPtr, state VoidState) --- urn:ubideco:semid:FRJRWe9x1NsTGTMnarxjr1zW8CgSQRWuXbZkhwdmVLZS#mambo-credit-sweden + | revealed (seal XSealBlindSealTxPtr, state VoidState) +-- urn:ubideco:semid:FSUnkjzFs2NyU91Q5mfqQ46eLdjmi3m4eh8afpc39ScM#blue-general-sting data AssignVoidStateBlindSealTxid :: confidential (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state VoidState) - | confidentialState (seal XchainBlindSealTxid, state VoidState) + | confidentialState (seal XSealBlindSealTxid, state VoidState) | confidentialSeal (seal BPCore.SecretSeal {- urn:ubideco:semid:81NKrdc9pBoBjsKaGBVN9wXLG4tKjkK4f8DLj7TNMZxh#santana-domingo-needle -}, state VoidState) - | revealed (seal XchainBlindSealTxid, state VoidState) + | revealed (seal XSealBlindSealTxid, state VoidState) -- urn:ubideco:semid:A9sThAqgwKPfuJcR4GDfTQHUAbbS5sbEXG5XVk7FZHEg#hunter-hello-retro data AssignmentType :: U16 -- urn:ubideco:semid:Bu8iXz4MmJnuUMW6AFAepa8BAa4DXLVAMdrNrkLh3S7a#granite-history-canvas @@ -134,8 +136,6 @@ data AssignmentsBlindSealTxid :: {AssignmentType -> ^ ..0xff TypedAssignsBlindSe data AttachId :: [Byte ^ 32] -- urn:ubideco:semid:9zzp5XyDaLvZSGhCEWtey1Y7xdD1soEYdGaimjyZexyf#agenda-ivory-blast data BlindingFactor :: [Byte ^ 32] --- urn:ubideco:semid:8Gjujg1Dk1CV23Pn7CWBdn7vq9rGUS7CZXQmh6MbR4B1#light-manual-neuron -data BundleItem :: inputs {U16 ^ ..0xff}, transition Transition? -- urn:ubideco:semid:HyVyGxhRswAZ3BHJqx6PKmcEGCUSHaL1Rc7qxxi811qE#pizza-natural-cyclone data ConcealedAttach :: [Byte ^ 32] -- urn:ubideco:semid:8YQWVpKJBaYsAwrXvuLDNEDEKRZxfoQJpu1G7X2ZN1tL#mirage-invite-newton @@ -220,24 +220,24 @@ data OpId :: [Byte ^ 32] data Opout :: op OpId , ty AssignmentType , no U16 --- urn:ubideco:semid:CiYicmKTiHc9cFEQvpEY3zWDi13ydMznRmsEdfPa1e8x#potato-gate-liter +-- urn:ubideco:semid:GNcKtjWdcEiXtDX9RQuR8zwNB845PScyvgjzuCvy5Dsu#algebra-pardon-othello data OutputAssignmentRevealedAttach :: opout Opout - , seal XchainExplicitSeal + , seal XSealExplicitSeal , state RevealedAttach , witness WitnessId? --- urn:ubideco:semid:5ArcjNeF6iWBssN3oayXy9d8Z5iWH9juBhCAe28QMi29#safari-amazon-radar +-- urn:ubideco:semid:3PB4sX4TDJMyLTk5yzm7DQbWhpcMzoHDriV3dYZd9VcL#alibi-credit-spain data OutputAssignmentRevealedData :: opout Opout - , seal XchainExplicitSeal + , seal XSealExplicitSeal , state RevealedData , witness WitnessId? --- urn:ubideco:semid:51tRLWfJzEeYeDCVn8DdrgERPPNSaew2fKxb3zeBsZat#guide-organic-human +-- urn:ubideco:semid:J1nZUtrWPX18FyXxDcRvyZK8tv2BKLJN6G8riUETUKNm#mars-scholar-potato data OutputAssignmentRevealedValue :: opout Opout - , seal XchainExplicitSeal + , seal XSealExplicitSeal , state RevealedFungible , witness WitnessId? --- urn:ubideco:semid:6dhzNQ2P4zizD6DK9CMbR5Tejj1hYtxj85wPPPueCdFQ#door-donald-welcome +-- urn:ubideco:semid:9tNXvuRwNLhtMduizsdKtiUhLSewRmwRL7knBegcQ7o4#sunday-random-vincent data OutputAssignmentVoidState :: opout Opout - , seal XchainExplicitSeal + , seal XSealExplicitSeal , state VoidState , witness WitnessId? -- urn:ubideco:semid:5twbh2U5hyaowidwum1iRNCqebBLxTuZTuNPt3SaRT13#nepal-delta-earth @@ -298,8 +298,8 @@ data Transition :: ffv Ffv , inputs Inputs , assignments AssignmentsBlindSealTxPtr , valencies Valencies --- urn:ubideco:semid:6CpmR5xzubTM5JnF7jQxVd4e17s4iJKMXeshqeb2bHGj#malta-heavy-harris -data TransitionBundle :: {OpId -> ^ ..0xff BundleItem} +-- urn:ubideco:semid:LnohmzqG7bkCL7hAjWd2tUrvEP1Pe5C6oiv5r5KjF2G#boxer-nadia-alias +data TransitionBundle :: inputMap {Bitcoin.Vout -> ^ 1.. OpId}, knownTransitions {OpId -> ^ 1.. Transition} -- urn:ubideco:semid:4d7FmcvNey5X175gyggcn4wftcf7mtGyn6N4r6Ek4roX#fiction-caramel-fractal data TransitionSchema :: metadata StrictTypes.SemId {- urn:ubideco:semid:8Ckj2p3GLKina636pSKJkj7GB6ft8XeoP4jfGkRUNwtp#cargo-plasma-catalog -} , globals {GlobalStateType -> ^ ..0xff Occurrences} @@ -334,13 +334,16 @@ data WitnessOrd :: onChain WitnessPos | offChain () -- urn:ubideco:semid:zaGYZruf2pxiZqk1bjmoivxK8DkFycoyjQB52uHYhGL#robert-average-artist data WitnessPos :: height U32, timestamp I64 --- urn:ubideco:semid:3UQZiL3kChuaakzDdyt8Y2sFAVcxFVaJyTtjNC49D4Df#diamond-order-martin -data XchainBlindSealTxPtr :: bitcoin BPCore.BlindSealTxPtr {- urn:ubideco:semid:9XdJg1BFMpMXPfaiw4Te79W2qYgArsEye6XPJUtj31L8#metro-chris-olympic -} +-- urn:ubideco:semid:6WtKj1L8L7zpomz5ExJnV6wBrsbtLnshuJLrkCids7ky#camera-kilo-morning +data XAnchor :: bitcoin AnchorSet + | liquid AnchorSet +-- urn:ubideco:semid:3WvEzjPwR7ka5PD6vCQR5ZJ9hc3K763GsaXJUD9TLFjf#pandora-finance-tomato +data XSealBlindSealTxPtr :: bitcoin BPCore.BlindSealTxPtr {- urn:ubideco:semid:9XdJg1BFMpMXPfaiw4Te79W2qYgArsEye6XPJUtj31L8#metro-chris-olympic -} | liquid BPCore.BlindSealTxPtr {- urn:ubideco:semid:9XdJg1BFMpMXPfaiw4Te79W2qYgArsEye6XPJUtj31L8#metro-chris-olympic -} --- urn:ubideco:semid:DywBwpGAdBpDeSvLqEP2Pvt5mb84r5baE1xDKkcEujBL#portal-kimono-jungle -data XchainBlindSealTxid :: bitcoin BPCore.BlindSealTxid {- urn:ubideco:semid:q529pAPHhD1aFgueAHy8QtfjUayszR85WgEg7s2a3KE#raymond-reply-phrase -} +-- urn:ubideco:semid:6qS3mM1bNePt2RZ8nR2Fzhj76sv3prarRThHcZDHD67B#nebula-civil-filter +data XSealBlindSealTxid :: bitcoin BPCore.BlindSealTxid {- urn:ubideco:semid:q529pAPHhD1aFgueAHy8QtfjUayszR85WgEg7s2a3KE#raymond-reply-phrase -} | liquid BPCore.BlindSealTxid {- urn:ubideco:semid:q529pAPHhD1aFgueAHy8QtfjUayszR85WgEg7s2a3KE#raymond-reply-phrase -} --- urn:ubideco:semid:WTenE41RzjcDoZ6DUhdrVV9yYoiVRrG72prS64ePDqP#prefix-halt-service -data XchainExplicitSeal :: bitcoin BPCore.ExplicitSeal {- urn:ubideco:semid:6xYzjyQ958BPRsJgBXL4N2DhswvgHWLp769Uzo4ybbUV#turtle-phoenix-panda -} +-- urn:ubideco:semid:BpdLEoqRHDNSVP2Xe3PkYQfQoJAKfua19i2YUg9RYxdK#photo-english-format +data XSealExplicitSeal :: bitcoin BPCore.ExplicitSeal {- urn:ubideco:semid:6xYzjyQ958BPRsJgBXL4N2DhswvgHWLp769Uzo4ybbUV#turtle-phoenix-panda -} | liquid BPCore.ExplicitSeal {- urn:ubideco:semid:6xYzjyQ958BPRsJgBXL4N2DhswvgHWLp769Uzo4ybbUV#turtle-phoenix-panda -}