Skip to content

Commit

Permalink
wallet: distinguish spent and unspent outputs
Browse files Browse the repository at this point in the history
  • Loading branch information
dr-orlovsky committed Sep 20, 2024
1 parent 954f7f2 commit 07c1b12
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 33 deletions.
5 changes: 2 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,5 @@ serde = ["serde_crate", "serde_yaml", "bp-std/serde", "descriptors/serde", "rgb-
features = ["all"]

[patch.crates-io]
bp-wallet = { git = "https://github.com/BP-WG/bp-wallet", branch = "develop" }
rgb-std = { git = "https://github.com/RGB-WG/rgb-std", branch = "fix/rgb-252" }
27 changes: 16 additions & 11 deletions cli/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ pub enum Command {
/// Reports information about state of a contract
#[display("state")]
State {
/// Show all state - not just the one owned by the wallet
/// Show all state, including already spent and not owned by the wallet
#[clap(short, long)]
all: bool,

Expand Down Expand Up @@ -379,9 +379,9 @@ impl Exec for RgbArgs {
witness,
} in history
{
print!("{direction: <9}\t");
print!("{direction:<9}\t");
if let AllocatedState::Amount(amount) = state {
print!("{: >9}", amount.value());
print!("{:>9}", amount.value());
} else {
print!("{state}");
}
Expand Down Expand Up @@ -563,22 +563,27 @@ impl Exec for RgbArgs {
id: Option<XWitnessId>,
) -> bool {
match self {
Filter::Wallet(wallet) => {
wallet.wallet().filter().should_include(outpoint, id)
}
Filter::Wallet(wallet) => wallet
.wallet()
.filter_unspent()
.should_include(outpoint, id),
_ => true,
}
}
}
impl<'w> Filter<'w> {
fn comment(&self, outpoint: XOutpoint) -> &'static str {
let outpoint = outpoint
.into_bp()
.into_bitcoin()
.expect("liquid is not yet supported");
match self {
Filter::Wallet(wallet) | Filter::WalletAll(wallet)
if wallet.wallet().filter().should_include(outpoint, None) =>
{
""
Filter::Wallet(rgb) if rgb.wallet().is_unspent(outpoint) => "",
Filter::WalletAll(rgb) if rgb.wallet().is_unspent(outpoint) => {
"-- unspent"
}
_ => "-- owner unknown",
Filter::WalletAll(_) => "-- spent",
_ => "-- third-party",
}
}
}
Expand Down
37 changes: 28 additions & 9 deletions src/filters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use bp::Bp;
use bpwallet::Wallet;
use rgbstd::interface::AssignmentsFilter;

use crate::{DescriptorRgb, WalletProvider, XChain, XOutpoint, XWitnessId};
use crate::{DescriptorRgb, XChain, XOutpoint, XWitnessId};

pub struct WalletOutpointsFilter<'a, K, D: DescriptorRgb<K>>(pub &'a Wallet<K, D>);

Expand All @@ -35,23 +36,41 @@ impl<'a, K, D: DescriptorRgb<K>> Clone for WalletOutpointsFilter<'a, K, D> {

impl<'a, K, D: DescriptorRgb<K>> AssignmentsFilter for WalletOutpointsFilter<'a, K, D> {
fn should_include(&self, output: impl Into<XOutpoint>, _: Option<XWitnessId>) -> bool {
let output = output.into();
self.0
.outpoints()
.any(|outpoint| XChain::Bitcoin(outpoint) == *output)
match output.into().into_bp() {
Bp::Bitcoin(outpoint) => self.0.has_outpoint(outpoint),
Bp::Liquid(_) => false,
}
}
}

pub struct WalletUnspentFilter<'a, K, D: DescriptorRgb<K>>(pub &'a Wallet<K, D>);

// We need manual derivation to ensure we can be copied and cloned even if descriptor is not
// copyable/clonable.
impl<'a, K, D: DescriptorRgb<K>> Copy for WalletUnspentFilter<'a, K, D> {}
impl<'a, K, D: DescriptorRgb<K>> Clone for WalletUnspentFilter<'a, K, D> {
fn clone(&self) -> Self { *self }
}

impl<'a, K, D: DescriptorRgb<K>> AssignmentsFilter for WalletUnspentFilter<'a, K, D> {
fn should_include(&self, output: impl Into<XOutpoint>, _: Option<XWitnessId>) -> bool {
match output.into().into_bp() {
Bp::Bitcoin(outpoint) => self.0.is_unspent(outpoint),
Bp::Liquid(_) => false,
}
}
}

pub struct WitnessOutpointsFilter<'a, K, D: DescriptorRgb<K>>(pub &'a Wallet<K, D>);
pub struct WalletWitnessFilter<'a, K, D: DescriptorRgb<K>>(pub &'a Wallet<K, D>);

// We need manual derivation to ensure we can be copied and cloned even if descriptor is not
// copyable/clonable.
impl<'a, K, D: DescriptorRgb<K>> Copy for WitnessOutpointsFilter<'a, K, D> {}
impl<'a, K, D: DescriptorRgb<K>> Clone for WitnessOutpointsFilter<'a, K, D> {
impl<'a, K, D: DescriptorRgb<K>> Copy for WalletWitnessFilter<'a, K, D> {}
impl<'a, K, D: DescriptorRgb<K>> Clone for WalletWitnessFilter<'a, K, D> {
fn clone(&self) -> Self { *self }
}

impl<'a, K, D: DescriptorRgb<K>> AssignmentsFilter for WitnessOutpointsFilter<'a, K, D> {
impl<'a, K, D: DescriptorRgb<K>> AssignmentsFilter for WalletWitnessFilter<'a, K, D> {
fn should_include(&self, _: impl Into<XOutpoint>, witness_id: Option<XWitnessId>) -> bool {
self.0
.history()
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,5 @@ pub mod resolvers {
}
}
}
pub use filters::{WalletOutpointsFilter, WitnessOutpointsFilter};
pub use filters::{WalletOutpointsFilter, WalletUnspentFilter, WalletWitnessFilter};
pub use wallet::RgbWallet;
19 changes: 11 additions & 8 deletions src/pay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,12 @@ 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::{
CompletionError, CompositionError, DescriptorRgb, PayError, RgbKeychain, Txid,
WitnessOutpointsFilter, XWitnessId,
WalletOutpointsFilter, WalletUnspentFilter, WalletWitnessFilter, XWitnessId,
};

#[derive(Clone, PartialEq, Debug)]
Expand Down Expand Up @@ -91,7 +90,7 @@ where W::Descr: DescriptorRgb<K>
{
fn should_include(&self, output: impl Into<XOutpoint>, id: Option<XWitnessId>) -> bool {
let output = output.into();
if !self.wallet.filter().should_include(output, id) {
if !self.wallet.filter_unspent().should_include(output, id) {
return false;
}
matches!(self.stock.contract_assignments_for(self.contract_id, [output]), Ok(list) if !list.is_empty())
Expand All @@ -101,13 +100,15 @@ where W::Descr: DescriptorRgb<K>
pub trait WalletProvider<K>: PsbtConstructor
where Self::Descr: DescriptorRgb<K>
{
fn filter(&self) -> impl AssignmentsFilter + Clone;
fn filter_outpoints(&self) -> impl AssignmentsFilter + Clone;
fn filter_unspent(&self) -> impl AssignmentsFilter + Clone;
fn filter_witnesses(&self) -> impl AssignmentsFilter + Clone;
fn with_descriptor_mut<R>(
&mut self,
f: impl FnOnce(&mut WalletDescr<K, Self::Descr>) -> R,
) -> R;
fn outpoints(&self) -> impl Iterator<Item = Outpoint>;
fn utxos(&self) -> impl Iterator<Item = Outpoint>;
fn txos(&self) -> impl Iterator<Item = Outpoint>;
fn txids(&self) -> impl Iterator<Item = Txid>;
fn history(&self) -> impl Iterator<Item = TxRow<impl Layer2Tx>> + '_;

Expand Down Expand Up @@ -352,12 +353,14 @@ where Self::Descr: DescriptorRgb<K>
}

impl<K, D: DescriptorRgb<K>> WalletProvider<K> for Wallet<K, D> {
fn filter(&self) -> impl AssignmentsFilter + Clone { WalletOutpointsFilter(self) }
fn filter_witnesses(&self) -> impl AssignmentsFilter + Clone { WitnessOutpointsFilter(self) }
fn filter_outpoints(&self) -> impl AssignmentsFilter + Clone { WalletOutpointsFilter(self) }
fn filter_unspent(&self) -> impl AssignmentsFilter + Clone { WalletUnspentFilter(self) }
fn filter_witnesses(&self) -> impl AssignmentsFilter + Clone { WalletWitnessFilter(self) }
fn with_descriptor_mut<R>(&mut self, f: impl FnOnce(&mut WalletDescr<K, D>) -> R) -> R {
self.descriptor_mut(f)
}
fn outpoints(&self) -> impl Iterator<Item = Outpoint> { self.coins().map(|coin| coin.outpoint) }
fn utxos(&self) -> impl Iterator<Item = Outpoint> { self.coins().map(|coin| coin.outpoint) }
fn txos(&self) -> impl Iterator<Item = Outpoint> { self.txos().map(|txo| txo.outpoint) }
fn txids(&self) -> impl Iterator<Item = Txid> { self.transactions().keys().copied() }

fn history(&self) -> impl Iterator<Item = TxRow<impl Layer2Tx>> + '_ { self.history() }
Expand Down
2 changes: 1 addition & 1 deletion src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ where W::Descr: DescriptorRgb<K>
) -> Result<Vec<ContractOp>, StockError<S, H, P, ContractIfaceError>> {
let contract = self.stock.contract_iface(contract_id, iface.into())?;
let wallet = &self.wallet;
Ok(contract.history(wallet.filter(), wallet.filter_witnesses()))
Ok(contract.history(wallet.filter_outpoints(), wallet.filter_witnesses()))
}

#[allow(clippy::result_large_err)]
Expand Down

0 comments on commit 07c1b12

Please sign in to comment.