diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 269b2ee..8c7c0c9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -55,6 +55,7 @@ jobs: matrix: os: [ ubuntu-22.04, ubuntu-latest, macos-13, macos-latest, windows-2019, windows-latest ] steps: + - uses: ilammy/setup-nasm@v1 - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - name: Platform ${{matrix.os}} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9821d00..81ede7e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,6 +23,7 @@ jobs: matrix: os: [ ubuntu-latest, macos-13, macos-latest, windows-latest ] steps: + - uses: ilammy/setup-nasm@v1 - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - name: Test ${{matrix.os}} @@ -31,7 +32,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly + - uses: dtolnay/rust-toolchain@stable - uses: jetli/wasm-pack-action@v0.4.0 - name: Add wasm32 target run: rustup target add wasm32-unknown-unknown diff --git a/Cargo.lock b/Cargo.lock index 27d6436..2e61ffd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -165,9 +165,9 @@ dependencies = [ [[package]] name = "arrayref" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" @@ -205,9 +205,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "aws-lc-rs" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae74d9bd0a7530e8afd1770739ad34b36838829d6ad61818f9230f683f5ad77" +checksum = "2f95446d919226d587817a7d21379e6eb099b97b45110a7f272a444ca5c54070" dependencies = [ "aws-lc-sys", "mirai-annotations", @@ -217,9 +217,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.20.1" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f0e249228c6ad2d240c2dc94b714d711629d52bad946075d8e9b2f5391f0703" +checksum = "234314bd569802ec87011d653d6815c6d7b9ffb969e9fee5b8b20ef860e8dce9" dependencies = [ "bindgen", "cc", @@ -393,9 +393,9 @@ dependencies = [ [[package]] name = "bp-electrum" -version = "0.11.0-beta.8" +version = "0.11.0-beta.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26ca7edfdada81772395612daccc1a62e1bb2ed99820832f329c06d6126dbeb4" +checksum = "0db2dc3a49489718e6e06cb796c536ca39eef8c036051afcb3f8d51cbc610fb8" dependencies = [ "amplify", "bp-std", @@ -425,6 +425,20 @@ dependencies = [ "ureq", ] +[[package]] +name = "bp-esplora" +version = "0.11.0-beta.8" +source = "git+https://github.com/BP-WG/bp-esplora-client?branch=master#4cdad88d83e660a06aae5457cd2c7eeed369baa5" +dependencies = [ + "amplify", + "bp-std", + "log", + "serde", + "serde_with", + "sha2", + "ureq", +] + [[package]] name = "bp-invoice" version = "0.11.0-beta.8" @@ -476,12 +490,13 @@ dependencies = [ [[package]] name = "bp-wallet" version = "0.11.0-beta.8" -source = "git+https://github.com/BP-WG/bp-wallet?branch=develop#1aba927f1398ca8312a4c48a632849d4ae987626" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b3cda17bafd67254e6f0be1b401a31d942dc54be138d5cc7976bb58291dff0" dependencies = [ "amplify", "base64", "bp-electrum", - "bp-esplora", + "bp-esplora 0.11.0-beta.8 (registry+https://github.com/rust-lang/crates.io-index)", "bp-std", "clap", "colored", @@ -513,9 +528,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.1.16" +version = "1.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9d013ecb737093c0e86b151a7b837993cf9ec6c502946cfb44bedc392421e0b" +checksum = "2d74707dde2ba56f86ae90effb3b43ddd369504387e718014de010cec7959800" dependencies = [ "jobserver", "libc", @@ -682,9 +697,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -1112,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]] @@ -1223,9 +1238,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "33ea5043e58958ee56f3e15a90aee535795cd7dfd319846288d93c5b57d85cbe" [[package]] name = "option-ext" @@ -1398,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#77f6ab81aadf09c04869fab1fa33eaa8b3b9366e" dependencies = [ "amplify", "baid64", @@ -1464,7 +1459,7 @@ dependencies = [ "baid64", "bp-core", "bp-electrum", - "bp-esplora", + "bp-esplora 0.11.0-beta.8 (git+https://github.com/BP-WG/bp-esplora-client?branch=master)", "bp-std", "bp-wallet", "chrono", @@ -1487,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#77f6ab81aadf09c04869fab1fa33eaa8b3b9366e" dependencies = [ "aluvm", "amplify", @@ -1524,7 +1518,6 @@ dependencies = [ "env_logger", "log", "psbt", - "rgb-interfaces", "rgb-runtime", "rgb-std", "serde", @@ -1567,9 +1560,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.38.35" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags 2.6.0", "errno", @@ -1580,9 +1573,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.12" +version = "0.23.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" dependencies = [ "aws-lc-rs", "log", @@ -1602,9 +1595,9 @@ checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" [[package]] name = "rustls-webpki" -version = "0.102.7" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84678086bd54edf2b415183ed7a94d0efb049f1b646a33e22a36f3794be6ae56" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "aws-lc-rs", "ring", @@ -1635,9 +1628,9 @@ checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "secp256k1" -version = "0.29.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0cc0f1cf93f4969faf3ea1c7d8a9faed25918d96affa959720823dfe86d4f3" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ "rand", "secp256k1-sys", @@ -1646,9 +1639,9 @@ dependencies = [ [[package]] name = "secp256k1-sys" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1433bd67156263443f14d603720b082dd3121779323fce20cba2aa07b874bc1b" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" dependencies = [ "cc", ] @@ -1678,18 +1671,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -2027,9 +2020,9 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" @@ -2467,17 +2460,3 @@ name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] diff --git a/Cargo.toml b/Cargo.toml index d200c5a..ef72c8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ bp-core = "0.11.0-beta.8" bp-seals = "0.11.0-beta.8" bp-std = "0.11.0-beta.8" bp-electrum = "0.11.0-beta.8" -bp-esplora = { version = "0.11.0-beta.8", default-features = false, features = ["blocking"] } +bp-esplora = { version = "0.11.0-beta.8", default-features = false, git = "https://github.com/BP-WG/bp-esplora-client", branch = "master" } descriptors = "0.11.0-beta.8" psbt = { version = "0.11.0-beta.8", features = ["client-side-validation"] } bp-wallet = { version = "0.11.0-beta.8" } @@ -90,11 +90,12 @@ getrandom = { version = "0.2", features = ["js"] } wasm-bindgen-test = "0.3" [features] -default = ["esplora_blocking", "mempool_blocking"] +default = [] all = ["esplora_blocking", "electrum_blocking", "mempool_blocking", "serde", "log", "fs", "cli"] fs = ["serde", "bp-wallet/fs", "rgb-std/fs"] cli = ["fs", "bp-wallet/cli"] -esplora_blocking = ["bp-esplora"] +esplora_blocking = ["bp-esplora", "bp-esplora/blocking"] +esplora_blocking-wasm = ["bp-esplora", "bp-esplora/blocking-wasm"] electrum_blocking = ["bp-electrum"] mempool_blocking = ["esplora_blocking"] serde = ["serde_crate", "serde_yaml", "bp-std/serde", "descriptors/serde", "rgb-psbt/serde"] @@ -103,4 +104,4 @@ 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" } diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 6a14bbf..f2c1537 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 = { version = "0.11.0-beta.8", features = ["fs"] } rgb-runtime = { version = "0.11.0-beta.8", path = "..", features = ["electrum_blocking", "esplora_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 7d368cf..7f6e6ec 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -29,26 +29,23 @@ 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::resolvers::ContractIssueResolver; 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}; @@ -75,10 +72,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")] @@ -134,13 +128,17 @@ pub enum Command { /// Print operation history for a default fungible token under a given /// interface - #[display("history-fungible")] - HistoryFungible { + #[display("history")] + History { + /// Print detailed information + #[clap(long)] + details: bool, + /// Contract identifier contract_id: ContractId, /// Interface to interpret the state data - iface: String, + iface: Option, }, /// Display all known UTXOs belonging to this wallet @@ -342,57 +340,91 @@ 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::History { + 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 iface: TypeName = match iface { + Some(iface) => tn!(iface.clone()), + None => { + let stock = wallet.stock(); + let info = stock.contract_info(*contract_id)?; + let schema = stock.schema(info.schema_id)?; + match schema.iimpls.len() { + 0 => { + eprintln!( + "contract doesn't implement any interface and thus can't be \ + read\n" + ); + return Ok(()); + } + 1 => schema + .iimpls + .first_key_value() + .expect("one interface is present") + .0 + .clone(), + _ => { + eprintln!( + "contract implements multiple interface, please select one of \ + them to read the contract:" + ); + for iface in schema.iimpls.keys() { + eprintln!("{iface}"); + } + eprintln!(); + return Ok(()); + } } - }; - 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!(); + } } } @@ -534,11 +566,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, } @@ -548,7 +584,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 55605d5..b85a6d7 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -54,14 +54,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), @@ -112,17 +109,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 085bfdc..750af4d 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 { @@ -57,5 +57,5 @@ pub mod resolvers { } } } +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 6293ce7..ec41e2c 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; @@ -29,19 +28,21 @@ use bpstd::XpubDerivable; use bpwallet::fs::FsTextStore; #[cfg(feature = "fs")] use bpwallet::Wallet; -use nonasync::persistence::{PersistenceError, PersistenceProvider}; +#[cfg(not(target_arch = "wasm32"))] +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; @@ -76,6 +77,7 @@ impl, S: StashProvider, H: StateProvider, P: IndexProvide FsBinStore: PersistenceProvider, FsBinStore: PersistenceProvider

, { + use nonasync::persistence::PersistenceError; let provider = FsBinStore::new(stock_path) .map_err(|e| WalletError::StockPersist(PersistenceError::with(e)))?; let stock = Stock::load(provider, autosave).map_err(WalletError::StockPersist)?; @@ -106,34 +108,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) - } -}