From b4d9880ac85dc02c0be45486ca3cadc37d5bdb50 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Wed, 31 Jul 2024 23:34:15 +0200 Subject: [PATCH] persistence: allow multiple witnesses per bundle --- Cargo.lock | 1 + Cargo.toml | 3 +-- src/persistence/index.rs | 12 ++---------- src/persistence/memory.rs | 39 ++++++++++++++++++--------------------- src/persistence/state.rs | 23 +++++++++++++++++++++++ src/persistence/stock.rs | 3 ++- 6 files changed, 47 insertions(+), 34 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..1bb6562e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -99,5 +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 = "contract-state2" } -rgb-core = { path = "../rgb-core" } +rgb-core = { git = "https://github.com/RGB-WG/rgb-core", branch = "contract-state2" } 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 b7edb131..2ad863f0 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 6bdb663b..0c558bdc 100644 --- a/src/persistence/stock.rs +++ b/src/persistence/stock.rs @@ -1250,9 +1250,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 {