From f0a85cd748e4f4432e6d794a632170e026091630 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Tue, 30 Jul 2024 19:29:23 +0200 Subject: [PATCH 01/12] chore: update to refactored witness-related types in RGB Core --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/containers/consignment.rs | 8 +++- src/containers/indexed.rs | 21 ++-------- src/contract/assignments.rs | 16 ++++--- src/contract/mod.rs | 7 ++-- src/interface/contract.rs | 5 +-- src/interface/resolver.rs | 10 ++--- src/persistence/memory.rs | 78 +++++++++++++++++------------------ src/persistence/state.rs | 49 +++++++++++----------- src/persistence/stock.rs | 27 ++++++------ src/resolvers.rs | 30 ++++---------- 12 files changed, 111 insertions(+), 144 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0532db74..fa915579 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -661,7 +661,7 @@ dependencies = [ [[package]] name = "rgb-core" version = "0.11.0-beta.6" -source = "git+https://github.com/RGB-WG/rgb-core?branch=mining#5a824a3c973c28285d116049a43c15bb26d9c7e6" +source = "git+https://github.com/RGB-WG/rgb-core?branch=movearound#29f0625833528bce5f9bef5e6f3e4b6be0dd3e5e" dependencies = [ "aluvm", "amplify", diff --git a/Cargo.toml b/Cargo.toml index 4942a4ec..e5b16a9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -99,4 +99,4 @@ features = ["all"] [patch.crates-io] bp-consensus = { git = "https://github.com/BP-WG/bp-core", branch = "master" } bp-invoice = { git = "https://github.com/BP-WG/bp-std.git", branch = "master" } -rgb-core = { git = "https://github.com/RGB-WG/rgb-core", branch = "mining" } +rgb-core = { git = "https://github.com/RGB-WG/rgb-core", branch = "movearound" } diff --git a/src/containers/consignment.rs b/src/containers/consignment.rs index f8edc1f3..60b29204 100644 --- a/src/containers/consignment.rs +++ b/src/containers/consignment.rs @@ -48,6 +48,7 @@ use super::{ ASCII_ARMOR_SCHEMA, ASCII_ARMOR_TERMINAL, ASCII_ARMOR_VERSION, }; use crate::interface::{Iface, IfaceImpl}; +use crate::persistence::MemContract; use crate::resolvers::ConsignmentResolver; use crate::{BundleExt, SecretSeal, LIB_NAME_RGB_STD}; @@ -348,7 +349,12 @@ impl Consignment { consignment: &index, fallback: resolver, }; - let mut status = Validator::validate(&index, &resolver, testnet); + let mut status = Validator::::validate( + &index, + &resolver, + testnet, + (&self.schema, self.contract_id()), + ); let validity = status.validity(); diff --git a/src/containers/indexed.rs b/src/containers/indexed.rs index d0c7d472..57ffe564 100644 --- a/src/containers/indexed.rs +++ b/src/containers/indexed.rs @@ -22,18 +22,15 @@ use std::collections::{BTreeMap, BTreeSet}; use std::ops::Deref; -use amplify::confinement::Collection; -use commit_verify::Conceal; use rgb::validation::{ConsignmentApi, EAnchor, Scripts}; +use rgb::vm::OpRef; use rgb::{ - BundleId, Extension, Genesis, OpId, OpRef, Operation, Schema, Transition, TransitionBundle, - XChain, XWitnessId, + BundleId, Extension, Genesis, OpId, Operation, Schema, Transition, TransitionBundle, XWitnessId, }; use strict_types::TypeSystem; use super::{Consignment, XPubWitness}; use crate::containers::anchors::ToWitnessId; -use crate::SecretSeal; // TODO: Transform consignment into this type instead of composing over it #[derive(Clone, Debug)] @@ -122,22 +119,12 @@ impl<'c, const TRANSFER: bool> ConsignmentApi for IndexedConsignment<'c, TRANSFE return Some(OpRef::Genesis(&self.genesis)); } self.transition(opid) - .map(OpRef::from) - .or_else(|| self.extension(opid).map(OpRef::from)) + .map(OpRef::Transition) + .or_else(|| self.extension(opid).map(OpRef::Extension)) } fn genesis(&self) -> &Genesis { &self.genesis } - fn terminals<'iter>(&self) -> impl Iterator)> + 'iter { - let mut set = BTreeSet::new(); - for (bundle_id, terminal) in &self.terminals { - for seal in &terminal.seals { - set.push((*bundle_id, seal.conceal())); - } - } - set.into_iter() - } - fn bundle_ids<'iter>(&self) -> impl Iterator + 'iter { self.bundle_idx .keys() diff --git a/src/contract/assignments.rs b/src/contract/assignments.rs index fe6b977c..871e616a 100644 --- a/src/contract/assignments.rs +++ b/src/contract/assignments.rs @@ -26,11 +26,11 @@ use std::fmt::Debug; use amplify::confinement::SmallVec; use commit_verify::Conceal; use invoice::Amount; -use rgb::vm::AssignmentWitness; +use rgb::vm::TxOrd; use rgb::{ Assign, AssignAttach, AssignData, AssignFungible, AssignRights, AssignmentType, AttachState, DataState, ExposedSeal, ExposedState, OpId, Opout, RevealedAttach, RevealedData, RevealedValue, - TypedAssigns, VoidState, WitnessOrd, XChain, XOutputSeal, XWitnessId, + TypedAssigns, VoidState, XChain, XOutputSeal, XWitnessId, }; use strict_encoding::{StrictDecode, StrictDumb, StrictEncode}; @@ -62,7 +62,7 @@ pub struct OutputAssignment { pub opout: Opout, pub seal: XOutputSeal, pub state: State, - pub witness: AssignmentWitness, + pub witness: Option, } impl PartialEq for OutputAssignment { @@ -140,7 +140,7 @@ impl OutputAssignment { information since it comes from genesis or extension", ), state, - witness: AssignmentWitness::Absent, + witness: None, } } @@ -153,12 +153,10 @@ impl OutputAssignment { } } - pub fn check_witness(&self, filter: &HashMap) -> bool { + pub fn check_witness(&self, filter: &HashMap) -> bool { match self.witness { - AssignmentWitness::Absent => true, - AssignmentWitness::Present(witness_id) => { - !matches!(filter.get(&witness_id), None | Some(WitnessOrd::Archived)) - } + None => true, + Some(witness_id) => !matches!(filter.get(&witness_id), None | Some(TxOrd::Archived)), } } } diff --git a/src/contract/mod.rs b/src/contract/mod.rs index a3ae1fba..9e3ed948 100644 --- a/src/contract/mod.rs +++ b/src/contract/mod.rs @@ -26,8 +26,7 @@ mod merge_reveal; pub use assignments::{KnownState, OutputAssignment, TypedAssignsExt}; pub use bundle::{BundleExt, RevealError}; pub use merge_reveal::{MergeReveal, MergeRevealError}; -use rgb::vm::AssignmentWitness; -use rgb::OpId; +use rgb::{OpId, XWitnessId}; use crate::LIB_NAME_RGB_STD; @@ -40,9 +39,9 @@ use crate::LIB_NAME_RGB_STD; pub struct OpEl { pub op: OpId, pub no: u16, - pub witness: AssignmentWitness, + pub witness: Option, } impl OpEl { - pub fn new(op: OpId, no: u16, witness: AssignmentWitness) -> OpEl { OpEl { op, no, witness } } + pub fn new(op: OpId, no: u16, witness: Option) -> OpEl { OpEl { op, no, witness } } } diff --git a/src/interface/contract.rs b/src/interface/contract.rs index 5cb55d27..62b014b1 100644 --- a/src/interface/contract.rs +++ b/src/interface/contract.rs @@ -25,7 +25,6 @@ use std::collections::HashMap; use amplify::confinement::SmallOrdSet; use invoice::{Allocation, Amount}; -use rgb::vm::AssignmentWitness; use rgb::{ AttachState, ContractId, DataState, OpId, RevealedAttach, RevealedData, RevealedValue, Schema, VoidState, XOutpoint, XOutputSeal, XWitnessId, @@ -396,7 +395,7 @@ impl ContractIface { let spent = f::<_, C::State>(state).map(OutputAssignment::from); let mut ops = HashMap::>::new(); for alloc in spent { - let AssignmentWitness::Present(witness_id) = alloc.witness else { + let Some(witness_id) = alloc.witness else { continue; }; if let Some(op) = ops.get_mut(&witness_id) { @@ -407,7 +406,7 @@ impl ContractIface { } for alloc in allocations { - let AssignmentWitness::Present(witness_id) = alloc.witness else { + let Some(witness_id) = alloc.witness else { continue; }; if let Some(op) = ops.get_mut(&witness_id) { diff --git a/src/interface/resolver.rs b/src/interface/resolver.rs index ffb415e3..86ebf5ba 100644 --- a/src/interface/resolver.rs +++ b/src/interface/resolver.rs @@ -20,11 +20,9 @@ // limitations under the License. use rgb::validation::{ResolveWitness, WitnessResolverError}; -use rgb::vm::WitnessAnchor; -use rgb::XWitnessTx; +use rgb::vm::{TxOrd, XWitnessTx}; use strict_encoding::StrictDumb; -use crate::resolvers::ResolveWitnessAnchor; use crate::XWitnessId; pub(crate) struct DumbResolver; @@ -33,10 +31,8 @@ impl ResolveWitness for DumbResolver { fn resolve_pub_witness(&self, _: XWitnessId) -> Result { Ok(XWitnessTx::strict_dumb()) } -} -impl ResolveWitnessAnchor for DumbResolver { - fn resolve_witness_anchor(&mut self, _: XWitnessId) -> Result { - Ok(WitnessAnchor::strict_dumb()) + fn resolve_pub_witness_ord(&self, _: XWitnessId) -> Result { + Ok(TxOrd::strict_dumb()) } } diff --git a/src/persistence/memory.rs b/src/persistence/memory.rs index 9550d108..29d60d6f 100644 --- a/src/persistence/memory.rs +++ b/src/persistence/memory.rs @@ -36,16 +36,17 @@ use amplify::confinement::{ use amplify::num::u24; use bp::dbc::tapret::TapretCommitment; use commit_verify::{CommitId, Conceal}; +use rgb::validation::ResolveWitness; use rgb::vm::{ - AssignmentWitness, ContractState, GlobalContractState, GlobalOrd, GlobalStateIter, - UnknownGlobalStateType, WitnessAnchor, + ContractStateAccess, GlobalContractState, GlobalOrd, GlobalStateIter, TxOrd, + UnknownGlobalStateType, WitnessOrd, }; use rgb::{ Assign, AssignmentType, Assignments, AssignmentsRef, AttachId, AttachState, BundleId, ContractId, DataState, ExposedSeal, ExposedState, Extension, FungibleState, Genesis, GenesisSeal, GlobalStateType, GraphSeal, Identity, OpId, Operation, Opout, RevealedAttach, RevealedData, RevealedValue, Schema, SchemaId, SecretSeal, Transition, TransitionBundle, - TypedAssigns, VoidState, WitnessOrd, XChain, XOutpoint, XOutputSeal, XWitnessId, + TypedAssigns, VoidState, XChain, XOutpoint, XOutputSeal, XWitnessId, }; use strict_encoding::{SerializeError, StrictDeserialize, StrictSerialize}; use strict_types::TypeSystem; @@ -64,7 +65,6 @@ use crate::contract::{KnownState, OutputAssignment}; use crate::interface::{Iface, IfaceClass, IfaceId, IfaceImpl, IfaceRef}; #[cfg(feature = "fs")] use crate::persistence::fs::FsStored; -use crate::resolvers::ResolveWitnessAnchor; use crate::{OpEl, LIB_NAME_RGB_STORAGE}; ////////// @@ -444,7 +444,7 @@ pub struct MemState { #[strict_type(skip)] filename: PathBuf, - witnesses: LargeOrdMap, + witnesses: LargeOrdMap, contracts: TinyOrdMap, } @@ -488,7 +488,7 @@ impl StateReadProvider for MemState { .witnesses .iter() .filter(|(id, _)| { - let id = **id; + let id = Some(**id); unfiltered .global .values() @@ -529,10 +529,10 @@ impl StateWriteProvider for MemState { self.contracts.get_mut(&contract_id).expect("just inserted") }; let mut writer = MemContractWriter { - writer: Box::new(|wa: WitnessAnchor| -> Result<(), SerializeError> { + writer: Box::new(|ord: WitnessOrd| -> Result<(), SerializeError> { // NB: We do not check the existence of the witness since we have a newer // version anyway and even if it is known we have to replace it - self.witnesses.insert(wa.witness_id, wa.witness_ord)?; + self.witnesses.insert(ord.witness_id, ord.pub_ord)?; Ok(()) }), contract, @@ -552,11 +552,11 @@ impl StateWriteProvider for MemState { .map(|contract| MemContractWriter { // We can't move this constructor to a dedicated method due to the rust borrower // checker - writer: Box::new(|wa: WitnessAnchor| -> Result<(), SerializeError> { + writer: Box::new(|ord: WitnessOrd| -> Result<(), SerializeError> { // NB: We do not check the existence of the witness since we have a newer // version anyway and even if it is known we have to replace // it - self.witnesses.insert(wa.witness_id, wa.witness_ord)?; + self.witnesses.insert(ord.witness_id, ord.pub_ord)?; Ok(()) }), contract, @@ -565,7 +565,7 @@ impl StateWriteProvider for MemState { fn update_witnesses( &mut self, - mut resolver: impl ResolveWitnessAnchor, + resolver: impl ResolveWitness, after_height: u32, ) -> Result { let after_height = NonZeroU32::new(after_height).unwrap_or(NonZeroU32::MIN); @@ -576,11 +576,11 @@ impl StateWriteProvider for MemState { mem::swap(&mut self.witnesses, &mut witnesses); let mut witnesses = witnesses.unbox(); for (id, ord) in &mut witnesses { - if matches!(ord, WitnessOrd::OnChain(pos) if pos.height() < after_height) { + if matches!(ord, TxOrd::OnChain(pos) if pos.height() < after_height) { continue; } - match resolver.resolve_witness_anchor(*id) { - Ok(new) => *ord = new.witness_ord, + match resolver.resolve_pub_witness_ord(*id) { + Ok(new) => *ord = new, Err(err) => { failed.insert(*id, err.to_string()); } @@ -618,7 +618,10 @@ impl MemGlobalState { /// the state data, but it doesn't interpret or validates the state against the /// schema. /// -/// To access the valid contract state use [`Contract`] APIs. +/// NB: MemContract provides an in-memory contract state used during contract +/// validation. It does not support filtering by witness transaction validity +/// and thus must not be used in any other cases in its explicit form. Pls see +/// [`MemContractFiltered`] instead. #[derive(Getters, Clone, Eq, PartialEq, Debug)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB_STORAGE)] @@ -659,7 +662,8 @@ impl MemContract { } } - fn add_operation(&mut self, op: &impl Operation, witness_id: AssignmentWitness) { + // TODO: Use `OpRef` type instead of `op` and `witness_id` + fn add_operation(&mut self, op: &impl Operation, witness_id: Option) { let opid = op.id(); for (ty, state) in op.globals() { @@ -720,7 +724,7 @@ impl MemContract { fn add_assignments( &mut self, - witness_id: AssignmentWitness, + witness_id: Option, opid: OpId, assignments: &Assignments, ) { @@ -729,7 +733,7 @@ impl MemContract { assignments: &[Assign], opid: OpId, ty: AssignmentType, - witness_id: AssignmentWitness, + witness_id: Option, ) { for (no, seal, state) in assignments .iter() @@ -737,12 +741,10 @@ impl MemContract { .filter_map(|(n, a)| a.to_revealed().map(|(seal, state)| (n, seal, state))) { let assigned_state = match witness_id { - AssignmentWitness::Present(witness_id) => { + Some(witness_id) => { OutputAssignment::with_witness(seal, witness_id, state, opid, ty, no as u16) } - AssignmentWitness::Absent => { - OutputAssignment::with_no_witness(seal, state, opid, ty, no as u16) - } + None => OutputAssignment::with_no_witness(seal, state, opid, ty, no as u16), }; contract_state .push(assigned_state) @@ -770,11 +772,11 @@ impl MemContract { } pub struct MemContractFiltered<'mem> { - filter: HashMap, + filter: HashMap, unfiltered: &'mem MemContract, } -impl<'mem> ContractState for MemContractFiltered<'mem> { +impl<'mem> ContractStateAccess for MemContractFiltered<'mem> { fn global( &self, ty: GlobalStateType, @@ -840,11 +842,11 @@ impl<'mem> ContractState for MemContractFiltered<'mem> { src.iter() .rev() .filter_map(|(el, data)| { - let ord = match el.witness.witness_id() { - None => GlobalOrd::genesis(el.no), + let ord = match el.witness { + None => GlobalOrd::genesis(el.op, el.no), Some(id) => { let ord = self.filter.get(&id)?; - GlobalOrd::with_witness(id, *ord, el.no) + GlobalOrd::with_witness(el.op, id, *ord, el.no) } }; Some((ord, data)) @@ -960,7 +962,7 @@ impl<'mem> ContractStateRead for MemContractFiltered<'mem> { } pub struct MemContractWriter<'mem> { - writer: Box Result<(), SerializeError> + 'mem>, + writer: Box Result<(), SerializeError> + 'mem>, contract: &'mem mut MemContract, } @@ -972,8 +974,7 @@ impl<'mem> ContractStateWrite for MemContractWriter<'mem> { /// If genesis violates RGB consensus rules and wasn't checked against the /// schema before adding to the history. fn add_genesis(&mut self, genesis: &Genesis) -> Result<(), Self::Error> { - self.contract - .add_operation(genesis, AssignmentWitness::Absent); + self.contract.add_operation(genesis, None); Ok(()) } @@ -984,11 +985,11 @@ impl<'mem> ContractStateWrite for MemContractWriter<'mem> { fn add_transition( &mut self, transition: &Transition, - witness_anchor: WitnessAnchor, + ord: WitnessOrd, ) -> Result<(), Self::Error> { - (self.writer)(witness_anchor)?; + (self.writer)(ord)?; self.contract - .add_operation(transition, AssignmentWitness::Present(witness_anchor.witness_id)); + .add_operation(transition, Some(ord.witness_id)); Ok(()) } @@ -996,14 +997,9 @@ impl<'mem> ContractStateWrite for MemContractWriter<'mem> { /// /// If state extension violates RGB consensus rules and wasn't checked /// against the schema before adding to the history. - fn add_extension( - &mut self, - extension: &Extension, - witness_anchor: WitnessAnchor, - ) -> Result<(), Self::Error> { - (self.writer)(witness_anchor)?; - self.contract - .add_operation(extension, AssignmentWitness::Present(witness_anchor.witness_id)); + fn add_extension(&mut self, extension: &Extension, ord: WitnessOrd) -> Result<(), Self::Error> { + (self.writer)(ord)?; + self.contract.add_operation(extension, Some(ord.witness_id)); Ok(()) } } diff --git a/src/persistence/state.rs b/src/persistence/state.rs index 1501a84f..00fee1ca 100644 --- a/src/persistence/state.rs +++ b/src/persistence/state.rs @@ -25,7 +25,8 @@ use std::fmt::Debug; use std::iter; use invoice::Amount; -use rgb::vm::{ContractState, WitnessAnchor}; +use rgb::validation::{ResolveWitness, WitnessResolverError}; +use rgb::vm::{ContractStateAccess, WitnessOrd}; use rgb::{ AssetTag, AttachState, BlindingFactor, ContractId, DataState, Extension, Genesis, Operation, RevealedAttach, RevealedData, RevealedValue, Schema, SchemaId, Transition, TransitionBundle, @@ -35,7 +36,6 @@ use rgb::{ use crate::containers::{ConsignmentExt, ToWitnessId}; use crate::contract::OutputAssignment; use crate::persistence::{StoreTransaction, UpdateRes}; -use crate::resolvers::ResolveWitnessAnchor; #[derive(Clone, PartialEq, Eq, Debug, Display, Error, From)] #[display(inner)] @@ -48,7 +48,7 @@ pub enum StateError { /// witness {0} can't be resolved: {1} #[display(doc_comments)] - Resolver(XWitnessId, String), + Resolver(XWitnessId, WitnessResolverError), /// {0} /// @@ -121,12 +121,12 @@ impl State

{ .map_err(StateError::ReadProvider) } - pub fn update_from_bundle( + pub fn update_from_bundle( &mut self, contract_id: ContractId, bundle: &TransitionBundle, witness_id: XWitnessId, - mut resolver: R, + resolver: R, ) -> Result<(), StateError

> { let mut updater = self .as_provider_mut() @@ -134,20 +134,20 @@ impl State

{ .map_err(StateError::WriteProvider)? .ok_or(StateInconsistency::UnknownContract(contract_id))?; for transition in bundle.known_transitions.values() { - let witness_anchor = resolver - .resolve_witness_anchor(witness_id) + let ord = resolver + .resolve_pub_witness_ord(witness_id) .map_err(|e| StateError::Resolver(witness_id, e))?; updater - .add_transition(transition, witness_anchor) + .add_transition(transition, WitnessOrd::with(witness_id, ord)) .map_err(StateError::WriteProvider)?; } Ok(()) } - pub fn update_from_consignment( + pub fn update_from_consignment( &mut self, consignment: impl ConsignmentExt, - mut resolver: R, + resolver: R, ) -> Result<(), StateError

> { let mut state = self .as_provider_mut() @@ -163,12 +163,13 @@ impl State

{ for bundle in bundled_witness.anchored_bundles.bundles() { for transition in bundle.known_transitions.values() { let witness_id = bundled_witness.pub_witness.to_witness_id(); - let witness_anchor = resolver - .resolve_witness_anchor(witness_id) + let ord = resolver + .resolve_pub_witness_ord(witness_id) .map_err(|e| StateError::Resolver(witness_id, e))?; + let witness_ord = WitnessOrd::with(witness_id, ord); state - .add_transition(transition, witness_anchor) + .add_transition(transition, witness_ord) .map_err(StateError::WriteProvider)?; for (id, used) in &mut extension_idx { if *used { @@ -177,12 +178,12 @@ impl State

{ for input in &transition.inputs { if input.prev_out.op == *id { *used = true; - if let Some(ord) = ordered_extensions.get_mut(id) { - if *ord > witness_anchor { - *ord = witness_anchor; + if let Some(witness_ord2) = ordered_extensions.get_mut(id) { + if *witness_ord2 > witness_ord { + *witness_ord2 = witness_ord; } } else { - ordered_extensions.insert(*id, witness_anchor); + ordered_extensions.insert(*id, witness_ord); } } } @@ -191,9 +192,9 @@ impl State

{ } } for extension in consignment.extensions() { - if let Some(witness_anchor) = ordered_extensions.get(&extension.id()) { + if let Some(witness_ord) = ordered_extensions.get(&extension.id()) { state - .add_extension(extension, *witness_anchor) + .add_extension(extension, *witness_ord) .map_err(StateError::WriteProvider)?; } } @@ -203,7 +204,7 @@ impl State

{ pub fn update_witnesses( &mut self, - resolver: impl ResolveWitnessAnchor, + resolver: impl ResolveWitness, after_height: u32, ) -> Result> { self.provider @@ -261,12 +262,12 @@ pub trait StateWriteProvider: StoreTransaction { fn update_witnesses( &mut self, - resolver: impl ResolveWitnessAnchor, + resolver: impl ResolveWitness, after_height: u32, ) -> Result; } -pub trait ContractStateRead: ContractState { +pub trait ContractStateRead: ContractStateAccess { fn contract_id(&self) -> ContractId; fn schema_id(&self) -> SchemaId; fn rights_all(&self) -> impl Iterator>; @@ -283,12 +284,12 @@ pub trait ContractStateWrite { fn add_transition( &mut self, transition: &Transition, - witness_anchor: WitnessAnchor, + witness_ord: WitnessOrd, ) -> Result<(), Self::Error>; fn add_extension( &mut self, extension: &Extension, - witness_anchor: WitnessAnchor, + witness_ord: WitnessOrd, ) -> Result<(), Self::Error>; } diff --git a/src/persistence/stock.rs b/src/persistence/stock.rs index 701224a9..11b282ad 100644 --- a/src/persistence/stock.rs +++ b/src/persistence/stock.rs @@ -33,7 +33,7 @@ use bp::Vout; use chrono::Utc; use commit_verify::Conceal; use invoice::{Amount, Beneficiary, InvoiceState, NonFungible, RgbInvoice}; -use rgb::validation::{DbcProof, EAnchor}; +use rgb::validation::{DbcProof, EAnchor, ResolveWitness, WitnessResolverError}; use rgb::{ validation, AssignmentType, BlindingFactor, BundleId, ContractId, GraphSeal, Identity, OpId, Operation, Opout, SchemaId, SecretSeal, Transition, XChain, XOutpoint, XOutputSeal, XWitnessId, @@ -58,7 +58,6 @@ use crate::interface::{ BuilderError, ContractBuilder, ContractIface, Iface, IfaceClass, IfaceId, IfaceRef, TransitionBuilder, }; -use crate::resolvers::ResolveWitnessAnchor; use crate::{MergeRevealError, RevealError}; pub type ContractAssignments = HashMap>; @@ -108,7 +107,7 @@ pub enum StockError< StashData(StashDataError), /// witness {0} can't be resolved: {1} - WitnessUnresolved(XWitnessId, String), + WitnessUnresolved(XWitnessId, WitnessResolverError), } impl From> @@ -1170,32 +1169,32 @@ impl Stock { Ok(status) } - pub fn import_contract( + pub fn import_contract( &mut self, contract: ValidContract, - resolver: &mut R, + resolver: R, ) -> Result> { self.consume_consignment(contract, resolver) } - pub fn accept_transfer( + pub fn accept_transfer( &mut self, contract: ValidTransfer, - resolver: &mut R, + resolver: R, ) -> Result> { self.consume_consignment(contract, resolver) } - fn consume_consignment( + fn consume_consignment( &mut self, consignment: ValidConsignment, - mut resolver: R, + resolver: R, ) -> Result> { let (mut consignment, status) = consignment.split(); consignment = self.stash.resolve_secrets(consignment)?; self.store_transaction(move |stash, state, index| { - state.update_from_consignment(&consignment, &mut resolver)?; + state.update_from_consignment(&consignment, &resolver)?; index.index_consignment(&consignment)?; stash.consume_consignment(consignment)?; Ok(()) @@ -1212,10 +1211,10 @@ impl Stock { /// /// Must be called before the consignment is created, when witness /// transaction is not yet mined. - pub fn consume_fascia( + pub fn consume_fascia( &mut self, fascia: Fascia, - mut resolver: R, + resolver: R, ) -> Result<(), StockError> { self.store_transaction(move |stash, state, index| { let witness_id = fascia.witness_id(); @@ -1234,7 +1233,7 @@ impl Stock { } index.index_bundle(contract_id, &bundle, witness_id)?; - state.update_from_bundle(contract_id, &bundle, witness_id, &mut resolver)?; + state.update_from_bundle(contract_id, &bundle, witness_id, &resolver)?; stash.consume_bundle(bundle)?; } Ok(()) @@ -1296,7 +1295,7 @@ impl Stock { pub fn update_witnesses( &mut self, - resolver: impl ResolveWitnessAnchor, + resolver: impl ResolveWitness, after_height: u32, ) -> Result> { Ok(self.state.update_witnesses(resolver, after_height)?) diff --git a/src/resolvers.rs b/src/resolvers.rs index c28a3728..85492c81 100644 --- a/src/resolvers.rs +++ b/src/resolvers.rs @@ -20,31 +20,10 @@ // limitations under the License. use rgb::validation::{ResolveWitness, WitnessResolverError}; -use rgb::vm::WitnessAnchor; -use rgb::{XWitnessId, XWitnessTx}; +use rgb::vm::{TxOrd, XWitnessId, XWitnessTx}; use crate::containers::IndexedConsignment; -pub trait ResolveWitnessAnchor { - /// Resolves position of the witness anchor in the consensus data: - /// blockchain, state channel etc. Used for ordering of global state and for - /// ensuring that the account only for the actual contract state after - /// blockchain re-orgs and channel updates. - /// - /// Witness resolution must happen as fast and as cheap as getting - /// key-values from HashMap. Thus, resolver must always be caching and - /// doesn't actually re-query indexers for deeply mined transactions. - // TODO: Return WitnessOrd instead of WitnessAnchor - fn resolve_witness_anchor(&mut self, witness_id: XWitnessId) -> Result; -} - -impl ResolveWitnessAnchor for &mut T { - #[inline] - fn resolve_witness_anchor(&mut self, witness_id: XWitnessId) -> Result { - (*self).resolve_witness_anchor(witness_id) - } -} - // TODO: Implement caching witness resolver pub(crate) struct ConsignmentResolver<'cons, R: ResolveWitness, const TRANSFER: bool> { @@ -65,4 +44,11 @@ impl<'cons, R: ResolveWitness, const TRANSFER: bool> ResolveWitness .ok_or(WitnessResolverError::Unknown(witness_id)) .or_else(|_| self.fallback.resolve_pub_witness(witness_id)) } + + fn resolve_pub_witness_ord( + &self, + witness_id: XWitnessId, + ) -> Result { + self.fallback.resolve_pub_witness_ord(witness_id) + } } From 9ad94959ba05bf57f0b5a4c405a5788a4af20d7f Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Wed, 31 Jul 2024 10:27:25 +0200 Subject: [PATCH 02/12] iface: split IfaceClass and IfaceWrapper traits required to get rid of needless generics appearing after the introduction of ContractStateRead --- src/interface/iface.rs | 27 ++++++++++++++------------- src/interface/mod.rs | 3 ++- src/persistence/stock.rs | 26 +++++++++++++------------- 3 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/interface/iface.rs b/src/interface/iface.rs index 8d70e5c3..fc31181a 100644 --- a/src/interface/iface.rs +++ b/src/interface/iface.rs @@ -37,8 +37,8 @@ use strict_encoding::{ }; use strict_types::{SemId, SymbolicSys, TypeLib}; -use crate::interface::{IfaceDisplay, IfaceImpl, VerNo}; -use crate::persistence::SchemaIfaces; +use crate::interface::{ContractIface, IfaceDisplay, IfaceImpl, VerNo}; +use crate::persistence::{ContractStateRead, SchemaIfaces}; use crate::LIB_NAME_RGB_STD; /// Interface identifier. @@ -336,24 +336,25 @@ pub struct TransitionIface { /// /// Interface standards like RGB20, RGB21 and RGB25 are actually interface /// classes. -/// -/// The instances implementing this trait are used as wrappers around -/// [`ContractIface`] object, allowing a simple API matching the interface class -/// requirements. -pub trait IfaceClass { +pub trait IfaceClass: Clone + Default { const IFACE_NAME: &'static str; const IFACE_IDS: &'static [IfaceId]; - /// An object which allows to configure specific interface features to - /// select one interface from the class. - type Features: Sized + Clone + Default; + type Wrapper: IfaceWrapper; + fn stl(&self) -> TypeLib; + fn iface(&self) -> Iface; + fn iface_id(&self) -> IfaceId; +} + +/// The instances implementing this trait are used as wrappers around +/// [`ContractIface`] object, allowing a simple API matching the interface class +/// requirements. +pub trait IfaceWrapper { /// Object which represent concise summary about a contract; type Info: Clone + Eq + Debug; - fn iface(features: Self::Features) -> Iface; - fn iface_id(features: Self::Features) -> IfaceId; - fn stl() -> TypeLib; + fn with(iface: ContractIface) -> Self; /// Constructs information object describing a specific class in terms of /// the interface class. diff --git a/src/interface/mod.rs b/src/interface/mod.rs index 03a613ca..ea5ef2d9 100644 --- a/src/interface/mod.rs +++ b/src/interface/mod.rs @@ -41,7 +41,8 @@ pub use contractum::IfaceDisplay; pub use filter::{FilterExclude, FilterIncludeAll, OutpointFilter}; pub use iface::{ ArgMap, AssignIface, ExtensionIface, GenesisIface, GlobalIface, Iface, IfaceClass, IfaceId, - IfaceInconsistency, IfaceRef, Modifier, OpName, OwnedIface, Req, TransitionIface, ValencyIface, + IfaceInconsistency, IfaceRef, IfaceWrapper, Modifier, OpName, OwnedIface, Req, TransitionIface, + ValencyIface, }; pub use iimpl::{IfaceImpl, ImplId, NamedField, NamedType, NamedVariant, SchemaTypeIndex}; pub use inheritance::{CheckInheritance, ExtensionError, InheritanceFailure}; diff --git a/src/persistence/stock.rs b/src/persistence/stock.rs index 11b282ad..6bdb663b 100644 --- a/src/persistence/stock.rs +++ b/src/persistence/stock.rs @@ -56,7 +56,7 @@ use crate::containers::{ use crate::info::{ContractInfo, IfaceInfo, SchemaInfo}; use crate::interface::{ BuilderError, ContractBuilder, ContractIface, Iface, IfaceClass, IfaceId, IfaceRef, - TransitionBuilder, + IfaceWrapper, TransitionBuilder, }; use crate::{MergeRevealError, RevealError}; @@ -502,12 +502,16 @@ impl Stock { #[allow(clippy::multiple_bound_locations)] pub fn contracts_by<'a, C: IfaceClass + 'a>( &'a self, - ) -> Result + 'a, StockError> - where C: From>> { + ) -> Result< + impl Iterator< + Item = > as IfaceWrapper>>::Info, + > + 'a, + StockError, + > { Ok(self.stash.geneses_by::()?.filter_map(|genesis| { self.contract_iface_class::(genesis.contract_id()) .as_ref() - .map(C::info) + .map(> as IfaceWrapper>>::info) .ok() })) } @@ -547,27 +551,23 @@ impl Stock { } #[allow(clippy::multiple_bound_locations)] - pub fn contract_iface_class<'a, C: IfaceClass>( - &'a self, + pub fn contract_iface_class( + &self, contract_id: ContractId, - ) -> Result> - where - C: From>>, - { + ) -> Result>, StockError> { let (schema_ifaces, state, info) = self.contract_raw(contract_id)?; let iimpl = self.stash.impl_for::(schema_ifaces)?; let iface = self.stash.iface(iimpl.iface_id)?; let (types, _) = self.stash.extract(&schema_ifaces.schema, [iface])?; - Ok(ContractIface { + Ok(C::Wrapper::with(ContractIface { state, schema: schema_ifaces.schema.clone(), iface: iimpl.clone(), types, info, - } - .into()) + })) } /// Returns the best matching abstract interface to a contract. From 3bc0d76465893b579248da2396a1efcf288a1ab3 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Tue, 30 Jul 2024 22:27:45 +0200 Subject: [PATCH 03/12] persistence: use new ContractStateEvolve and validator APIs --- Cargo.lock | 2 +- Cargo.toml | 3 +- src/containers/consignment.rs | 4 +- src/containers/indexed.rs | 3 +- src/persistence/memory.rs | 107 +++++++++-- src/persistence/mod.rs | 3 +- src/stl/stl.rs | 2 +- stl/RGBStorage@0.11.0.sta | 335 +++++++++++++++++----------------- stl/RGBStorage@0.11.0.stl | Bin 13027 -> 12972 bytes stl/RGBStorage@0.11.0.sty | 29 ++- 10 files changed, 280 insertions(+), 208 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fa915579..101ef56d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -661,7 +661,7 @@ dependencies = [ [[package]] name = "rgb-core" version = "0.11.0-beta.6" -source = "git+https://github.com/RGB-WG/rgb-core?branch=movearound#29f0625833528bce5f9bef5e6f3e4b6be0dd3e5e" +source = "git+https://github.com/RGB-WG/rgb-core?branch=contract-state2#2604db8514b9f33f18fd6db78c327055a7c6618d" dependencies = [ "aluvm", "amplify", diff --git a/Cargo.toml b/Cargo.toml index e5b16a9f..7346e8df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -99,4 +99,5 @@ features = ["all"] [patch.crates-io] bp-consensus = { git = "https://github.com/BP-WG/bp-core", branch = "master" } bp-invoice = { git = "https://github.com/BP-WG/bp-std.git", branch = "master" } -rgb-core = { git = "https://github.com/RGB-WG/rgb-core", branch = "movearound" } +rgb-core = { git = "https://github.com/RGB-WG/rgb-core", branch = "contract-state2" } +#rgb-core = { path = "../rgb-core" } diff --git a/src/containers/consignment.rs b/src/containers/consignment.rs index 60b29204..3d149f3c 100644 --- a/src/containers/consignment.rs +++ b/src/containers/consignment.rs @@ -48,7 +48,7 @@ use super::{ ASCII_ARMOR_SCHEMA, ASCII_ARMOR_TERMINAL, ASCII_ARMOR_VERSION, }; use crate::interface::{Iface, IfaceImpl}; -use crate::persistence::MemContract; +use crate::persistence::{MemContract, MemContractState}; use crate::resolvers::ConsignmentResolver; use crate::{BundleExt, SecretSeal, LIB_NAME_RGB_STD}; @@ -349,7 +349,7 @@ impl Consignment { consignment: &index, fallback: resolver, }; - let mut status = Validator::::validate( + let mut status = Validator::, _, _>::validate( &index, &resolver, testnet, diff --git a/src/containers/indexed.rs b/src/containers/indexed.rs index 57ffe564..73129b35 100644 --- a/src/containers/indexed.rs +++ b/src/containers/indexed.rs @@ -22,8 +22,7 @@ use std::collections::{BTreeMap, BTreeSet}; use std::ops::Deref; -use rgb::validation::{ConsignmentApi, EAnchor, Scripts}; -use rgb::vm::OpRef; +use rgb::validation::{ConsignmentApi, EAnchor, OpRef, Scripts}; use rgb::{ BundleId, Extension, Genesis, OpId, Operation, Schema, Transition, TransitionBundle, XWitnessId, }; diff --git a/src/persistence/memory.rs b/src/persistence/memory.rs index 29d60d6f..edea9b64 100644 --- a/src/persistence/memory.rs +++ b/src/persistence/memory.rs @@ -23,6 +23,7 @@ use std::borrow::Borrow; use std::cmp::Ordering; use std::collections::{BTreeMap, BTreeSet, HashMap}; use std::convert::Infallible; +use std::fmt::{Debug, Formatter}; use std::num::NonZeroU32; #[cfg(feature = "fs")] use std::path::PathBuf; @@ -38,8 +39,8 @@ use bp::dbc::tapret::TapretCommitment; use commit_verify::{CommitId, Conceal}; use rgb::validation::ResolveWitness; use rgb::vm::{ - ContractStateAccess, GlobalContractState, GlobalOrd, GlobalStateIter, TxOrd, - UnknownGlobalStateType, WitnessOrd, + AnchoredOpRef, ContractStateAccess, ContractStateEvolve, GlobalContractState, GlobalOrd, + GlobalStateIter, TxOrd, UnknownGlobalStateType, WitnessOrd, }; use rgb::{ Assign, AssignmentType, Assignments, AssignmentsRef, AttachId, AttachState, BundleId, @@ -445,7 +446,7 @@ pub struct MemState { filename: PathBuf, witnesses: LargeOrdMap, - contracts: TinyOrdMap, + contracts: TinyOrdMap, } impl StrictSerialize for MemState {} @@ -473,7 +474,7 @@ impl StoreTransaction for MemState { impl StateProvider for MemState {} impl StateReadProvider for MemState { - type ContractRead<'a> = MemContractFiltered<'a>; + type ContractRead<'a> = MemContract<&'a MemContractState>; type Error = StateInconsistency; fn contract_state( @@ -501,7 +502,7 @@ impl StateReadProvider for MemState { }) .map(|(id, ord)| (*id, *ord)) .collect(); - Ok(MemContractFiltered { filter, unfiltered }) + Ok(MemContract { filter, unfiltered }) } } @@ -525,7 +526,7 @@ impl StateWriteProvider for MemState { } } else { self.contracts - .insert(contract_id, MemContract::new(schema, contract_id))?; + .insert(contract_id, MemContractState::new(schema, contract_id))?; self.contracts.get_mut(&contract_id).expect("just inserted") }; let mut writer = MemContractWriter { @@ -621,7 +622,7 @@ impl MemGlobalState { /// NB: MemContract provides an in-memory contract state used during contract /// validation. It does not support filtering by witness transaction validity /// and thus must not be used in any other cases in its explicit form. Pls see -/// [`MemContractFiltered`] instead. +/// [`MemContract`] instead. #[derive(Getters, Clone, Eq, PartialEq, Debug)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB_STORAGE)] @@ -630,7 +631,7 @@ impl MemGlobalState { derive(Serialize, Deserialize), serde(crate = "serde_crate", rename_all = "camelCase") )] -pub struct MemContract { +pub struct MemContractState { #[getter(as_copy)] schema_id: SchemaId, #[getter(as_copy)] @@ -643,7 +644,7 @@ pub struct MemContract { attach: LargeOrdSet>, } -impl MemContract { +impl MemContractState { pub fn new(schema: &Schema, contract_id: ContractId) -> Self { let global = TinyOrdMap::from_iter_unsafe( schema @@ -651,7 +652,7 @@ impl MemContract { .iter() .map(|(ty, glob)| (*ty, MemGlobalState::new(glob.max_items))), ); - MemContract { + MemContractState { schema_id: schema.schema_id(), contract_id, global, @@ -771,12 +772,18 @@ impl MemContract { } } -pub struct MemContractFiltered<'mem> { +pub struct MemContract> { filter: HashMap, - unfiltered: &'mem MemContract, + unfiltered: M, } -impl<'mem> ContractStateAccess for MemContractFiltered<'mem> { +impl> Debug for MemContract { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str("MemContractFiltered { .. }") + } +} + +impl> ContractStateAccess for MemContract { fn global( &self, ty: GlobalStateType, @@ -833,6 +840,7 @@ impl<'mem> ContractStateAccess for MemContractFiltered<'mem> { let state = self .unfiltered + .borrow() .global .get(&ty) .ok_or(UnknownGlobalStateType(ty))?; @@ -866,6 +874,7 @@ impl<'mem> ContractStateAccess for MemContractFiltered<'mem> { fn rights(&self, outpoint: XOutpoint, ty: AssignmentType) -> u32 { self.unfiltered + .borrow() .rights .iter() .filter(|assignment| { @@ -881,6 +890,7 @@ impl<'mem> ContractStateAccess for MemContractFiltered<'mem> { ty: AssignmentType, ) -> impl DoubleEndedIterator { self.unfiltered + .borrow() .fungibles .iter() .filter(move |assignment| { @@ -896,6 +906,7 @@ impl<'mem> ContractStateAccess for MemContractFiltered<'mem> { ty: AssignmentType, ) -> impl DoubleEndedIterator> { self.unfiltered + .borrow() .data .iter() .filter(move |assignment| { @@ -911,6 +922,7 @@ impl<'mem> ContractStateAccess for MemContractFiltered<'mem> { ty: AssignmentType, ) -> impl DoubleEndedIterator> { self.unfiltered + .borrow() .attach .iter() .filter(move |assignment| { @@ -921,16 +933,74 @@ impl<'mem> ContractStateAccess for MemContractFiltered<'mem> { } } -impl<'mem> ContractStateRead for MemContractFiltered<'mem> { +impl ContractStateEvolve for MemContract { + type Context<'ctx> = (&'ctx Schema, ContractId); + + fn init(context: Self::Context<'_>) -> Self { + Self { + filter: empty!(), + unfiltered: MemContractState::new(context.0, context.1), + } + } + + fn evolve_state(&mut self, op: AnchoredOpRef) -> Result<(), confinement::Error> { + fn ordering(filter: &HashMap, witness_id: XWitnessId) -> WitnessOrd { + let ord = filter.get(&witness_id).expect("unknown witness id"); + WitnessOrd { + pub_ord: *ord, + witness_id, + } + } + (move || -> Result<(), SerializeError> { + fn writer(me: &mut MemContract) -> MemContractWriter { + MemContractWriter { + writer: Box::new(|ord: WitnessOrd| -> Result<(), SerializeError> { + // NB: We do not check the existence of the witness since we have a newer + // version anyway and even if it is known we have to replace it + me.filter.insert(ord.witness_id, ord.pub_ord); + Ok(()) + }), + contract: &mut me.unfiltered, + } + } + match op { + AnchoredOpRef::Genesis(genesis) => { + let mut writer = writer(self); + writer.add_genesis(genesis) + } + AnchoredOpRef::Transition(transition, witness_id) => { + let ord = ordering(&self.filter, witness_id); + let mut writer = writer(self); + writer.add_transition(transition, ord) + } + AnchoredOpRef::Extension(extension, witness_id) => { + let ord = ordering(&self.filter, witness_id); + let mut writer = writer(self); + writer.add_extension(extension, ord) + } + } + })() + .map_err(|err| match err { + SerializeError::Io(_) => { + unreachable!("I/O errors are not possible for memory structures") + } + SerializeError::Confinement(e) => e, + })?; + Ok(()) + } +} + +impl> ContractStateRead for MemContract { #[inline] - fn contract_id(&self) -> ContractId { self.unfiltered.contract_id } + fn contract_id(&self) -> ContractId { self.unfiltered.borrow().contract_id } #[inline] - fn schema_id(&self) -> SchemaId { self.unfiltered.schema_id } + fn schema_id(&self) -> SchemaId { self.unfiltered.borrow().schema_id } #[inline] fn rights_all(&self) -> impl Iterator> { self.unfiltered + .borrow() .rights .iter() .filter(|assignment| assignment.check_witness(&self.filter)) @@ -939,6 +1009,7 @@ impl<'mem> ContractStateRead for MemContractFiltered<'mem> { #[inline] fn fungible_all(&self) -> impl Iterator> { self.unfiltered + .borrow() .fungibles .iter() .filter(|assignment| assignment.check_witness(&self.filter)) @@ -947,6 +1018,7 @@ impl<'mem> ContractStateRead for MemContractFiltered<'mem> { #[inline] fn data_all(&self) -> impl Iterator> { self.unfiltered + .borrow() .data .iter() .filter(|assignment| assignment.check_witness(&self.filter)) @@ -955,6 +1027,7 @@ impl<'mem> ContractStateRead for MemContractFiltered<'mem> { #[inline] fn attach_all(&self) -> impl Iterator> { self.unfiltered + .borrow() .attach .iter() .filter(|assignment| assignment.check_witness(&self.filter)) @@ -963,7 +1036,7 @@ impl<'mem> ContractStateRead for MemContractFiltered<'mem> { pub struct MemContractWriter<'mem> { writer: Box Result<(), SerializeError> + 'mem>, - contract: &'mem mut MemContract, + contract: &'mem mut MemContractState, } impl<'mem> ContractStateWrite for MemContractWriter<'mem> { diff --git a/src/persistence/mod.rs b/src/persistence/mod.rs index 439a1240..b20bc910 100644 --- a/src/persistence/mod.rs +++ b/src/persistence/mod.rs @@ -43,7 +43,8 @@ pub use index::{ Index, IndexError, IndexInconsistency, IndexProvider, IndexReadError, IndexReadProvider, IndexWriteError, IndexWriteProvider, }; -pub use memory::{MemContract, MemGlobalState, MemIndex, MemStash, MemState}; +pub(crate) use memory::MemContract; +pub use memory::{MemContractState, MemGlobalState, MemIndex, MemStash, MemState}; pub use stash::{ ProviderError as StashProviderError, SchemaIfaces, Stash, StashDataError, StashError, StashInconsistency, StashProvider, StashReadProvider, StashWriteProvider, diff --git a/src/stl/stl.rs b/src/stl/stl.rs index 93a1e860..02f8aa3f 100644 --- a/src/stl/stl.rs +++ b/src/stl/stl.rs @@ -41,7 +41,7 @@ use crate::LIB_NAME_RGB_STD; /// Strict types id for the library providing standard data types which may be /// used in RGB smart contracts. pub const LIB_ID_RGB_STORAGE: &str = - "stl:DKVsldZ8-zYwnjtp-igdl80j-eifV0Ef-asKEvF6-JyZP3to#song-wave-story"; + "stl:aHl1Tsu8-RxChWD4-DXqrqOO-qFlJXsK-0uWYfDv-H5BgmxE#plastic-shelter-podium"; /// Strict types id for the library providing standard data types which may be /// used in RGB smart contracts. diff --git a/stl/RGBStorage@0.11.0.sta b/stl/RGBStorage@0.11.0.sta index 2369a8e9..4333e3f2 100644 --- a/stl/RGBStorage@0.11.0.sta +++ b/stl/RGBStorage@0.11.0.sta @@ -1,21 +1,21 @@ -----BEGIN STRICT TYPE LIB----- -Id: stl:DKVsldZ8-zYwnjtp-igdl80j-eifV0Ef-asKEvF6-JyZP3to#song-wave-story +Id: stl:aHl1Tsu8-RxChWD4-DXqrqOO-qFlJXsK-0uWYfDv-H5BgmxE#plastic-shelter-podium Name: RGBStorage Dependencies: StrictTypes#century-comrade-chess, AluVM#congo-archive-folio, BPCore#garbo-radius-peru, - RGBLogic#target-locate-justin, + RGBLogic#lobster-dilemma-famous, RGBCommit#orbit-airport-voice, RGBStd#carol-edgar-escape, Std#ralph-blue-lucky, CommitVerify#tennis-peace-olympic, Bitcoin#signal-color-cipher -Check-SHA256: 7757f149c764f7f97d07b74391e57da90d849ba134e72193e1004714e78eb198 +Check-SHA256: 762f9f0746599dfafb02c4a3c8e6300c29b33f5632a50e3e6101df44473526f0 3Q|WxQ*>`~VP|CtMe3tp+xFv-0Xp&G?S=|}9rRaeU`~uMrbA>C`}q*r3sZD*X=8L$d2nTOVsJHoA?4$s wuZp1Wc+9AOf`(TIbyKWjTy4WkGaM+1wm|eR!wgnmidRhTh1hu7-!n@1Cr{swqbZoGSd8tmgp<3rE>;C -P(yEWWro%lZ6!ob>Z)F^+#%=mzd2Bb4}cMy22w{tQ*>m? EFN!zncXl9K5w2;FV{y1jDTJCC^p$-mHEbO0#qjhQ*>nE(cK(6LD#rwNz2*s{WQVl8bg5o8r0R+^o=IR l4@lPLvL+uX>?X)a%pCH^=uPjBlbC`N(qzPM@Gr{imSMTSY5T*7C#t%#3&jH2SRCdV{d702?arHbyiIV @@ -51,172 +51,171 @@ H8-hI70Bv^+*0?ef%0)>Q3WPbltNdpi4*91)SI!>2Tf&jb75y?IG#g>Clv)aMjKgwAH@`bu1x<7g|G$} _h5K%L=laq&yA1JoJ^{7>oKLkF4~iax8KK|47hp@Qe|^xa&~28LV0v$b1}=fEj#9D^K)f#Cf|Xn@L3mU 0Z2&n-dr?jcD1Ll0RawDWpib6c4cHjd30rSGT61bmh)A>-9G+(AKhLw+s!eDmlO2>&}_PPHjCBJR|r&c Wo1rpWM%K_6AuO0fiYoI|8ZKC9(55{UNs2(LOhfb*8wh)9?K3=Wpib6c4cHjd30rSH2#7XN#A(BKKz&v -`r;e6DUv<<*U}c>>c184%wsNMHUptBVZ#B!U% +`r;e6DUv<<*U}c>*a184%wsNMHUptBVZ#B!U% rHo-i1kG~VoNp!e_~i}U4@G!%Wo~n6Z*Eg#Xk~3-1ACLTJsO2B2U!6ncg?mz@CdC==Kxq?gSEg)z2E{| 2tsvkWNc+gWC^x^65s1c;WeUUgB8EM+V1n+T -F3m?Yd1DC`X&GvUv9(-1>WQHGZVX3kZ(?C=R$**)WpfU{xa~M%Zlr^{H1ibsRCVcA*+(@K+$R_oJY%-u -iLnY-X>@L7b8}E{a}*^!j!?y>j|%qbz^!%<&Wu0B;HjDvS(4Y;;Uvd1Z1jQ)P4|MRdNw -xo;mu -Vif;}u9xW_W$|0g2|;snWpq?wXLByHrT!PdFhnqz;9Q#@6CZZuL4t`n9TUcD*&5hFi^PVx{q1b@^7zTcrn*%qZTXbx0zVQzD2bZKvH -RC#b^Ho-KZ`k;Xmr`<4sJYKN!!u{G5u+^j1lf!PF4>GEG3r}NXb#iiLZewM0IVbbqN^4g)WDG0#SSGl- -+Q@e<+6H_!d>A}?>ez-^4c1W;i8W#IuP5(!0kbY*UHX>V>q -Fkyv=$keM8CP2si$rmim(Ekws4U>QXM0|*v-OPCjO=V)bO0J^ce(PcW1p-OKH=(^)qT -gDk?v){Y2{bs0f(b7^O8ZDnqBb3$xsZe&wsVQf@*P;_!e?dHP>9R0ZFSEMRj;Km4qfBYZ5UUs>0bg9bq -iCNAIR$**qZew{=d2nS&y&7&8`-VFfe10WfHD}v`L+>hHy6d9F3e5?wo>3YSP-SFga&u*FLvL+uX>@I6 -Zgfg$dIyj=yj0m~TwLF91B-Lz;+I~gZmLfZ*F5{VQgh&Ms;pyX<}?;Py;H}8urLW|I0R= -+5HbL-$PaXl1;**YiA}19=cql5L9wuZgXjLX>V>qb#7#AWm6H&L#ixMu*i?c0&0P(dEtC_h4cCjts9h^ -`DC;F84*WpZ(?C=Q*>c;Wm98lWo=MdwWnpYocxhx>k8=qnO(R<<%JIK<1B78x*e6}1oxDzJ3ElvocGBq|L349yXKqquc4c8~ -Wn@HQbYVhlX>MdwWnpYocxhx|>KFFSbgda38zdDXQo#`G_0 -1v*0&52ohAEX3$~{+&W0M0{2&GbC -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 -Z*_EY00{!NFEi>Xk6pZ8Tw%5WIk~G+K)5%bR49 -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 -&nOG#EL&$!Mw -bxg)RqK0VQ|Mwn6X+txo3vSYd;;z)HQ~0$c*N -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 +PjGK_bd$i8ToRU7hj7c;WkPIeZe&wsVQf@* +X=IdA)7t~9tEf?*r}jS36zkMYeK9}${s8)2BzjZ?kPrw{V`yb-W|y2ahx3nF|Vuawki#7NH?S|Q-Q!u2{b3PW#hbaG*1bV+2Uj4+W$ +OUgRJVvNU?M2#%ns>Kwa1v8ba_B>T#2Nxy{Ms;pyX<}?;Q*>c;WvQZiSChz_$|Xx}eRkFNAr%^eLl(1e +@}~9=0-ijXfCfo!aCLNZw&;L{94K`ndk%K5+?9Jv$dw7jc}U5p5@2#$kUJ%u2uWmRZggpMdB|&mdkb29 +#*qXha^)f?kI>J>8dqqbOFybHKpQ-MBMCulbWCA+WpXjekD95&21^?K{bw7Oyejxis%MV9vZ(?C=Q*>c;WmI`^W!AhivB7pz2;cIXd9lO&nf5a*AnfQld~-bdb4MdwWnpYocu;h5+M7`mSQb`xkca!3_>4e9YQ#rfba;u!+d5tm#=h2RwFD4YLug@XZc}Ara%FT=WnpaHg=PS6VPp{$?vC-- +s`v@B8YHl)C#jpVFzBk!DMw8SR$**qZewX>bKWD7Yo@G%*b#-tU^&3KX?w7l?~*Sh8@1jRRblZzybDKc +Z(?C=Q*>c;W#7-Kk@bh=O+>c=6Nvv2!%cWh5H`};;G4e`yboldn7 +SVL%GX>L$;VpnN&Ze??G{4_<~U(XE-|Ev|Hdb$N7;9H9;8!%;3hl7uME$faw4?}NmV`X7%Wn@8gbYWv? +|7c^tcv66A`G>fI% +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 +X=6`tZ*_EY00{!NFEi>Xk6pZ8Tw%5WIk~G+K)5 +%bR49t5yk`^qQ9d0000000030|Nj600000EZ*_EVZ)t9HPjGK_baMa-0=6++>M;9#?)(c8mUNj%`dgQj +SJu&d2G{D9FxPD~VbNz7y+ac4_6daU{%%bk3j+fu`A*2Y1(Gbp$uTFEssITBwlQ4lF#CP({0kA5beTu` +TbGnq*3ogq>HOrf1lB-q;n)I5N0000000000|Ns9000000 +0000000000|Ns90000005KU!mLvL<$a$#e1Q*>c;Wd;Wbb7N>_ZDC1d0=6++>M;9#?)(c8mUNj%`dgQj +SJu&d2G{D9FxPD~VU$wS+XJhss8OG%_CC-Q>(otsF+cqN0Qy}ddQ=3E5DH^&Zgg^CV{}Pm0=6++>M;9# +?)(c8mUNj%`dgQjSJu&d2G{D9FxPD~VV?*rNjk^^qPoT1+zTRnAg`3vXv9d*8d@RXy~6c6G6rXCZ(?C= +015)OFEi>Xk6pZ8Txk?vf5kh_h+&YE#h%O8d1V_{UOl9{V;uR#^q% +KQ|D6@UrgHD8Pn~kr<(gaR)wpptCRljin0000000030|Ns9000004WMOn+00{!af$yWt)@)i+7y=gQ +ng90cs$Q?$A?NhJIZ%cVfDxNuKY4$``7oZ);noRy3n6DO2)Q4;H@bN5MlNj7(#BT+0000000030|Ns90 +00006VRUq1V`u;g0>gptqs-Q9T2mMT7U`M)_Ufu$uiPQ$^uIY!h7W)dn`vpvk*iEz1m@>LhD1|b9AmK% +IADG&k)euf*x}6a-2eap000000RR90{{R3001i!MZAWZxVqt7kbYXO51_A|ZZf|#P015)bf$yWt)@)i+ +7y=gQng90cs$Q?$A?NhJIZ%cVfDxOp75Y^g(<6SA(C9k+Q9g8C2$|Z6BB=4C11?tJB&WXuwlQ4lF#CP( +{0kA5beTu`TbGnq*3oM;9#?)(c8mUNj%`dgQj +SJu&d2G{D9FxPD~VU@_{dLDIRU(}XWLTZugenOC;Z(5k~zEJnJiX;;E#R9f5Tg)RqK0VQ|Mwn6X+txo3vSYd;;z)HQ~0$cz90000000960{{R30000wW +b#7#AWkYXnbaG*1bV+VxWq1Gz0=6++>M;9#?)(c8mUNj%`dgQjSJu&d2G{D9FxPD~VFP=UwLKbzE(ciw +C3nrXLGTEzPUiqvVS}~6O1M;9#?)(c8mUNj%`dgQjSJu&d2G{D9FxPD~VTo&4CC$c=UszhlV5m?Ru@{iVU*wrVdeH+Q@FPbX@c;k- +000000RR900000001abrZgg^CV{}PwWMy~&3IetMU`Np56icmN6lwlQ4lF#CP({0kA5beTu`TbGnq*3oKtpQ8M_qJyiO1VIUJ=H=)@ii_0=6++>M;9#?)(c8mUNj%`dgQjSJu&d2G{D9FxPD~ +VUrNNfUz`Mi!Z}iQtl5;XwV(E`Zdd&WRj~^37YhpmjD0&000000RR900000000>QGZBuk%b7%$)2ygptqs-Q9T2mMT7U`M)_Ufu$uiPQ$^uIY!h7W)do6>hLb?3Jmz+|vF&&E~F32+|Fmge=B +|Eq%4$%~#cQ~&?~000000RI300000000wDhVPj=;015)bf$yWt)@)i+7y=gQng90cs$Q?$A?NhJIZ%cV +fDxNJ%D{mG2;nQMTOnwNgyXhzrB~SH04;UKo5i(1Vxw^a!-4Ok%+_pLQy2mk>6!ob>Z)F^+#%=mzd2Bb +4}cMyIma44eh@g%x4xWoee18jkej%UZIDDtP|$FhF<2o`0000000000{{R30000002WMq&Wpib7015)O +FEi>Xk6pZ8Tw@2rNlD$O59e#ogQsB77jPl+h5j^*S;F +wlQ4lF#CP({0kA5beTu`TbGnq*3ogptqs-Q9T2mMT7U`M)_Ufu$uiPQ$^uIY!h7W)dn+BMzx7s+u +ExGllhUte$e$Op^sMk_Bzn7+|3$axzr2q*6!-4Ok%+_pLQy2mk>6!ob>Z)F^+#%=mzd2Bb4}cMyV?EP} +uuDl+D$lsiICW4a8e$Z2e6I7`3evG=Yh^sO0000000000{{R30000000000000000{{R30000002V!+@ +WNc+~015)OFEi>Xk6pZ8Tv6dy}<28ig(gSpg+?&9*`C2(3=%09avz +wZKZf-~wC%wlQ4lF#CP({0kA5beTu`TbGnq*3owZFzp>JB4@xD;^wu&SY_r(IXFEi +>Xk6pZ8Tv$oM&@m2OQ0ewbQwN{Ihzb=0(75jKBm?VEtv`0VWav0000000030|Ns9000009cWHEPWpi_7 +a{vkgwlQ4lF#CP({0kA5beTu`TbGnq*3ogptqs-Q9T2mMT7U`M)_Ufu$uiPQ$^uIY!h7W)dn_e*Xi0g$2n6-TWNhgSA?dx`~TofZYQwM8# +9N}@u^Z)<=000000RR90{{R3001IJsbYWv?ZDnqBa{vkgwlQ4lF#CP({0kA5beTu`TbGnq*3oxYW^+v~7{W03rq(;firJ0000000000|Ns90000003UqmJWm9=`bY*PC`}q*r{eiB7ehUYis7~w1CQOqefKeZ3;Wd%uopqe!>_vj93Tb3zZggpMX=QT& +3Ietbs~EXcCXx(drQcb3B`Fx$)^%va$Ar) +C7c4of$yWt)@)i+7y=gQng90cs$Q?$A?NhJIZ%cVfDxOb;91nsu+1H%suE1D6u@lRoC;S?XbB(j&QSOS +Pz0a=0000000030{{R3000004Y-wV1015(Pa5aA+<>R2XhQO_4{AcS-HH^7AVzASV8M4NYxyCl9FjWFA +`CQ2GiK9iLKbGE6DZmrA4)G`0A&^0p`%?-6VsJHoA?4$swuZp1Wc+9AOf`(TIbyKWjTy4WkGaM+5(KBV +0uX$PL@)I=)&*`^S@`8Scoz5#{lyP)a751L0000000000|Nj60000001aoO;a{vkg!-4Ok%+_pLQy2mk +>6!ob>Z)F^+#%=mzd2Bb4}cMy;?xyT5z&Ua+M@}mOiDpYxh>^^GknUxTJ!XL#OUcE0>gptqs-Q9T2mMT +7U`M)_Ufu$uiPQ$^uIY!h7W)dn}!$=ENEw7&f?o%+)B!ZpG}K!%4G?I4vp$|ttu*CMF0Q*000000RR60 +0000000>QGZBuk%bY%tt33q99Ze??GWpe-u0=6++>M;9#?)(c8mUNj%`dgQjSJu&d2G{D9FxPD~VTo&4 +CC$c=UszhlV5m?Ru@{iVU*wrVdeH+Q@FPbX@dBq1@f2!3F}9pjqENaGM*m)mvgA&6NwpvNR8OilE(zRs +vAaSw-)KgQ7fAeMNYpypNfHyE8AFfUoC2TNw^jfE0000000960|Nj60000SNZ*FvQVPkZ2015)OFEi>Xk6pZ8Tw@2rNlD$O59e#ogQsB77jPl+h5j^*S;ECay7U +sgJX1q%>cJ$|yD%XkEW=AK-dqj9T5dYzU|{0000000000{{R3000000 -----END STRICT TYPE LIB----- diff --git a/stl/RGBStorage@0.11.0.stl b/stl/RGBStorage@0.11.0.stl index e21f8ac82b599ff6cbcd7f23643cfc0fe4670056..364f1d25bfb49133a192b656e7983663e0b0d9eb 100644 GIT binary patch delta 771 zcmaEyx+Zl(tLAcnk0M#NhTG-@FACVj>-Imcd(#uYQqQgOKSKOhndxzI1i3r;gpyIo^!#38Cih%s$JLMa9JDJ|kaO)QL_>$yDs@@8ZpwuhR(}HRN@6)?-s)6Z@o@>W zUf2Vb<@;Iu3tV$1hs*X&exRi=Sxb&r;c}GJ*4Y7FObQyCYi~FEwdBt3U4Qfvi(kpM z$W^~X_ZLq7B_lW4T&_;x%Zlp{+C79OtZtE-xprCA7SAoWelZ@^mpeLx;nBwvdNGp^ zh)ZqWDi_WwDBzo#>ztogQk0ln5?qp4k~&#J<*~w@-A87#oj%_5FlJ4q)WrJ_I=en9 zOFTKWF}U0`H8gq8g2~dVR)j4nRkbDTP@^~lwRMKMEBiNRE-{I3J*8nLkrBH;U;aT=N>9|??KvDPOcUlnEF>0 delta 795 zcmZ3J`Z#q$t7h9Z@%*WiTX&rEkeMD4`QVCNJ148k@mWk3vhDUw3qs>Lg4~^a^3yYu zCqL9w*m&*%2cyU2^_-iv`1bF3Ymt<lvztA!VCo1`>dh?HxYrgB3X(1^j@Ku8C8h{G+PU~(7*B}b`nZedZ>weoAGoqJbfW6ycsZ%fDdH~} z6?~(aH*j5e*m8Ply-yk=ORiJp&by~AEN{-;ojdd8+4HiK<)maM`$%q@T%)5fIapnc>3)^|Yb@DnX`N=0_`c%)and9Iik#aNJ z$Mt#&o56ICcIivEPm1_HZwZ*n!MHew#lOHcXYxud1zw)uW`dIJR>vq&bK7> zqRpH$CqGfKA{ ^ ..0xffffff {RGBCommit.Opout ^ ..0xffffff}} -@mnemonic(quasi-battery-mirror) -data MemContract : schemaId RGBCommit.SchemaId +@mnemonic(shake-square-wizard) +data MemContractState : schemaId RGBCommit.SchemaId , contractId RGBCommit.ContractId , global {RGBCommit.GlobalStateType -> ^ ..0xff MemGlobalState} , rights {RGBStd.OutputAssignmentVoidState ^ ..0xffffffff} @@ -228,7 +227,7 @@ data MemContract : schemaId RGBCommit.SchemaId , data {RGBStd.OutputAssignmentRevealedData ^ ..0xffffffff} , attach {RGBStd.OutputAssignmentRevealedAttach ^ ..0xffffffff} -@mnemonic(chamber-academy-academy) +@mnemonic(numeric-plasma-tunnel) data MemGlobalState : known {RGBStd.OpEl -> ^ ..0xffffffff RGBCommit.DataState}, limit U24 @mnemonic(flower-unicorn-bazaar) @@ -253,7 +252,7 @@ data MemStash : schemata {RGBCommit.SchemaId -> ^ ..0xff RGBStd.SchemaI , libs {AluVM.LibId -> AluVM.Lib} , sigs {RGBStd.ContentId -> RGBStd.ContentSigs} -@mnemonic(forum-context-flood) -data MemState : witnesses {RGBCommit.XChainTxid -> ^ ..0xffffffff RGBCommit.WitnessOrd}, contracts {RGBCommit.ContractId -> ^ ..0xff MemContract} +@mnemonic(maximum-blonde-herbert) +data MemState : witnesses {RGBCommit.XChainTxid -> ^ ..0xffffffff RGBLogic.TxOrd}, contracts {RGBCommit.ContractId -> ^ ..0xff MemContractState} From 77728ce326943b6dace6dc8d87dfbbe8f98ed332 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Wed, 31 Jul 2024 19:35:53 +0200 Subject: [PATCH 04/12] persistence: use new consensus ordering from RCP-240731A --- Cargo.lock | 1 - Cargo.toml | 4 +- src/contract/assignments.rs | 8 ++- src/contract/mod.rs | 62 +++++++++++++++--- src/interface/builder.rs | 9 +++ src/interface/resolver.rs | 6 +- src/lib.rs | 2 +- src/persistence/memory.rs | 126 +++++++++++++++++++++--------------- src/persistence/state.rs | 19 +++--- src/resolvers.rs | 4 +- 10 files changed, 159 insertions(+), 82 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 101ef56d..66107ae0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -661,7 +661,6 @@ dependencies = [ [[package]] name = "rgb-core" version = "0.11.0-beta.6" -source = "git+https://github.com/RGB-WG/rgb-core?branch=contract-state2#2604db8514b9f33f18fd6db78c327055a7c6618d" dependencies = [ "aluvm", "amplify", diff --git a/Cargo.toml b/Cargo.toml index 7346e8df..e604444f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -99,5 +99,5 @@ features = ["all"] [patch.crates-io] bp-consensus = { git = "https://github.com/BP-WG/bp-core", branch = "master" } bp-invoice = { git = "https://github.com/BP-WG/bp-std.git", branch = "master" } -rgb-core = { git = "https://github.com/RGB-WG/rgb-core", branch = "contract-state2" } -#rgb-core = { path = "../rgb-core" } +#rgb-core = { git = "https://github.com/RGB-WG/rgb-core", branch = "contract-state2" } +rgb-core = { path = "../rgb-core" } diff --git a/src/contract/assignments.rs b/src/contract/assignments.rs index 871e616a..65090546 100644 --- a/src/contract/assignments.rs +++ b/src/contract/assignments.rs @@ -26,7 +26,7 @@ use std::fmt::Debug; use amplify::confinement::SmallVec; use commit_verify::Conceal; use invoice::Amount; -use rgb::vm::TxOrd; +use rgb::vm::WitnessOrd; use rgb::{ Assign, AssignAttach, AssignData, AssignFungible, AssignRights, AssignmentType, AttachState, DataState, ExposedSeal, ExposedState, OpId, Opout, RevealedAttach, RevealedData, RevealedValue, @@ -153,10 +153,12 @@ impl OutputAssignment { } } - pub fn check_witness(&self, filter: &HashMap) -> bool { + pub fn check_witness(&self, filter: &HashMap) -> bool { match self.witness { None => true, - Some(witness_id) => !matches!(filter.get(&witness_id), None | Some(TxOrd::Archived)), + Some(witness_id) => { + !matches!(filter.get(&witness_id), None | Some(WitnessOrd::Archived)) + } } } } diff --git a/src/contract/mod.rs b/src/contract/mod.rs index 9e3ed948..c64edbb7 100644 --- a/src/contract/mod.rs +++ b/src/contract/mod.rs @@ -26,22 +26,64 @@ mod merge_reveal; pub use assignments::{KnownState, OutputAssignment, TypedAssignsExt}; pub use bundle::{BundleExt, RevealError}; pub use merge_reveal::{MergeReveal, MergeRevealError}; +use rgb::vm::AnchoredOpRef; use rgb::{OpId, XWitnessId}; use crate::LIB_NAME_RGB_STD; -/// Reference to operation element. -#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB_STD, tags = order)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", rename_all = "camelCase") +)] +pub enum OpWitness { + #[strict_type(dumb)] + Genesis, + Transition(XWitnessId), + Extension(XWitnessId), +} + +impl From> for OpWitness { + fn from(aor: AnchoredOpRef) -> Self { + match aor { + AnchoredOpRef::Genesis(_) => OpWitness::Genesis, + AnchoredOpRef::Transition(_, witness_id) => OpWitness::Transition(witness_id), + AnchoredOpRef::Extension(_, witness_id) => OpWitness::Transition(witness_id), + } + } +} + +impl OpWitness { + #[inline] + pub fn witness_id(&self) -> Option { + match self { + OpWitness::Genesis => None, + OpWitness::Transition(witness_id) | OpWitness::Extension(witness_id) => { + Some(*witness_id) + } + } + } +} + +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB_STD)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] -#[display("{op}/{no}")] -pub struct OpEl { - pub op: OpId, - pub no: u16, - pub witness: Option, +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", rename_all = "camelCase") +)] +pub struct GlobalOut { + pub opid: OpId, + pub nonce: u8, + pub index: u16, + pub op_witness: OpWitness, } -impl OpEl { - pub fn new(op: OpId, no: u16, witness: Option) -> OpEl { OpEl { op, no, witness } } +impl GlobalOut { + #[inline] + pub fn witness_id(&self) -> Option { self.op_witness.witness_id() } } diff --git a/src/interface/builder.rs b/src/interface/builder.rs index 74659b75..84c6c500 100644 --- a/src/interface/builder.rs +++ b/src/interface/builder.rs @@ -434,6 +434,7 @@ impl ContractBuilder { pub struct TransitionBuilder { contract_id: ContractId, builder: OperationBuilder, + nonce: u8, transition_type: TransitionType, inputs: TinyOrdMap, } @@ -530,6 +531,7 @@ impl TransitionBuilder { Self { contract_id, builder: OperationBuilder::with(iface, schema, iimpl, types), + nonce: u8::MAX, transition_type, inputs: none!(), } @@ -546,6 +548,7 @@ impl TransitionBuilder { Self { contract_id, builder: OperationBuilder::deterministic(iface, schema, iimpl, types), + nonce: u8::MAX, transition_type, inputs: none!(), } @@ -555,6 +558,11 @@ impl TransitionBuilder { pub fn transition_type(&self) -> TransitionType { self.transition_type } + pub fn set_nonce(mut self, nonce: u8) -> Self { + self.nonce = nonce; + self + } + #[inline] pub fn asset_tag(&self, name: impl Into) -> Result { self.builder.asset_tag(name) @@ -795,6 +803,7 @@ impl TransitionBuilder { let transition = Transition { ffv: none!(), contract_id: self.contract_id, + nonce: self.nonce, transition_type: self.transition_type, metadata: empty!(), globals: global, diff --git a/src/interface/resolver.rs b/src/interface/resolver.rs index 86ebf5ba..7f563820 100644 --- a/src/interface/resolver.rs +++ b/src/interface/resolver.rs @@ -20,7 +20,7 @@ // limitations under the License. use rgb::validation::{ResolveWitness, WitnessResolverError}; -use rgb::vm::{TxOrd, XWitnessTx}; +use rgb::vm::{WitnessOrd, XWitnessTx}; use strict_encoding::StrictDumb; use crate::XWitnessId; @@ -32,7 +32,7 @@ impl ResolveWitness for DumbResolver { Ok(XWitnessTx::strict_dumb()) } - fn resolve_pub_witness_ord(&self, _: XWitnessId) -> Result { - Ok(TxOrd::strict_dumb()) + fn resolve_pub_witness_ord(&self, _: XWitnessId) -> Result { + Ok(WitnessOrd::strict_dumb()) } } diff --git a/src/lib.rs b/src/lib.rs index c14badfc..92ec2d38 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,7 +56,7 @@ pub mod info; pub use bp::{Outpoint, Txid}; pub use contract::{ - BundleExt, KnownState, MergeReveal, MergeRevealError, OpEl, OutputAssignment, RevealError, + BundleExt, KnownState, MergeReveal, MergeRevealError, OutputAssignment, RevealError, TypedAssignsExt, }; pub use invoice::{Allocation, Amount, CoinAmount, OwnedFraction, Precision, TokenIndex}; diff --git a/src/persistence/memory.rs b/src/persistence/memory.rs index edea9b64..b7edb131 100644 --- a/src/persistence/memory.rs +++ b/src/persistence/memory.rs @@ -40,7 +40,7 @@ use commit_verify::{CommitId, Conceal}; use rgb::validation::ResolveWitness; use rgb::vm::{ AnchoredOpRef, ContractStateAccess, ContractStateEvolve, GlobalContractState, GlobalOrd, - GlobalStateIter, TxOrd, UnknownGlobalStateType, WitnessOrd, + GlobalStateIter, UnknownGlobalStateType, WitnessOrd, }; use rgb::{ Assign, AssignmentType, Assignments, AssignmentsRef, AttachId, AttachState, BundleId, @@ -62,11 +62,11 @@ use super::{ use crate::containers::{ AnchorSet, ContentId, ContentRef, ContentSigs, SealWitness, SigBlob, Supplement, TrustLevel, }; -use crate::contract::{KnownState, OutputAssignment}; +use crate::contract::{GlobalOut, KnownState, OpWitness, OutputAssignment}; use crate::interface::{Iface, IfaceClass, IfaceId, IfaceImpl, IfaceRef}; #[cfg(feature = "fs")] use crate::persistence::fs::FsStored; -use crate::{OpEl, LIB_NAME_RGB_STORAGE}; +use crate::LIB_NAME_RGB_STORAGE; ////////// // STASH @@ -445,7 +445,7 @@ pub struct MemState { #[strict_type(skip)] filename: PathBuf, - witnesses: LargeOrdMap, + witnesses: LargeOrdMap, contracts: TinyOrdMap, } @@ -494,7 +494,7 @@ impl StateReadProvider for MemState { .global .values() .flat_map(|state| state.known.keys()) - .any(|el| el.witness == id) || + .any(|out| out.witness_id() == id) || unfiltered.rights.iter().any(|a| a.witness == id) || unfiltered.fungibles.iter().any(|a| a.witness == id) || unfiltered.data.iter().any(|a| a.witness == id) || @@ -530,12 +530,14 @@ impl StateWriteProvider for MemState { self.contracts.get_mut(&contract_id).expect("just inserted") }; let mut writer = MemContractWriter { - writer: Box::new(|ord: WitnessOrd| -> Result<(), SerializeError> { - // NB: We do not check the existence of the witness since we have a newer - // version anyway and even if it is known we have to replace it - self.witnesses.insert(ord.witness_id, ord.pub_ord)?; - Ok(()) - }), + writer: Box::new( + |witness_id: XWitnessId, ord: WitnessOrd| -> Result<(), SerializeError> { + // NB: We do not check the existence of the witness since we have a newer + // version anyway and even if it is known we have to replace it + self.witnesses.insert(witness_id, ord)?; + Ok(()) + }, + ), contract, }; writer.add_genesis(genesis)?; @@ -553,13 +555,15 @@ impl StateWriteProvider for MemState { .map(|contract| MemContractWriter { // We can't move this constructor to a dedicated method due to the rust borrower // checker - writer: Box::new(|ord: WitnessOrd| -> Result<(), SerializeError> { - // NB: We do not check the existence of the witness since we have a newer - // version anyway and even if it is known we have to replace - // it - self.witnesses.insert(ord.witness_id, ord.pub_ord)?; - Ok(()) - }), + writer: Box::new( + |witness_id: XWitnessId, ord: WitnessOrd| -> Result<(), SerializeError> { + // NB: We do not check the existence of the witness since we have a newer + // version anyway and even if it is known we have to replace + // it + self.witnesses.insert(witness_id, ord)?; + Ok(()) + }, + ), contract, })) } @@ -577,7 +581,7 @@ impl StateWriteProvider for MemState { mem::swap(&mut self.witnesses, &mut witnesses); let mut witnesses = witnesses.unbox(); for (id, ord) in &mut witnesses { - if matches!(ord, TxOrd::OnChain(pos) if pos.height() < after_height) { + if matches!(ord, WitnessOrd::Mined(pos) if pos.height() < after_height) { continue; } match resolver.resolve_pub_witness_ord(*id) { @@ -601,7 +605,7 @@ impl StateWriteProvider for MemState { #[strict_type(lib = LIB_NAME_RGB_STORAGE)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] pub struct MemGlobalState { - known: LargeOrdMap, + known: LargeOrdMap, limit: u24, } @@ -663,8 +667,7 @@ impl MemContractState { } } - // TODO: Use `OpRef` type instead of `op` and `witness_id` - fn add_operation(&mut self, op: &impl Operation, witness_id: Option) { + fn add_operation(&mut self, op: AnchoredOpRef) { let opid = op.id(); for (ty, state) in op.globals() { @@ -673,9 +676,14 @@ impl MemContractState { .get_mut(ty) .expect("global map must be initialized from the schema"); for (idx, s) in state.iter().enumerate() { - let el = OpEl::new(opid, idx as u16, witness_id); + let out = GlobalOut { + opid, + nonce: op.nonce(), + index: idx as u16, + op_witness: OpWitness::from(op), + }; map.known - .insert(el, s.clone()) + .insert(out, s.clone()) .expect("contract global state exceeded 2^32 items, which is unrealistic"); } } @@ -713,6 +721,7 @@ impl MemContractState { } */ + let witness_id = op.witness_id(); match op.assignments() { AssignmentsRef::Genesis(assignments) => { self.add_assignments(witness_id, opid, assignments) @@ -773,7 +782,7 @@ impl MemContractState { } pub struct MemContract> { - filter: HashMap, + filter: HashMap, unfiltered: M, } @@ -788,7 +797,7 @@ impl> ContractStateAccess for MemContract { &self, ty: GlobalStateType, ) -> Result, UnknownGlobalStateType> { - type Src<'a> = &'a BTreeMap; + type Src<'a> = &'a BTreeMap; type FilteredIter<'a> = Box + 'a>; struct Iter<'a> { src: Src<'a>, @@ -849,12 +858,16 @@ impl> ContractStateAccess for MemContract { Box::new( src.iter() .rev() - .filter_map(|(el, data)| { - let ord = match el.witness { - None => GlobalOrd::genesis(el.op, el.no), - Some(id) => { + .filter_map(|(out, data)| { + let ord = match out.op_witness { + OpWitness::Genesis => GlobalOrd::genesis(out.index), + OpWitness::Transition(id) => { + let ord = self.filter.get(&id)?; + GlobalOrd::transition(out.opid, out.index, out.nonce, *ord) + } + OpWitness::Extension(id) => { let ord = self.filter.get(&id)?; - GlobalOrd::with_witness(el.op, id, *ord, el.no) + GlobalOrd::extension(out.opid, out.index, out.nonce, *ord) } }; Some((ord, data)) @@ -944,22 +957,24 @@ impl ContractStateEvolve for MemContract { } fn evolve_state(&mut self, op: AnchoredOpRef) -> Result<(), confinement::Error> { - fn ordering(filter: &HashMap, witness_id: XWitnessId) -> WitnessOrd { - let ord = filter.get(&witness_id).expect("unknown witness id"); - WitnessOrd { - pub_ord: *ord, - witness_id, - } + fn ordering( + filter: &HashMap, + witness_id: XWitnessId, + ) -> WitnessOrd { + *filter.get(&witness_id).expect("unknown witness id") } (move || -> Result<(), SerializeError> { fn writer(me: &mut MemContract) -> MemContractWriter { MemContractWriter { - writer: Box::new(|ord: WitnessOrd| -> Result<(), SerializeError> { - // NB: We do not check the existence of the witness since we have a newer - // version anyway and even if it is known we have to replace it - me.filter.insert(ord.witness_id, ord.pub_ord); - Ok(()) - }), + writer: Box::new( + |witness_id: XWitnessId, ord: WitnessOrd| -> Result<(), SerializeError> { + // NB: We do not check the existence of the witness since we have a + // newer version anyway and even if it is + // known we have to replace it + me.filter.insert(witness_id, ord); + Ok(()) + }, + ), contract: &mut me.unfiltered, } } @@ -971,12 +986,12 @@ impl ContractStateEvolve for MemContract { AnchoredOpRef::Transition(transition, witness_id) => { let ord = ordering(&self.filter, witness_id); let mut writer = writer(self); - writer.add_transition(transition, ord) + writer.add_transition(transition, witness_id, ord) } AnchoredOpRef::Extension(extension, witness_id) => { let ord = ordering(&self.filter, witness_id); let mut writer = writer(self); - writer.add_extension(extension, ord) + writer.add_extension(extension, witness_id, ord) } } })() @@ -1035,7 +1050,7 @@ impl> ContractStateRead for MemContract { } pub struct MemContractWriter<'mem> { - writer: Box Result<(), SerializeError> + 'mem>, + writer: Box Result<(), SerializeError> + 'mem>, contract: &'mem mut MemContractState, } @@ -1047,7 +1062,7 @@ impl<'mem> ContractStateWrite for MemContractWriter<'mem> { /// If genesis violates RGB consensus rules and wasn't checked against the /// schema before adding to the history. fn add_genesis(&mut self, genesis: &Genesis) -> Result<(), Self::Error> { - self.contract.add_operation(genesis, None); + self.contract.add_operation(AnchoredOpRef::Genesis(genesis)); Ok(()) } @@ -1058,11 +1073,12 @@ impl<'mem> ContractStateWrite for MemContractWriter<'mem> { fn add_transition( &mut self, transition: &Transition, + witness_id: XWitnessId, ord: WitnessOrd, ) -> Result<(), Self::Error> { - (self.writer)(ord)?; + (self.writer)(witness_id, ord)?; self.contract - .add_operation(transition, Some(ord.witness_id)); + .add_operation(AnchoredOpRef::Transition(transition, witness_id)); Ok(()) } @@ -1070,9 +1086,15 @@ impl<'mem> ContractStateWrite for MemContractWriter<'mem> { /// /// If state extension violates RGB consensus rules and wasn't checked /// against the schema before adding to the history. - fn add_extension(&mut self, extension: &Extension, ord: WitnessOrd) -> Result<(), Self::Error> { - (self.writer)(ord)?; - self.contract.add_operation(extension, Some(ord.witness_id)); + fn add_extension( + &mut self, + extension: &Extension, + witness_id: XWitnessId, + ord: WitnessOrd, + ) -> Result<(), Self::Error> { + (self.writer)(witness_id, ord)?; + self.contract + .add_operation(AnchoredOpRef::Extension(extension, witness_id)); Ok(()) } } diff --git a/src/persistence/state.rs b/src/persistence/state.rs index 00fee1ca..a77cc30d 100644 --- a/src/persistence/state.rs +++ b/src/persistence/state.rs @@ -138,7 +138,7 @@ impl State

{ .resolve_pub_witness_ord(witness_id) .map_err(|e| StateError::Resolver(witness_id, e))?; updater - .add_transition(transition, WitnessOrd::with(witness_id, ord)) + .add_transition(transition, witness_id, ord) .map_err(StateError::WriteProvider)?; } Ok(()) @@ -163,13 +163,12 @@ impl State

{ for bundle in bundled_witness.anchored_bundles.bundles() { for transition in bundle.known_transitions.values() { let witness_id = bundled_witness.pub_witness.to_witness_id(); - let ord = resolver + let witness_ord = resolver .resolve_pub_witness_ord(witness_id) .map_err(|e| StateError::Resolver(witness_id, e))?; - let witness_ord = WitnessOrd::with(witness_id, ord); state - .add_transition(transition, witness_ord) + .add_transition(transition, witness_id, witness_ord) .map_err(StateError::WriteProvider)?; for (id, used) in &mut extension_idx { if *used { @@ -178,12 +177,13 @@ impl State

{ for input in &transition.inputs { if input.prev_out.op == *id { *used = true; - if let Some(witness_ord2) = ordered_extensions.get_mut(id) { + if let Some((_, witness_ord2)) = ordered_extensions.get_mut(id) { + // TODO: Double-check this ordering if *witness_ord2 > witness_ord { *witness_ord2 = witness_ord; } } else { - ordered_extensions.insert(*id, witness_ord); + ordered_extensions.insert(*id, (witness_id, witness_ord)); } } } @@ -192,11 +192,12 @@ impl State

{ } } for extension in consignment.extensions() { - if let Some(witness_ord) = ordered_extensions.get(&extension.id()) { + if let Some((witness_id, witness_ord)) = ordered_extensions.get(&extension.id()) { state - .add_extension(extension, *witness_ord) + .add_extension(extension, *witness_id, *witness_ord) .map_err(StateError::WriteProvider)?; } + // TODO: Do something otherwise } Ok(()) @@ -284,12 +285,14 @@ pub trait ContractStateWrite { fn add_transition( &mut self, transition: &Transition, + witness_id: XWitnessId, witness_ord: WitnessOrd, ) -> Result<(), Self::Error>; fn add_extension( &mut self, extension: &Extension, + witness_id: XWitnessId, witness_ord: WitnessOrd, ) -> Result<(), Self::Error>; } diff --git a/src/resolvers.rs b/src/resolvers.rs index 85492c81..76dc5e7b 100644 --- a/src/resolvers.rs +++ b/src/resolvers.rs @@ -20,7 +20,7 @@ // limitations under the License. use rgb::validation::{ResolveWitness, WitnessResolverError}; -use rgb::vm::{TxOrd, XWitnessId, XWitnessTx}; +use rgb::vm::{WitnessOrd, XWitnessId, XWitnessTx}; use crate::containers::IndexedConsignment; @@ -48,7 +48,7 @@ impl<'cons, R: ResolveWitness, const TRANSFER: bool> ResolveWitness fn resolve_pub_witness_ord( &self, witness_id: XWitnessId, - ) -> Result { + ) -> Result { self.fallback.resolve_pub_witness_ord(witness_id) } } From 3263dfc5e5a7449d2582d9813f32c77f2fd2ed94 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Wed, 31 Jul 2024 22:04:30 +0200 Subject: [PATCH 05/12] persistence: fix ordering of state extensions --- src/containers/consignment.rs | 2 ++ src/persistence/state.rs | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/containers/consignment.rs b/src/containers/consignment.rs index 3d149f3c..03cf2f13 100644 --- a/src/containers/consignment.rs +++ b/src/containers/consignment.rs @@ -383,6 +383,8 @@ impl Consignment { } // TODO: check attach ids from data containers are present in operations // TODO: validate sigs and remove untrusted + // TODO: Check that all extensions present in the consignment are used by state + // transitions if validity != Validity::Valid { Err((status, self)) diff --git a/src/persistence/state.rs b/src/persistence/state.rs index a77cc30d..319fe8d6 100644 --- a/src/persistence/state.rs +++ b/src/persistence/state.rs @@ -178,8 +178,7 @@ impl State

{ if input.prev_out.op == *id { *used = true; if let Some((_, witness_ord2)) = ordered_extensions.get_mut(id) { - // TODO: Double-check this ordering - if *witness_ord2 > witness_ord { + if *witness_ord2 < witness_ord { *witness_ord2 = witness_ord; } } else { @@ -197,7 +196,9 @@ impl State

{ .add_extension(extension, *witness_id, *witness_ord) .map_err(StateError::WriteProvider)?; } - // TODO: Do something otherwise + // Otherwise consignment includes state extensions which are not + // used in transaction graph. This must not be the case for the + // validated consignments. } Ok(()) From 738d1b38bd2e61503a446accf50d711a49241d44 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Thu, 1 Aug 2024 14:16:55 +0200 Subject: [PATCH 06/12] persistence: export MemContract, add default generics --- src/persistence/memory.rs | 2 +- src/persistence/mod.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/persistence/memory.rs b/src/persistence/memory.rs index b7edb131..58d81cfb 100644 --- a/src/persistence/memory.rs +++ b/src/persistence/memory.rs @@ -781,7 +781,7 @@ impl MemContractState { } } -pub struct MemContract> { +pub struct MemContract = MemContractState> { filter: HashMap, unfiltered: M, } diff --git a/src/persistence/mod.rs b/src/persistence/mod.rs index b20bc910..b2da2a38 100644 --- a/src/persistence/mod.rs +++ b/src/persistence/mod.rs @@ -43,8 +43,7 @@ pub use index::{ Index, IndexError, IndexInconsistency, IndexProvider, IndexReadError, IndexReadProvider, IndexWriteError, IndexWriteProvider, }; -pub(crate) use memory::MemContract; -pub use memory::{MemContractState, MemGlobalState, MemIndex, MemStash, MemState}; +pub use memory::{MemContract, MemContractState, MemGlobalState, MemIndex, MemStash, MemState}; pub use stash::{ ProviderError as StashProviderError, SchemaIfaces, Stash, StashDataError, StashError, StashInconsistency, StashProvider, StashReadProvider, StashWriteProvider, From 5b116661704b1cfcb8a3d2042d89b50ff264615a Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Thu, 1 Aug 2024 14:22:13 +0200 Subject: [PATCH 07/12] stl: update libraries and tests --- Cargo.lock | 1 + Cargo.toml | 4 ++-- asset/armored_contract.default | 4 ++-- asset/armored_transfer.default | 4 ++-- src/containers/consignment.rs | 20 ++++++++++++-------- src/stl/stl.rs | 4 ++-- 6 files changed, 21 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 66107ae0..2566c3e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -661,6 +661,7 @@ dependencies = [ [[package]] name = "rgb-core" version = "0.11.0-beta.6" +source = "git+https://github.com/RGB-WG/rgb-core?branch=contract-state2#d06c627c846e9111a940aa84a8d5a670317d1f40" dependencies = [ "aluvm", "amplify", diff --git a/Cargo.toml b/Cargo.toml index e604444f..7346e8df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -99,5 +99,5 @@ features = ["all"] [patch.crates-io] bp-consensus = { git = "https://github.com/BP-WG/bp-core", branch = "master" } bp-invoice = { git = "https://github.com/BP-WG/bp-std.git", branch = "master" } -#rgb-core = { git = "https://github.com/RGB-WG/rgb-core", branch = "contract-state2" } -rgb-core = { path = "../rgb-core" } +rgb-core = { git = "https://github.com/RGB-WG/rgb-core", branch = "contract-state2" } +#rgb-core = { path = "../rgb-core" } diff --git a/asset/armored_contract.default b/asset/armored_contract.default index 1104f0d3..86b189f4 100644 --- a/asset/armored_contract.default +++ b/asset/armored_contract.default @@ -1,8 +1,8 @@ -----BEGIN RGB CONSIGNMENT----- -Id: rgb:csg:poAMvm9j-NdapxqA-MJ!5dwP-d!IIt2A-T!5OiXE-Tl54Yew#guide-campus-arctic +Id: rgb:csg:Kej4ueIS-4VLOUQe-SpFUOCe-BzEZ$Lt-C4PNmJC-J0Um7WM#cigar-pretend-mayor Version: 2 Type: contract -Contract: rgb:qm7P!06T-uuBQT56-ovwOLzx-9Gka7Nb-84Nwo8g-blLb8kw +Contract: rgb:T24t0N1D-eiInTgb-BXlrrXz-$7OgV6n-WJWHPUD-BWNuqZw Schema: rgb:sch:CyqM42yAdM1moWyNZPQedAYt73BM$k9z$dKLUXY1voA#cello-global-deluxe Check-SHA256: 181748dae0c83cbb44f6ccfdaddf6faca0bc4122a9f35fef47bab9aea023e4a1 diff --git a/asset/armored_transfer.default b/asset/armored_transfer.default index 1104f0d3..86b189f4 100644 --- a/asset/armored_transfer.default +++ b/asset/armored_transfer.default @@ -1,8 +1,8 @@ -----BEGIN RGB CONSIGNMENT----- -Id: rgb:csg:poAMvm9j-NdapxqA-MJ!5dwP-d!IIt2A-T!5OiXE-Tl54Yew#guide-campus-arctic +Id: rgb:csg:Kej4ueIS-4VLOUQe-SpFUOCe-BzEZ$Lt-C4PNmJC-J0Um7WM#cigar-pretend-mayor Version: 2 Type: contract -Contract: rgb:qm7P!06T-uuBQT56-ovwOLzx-9Gka7Nb-84Nwo8g-blLb8kw +Contract: rgb:T24t0N1D-eiInTgb-BXlrrXz-$7OgV6n-WJWHPUD-BWNuqZw Schema: rgb:sch:CyqM42yAdM1moWyNZPQedAYt73BM$k9z$dKLUXY1voA#cello-global-deluxe Check-SHA256: 181748dae0c83cbb44f6ccfdaddf6faca0bc4122a9f35fef47bab9aea023e4a1 diff --git a/src/containers/consignment.rs b/src/containers/consignment.rs index 03cf2f13..407c8b18 100644 --- a/src/containers/consignment.rs +++ b/src/containers/consignment.rs @@ -434,7 +434,11 @@ impl StrictArmor for Consignment { impl FromStr for Consignment { type Err = armor::StrictArmorError; - fn from_str(s: &str) -> Result { Self::from_ascii_armored_str(s) } + fn from_str(s: &str) -> Result { + Self::from_ascii_armored_str(s) + + // TODO: Ensure that contract.transfer == TRANSFER + } } #[cfg(test)] @@ -457,10 +461,10 @@ mod test { assert!( Contract::from_str( r#"-----BEGIN RGB CONSIGNMENT----- -Id: rgb:csg:poAMvm9j-NdapxqA-MJ!5dwP-d!IIt2A-T!5OiXE-Tl54Yew#guide-campus-arctic +Id: rgb:csg:Kej4ueIS-4VLOUQe-SpFUOCe-BzEZ$Lt-C4PNmJC-J0Um7WM#cigar-pretend-mayor Version: 2 Type: contract -Contract: rgb:qm7P!06T-uuBQT56-ovwOLzx-9Gka7Nb-84Nwo8g-blLb8kw +Contract: rgb:T24t0N1D-eiInTgb-BXlrrXz-$7OgV6n-WJWHPUD-BWNuqZw Schema: rgb:sch:CyqM42yAdM1moWyNZPQedAYt73BM$k9z$dKLUXY1voA#cello-global-deluxe Check-SHA256: 181748dae0c83cbb44f6ccfdaddf6faca0bc4122a9f35fef47bab9aea023e4a1 @@ -530,10 +534,10 @@ Check-SHA256: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa assert!( Transfer::from_str( r#"-----BEGIN RGB CONSIGNMENT----- -Id: rgb:csg:poAMvm9j-NdapxqA-MJ!5dwP-d!IIt2A-T!5OiXE-Tl54Yew#guide-campus-arctic +Id: rgb:csg:Kej4ueIS-4VLOUQe-SpFUOCe-BzEZ$Lt-C4PNmJC-J0Um7WM#cigar-pretend-mayor Version: 2 -Type: contract -Contract: rgb:qm7P!06T-uuBQT56-ovwOLzx-9Gka7Nb-84Nwo8g-blLb8kw +Type: transfer +Contract: rgb:T24t0N1D-eiInTgb-BXlrrXz-$7OgV6n-WJWHPUD-BWNuqZw Schema: rgb:sch:CyqM42yAdM1moWyNZPQedAYt73BM$k9z$dKLUXY1voA#cello-global-deluxe Check-SHA256: 181748dae0c83cbb44f6ccfdaddf6faca0bc4122a9f35fef47bab9aea023e4a1 @@ -552,7 +556,7 @@ Check-SHA256: 181748dae0c83cbb44f6ccfdaddf6faca0bc4122a9f35fef47bab9aea023e4a1 r#"-----BEGIN RGB CONSIGNMENT----- Id: rgb:csg:aaaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa#guide-campus-arctic Version: 2 -Type: contract +Type: transfer Contract: rgb:qm7P!06T-uuBQT56-ovwOLzx-9Gka7Nb-84Nwo8g-blLb8kw Schema: rgb:sch:CyqM42yAdM1moWyNZPQedAYt73BM$k9z$dKLUXY1voA#cello-global-deluxe Check-SHA256: 181748dae0c83cbb44f6ccfdaddf6faca0bc4122a9f35fef47bab9aea023e4a1 @@ -572,7 +576,7 @@ Check-SHA256: 181748dae0c83cbb44f6ccfdaddf6faca0bc4122a9f35fef47bab9aea023e4a1 r#"-----BEGIN RGB CONSIGNMENT----- Id: rgb:csg:poAMvm9j-NdapxqA-MJ!5dwP-d!IIt2A-T!5OiXE-Tl54Yew#guide-campus-arctic Version: 2 -Type: contract +Type: transfer Contract: rgb:qm7P!06T-uuBQT56-ovwOLzx-9Gka7Nb-84Nwo8g-blLb8kw Schema: rgb:sch:CyqM42yAdM1moWyNZPQedAYt73BM$k9z$dKLUXY1voA#cello-global-deluxe Check-SHA256: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa diff --git a/src/stl/stl.rs b/src/stl/stl.rs index 02f8aa3f..d1add581 100644 --- a/src/stl/stl.rs +++ b/src/stl/stl.rs @@ -41,7 +41,7 @@ use crate::LIB_NAME_RGB_STD; /// Strict types id for the library providing standard data types which may be /// used in RGB smart contracts. pub const LIB_ID_RGB_STORAGE: &str = - "stl:aHl1Tsu8-RxChWD4-DXqrqOO-qFlJXsK-0uWYfDv-H5BgmxE#plastic-shelter-podium"; + "stl:7oFQsHNj-DaUodoT-JEpv1QK-Px0BgqI-gdu6DzR-sY0XMwQ#sheriff-biology-social"; /// Strict types id for the library providing standard data types which may be /// used in RGB smart contracts. @@ -50,7 +50,7 @@ pub const LIB_ID_RGB_CONTRACT: &str = /// Strict types id for the library representing of RGB StdLib data types. pub const LIB_ID_RGB_STD: &str = - "stl:w4Hvo8zW-bFpTGAI-W6Zn$9u-qqXq$cI-ef0vzlQ-hg!AEZs#carol-edgar-escape"; + "stl:R5kQAOh4-zB20Wn0-2z3MtK!-dNvcxhT-nZOmQtF-DWVziK4#forget-radar-macro"; fn _rgb_std_stl() -> Result { LibBuilder::new(libname!(LIB_NAME_RGB_STD), tiny_bset! { From bc2ec1c22a4bfe3e4b394b9f133a6ef9cb6082a3 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Thu, 1 Aug 2024 15:00:54 +0200 Subject: [PATCH 08/12] containers: refactor Consignment terminals to contain only secret seals terminals were used before in RGB Core. With change of the validation algorithm they are used just in standard library to resolve secret seal. This change remove unnecessary data from the terminals structure. --- Cargo.toml | 1 - src/containers/consignment.rs | 47 ++++++++++++----------------- src/containers/mod.rs | 5 ++- src/containers/seal.rs | 57 ----------------------------------- src/containers/seals.md | 17 ++++++----- src/containers/util.rs | 48 +++-------------------------- src/persistence/stash.rs | 12 +++----- src/persistence/stock.rs | 36 ++++++---------------- 8 files changed, 47 insertions(+), 176 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7346e8df..1bb6562e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -100,4 +100,3 @@ features = ["all"] bp-consensus = { git = "https://github.com/BP-WG/bp-core", branch = "master" } bp-invoice = { git = "https://github.com/BP-WG/bp-std.git", branch = "master" } rgb-core = { git = "https://github.com/RGB-WG/rgb-core", branch = "contract-state2" } -#rgb-core = { path = "../rgb-core" } diff --git a/src/containers/consignment.rs b/src/containers/consignment.rs index 407c8b18..8ca1f078 100644 --- a/src/containers/consignment.rs +++ b/src/containers/consignment.rs @@ -43,9 +43,9 @@ use strict_encoding::{StrictDeserialize, StrictDumb, StrictSerialize}; use strict_types::TypeSystem; use super::{ - BundledWitness, ContainerVer, ContentId, ContentSigs, IndexedConsignment, Supplement, Terminal, - TerminalDisclose, ASCII_ARMOR_CONSIGNMENT_TYPE, ASCII_ARMOR_CONTRACT, ASCII_ARMOR_IFACE, - ASCII_ARMOR_SCHEMA, ASCII_ARMOR_TERMINAL, ASCII_ARMOR_VERSION, + BundledWitness, ContainerVer, ContentId, ContentSigs, IndexedConsignment, Supplement, + ASCII_ARMOR_CONSIGNMENT_TYPE, ASCII_ARMOR_CONTRACT, ASCII_ARMOR_IFACE, ASCII_ARMOR_SCHEMA, + ASCII_ARMOR_TERMINAL, ASCII_ARMOR_VERSION, }; use crate::interface::{Iface, IfaceImpl}; use crate::persistence::{MemContract, MemContractState}; @@ -185,8 +185,8 @@ pub struct Consignment { /// contract. pub transfer: bool, - /// Set of seals which are history terminals. - pub terminals: SmallOrdMap, + /// Set of secret seals which are history terminals. + pub terminals: SmallOrdMap>, /// Genesis data. pub genesis: Genesis, @@ -246,7 +246,7 @@ impl CommitEncode for Consignment { e.commit_to_set(&LargeOrdSet::from_iter_unsafe( self.extensions.iter().map(Extension::disclose_hash), )); - e.commit_to_set(&SmallOrdSet::from_iter_unsafe(self.terminals_disclose())); + e.commit_to_map(&self.terminals); e.commit_to_set(&SmallOrdSet::from_iter_unsafe(self.attachments.keys().copied())); e.commit_to_set(&TinyOrdSet::from_iter_unsafe( @@ -287,36 +287,27 @@ impl Consignment { #[inline] pub fn schema_id(&self) -> SchemaId { self.schema.schema_id() } - pub fn terminal_secrets(&self) -> impl Iterator)> { - self.terminals - .clone() - .into_iter() - .flat_map(|(id, term)| term.secrets().map(move |secret| (id, secret))) - } - - pub fn terminals_disclose(&self) -> impl Iterator + '_ { - self.terminals.iter().flat_map(|(id, term)| { - term.seals.iter().map(|seal| TerminalDisclose { - bundle_id: *id, - seal: *seal, - }) - }) - } - - #[must_use] - pub fn reveal_bundle_seal(mut self, bundle_id: BundleId, revealed: XChain) -> Self { + pub fn reveal_terminal_seals( + mut self, + f: impl Fn(XChain) -> Result>, E>, + ) -> Result { // We need to clone since ordered set does not allow us to mutate members. let mut bundles = LargeOrdSet::with_capacity(self.bundles.len()); for mut bundled_witness in self.bundles { - for bundle in bundled_witness.anchored_bundles.bundles_mut() { - if bundle.bundle_id() == bundle_id { - bundle.reveal_seal(revealed); + for (bundle_id, secret) in &self.terminals { + if let Some(seal) = f(*secret)? { + for bundle in bundled_witness.anchored_bundles.bundles_mut() { + if bundle.bundle_id() == *bundle_id { + bundle.reveal_seal(seal); + break; + } + } } } bundles.push(bundled_witness).ok(); } self.bundles = bundles; - self + Ok(self) } pub fn into_contract(self) -> Contract { diff --git a/src/containers/mod.rs b/src/containers/mod.rs index d85f0d9f..85ede3c6 100644 --- a/src/containers/mod.rs +++ b/src/containers/mod.rs @@ -53,15 +53,14 @@ pub use kit::{Kit, KitId, ValidKit}; pub use partials::{ Batch, BundleDichotomy, CloseMethodSet, Fascia, TransitionInfo, TransitionInfoError, }; -pub use seal::{BuilderSeal, TerminalSeal, VoutSeal}; +pub use seal::{BuilderSeal, VoutSeal}; pub use suppl::{ AnnotationName, Annotations, ContentRef, SupplId, SupplItem, SupplMap, SupplSub, Supplement, TickerSuppl, VelocityHint, SUPPL_ANNOT_IFACE_CLASS, SUPPL_ANNOT_IFACE_FEATURES, SUPPL_ANNOT_VELOCITY, }; pub use util::{ - ContainerVer, ContentId, ContentSigs, DumbValidator, SigBlob, SigValidator, Terminal, - TerminalDisclose, TrustLevel, + ContainerVer, ContentId, ContentSigs, DumbValidator, SigBlob, SigValidator, TrustLevel, }; pub const ASCII_ARMOR_NAME: &str = "Name"; diff --git a/src/containers/seal.rs b/src/containers/seal.rs index 8c810d86..49dc3079 100644 --- a/src/containers/seal.rs +++ b/src/containers/seal.rs @@ -24,7 +24,6 @@ use bp::seals::txout::{BlindSeal, CloseMethod, SealTxid}; use bp::secp256k1::rand::{thread_rng, RngCore}; use bp::Vout; -use commit_verify::Conceal; use rgb::{GraphSeal, Layer1, SecretSeal, TxoSeal, XChain}; use crate::LIB_NAME_RGB_STD; @@ -108,62 +107,6 @@ impl From for GraphSeal { } } -/// Seal endpoint is a confidential seal which may be linked to the witness -/// transaction, but does not contain information about its id. -/// -/// Seal endpoint can be either a pointer to the output in the witness -/// transaction, plus blinding factor value, or a confidential seal -/// [`SecretSeal`] value pointing some external unknown transaction -/// output -/// -/// Seal endpoint is required in situations where sender assigns state to the -/// witness transaction output on behalf of receiver -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, From)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD, tags = custom, dumb = Self::ConcealedUtxo(strict_dumb!()))] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub enum TerminalSeal { - /// External transaction output in concealed form (see [`SecretSeal`]) - #[from] - #[strict_type(tag = 0)] - ConcealedUtxo(SecretSeal), - - /// Seal contained within the witness transaction - #[from] - #[strict_type(tag = 1)] - WitnessVout(VoutSeal), -} - -impl TerminalSeal { - /// Constructs [`TerminalSeal`] for the witness transaction. Uses - /// `thread_rng` to initialize blinding factor. - pub fn new_vout(method: CloseMethod, vout: impl Into) -> TerminalSeal { - TerminalSeal::WitnessVout(VoutSeal::new(method, vout)) - } - - pub fn secret_seal(&self) -> Option { - match self { - TerminalSeal::ConcealedUtxo(seal) => Some(*seal), - TerminalSeal::WitnessVout(_) => None, - } - } -} - -impl Conceal for TerminalSeal { - type Concealed = SecretSeal; - - fn conceal(&self) -> Self::Concealed { - match *self { - TerminalSeal::ConcealedUtxo(hash) => hash, - TerminalSeal::WitnessVout(seal) => GraphSeal::from(seal).conceal(), - } - } -} - /// Seal used by operation builder which can be either revealed or concealed. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, From)] pub enum BuilderSeal { diff --git a/src/containers/seals.md b/src/containers/seals.md index 0c066771..3136a22f 100644 --- a/src/containers/seals.md +++ b/src/containers/seals.md @@ -14,15 +14,16 @@ Single-use-seals in RGB can have multiple forms because of the confidentiality options and ability to be linked to the witness transaction closing previous seal in RGB state evolution graph. -| **Type name** | **Lib** | **Txid** | **Blinding** | **Private** | **String serialization** | **Use case** | -|------------------|---------| --------- | ------------ |-------------|-----------------------------------------|---------------| -| [`Outpoint`] | BP Core | Required | No | No | `:` | Genesis | -| [`BlindSeal`] | BP Core | Required | Yes | No | `:</~>:#` | Stash | -| [`SecretSeal`] | BP Core | Unknown | Implicit | Yes | `txob:#` | Ext. payments | -| [`ExplicitSeal`] | BP Core | Optional | Yes | No | `:</~>:` | Internal | -| [`VoutSeal`] | RGB Std | Absent | Yes | No | `:~:#` | SealEndpoint | -| [`TerminalSeal`] | RGB Std | Optional | Varies | Can be | `/` | Consignments | +| **Type name** | **Lib** | **Txid** | **Blinding** | **Private** | **String serialization** | **Use case** | +|------------------|---------|----------|--------------|-------------|-----------------------------------------|---------------| +| [`Outpoint`] | BP Core | Required | No | No | `:` | Genesis | +| [`BlindSeal`] | BP Core | Required | Yes | No | `:</~>:#` | Stash | +| [`SecretSeal`] | BP Core | Unknown | Implicit | Yes | `txob:#` | Ext. payments | +| [`ExplicitSeal`] | BP Core | Optional | Yes | No | `:</~>:` | Internal | +| [`VoutSeal`] | RGB Std | Absent | Yes | No | `:~:#` | SealEndpoint | [`Outpoint`]: bp::Outpoint + [`BlindSeal`]: bp::seals::txout::blind::BlindSeal + [`ExplicitSeal`]: bp::seals::txout::ExplicitSeal diff --git a/src/containers/util.rs b/src/containers/util.rs index b53b217c..0e73a450 100644 --- a/src/containers/util.rs +++ b/src/containers/util.rs @@ -21,54 +21,14 @@ use std::collections::{btree_map, BTreeMap}; -use amplify::confinement::{Confined, NonEmptyBlob, SmallOrdSet}; +use amplify::confinement::{Confined, NonEmptyBlob}; use commit_verify::StrictHash; -use rgb::{BundleId, ContractId, Identity, SchemaId, XChain}; +use rgb::{ContractId, Identity, SchemaId}; use strict_encoding::StrictDumb; -use super::{SupplId, TerminalSeal}; +use super::SupplId; use crate::interface::{IfaceId, ImplId}; -use crate::{SecretSeal, LIB_NAME_RGB_STD}; - -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub struct TerminalDisclose { - pub bundle_id: BundleId, - pub seal: XChain, -} - -#[derive(Clone, Eq, PartialEq, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub struct Terminal { - pub seals: SmallOrdSet>, -} - -impl Terminal { - pub fn new(seal: XChain) -> Self { - Terminal { - seals: small_bset![seal], - } - } - - pub fn secrets(&self) -> impl Iterator> { - self.seals - .clone() - .into_iter() - .filter_map(|seal| seal.map_ref(TerminalSeal::secret_seal).transpose()) - } -} +use crate::LIB_NAME_RGB_STD; #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Default)] #[derive(StrictType, StrictEncode, StrictDecode)] diff --git a/src/persistence/stash.rs b/src/persistence/stash.rs index 32f99a46..ace8a820 100644 --- a/src/persistence/stash.rs +++ b/src/persistence/stash.rs @@ -466,15 +466,11 @@ impl Stash

{ &self, mut consignment: Consignment, ) -> Result, StashError

> { - for (bundle_id, secret) in consignment.terminal_secrets() { - if let Some(seal) = self - .provider + consignment = consignment.reveal_terminal_seals(|secret| { + self.provider .seal_secret(secret) - .map_err(StashError::ReadProvider)? - { - consignment = consignment.reveal_bundle_seal(bundle_id, seal); - } - } + .map_err(StashError::ReadProvider) + })?; Ok(consignment) } diff --git a/src/persistence/stock.rs b/src/persistence/stock.rs index 6bdb663b..9e2ef29e 100644 --- a/src/persistence/stock.rs +++ b/src/persistence/stock.rs @@ -31,7 +31,6 @@ use amplify::Wrapper; use bp::seals::txout::CloseMethod; use bp::Vout; use chrono::Utc; -use commit_verify::Conceal; use invoice::{Amount, Beneficiary, InvoiceState, NonFungible, RgbInvoice}; use rgb::validation::{DbcProof, EAnchor, ResolveWitness, WitnessResolverError}; use rgb::{ @@ -49,9 +48,9 @@ use super::{ }; use crate::containers::{ AnchorSet, AnchoredBundles, Batch, BuilderSeal, BundledWitness, Consignment, ContainerVer, - ContentId, ContentRef, Contract, Fascia, Kit, SealWitness, SupplItem, SupplSub, Terminal, - TerminalSeal, Transfer, TransitionInfo, TransitionInfoError, ValidConsignment, ValidContract, - ValidKit, ValidTransfer, VelocityHint, SUPPL_ANNOT_VELOCITY, + ContentId, ContentRef, Contract, Fascia, Kit, SealWitness, SupplItem, SupplSub, Transfer, + TransitionInfo, TransitionInfoError, ValidConsignment, ValidContract, ValidKit, ValidTransfer, + VelocityHint, SUPPL_ANNOT_VELOCITY, }; use crate::info::{ContractInfo, IfaceInfo, SchemaInfo}; use crate::interface::{ @@ -162,9 +161,6 @@ pub enum ConsignError { /// too many transitions. TooManyBundles, - /// public state at operation output {0} is concealed. - ConcealedPublicState(Opout), - #[from] #[display(inner)] MergeReveal(MergeRevealError), @@ -764,7 +760,7 @@ impl Stock { // 1.3. Collect all state transitions assigning state to the provided outpoints let mut bundled_witnesses = BTreeMap::::new(); let mut transitions = BTreeMap::::new(); - let mut terminals = BTreeMap::::new(); + let mut terminals = BTreeMap::>::new(); for opout in opouts { if opout.op == contract_id { continue; // we skip genesis since it will be present anywhere @@ -774,28 +770,14 @@ impl Stock { transitions.insert(opout.op, transition.clone()); let bundle_id = self.index.bundle_id_for_op(transition.id())?; - // 2. Collect seals from terminal transitions to add to the consignment + // 2. Collect secret seals from terminal transitions to add to the consignment // terminals - for (type_id, typed_assignments) in transition.assignments.iter() { + for typed_assignments in transition.assignments.values() { for index in 0..typed_assignments.len_u16() { let seal = typed_assignments.to_confidential_seals()[index as usize]; if secret_seals.contains(&seal) { - terminals - .entry(bundle_id) - .or_insert(Terminal::new(seal.map(TerminalSeal::from))) - .seals - .push(seal.map(TerminalSeal::from)) - .map_err(|_| ConsignError::TooManyTerminals)?; - } else if opout.no == index && opout.ty == *type_id { - if let Some(seal) = typed_assignments - .revealed_seal_at(index) - .expect("index exists") - { - let seal = seal.map(|s| s.conceal()).map(TerminalSeal::from); - terminals.insert(bundle_id, Terminal::new(seal)); - } else { - return Err(ConsignError::ConcealedPublicState(opout).into()); - } + let res = terminals.insert(bundle_id, seal); + assert_eq!(res, None); } } } @@ -1313,7 +1295,7 @@ mod test { use std::str::FromStr; use baid64::FromBaid64Str; - use commit_verify::{DigestExt, Sha256}; + use commit_verify::{Conceal, DigestExt, Sha256}; use strict_encoding::TypeName; use super::*; From 014ebdf486c6936447d17d6c4b2f194024739f4a Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Thu, 1 Aug 2024 15:55:14 +0200 Subject: [PATCH 09/12] containers: fix consignment checks --- asset/armored_transfer.default | 8 +-- asset/transfer.default | Bin 145 -> 145 bytes src/containers/consignment.rs | 104 ++++++++++++++++++++------------- src/containers/file.rs | 29 +++------ src/containers/mod.rs | 4 +- src/persistence/stock.rs | 6 +- 6 files changed, 79 insertions(+), 72 deletions(-) diff --git a/asset/armored_transfer.default b/asset/armored_transfer.default index 86b189f4..3cd29dcc 100644 --- a/asset/armored_transfer.default +++ b/asset/armored_transfer.default @@ -1,12 +1,12 @@ -----BEGIN RGB CONSIGNMENT----- -Id: rgb:csg:Kej4ueIS-4VLOUQe-SpFUOCe-BzEZ$Lt-C4PNmJC-J0Um7WM#cigar-pretend-mayor +Id: rgb:csg:9jMKgkmP-alPghZC-bu65ctP-GT5tKgM-cAbaTLT-rhu8xQo#urban-athena-adam Version: 2 -Type: contract +Type: transfer Contract: rgb:T24t0N1D-eiInTgb-BXlrrXz-$7OgV6n-WJWHPUD-BWNuqZw Schema: rgb:sch:CyqM42yAdM1moWyNZPQedAYt73BM$k9z$dKLUXY1voA#cello-global-deluxe -Check-SHA256: 181748dae0c83cbb44f6ccfdaddf6faca0bc4122a9f35fef47bab9aea023e4a1 +Check-SHA256: 562a944631243e23a8de1d2aa2a5621be13351fc6f4d9aa8127c12ac4fb54d97 -0ssI2000000000000000000000000000000000000000000000000000000D0CRI`I$>^aZh38Qb#nj! +0s#O3000000000000000000000000000000000000000000000000000000D0CRI`I$>^aZh38Qb#nj! 0000000000000000000000d59ZDjxe00000000dDb8~4rVQz13d2MfXa{vGU00000000000000000000 0000000000000 diff --git a/asset/transfer.default b/asset/transfer.default index 2ae06b2d2c6de3d7ea33512f70115159a87054c5..5685f5e9a3e3155df8b77b179518b100bfecf734 100644 GIT binary patch delta 23 ecmbQpIFXSv$lZw{#4U)4aiXB=L StrictArmor for Consignment { } headers } + fn parse_armor_headers(&mut self, headers: Vec) -> Result<(), StrictArmorError> { + // TODO: Check remaining headers - terminals, version, iface, contract, schema + if let Some(header) = headers + .iter() + .find(|header| header.title == ASCII_ARMOR_CONSIGNMENT_TYPE) + { + if self.transfer && header.values.len() != 1 && header.values[0] != "transfer" { + // TODO: Add header-specific errors to StrictArmorError + // return Err(Strict) + } + } + Ok(()) + } +} + +// TODO: Remove after header-specific variants are added to StrictArmorError +#[derive(Debug, Display, Error, From)] +pub enum ConsignmentParseError { + #[display(inner)] + #[from] + Armor(armor::StrictArmorError), + + #[display("required consignment type doesn't match the actual type")] + Type, } impl FromStr for Consignment { - type Err = armor::StrictArmorError; + type Err = ConsignmentParseError; fn from_str(s: &str) -> Result { - Self::from_ascii_armored_str(s) + let consignment = Self::from_ascii_armored_str(s)?; - // TODO: Ensure that contract.transfer == TRANSFER + if consignment.transfer != TRANSFER { + return Err(ConsignmentParseError::Type); + } + + Ok(consignment) } } @@ -438,35 +466,20 @@ mod test { #[test] fn contract_str_round_trip() { - let contract = Contract::from_str(include_str!("../../asset/armored_contract.default")) + let mut contract = Contract::from_str(include_str!("../../asset/armored_contract.default")) .expect("contract from str should work"); assert_eq!( contract.to_string(), include_str!("../../asset/armored_contract.default"), "contract string round trip fails" ); + contract.transfer = true; + eprintln!("{contract}"); } #[test] fn error_contract_strs() { - assert!( - Contract::from_str( - r#"-----BEGIN RGB CONSIGNMENT----- -Id: rgb:csg:Kej4ueIS-4VLOUQe-SpFUOCe-BzEZ$Lt-C4PNmJC-J0Um7WM#cigar-pretend-mayor -Version: 2 -Type: contract -Contract: rgb:T24t0N1D-eiInTgb-BXlrrXz-$7OgV6n-WJWHPUD-BWNuqZw -Schema: rgb:sch:CyqM42yAdM1moWyNZPQedAYt73BM$k9z$dKLUXY1voA#cello-global-deluxe -Check-SHA256: 181748dae0c83cbb44f6ccfdaddf6faca0bc4122a9f35fef47bab9aea023e4a1 - -0ssI2000000000000000000000000000000000000000000000000000000D0CRI`I$>^aZh38Qb#nj! -0000000000000000000000d59ZDjxe00000000dDb8~4rVQz13d2MfXa{vGU00000000000000000000 -0000000000000 - ------END RGB CONSIGNMENT-----"# - ) - .is_ok() - ); + assert!(Contract::from_str(include_str!("../../asset/armored_contract.default")).is_ok()); // Wrong Id assert!( @@ -522,37 +535,41 @@ Check-SHA256: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa #[test] fn error_transfer_strs() { + let s = include_str!("../../asset/armored_transfer.default"); + assert!(matches!(Transfer::from_str(s), Ok(_))); + + // Wrong Id assert!( Transfer::from_str( r#"-----BEGIN RGB CONSIGNMENT----- -Id: rgb:csg:Kej4ueIS-4VLOUQe-SpFUOCe-BzEZ$Lt-C4PNmJC-J0Um7WM#cigar-pretend-mayor +Id: rgb:csg:aaaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa#guide-campus-arctic Version: 2 Type: transfer Contract: rgb:T24t0N1D-eiInTgb-BXlrrXz-$7OgV6n-WJWHPUD-BWNuqZw Schema: rgb:sch:CyqM42yAdM1moWyNZPQedAYt73BM$k9z$dKLUXY1voA#cello-global-deluxe -Check-SHA256: 181748dae0c83cbb44f6ccfdaddf6faca0bc4122a9f35fef47bab9aea023e4a1 +Check-SHA256: 562a944631243e23a8de1d2aa2a5621be13351fc6f4d9aa8127c12ac4fb54d97 -0ssI2000000000000000000000000000000000000000000000000000000D0CRI`I$>^aZh38Qb#nj! +0s#O3000000000000000000000000000000000000000000000000000000D0CRI`I$>^aZh38Qb#nj! 0000000000000000000000d59ZDjxe00000000dDb8~4rVQz13d2MfXa{vGU00000000000000000000 0000000000000 -----END RGB CONSIGNMENT-----"# ) - .is_ok() + .is_err() ); - // Wrong Id + // Wrong checksum assert!( Transfer::from_str( r#"-----BEGIN RGB CONSIGNMENT----- -Id: rgb:csg:aaaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa#guide-campus-arctic +Id: rgb:csg:9jMKgkmP-alPghZC-bu65ctP-GT5tKgM-cAbaTLT-rhu8xQo#urban-athena-adam Version: 2 Type: transfer -Contract: rgb:qm7P!06T-uuBQT56-ovwOLzx-9Gka7Nb-84Nwo8g-blLb8kw +Contract: rgb:T24t0N1D-eiInTgb-BXlrrXz-$7OgV6n-WJWHPUD-BWNuqZw Schema: rgb:sch:CyqM42yAdM1moWyNZPQedAYt73BM$k9z$dKLUXY1voA#cello-global-deluxe -Check-SHA256: 181748dae0c83cbb44f6ccfdaddf6faca0bc4122a9f35fef47bab9aea023e4a1 +Check-SHA256: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -0ssI2000000000000000000000000000000000000000000000000000000D0CRI`I$>^aZh38Qb#nj! +0s#O3000000000000000000000000000000000000000000000000000000D0CRI`I$>^aZh38Qb#nj! 0000000000000000000000d59ZDjxe00000000dDb8~4rVQz13d2MfXa{vGU00000000000000000000 0000000000000 @@ -561,24 +578,29 @@ Check-SHA256: 181748dae0c83cbb44f6ccfdaddf6faca0bc4122a9f35fef47bab9aea023e4a1 .is_err() ); - // Wrong checksum - assert!( + // Wrong type + // TODO: Uncomment once ASCII headers get checked + /*assert!(matches!( Transfer::from_str( r#"-----BEGIN RGB CONSIGNMENT----- -Id: rgb:csg:poAMvm9j-NdapxqA-MJ!5dwP-d!IIt2A-T!5OiXE-Tl54Yew#guide-campus-arctic +Id: rgb:csg:9jMKgkmP-alPghZC-bu65ctP-GT5tKgM-cAbaTLT-rhu8xQo#urban-athena-adam Version: 2 -Type: transfer -Contract: rgb:qm7P!06T-uuBQT56-ovwOLzx-9Gka7Nb-84Nwo8g-blLb8kw +Type: contract +Contract: rgb:T24t0N1D-eiInTgb-BXlrrXz-$7OgV6n-WJWHPUD-BWNuqZw Schema: rgb:sch:CyqM42yAdM1moWyNZPQedAYt73BM$k9z$dKLUXY1voA#cello-global-deluxe Check-SHA256: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -0ssI2000000000000000000000000000000000000000000000000000000D0CRI`I$>^aZh38Qb#nj! +0s#O3000000000000000000000000000000000000000000000000000000D0CRI`I$>^aZh38Qb#nj! 0000000000000000000000d59ZDjxe00000000dDb8~4rVQz13d2MfXa{vGU00000000000000000000 0000000000000 -----END RGB CONSIGNMENT-----"# - ) - .is_err() - ); + ), + Err(ConsignmentParseError::Type) + ));*/ + assert!(matches!( + Transfer::from_str(include_str!("../../asset/armored_contract.default")), + Err(ConsignmentParseError::Type) + )); } } diff --git a/src/containers/file.rs b/src/containers/file.rs index 78d02ef3..220299c9 100644 --- a/src/containers/file.rs +++ b/src/containers/file.rs @@ -358,7 +358,7 @@ mod test { fn almost_default_transfer() -> Transfer { Transfer { version: Default::default(), - transfer: Default::default(), + transfer: true, terminals: Default::default(), genesis: rgb::Genesis { ffv: Default::default(), @@ -406,39 +406,26 @@ mod test { #[test] fn transfer_save_load_round_trip() { - let mut transfer_file = OpenOptions::new() - .read(true) - .open(DEFAULT_TRANSFER_PATH) - .unwrap(); - let transfer = Transfer::load(transfer_file).expect("fail to load transfer.default"); + let transfer = + Transfer::load_file(DEFAULT_TRANSFER_PATH).expect("fail to load transfer.default"); let default_transfer = almost_default_transfer(); assert_eq!(&transfer, &default_transfer, "transfer default is not same as before"); - transfer_file = OpenOptions::new() - .write(true) - .open(DEFAULT_TRANSFER_PATH) - .unwrap(); default_transfer - .save(transfer_file) + .save_file(DEFAULT_TRANSFER_PATH) .expect("fail to export transfer"); - transfer_file = OpenOptions::new() - .read(true) - .open(DEFAULT_TRANSFER_PATH) - .unwrap(); - let transfer = Transfer::load(transfer_file).expect("fail to load transfer.default"); + let transfer = + Transfer::load_file(DEFAULT_TRANSFER_PATH).expect("fail to load transfer.default"); assert_eq!(&transfer, &default_transfer, "transfer roudtrip does not work"); } #[cfg(feature = "fs")] #[test] fn armored_transfer_save_load_round_trip() { - let transfer_file = OpenOptions::new() - .read(true) - .open(DEFAULT_TRANSFER_PATH) - .unwrap(); - let transfer = Transfer::load(transfer_file).expect("fail to load transfer.default"); + let transfer = + Transfer::load_file(DEFAULT_TRANSFER_PATH).expect("fail to load transfer.default"); let unarmored_transfer = Transfer::load_armored(ARMORED_TRANSFER_PATH).expect("fail to export armored transfer"); assert_eq!(transfer, unarmored_transfer, "transfer unarmored is not the same"); diff --git a/src/containers/mod.rs b/src/containers/mod.rs index 85ede3c6..95dabf62 100644 --- a/src/containers/mod.rs +++ b/src/containers/mod.rs @@ -43,8 +43,8 @@ pub use anchors::{ AnchorSet, AnchoredBundles, BundledWitness, PubWitness, SealWitness, ToWitnessId, XPubWitness, }; pub use consignment::{ - Consignment, ConsignmentExt, ConsignmentId, Contract, Transfer, ValidConsignment, - ValidContract, ValidTransfer, + Consignment, ConsignmentExt, ConsignmentId, ConsignmentParseError, Contract, Transfer, + ValidConsignment, ValidContract, ValidTransfer, }; pub use disclosure::Disclosure; pub use file::{FileContent, LoadError, UniversalFile}; diff --git a/src/persistence/stock.rs b/src/persistence/stock.rs index 9e2ef29e..a891a742 100644 --- a/src/persistence/stock.rs +++ b/src/persistence/stock.rs @@ -704,8 +704,7 @@ impl Stock { &self, contract_id: ContractId, ) -> Result> { - let mut consignment = self.consign::(contract_id, [], [])?; - consignment.transfer = false; + let consignment = self.consign::(contract_id, [], [])?; Ok(consignment) } @@ -715,8 +714,7 @@ impl Stock { outputs: impl AsRef<[XOutputSeal]>, secret_seals: impl AsRef<[XChain]>, ) -> Result> { - let mut consignment = self.consign(contract_id, outputs, secret_seals)?; - consignment.transfer = true; + let consignment = self.consign(contract_id, outputs, secret_seals)?; Ok(consignment) } From 1b8caf826abe7f2102044373f6d739046767e8b0 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Thu, 1 Aug 2024 16:59:57 +0200 Subject: [PATCH 10/12] persistance: allow assigning custom priority for nonce from RCP240731A --- src/containers/partials.rs | 7 +++++++ src/persistence/stock.rs | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/containers/partials.rs b/src/containers/partials.rs index 95a40efa..b92dcaca 100644 --- a/src/containers/partials.rs +++ b/src/containers/partials.rs @@ -218,6 +218,13 @@ impl Batch { self.blanks.iter().for_each(|i| methods |= i.method); methods } + + pub fn set_priority(&mut self, priority: u8) { + self.main.transition.nonce = priority; + for mut info in &mut self.blanks { + info.transition.nonce = priority; + } + } } #[derive(Clone, PartialEq, Eq, Debug)] diff --git a/src/persistence/stock.rs b/src/persistence/stock.rs index a891a742..370434a0 100644 --- a/src/persistence/stock.rs +++ b/src/persistence/stock.rs @@ -909,6 +909,7 @@ impl Stock { prev_outputs, method, beneficiary_vout, + u8::MAX, allocator, |_, _| BlindingFactor::random(), |_, _| rand::random(), @@ -925,6 +926,7 @@ impl Stock { prev_outputs: impl IntoIterator>, method: CloseMethod, beneficiary_vout: Option>, + priority: u8, allocator: impl Fn(ContractId, AssignmentType, VelocityHint) -> Option, pedersen_blinder: impl Fn(ContractId, AssignmentType) -> BlindingFactor, seal_blinder: impl Fn(ContractId, AssignmentType) -> u64, @@ -1109,7 +1111,9 @@ impl Stock { let main = TransitionInfo::new(main_transition, main_inputs) .map_err(|_| ComposeError::TooManyInputs)?; - Ok(Batch { main, blanks }) + let mut batch = Batch { main, blanks }; + batch.set_priority(priority); + Ok(batch) } fn store_transaction( From 220c593a798395696a57f8e8d14aef309490701f Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Wed, 31 Jul 2024 23:34:15 +0200 Subject: [PATCH 11/12] persistence: allow multiple witnesses per bundle --- src/persistence/index.rs | 12 ++---------- src/persistence/memory.rs | 39 ++++++++++++++++++--------------------- src/persistence/state.rs | 23 +++++++++++++++++++++++ src/persistence/stock.rs | 3 ++- 4 files changed, 45 insertions(+), 32 deletions(-) diff --git a/src/persistence/index.rs b/src/persistence/index.rs index ffe55bd4..85c5c715 100644 --- a/src/persistence/index.rs +++ b/src/persistence/index.rs @@ -101,14 +101,6 @@ pub enum IndexInconsistency { /// outpoint {0} is not part of the contract {1}. OutpointUnknown(XOutputSeal, ContractId), - /// index already contains information about bundle {bundle_id} which - /// specifies witness {present} instead of witness {expected}. - DistinctBundleWitness { - bundle_id: BundleId, - present: XWitnessId, - expected: XWitnessId, - }, - /// index already contains information about bundle {bundle_id} which /// specifies contract {present} instead of contract {expected}. DistinctBundleContract { @@ -336,7 +328,7 @@ impl Index

{ pub(super) fn bundle_info( &self, bundle_id: BundleId, - ) -> Result<(XWitnessId, ContractId), IndexError

> { + ) -> Result<(impl Iterator + '_, ContractId), IndexError

> { Ok(self.provider.bundle_info(bundle_id)?) } } @@ -390,7 +382,7 @@ pub trait IndexReadProvider { fn bundle_info( &self, bundle_id: BundleId, - ) -> Result<(XWitnessId, ContractId), IndexReadError>; + ) -> Result<(impl Iterator, ContractId), IndexReadError>; } pub trait IndexWriteProvider: StoreTransaction { diff --git a/src/persistence/memory.rs b/src/persistence/memory.rs index 58d81cfb..ef6086f6 100644 --- a/src/persistence/memory.rs +++ b/src/persistence/memory.rs @@ -504,6 +504,14 @@ impl StateReadProvider for MemState { .collect(); Ok(MemContract { filter, unfiltered }) } + + fn is_valid_witness(&self, witness_id: XWitnessId) -> Result { + let ord = self + .witnesses + .get(&witness_id) + .ok_or(StateInconsistency::AbsentValidWitness)?; + Ok(ord.is_valid()) + } } impl StateWriteProvider for MemState { @@ -1137,7 +1145,7 @@ pub struct MemIndex { op_bundle_index: MediumOrdMap, bundle_contract_index: MediumOrdMap, - bundle_witness_index: MediumOrdMap, + bundle_witness_index: MediumOrdMap>, contract_index: TinyOrdMap, terminal_index: MediumOrdMap, Opout>, } @@ -1241,8 +1249,8 @@ impl IndexReadProvider for MemIndex { fn bundle_info( &self, bundle_id: BundleId, - ) -> Result<(XWitnessId, ContractId), IndexReadError> { - let witness_id = self + ) -> Result<(impl Iterator, ContractId), IndexReadError> { + let witness_ids = self .bundle_witness_index .get(&bundle_id) .ok_or(IndexInconsistency::BundleWitnessUnknown(bundle_id))?; @@ -1250,7 +1258,7 @@ impl IndexReadProvider for MemIndex { .bundle_contract_index .get(&bundle_id) .ok_or(IndexInconsistency::BundleContractUnknown(bundle_id))?; - Ok((*witness_id, *contract_id)) + Ok((witness_ids.iter().copied(), *contract_id)) } } @@ -1272,18 +1280,6 @@ impl IndexWriteProvider for MemIndex { witness_id: XWitnessId, contract_id: ContractId, ) -> Result> { - if let Some(alt) = self - .bundle_witness_index - .get(&bundle_id) - .filter(|alt| *alt != &witness_id) - { - return Err(IndexInconsistency::DistinctBundleWitness { - bundle_id, - present: *alt, - expected: witness_id, - } - .into()); - } if let Some(alt) = self .bundle_contract_index .get(&bundle_id) @@ -1296,16 +1292,17 @@ impl IndexWriteProvider for MemIndex { } .into()); } - let present1 = self + let mut set = self .bundle_witness_index - .insert(bundle_id, witness_id)? - .is_some(); + .remove(&bundle_id)? + .unwrap_or_default(); + set.push(witness_id)?; + self.bundle_witness_index.insert(bundle_id, set)?; let present2 = self .bundle_contract_index .insert(bundle_id, contract_id)? .is_some(); - debug_assert_eq!(present1, present2); - Ok(!present1) + Ok(!present2) } fn register_operation( diff --git a/src/persistence/state.rs b/src/persistence/state.rs index 319fe8d6..8f3d586a 100644 --- a/src/persistence/state.rs +++ b/src/persistence/state.rs @@ -19,6 +19,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::borrow::Borrow; use std::collections::BTreeMap; use std::error::Error; use std::fmt::Debug; @@ -64,6 +65,9 @@ pub enum StateError { pub enum StateInconsistency { /// contract state {0} is not known. UnknownContract(ContractId), + /// valid (non-archived) witness is absent in the list of witnesses for a + /// state transition bundle. + AbsentValidWitness, } #[derive(Clone, Eq, PartialEq, Debug, Hash)] @@ -121,6 +125,23 @@ impl State

{ .map_err(StateError::ReadProvider) } + pub fn select_valid_witness( + &self, + witness_ids: impl IntoIterator>, + ) -> Result> { + for witness_id in witness_ids { + let witness_id = *witness_id.borrow(); + if self + .provider + .is_valid_witness(witness_id) + .map_err(StateError::ReadProvider)? + { + return Ok(witness_id); + } + } + Err(StateError::Inconsistency(StateInconsistency::AbsentValidWitness)) + } + pub fn update_from_bundle( &mut self, contract_id: ContractId, @@ -244,6 +265,8 @@ pub trait StateReadProvider { &self, contract_id: ContractId, ) -> Result, Self::Error>; + + fn is_valid_witness(&self, witness_id: XWitnessId) -> Result; } pub trait StateWriteProvider: StoreTransaction { diff --git a/src/persistence/stock.rs b/src/persistence/stock.rs index 370434a0..d4723855 100644 --- a/src/persistence/stock.rs +++ b/src/persistence/stock.rs @@ -1234,9 +1234,10 @@ impl Stock { } fn bundled_witness(&self, bundle_id: BundleId) -> Result> { - let (witness_id, contract_id) = self.index.bundle_info(bundle_id)?; + let (witness_ids, contract_id) = self.index.bundle_info(bundle_id)?; let bundle = self.stash.bundle(bundle_id)?.clone(); + let witness_id = self.state.select_valid_witness(witness_ids)?; let witness = self.stash.witness(witness_id)?; let anchor = witness.anchors.clone(); let (tapret, opret) = match anchor { From 30c7fae982ded74bb0b17ba94112d9af03ef20c0 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 9 Aug 2024 15:20:32 +0200 Subject: [PATCH 12/12] containers: fix compilation bug --- src/containers/partials.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/containers/partials.rs b/src/containers/partials.rs index b92dcaca..3c1e32a3 100644 --- a/src/containers/partials.rs +++ b/src/containers/partials.rs @@ -221,7 +221,7 @@ impl Batch { pub fn set_priority(&mut self, priority: u8) { self.main.transition.nonce = priority; - for mut info in &mut self.blanks { + for info in &mut self.blanks { info.transition.nonce = priority; } }