diff --git a/Cargo.lock b/Cargo.lock index 998f03d..7a7f3c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1859,8 +1859,7 @@ dependencies = [ [[package]] name = "rgb-core" version = "0.11.0-beta.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd852c74f2b91711ef730a4aa5149442e2488425b3db811c6a55b72eb6db41d4" +source = "git+https://github.com/RGB-WG/rgb-core?branch=master#84892fd826f6f7805b076e70cba467b85c1eb8f7" dependencies = [ "aluvm", "amplify", @@ -3179,3 +3178,13 @@ dependencies = [ "quote", "syn 2.0.66", ] + +[[patch.unused]] +name = "bp-wallet" +version = "0.11.0-beta.6.1" +source = "git+https://github.com/BP-WG/bp-wallet?branch=master#783d977240ab1e8e0f5edfe0befba9a561971d8d" + +[[patch.unused]] +name = "rgb-std" +version = "0.11.0-beta.6" +source = "git+https://github.com/RGB-WG/rgb-std?branch=master#7ac337b3d84204e43f3818d298f7fc46b24341c2" diff --git a/Cargo.toml b/Cargo.toml index 5d3c6aa..1978ac0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,8 +88,9 @@ reqwest = { workspace = true, optional = true } [features] default = ["esplora_blocking", "mempool_blocking"] -all = ["esplora_blocking", "electrum_blocking", "mempool_blocking", "serde", "log", "fs"] +all = ["esplora_blocking", "electrum_blocking", "mempool_blocking", "serde", "log", "fs", "cli"] fs = ["serde", "bp-wallet/fs"] +cli = ["fs"] esplora_blocking = ["bp-esplora"] electrum_blocking = ["bp-electrum"] serde = ["serde_crate", "serde_yaml", "bp-std/serde", "descriptors/serde", "rgb-psbt/serde"] @@ -97,3 +98,8 @@ mempool_blocking = ["reqwest"] [package.metadata.docs.rs] features = ["all"] + +[patch.crates-io] +bp-wallet = { git = "https://github.com/BP-WG/bp-wallet", branch = "master" } +rgb-core = { git = "https://github.com/RGB-WG/rgb-core", branch = "master" } +rgb-std = { git = "https://github.com/RGB-WG/rgb-std", branch = "master" } diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 3bd7520..b1887ff 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -27,7 +27,7 @@ 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.6", path = "..", features = ["electrum_blocking", "esplora_blocking", "log", "serde", "fs"] } +rgb-runtime = { version = "0.11.0-beta.6", path = "..", features = ["electrum_blocking", "esplora_blocking", "log", "serde", "fs", "cli"] } log = { workspace = true } env_logger = "0.11.3" clap = { version = "4.4.8", features = ["derive", "env"] } diff --git a/cli/src/args.rs b/cli/src/args.rs index 01823e4..8b87ddb 100644 --- a/cli/src/args.rs +++ b/cli/src/args.rs @@ -23,14 +23,14 @@ use std::fs; use std::io::ErrorKind; -use std::path::Path; +use std::path::PathBuf; use bpstd::{Wpkh, XpubDerivable}; use bpwallet::cli::{Args as BpArgs, Config, DescriptorOpts}; use bpwallet::Wallet; -use rgb::{AnyResolver, RgbDescr, StoredStock, StoredWallet, TapretKey, WalletError}; -use rgbstd::persistence::fs::{LoadFs, StoreFs}; -use rgbstd::persistence::Stock; +use rgb::persistence::Stock; +use rgb::resolvers::AnyResolver; +use rgb::{RgbDescr, RgbWallet, TapretKey, WalletError}; use strict_types::encoding::{DecodeError, DeserializeError}; use crate::Command; @@ -77,41 +77,49 @@ impl Default for RgbArgs { } impl RgbArgs { - pub(crate) fn load_stock(&self, stock_path: &Path) -> Result { + pub(crate) fn load_stock( + &self, + stock_path: impl ToOwned, + ) -> Result { + let stock_path = stock_path.to_owned(); + if self.verbose > 1 { - eprint!("Loading stock ... "); + eprint!("Loading stock from `{}` ... ", stock_path.display()); } - Stock::load(stock_path).map_err(WalletError::from).or_else(|err| { + let stock = Stock::load(stock_path.clone()).map_err(WalletError::from).or_else(|err| { if matches!(err, WalletError::Deserialize(DeserializeError::Decode(DecodeError::Io(ref err))) if err.kind() == ErrorKind::NotFound) { if self.verbose > 1 { eprint!("stock file is absent, creating a new one ... "); } - let stock = Stock::default(); - fs::create_dir_all(stock_path)?; - stock.store(stock_path)?; - if self.verbose > 1 { - eprintln!("success"); - } + fs::create_dir_all(&stock_path)?; + let stock = Stock::new(stock_path.to_owned()); + stock.store()?; return Ok(stock) } eprintln!("stock file is damaged, failing"); Err(err) - }) + })?; + + if self.verbose > 1 { + eprintln!("success"); + } + + Ok(stock) } - pub fn rgb_stock(&self) -> Result { + pub fn rgb_stock(&self) -> Result { let stock_path = self.general.base_dir(); - let stock = self.load_stock(&stock_path)?; - Ok(StoredStock::attach(stock_path, stock)) + let stock = self.load_stock(stock_path)?; + Ok(stock) } pub fn rgb_wallet( &self, config: &Config, - ) -> Result>, WalletError> { + ) -> Result>, WalletError> { let stock_path = self.general.base_dir(); - let stock = self.load_stock(&stock_path)?; + let stock = self.load_stock(stock_path)?; self.rgb_wallet_from_stock(config, stock) } @@ -119,11 +127,9 @@ impl RgbArgs { &self, config: &Config, stock: Stock, - ) -> Result>, WalletError> { - let stock_path = self.general.base_dir(); - let wallet = self.inner.bp_runtime::(config)?; - let wallet_path = wallet.path().clone(); - let wallet = StoredWallet::attach(stock_path, wallet_path, stock, wallet.detach()); + ) -> Result>, WalletError> { + let wallet = self.inner.bp_wallet::(config)?; + let wallet = RgbWallet::new(stock, wallet); Ok(wallet) } diff --git a/cli/src/command.rs b/cli/src/command.rs index 22ed903..137d096 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -35,7 +35,7 @@ use rgb::containers::{ BuilderSeal, ContainerVer, ContentId, ContentSigs, Contract, FileContent, Supplement, Terminal, Transfer, UniversalFile, }; -use rgb::interface::{AmountChange, IfaceId}; +use rgb::interface::{AmountChange, IfaceId, OutpointFilter}; use rgb::invoice::{Beneficiary, Pay2Vout, RgbInvoice, RgbInvoiceBuilder, XChainNet}; use rgb::persistence::StashReadProvider; use rgb::schema::SchemaId; @@ -43,10 +43,9 @@ use rgb::validation::Validity; use rgb::vm::RgbIsa; use rgb::{ BundleId, ContractId, DescriptorRgb, GenesisSeal, GraphSeal, Identity, OutputSeal, RgbDescr, - RgbKeychain, StateType, StoredWallet, TransferParams, WalletError, WalletProvider, XChain, + RgbKeychain, RgbWallet, StateType, TransferParams, WalletError, WalletProvider, XChain, XOutpoint, XOutputSeal, }; -use rgbstd::interface::OutpointFilter; use serde_crate::{Deserialize, Serialize}; use strict_types::encoding::{FieldName, TypeName}; use strict_types::StrictVal; @@ -485,7 +484,7 @@ impl Exec for RgbArgs { all, } => { let stock_path = self.general.base_dir(); - let stock = self.load_stock(&stock_path)?; + let stock = self.load_stock(stock_path)?; let contract = stock.contract_iface(*contract_id, tn!(iface.to_owned()))?; @@ -508,8 +507,8 @@ impl Exec for RgbArgs { } enum Filter { - Wallet(StoredWallet>), - WalletAll(StoredWallet>), + Wallet(RgbWallet>), + WalletAll(RgbWallet>), NoWallet, } impl OutpointFilter for Filter { diff --git a/src/errors.rs b/src/errors.rs index 0632e29..bd9aabc 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -50,7 +50,15 @@ pub enum WalletError { Deserialize(DeserializeError), #[from] - Load(LoadError), + StockLoad(LoadError), + + #[cfg(feature = "fs")] + #[from] + WalletLoad(bpwallet::fs::LoadError), + + #[cfg(feature = "cli")] + #[from] + WalletExect(bpwallet::cli::ExecError), #[from] Builder(BuilderError), @@ -82,11 +90,6 @@ pub enum WalletError { #[display(doc_comments)] IncompleteContract(validation::Status), - #[cfg(feature = "fs")] - #[from] - #[from(bpwallet::LoadError)] - Bp(bpwallet::RuntimeError), - /// resolver error: {0} #[display(doc_comments)] Resolver(String), diff --git a/src/resolvers/any.rs b/src/indexers/any.rs similarity index 94% rename from src/resolvers/any.rs rename to src/indexers/any.rs index b68fd27..5dfe0c4 100644 --- a/src/resolvers/any.rs +++ b/src/indexers/any.rs @@ -73,7 +73,7 @@ impl AnyResolver { pub fn mempool_blocking(url: &str, _: Option<()>) -> Result { Ok(AnyResolver { inner: Box::new( - crate::mempool_blocking::MemPoolClient::new(url), + super::mempool_blocking::MemPoolClient::new(url), ), terminal_txes: Default::default(), }) @@ -147,13 +147,3 @@ impl ResolveWitness for AnyResolver { }) } } - - -impl ResolveWitness for &AnyResolver { - fn resolve_pub_witness( - &self, - witness_id: XWitnessId, - ) -> Result { - (*self).resolve_pub_witness(witness_id) - } -} diff --git a/src/resolvers/electrum_blocking.rs b/src/indexers/electrum_blocking.rs similarity index 100% rename from src/resolvers/electrum_blocking.rs rename to src/indexers/electrum_blocking.rs diff --git a/src/resolvers/esplora_blocking.rs b/src/indexers/esplora_blocking.rs similarity index 100% rename from src/resolvers/esplora_blocking.rs rename to src/indexers/esplora_blocking.rs diff --git a/src/resolvers/mempool_blocking.rs b/src/indexers/mempool_blocking.rs similarity index 100% rename from src/resolvers/mempool_blocking.rs rename to src/indexers/mempool_blocking.rs diff --git a/src/resolvers/mod.rs b/src/indexers/mod.rs similarity index 100% rename from src/resolvers/mod.rs rename to src/indexers/mod.rs diff --git a/src/lib.rs b/src/lib.rs index 0602dd4..44d3fe1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,20 +26,22 @@ extern crate amplify; extern crate serde_crate as serde; mod descriptor; -#[allow(hidden_glob_reexports)] -mod resolvers; -mod wallet; +mod indexers; +mod wrapper; pub mod pay; mod errors; -#[cfg(feature = "fs")] -mod store; +mod wallet; pub use descriptor::{DescriptorRgb, RgbDescr, RgbKeychain, TapTweakAlreadyAssigned, TapretKey}; pub use errors::{CompletionError, CompositionError, HistoryError, PayError, WalletError}; pub use pay::{TransferParams, WalletProvider}; -#[cfg(any(feature = "electrum_blocking", feature = "esplora_blocking"))] -pub use resolvers::*; pub use rgbstd::*; -#[cfg(feature = "fs")] -pub use store::{StoredStock, StoredWallet}; -pub use wallet::{WalletStock, WalletWrapper}; +pub mod resolvers { + pub use rgbstd::resolvers::*; + + #[cfg(any(feature = "electrum_blocking", feature = "esplora_blocking"))] + pub use super::indexers::*; + pub use super::indexers::{AnyResolver, RgbResolver}; +} +pub use wallet::RgbWallet; +pub use wrapper::WalletWrapper; diff --git a/src/pay.rs b/src/pay.rs index 98a8a50..c6d2af2 100644 --- a/src/pay.rs +++ b/src/pay.rs @@ -21,13 +21,12 @@ use std::collections::{BTreeMap, BTreeSet}; use std::marker::PhantomData; -use std::ops::DerefMut; use bp::dbc::tapret::TapretProof; use bp::seals::txout::ExplicitSeal; use bp::{Outpoint, Sats, ScriptPubkey, Vout}; use bpstd::{psbt, Address}; -use bpwallet::Wallet; +use bpwallet::{Save, Wallet, WalletDescr}; use psrgbt::{ Beneficiary as BpBeneficiary, Psbt, PsbtConstructor, PsbtMeta, RgbPsbt, TapretKeyError, TxParams, @@ -39,7 +38,7 @@ use rgbstd::persistence::{IndexProvider, StashProvider, StateProvider, Stock}; use rgbstd::{ContractId, DataState, XChain, XOutpoint}; use crate::invoice::NonFungible; -use crate::wallet::WalletWrapper; +use crate::wrapper::WalletWrapper; use crate::{CompletionError, CompositionError, DescriptorRgb, PayError, RgbKeychain, Txid}; #[derive(Clone, PartialEq, Debug)] @@ -99,7 +98,7 @@ where Self::Descr: DescriptorRgb type Filter<'a>: Copy + WitnessFilter + OutpointFilter where Self: 'a; fn filter(&self) -> Self::Filter<'_>; - fn descriptor_mut(&mut self) -> &mut Self::Descr; + fn descriptor_mut(&mut self, f: impl FnOnce(&mut WalletDescr) -> R) -> R; fn outpoints(&self) -> impl Iterator; fn txids(&self) -> impl Iterator; @@ -291,8 +290,7 @@ where Self::Descr: DescriptorRgb .terminal_derivation() .ok_or(CompletionError::InconclusiveDerivation)?; let tapret_commitment = output.tapret_commitment()?; - self.descriptor_mut() - .add_tapret_tweak(terminal, tapret_commitment)?; + self.descriptor_mut(|descr| descr.add_tapret_tweak(terminal, tapret_commitment))?; } let witness_txid = psbt.txid(); @@ -322,10 +320,14 @@ where Self::Descr: DescriptorRgb } } -impl> WalletProvider for Wallet { +impl> WalletProvider for Wallet +where Wallet: Save +{ type Filter<'a> = WalletWrapper<'a, K, D> where Self: 'a; fn filter(&self) -> Self::Filter<'_> { WalletWrapper(self) } - fn descriptor_mut(&mut self) -> &mut Self::Descr { self.deref_mut() } + fn 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() } } diff --git a/src/store.rs b/src/store.rs deleted file mode 100644 index 2285255..0000000 --- a/src/store.rs +++ /dev/null @@ -1,264 +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 std::collections::HashMap; -use std::error::Error; -use std::marker::PhantomData; -use std::ops::{Deref, DerefMut}; -use std::path::{Path, PathBuf}; - -use bpstd::XpubDerivable; -use bpwallet::{StoreError, Wallet, WalletDescr}; -use psrgbt::{Psbt, PsbtMeta}; -use rgbstd::containers::Transfer; -use rgbstd::interface::{AmountChange, IfaceOp, IfaceRef}; -use rgbstd::persistence::fs::StoreFs; -use rgbstd::persistence::{ - IndexProvider, MemIndex, MemStash, MemState, StashProvider, StateProvider, Stock, -}; - -use super::{ - CompletionError, CompositionError, ContractId, DescriptorRgb, PayError, TransferParams, - WalletError, WalletProvider, WalletStock, XWitnessId, -}; -use crate::invoice::RgbInvoice; - -pub trait Store { - type Err: Error; - fn store(&self, path: impl AsRef) -> Result<(), Self::Err>; -} - -impl> Store for Wallet -where - for<'de> WalletDescr: serde::Serialize + serde::Deserialize<'de>, - for<'de> D: serde::Serialize + serde::Deserialize<'de>, -{ - type Err = StoreError; - fn store(&self, path: impl AsRef) -> Result<(), Self::Err> { self.store(path.as_ref()) } -} - -#[derive(Getters)] -pub struct StoredStock< - S: StashProvider = MemStash, - H: StateProvider = MemState, - P: IndexProvider = MemIndex, -> where - S: StoreFs, - H: StoreFs, - P: StoreFs, -{ - stock_path: PathBuf, - stock: Stock, - #[getter(prefix = "is_")] - dirty: bool, -} - -impl Deref for StoredStock -where - S: StoreFs, - H: StoreFs, - P: StoreFs, -{ - type Target = Stock; - - fn deref(&self) -> &Self::Target { &self.stock } -} - -impl DerefMut for StoredStock -where - S: StoreFs, - H: StoreFs, - P: StoreFs, -{ - fn deref_mut(&mut self) -> &mut Self::Target { - self.dirty = true; - &mut self.stock - } -} - -impl StoredStock -where - S: StoreFs, - H: StoreFs, - P: StoreFs, -{ - pub fn attach(path: PathBuf, stock: Stock) -> Self { - Self { - stock_path: path, - stock, - dirty: false, - } - } - - pub fn store(&self) { - if self.dirty { - self.stock - .store(&self.stock_path) - .expect("error saving data"); - } - } -} - -impl Drop for StoredStock -where - S: StoreFs, - H: StoreFs, - P: StoreFs, -{ - fn drop(&mut self) { self.store() } -} - -#[derive(Getters)] -pub struct StoredWallet< - W: WalletProvider, - K = XpubDerivable, - S: StashProvider = MemStash, - H: StateProvider = MemState, - P: IndexProvider = MemIndex, -> where - W::Descr: DescriptorRgb, - W: Store, - S: StoreFs, - H: StoreFs, - P: StoreFs, -{ - stock_path: PathBuf, - wallet_path: Option, - stock: Stock, - wallet: W, - #[getter(prefix = "is_")] - stock_dirty: bool, - #[getter(prefix = "is_")] - wallet_dirty: bool, - #[getter(skip)] - _phantom: PhantomData, -} - -impl, S: StashProvider, H: StateProvider, P: IndexProvider> - StoredWallet -where - W::Descr: DescriptorRgb, - W: Store, - S: StoreFs, - H: StoreFs, - P: StoreFs, -{ - pub fn attach( - stock_path: PathBuf, - wallet_path: Option, - stock: Stock, - wallet: W, - ) -> Self { - Self { - stock_path, - wallet_path, - stock, - wallet, - stock_dirty: false, - wallet_dirty: false, - _phantom: PhantomData, - } - } - - pub fn stock_mut(&mut self) -> &mut Stock { - self.stock_dirty = true; - &mut self.stock - } - - pub fn wallet_mut(&mut self) -> &mut W { - self.wallet_dirty = true; - &mut self.wallet - } - - #[allow(clippy::result_large_err)] - pub fn fungible_history( - &self, - contract_id: ContractId, - iface: impl Into, - ) -> Result>, WalletError> { - self.stock - .fungible_history(&self.wallet, contract_id, iface) - } - - #[allow(clippy::result_large_err)] - pub fn pay( - &mut self, - invoice: &RgbInvoice, - params: TransferParams, - ) -> Result<(Psbt, PsbtMeta, Transfer), PayError> { - self.stock_dirty = true; - self.wallet_dirty = true; - self.wallet.pay(&mut self.stock, invoice, params) - } - - #[allow(clippy::result_large_err)] - pub fn construct_psbt( - &mut self, - invoice: &RgbInvoice, - params: TransferParams, - ) -> Result<(Psbt, PsbtMeta), CompositionError> { - self.wallet_dirty = true; - self.wallet.construct_psbt_rgb(&self.stock, invoice, params) - } - - #[allow(clippy::result_large_err)] - pub fn transfer( - &mut self, - invoice: &RgbInvoice, - psbt: &mut Psbt, - ) -> Result { - self.stock_dirty = true; - self.wallet_dirty = true; - self.wallet.transfer(&mut self.stock, invoice, psbt) - } - - pub fn store(&self) { - let r1 = if self.stock_dirty { - self.stock - .store(&self.stock_path) - .map_err(|e| e.to_string()) - } else { - Ok(()) - }; - let r2 = if self.wallet_dirty { - if let Some(path) = self.wallet_path.as_ref() { - self.wallet.store(path).map_err(|e| e.to_string()) - } else { - Ok(()) - } - } else { - Ok(()) - }; - r1.and(r2).expect("error saving data"); - } -} - -impl, S: StashProvider, H: StateProvider, P: IndexProvider> Drop - for StoredWallet -where - W::Descr: DescriptorRgb, - W: Store, - S: StoreFs, - H: StoreFs, - P: StoreFs, -{ - fn drop(&mut self) { self.store() } -} diff --git a/src/wallet.rs b/src/wallet.rs index bba7f3f..9c2f169 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -20,66 +20,97 @@ // limitations under the License. use std::collections::HashMap; +use std::marker::PhantomData; +#[cfg(feature = "fs")] +use std::path::{Path, PathBuf}; -use bpwallet::Wallet; -use rgbstd::interface::{AmountChange, IfaceOp, IfaceRef, OutpointFilter, WitnessFilter}; -use rgbstd::persistence::{IndexProvider, StashProvider, StateProvider, Stock}; - -use crate::{ - AssignmentWitness, ContractId, DescriptorRgb, HistoryError, WalletError, WalletProvider, - XChain, XOutpoint, XWitnessId, +use bpstd::XpubDerivable; +#[cfg(feature = "fs")] +use bpwallet::fs::Warning; +use bpwallet::{Wallet, WalletDescr}; +use psrgbt::{Psbt, PsbtMeta}; +use rgbstd::containers::Transfer; +use rgbstd::interface::{AmountChange, IfaceOp, IfaceRef}; +#[cfg(feature = "fs")] +use rgbstd::persistence::fs::FsStored; +use rgbstd::persistence::{ + IndexProvider, MemIndex, MemStash, MemState, StashProvider, StateProvider, Stock, }; -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) - } -} +use super::{ + CompletionError, CompositionError, ContractId, DescriptorRgb, HistoryError, PayError, + TransferParams, WalletError, WalletProvider, XWitnessId, +}; +use crate::invoice::RgbInvoice; -impl<'a, K, D: DescriptorRgb> WitnessFilter for WalletWrapper<'a, K, D> { - fn include_witness(&self, witness: impl Into) -> bool { - let witness = witness.into(); - self.0 - .txids() - .any(|txid| AssignmentWitness::Present(XWitnessId::Bitcoin(txid)) == witness) - } +#[derive(Getters)] +pub struct RgbWallet< + W: WalletProvider, + K = XpubDerivable, + S: StashProvider = MemStash, + H: StateProvider = MemState, + P: IndexProvider = MemIndex, +> where W::Descr: DescriptorRgb +{ + stock: Stock, + wallet: W, + warnings: Vec, + #[getter(skip)] + _phantom: PhantomData, } -pub trait WalletStock, K> -where W::Descr: DescriptorRgb +#[cfg(feature = "fs")] +impl, S: StashProvider, H: StateProvider, P: IndexProvider> + RgbWallet, K, S, H, P> +where + S: FsStored, + H: FsStored, + P: FsStored, + for<'de> WalletDescr: serde::Serialize + serde::Deserialize<'de>, + for<'de> D: serde::Serialize + serde::Deserialize<'de>, { #[allow(clippy::result_large_err)] - fn fungible_history( - &self, - wallet: &W, - contract_id: ContractId, - iface: impl Into, - ) -> Result>, WalletError>; + pub fn load( + stock_path: impl ToOwned, + wallet_path: impl AsRef, + ) -> Result { + let stock = Stock::load(stock_path)?; + let (wallet, warnings) = Wallet::load(wallet_path.as_ref(), true)?; + Ok(Self { + wallet, + stock, + warnings, + _phantom: PhantomData, + }) + } } -impl, K, S: StashProvider, H: StateProvider, P: IndexProvider> - WalletStock for Stock +impl, S: StashProvider, H: StateProvider, P: IndexProvider> + RgbWallet where W::Descr: DescriptorRgb { - // TODO: Integrate into BP Wallet `TxRow` as L2 and provide transactional info - fn fungible_history( + pub fn new(stock: Stock, wallet: W) -> Self { + Self { + stock, + wallet, + warnings: none!(), + _phantom: PhantomData, + } + } + + pub fn stock_mut(&mut self) -> &mut Stock { &mut self.stock } + + pub fn wallet_mut(&mut self) -> &mut W { &mut self.wallet } + + #[allow(clippy::result_large_err)] + pub fn fungible_history( &self, - wallet: &W, contract_id: ContractId, iface: impl Into, ) -> Result>, WalletError> { + let wallet = &self.wallet; let iref = iface.into(); - let iface = self.iface(iref.clone()).map_err(|e| e.to_string())?; + let iface = self.stock.iface(iref.clone()).map_err(|e| e.to_string())?; let default_op = iface .default_operation .as_ref() @@ -93,10 +124,38 @@ where W::Descr: DescriptorRgb .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(), wallet.filter()) .map_err(|e| e.to_string())?) } + + #[allow(clippy::result_large_err)] + pub fn pay( + &mut self, + invoice: &RgbInvoice, + params: TransferParams, + ) -> Result<(Psbt, PsbtMeta, Transfer), PayError> { + self.wallet.pay(&mut self.stock, invoice, params) + } + + #[allow(clippy::result_large_err)] + pub fn construct_psbt( + &mut self, + invoice: &RgbInvoice, + params: TransferParams, + ) -> Result<(Psbt, PsbtMeta), CompositionError> { + self.wallet.construct_psbt_rgb(&self.stock, invoice, params) + } + + #[allow(clippy::result_large_err)] + pub fn transfer( + &mut self, + invoice: &RgbInvoice, + psbt: &mut Psbt, + ) -> Result { + self.wallet.transfer(&mut self.stock, invoice, psbt) + } } diff --git a/src/wrapper.rs b/src/wrapper.rs new file mode 100644 index 0000000..5ce4a07 --- /dev/null +++ b/src/wrapper.rs @@ -0,0 +1,57 @@ +// 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::{Save, Wallet}; +use rgbstd::interface::{OutpointFilter, WitnessFilter}; + +use crate::{AssignmentWitness, DescriptorRgb, WalletProvider, XChain, XOutpoint, XWitnessId}; + +pub struct WalletWrapper<'a, K, D: DescriptorRgb>(pub &'a Wallet) +where Wallet: Save; + +impl<'a, K, D: DescriptorRgb> Copy for WalletWrapper<'a, K, D> where Wallet: Save {} +impl<'a, K, D: DescriptorRgb> Clone for WalletWrapper<'a, K, D> +where Wallet: Save +{ + fn clone(&self) -> Self { *self } +} + +impl<'a, K, D: DescriptorRgb> OutpointFilter for WalletWrapper<'a, K, D> +where Wallet: Save +{ + fn include_outpoint(&self, output: impl Into) -> bool { + let output = output.into(); + self.0 + .outpoints() + .any(|outpoint| XChain::Bitcoin(outpoint) == *output) + } +} + +impl<'a, K, D: DescriptorRgb> WitnessFilter for WalletWrapper<'a, K, D> +where Wallet: Save +{ + fn include_witness(&self, witness: impl Into) -> bool { + let witness = witness.into(); + self.0 + .txids() + .any(|txid| AssignmentWitness::Present(XWitnessId::Bitcoin(txid)) == witness) + } +}