From c2a3a705b70a644c1a78e2f626d5aac9668581cb Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sat, 7 Sep 2024 17:04:08 +0200 Subject: [PATCH 01/20] cli: improve short help string --- cli/src/command.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/cli/src/command.rs b/cli/src/command.rs index 2541e47..d73bf18 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -175,8 +175,9 @@ pub enum Command { value: u64, }, - /// Prepare PSBT file for transferring RGB assets. In the most of cases you - /// need to use `transfer` command instead of `prepare` and `consign`. + /// Prepare PSBT file for transferring RGB assets + /// + /// In the most of cases you need to use `transfer` command instead of `prepare` and `consign`. #[display("prepare")] Prepare { /// Encode PSBT as V2 @@ -198,8 +199,9 @@ pub enum Command { psbt: Option, }, - /// Prepare consignment for transferring RGB assets. In the most of the - /// cases you need to use `transfer` command instead of `prepare` and + /// Prepare consignment for transferring RGB assets + /// + /// In the most of the cases you need to use `transfer` command instead of `prepare` and /// `consign`. #[display("prepare")] Consign { @@ -299,6 +301,7 @@ pub enum Command { #[display(lowercase)] #[clap(hide = true)] pub enum DebugCommand { + /// List known tapret tweaks for a wallet Taprets, } From 52fa963b5e44ee65edc92bcb3959ff6bbcd940f8 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sat, 7 Sep 2024 19:24:45 +0200 Subject: [PATCH 02/20] cli: fix stock creation --- Cargo.lock | 4 ++-- cli/src/args.rs | 16 ++++++++++++---- src/errors.rs | 10 ++-------- src/wallet.rs | 8 +++++--- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9fdc757..cd8c33f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1199,9 +1199,9 @@ dependencies = [ [[package]] name = "nonasync" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94a84b7c873630913f738950f17412b9d5b24cad6866b98b802253f8cbbefabb" +checksum = "4b1005555d351f593bf72ffc3a89a0d42e243df004d2c4ded17699f10b562b98" dependencies = [ "amplify", "log", diff --git a/cli/src/args.rs b/cli/src/args.rs index 4f4697a..4614f5d 100644 --- a/cli/src/args.rs +++ b/cli/src/args.rs @@ -104,19 +104,27 @@ impl RgbArgs { } let provider = FsBinStore::new(stock_path.clone())?; - let mut stock = Stock::load(provider, true).map_err(WalletError::WalletPersist).or_else(|err| { - if matches!(err, WalletError::Deserialize(DeserializeError::Decode(DecodeError::Io(ref err))) if err.kind() == ErrorKind::NotFound) { + let mut stock = Stock::load(provider, true).or_else(|err| { + if err + .0 + .downcast_ref::() + .map(|e| matches!(e, DeserializeError::Decode(DecodeError::Io(ref e)) if e.kind() == ErrorKind::NotFound)) + .unwrap_or_default() + { if self.verbose > 1 { eprint!("stock file is absent, creating a new one ... "); } fs::create_dir_all(&stock_path)?; let provider = FsBinStore::new(stock_path)?; let mut stock = Stock::in_memory(); - stock.make_persistent(provider, true).map_err(WalletError::StockPersist)?; + stock + .make_persistent(provider, true) + .map_err(WalletError::StockPersist)?; return Ok(stock); } eprintln!("stock file is damaged, failing"); - Err(err) + error!("Unable to load stock data: {err:?}"); + Err(WalletError::StockPersist(err)) })?; if self.sync { diff --git a/src/errors.rs b/src/errors.rs index 70bfce9..55605d5 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -34,7 +34,7 @@ use rgbstd::persistence::{ ComposeError, ConsignError, ContractIfaceError, FasciaError, Stock, StockError, StockErrorAll, StockErrorMem, }; -use strict_types::encoding::{DeserializeError, Ident, SerializeError}; +use strict_types::encoding::Ident; use crate::{validation, TapTweakAlreadyAssigned}; @@ -43,13 +43,7 @@ use crate::{validation, TapTweakAlreadyAssigned}; pub enum WalletError { #[from] #[from(io::Error)] - Io(IoError), - - #[from] - Serialize(SerializeError), - - #[from] - Deserialize(DeserializeError), + File(IoError), #[from] StockLoad(LoadError), diff --git a/src/wallet.rs b/src/wallet.rs index e47d364..6293ce7 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -29,7 +29,7 @@ use bpstd::XpubDerivable; use bpwallet::fs::FsTextStore; #[cfg(feature = "fs")] use bpwallet::Wallet; -use nonasync::persistence::PersistenceProvider; +use nonasync::persistence::{PersistenceError, PersistenceProvider}; use psrgbt::{Psbt, PsbtMeta}; use rgbstd::containers::Transfer; use rgbstd::interface::{AmountChange, IfaceOp, IfaceRef}; @@ -76,9 +76,11 @@ impl, S: StashProvider, H: StateProvider, P: IndexProvide FsBinStore: PersistenceProvider, FsBinStore: PersistenceProvider

, { - let provider = FsBinStore::new(stock_path)?; + let provider = FsBinStore::new(stock_path) + .map_err(|e| WalletError::StockPersist(PersistenceError::with(e)))?; let stock = Stock::load(provider, autosave).map_err(WalletError::StockPersist)?; - let provider = FsTextStore::new(wallet_path)?; + let provider = FsTextStore::new(wallet_path) + .map_err(|e| WalletError::WalletPersist(PersistenceError::with(e)))?; let wallet = Wallet::load(provider, autosave).map_err(WalletError::WalletPersist)?; Ok(Self { wallet, From 86939074f7a1abc9b8728bc8b9201682f0c4a819 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sat, 7 Sep 2024 19:25:01 +0200 Subject: [PATCH 03/20] chore: move interfaces dependency to cli --- Cargo.lock | 3 +-- Cargo.toml | 6 ++++-- cli/Cargo.toml | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cd8c33f..27d6436 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -476,8 +476,7 @@ dependencies = [ [[package]] name = "bp-wallet" version = "0.11.0-beta.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b3cda17bafd67254e6f0be1b401a31d942dc54be138d5cc7976bb58291dff0" +source = "git+https://github.com/BP-WG/bp-wallet?branch=develop#1aba927f1398ca8312a4c48a632849d4ae987626" dependencies = [ "amplify", "base64", diff --git a/Cargo.toml b/Cargo.toml index 2ca0e45..d200c5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ license = "Apache-2.0" [workspace.dependencies] amplify = "4.7.0" -nonasync = { version = "0.1.0", features = ["log"] } +nonasync = { version = "0.1.2", features = ["log"] } baid64 = "0.2.2" strict_encoding = "2.7.0" strict_types = "2.7.0" @@ -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"] } @@ -102,3 +101,6 @@ serde = ["serde_crate", "serde_yaml", "bp-std/serde", "descriptors/serde", "rgb- [package.metadata.docs.rs] features = ["all"] + +[patch.crates-io] +bp-wallet = { git = "https://github.com/BP-WG/bp-wallet", branch = "develop" } diff --git a/cli/Cargo.toml b/cli/Cargo.toml index b0db47f..6a14bbf 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -26,8 +26,8 @@ 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", "log", "serde", "fs", "cli"] } +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" clap = { version = "4.5.17", features = ["derive", "env"] } From d8c5fd2fcfa39d1ee35245ee7e6d91029253c40c Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sat, 7 Sep 2024 19:47:59 +0200 Subject: [PATCH 04/20] resolvers: add ContractIssueResolver --- src/lib.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 7dd5e57..085bfdc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,6 +40,22 @@ pub mod resolvers { #[cfg(any(feature = "electrum_blocking", feature = "esplora_blocking"))] pub use super::indexers::*; pub use super::indexers::{AnyResolver, RgbResolver}; + use super::validation::{ResolveWitness, WitnessResolverError}; + use super::vm::{WitnessOrd, XWitnessTx}; + use super::XWitnessId; + + pub struct ContractIssueResolver; + impl ResolveWitness for ContractIssueResolver { + fn resolve_pub_witness(&self, _: XWitnessId) -> Result { + panic!("contract issue resolver must not be used for an already-existing contracts") + } + fn resolve_pub_witness_ord( + &self, + _: XWitnessId, + ) -> Result { + panic!("contract issue resolver must not be used for an already-existing contracts") + } + } } pub use wallet::RgbWallet; pub use wrapper::WalletWrapper; From ce1b90800784a29e82f70d87f594473f7d80f5a9 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sat, 7 Sep 2024 19:48:17 +0200 Subject: [PATCH 05/20] cli: does not require issue command to connect to an indexer/resolver --- cli/src/command.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/src/command.rs b/cli/src/command.rs index d73bf18..7d368cf 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -38,6 +38,7 @@ use rgb::containers::{ use rgb::interface::{AmountChange, IfaceId, OutpointFilter}; use rgb::invoice::{Beneficiary, Pay2Vout, RgbInvoice, RgbInvoiceBuilder, XChainNet}; use rgb::persistence::StashReadProvider; +use rgb::resolvers::ContractIssueResolver; use rgb::schema::SchemaId; use rgb::validation::Validity; use rgb::vm::RgbIsa; @@ -753,8 +754,7 @@ impl Exec for RgbArgs { let contract = builder.issue_contract()?; let id = contract.contract_id(); - let resolver = self.resolver()?; - stock.import_contract(contract, &resolver)?; + stock.import_contract(contract, &ContractIssueResolver)?; eprintln!( "A new contract {id} is issued and added to the stash.\nUse `export` command \ to export the contract." From 22e50ec02313c660d9c30626c2d4614a265cf81b Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sun, 8 Sep 2024 00:17:11 +0200 Subject: [PATCH 06/20] example: improve demo contract --- examples/rgb20-demo.con | 53 ++++++++++------- examples/rgb20-demo.rgb | Bin 20331 -> 6850 bytes examples/rgb20-demo.rgba | 120 +++++++++++++++++++++++++++++++++++++++ examples/rgb20-demo.yaml | 6 +- 4 files changed, 154 insertions(+), 25 deletions(-) create mode 100644 examples/rgb20-demo.rgba diff --git a/examples/rgb20-demo.con b/examples/rgb20-demo.con index a6abe6f..bfeef2c 100644 --- a/examples/rgb20-demo.con +++ b/examples/rgb20-demo.con @@ -1,25 +1,34 @@ -contract Test: RGB20 - spec: - "TEST" "Test Asset" centiMicro - terms: - """ - SUBJECT TO, AND WITHOUT IN ANY WAY LIMITING, THE REPRESENTATIONS AND WARRANTIES OF ANY SELLER - EXPRESSLY SET FORTH IN THIS AGREEMENT OR ANY OTHER EXPRESS OBLIGATION OF SELLERS PURSUANT TO THE - TERMS HEREOF, AND ACKNOWLEDGING THE PRIOR USE OF THE PROPERTY AND PURCHASER’S OPPORTUNITY - TO INSPECT THE PROPERTY, PURCHASER AGREES TO PURCHASE THE PROPERTY “AS IS”, “WHERE IS”, - WITH ALL FAULTS AND CONDITIONS THEREON. ANY WRITTEN OR ORAL INFORMATION, REPORTS, STATEMENTS, - DOCUMENTS OR RECORDS CONCERNING THE PROPERTY PROVIDED OR MADE AVAILABLE TO PURCHASER, ITS AGENTS - OR CONSTITUENTS BY ANY SELLER, ANY SELLER’S AGENTS, EMPLOYEES OR THIRD PARTIES REPRESENTING OR - PURPORTING TO REPRESENT ANY SELLER, SHALL NOT BE REPRESENTATIONS OR WARRANTIES, UNLESS - SPECIFICALLY SET FORTH HEREIN. IN PURCHASING THE PROPERTY OR TAKING OTHER ACTION HEREUNDER, - PURCHASER HAS NOT AND SHALL NOT RELY ON ANY SUCH DISCLOSURES, BUT RATHER, PURCHASER SHALL RELY - ONLY ON PURCHASER’S OWN INSPECTION OF THE PROPERTY AND THE REPRESENTATIONS AND WARRANTIES - HEREIN. PURCHASER ACKNOWLEDGES THAT THE PURCHASE PRICE REFLECTS AND TAKES INTO ACCOUNT THAT THE - PROPERTY IS BEING SOLD “AS IS”. - """ +contract Test: NonInflatableAsset + global spec + ticker = "Demo", + name = "Demo asset" + details = "Pay attention: the asset has no value" + precision = centiMicro - issuedSupply: + global terms + text = """ + SUBJECT TO, AND WITHOUT IN ANY WAY LIMITING, THE REPRESENTATIONS AND WARRANTIES OF ANY SELLER + EXPRESSLY SET FORTH IN THIS AGREEMENT OR ANY OTHER EXPRESS OBLIGATION OF SELLERS PURSUANT TO THE + TERMS HEREOF, AND ACKNOWLEDGING THE PRIOR USE OF THE PROPERTY AND PURCHASER’S OPPORTUNITY + TO INSPECT THE PROPERTY, PURCHASER AGREES TO PURCHASE THE PROPERTY “AS IS”, “WHERE IS”, + WITH ALL FAULTS AND CONDITIONS THEREON. ANY WRITTEN OR ORAL INFORMATION, REPORTS, STATEMENTS, + DOCUMENTS OR RECORDS CONCERNING THE PROPERTY PROVIDED OR MADE AVAILABLE TO PURCHASER, ITS AGENTS + OR CONSTITUENTS BY ANY SELLER, ANY SELLER’S AGENTS, EMPLOYEES OR THIRD PARTIES REPRESENTING OR + PURPORTING TO REPRESENT ANY SELLER, SHALL NOT BE REPRESENTATIONS OR WARRANTIES, UNLESS + SPECIFICALLY SET FORTH HEREIN. IN PURCHASING THE PROPERTY OR TAKING OTHER ACTION HEREUNDER, + PURCHASER HAS NOT AND SHALL NOT RELY ON ANY SUCH DISCLOSURES, BUT RATHER, PURCHASER SHALL RELY + ONLY ON PURCHASER’S OWN INSPECTION OF THE PROPERTY AND THE REPRESENTATIONS AND WARRANTIES + HEREIN. PURCHASER ACKNOWLEDGES THAT THE PURCHASE PRICE REFLECTS AND TAKES INTO ACCOUNT THAT THE + PROPERTY IS BEING SOLD “AS IS”. + """ + media = ~ + + global issuedSupply 1_000_000__000_000_00 - assetOwner: - 1_000_000__000_000_00 tapret1st:01d46e52c4bdb51931a0eae83e958c78bdef9cac2057b36d55370410edafdd42:0 + owned assetOwner + state = 1_000_000__000_000_00 + seal = + method := tapret1st + txid = #01d46e52c4bdb51931a0eae83e958c78bdef9cac2057b36d55370410edafdd42 + vout = 0 diff --git a/examples/rgb20-demo.rgb b/examples/rgb20-demo.rgb index 1e777f587835d108285f3aa4b246c65bfb35ee28..0e2644aa8ce489ac17512091b78b0a19e8cb6d97 100644 GIT binary patch delta 3504 zcmcgvdsq`!7QZuj@{ov7Xa#iwMT$xVL{fLkZ zQg$bm790m~Ji}kER4H}YDlHBHNG~YxLx_mSTC=1`?ymGlms?Ta^K0V@^PhaMuj*9U z=2Mk`b&Cf9U%(0xi7i5~WNjKE$4|cuzs!#8qz?E1boX;;<&-W60xSTrpJUT?yBhY1 zV^=>NOgnQ^m$0VS%HX=xcH4_XJ@lyHq>As!(*SATb#DVmXa0o_!Xj{Lm z3DGKLxH2_Mu8}8aVL>>KX-YqyVv5$=6m8@ZnWr|-JuNfDPq`>2%w3X#Y2xEoCc68m zXK7`@G9P!jG#&R#oSo|K8xcAuHkS?>CYoemb;D%K4()+f5;`p!7k z^4DtNRw_`?!~;uX`ndPr&dnfR6UUoJt-qkFbn9Ouy+U{qwo(?z5<$nCA{?7dJ zjX#X@dA#?%O_yrWc6{|bM;poUK-*Lo9%n$ES#b*1w#{=D1W3~2gB z4PpwJHYX=bN7FE36;Sp^_0rV5b#a6vE80&?5tV^w%QY$K5MQH~EAdoJO+!3ZeC%%k z38?A|U8R+01yLoa)ZlMR0FyGpqzxWpM}eFCL}+9(QZq0GRfdQhNy}0t%d?Oixmuo$ zQD`6v6^1KaHHgVMM3asoBmN?z_vKkxsua0~y01~0j!nQIDjFHw8J5_40&2-uc{pU+OAs`Pur&#Z{!P*dcH#B zctX3~whn9)fNhVOTPIh%H>dlCV2^G6-gR!>H`nNEIMDSvby6~NqIsJ3kv=jC>qe7zx3yyH0q@=5568whx zlGC#Cqhej^qNfh(PY!XvS#iE{y33YH0;YyXV}<2uD8hv<*_a|jet+{}@A6rj_Z+{p zwahx*U-Z%RmcS4H+fQqK2YZC=)y}4 z2XCL&&WPluOOHS)aCse5+WuTsUto zILo&MJ6oB+VR`>bt}XmuCL0|WHF%Ikc1{FS9daP1uE11vT$ZUg|Fat6m+r#DwaIi9 zGqn$1O1fN4y=a)^E|xStsrO6t{8RM}`4G5g{9~J&kIb)rNzeVwht8QF3F8Me9o$cD zvvzzl0L>q5hD=OyV5a8LZ?G4JTyMUc{GCzKH)N}rL}upK>Kg(t?pCkbux0J}Yr`c~ zr?&!qw2x3$Lb{B%A$Lu7a$@SP9GJ`!A_P-lOtTS}&DCV2ti;qz&4#U{28Vx3e8HH^ zC&g1fAeX#5jpReZ6mVzq%-94%jkbFjHf$osRW^LSx36H9^}TPO1_ngPT^4X4re@CA zkEquN!DNPs06r6gqp1L>iQ2SY*z?(UtJu2dUme#RolCA3t`DOA(XfId=K-4NeKs{TjOhF)mWfh- literal 20331 zcmeG^d0Z1ov+3M`9HMxj!g%4TD9E88sDuz80tqGoL}MHiJ-5EXCv z#QQ$i_4C%x3-1d>S#Ml#-Sx&}<#kU0ao6`|_osin_j{Q?x_YL&YpScOyQ`{eMiv$< zl*okX%a}5`^Yes(WE#m<)Ka&+_IaMQy+1He;S* zX3rbt=aZMq<}kYkUR=9BR$}Nd)cf5tz&OWi7i@!{6*$XUwfm9zqH4mLc`;-2E^OV! zTaY;PBo^ph4H)Nnlcx^GRehfYlx#ocJgwk))q$(cZRQvUU8$&gadc4I!ij)!ftU1e z@1O@Xj`j5&746xW9@EMl}?7it>`|D4-8v9!= zZ~pjQd34&GgkI|(JYBLoXr6be4w4P4Lc&mbo=PuJDQeEBtz|HPLl-(E)VpBi`Zi+N zs&(2AUCUorg+w_?rY)Shr__Gh6y}hCO~vhx^pkCJnZyCh&%F0CHRFwwx*i*0oRJr@ z*rjZ$uIkm?Dw{apb-ct%_T9nb-Ct+#n-G6za^{{w#gpFp!Y;o4ekpcWE*0L}@RZ+n z?Y@JT-S+Q0_aMu9a_PI1KPIlbL$d~YqNk?9S3$U`WR7u&8o|?Hh0*2 z3YUbgK}gV5kC3iDmu;PH#51KPGTlVp9~pFNSm(EUZcnV$E^q3!QqaA9%pSW~#{>JS zCzgyj%(Wu4$#RuZn@;+f*qbB9*oCHrm9`8U+llNnzJ{fK+%ICyo4(i^0h!7t@C*~K zSK)L#=4EL~Jx&mdVrH(}`S#S!1%9D56Ra{W_#N}mn|6<>#zduS{aUYZvC&1_&TT9K7T4phcG|#*tSQFIdAlM7mOiV{ANIrEAa4Fd0ak=)g`<5-Ap<3<&(Cp%jy(_ofR>eX)6%5(!k8V%_wco%+>QK?GB$;j{ObF!{{B|+jw zEs@%dovt$x-8m{lTAI?Glcv?1h#oApf$Yg4bnq=6>`}PU;K?%R@m?%4%izT!Q;az7 z&DI!9Mjy6Dn~D3f2yJ$64uNOl`aT?7o18-WvGrO#?hmo*4EnxS1fHf9qHF+L2e}f+ zfoxRj`tg#DxDt}lpAWf8kXnO&08>c@0f7Jn2LquHgoFT*2!w`$urLrF4#Z+Ga3F|? z0FjX(Dhfy>Kq>{%(O}RZAd>;P94HhZCI-aDg19&^crb{M2SbKf6B)sE{U2(T1Q{Vm z-5U1ku`Ge3QW|kGk*hPPVbO3tszqv5sUv_=36he4N(Izvfa5@;0m;cAB?V};VB|=U znhJC}kd_AYdSEbs^mLGs0gOgK5P&3s$pkVpK~@&X&IUO-VALopRXri2+3DJJd`!aw z*;o##)v57ucITY~TK`b7Y!jJnpp$4$ zy3t_JJm1+Vw?*mLzTtl`JbSp_$lX!hv56Uis*`7*sM;_!L|bMu(T-^(T$m=p)iNReKTQah z8rO|)B(PoTUyyvG<#lC{GWo%;VqV|da<*|+RgkCPW$ii)T>_>+-p}6 ze*7hCXzin4l74$*Tb>h~UsG%^0Os9;!LNg@wsUSJ?0A%wb>Qt)pVHs;S6pW0dPJ?Y zf0mP9VogjP#{pAwmjB0bZ=1Zt)_WA=tem&!j%f4qy^il+XcWbvcFU)9ZFORn!Exr9 zR(6ZBuO1nCctMy)?Z@@+UrpOQ_vD1{UPk~9h_#*lW?4?BEeCI~?bIs&qD#Xs7US++ zg~9ulzx8(-q@dkHgXk0%E|UWn&oqLTmNZLMR)%bmxBCVI7@!=0P6 zAM6=BEdSN58wpupIf*QcZSo!N$v|l@fKo5GocRv-W}(!F zjZ$9@N_%rr+J}cyKR!zRtvE34-wpw9PZtXGa_zkK)egtz zEq5;UuNXes$+R*f;?^irtAw+?d`DFr>+v9;1Lzd0ZbR3si=I+zHO`IkqiZ}mVZj(ZI=A-cF|RBQkI1<_<=(F6tBzN`Pw2jXc}-Zmorb0R z16j`Bc3bUnt}0x7Khp}0Od#-1-UzMV7<2fw7BO|<5;qk@BNj6tx zo~s898M&tU-0tg22W(DUA}ib!B!jOh$b)+Ny}Kt#V9WGvL7bzI!K&dSfTl zWAjfZ-l@JGyX}}?aQ_{ryyphx7cE?auRA*_%9;D9=WS)=)r)=JWIC1iJ~3a}ZzgL1 z8|~dazD8U5wha2Vx`S_3J12ikzw6r7We59B>hkw63$}z{BP!9;JGD>POe{2 zJlf53_>@)4-nVb%*-b(mUT2+cdv`4(kzn;O>)4;`)-0$}yO~vFvw%^m@z+S%pOnpS zD<=IaAGdwW&YbN|ET8jqL&@c9v(_f+2ES6XtAwS;&Ood5kTJEW=(}^H4$LWUS7=+d zASqDc`K=CJZD6K`ytU+Qpf;hKx@9!@L_+%a)caq=affuilXo%6wAw=Drf z!2L;HjjObP zgX~)^6Pl+wot9;Vo<*T7+nOq%@*G&IG8(vbr7+8@FNJwNG&S@FD?Vx}fjMg{swK+W zKx<58XhIc5!sWqyZq zqc{AnVcxiBI+lBLYpQT$2-Y4P7mclN%R_Bv8){N|UOZC%SaQ zuVmpkw|gr)O~11qsxV*SYWcQeQtt}+w!2eq+y>su(KGshzq6MQz;V-R_(|vSLceV)@{I|Ni|6x2?NsdXxsdfL3LuQfI>FysG2|Z|LSdqjSJ2 zNxRdBW>)X(e<5S@BqHBea&mHBY7>HTnMRwEQt_OtjrTsTY&U8l%Wlw@ei@$KmwNHcyTh zV;?sBHgn&rd@JY}R>951pr_l3=$yA4K&Nh%^*Zxw)~slda`Vb+R~~!juFzFou79{Z zsl4*t7-3e_b_^K9ofaTOo@W z0%Q?GfGlDNkVOmuvWOw@wJ)1gS`@qI(jQZocHANLzd1tPcg1}F_QkU8gGo;k%qChr6br4IGS^tmwR*y&(P&j#xG&4< z6e{*-Plbj|+^9^(IZj%FFyW$X6^^S3flg=0QbG?pcWGYmixZXS&Xze=O}jP1BetT( zV0Y+H=HdM*@sSa1JeC9KtYW%j3y=zwhy312TrvySW#TG6nW8n4IIdUok%t`RaGpl1K^e`eF-LOL z$-l-`hWvUgsqf7uCrYk3+j_8Wko!%J(!EDZIe>0=+mg3_af^9(-9P9C#^o#JwtJ4x zbYJnhQd*U&9NN7F_eVb6^sZa?8WjAWUGK$7Zst8&t<((HY}jJuIoq-0^x09EdO??M z_sUJpp~o3*g0y?8TAK`A_;f?Ib8+>DwToiTrR;KXn~)*jc>iA$mW*T1jfwJCy%xG& zc**MjH3RZ(_rPzv_wD|!c=M5whhN=wh+m3jRjumiT_zlQA(X$>hV=oAEfKM-zozGO z>o2`=PiGj?JFyOrZa;tD8-KkqG`$GM#>a+qJ8;AI9lecdv$U**v%+poN!i z8gR9Gcx*?|of`5C)bSocJ12AoRjNXphSNDw=!{DapfiW*j93PPZhPXgxajB!H_|Cf z(4I-d=}bzV8Yh)n9YJS=(>aI#nF?}8veWuvsVld{rMdJ$!H@$^3r>WMOP7w2G#kt8 z`0`*rT{#EPtx9wUU})9K)~f0D7M21_8zlujk06SzdZgfFJq;r z>(s=_^Ab48sb9Z|2}wm?IW-qI zl4}FbGw(hW7_Y3}(TC^c{LA=5P0FC__?w=C(m5$$Jj7jFLEgbfXYUKZ2mPFT(~pnr zIPcxR%G@5l6Y01L(Af@v!Le2n z(1e%5aW%rx09J@OqI3h)grUVq-N{f~1H1!{TD?Z6M9~Tj$}AH;wlSr{ZsYaUAQC!X zB)guaF#e~GX;khrwwTLMH_W+4ghFwzt^GtPyakLe5Jy#OOdVB`!2&EcmmLiI2>{(u z4fsr0C_>=*9f7OOj57BbJ?3gmq{)akdW?ud@8U2xVq^yZTDE|{_P_3BU+5ePlw#T> zK_wd*BT8cq_>4idwe=Kzp)Fz`gN@h#8e_1MrsH*E4~cKoO|v|}{xClY1jP%s9?vEl z$qd!!Iw7KX!<9ixAq|HNV(IZL=*dP>iJF?RHy#}7aXrzE3+j;874c-|sqi<(?oYFZ zqPTIl8b|5gBEb(u39~@ZX-z@QO zG#|BC;@>RsZ9uf#J^eM-_U3LmiV_Xgtt;^AEg1Be@#FaX<~_g`!*lHTjJj=@o%L%OZ=N9 z{_XQ$grFd5OZ?ksAL#+cqU$}%!zu+EZVOA-joRZnwZ|62CMEObi8C`Vy?;^oZVp|H z7yPvl6=Q-nyK_7$91CU7^F9x)fsZcm8iJ7E9sG;XXW=vGkwce;;0kzKcfpExy~}_dP!^(kpRd^~s}~G8ISRFMH^Efxg9|euCu3V1)#S*@WOpreur? z!~&h8l{pyvx()^0!9U1Q!Ff~(27i46gZD7%8xV(^LM#b$#}wfrOeTt!iR2=QLZA>!C315kflMZlD8wQ; zCJm+9$wiToA{mB>2BX^Y$aqXHQedG{nIasaR)mXT%P^Tp6a_J1QW@1q3h~S8n_$x5 zNO2hT5`@%@P>w~%$mB8bU65dk9EC_0CC6Ypku=oY1A#C?B8`g_g@i$xsXEazF?>Ue zT!avzK19o;(IS~5o+2J%6ow1rBH8WSd653-Xh>v?L<~#OJH!%sG{v7r&D;?c<~HU| z$WhJuXP>{}cJ4xf923iL=Pq)`;7uGNwf>&sHYSLS#6krzkqR?2g;Gff6w5-eXn~9pp9bkd8Y)TvVf|5JB(V^o4UL48#4Nv%J+P`+0;hyPD3r#ax!TaKe&WID9}HO?hRLOoA!uII zPm-S2)F0j8|7LEz;-EDX{n?Gr$WK_PMtA6MZGgdIfXzQXsa|DDi@Uyj&2IZ`(+7Dx szti(S$xR)fc7BxM)3h0E#9;ip%d+u7gBK?9>v4^vx#(ABL_@>%UkS#K%>V!Z diff --git a/examples/rgb20-demo.rgba b/examples/rgb20-demo.rgba new file mode 100644 index 0000000..e8bd385 --- /dev/null +++ b/examples/rgb20-demo.rgba @@ -0,0 +1,120 @@ +-----BEGIN RGB CONSIGNMENT----- +Id: rgb:csg:qX!1P1VF-il9DLFx-yjcpNvH-31pdCbd-gXhPSiG-7hvhtYY#austria-ricardo-demand +Version: 2 +Type: contract +Contract: rgb:Dl4Difx7-VxSWda4-Bv!Z89y-CbiVUkF-0vIxjRy-vvqO77A +Schema: rgb:sch:RDYhMTR!9gv8Y2GLv9UNBEK1hcrCmdLDFk9Qd5fnO8k#brave-dinner-banana +Interface: RGB20Fixed +Check-SHA256: db958c5cca1988c753665fc9a3da383615cbea62f0e1e1e6d7a7add2539d17ec + +0ssI2002ZbAu%+5_6z)DVT-@j4Fp29h04O2(!&-{P^aZh38Qb#nm# +0iX|oIUs*Z=yeHROa_%`=El@tIJ|sRf`I8Lo365whq9yq1JDNn05|{xL_$XkL}g-iXCPs7b7gb^B~W2` +AYpWLWo~q7Z*DpubZBKDVRLh3bRcM9b0BVSAa-GFb!7t42LS+;1d;?(RYFQdLsTGCPb?roPDCJANmNKr +Ra78JP9Q-}Ss+(ISs+YFO-WQqPDd;tR7gc2QbkZwMN>siR6$fpPfk-HK~6* +Ss+tIOiV>mARt9pP*O!xQ%qSPQ$6cNl#87Peve9MNCXZQd1yMRZ>$`K~7X4R8JsONJSu2MN&;uAV@`0MNdX7AVE$w%R9PS(R8JsDPE$}tLsTGCNJSt}QcqAtQdC(iAW&6OLr6hWMN%L^M^Z&aQy^4NAW&6OLr6hWMIcm2 +MIca8Pf$ftR9PV6fSf^7AW2i=fSoKL;((l2NJUabAW2i=fSoKLAXiCLNFYH>Odv)tPf|flAW2R}Pf|@mR7p=xEFe-vP)|}+Q!F4;R6$fl +O+`*rQ!F4LL{CFiO+`*rQy@=LAW}s`Pf|ovAVW`1Lq$?fNlr%~R7gc2P*P7&MN(8*AW%|IR!KxfL?BO6 +AWcC;MIb>|K}k$OLQF*GpK~qIiEFei#Qy@V{MNU*xAWu>tLr+dqR7q4-MNU*xAVOInK~7m9 +Q$tR7gouL?BQ>QdCJrQy@}BP*O!x +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 +BjVedlDqGVd368bwG2#j+mD9lQD0sr<;4X&8%0D>TgISeJ)kNFk^3Xf*%skbRRcZ*dS!BNFavLH +WibPEcW*KUbZByAWite3Z*_7s1ZHV=WiUrS2@UrbB_UrkK{UrtT}Ur$d0Ur8 +UsO~AUsY8CUshHEUsqQGUszZIUs+iKUs_rLVPOGcVgX}g0c2zWWn}?oW&vks0cdCeX=wp!Y5{9&0c>mm +ZEXQ=ZUJv^0dQ~uad821ashL50d#Z$b#(!Db^&*H0eE-;d3gbPdSj|16zH)>$g+grvznd|(VVK)`s##^ +Jh_COk!RL4N(lR@SaKRYGgJn%Xv1$>f_VvG%;GuzyszPjx|liD+IRr~000000093000000004kq#k^Az +$U%@qU7>2Bz={du0O&G0&#r1CLJBFZ06hf(#5#SRxw8U!bIFfg*5RxK^~=*jK)}Ad3JOG^S@OiTh_O-%w{PEG<}Pfr40P*4J2QBeY4Qc?n6Q&R$8R8#_ARaF9CR#pOES62dGSXcsI +Sy=*KT3P{NVF6-d0b^qUWMlzlWdUYp0cU3cXlMaxX#r|#0c&dkY-|B-Z2@j>0dH>saBu-}aRG920dsQ! +baVlAbpdvE0e5!+cz6MMc>#KQh>TceV>4NmyOwH13hJ +J{u?^n-fFP~dpvnj-AyBKaJW)+{-ce}5$#DguerIN21_K0id2nSM +uyu|U!T^j90F36+)E=H0H{s0>nH0sEektM3d!V}qb9G{Ld2nSf*z$T8ClZi8YCe|m_*?{lv>_T7tkE!8 +{87}TyWT7ZV`yP=b7gcd*z$T8ClZi8YCe|m_*?{lv>_T7tkE!8{87}TyWT9nkIU)BIae{Jw9R4r0N>}O +)+shqImKG);D@8R3aUm3Jkg?^%&nV|dnPbniKwLeAs8?!PIJYq3V03Xs{mee0000000000KL7v#00000 +#5#SRxw8U!bIFfg*5RxK^~=*jK)}Ad3JH<(%oY0=TGqa6l5KfYJkC@$v(fAa&d%-e7ws4kFK+d0H97bAybczVb@xPq-Dzr4oJgUK7Oif +U&jRjKPz&##IG7-47St%2#c>Z5R>jkTb_MKDq#SEVo@@XB+#WAdR)2C|*D$SwhJOvD$-0{Gfin@` +^Yz +>$s@6fa*%L>wyFU00eGtZe`d%zThtn<+N=058)p7{qSMOjh9_9t?BNfyg->Vo@@aGb8l^B+#WAdR)2C| +*D$SwhJOvD$-0{Gfin@`G@u4Q3HlB(d+LiLJm-R=h;`?dxBvhE0000004D$d000000QnaP1l_I#dHB_@bgMhk0_N&La@nc5 +HwP6O+keCip#vHLVPOGcVgX}g0c2zWWn}?oW&vks0cdCeX=wp!Y5{9&0c>mmZEXQ=ZUJv^0dQ~uad821 +ashL50d#Z$b#(!Db^&*H0eE-;d3gbPdi$wZavD7|R0gwX!*5!Gc?n?5;yM1jui=Thm^szjcmV+b0|P-! +RR}^*L`g?QQ&a;|M?xV03jhEB(4Y?i2MYiJ01F5J01E*E0La=00XZ-L(V!0j2Lu2B0RR910000 + +-----END RGB CONSIGNMENT----- + + diff --git a/examples/rgb20-demo.yaml b/examples/rgb20-demo.yaml index 6938c47..575c6d7 100644 --- a/examples/rgb20-demo.yaml +++ b/examples/rgb20-demo.yaml @@ -2,8 +2,8 @@ interface: RGB20Fixed globals: spec: - ticker: DBG - name: Debug asset + ticker: DEMO + name: Demo asset details: "Pay attention: the asset has no value" precision: 2 terms: @@ -26,5 +26,5 @@ globals: assignments: assetOwner: - seal: tapret1st:fb9ae7ae4b70a27e7fdfdefac91b37967b549d65007dbf25470b0817a2ae810a:1 + seal: tapret1st:b449f7eaa3f98c145b27ad0eeb7b5679ceb567faef7a52479bc995792b65f804:1 amount: 100000000 # this is 1 million (we have two digits for cents) From 406f80a67d68c5d799281d90cbf510b94271c268 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 20 Sep 2024 17:43:30 +0200 Subject: [PATCH 07/20] 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) - } -} From 419264c9d936d8856771700f1240313f5a928a4d Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 20 Sep 2024 18:05:56 +0200 Subject: [PATCH 08/20] cli: automatically choose interface for a contract --- Cargo.lock | 4 ++-- cli/src/command.rs | 43 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8ee6515..2e61ffd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1416,7 +1416,7 @@ dependencies = [ [[package]] name = "rgb-invoice" version = "0.11.0-beta.8" -source = "git+https://github.com/RGB-WG/rgb-std?branch=fix/rgb-252#0a0ed6697f16c45a35b111f1bc88a429cb90ab94" +source = "git+https://github.com/RGB-WG/rgb-std?branch=fix/rgb-252#77f6ab81aadf09c04869fab1fa33eaa8b3b9366e" dependencies = [ "amplify", "baid64", @@ -1482,7 +1482,7 @@ dependencies = [ [[package]] name = "rgb-std" version = "0.11.0-beta.8" -source = "git+https://github.com/RGB-WG/rgb-std?branch=fix/rgb-252#0a0ed6697f16c45a35b111f1bc88a429cb90ab94" +source = "git+https://github.com/RGB-WG/rgb-std?branch=fix/rgb-252#77f6ab81aadf09c04869fab1fa33eaa8b3b9366e" dependencies = [ "aluvm", "amplify", diff --git a/cli/src/command.rs b/cli/src/command.rs index 96f2d4b..fc9a581 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -127,8 +127,8 @@ 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, @@ -137,7 +137,7 @@ pub enum Command { contract_id: ContractId, /// Interface to interpret the state data - iface: String, + iface: Option, }, /// Display all known UTXOs belonging to this wallet @@ -343,13 +343,46 @@ impl Exec for RgbArgs { } } - Command::HistoryFungible { + Command::History { contract_id, iface, details, } => { let wallet = self.rgb_wallet(&config)?; - let iface: TypeName = tn!(iface.clone()); + 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 history = wallet.history(*contract_id, iface)?; if *details { println!("Operation\tValue\tState\tSeal\tWitness\tOpIds"); From c85b0ae8c310fc311e2a4c65f57ad4af4c5b6cf4 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 20 Sep 2024 18:15:14 +0200 Subject: [PATCH 09/20] cli: make iface optional in state command --- cli/src/command.rs | 81 +++++++++++++++++++++++++++------------------- cli/src/main.rs | 4 ++- 2 files changed, 50 insertions(+), 35 deletions(-) diff --git a/cli/src/command.rs b/cli/src/command.rs index 7f6e6ec..5796406 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -21,6 +21,7 @@ use std::fs; use std::fs::File; +use std::ops::ControlFlow; use std::path::PathBuf; use std::str::FromStr; @@ -46,6 +47,7 @@ use rgb::{ RgbDescr, RgbKeychain, RgbWallet, StateType, TransferParams, WalletError, WalletProvider, XChain, XOutpoint, XWitnessId, }; +use rgbstd::persistence::StockError; use seals::SecretSeal; use serde_crate::{Deserialize, Serialize}; use strict_types::encoding::{FieldName, TypeName}; @@ -123,7 +125,7 @@ pub enum Command { contract_id: ContractId, /// Interface to interpret the state data - iface: String, + iface: Option, }, /// Print operation history for a default fungible token under a given @@ -355,37 +357,10 @@ impl Exec for RgbArgs { let wallet = self.rgb_wallet(&config)?; 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(()); - } - } - } + None => match contract_default_iface_name(*contract_id, wallet.stock())? { + ControlFlow::Continue(name) => name, + ControlFlow::Break(_) => return Ok(()), + }, }; let history = wallet.history(*contract_id, iface)?; if *details { @@ -534,6 +509,14 @@ impl Exec for RgbArgs { } } + let iface: TypeName = match iface { + Some(iface) => tn!(iface.clone()), + None => match contract_default_iface_name(*contract_id, &stock)? { + ControlFlow::Continue(name) => name, + ControlFlow::Break(_) => return Ok(()), + }, + }; + let stock_wallet = match self.rgb_wallet_from_stock(&config, stock) { Ok(wallet) => StockOrWallet::Wallet(wallet), Err((stock, _)) => StockOrWallet::Stock(stock), @@ -1148,8 +1131,38 @@ impl Exec for RgbArgs { eprintln!("Transfer accepted into the stash"); } } - println!(); - Ok(()) } } + +fn contract_default_iface_name( + contract_id: ContractId, + stock: &Stock, +) -> Result, StockError> { + let info = stock.contract_info(contract_id)?; + let schema = stock.schema(info.schema_id)?; + Ok(match schema.iimpls.len() { + 0 => { + eprintln!("contract doesn't implement any interface and thus can't be read"); + ControlFlow::Break(()) + } + 1 => ControlFlow::Continue( + 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}"); + } + ControlFlow::Break(()) + } + }) +} diff --git a/cli/src/main.rs b/cli/src/main.rs index 8475d3a..3d94c02 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -63,5 +63,7 @@ fn run() -> Result<(), WalletError> { let conf = Config::load(&args.conf_path("rgb")); debug!("Executing command: {:?}", args.command); - args.exec(conf, "rgb") + args.exec(conf, "rgb")?; + println!(); + Ok(()) } From de9587aebaab23ff7b377cf1a73eddca11df91b2 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 20 Sep 2024 18:23:13 +0200 Subject: [PATCH 10/20] cli: print information about the mining status in contract state --- cli/src/command.rs | 42 +++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/cli/src/command.rs b/cli/src/command.rs index 5796406..8a4f1b3 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -47,7 +47,9 @@ use rgb::{ RgbDescr, RgbKeychain, RgbWallet, StateType, TransferParams, WalletError, WalletProvider, XChain, XOutpoint, XWitnessId, }; -use rgbstd::persistence::StockError; +use rgbstd::interface::ContractIface; +use rgbstd::persistence::{MemContractState, StockError}; +use rgbstd::{KnownState, OutputAssignment}; use seals::SecretSeal; use serde_crate::{Deserialize, Serialize}; use strict_types::encoding::{FieldName, TypeName}; @@ -577,67 +579,57 @@ impl Exec for RgbArgs { } println!("\nOwned:"); + fn witness( + allocation: &OutputAssignment, + contract: &ContractIface>, + ) -> String { + allocation + .witness + .and_then(|w| contract.witness_info(w)) + .map(|info| format!("{} ({})", info.id, info.ord)) + .unwrap_or_else(|| s!("~")) + } for owned in &contract.iface.assignments { println!(" {}:", owned.name); if let Ok(allocations) = contract.fungible(owned.name.clone(), &filter) { for allocation in allocations { - let witness = allocation - .witness - .as_ref() - .map(XWitnessId::to_string) - .unwrap_or(s!("~")); println!( " value={}, utxo={}, witness={} {}", allocation.state.value(), allocation.seal, - witness, + witness(&allocation, &contract), filter.comment(allocation.seal.to_outpoint()) ); } } if let Ok(allocations) = contract.data(owned.name.clone(), &filter) { for allocation in allocations { - let witness = allocation - .witness - .as_ref() - .map(XWitnessId::to_string) - .unwrap_or(s!("~")); println!( " data={}, utxo={}, witness={} {}", allocation.state, allocation.seal, - witness, + witness(&allocation, &contract), filter.comment(allocation.seal.to_outpoint()) ); } } if let Ok(allocations) = contract.attachments(owned.name.clone(), &filter) { for allocation in allocations { - let witness = allocation - .witness - .as_ref() - .map(XWitnessId::to_string) - .unwrap_or(s!("~")); println!( " file={}, utxo={}, witness={} {}", allocation.state, allocation.seal, - witness, + witness(&allocation, &contract), filter.comment(allocation.seal.to_outpoint()) ); } } if let Ok(allocations) = contract.rights(owned.name.clone(), &filter) { for allocation in allocations { - let witness = allocation - .witness - .as_ref() - .map(XWitnessId::to_string) - .unwrap_or(s!("~")); println!( " utxo={}, witness={} {}", allocation.seal, - witness, + witness(&allocation, &contract), filter.comment(allocation.seal.to_outpoint()) ); } From e8c987f796cf2370bca7f4afbfd3e5cd5eaf2030 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 20 Sep 2024 18:28:21 +0200 Subject: [PATCH 11/20] cli: fix missed mempool feature --- cli/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/Cargo.toml b/cli/Cargo.toml index f2c1537..46ba37a 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -26,7 +26,7 @@ bp-std = { workspace = true, features = ["serde"] } bp-wallet = { workspace = true, features = ["cli"] } psbt = { workspace = true } rgb-std = { workspace = true, features = ["serde"] } -rgb-runtime = { version = "0.11.0-beta.8", path = "..", features = ["electrum_blocking", "esplora_blocking", "log", "serde", "fs", "cli"] } +rgb-runtime = { version = "0.11.0-beta.8", path = "..", features = ["electrum_blocking", "esplora_blocking", "mempool_blocking", "log", "serde", "fs", "cli"] } log = { workspace = true } env_logger = "0.11.5" clap = { version = "4.5.17", features = ["derive", "env"] } From 954f7f28f7f59e55d638ff2344917eb113e21b12 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 20 Sep 2024 18:36:41 +0200 Subject: [PATCH 12/20] cli: fix displaying fungible amount with history command --- cli/src/command.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cli/src/command.rs b/cli/src/command.rs index 8a4f1b3..a2dea3e 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -47,7 +47,7 @@ use rgb::{ RgbDescr, RgbKeychain, RgbWallet, StateType, TransferParams, WalletError, WalletProvider, XChain, XOutpoint, XWitnessId, }; -use rgbstd::interface::ContractIface; +use rgbstd::interface::{AllocatedState, ContractIface}; use rgbstd::persistence::{MemContractState, StockError}; use rgbstd::{KnownState, OutputAssignment}; use seals::SecretSeal; @@ -379,7 +379,12 @@ impl Exec for RgbArgs { witness, } in history { - print!("{direction}\t{state}"); + print!("{direction: <9}\t"); + if let AllocatedState::Amount(amount) = state { + print!("{: >9}", amount.value()); + } else { + print!("{state}"); + } if *details { print!("\t{ty}"); } @@ -392,7 +397,7 @@ impl Exec for RgbArgs { ); if *details { println!( - "{}", + "\t{}", opids .iter() .map(OpId::to_string) From 07c1b12e2e6dd803a18bf37f2af5a4e484969213 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 20 Sep 2024 20:14:27 +0200 Subject: [PATCH 13/20] wallet: distinguish spent and unspent outputs --- Cargo.lock | 5 ++--- Cargo.toml | 1 + cli/src/command.rs | 27 ++++++++++++++++----------- src/filters.rs | 37 ++++++++++++++++++++++++++++--------- src/lib.rs | 2 +- src/pay.rs | 19 +++++++++++-------- src/wallet.rs | 2 +- 7 files changed, 60 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2e61ffd..9910d6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -490,8 +490,7 @@ dependencies = [ [[package]] name = "bp-wallet" version = "0.11.0-beta.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b3cda17bafd67254e6f0be1b401a31d942dc54be138d5cc7976bb58291dff0" +source = "git+https://github.com/BP-WG/bp-wallet?branch=develop#65588b519cc91778350065564ae4db5eff34f72b" dependencies = [ "amplify", "base64", @@ -1127,7 +1126,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index ef72c8c..79190d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" } diff --git a/cli/src/command.rs b/cli/src/command.rs index a2dea3e..07f1055 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -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, @@ -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}"); } @@ -563,22 +563,27 @@ impl Exec for RgbArgs { id: Option, ) -> 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", } } } diff --git a/src/filters.rs b/src/filters.rs index 9086a4f..02ac6ea 100644 --- a/src/filters.rs +++ b/src/filters.rs @@ -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>(pub &'a Wallet); @@ -35,23 +36,41 @@ impl<'a, K, D: DescriptorRgb> Clone for WalletOutpointsFilter<'a, K, D> { 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) + match output.into().into_bp() { + Bp::Bitcoin(outpoint) => self.0.has_outpoint(outpoint), + Bp::Liquid(_) => false, + } + } +} + +pub struct WalletUnspentFilter<'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 WalletUnspentFilter<'a, K, D> {} +impl<'a, K, D: DescriptorRgb> Clone for WalletUnspentFilter<'a, K, D> { + fn clone(&self) -> Self { *self } +} + +impl<'a, K, D: DescriptorRgb> AssignmentsFilter for WalletUnspentFilter<'a, K, D> { + fn should_include(&self, output: impl Into, _: Option) -> bool { + match output.into().into_bp() { + Bp::Bitcoin(outpoint) => self.0.is_unspent(outpoint), + Bp::Liquid(_) => false, + } } } -pub struct WitnessOutpointsFilter<'a, K, D: DescriptorRgb>(pub &'a Wallet); +pub struct WalletWitnessFilter<'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> { +impl<'a, K, D: DescriptorRgb> Copy for WalletWitnessFilter<'a, K, D> {} +impl<'a, K, D: DescriptorRgb> Clone for WalletWitnessFilter<'a, K, D> { fn clone(&self) -> Self { *self } } -impl<'a, K, D: DescriptorRgb> AssignmentsFilter for WitnessOutpointsFilter<'a, K, D> { +impl<'a, K, D: DescriptorRgb> AssignmentsFilter for WalletWitnessFilter<'a, K, D> { fn should_include(&self, _: impl Into, witness_id: Option) -> bool { self.0 .history() diff --git a/src/lib.rs b/src/lib.rs index 750af4d..d37fcf1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,5 +57,5 @@ pub mod resolvers { } } } -pub use filters::{WalletOutpointsFilter, WitnessOutpointsFilter}; +pub use filters::{WalletOutpointsFilter, WalletUnspentFilter, WalletWitnessFilter}; pub use wallet::RgbWallet; diff --git a/src/pay.rs b/src/pay.rs index d61947d..d2237b8 100644 --- a/src/pay.rs +++ b/src/pay.rs @@ -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)] @@ -91,7 +90,7 @@ where W::Descr: DescriptorRgb { fn should_include(&self, output: impl Into, id: Option) -> 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()) @@ -101,13 +100,15 @@ where W::Descr: DescriptorRgb pub trait WalletProvider: PsbtConstructor where Self::Descr: DescriptorRgb { - 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( &mut self, f: impl FnOnce(&mut WalletDescr) -> R, ) -> R; - fn outpoints(&self) -> impl Iterator; + fn utxos(&self) -> impl Iterator; + fn txos(&self) -> impl Iterator; fn txids(&self) -> impl Iterator; fn history(&self) -> impl Iterator> + '_; @@ -352,12 +353,14 @@ where Self::Descr: DescriptorRgb } impl> WalletProvider for Wallet { - 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(&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 utxos(&self) -> impl Iterator { self.coins().map(|coin| coin.outpoint) } + fn txos(&self) -> impl Iterator { self.txos().map(|txo| txo.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 ec41e2c..11709f8 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -115,7 +115,7 @@ where W::Descr: DescriptorRgb ) -> Result, StockError> { 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)] From b36d00f3be5d05074d72fd569b00416f99a9e1f6 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 20 Sep 2024 20:33:51 +0200 Subject: [PATCH 14/20] cli: sort history --- Cargo.lock | 6 +++--- cli/src/command.rs | 15 +++++++++------ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9910d6b..94b0831 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1126,7 +1126,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]] @@ -1415,7 +1415,7 @@ dependencies = [ [[package]] name = "rgb-invoice" version = "0.11.0-beta.8" -source = "git+https://github.com/RGB-WG/rgb-std?branch=fix/rgb-252#77f6ab81aadf09c04869fab1fa33eaa8b3b9366e" +source = "git+https://github.com/RGB-WG/rgb-std?branch=fix/rgb-252#2c915f82b1cc3e3d7845696f085bc6dbe32b17bb" dependencies = [ "amplify", "baid64", @@ -1481,7 +1481,7 @@ dependencies = [ [[package]] name = "rgb-std" version = "0.11.0-beta.8" -source = "git+https://github.com/RGB-WG/rgb-std?branch=fix/rgb-252#77f6ab81aadf09c04869fab1fa33eaa8b3b9366e" +source = "git+https://github.com/RGB-WG/rgb-std?branch=fix/rgb-252#2c915f82b1cc3e3d7845696f085bc6dbe32b17bb" dependencies = [ "aluvm", "amplify", diff --git a/cli/src/command.rs b/cli/src/command.rs index 07f1055..4a1e6e3 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -41,7 +41,7 @@ use rgb::persistence::{MemContract, StashReadProvider, Stock}; use rgb::resolvers::ContractIssueResolver; use rgb::schema::SchemaId; use rgb::validation::Validity; -use rgb::vm::RgbIsa; +use rgb::vm::{RgbIsa, WitnessOrd}; use rgb::{ BundleId, ContractId, DescriptorRgb, GenesisSeal, GraphSeal, Identity, OpId, OutputSeal, RgbDescr, RgbKeychain, RgbWallet, StateType, TransferParams, WalletError, WalletProvider, @@ -364,7 +364,8 @@ impl Exec for RgbArgs { ControlFlow::Break(_) => return Ok(()), }, }; - let history = wallet.history(*contract_id, iface)?; + let mut history = wallet.history(*contract_id, iface)?; + history.sort_by_key(|op| op.witness.map(|w| w.ord).unwrap_or(WitnessOrd::Archived)); if *details { println!("Operation\tValue\tState\tSeal\tWitness\tOpIds"); } else { @@ -379,11 +380,11 @@ impl Exec for RgbArgs { witness, } in history { - print!("{direction:<9}\t"); + print!("{:9}\t", direction.to_string()); if let AllocatedState::Amount(amount) = state { - print!("{:>9}", amount.value()); + print!("{: >9}", amount.value()); } else { - print!("{state}"); + print!("{state:>9}"); } if *details { print!("\t{ty}"); @@ -582,7 +583,9 @@ impl Exec for RgbArgs { Filter::WalletAll(rgb) if rgb.wallet().is_unspent(outpoint) => { "-- unspent" } - Filter::WalletAll(_) => "-- spent", + Filter::WalletAll(rgb) if rgb.wallet().has_outpoint(outpoint) => { + "-- spent" + } _ => "-- third-party", } } From 8e0802c8fb58ffc7db23aaf857febc098c8ebd19 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 20 Sep 2024 21:02:37 +0200 Subject: [PATCH 15/20] cli: improve state printout --- Cargo.lock | 3 +-- Cargo.toml | 1 + cli/src/command.rs | 22 +++++++++++----------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 94b0831..0a4abdc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1393,8 +1393,7 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rgb-core" version = "0.11.0-beta.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43dc70212f5eff8189f3cdfef2d11f53f7be4c4128db9839b5d56a0f9ef60c98" +source = "git+https://github.com/RGB-WG/rgb-core?branch=fix/273#e2a451fa793fe5403b4114b5ed5e9e47c69674cd" dependencies = [ "aluvm", "amplify", diff --git a/Cargo.toml b/Cargo.toml index 79190d1..7efaf9c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -105,4 +105,5 @@ features = ["all"] [patch.crates-io] bp-wallet = { git = "https://github.com/BP-WG/bp-wallet", branch = "develop" } +rgb-core = { git = "https://github.com/RGB-WG/rgb-core", branch = "fix/273" } rgb-std = { git = "https://github.com/RGB-WG/rgb-std", branch = "fix/rgb-252" } diff --git a/cli/src/command.rs b/cli/src/command.rs index 4a1e6e3..fcb28d7 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -367,9 +367,9 @@ impl Exec for RgbArgs { let mut history = wallet.history(*contract_id, iface)?; history.sort_by_key(|op| op.witness.map(|w| w.ord).unwrap_or(WitnessOrd::Archived)); if *details { - println!("Operation\tValue\tState\tSeal\tWitness\tOpIds"); + println!("Operation\tValue \tState\t{:78}\tWitness", "Seal"); } else { - println!("Operation\tValue\tSeal\tWitness"); + println!("Operation\tValue \t{:78}\tWitness", "Seal"); } for ContractOp { direction, @@ -389,7 +389,7 @@ impl Exec for RgbArgs { if *details { print!("\t{ty}"); } - print!( + println!( "\t{}\t{}", to.first().expect("at least one receiver is always present"), witness @@ -398,15 +398,13 @@ impl Exec for RgbArgs { ); if *details { println!( - "\t{}", + "\topid={}", opids .iter() .map(OpId::to_string) .collect::>() - .join(", ") + .join("\n\topid=") ) - } else { - println!(); } } } @@ -603,11 +601,12 @@ impl Exec for RgbArgs { .unwrap_or_else(|| s!("~")) } for owned in &contract.iface.assignments { + println!(" State \t{:78}\tWitness", "Seal"); println!(" {}:", owned.name); if let Ok(allocations) = contract.fungible(owned.name.clone(), &filter) { for allocation in allocations { println!( - " value={}, utxo={}, witness={} {}", + " {: >9}\t{}\t{} {}", allocation.state.value(), allocation.seal, witness(&allocation, &contract), @@ -618,7 +617,7 @@ impl Exec for RgbArgs { if let Ok(allocations) = contract.data(owned.name.clone(), &filter) { for allocation in allocations { println!( - " data={}, utxo={}, witness={} {}", + " {: >9}\t{}\t{} {}", allocation.state, allocation.seal, witness(&allocation, &contract), @@ -629,7 +628,7 @@ impl Exec for RgbArgs { if let Ok(allocations) = contract.attachments(owned.name.clone(), &filter) { for allocation in allocations { println!( - " file={}, utxo={}, witness={} {}", + " {: >9}\t{}\t{} {}", allocation.state, allocation.seal, witness(&allocation, &contract), @@ -640,7 +639,8 @@ impl Exec for RgbArgs { if let Ok(allocations) = contract.rights(owned.name.clone(), &filter) { for allocation in allocations { println!( - " utxo={}, witness={} {}", + " {: >9}\t{}\t{} {}", + "right", allocation.seal, witness(&allocation, &contract), filter.comment(allocation.seal.to_outpoint()) From da65a6dd6e317cc427af07ae3b9582bb9e212eb8 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sun, 22 Sep 2024 19:06:22 +0200 Subject: [PATCH 16/20] cli: make iface optional in invoice command --- cli/src/command.rs | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/cli/src/command.rs b/cli/src/command.rs index fcb28d7..19d42ce 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -172,9 +172,9 @@ pub enum Command { contract_id: ContractId, /// Interface to interpret the state data - iface: String, + iface: Option, - /// Value to transfer + /// Value (for fungible token) or token ID (for NFT) to transfer value: u64, }, @@ -357,12 +357,10 @@ impl Exec for RgbArgs { details, } => { let wallet = self.rgb_wallet(&config)?; - let iface: TypeName = match iface { - Some(iface) => tn!(iface.clone()), - None => match contract_default_iface_name(*contract_id, wallet.stock())? { - ControlFlow::Continue(name) => name, - ControlFlow::Break(_) => return Ok(()), - }, + let iface = match contract_default_iface_name(*contract_id, wallet.stock(), iface)? + { + ControlFlow::Continue(name) => name, + ControlFlow::Break(_) => return Ok(()), }; let mut history = wallet.history(*contract_id, iface)?; history.sort_by_key(|op| op.witness.map(|w| w.ord).unwrap_or(WitnessOrd::Archived)); @@ -515,12 +513,9 @@ impl Exec for RgbArgs { } } - let iface: TypeName = match iface { - Some(iface) => tn!(iface.clone()), - None => match contract_default_iface_name(*contract_id, &stock)? { - ControlFlow::Continue(name) => name, - ControlFlow::Break(_) => return Ok(()), - }, + let iface = match contract_default_iface_name(*contract_id, &stock, iface)? { + ControlFlow::Continue(name) => name, + ControlFlow::Break(_) => return Ok(()), }; let stock_wallet = match self.rgb_wallet_from_stock(&config, stock) { @@ -791,7 +786,12 @@ impl Exec for RgbArgs { value, } => { let mut wallet = self.rgb_wallet(&config)?; - let iface = TypeName::try_from(iface.to_owned()).expect("invalid interface name"); + + let iface = match contract_default_iface_name(*contract_id, wallet.stock(), iface)? + { + ControlFlow::Continue(name) => name, + ControlFlow::Break(_) => return Ok(()), + }; let outpoint = wallet .wallet() @@ -1143,7 +1143,11 @@ impl Exec for RgbArgs { fn contract_default_iface_name( contract_id: ContractId, stock: &Stock, + iface: &Option, ) -> Result, StockError> { + if let Some(iface) = iface { + return Ok(ControlFlow::Continue(tn!(iface.clone()))); + }; let info = stock.contract_info(contract_id)?; let schema = stock.schema(info.schema_id)?; Ok(match schema.iimpls.len() { From 955c52f55dfcd0d9e1e9db8b0c4e157774a5ff4b Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sun, 22 Sep 2024 21:28:07 +0200 Subject: [PATCH 17/20] cli: support NFT invoicing --- cli/src/command.rs | 163 ++++++++++++++++++++++++++++++++++++++------- src/errors.rs | 2 + 2 files changed, 140 insertions(+), 25 deletions(-) diff --git a/cli/src/command.rs b/cli/src/command.rs index 19d42ce..fe01db2 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -43,12 +43,13 @@ use rgb::schema::SchemaId; use rgb::validation::Validity; use rgb::vm::{RgbIsa, WitnessOrd}; use rgb::{ - BundleId, ContractId, DescriptorRgb, GenesisSeal, GraphSeal, Identity, OpId, OutputSeal, - RgbDescr, RgbKeychain, RgbWallet, StateType, TransferParams, WalletError, WalletProvider, - XChain, XOutpoint, XWitnessId, + Allocation, BundleId, ContractId, DescriptorRgb, GenesisSeal, GraphSeal, Identity, OpId, + OutputSeal, RgbDescr, RgbKeychain, RgbWallet, StateType, TokenIndex, TransferParams, + WalletError, WalletProvider, XChain, XOutpoint, XWitnessId, }; -use rgbstd::interface::{AllocatedState, ContractIface}; +use rgbstd::interface::{AllocatedState, ContractIface, OwnedIface}; use rgbstd::persistence::{MemContractState, StockError}; +use rgbstd::stl::rgb_contract_stl; use rgbstd::{KnownState, OutputAssignment}; use seals::SecretSeal; use serde_crate::{Deserialize, Serialize}; @@ -120,7 +121,7 @@ pub enum Command { #[display("state")] State { /// Show all state, including already spent and not owned by the wallet - #[clap(short, long)] + #[arg(short, long)] all: bool, /// Contract identifier @@ -135,7 +136,7 @@ pub enum Command { #[display("history")] History { /// Print detailed information - #[clap(long)] + #[arg(long)] details: bool, /// Contract identifier @@ -165,17 +166,31 @@ pub enum Command { #[display("invoice")] Invoice { /// Force address-based invoice - #[clap(short, long)] + #[arg(short, long)] address_based: bool, - /// Contract identifier - contract_id: ContractId, - /// Interface to interpret the state data + #[arg(short, long)] iface: Option, + /// Operation to use for the invoice + /// + /// If no operation is provided, the interface default operation is used. + #[arg(short, long)] + operation: Option, + + /// State name to use for the invoice + /// + /// If no state name is provided, the interface default state name for the operation is + /// used. + #[arg(short, long, requires = "operation")] + state: Option, + + /// Contract identifier + contract_id: ContractId, + /// Value (for fungible token) or token ID (for NFT) to transfer - value: u64, + value: Option, }, /// Prepare PSBT file for transferring RGB assets @@ -189,7 +204,7 @@ pub enum Command { /// Amount of satoshis which should be paid to the address-based /// beneficiary - #[clap(long, default_value = "2000")] + #[arg(long, default_value = "2000")] sats: Sats, /// Invoice data @@ -222,19 +237,19 @@ pub enum Command { #[display("transfer")] Transfer { /// Encode PSBT as V2 - #[clap(short = '2')] + #[arg(short = '2')] v2: bool, /// Amount of satoshis which should be paid to the address-based /// beneficiary - #[clap(long, default_value = "2000")] + #[arg(long, default_value = "2000")] sats: Sats, /// Invoice data invoice: RgbInvoice, /// Fee for bitcoin transaction, in satoshis - #[clap(short, long, default_value = "400")] + #[arg(short, long, default_value = "400")] fee: Sats, /// File for generated transfer consignment @@ -781,18 +796,14 @@ impl Exec for RgbArgs { } Command::Invoice { address_based, + operation, + state, contract_id, iface, value, } => { let mut wallet = self.rgb_wallet(&config)?; - let iface = match contract_default_iface_name(*contract_id, wallet.stock(), iface)? - { - ControlFlow::Continue(name) => name, - ControlFlow::Break(_) => return Ok(()), - }; - let outpoint = wallet .wallet() .coinselect(Sats::ZERO, |utxo| { @@ -828,11 +839,113 @@ impl Exec for RgbArgs { Beneficiary::BlindedSeal(*seal.to_secret_seal().as_reduced_unsafe()) } }; - let invoice = RgbInvoiceBuilder::new(XChainNet::bitcoin(network, beneficiary)) + + let iface = match contract_default_iface_name(*contract_id, wallet.stock(), iface)? + { + ControlFlow::Continue(name) => wallet.stock().iface(name)?, + ControlFlow::Break(_) => return Ok(()), + }; + let iface_name = &iface.name; + let Some(op_name) = operation + .clone() + .map(FieldName::try_from) + .transpose() + .map_err(|e| WalletError::Invoicing(format!("invalid operation name - {e}")))? + .or(iface.default_operation.clone()) + else { + return Err(WalletError::Invoicing(format!( + "interface {iface_name} doesn't have default operation" + ))); + }; + let Some(iface_op) = iface.transitions.get(&op_name) else { + return Err(WalletError::Invoicing(format!( + "interface {iface_name} doesn't have operation {op_name}" + ))); + }; + let state_name = state + .clone() + .map(FieldName::try_from) + .transpose() + .map_err(|e| WalletError::Invoicing(format!("invalid state name - {e}")))? + .or_else(|| iface_op.default_assignment.clone()) + .ok_or_else(|| { + WalletError::Invoicing(format!( + "interface {iface_name} doesn't have a default state for the \ + operation {op_name}" + )) + })?; + let Some(assign_iface) = iface.assignments.get(&state_name) else { + return Err(WalletError::Invoicing(format!( + "interface {iface_name} doesn't have state {state_name} in operation \ + {op_name}" + ))); + }; + + let mut builder = RgbInvoiceBuilder::new(XChainNet::bitcoin(network, beneficiary)) .set_contract(*contract_id) - .set_interface(iface) - .set_amount_raw(*value) - .finish(); + .set_interface(iface_name.clone()); + + if operation.is_some() { + builder = builder.set_operation(op_name); + if let Some(state) = state { + builder = builder.set_operation(fname!(state.clone())); + } + } + + match (assign_iface.owned_state, value) { + ( + OwnedIface::Rights + | OwnedIface::Amount + | OwnedIface::AnyData + | OwnedIface::Data(_), + None, + ) => { + // There is no state which has to be added to the invoice + } + (OwnedIface::Rights, Some(_)) => { + return Err(WalletError::Invoicing(format!( + "state {state_name} in interface {iface_name} defines a right and it \ + can't has a value" + ))); + } + (OwnedIface::Amount, Some(amount)) => { + builder = builder.set_amount_raw(*amount); + } + (OwnedIface::Data(sem_id), Some(_)) + if sem_id + != rgb_contract_stl() + .types + .get(&tn!("Allocation")) + .expect("STL is broken") + .sem_id_named(&tn!("Allocation")) => + { + return Err(WalletError::Invoicing(format!( + "state {state_name} in interface {iface_name} has a type which can't \ + be used with a non-fungible state allocation" + ))); + } + (OwnedIface::AnyData | OwnedIface::Data(_), Some(value)) => { + builder = builder.set_allocation_raw(Allocation::with( + TokenIndex::from(*value as u32), + // TODO: Support fractional NFT invoicing + 0, + )) + } + + (OwnedIface::Any, _) => { + return Err(WalletError::Invoicing(format!( + "state {state_name} in interface {iface_name} can be of any type; \ + adding it to the invoice is impossible" + ))); + } + (OwnedIface::AnyAttach, _) => { + return Err(WalletError::Invoicing(s!( + "invoicing with attachments is not yet supported" + ))); + } + } + + let invoice = builder.finish(); println!("{invoice}"); } Command::Prepare { diff --git a/src/errors.rs b/src/errors.rs index b85a6d7..72d32b8 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -62,6 +62,8 @@ pub enum WalletError { #[from] Contract(ContractError), + Invoicing(String), + #[from] PsbtDecode(psrgbt::DecodeError), From 94e48de7f5b3c7333fdf2f37a8015c87388e749b Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sun, 22 Sep 2024 21:43:58 +0200 Subject: [PATCH 18/20] cli: support invoicing with fractional token amounts --- cli/src/command.rs | 58 +++++++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/cli/src/command.rs b/cli/src/command.rs index fe01db2..93aec14 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -44,8 +44,8 @@ use rgb::validation::Validity; use rgb::vm::{RgbIsa, WitnessOrd}; use rgb::{ Allocation, BundleId, ContractId, DescriptorRgb, GenesisSeal, GraphSeal, Identity, OpId, - OutputSeal, RgbDescr, RgbKeychain, RgbWallet, StateType, TokenIndex, TransferParams, - WalletError, WalletProvider, XChain, XOutpoint, XWitnessId, + OutputSeal, OwnedFraction, RgbDescr, RgbKeychain, RgbWallet, StateType, TokenIndex, + TransferParams, WalletError, WalletProvider, XChain, XOutpoint, XWitnessId, }; use rgbstd::interface::{AllocatedState, ContractIface, OwnedIface}; use rgbstd::persistence::{MemContractState, StockError}; @@ -189,8 +189,17 @@ pub enum Command { /// Contract identifier contract_id: ContractId, - /// Value (for fungible token) or token ID (for NFT) to transfer - value: Option, + /// Amount of tokens (in the smallest unit) to transfer + #[arg(short, long)] + amount: Option, + + /// Token index for NFT transfer + #[arg(long)] + token_index: Option, + + /// Fraction of an NFT token to transfer + #[arg(long, requires = "token_index")] + token_fraction: Option, }, /// Prepare PSBT file for transferring RGB assets @@ -800,7 +809,9 @@ impl Exec for RgbArgs { state, contract_id, iface, - value, + amount, + token_index, + token_fraction, } => { let mut wallet = self.rgb_wallet(&config)?; @@ -892,26 +903,42 @@ impl Exec for RgbArgs { } } - match (assign_iface.owned_state, value) { + match (assign_iface.owned_state, amount, token_index.map(|i| (i, token_fraction))) { ( OwnedIface::Rights | OwnedIface::Amount | OwnedIface::AnyData | OwnedIface::Data(_), None, + None, ) => { // There is no state which has to be added to the invoice } - (OwnedIface::Rights, Some(_)) => { + (OwnedIface::Rights, Some(_), None | Some(_)) + | (OwnedIface::Rights, None, Some(_)) => { return Err(WalletError::Invoicing(format!( "state {state_name} in interface {iface_name} defines a right and it \ - can't has a value" + can't has a value or a token information" ))); } - (OwnedIface::Amount, Some(amount)) => { + (OwnedIface::Amount, _, Some(_)) => { + return Err(WalletError::Invoicing(format!( + "state {state_name} in interface {iface_name} defines a fungible \ + state, while a non-fungible token index is provided for the invoice. \ + Please use only --amount argument" + ))); + } + (OwnedIface::Amount, Some(amount), None) => { builder = builder.set_amount_raw(*amount); } - (OwnedIface::Data(sem_id), Some(_)) + (OwnedIface::Data(_) | OwnedIface::AnyData, Some(_), _) => { + return Err(WalletError::Invoicing(format!( + "state {state_name} in interface {iface_name} defines a non-fungible \ + state, while a fungible amount is provided for the invoice. Please \ + use only --token-index and --token-fraction arguments" + ))); + } + (OwnedIface::Data(sem_id), None, Some(_)) if sem_id != rgb_contract_stl() .types @@ -924,21 +951,20 @@ impl Exec for RgbArgs { be used with a non-fungible state allocation" ))); } - (OwnedIface::AnyData | OwnedIface::Data(_), Some(value)) => { + (OwnedIface::AnyData | OwnedIface::Data(_), None, Some((index, fraction))) => { builder = builder.set_allocation_raw(Allocation::with( - TokenIndex::from(*value as u32), - // TODO: Support fractional NFT invoicing - 0, + index, + fraction.unwrap_or(OwnedFraction::from(0)), )) } - (OwnedIface::Any, _) => { + (OwnedIface::Any, _, _) => { return Err(WalletError::Invoicing(format!( "state {state_name} in interface {iface_name} can be of any type; \ adding it to the invoice is impossible" ))); } - (OwnedIface::AnyAttach, _) => { + (OwnedIface::AnyAttach, _, _) => { return Err(WalletError::Invoicing(s!( "invoicing with attachments is not yet supported" ))); From a3b2e437cebfd5c056d52ee5d9c181caf6a51e10 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Wed, 25 Sep 2024 17:15:33 +0200 Subject: [PATCH 19/20] wallet: add support for custom L2s --- src/filters.rs | 32 +++++++++++++++++++------------- src/pay.rs | 27 +++++++++++++++++---------- src/wallet.rs | 26 ++++++++++++++++++-------- 3 files changed, 54 insertions(+), 31 deletions(-) diff --git a/src/filters.rs b/src/filters.rs index 02ac6ea..1238adc 100644 --- a/src/filters.rs +++ b/src/filters.rs @@ -20,21 +20,23 @@ // limitations under the License. use bp::Bp; -use bpwallet::Wallet; +use bpwallet::{Layer2, Wallet}; use rgbstd::interface::AssignmentsFilter; use crate::{DescriptorRgb, XChain, XOutpoint, XWitnessId}; -pub struct WalletOutpointsFilter<'a, K, D: DescriptorRgb>(pub &'a Wallet); +pub struct WalletOutpointsFilter<'a, K, D: DescriptorRgb, L2: Layer2>(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> { +impl<'a, K, D: DescriptorRgb, L2: Layer2> Copy for WalletOutpointsFilter<'a, K, D, L2> {} +impl<'a, K, D: DescriptorRgb, L2: Layer2> Clone for WalletOutpointsFilter<'a, K, D, L2> { fn clone(&self) -> Self { *self } } -impl<'a, K, D: DescriptorRgb> AssignmentsFilter for WalletOutpointsFilter<'a, K, D> { +impl<'a, K, D: DescriptorRgb, L2: Layer2> AssignmentsFilter + for WalletOutpointsFilter<'a, K, D, L2> +{ fn should_include(&self, output: impl Into, _: Option) -> bool { match output.into().into_bp() { Bp::Bitcoin(outpoint) => self.0.has_outpoint(outpoint), @@ -43,16 +45,18 @@ impl<'a, K, D: DescriptorRgb> AssignmentsFilter for WalletOutpointsFilter<'a, } } -pub struct WalletUnspentFilter<'a, K, D: DescriptorRgb>(pub &'a Wallet); +pub struct WalletUnspentFilter<'a, K, D: DescriptorRgb, L2: Layer2>(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 WalletUnspentFilter<'a, K, D> {} -impl<'a, K, D: DescriptorRgb> Clone for WalletUnspentFilter<'a, K, D> { +impl<'a, K, D: DescriptorRgb, L2: Layer2> Copy for WalletUnspentFilter<'a, K, D, L2> {} +impl<'a, K, D: DescriptorRgb, L2: Layer2> Clone for WalletUnspentFilter<'a, K, D, L2> { fn clone(&self) -> Self { *self } } -impl<'a, K, D: DescriptorRgb> AssignmentsFilter for WalletUnspentFilter<'a, K, D> { +impl<'a, K, D: DescriptorRgb, L2: Layer2> AssignmentsFilter + for WalletUnspentFilter<'a, K, D, L2> +{ fn should_include(&self, output: impl Into, _: Option) -> bool { match output.into().into_bp() { Bp::Bitcoin(outpoint) => self.0.is_unspent(outpoint), @@ -61,16 +65,18 @@ impl<'a, K, D: DescriptorRgb> AssignmentsFilter for WalletUnspentFilter<'a, K } } -pub struct WalletWitnessFilter<'a, K, D: DescriptorRgb>(pub &'a Wallet); +pub struct WalletWitnessFilter<'a, K, D: DescriptorRgb, L2: Layer2>(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 WalletWitnessFilter<'a, K, D> {} -impl<'a, K, D: DescriptorRgb> Clone for WalletWitnessFilter<'a, K, D> { +impl<'a, K, D: DescriptorRgb, L2: Layer2> Copy for WalletWitnessFilter<'a, K, D, L2> {} +impl<'a, K, D: DescriptorRgb, L2: Layer2> Clone for WalletWitnessFilter<'a, K, D, L2> { fn clone(&self) -> Self { *self } } -impl<'a, K, D: DescriptorRgb> AssignmentsFilter for WalletWitnessFilter<'a, K, D> { +impl<'a, K, D: DescriptorRgb, L2: Layer2> AssignmentsFilter + for WalletWitnessFilter<'a, K, D, L2> +{ fn should_include(&self, _: impl Into, witness_id: Option) -> bool { self.0 .history() diff --git a/src/pay.rs b/src/pay.rs index d2237b8..63a5975 100644 --- a/src/pay.rs +++ b/src/pay.rs @@ -26,7 +26,7 @@ use bp::dbc::tapret::TapretProof; use bp::seals::txout::ExplicitSeal; use bp::{Outpoint, Sats, ScriptPubkey, Vout}; use bpstd::{psbt, Address}; -use bpwallet::{Layer2Tx, TxRow, Wallet, WalletDescr}; +use bpwallet::{Layer2, Layer2Tx, NoLayer2, TxRow, Wallet, WalletDescr}; use psrgbt::{ Beneficiary as BpBeneficiary, Psbt, PsbtConstructor, PsbtMeta, RgbPsbt, TapretKeyError, TxParams, @@ -64,28 +64,31 @@ impl TransferParams { struct ContractOutpointsFilter< 'stock, 'wallet, - W: WalletProvider + ?Sized, + W: WalletProvider + ?Sized, K, S: StashProvider, H: StateProvider, P: IndexProvider, + L2: Layer2 = NoLayer2, > where W::Descr: DescriptorRgb { contract_id: ContractId, stock: &'stock Stock, wallet: &'wallet W, - _phantom: PhantomData, + _key_phantom: PhantomData, + _layer2_phantom: PhantomData, } impl< 'stock, 'wallet, - W: WalletProvider + ?Sized, + W: WalletProvider + ?Sized, K, S: StashProvider, H: StateProvider, P: IndexProvider, -> AssignmentsFilter for ContractOutpointsFilter<'stock, 'wallet, W, K, S, H, P> + L2: Layer2, +> AssignmentsFilter for ContractOutpointsFilter<'stock, 'wallet, W, K, S, H, P, L2> where W::Descr: DescriptorRgb { fn should_include(&self, output: impl Into, id: Option) -> bool { @@ -97,7 +100,7 @@ where W::Descr: DescriptorRgb } } -pub trait WalletProvider: PsbtConstructor +pub trait WalletProvider: PsbtConstructor where Self::Descr: DescriptorRgb { fn filter_outpoints(&self) -> impl AssignmentsFilter + Clone; @@ -105,7 +108,7 @@ where Self::Descr: DescriptorRgb fn filter_witnesses(&self) -> impl AssignmentsFilter + Clone; fn with_descriptor_mut( &mut self, - f: impl FnOnce(&mut WalletDescr) -> R, + f: impl FnOnce(&mut WalletDescr) -> R, ) -> R; fn utxos(&self) -> impl Iterator; fn txos(&self) -> impl Iterator; @@ -164,7 +167,8 @@ where Self::Descr: DescriptorRgb contract_id, stock, wallet: self, - _phantom: PhantomData, + _key_phantom: PhantomData, + _layer2_phantom: PhantomData, }; let contract = stock .contract_iface(contract_id, iface_name) @@ -352,11 +356,14 @@ where Self::Descr: DescriptorRgb } } -impl> WalletProvider for Wallet { +impl, L2: Layer2> WalletProvider for Wallet { 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(&mut self, f: impl FnOnce(&mut WalletDescr) -> R) -> R { + fn with_descriptor_mut( + &mut self, + f: impl FnOnce(&mut WalletDescr) -> R, + ) -> R { self.descriptor_mut(f) } fn utxos(&self) -> impl Iterator { self.coins().map(|coin| coin.outpoint) } diff --git a/src/wallet.rs b/src/wallet.rs index 11709f8..c24ac80 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -28,6 +28,7 @@ use bpstd::XpubDerivable; use bpwallet::fs::FsTextStore; #[cfg(feature = "fs")] use bpwallet::Wallet; +use bpwallet::{Layer2, NoLayer2}; #[cfg(not(target_arch = "wasm32"))] use nonasync::persistence::PersistenceProvider; use psrgbt::{Psbt, PsbtMeta}; @@ -48,22 +49,25 @@ use crate::invoice::RgbInvoice; #[derive(Getters)] pub struct RgbWallet< - W: WalletProvider, + W: WalletProvider, K = XpubDerivable, S: StashProvider = MemStash, H: StateProvider = MemState, P: IndexProvider = MemIndex, + L2: Layer2 = NoLayer2, > where W::Descr: DescriptorRgb { stock: Stock, wallet: W, #[getter(skip)] - _phantom: PhantomData, + _key_phantom: PhantomData, + #[getter(skip)] + _layer2_phantom: PhantomData, } #[cfg(feature = "fs")] -impl, S: StashProvider, H: StateProvider, P: IndexProvider> - RgbWallet, K, S, H, P> +impl, S: StashProvider, H: StateProvider, P: IndexProvider, L2: Layer2> + RgbWallet, K, S, H, P, L2> { #[allow(clippy::result_large_err)] pub fn load( @@ -73,9 +77,13 @@ impl, S: StashProvider, H: StateProvider, P: IndexProvide ) -> Result where D: serde::Serialize + for<'de> serde::Deserialize<'de>, + L2::Descr: serde::Serialize + for<'de> serde::Deserialize<'de>, + L2::Data: serde::Serialize + for<'de> serde::Deserialize<'de>, + L2::Cache: serde::Serialize + for<'de> serde::Deserialize<'de>, FsBinStore: PersistenceProvider, FsBinStore: PersistenceProvider, FsBinStore: PersistenceProvider

, + FsTextStore: PersistenceProvider, { use nonasync::persistence::PersistenceError; let provider = FsBinStore::new(stock_path) @@ -87,20 +95,22 @@ impl, S: StashProvider, H: StateProvider, P: IndexProvide Ok(Self { wallet, stock, - _phantom: PhantomData, + _key_phantom: PhantomData, + _layer2_phantom: PhantomData, }) } } -impl, S: StashProvider, H: StateProvider, P: IndexProvider> - RgbWallet +impl, S: StashProvider, H: StateProvider, P: IndexProvider, L2: Layer2> + RgbWallet where W::Descr: DescriptorRgb { pub fn new(stock: Stock, wallet: W) -> Self { Self { stock, wallet, - _phantom: PhantomData, + _key_phantom: PhantomData, + _layer2_phantom: PhantomData, } } From 6c4cf742de9ae522ea4f80e634f69a3b5b5c50c8 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Wed, 25 Sep 2024 20:18:56 +0200 Subject: [PATCH 20/20] indexers: re-export errors and configs --- src/indexers/electrum_blocking.rs | 3 ++- src/indexers/esplora_blocking.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/indexers/electrum_blocking.rs b/src/indexers/electrum_blocking.rs index 599d369..7c2b30c 100644 --- a/src/indexers/electrum_blocking.rs +++ b/src/indexers/electrum_blocking.rs @@ -25,7 +25,8 @@ use std::iter; use bp::ConsensusDecode; use bpstd::{Network, Tx, Txid}; -use electrum::{Client, ElectrumApi, Error, Param}; +use electrum::{Client, ElectrumApi, Param}; +pub use electrum::{Config, ConfigBuilder, Error, Socks5Config}; use rgbstd::vm::WitnessPos; use super::RgbResolver; diff --git a/src/indexers/esplora_blocking.rs b/src/indexers/esplora_blocking.rs index 9b25ebd..324528a 100644 --- a/src/indexers/esplora_blocking.rs +++ b/src/indexers/esplora_blocking.rs @@ -21,7 +21,8 @@ use bp::Tx; use bpstd::{Network, Txid}; -use esplora::{BlockingClient, Error}; +use esplora::BlockingClient; +pub use esplora::{Builder, Config, Error}; use rgbstd::vm::WitnessPos; use super::RgbResolver;