From 406f80a67d68c5d799281d90cbf510b94271c268 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 20 Sep 2024 17:43:30 +0200 Subject: [PATCH] wallet: use new history API. Remve interfaces dependency --- Cargo.lock | 28 ++-------- Cargo.toml | 4 +- cli/Cargo.toml | 1 - cli/src/command.rs | 125 +++++++++++++++++++++++---------------------- src/errors.rs | 16 +----- src/filters.rs | 60 ++++++++++++++++++++++ src/lib.rs | 6 +-- src/pay.rs | 29 ++++++----- src/wallet.rs | 38 ++++---------- src/wrapper.rs | 41 --------------- 10 files changed, 158 insertions(+), 190 deletions(-) create mode 100644 src/filters.rs delete mode 100644 src/wrapper.rs diff --git a/Cargo.lock b/Cargo.lock index 02edff3..8ee6515 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1127,7 +1127,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -1413,30 +1413,10 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "rgb-interfaces" -version = "0.11.0-beta.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37dea80df39205f6c1d0fc4a6d2ea0401d5e6b9b28ed852b707cb01517b3c991" -dependencies = [ - "aluvm", - "amplify", - "bp-core", - "chrono", - "getrandom", - "rgb-std", - "serde_json", - "sha2", - "strict_encoding", - "strict_types", - "wasm-bindgen", -] - [[package]] name = "rgb-invoice" version = "0.11.0-beta.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d42e9d284f857f3dccce63521789f3da75568f55488d255ebacc8fe343012a5d" +source = "git+https://github.com/RGB-WG/rgb-std?branch=fix/rgb-252#0a0ed6697f16c45a35b111f1bc88a429cb90ab94" dependencies = [ "amplify", "baid64", @@ -1502,8 +1482,7 @@ dependencies = [ [[package]] name = "rgb-std" version = "0.11.0-beta.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763a97b5e58521f41837b1a92f480041fd72a0659f22cb132354d2bbcba25b25" +source = "git+https://github.com/RGB-WG/rgb-std?branch=fix/rgb-252#0a0ed6697f16c45a35b111f1bc88a429cb90ab94" dependencies = [ "aluvm", "amplify", @@ -1539,7 +1518,6 @@ dependencies = [ "env_logger", "log", "psbt", - "rgb-interfaces", "rgb-runtime", "rgb-std", "serde", diff --git a/Cargo.toml b/Cargo.toml index cabccb7..e8f56b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,6 @@ psbt = { version = "0.11.0-beta.8", features = ["client-side-validation"] } bp-wallet = { version = "0.11.0-beta.8" } rgb-std = { version = "0.11.0-beta.8" } rgb-psbt = { version = "0.11.0-beta.8", path = "psbt" } -rgb-interfaces = "0.11.0-beta.8" indexmap = "2.4.0" chrono = "0.4.38" serde_crate = { package = "serde", version = "1", features = ["derive"] } @@ -103,3 +102,6 @@ serde = ["serde_crate", "serde_yaml", "bp-std/serde", "descriptors/serde", "rgb- [package.metadata.docs.rs] features = ["all"] + +[patch.crates-io] +rgb-std = { git = "https://github.com/RGB-WG/rgb-std", branch = "fix/rgb-252" } diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 2905353..574927c 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -26,7 +26,6 @@ bp-std = { workspace = true, features = ["serde"] } bp-wallet = { workspace = true, features = ["cli"] } psbt = { workspace = true } rgb-std = { workspace = true, features = ["serde"] } -rgb-interfaces = { workspace = true } rgb-runtime = { version = "0.11.0-beta.7", path = "..", features = ["electrum_blocking", "esplora_blocking", "mempool_blocking", "log", "serde", "fs", "cli"] } log = { workspace = true } env_logger = "0.11.5" diff --git a/cli/src/command.rs b/cli/src/command.rs index 2541e47..96f2d4b 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -29,25 +29,22 @@ use baid64::DisplayBaid64; use bpstd::{Sats, XpubDerivable}; use bpwallet::cli::{BpCommand, Config, Exec}; use bpwallet::Wallet; -use ifaces::{IfaceStandard, Rgb20, Rgb21, Rgb25}; use psbt::{Psbt, PsbtVer}; use rgb::containers::{ - BuilderSeal, ContainerVer, ContentId, ContentSigs, Contract, FileContent, Supplement, Transfer, - UniversalFile, + BuilderSeal, ConsignmentExt, ContainerVer, ContentId, ContentSigs, Contract, FileContent, + Supplement, Transfer, UniversalFile, }; -use rgb::interface::{AmountChange, IfaceId, OutpointFilter}; +use rgb::interface::{AssignmentsFilter, ContractOp, IfaceId}; use rgb::invoice::{Beneficiary, Pay2Vout, RgbInvoice, RgbInvoiceBuilder, XChainNet}; -use rgb::persistence::StashReadProvider; +use rgb::persistence::{MemContract, StashReadProvider, Stock}; use rgb::schema::SchemaId; use rgb::validation::Validity; use rgb::vm::RgbIsa; use rgb::{ - BundleId, ContractId, DescriptorRgb, GenesisSeal, GraphSeal, Identity, OutputSeal, RgbDescr, - RgbKeychain, RgbWallet, StateType, TransferParams, WalletError, WalletProvider, XChain, - XOutpoint, XOutputSeal, XWitnessId, + BundleId, ContractId, DescriptorRgb, GenesisSeal, GraphSeal, Identity, OpId, OutputSeal, + RgbDescr, RgbKeychain, RgbWallet, StateType, TransferParams, WalletError, WalletProvider, + XChain, XOutpoint, XWitnessId, }; -use rgbstd::containers::ConsignmentExt; -use rgbstd::persistence::{MemContract, Stock}; use seals::SecretSeal; use serde_crate::{Deserialize, Serialize}; use strict_types::encoding::{FieldName, TypeName}; @@ -74,10 +71,7 @@ pub enum Command { /// Prints out list of known RGB contracts #[display("contracts")] - Contracts { - /// Select only contracts using specific interface standard - standard: Option, - }, + Contracts, /// Imports RGB data into the stash: contracts, schema, interfaces, etc #[display("import")] @@ -135,6 +129,10 @@ pub enum Command { /// interface #[display("history-fungible")] HistoryFungible { + /// Print detailed information + #[clap(long)] + details: bool, + /// Contract identifier contract_id: ContractId, @@ -338,57 +336,58 @@ impl Exec for RgbArgs { print!("{info}"); } } - Command::Contracts { standard: None } => { + Command::Contracts => { let stock = self.rgb_stock()?; for info in stock.contracts()? { print!("{info}"); } } - Command::Contracts { - standard: Some(IfaceStandard::Rgb20), - } => { - let stock = self.rgb_stock()?; - for info in stock.contracts_by::()? { - print!("{info}"); - } - } - Command::Contracts { - standard: Some(IfaceStandard::Rgb21), - } => { - let stock = self.rgb_stock()?; - for info in stock.contracts_by::()? { - print!("{info}"); - } - } - Command::Contracts { - standard: Some(IfaceStandard::Rgb25), - } => { - let stock = self.rgb_stock()?; - for info in stock.contracts_by::()? { - print!("{info}"); - } - } - Command::HistoryFungible { contract_id, iface } => { + Command::HistoryFungible { + contract_id, + iface, + details, + } => { let wallet = self.rgb_wallet(&config)?; let iface: TypeName = tn!(iface.clone()); - let history = wallet.fungible_history(*contract_id, iface)?; - println!("Amount\tCounterparty\tWitness Id"); - for (id, op) in history { - let (cparty, more) = match op.state_change { - AmountChange::Dec(_) => { - (op.beneficiaries.first(), op.beneficiaries.len().saturating_sub(1)) - } - AmountChange::Zero => continue, - AmountChange::Inc(_) => { - (op.payers.first(), op.payers.len().saturating_sub(1)) - } - }; - let more = if more > 0 { format!(" (+{more})") } else { s!("") }; - let cparty = cparty - .map(XOutputSeal::to_string) - .unwrap_or_else(|| s!("none")); - println!("{}\t{}{}\t{}", op.state_change, cparty, more, id); + let history = wallet.history(*contract_id, iface)?; + if *details { + println!("Operation\tValue\tState\tSeal\tWitness\tOpIds"); + } else { + println!("Operation\tValue\tSeal\tWitness"); + } + for ContractOp { + direction, + ty, + opids, + state, + to, + witness, + } in history + { + print!("{direction}\t{state}"); + if *details { + print!("\t{ty}"); + } + print!( + "\t{}\t{}", + to.first().expect("at least one receiver is always present"), + witness + .map(|info| format!("{} ({})", info.id, info.ord)) + .unwrap_or_else(|| s!("~")) + ); + if *details { + println!( + "{}", + opids + .iter() + .map(OpId::to_string) + .collect::>() + .join(", ") + ) + } else { + println!(); + } } } @@ -530,11 +529,15 @@ impl Exec for RgbArgs { WalletAll(&'w RgbWallet>), NoWallet, } - impl<'w> OutpointFilter for Filter<'w> { - fn include_outpoint(&self, outpoint: impl Into) -> bool { + impl<'w> AssignmentsFilter for Filter<'w> { + fn should_include( + &self, + outpoint: impl Into, + id: Option, + ) -> bool { match self { Filter::Wallet(wallet) => { - wallet.wallet().filter().include_outpoint(outpoint) + wallet.wallet().filter().should_include(outpoint, id) } _ => true, } @@ -544,7 +547,7 @@ impl Exec for RgbArgs { fn comment(&self, outpoint: XOutpoint) -> &'static str { match self { Filter::Wallet(wallet) | Filter::WalletAll(wallet) - if wallet.wallet().filter().include_outpoint(outpoint) => + if wallet.wallet().filter().should_include(outpoint, None) => { "" } diff --git a/src/errors.rs b/src/errors.rs index 70bfce9..de6a609 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -60,14 +60,11 @@ pub enum WalletError { #[cfg(feature = "cli")] #[from] - WalletExect(bpwallet::cli::ExecError), + WalletExec(bpwallet::cli::ExecError), #[from] Builder(BuilderError), - #[from] - History(HistoryError), - #[from] Contract(ContractError), @@ -118,17 +115,6 @@ impl From<(Stock, WalletError)> for WalletError { fn from((_, e): (Stock, WalletError)) -> Self { e } } -#[derive(Debug, Display, Error, From)] -#[display(doc_comments)] -pub enum HistoryError { - /// interface doesn't define default operation - NoDefaultOp, - /// default operation defined by the interface is not a state transition - DefaultOpNotTransition, - /// interface doesn't define default fungible state - NoDefaultAssignment, -} - #[allow(clippy::large_enum_variant)] #[derive(Debug, Display, Error, From)] pub enum PayError { diff --git a/src/filters.rs b/src/filters.rs new file mode 100644 index 0000000..9086a4f --- /dev/null +++ b/src/filters.rs @@ -0,0 +1,60 @@ +// RGB wallet library for smart contracts on Bitcoin & Lightning network +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2019-2023 by +// Dr Maxim Orlovsky +// +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use bpwallet::Wallet; +use rgbstd::interface::AssignmentsFilter; + +use crate::{DescriptorRgb, WalletProvider, XChain, XOutpoint, XWitnessId}; + +pub struct WalletOutpointsFilter<'a, K, D: DescriptorRgb>(pub &'a Wallet); + +// We need manual derivation to ensure we can be copied and cloned even if descriptor is not +// copyable/clonable. +impl<'a, K, D: DescriptorRgb> Copy for WalletOutpointsFilter<'a, K, D> {} +impl<'a, K, D: DescriptorRgb> Clone for WalletOutpointsFilter<'a, K, D> { + fn clone(&self) -> Self { *self } +} + +impl<'a, K, D: DescriptorRgb> AssignmentsFilter for WalletOutpointsFilter<'a, K, D> { + fn should_include(&self, output: impl Into, _: Option) -> bool { + let output = output.into(); + self.0 + .outpoints() + .any(|outpoint| XChain::Bitcoin(outpoint) == *output) + } +} + +pub struct WitnessOutpointsFilter<'a, K, D: DescriptorRgb>(pub &'a Wallet); + +// We need manual derivation to ensure we can be copied and cloned even if descriptor is not +// copyable/clonable. +impl<'a, K, D: DescriptorRgb> Copy for WitnessOutpointsFilter<'a, K, D> {} +impl<'a, K, D: DescriptorRgb> Clone for WitnessOutpointsFilter<'a, K, D> { + fn clone(&self) -> Self { *self } +} + +impl<'a, K, D: DescriptorRgb> AssignmentsFilter for WitnessOutpointsFilter<'a, K, D> { + fn should_include(&self, _: impl Into, witness_id: Option) -> bool { + self.0 + .history() + .any(|row| witness_id == Some(XChain::Bitcoin(row.txid))) + } +} diff --git a/src/lib.rs b/src/lib.rs index 7dd5e57..c9ec685 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,13 +27,13 @@ extern crate serde_crate as serde; mod descriptor; mod indexers; -mod wrapper; +mod filters; pub mod pay; mod errors; mod wallet; pub use descriptor::{DescriptorRgb, RgbDescr, RgbKeychain, TapTweakAlreadyAssigned, TapretKey}; -pub use errors::{CompletionError, CompositionError, HistoryError, PayError, WalletError}; +pub use errors::{CompletionError, CompositionError, PayError, WalletError}; pub use pay::{TransferParams, WalletProvider}; pub use rgbstd::*; pub mod resolvers { @@ -41,5 +41,5 @@ pub mod resolvers { pub use super::indexers::*; pub use super::indexers::{AnyResolver, RgbResolver}; } +pub use filters::{WalletOutpointsFilter, WitnessOutpointsFilter}; pub use wallet::RgbWallet; -pub use wrapper::WalletWrapper; diff --git a/src/pay.rs b/src/pay.rs index 3d7374c..d61947d 100644 --- a/src/pay.rs +++ b/src/pay.rs @@ -26,24 +26,25 @@ use bp::dbc::tapret::TapretProof; use bp::seals::txout::ExplicitSeal; use bp::{Outpoint, Sats, ScriptPubkey, Vout}; use bpstd::{psbt, Address}; -use bpwallet::{Wallet, WalletDescr}; +use bpwallet::{Layer2Tx, TxRow, Wallet, WalletDescr}; use psrgbt::{ Beneficiary as BpBeneficiary, Psbt, PsbtConstructor, PsbtMeta, RgbPsbt, TapretKeyError, TxParams, }; use rgbstd::containers::Transfer; -use rgbstd::interface::OutpointFilter; +use rgbstd::interface::AssignmentsFilter; use rgbstd::invoice::{Amount, Beneficiary, InvoiceState, RgbInvoice}; use rgbstd::persistence::{IndexProvider, StashProvider, StateProvider, Stock}; use rgbstd::validation::ResolveWitness; use rgbstd::{ContractId, DataState, XChain, XOutpoint}; +use crate::filters::WalletOutpointsFilter; use crate::invoice::NonFungible; use crate::validation::WitnessResolverError; use crate::vm::{WitnessOrd, XWitnessTx}; -use crate::wrapper::WalletWrapper; use crate::{ - CompletionError, CompositionError, DescriptorRgb, PayError, RgbKeychain, Txid, XWitnessId, + CompletionError, CompositionError, DescriptorRgb, PayError, RgbKeychain, Txid, + WitnessOutpointsFilter, XWitnessId, }; #[derive(Clone, PartialEq, Debug)] @@ -85,12 +86,12 @@ impl< S: StashProvider, H: StateProvider, P: IndexProvider, -> OutpointFilter for ContractOutpointsFilter<'stock, 'wallet, W, K, S, H, P> +> AssignmentsFilter for ContractOutpointsFilter<'stock, 'wallet, W, K, S, H, P> where W::Descr: DescriptorRgb { - fn include_outpoint(&self, output: impl Into) -> bool { + fn should_include(&self, output: impl Into, id: Option) -> bool { let output = output.into(); - if !self.wallet.filter().include_outpoint(output) { + if !self.wallet.filter().should_include(output, id) { return false; } matches!(self.stock.contract_assignments_for(self.contract_id, [output]), Ok(list) if !list.is_empty()) @@ -100,15 +101,15 @@ where W::Descr: DescriptorRgb pub trait WalletProvider: PsbtConstructor where Self::Descr: DescriptorRgb { - type Filter<'a>: Copy + OutpointFilter - where Self: 'a; - fn filter(&self) -> Self::Filter<'_>; + fn filter(&self) -> impl AssignmentsFilter + Clone; + fn filter_witnesses(&self) -> impl AssignmentsFilter + Clone; fn with_descriptor_mut( &mut self, f: impl FnOnce(&mut WalletDescr) -> R, ) -> R; fn outpoints(&self) -> impl Iterator; fn txids(&self) -> impl Iterator; + fn history(&self) -> impl Iterator> + '_; // TODO: Add method `color` to add RGB information to an already existing PSBT @@ -351,13 +352,13 @@ where Self::Descr: DescriptorRgb } impl> WalletProvider for Wallet { - type Filter<'a> = WalletWrapper<'a, K, D> - where - Self: 'a; - fn filter(&self) -> Self::Filter<'_> { WalletWrapper(self) } + fn filter(&self) -> impl AssignmentsFilter + Clone { WalletOutpointsFilter(self) } + fn filter_witnesses(&self) -> impl AssignmentsFilter + Clone { WitnessOutpointsFilter(self) } fn with_descriptor_mut(&mut self, f: impl FnOnce(&mut WalletDescr) -> R) -> R { self.descriptor_mut(f) } fn outpoints(&self) -> impl Iterator { self.coins().map(|coin| coin.outpoint) } fn txids(&self) -> impl Iterator { self.transactions().keys().copied() } + + fn history(&self) -> impl Iterator> + '_ { self.history() } } diff --git a/src/wallet.rs b/src/wallet.rs index 0041360..a9226ae 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -19,7 +19,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::HashMap; use std::marker::PhantomData; #[cfg(feature = "fs")] use std::path::PathBuf; @@ -33,16 +32,17 @@ use bpwallet::Wallet; use nonasync::persistence::PersistenceProvider; use psrgbt::{Psbt, PsbtMeta}; use rgbstd::containers::Transfer; -use rgbstd::interface::{AmountChange, IfaceOp, IfaceRef}; +use rgbstd::interface::{ContractOp, IfaceRef}; #[cfg(feature = "fs")] use rgbstd::persistence::fs::FsBinStore; use rgbstd::persistence::{ - IndexProvider, MemIndex, MemStash, MemState, StashProvider, StateProvider, Stock, + ContractIfaceError, IndexProvider, MemIndex, MemStash, MemState, StashProvider, StateProvider, + Stock, StockError, }; use super::{ - CompletionError, CompositionError, ContractId, DescriptorRgb, HistoryError, PayError, - TransferParams, WalletError, WalletProvider, XWitnessId, + CompletionError, CompositionError, ContractId, DescriptorRgb, PayError, TransferParams, + WalletError, WalletProvider, }; use crate::invoice::RgbInvoice; @@ -105,34 +105,14 @@ where W::Descr: DescriptorRgb pub fn wallet_mut(&mut self) -> &mut W { &mut self.wallet } - #[allow(clippy::result_large_err)] - pub fn fungible_history( + pub fn history( &self, contract_id: ContractId, iface: impl Into, - ) -> Result>, WalletError> { + ) -> Result, StockError> { + let contract = self.stock.contract_iface(contract_id, iface.into())?; let wallet = &self.wallet; - let iref = iface.into(); - let iface = self.stock.iface(iref.clone()).map_err(|e| e.to_string())?; - let default_op = iface - .default_operation - .as_ref() - .ok_or(HistoryError::NoDefaultOp)?; - let state_name = iface - .transitions - .get(default_op) - .ok_or(HistoryError::DefaultOpNotTransition)? - .default_assignment - .as_ref() - .ok_or(HistoryError::NoDefaultAssignment)? - .clone(); - let contract = self - .stock - .contract_iface(contract_id, iref) - .map_err(|e| e.to_string())?; - Ok(contract - .fungible_ops::(state_name, wallet.filter()) - .map_err(|e| e.to_string())?) + Ok(contract.history(wallet.filter(), wallet.filter_witnesses())) } #[allow(clippy::result_large_err)] diff --git a/src/wrapper.rs b/src/wrapper.rs deleted file mode 100644 index fd46e1e..0000000 --- a/src/wrapper.rs +++ /dev/null @@ -1,41 +0,0 @@ -// RGB wallet library for smart contracts on Bitcoin & Lightning network -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2019-2023 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use bpwallet::Wallet; -use rgbstd::interface::OutpointFilter; - -use crate::{DescriptorRgb, WalletProvider, XChain, XOutpoint}; - -pub struct WalletWrapper<'a, K, D: DescriptorRgb>(pub &'a Wallet); - -impl<'a, K, D: DescriptorRgb> Copy for WalletWrapper<'a, K, D> {} -impl<'a, K, D: DescriptorRgb> Clone for WalletWrapper<'a, K, D> { - fn clone(&self) -> Self { *self } -} - -impl<'a, K, D: DescriptorRgb> OutpointFilter for WalletWrapper<'a, K, D> { - fn include_outpoint(&self, output: impl Into) -> bool { - let output = output.into(); - self.0 - .outpoints() - .any(|outpoint| XChain::Bitcoin(outpoint) == *output) - } -}