From 90df45ad15abca93adc66fc1f558a410876de86d Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sun, 19 Nov 2023 19:47:49 +0100 Subject: [PATCH 01/27] wip on transfers --- Cargo.lock | 4 ++ Cargo.toml | 10 +++- cli/Cargo.toml | 3 +- cli/src/command.rs | 124 ++++++++++++++++++++------------------- examples/rgb20-demo.yaml | 12 ++-- src/descriptor.rs | 2 +- src/lib.rs | 3 +- {psbt/src => src}/pay.rs | 27 ++++----- src/runtime.rs | 30 +--------- 9 files changed, 99 insertions(+), 116 deletions(-) rename {psbt/src => src}/pay.rs (94%) diff --git a/Cargo.lock b/Cargo.lock index 712a83a..2615c0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1526,9 +1526,12 @@ dependencies = [ "bp-esplora", "bp-std", "bp-wallet", + "chrono", "descriptors", "indexmap 2.1.0", "log", + "psbt", + "rgb-invoice", "rgb-persist-fs", "rgb-std", "serde", @@ -1572,6 +1575,7 @@ dependencies = [ "commit_verify", "env_logger", "log", + "psbt", "rgb-invoice", "rgb-runtime", "rgb-std", diff --git a/Cargo.toml b/Cargo.toml index cdbdfd1..ce73ad3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,10 +32,13 @@ bp-core = "0.11.0-beta.1" bp-seals = "0.11.0-beta.1" bp-std = "0.11.0-beta.2" bp-wallet = "0.11.0-beta.2" -bp-utils = "0.11.0-beta.2" +bp-util = "0.11.0-beta.2" bp-esplora = "0.11.0-beta.1" +descriptors = "0.11.0-beta.1" +psbt = "0.11.0-beta.1" rgb-std = { version = "0.11.0-beta.2", features = ["fs"] } indexmap = "2.0.2" +chrono = "0.4.31" serde_crate = { package = "serde", version = "1", features = ["derive"] } serde_yaml = "0.9.19" log = { version = "0.4", features = ["max_level_trace", "release_max_level_debug"] } @@ -65,10 +68,13 @@ bp-core = { workspace = true } bp-std = { workspace = true } bp-wallet = { workspace = true, features = ["fs"] } bp-esplora = { workspace = true, optional = true } -descriptors = "0.11.0-beta.2" +descriptors = { workspace = true } +psbt = { workspace = true } rgb-std = { workspace = true } +rgb-invoice = { version = "0.11.0-alpha.1", path = "./invoice" } rgb-persist-fs = { version = "0.11.0-alpha", path = "fs" } indexmap = { workspace = true } +chrono = { workspace = true } serde_crate = { workspace = true, optional = true } serde_yaml = { workspace = true, optional = true } log = { workspace = true, optional = true } diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 84a5c9d..b0c334f 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -25,7 +25,8 @@ bp-seals = { workspace = true } bp-std = { workspace = true, features = ["serde"] } bp-wallet = { workspace = true } bp-esplora = { workspace = true } -bp-util = "0.11.0-beta.2" +bp-util = { workspace = true } +psbt = { workspace = true } rgb-std = { workspace = true, features = ["serde"] } rgb-invoice = { version = "0.11.0-alpha.2", path = "../invoice" } rgb-runtime = { version = "0.11.0-alpha.2", path = "..", features = ["log", "serde"] } diff --git a/cli/src/command.rs b/cli/src/command.rs index cf06f7d..abc2e40 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -26,6 +26,8 @@ use std::str::FromStr; use amplify::confinement::U16; use bp_util::{Config, Exec}; use bpstd::{Sats, Txid}; +use bpwallet::{Invoice, TxParams}; +use psbt::Psbt; use rgb_rt::{DescriptorRgb, RgbDescr, RgbKeychain, RuntimeError}; use rgbinvoice::{Beneficiary, InvoiceState, RgbInvoice, RgbTransport}; use rgbstd::containers::{Bindle, Transfer, UniversalBindle}; @@ -63,138 +65,143 @@ pub enum Command { #[display(inner)] Bp(bp_util::Command), - /// Prints out list of known RGB schemata. + /// Prints out list of known RGB schemata Schemata, - /// Prints out list of known RGB interfaces. + /// Prints out list of known RGB interfaces Interfaces, - /// Prints out list of known RGB contracts. + /// Prints out list of known RGB contracts Contracts, - /// Imports RGB data into the stash: contracts, schema, interfaces, etc. + /// Imports RGB data into the stash: contracts, schema, interfaces, etc #[display("import")] Import { - /// Use BASE64 ASCII armoring for binary data. + /// Use BASE64 ASCII armoring for binary data #[arg(short)] armored: bool, - /// File with RGB data. If not provided, assumes `-a` and prints out - /// data to STDOUT. + /// File with RGB data + /// + /// If not provided, assumes `-a` and prints out data to STDOUT file: PathBuf, }, - /// Exports existing RGB contract. + /// Exports existing RGB contract #[display("export")] Export { - /// Use BASE64 ASCII armoring for binary data. + /// Use BASE64 ASCII armoring for binary data #[arg(short)] armored: bool, - /// Contract to export. + /// Contract to export contract: ContractId, - /// File with RGB data. If not provided, assumes `-a` and reads the data - /// from STDIN. + /// File with RGB data + /// + /// If not provided, assumes `-a` and reads the data from STDIN file: Option, }, - /// Reports information about state of a contract. + /// Reports information about state of a contract #[display("state")] State { - /// Contract identifier. + /// Contract identifier contract_id: ContractId, - /// Interface to interpret the state data. + /// Interface to interpret the state data iface: String, }, - /// Issues new contract. + /// Issues new contract #[display("issue")] Issue { - /// Schema name to use for the contract. + /// Schema name to use for the contract schema: SchemaId, //String, - /// File containing contract genesis description in YAML format. + /// File containing contract genesis description in YAML format contract: PathBuf, }, - /// Create new invoice. + /// Create new invoice #[display("invoice")] Invoice { - /// Force address-based invoice. + /// Force address-based invoice #[clap(short, long)] address_based: bool, - /// Contract identifier. + /// Contract identifier contract_id: ContractId, - /// Interface to interpret the state data. + /// Interface to interpret the state data iface: String, - /// Value to transfer. + /// Value to transfer value: u64, }, - /// Create new transfer. + /// Transfer RGB assets #[display("transfer")] Transfer { + /// Method for single-use-seals #[clap(long, default_value = "tapret1st")] - /// Method for single-use-seals. method: CloseMethod, - /// PSBT file. - psbt_file: PathBuf, - - /// Invoice data. + /// Invoice data invoice: RgbInvoice, - /// Filename to save transfer consignment. - out_file: PathBuf, + /// Fee + fee: Sats, + + /// File for generated PSBT + psbt: PathBuf, + + /// File for generated transfer consignment + consignment: PathBuf, }, - /// Inspects any RGB data file. + /// Inspects any RGB data file #[display("inspect")] Inspect { - #[clap(short, long, default_value = "yaml")] /// Format used for data inspection + #[clap(short, long, default_value = "yaml")] format: InspectFormat, - /// RGB file to inspect. + /// RGB file to inspect file: PathBuf, }, - /// Debug-dump all stash and inventory data. + /// Debug-dump all stash and inventory data #[display("dump")] Dump { - /// Directory to put the dump into. + /// Directory to put the dump into #[arg(default_value = "./rgb-dump")] root_dir: String, }, - /// Validate transfer consignment. + /// Validate transfer consignment #[display("validate")] Validate { - /// File with the transfer consignment. + /// File with the transfer consignment file: PathBuf, }, - /// Validate transfer consignment & accept to the stash. + /// Validate transfer consignment & accept to the stash #[display("accept")] Accept { - /// Force accepting consignments with non-mined terminal witness. + /// Force accepting consignments with non-mined terminal witness #[arg(short, long)] force: bool, - /// File with the transfer consignment. + /// File with the transfer consignment file: PathBuf, }, /// Set first opret/tapret output to host a commitment #[display("set-host")] SetHost { + /// Method for single-use-seals #[arg(long, default_value = "tapret1st")] - /// Method for single-use-seals. method: CloseMethod, - /// PSBT file. + /// PSBT file psbt_file: PathBuf, }, } @@ -560,29 +567,26 @@ impl Exec for RgbArgs { #[allow(unused_variables)] Command::Transfer { method, - psbt_file, invoice, - out_file, + fee, + psbt: psbt_filename, + consignment: out_file, } => { - todo!() - /* - // TODO: Check PSBT format - let psbt_data = fs::read(&psbt_file)?; - let mut psbt = Psbt::deserialize(&psbt_data)?; + let mut runtime = self.rgb_runtime(&config)?; + + eprint!("Constructing PSBT ... "); + let mut psbt = + runtime + .wallet_mut() + .construct_psbt(coins, Invoice, TxParams::with(*fee))?; + eprintln!("success"); + + eprint!("Constructing transfer consignment ... "); let transfer = runtime .pay(invoice, &mut psbt, method) .map_err(|err| err.to_string())?; - fs::write(&psbt_file, psbt.serialize())?; - // TODO: Print PSBT as Base64 transfer.save(&out_file)?; - eprintln!("Transfer is created and saved into '{}'.", out_file.display()); - eprintln!( - "PSBT file '{}' is updated with all required commitments and ready to be \ - signed.", - psbt_file.display() - ); - eprintln!("Stash data are updated."); - */ + eprintln!("success"); } Command::Inspect { file, format } => { let bindle = UniversalBindle::load_file(file)?; diff --git a/examples/rgb20-demo.yaml b/examples/rgb20-demo.yaml index dddc475..3c9fbe4 100644 --- a/examples/rgb20-demo.yaml +++ b/examples/rgb20-demo.yaml @@ -3,10 +3,10 @@ interface: RGB20 globals: spec: naming: - ticker: TST - name: Test asset by RGB command-line + ticker: DBG + name: Debug asset details: ~ - precision: 8 + precision: 2 data: terms: > SUBJECT TO, AND WITHOUT IN ANY WAY LIMITING, THE REPRESENTATIONS AND WARRANTIES OF ANY SELLER @@ -23,10 +23,10 @@ globals: HEREIN. PURCHASER ACKNOWLEDGES THAT THE PURCHASE PRICE REFLECTS AND TAKES INTO ACCOUNT THAT THE PROPERTY IS BEING SOLD “AS IS”. media: ~ - issuedSupply: 100000000000000 + issuedSupply: 100000000 created: 1687969158 assignments: assetOwner: - seal: tapret1st:ca43e9a01782343c78fa67cc20d75d81545a8d38a031d361180c36c088639fed:0 - amount: 100000000000000 + seal: tapret1st:4729190e129e4eb0b95b1e2b6050ade623e9b82a740f2d61c22488e0b3cc145a:1 + amount: 100000000 diff --git a/src/descriptor.rs b/src/descriptor.rs index 318714c..4eb5157 100644 --- a/src/descriptor.rs +++ b/src/descriptor.rs @@ -1,4 +1,4 @@ -// RGB standard library for working with smart contracts on Bitcoin & Lightning +// RGB wallet library for smart contracts on Bitcoin & Lightning network // // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/lib.rs b/src/lib.rs index bc471d8..a3cc7b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -// RGB smart contract wallet runtime +// RGB wallet library for smart contracts on Bitcoin & Lightning network // // SPDX-License-Identifier: Apache-2.0 // @@ -30,6 +30,7 @@ extern crate serde_crate as serde; mod runtime; mod descriptor; +mod pay; pub use descriptor::{DescriptorRgb, RgbDescr, RgbKeychain, TapretKey}; pub use runtime::{Runtime, RuntimeError}; diff --git a/psbt/src/pay.rs b/src/pay.rs similarity index 94% rename from psbt/src/pay.rs rename to src/pay.rs index 74df140..1cdbebc 100644 --- a/psbt/src/pay.rs +++ b/src/pay.rs @@ -24,20 +24,17 @@ use std::collections::{BTreeMap, HashMap}; use std::error::Error; use std::iter; -use amplify::RawArray; -use bitcoin::hashes::Hash; -use bitcoin::psbt::Psbt; use bp::seals::txout::CloseMethod; use bp::{Outpoint, Txid}; use chrono::Utc; -use rgb::{AssignmentType, ContractId, GraphSeal, Operation, Opout}; +use psbt::Psbt; +use rgbinvoice::{Beneficiary, RgbInvoice}; use rgbstd::containers::{Bindle, BuilderSeal, Transfer}; use rgbstd::interface::{BuilderError, ContractSuppl, TypedState, VelocityHint}; use rgbstd::persistence::{ConsignerError, Inventory, InventoryError, Stash}; +use rgbstd::{AssignmentType, ContractId, GraphSeal, Operation, Opout}; -use crate::invoice::Beneficiary; -use crate::psbt::{DbcPsbtError, PsbtDbc, RgbExt, RgbInExt, RgbOutExt, RgbPsbtError}; -use crate::{RgbInvoice, RGB_NATIVE_DERIVATION_INDEX, RGB_TAPRET_DERIVATION_INDEX}; +use crate::Runtime; #[derive(Debug, Display, Error, From)] #[display(inner)] @@ -86,13 +83,13 @@ where E1: From DbcPsbt(DbcPsbtError), } -pub trait InventoryWallet: Inventory { +impl Runtime { /// # Assumptions /// /// 1. If PSBT output has BIP32 derivation information it belongs to our /// wallet - except when it matches address from the invoice. #[allow(clippy::result_large_err, clippy::type_complexity)] - fn pay( + fn transfer( &mut self, invoice: RgbInvoice, psbt: &mut Psbt, @@ -284,21 +281,19 @@ pub trait InventoryWallet: Inventory { // TODO: Ensure that with PSBTv2 we remove flag allowing PSBT modification. // 4. Prepare transfer - let witness_txid = psbt.unsigned_tx.txid(); + let witness_txid = psbt.txid(); self.consume_anchor(anchor)?; for (id, bundle) in bundles { self.consume_bundle(id, bundle, witness_txid.to_byte_array().into())?; } let beneficiary = match beneficiary { - BuilderSeal::Revealed(seal) => BuilderSeal::Revealed( - seal.resolve(Txid::from_raw_array(witness_txid.to_byte_array())), - ), + BuilderSeal::Revealed(seal) => { + BuilderSeal::Revealed(seal.resolve(witness_txid.to_byte_array())) + } BuilderSeal::Concealed(seal) => BuilderSeal::Concealed(seal), }; - let transfer = self.transfer(contract_id, [beneficiary])?; + let transfer = self.stock().transfer(contract_id, [beneficiary])?; Ok(transfer) } } - -impl InventoryWallet for I where I: Inventory {} diff --git a/src/runtime.rs b/src/runtime.rs index 350ad6c..80f1361 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -1,4 +1,4 @@ -// RGB smart contract wallet runtime +// RGB wallet library for smart contracts on Bitcoin & Lightning network // // SPDX-License-Identifier: Apache-2.0 // @@ -223,38 +223,10 @@ impl, K> Runtime { pub fn attach(&mut self, wallet: Wallet) { self.wallet = wallet } - pub fn descriptor(&self) -> &D { self.wallet.deref() } - pub fn unload(self) {} pub fn address_network(&self) -> AddressNetwork { self.network.into() } - /* - pub fn create_wallet( - &mut self, - name: &Ident, - xpub: ExtendedPubKey, - ) -> Result<&RgbDescr, RuntimeError> { - let descr = RgbDescr::Tapret(Tapret { - xpub, - taprets: empty!(), - }); - let entry = match self.wallets.entry(name.clone()) { - Entry::Occupied(_) => return Err(format!("wallet named {name} already exists").into()), - Entry::Vacant(entry) => entry.insert(descr), - }; - Ok(entry) - } - - pub fn wallet(&mut self, name: &Ident) -> Result { - let descr = self - .wallets - .get(name) - .ok_or(RuntimeError::WalletUnknown(name.clone()))?; - Ok(RgbWallet::new(descr.clone())) - } - */ - pub fn import_contract( &mut self, contract: Contract, From 2d58939c56eeb041a299dcb9fc5ac44df7a9e709 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 1 Dec 2023 21:09:59 +0100 Subject: [PATCH 02/27] wip on pay method refactoring --- src/pay.rs | 99 ++++++++++++++++++++---------------------------------- 1 file changed, 36 insertions(+), 63 deletions(-) diff --git a/src/pay.rs b/src/pay.rs index 1cdbebc..89267be 100644 --- a/src/pay.rs +++ b/src/pay.rs @@ -21,6 +21,7 @@ use std::cmp::Ordering; use std::collections::{BTreeMap, HashMap}; +use std::convert::Infallible; use std::error::Error; use std::iter; @@ -32,24 +33,16 @@ use rgbinvoice::{Beneficiary, RgbInvoice}; use rgbstd::containers::{Bindle, BuilderSeal, Transfer}; use rgbstd::interface::{BuilderError, ContractSuppl, TypedState, VelocityHint}; use rgbstd::persistence::{ConsignerError, Inventory, InventoryError, Stash}; -use rgbstd::{AssignmentType, ContractId, GraphSeal, Operation, Opout}; +use rgbstd::{ + AssignmentType, ContractId, GraphSeal, Operation, Opout, SealDefinition, + RGB_NATIVE_DERIVATION_INDEX, RGB_TAPRET_DERIVATION_INDEX, +}; use crate::Runtime; #[derive(Debug, Display, Error, From)] #[display(inner)] -pub enum PayError -where E1: From -{ - /// not enough PSBT output found to put all required state (can't add - /// assignment type {1} for {0}-velocity state). - #[display(doc_comments)] - NoBlankOrChange(VelocityHint, AssignmentType), - - /// PSBT lacks beneficiary output matching the invoice. - #[display(doc_comments)] - NoBeneficiaryOutput, - +pub enum PayError { /// unspecified contract #[display(doc_comments)] NoContract, @@ -68,36 +61,23 @@ where E1: From InvoiceExpired, #[from] - Inventory(InventoryError), + Inventory(InventoryError), #[from] Builder(BuilderError), #[from] - Consigner(ConsignerError), - - #[from] - RgbPsbt(RgbPsbtError), + Consigner(ConsignerError), +} - #[from] - DbcPsbt(DbcPsbtError), +#[derive(Clone, Eq, PartialEq, Hash, Debug)] +pub struct Payment { + pub unsigned_psbt: Psbt, + pub transfer: Bindle, } impl Runtime { - /// # Assumptions - /// - /// 1. If PSBT output has BIP32 derivation information it belongs to our - /// wallet - except when it matches address from the invoice. - #[allow(clippy::result_large_err, clippy::type_complexity)] - fn transfer( - &mut self, - invoice: RgbInvoice, - psbt: &mut Psbt, - method: CloseMethod, - ) -> Result, PayError::Error>> - where - Self::Error: From<::Error>, - { + pub fn pay(&mut self, invoice: RgbInvoice, method: CloseMethod) -> Result { // 1. Prepare the data if let Some(expiry) = invoice.expiry { if expiry < Utc::now().timestamp() { @@ -109,6 +89,8 @@ impl Runtime { let mut main_builder = self.transition_builder(contract_id, iface.clone(), invoice.operation)?; + // 2. Construct PSBT + let (beneficiary_output, beneficiary) = match invoice.beneficiary { Beneficiary::BlindedSeal(seal) => { let seal = BuilderSeal::Concealed(seal); @@ -116,45 +98,37 @@ impl Runtime { } Beneficiary::WitnessUtxo(addr) => { let vout = psbt - .unsigned_tx - .output - .iter() - .enumerate() - .find(|(_, txout)| txout.script_pubkey == addr.script_pubkey()) - .map(|(no, _)| no as u32) + .outputs() + .position(|out| out.script == addr.script_pubkey()) .ok_or(PayError::NoBeneficiaryOutput)?; - let seal = BuilderSeal::Revealed(GraphSeal::new_vout(method, vout)); + let seal = BuilderSeal::Revealed(SealDefinition::Bitcoin(GraphSeal::new_vout( + method, vout, + ))); (Some(vout), seal) } }; - let prev_outputs = psbt - .unsigned_tx - .input - .iter() - .map(|txin| txin.previous_output) - .map(|outpoint| Outpoint::new(outpoint.txid.to_byte_array().into(), outpoint.vout)) - .collect::>(); + let prev_outpoints = psbt.inputs().map(|inp| inp.prevout().outpoint()); // Classify PSBT outputs which can be used for assignments - let mut out_classes = HashMap::>::new(); - for (no, outp) in psbt.outputs.iter().enumerate() { - if beneficiary_output == Some(no as u32) { + let mut out_classes = HashMap::>::new(); + for (no, outp) in psbt.outputs().enumerate() { + if beneficiary_output == Some(no) { continue; } if outp // NB: Here we assume that if output has derivation information it belongs to our wallet. .bip32_derivation - .first_key_value() + .first() .map(|(_, src)| src) - .or_else(|| outp.tap_key_origins.first_key_value().map(|(_, (_, src))| src)) - .and_then(|(_, src)| src.into_iter().rev().nth(1)) + .or_else(|| outp.tap_bip32_derivation.first().map(|(_, d)| &d.origin)) + .and_then(|orig| orig.derivation().iter().rev().nth(1)) .copied() .map(u32::from) .filter(|index| *index == RGB_NATIVE_DERIVATION_INDEX || *index == RGB_TAPRET_DERIVATION_INDEX) .is_some() { let class = outp.rgb_velocity_hint().unwrap_or_default(); - out_classes.entry(class).or_default().push(no as u32); + out_classes.entry(class).or_default().push(no); } } let mut out_classes = out_classes @@ -163,7 +137,7 @@ impl Runtime { .collect::>(); let mut output_for_assignment = |suppl: Option<&ContractSuppl>, assignment_type: AssignmentType| - -> Result, PayError<_, _>> { + -> Result, PayError> { let velocity = suppl .and_then(|suppl| suppl.owned_state.get(&assignment_type)) .map(|s| s.velocity) @@ -178,7 +152,7 @@ impl Runtime { }) .ok_or(PayError::NoBlankOrChange(velocity, assignment_type))?; let seal = GraphSeal::new_vout(method, vout); - Ok(BuilderSeal::Revealed(seal)) + Ok(BuilderSeal::Revealed(SealDefinition::Bitcoin(seal))) }; // 2. Prepare and self-consume transition @@ -196,12 +170,12 @@ impl Runtime { .and_then(|set| set.first()) .cloned(); let mut sum_inputs = 0u64; - for (opout, state) in self.state_for_outpoints(contract_id, prev_outputs.iter().copied())? { + for (opout, state) in self.state_for_outputs(contract_id, prev_outpoints)? { main_builder = main_builder.add_input(opout)?; if opout.ty != assignment_id { let seal = output_for_assignment(suppl.as_ref(), opout.ty)?; main_builder = main_builder.add_raw_state(opout.ty, seal, state)?; - } else if let TypedState::Amount(value) = state { + } else if let TypedState::Amount(value, _) = state { sum_inputs += value; } } @@ -229,7 +203,7 @@ impl Runtime { // 3. Prepare and self-consume other transitions let mut contract_inputs = HashMap::>::new(); let mut spent_state = HashMap::>::new(); - for outpoint in prev_outputs { + for outpoint in prev_outpoints { for id in self.contracts_by_outpoints([outpoint])? { contract_inputs.entry(id).or_default().push(outpoint); if id == contract_id { @@ -262,9 +236,8 @@ impl Runtime { other_transitions.insert(contract_id, transition); for (id, transition) in other_transitions { let inputs = contract_inputs.remove(&id).unwrap_or_default(); - for (input, txin) in psbt.inputs.iter_mut().zip(&psbt.unsigned_tx.input) { - let prevout = txin.previous_output; - let outpoint = Outpoint::new(prevout.txid.to_byte_array().into(), prevout.vout); + for input in psbt.inputs() { + let outpoint = input.prevout().outpoint(); if inputs.contains(&outpoint) { input.set_rgb_consumer(id, transition.id())?; } From 333057a959a0e17c10529851ffa779f35c1a1af5 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sat, 2 Dec 2023 18:43:47 +0100 Subject: [PATCH 03/27] wip on payment workflow --- cli/src/command.rs | 27 ++++++++++++++++++++------- src/pay.rs | 5 +++-- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/cli/src/command.rs b/cli/src/command.rs index abc2e40..a8289fe 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -150,11 +150,11 @@ pub enum Command { /// Fee fee: Sats, - /// File for generated PSBT - psbt: PathBuf, - /// File for generated transfer consignment consignment: PathBuf, + + /// Name of PSBT file to save. If not given, prints PSBT to STDOUT + psbt: Option, }, /// Inspects any RGB data file @@ -572,13 +572,26 @@ impl Exec for RgbArgs { psbt: psbt_filename, consignment: out_file, } => { + // 1. BP Wallet: Do coin selection (using Layer2 components) + // 2. BP Wallet: Construct PSBT prototype (no state transitions) + // ... complete PSBT structure updates in multi-party protocols + // 3. RGB Std: Prepare stencil - main state transition and blank state + // transitions + // 4. RGB PSBT: Embed stencil into PSBT + // ... complete PSBT client-side updates in multi-party protocols + // 5. RGB PSBT: Anchorize PSBT, extract disclosure + // 6. RGB Std: Merge disclosure into the stash, cache and index + // 7. RGB Std: Prepare consignment + let mut runtime = self.rgb_runtime(&config)?; + // TODO: Support lock time and RBFs + let params = TxParams::with(*fee); + eprint!("Constructing PSBT ... "); - let mut psbt = - runtime - .wallet_mut() - .construct_psbt(coins, Invoice, TxParams::with(*fee))?; + let mut psbt = runtime + .wallet_mut() + .construct_psbt(coins, Invoice, params)?; eprintln!("success"); eprint!("Constructing transfer consignment ... "); diff --git a/src/pay.rs b/src/pay.rs index 89267be..dcf423f 100644 --- a/src/pay.rs +++ b/src/pay.rs @@ -200,7 +200,8 @@ impl Runtime { } }; - // 3. Prepare and self-consume other transitions + // 3. Prepare other transitions + // Enumerate state let mut contract_inputs = HashMap::>::new(); let mut spent_state = HashMap::>::new(); for outpoint in prev_outpoints { @@ -215,7 +216,7 @@ impl Runtime { .extend(self.state_for_outpoints(id, [outpoint])?); } } - // Construct blank transitions, self-consume them + // Construct blank transitions let mut other_transitions = HashMap::with_capacity(spent_state.len()); for (id, opouts) in spent_state { let mut blank_builder = self.blank_builder(id, iface.clone())?; From 88ef5cb82267e24f2d11cd766ab53f35699fe42b Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sat, 2 Dec 2023 19:23:04 +0100 Subject: [PATCH 04/27] wip on moving out invoice library --- Cargo.toml | 3 - invoice/Cargo.toml | 30 -- invoice/src/builder.rs | 170 ----------- invoice/src/invoice.rs | 70 ----- invoice/src/lib.rs | 34 --- invoice/src/parse.rs | 654 ----------------------------------------- 6 files changed, 961 deletions(-) delete mode 100644 invoice/Cargo.toml delete mode 100644 invoice/src/builder.rs delete mode 100644 invoice/src/invoice.rs delete mode 100644 invoice/src/lib.rs delete mode 100644 invoice/src/parse.rs diff --git a/Cargo.toml b/Cargo.toml index ce73ad3..b7327f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,10 @@ [workspace] members = [ - "invoice", "cli", "fs", "." ] default-members = [ - "invoice", "cli", "fs", "." @@ -71,7 +69,6 @@ bp-esplora = { workspace = true, optional = true } descriptors = { workspace = true } psbt = { workspace = true } rgb-std = { workspace = true } -rgb-invoice = { version = "0.11.0-alpha.1", path = "./invoice" } rgb-persist-fs = { version = "0.11.0-alpha", path = "fs" } indexmap = { workspace = true } chrono = { workspace = true } diff --git a/invoice/Cargo.toml b/invoice/Cargo.toml deleted file mode 100644 index e5301c8..0000000 --- a/invoice/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "rgb-invoice" -version = { workspace = true } -description = "Invoicing library for RGB smart contracts" -keywords = { workspace = true } -categories = { workspace = true } -readme = "../README.md" -authors = { workspace = true } -repository = { workspace = true } -homepage = { workspace = true } -rust-version = { workspace = true } -edition = { workspace = true } -license = { workspace = true } - -[lib] -name = "rgbinvoice" - -[dependencies] -amplify = { workspace = true } -baid58 = { workspace = true } -strict_encoding = { workspace = true } -bp-seals = { workspace = true } -bp-std = { workspace = true } -rgb-std = { workspace = true } -indexmap = { workspace = true } -fluent-uri = "0.1.4" -percent-encoding = "2.3.0" - -[features] -default = [] diff --git a/invoice/src/builder.rs b/invoice/src/builder.rs deleted file mode 100644 index 6ecb38a..0000000 --- a/invoice/src/builder.rs +++ /dev/null @@ -1,170 +0,0 @@ -// RGB wallet library for smart contracts on Bitcoin & Lightning network -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2019-2023 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::str::FromStr; - -use bpstd::Network; -use rgbstd::stl::Precision; -use rgbstd::ContractId; - -use super::{Beneficiary, InvoiceState, RgbInvoice, RgbTransport, TransportParseError}; - -#[derive(Clone, Eq, PartialEq, Debug)] -pub struct RgbInvoiceBuilder(RgbInvoice); - -#[allow(clippy::result_large_err)] -impl RgbInvoiceBuilder { - pub fn new(beneficiary: impl Into) -> Self { - Self(RgbInvoice { - transports: vec![RgbTransport::UnspecifiedMeans], - contract: None, - iface: None, - operation: None, - assignment: None, - beneficiary: beneficiary.into(), - owned_state: InvoiceState::Void, - network: None, - expiry: None, - unknown_query: none!(), - }) - } - - pub fn with(contract_id: ContractId, beneficiary: impl Into) -> Self { - Self::new(beneficiary).set_contract(contract_id) - } - - pub fn rgb20(contract_id: ContractId, beneficiary: impl Into) -> Self { - Self::with(contract_id, beneficiary).set_interface("RGB20") - } - - pub fn rgb20_anything(beneficiary: impl Into) -> Self { - Self::new(beneficiary).set_interface("RGB20") - } - - pub fn set_contract(mut self, contract_id: ContractId) -> Self { - self.0.contract = Some(contract_id); - self - } - - pub fn set_interface(mut self, name: &'static str) -> Self { - self.0.iface = Some(tn!(name)); - self - } - - pub fn set_operation(mut self, name: &'static str) -> Self { - self.0.operation = Some(tn!(name)); - self - } - - pub fn set_assignment(mut self, name: &'static str) -> Self { - self.0.assignment = Some(fname!(name)); - self - } - - pub fn set_amount_raw(mut self, amount: u64) -> Self { - self.0.owned_state = InvoiceState::Amount(amount); - self - } - - pub fn set_amount( - self, - integer: u64, - decimals: u64, - precision: Precision, - ) -> Result { - // 2^64 ~ 10^19 < 10^18 (18 is max value for Precision enum) - let pow = 10u64.pow(precision as u32); - // number of decimals can't be larger than the smallest possible integer - if decimals >= pow { - return Err(self); - } - let Some(mut amount) = integer.checked_mul(pow) else { - return Err(self); - }; - amount = amount.checked_add(decimals).expect( - "integer has at least the same number of zeros in the lowest digits as much as \ - decimals has digits at most, so overflow is not possible", - ); - Ok(self.set_amount_raw(amount)) - } - - /// # Safety - /// - /// The function may cause the loss of the information about the precise - /// amout of the asset, since f64 type doesn't provide full precision - /// required for that. - pub unsafe fn set_amount_approx(self, amount: f64, precision: Precision) -> Result { - if amount <= 0.0 { - return Err(self); - } - let coins = amount.floor(); - let cents = amount - coins; - self.set_amount(coins as u64, cents as u64, precision) - } - - pub fn set_network(mut self, network: impl Into) -> Self { - self.0.network = Some(network.into()); - self - } - - pub fn set_expiry_timestamp(mut self, expiry: i64) -> Self { - self.0.expiry = Some(expiry); - self - } - - pub fn add_transport(self, transport: &str) -> Result { - let transport = match RgbTransport::from_str(transport) { - Err(err) => return Err((self, err)), - Ok(transport) => transport, - }; - Ok(self.add_transport_raw(transport)) - } - - pub fn add_transport_raw(mut self, transport: RgbTransport) -> Self { - self.0.transports.push(transport); - self - } - - pub fn add_transports<'a>( - self, - transports: impl IntoIterator, - ) -> Result { - let res = transports - .into_iter() - .map(RgbTransport::from_str) - .collect::, TransportParseError>>(); - let transports = match res { - Err(err) => return Err((self, err)), - Ok(transports) => transports, - }; - Ok(self.add_transports_raw(transports)) - } - - pub fn add_transports_raw( - mut self, - transports: impl IntoIterator, - ) -> Self { - self.0.transports.extend(transports); - self - } - - pub fn finish(self) -> RgbInvoice { self.0 } -} diff --git a/invoice/src/invoice.rs b/invoice/src/invoice.rs deleted file mode 100644 index d7c5dc8..0000000 --- a/invoice/src/invoice.rs +++ /dev/null @@ -1,70 +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 bpstd::{Address, Network}; -use indexmap::IndexMap; -use rgbstd::{AttachId, ContractId, SecretSeal}; -use strict_encoding::{FieldName, TypeName}; - -#[derive(Clone, Eq, PartialEq, Hash, Debug)] -pub enum RgbTransport { - JsonRpc { tls: bool, host: String }, - RestHttp { tls: bool, host: String }, - WebSockets { tls: bool, host: String }, - Storm {/* todo */}, - UnspecifiedMeans, -} - -#[derive(Clone, Eq, PartialEq, Hash, Debug, Display)] -pub enum InvoiceState { - #[display("")] - Void, - #[display("{0}")] - Amount(u64), - #[display("...")] // TODO - Data(Vec /* StrictVal */), - #[display(inner)] - Attach(AttachId), -} - -#[derive(Clone, Eq, PartialEq, Hash, Debug, Display, From)] -#[display(inner)] -pub enum Beneficiary { - #[from] - BlindedSeal(SecretSeal), - #[from] - WitnessUtxo(Address), -} - -#[derive(Clone, Eq, PartialEq, Debug)] -pub struct RgbInvoice { - pub transports: Vec, - pub contract: Option, - pub iface: Option, - pub operation: Option, - pub assignment: Option, - pub beneficiary: Beneficiary, - pub owned_state: InvoiceState, - pub network: Option, - /// UTC unix timestamp - pub expiry: Option, - pub unknown_query: IndexMap, -} diff --git a/invoice/src/lib.rs b/invoice/src/lib.rs deleted file mode 100644 index c9f1db2..0000000 --- a/invoice/src/lib.rs +++ /dev/null @@ -1,34 +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. - -#[macro_use] -extern crate amplify; -#[macro_use] -extern crate strict_encoding; - -#[allow(clippy::module_inception)] -mod invoice; -mod parse; -mod builder; - -pub use builder::RgbInvoiceBuilder; -pub use invoice::{Beneficiary, InvoiceState, RgbInvoice, RgbTransport}; -pub use parse::{InvoiceParseError, TransportParseError}; diff --git a/invoice/src/parse.rs b/invoice/src/parse.rs deleted file mode 100644 index 72c70cb..0000000 --- a/invoice/src/parse.rs +++ /dev/null @@ -1,654 +0,0 @@ -// RGB wallet library for smart contracts on Bitcoin & Lightning network -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2019-2023 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::fmt::{self, Debug, Display, Formatter}; -use std::num::ParseIntError; -use std::str::FromStr; - -use bpstd::{Address, AddressNetwork, Network, UnknownNetwork}; -use fluent_uri::enc::EStr; -use fluent_uri::Uri; -use indexmap::IndexMap; -use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS}; -use rgbstd::{ContractId, SecretSeal}; -use strict_encoding::{InvalidIdent, TypeName}; - -use super::{Beneficiary, InvoiceState, RgbInvoice, RgbTransport}; - -const OMITTED: char = '~'; -const EXPIRY: &str = "expiry"; -const NETWORK: &str = "network"; -const ENDPOINTS: &str = "endpoints"; -const TRANSPORT_SEP: char = ','; -const TRANSPORT_HOST_SEP: &str = "://"; -const QUERY_ENCODE: &AsciiSet = &CONTROLS - .add(b' ') - .add(b'"') - .add(b'#') - .add(b'<') - .add(b'>') - .add(b'[') - .add(b']') - .add(b'&') - .add(b'='); - -#[derive(Clone, PartialEq, Eq, Debug, Display, Error, From)] -#[display(inner)] -pub enum TransportParseError { - #[display(doc_comments)] - /// invalid transport {0}. - InvalidTransport(String), - - #[display(doc_comments)] - /// invalid transport host {0}. - InvalidTransportHost(String), -} - -#[derive(Clone, PartialEq, Eq, Debug, Display, Error, From)] -#[display(inner)] -pub enum InvoiceParseError { - #[from] - Uri(fluent_uri::ParseError), - - #[display(doc_comments)] - /// invalid invoice. - Invalid, - - #[display(doc_comments)] - /// invalid invoice scheme {0}. - InvalidScheme(String), - - #[display(doc_comments)] - /// no invoice transport has been provided. - NoTransport, - - #[display(doc_comments)] - /// invalid invoice: contract ID present but no contract interface provided. - ContractIdNoIface, - - #[display(doc_comments)] - /// invalid contract ID. - InvalidContractId(String), - - #[display(doc_comments)] - /// invalid interface {0}. - InvalidIface(String), - - #[display(doc_comments)] - /// invalid expiration timestamp {0}. - InvalidExpiration(String), - - #[display(inner)] - #[from] - InvalidNetwork(UnknownNetwork), - - #[display(doc_comments)] - /// address network `{0:#?}` doesn't match network `{1}` specified in the - /// invoice. - NetworkMismatch(AddressNetwork, Network), - - #[display(doc_comments)] - /// invalid query parameter {0}. - InvalidQueryParam(String), - - #[from] - Id(baid58::Baid58ParseError), - - #[display(doc_comments)] - /// can't recognize beneficiary "": it should be either a bitcoin address or - /// a blinded UTXO seal. - Beneficiary(String), - - #[from] - Num(ParseIntError), - - #[from] - #[display(doc_comments)] - /// invalid interface name. - IfaceName(InvalidIdent), -} - -impl RgbInvoice { - fn has_params(&self) -> bool { - self.expiry.is_some() || - self.transports != vec![RgbTransport::UnspecifiedMeans] || - !self.unknown_query.is_empty() - } - - fn query_params(&self) -> IndexMap { - let mut query_params: IndexMap = IndexMap::new(); - if let Some(expiry) = self.expiry { - query_params.insert(EXPIRY.to_string(), expiry.to_string()); - } - if self.transports != vec![RgbTransport::UnspecifiedMeans] { - let mut transports: Vec = vec![]; - for transport in self.transports.clone() { - transports.push(transport.to_string()); - } - query_params.insert(ENDPOINTS.to_string(), transports.join(&TRANSPORT_SEP.to_string())); - } - query_params.extend(self.unknown_query.clone()); - query_params - } -} - -impl Display for RgbTransport { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - RgbTransport::JsonRpc { tls, host } => { - let s = if *tls { "s" } else { "" }; - write!(f, "rpc{s}{TRANSPORT_HOST_SEP}{}", host)?; - } - RgbTransport::RestHttp { tls, host } => { - let s = if *tls { "s" } else { "" }; - write!(f, "http{s}{TRANSPORT_HOST_SEP}{}", host)?; - } - RgbTransport::WebSockets { tls, host } => { - let s = if *tls { "s" } else { "" }; - write!(f, "ws{s}{TRANSPORT_HOST_SEP}{}", host)?; - } - RgbTransport::Storm {} => { - write!(f, "storm{TRANSPORT_HOST_SEP}_/")?; - } - RgbTransport::UnspecifiedMeans => {} - }; - Ok(()) - } -} - -impl FromStr for RgbTransport { - type Err = TransportParseError; - - fn from_str(s: &str) -> Result { - let tokens = s.split_once(TRANSPORT_HOST_SEP); - if tokens.is_none() { - return Err(TransportParseError::InvalidTransport(s.to_string())); - } - let (trans_type, host) = tokens.unwrap(); - if host.is_empty() { - return Err(TransportParseError::InvalidTransportHost(host.to_string())); - } - let host = host.to_string(); - let transport = match trans_type { - "rpc" => RgbTransport::JsonRpc { tls: false, host }, - "rpcs" => RgbTransport::JsonRpc { tls: true, host }, - "http" => RgbTransport::RestHttp { tls: false, host }, - "https" => RgbTransport::RestHttp { tls: true, host }, - "ws" => RgbTransport::WebSockets { tls: false, host }, - "wss" => RgbTransport::WebSockets { tls: true, host }, - "storm" => RgbTransport::Storm {}, - _ => return Err(TransportParseError::InvalidTransport(s.to_string())), - }; - Ok(transport) - } -} - -impl Display for RgbInvoice { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let amt = self.owned_state.to_string(); - if let Some(contract) = self.contract { - Display::fmt(&contract, f)?; - f.write_str("/")?; - } else { - write!(f, "rgb:{OMITTED}/")?; - } - if let Some(iface) = self.iface.clone() { - write!(f, "{iface}/")?; - } else { - write!(f, "{OMITTED}/")?; - } - if let Some(ref op) = self.operation { - write!(f, "{op}/")?; - } - if let Some(ref assignment_name) = self.assignment { - write!(f, "{assignment_name}/")?; - } - if !amt.is_empty() { - write!(f, "{amt}+")?; - } - Display::fmt(&self.beneficiary, f)?; - if self.has_params() { - f.write_str("?")?; - } - let query_params = self.query_params(); - for (key, val) in query_params.iter().take(1) { - write!( - f, - "{}={}", - utf8_percent_encode(key, QUERY_ENCODE), - utf8_percent_encode(val, QUERY_ENCODE) - )?; - } - for (key, val) in query_params.iter().skip(1) { - write!( - f, - "&{}={}", - utf8_percent_encode(key, QUERY_ENCODE), - utf8_percent_encode(val, QUERY_ENCODE) - )?; - } - Ok(()) - } -} - -impl FromStr for RgbInvoice { - type Err = InvoiceParseError; - - fn from_str(s: &str) -> Result { - let uri = Uri::parse(s)?; - - let scheme = uri.scheme().ok_or(InvoiceParseError::Invalid)?.to_string(); - if scheme != "rgb" { - return Err(InvoiceParseError::InvalidScheme(scheme)); - } - - let path = uri - .path() - .segments() - .map(|e| e.to_string()) - .collect::>(); - - let mut network = None; - let mut address_network = None; - - let mut next_path_index = 0; - - let contract_id_str = &path[next_path_index]; - let contract = match ContractId::from_str(contract_id_str) { - Ok(cid) => Some(cid), - Err(_) if contract_id_str == &OMITTED.to_string() => None, - Err(_) => return Err(InvoiceParseError::InvalidContractId(contract_id_str.clone())), - }; - next_path_index += 1; - - let iface_str = &path[next_path_index]; - let iface = match TypeName::try_from(iface_str.clone()) { - Ok(i) => Some(i), - Err(_) if iface_str == &OMITTED.to_string() => None, - Err(_) => return Err(InvoiceParseError::InvalidIface(iface_str.clone())), - }; - next_path_index += 1; - if contract.is_some() && iface.is_none() { - return Err(InvoiceParseError::ContractIdNoIface); - } - - let mut assignment = path[next_path_index].split('+'); - // TODO: support other state types - let (beneficiary_str, value) = match (assignment.next(), assignment.next()) { - (Some(a), Some(b)) => (b, InvoiceState::Amount(a.parse::()?)), - (Some(b), None) => (b, InvoiceState::Void), - _ => return Err(InvoiceParseError::Invalid), - }; - - let beneficiary = - match (SecretSeal::from_str(beneficiary_str), Address::from_str(beneficiary_str)) { - (Ok(seal), Err(_)) => Beneficiary::BlindedSeal(seal), - (Err(_), Ok(addr)) => { - address_network = Some(addr.network); - Beneficiary::WitnessUtxo(addr) - } - (Err(_), Err(_)) => { - return Err(InvoiceParseError::Beneficiary(beneficiary_str.to_owned())); - } - (Ok(_), Ok(_)) => { - panic!("found a string which is both valid bitcoin address and UTXO blind seal") - } - }; - - let mut query_params = map_query_params(&uri)?; - - let transports = if let Some(endpoints) = query_params.remove(ENDPOINTS) { - let tokens: Vec<&str> = endpoints.split(TRANSPORT_SEP).collect(); - let mut transport_vec: Vec = vec![]; - for token in tokens { - transport_vec.push( - RgbTransport::from_str(token) - .map_err(|e| InvoiceParseError::InvalidQueryParam(e.to_string()))?, - ); - } - transport_vec - } else { - vec![RgbTransport::UnspecifiedMeans] - }; - - let mut expiry = None; - if let Some(exp) = query_params.remove(EXPIRY) { - let timestamp = exp - .parse::() - .map_err(|e| InvoiceParseError::InvalidExpiration(e.to_string()))?; - expiry = Some(timestamp); - } - - if let Some(nw) = query_params.remove(NETWORK) { - let nw = Network::from_str(&nw)?; - if let Some(an) = address_network { - if an.is_testnet() != nw.is_testnet() { - return Err(InvoiceParseError::NetworkMismatch(an, nw)); - } - } - } else if let Some(an) = address_network { - network = Some(match an { - AddressNetwork::Mainnet => Network::Mainnet, - AddressNetwork::Testnet => Network::Testnet3, - AddressNetwork::Regtest => Network::Regtest, - }) - } - - Ok(RgbInvoice { - transports, - contract, - iface, - operation: None, - assignment: None, - beneficiary, - owned_state: value, - network, - expiry, - unknown_query: query_params, - }) - } -} - -fn percent_decode(estr: &EStr) -> Result { - Ok(estr - .decode() - .into_string() - .map_err(|e| InvoiceParseError::InvalidQueryParam(e.to_string()))? - .to_string()) -} - -fn map_query_params(uri: &Uri<&str>) -> Result, InvoiceParseError> { - let mut map: IndexMap = IndexMap::new(); - if let Some(q) = uri.query() { - let params = q.split('&'); - for p in params { - if let Some((k, v)) = p.split_once('=') { - map.insert(percent_decode(k)?, percent_decode(v)?); - } else { - return Err(InvoiceParseError::InvalidQueryParam(p.to_string())); - } - } - } - Ok(map) -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn parse() { - // all path parameters - let invoice_str = "rgb:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/RGB20/\ - 100+utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb"; - let invoice = RgbInvoice::from_str(invoice_str).unwrap(); - assert_eq!(invoice.to_string(), invoice_str); - assert_eq!(format!("{invoice:#}"), invoice_str.replace('-', "")); - - // no amount - let invoice_str = "rgb:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/RGB20/\ - utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb"; - let invoice = RgbInvoice::from_str(invoice_str).unwrap(); - assert_eq!(invoice.to_string(), invoice_str); - - // no contract ID - let invoice_str = - "rgb:~/RGB20/utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb"; - let invoice = RgbInvoice::from_str(invoice_str).unwrap(); - assert_eq!(invoice.to_string(), invoice_str); - - // no contract ID nor iface - let invoice_str = "rgb:~/~/utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb"; - let invoice = RgbInvoice::from_str(invoice_str).unwrap(); - assert_eq!(invoice.to_string(), invoice_str); - - // contract ID provided but no iface - let invoice_str = "rgb:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/~/utxob:\ - egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb"; - let result = RgbInvoice::from_str(invoice_str); - assert!(matches!(result, Err(InvoiceParseError::ContractIdNoIface))); - - // invalid contract ID - let invalid_contract_id = "invalid"; - let invoice_str = format!( - "rgb:{invalid_contract_id}/RGB20/utxob:\ - egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb" - ); - let result = RgbInvoice::from_str(&invoice_str); - assert!(matches!(result, - Err(InvoiceParseError::InvalidContractId(c)) if c == invalid_contract_id)); - - // with expiration - let invoice_str = "rgb:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/RGB20/\ - 100+utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb?\ - expiry=1682086371"; - let invoice = RgbInvoice::from_str(invoice_str).unwrap(); - assert_eq!(invoice.to_string(), invoice_str); - - // bad expiration - let invoice_str = "rgb:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/RGB20/\ - 100+utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb?\ - expiry=six"; - let result = RgbInvoice::from_str(invoice_str); - assert!(matches!(result, Err(InvoiceParseError::InvalidExpiration(_)))); - - // with bad query parameter - let invoice_str = "rgb:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/RGB20/\ - 100+utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb?expiry"; - let result = RgbInvoice::from_str(invoice_str); - assert!(matches!(result, Err(InvoiceParseError::InvalidQueryParam(_)))); - - // with an unknown query parameter - let invoice_str = "rgb:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/RGB20/\ - 100+utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb?\ - unknown=new"; - let invoice = RgbInvoice::from_str(invoice_str).unwrap(); - assert_eq!(invoice.to_string(), invoice_str); - - // with two unknown query parameters - let invoice_str = "rgb:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/RGB20/\ - 100+utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb?\ - unknown=new&another=new"; - let invoice = RgbInvoice::from_str(invoice_str).unwrap(); - assert_eq!(invoice.to_string(), invoice_str); - - // with expiration and an unknown query parameter - let invoice_str = "rgb:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/RGB20/\ - 100+utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb?\ - expiry=1682086371&unknown=new"; - let invoice = RgbInvoice::from_str(invoice_str).unwrap(); - assert_eq!(invoice.to_string(), invoice_str); - - // with an unknown query parameter containing percent-encoded text - let invoice_base = "rgb:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/RGB20/\ - 100+utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb?"; - let query_key_encoded = ":@-%20%23"; - let query_key_decoded = ":@- #"; - let query_val_encoded = "?/.%26%3D"; - let query_val_decoded = "?/.&="; - let invoice = - RgbInvoice::from_str(&format!("{invoice_base}{query_key_encoded}={query_val_encoded}")) - .unwrap(); - let query_params = invoice.query_params(); - assert_eq!(query_params[query_key_decoded], query_val_decoded); - assert_eq!( - invoice.to_string(), - format!("{invoice_base}{query_key_encoded}={query_val_encoded}") - ); - - // no scheme - let invoice_str = "2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/~/utxob:\ - egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb"; - let result = RgbInvoice::from_str(invoice_str); - assert!(matches!(result, Err(InvoiceParseError::Invalid))); - - // invalid scheme - let invoice_str = "bad:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/~/utxob:\ - egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb"; - let result = RgbInvoice::from_str(invoice_str); - assert!(matches!(result, Err(InvoiceParseError::InvalidScheme(_)))); - - // empty transport endpoint specification - let invoice_str = "rgb:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/RGB20/\ - 100+utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb?\ - endpoints="; - let result = RgbInvoice::from_str(invoice_str); - assert!(matches!(result, Err(InvoiceParseError::InvalidQueryParam(_)))); - - // invalid transport endpoint specification - let invoice_str = "rgb:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/RGB20/\ - 100+utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb?\ - endpoints=bad"; - let result = RgbInvoice::from_str(invoice_str); - assert!(matches!(result, Err(InvoiceParseError::InvalidQueryParam(_)))); - - // invalid transport variant - let invoice_str = "rgb:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/RGB20/\ - 100+utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb?\ - endpoints=rpca://host.example.com"; - let result = RgbInvoice::from_str(invoice_str); - assert!(matches!(result, Err(InvoiceParseError::InvalidQueryParam(_)))); - - // rgb-rpc variant - let invoice_str = "rgb:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/RGB20/\ - 100+utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb?\ - endpoints=rpc://host.example.com"; - let invoice = RgbInvoice::from_str(invoice_str).unwrap(); - assert_eq!(invoice.transports, vec![RgbTransport::JsonRpc { - tls: false, - host: "host.example.com".to_string() - }]); - assert_eq!(invoice.to_string(), invoice_str); - - // rgb-rpc variant, host containing authentication, "-" characters and port - let invoice_str = "rgb:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/RGB20/\ - 100+utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb?\ - endpoints=rpcs://user:pass@host-1.ex-ample.com:1234"; - let invoice = RgbInvoice::from_str(invoice_str).unwrap(); - assert_eq!(invoice.transports, vec![RgbTransport::JsonRpc { - tls: true, - host: "user:pass@host-1.ex-ample.com:1234".to_string() - }]); - assert_eq!(invoice.to_string(), invoice_str); - - // rgb-rpc variant, IPv6 host - let invoice_str = "rgb:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/RGB20/\ - 100+utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb?\ - endpoints=rpcs://%5B2001:db8::1%5D:1234"; - let invoice = RgbInvoice::from_str(invoice_str).unwrap(); - assert_eq!(invoice.transports, vec![RgbTransport::JsonRpc { - tls: true, - host: "[2001:db8::1]:1234".to_string() - }]); - assert_eq!(invoice.to_string(), invoice_str); - - // rgb-rpc variant with missing host - let invoice_str = "rgb:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/RGB20/\ - 100+utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb?\ - endpoints=rpc://"; - let result = RgbInvoice::from_str(invoice_str); - assert!(matches!(result, Err(InvoiceParseError::InvalidQueryParam(_)))); - - // rgb-rpc variant with invalid separator - let invoice_str = "rgb:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/RGB20/\ - 100+utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb?\ - endpoints=rpc/host.example.com"; - let result = RgbInvoice::from_str(invoice_str); - assert!(matches!(result, Err(InvoiceParseError::InvalidQueryParam(_)))); - - // rgb-rpc variant with invalid transport host specification - let invoice_str = "rgb:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/RGB20/\ - 100+utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb?\ - endpoints=rpc://ho]t"; - let result = RgbInvoice::from_str(invoice_str); - assert!(matches!(result, Err(InvoiceParseError::Uri(_)))); - - // rgb+http variant - let invoice_str = "rgb:\ - 2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/RGB20/\ - 100+utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb?endpoints=https://\ - host.example.com"; - let invoice = RgbInvoice::from_str(invoice_str).unwrap(); - let transports = vec![RgbTransport::RestHttp { - tls: true, - host: "host.example.com".to_string(), - }]; - assert_eq!(invoice.transports, transports); - assert_eq!(invoice.to_string(), invoice_str); - - // rgb+ws variant - let invoice_str = "rgb:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/RGB20/\ - 100+utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb?\ - endpoints=wss://host.example.com"; - let invoice = RgbInvoice::from_str(invoice_str).unwrap(); - let transports = vec![RgbTransport::WebSockets { - tls: true, - host: "host.example.com".to_string(), - }]; - assert_eq!(invoice.transports, transports); - assert_eq!(invoice.to_string(), invoice_str); - - // TODO: rgb+storm variant - - // multiple transports - let invoice_str = "rgb:\ - 2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/RGB20/\ - 100+utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb?endpoints=rpcs://\ - host1.example.com,http://host2.example.com,ws://host3.example.com"; - let invoice = RgbInvoice::from_str(invoice_str).unwrap(); - let transports = vec![ - RgbTransport::JsonRpc { - tls: true, - host: "host1.example.com".to_string(), - }, - RgbTransport::RestHttp { - tls: false, - host: "host2.example.com".to_string(), - }, - RgbTransport::WebSockets { - tls: false, - host: "host3.example.com".to_string(), - }, - ]; - assert_eq!(invoice.transports, transports); - assert_eq!(invoice.to_string(), invoice_str); - - // empty transport parse error - let result = RgbTransport::from_str(""); - assert!(matches!(result, Err(TransportParseError::InvalidTransport(_)))); - - // invalid transport parse error - let result = RgbTransport::from_str("bad"); - assert!(matches!(result, Err(TransportParseError::InvalidTransport(_)))); - - // invalid transport variant parse error - let result = RgbTransport::from_str("rpca://host.example.com"); - assert!(matches!(result, Err(TransportParseError::InvalidTransport(_)))); - - // rgb-rpc variant with missing host parse error - let result = RgbTransport::from_str("rpc://"); - assert!(matches!(result, Err(TransportParseError::InvalidTransportHost(_)))); - - // rgb-rpc variant with invalid separator parse error - let result = RgbTransport::from_str("rpc/host.example.com"); - assert!(matches!(result, Err(TransportParseError::InvalidTransport(_)))); - } -} From 5bf1ca3cb3ace11fe14eb749a3e78b0b5f5657af Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Mon, 11 Dec 2023 00:53:56 +0100 Subject: [PATCH 05/27] wip on moving transfers to std lib --- Cargo.lock | 380 ++++++++++++++++++++++++++++++++---------------- Cargo.toml | 7 + cli/Cargo.toml | 1 - psbt/Cargo.toml | 14 +- psbt/src/lib.rs | 74 ++++++++-- src/pay.rs | 168 +++------------------ 6 files changed, 354 insertions(+), 290 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2615c0e..299bcfc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -124,9 +124,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" dependencies = [ "anstyle", "anstyle-parse", @@ -144,30 +144,30 @@ checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.1" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -369,6 +369,17 @@ dependencies = [ "ureq", ] +[[package]] +name = "bp-invoice" +version = "0.11.0-beta.2" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#664fa950efcb5bb653c9af4238c64e0610447200" +dependencies = [ + "amplify", + "bech32", + "bitcoin_hashes", + "bp-consensus", +] + [[package]] name = "bp-seals" version = "0.11.0-beta.1" @@ -486,14 +497,14 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] name = "clap" -version = "4.4.8" +version = "4.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64" +checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" dependencies = [ "clap_builder", "clap_derive", @@ -501,9 +512,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.8" +version = "4.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc" +checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" dependencies = [ "anstream", "anstyle", @@ -564,6 +575,16 @@ dependencies = [ "strict_types", ] +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + [[package]] name = "constant_time_eq" version = "0.3.0" @@ -572,9 +593,9 @@ checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -582,9 +603,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" @@ -657,9 +678,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" dependencies = [ "powerfmt", "serde", @@ -705,7 +726,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -744,12 +765,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -800,9 +821,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -871,9 +892,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "h2" @@ -911,9 +932,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "heck" @@ -952,9 +973,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http", @@ -1047,9 +1068,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -1073,7 +1094,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.2", + "hashbrown 0.14.3", "serde", ] @@ -1091,20 +1112,20 @@ checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" -version = "0.3.65" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" dependencies = [ "wasm-bindgen", ] @@ -1134,9 +1155,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "log" @@ -1167,13 +1188,13 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1224,15 +1245,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" -version = "0.10.59" +version = "0.10.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a257ad03cd8fb16ad4172fedf8094451e1af1c4b70097636ef2eac9a5f0cc33" +checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" dependencies = [ "bitflags 2.4.1", "cfg-if", @@ -1262,9 +1283,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.95" +version = "0.9.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9" +checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" dependencies = [ "cc", "libc", @@ -1286,9 +1307,9 @@ checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project-lite" @@ -1322,9 +1343,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ "unicode-ident", ] @@ -1344,6 +1365,21 @@ dependencies = [ "serde", ] +[[package]] +name = "psbt-rgb" +version = "0.11.0-alpha.2" +dependencies = [ + "amplify", + "baid58", + "bp-std", + "getrandom", + "psbt", + "rand", + "rgb-std", + "wasm-bindgen", + "wasm-bindgen-test", +] + [[package]] name = "quote" version = "1.0.33" @@ -1494,17 +1530,20 @@ dependencies = [ [[package]] name = "rgb-invoice" -version = "0.11.0-alpha.2" +version = "0.11.0-beta.2" +source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#a5baa8964039abb3ef639f5ba449fd3f5de043cf" dependencies = [ "amplify", "baid58", - "bp-seals", - "bp-std", + "bp-core", + "bp-invoice", "fluent-uri", "indexmap 2.1.0", "percent-encoding", - "rgb-std", + "rgb-core", + "serde", "strict_encoding", + "strict_types", ] [[package]] @@ -1531,7 +1570,6 @@ dependencies = [ "indexmap 2.1.0", "log", "psbt", - "rgb-invoice", "rgb-persist-fs", "rgb-std", "serde", @@ -1542,8 +1580,7 @@ dependencies = [ [[package]] name = "rgb-std" version = "0.11.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e322467d948a5695007dfb2e25b6a33bd05cb28339a61b4cca6ba6b55d6513d2" +source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#a5baa8964039abb3ef639f5ba449fd3f5de043cf" dependencies = [ "amplify", "baid58", @@ -1554,6 +1591,7 @@ dependencies = [ "getrandom", "indexmap 2.1.0", "rgb-core", + "rgb-invoice", "serde", "strict_encoding", "strict_types", @@ -1576,7 +1614,6 @@ dependencies = [ "env_logger", "log", "psbt", - "rgb-invoice", "rgb-runtime", "rgb-std", "serde", @@ -1589,16 +1626,16 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.5" +version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" dependencies = [ "cc", "getrandom", "libc", "spin", "untrusted", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1618,22 +1655,22 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.25" +version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "rustls" -version = "0.21.9" +version = "0.21.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", "ring", @@ -1653,9 +1690,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "schannel" @@ -1663,9 +1700,15 @@ version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "sct" version = "0.7.1" @@ -1694,7 +1737,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2acea373acb8c21ecb5a23741452acd2593ed44ee3d343e72baaa143bc89d0d5" dependencies = [ "rand", - "secp256k1-sys 0.9.0", + "secp256k1-sys 0.9.1", "serde", ] @@ -1709,9 +1752,9 @@ dependencies = [ [[package]] name = "secp256k1-sys" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09e67c467c38fd24bd5499dc9a18183b31575c12ee549197e3e20d57aa4fe3b7" +checksum = "4dd97a086ec737e30053fd5c46f097465d25bb81dd3608825f65298c4c98be83" dependencies = [ "cc", ] @@ -1764,18 +1807,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.192" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.192" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", @@ -1921,7 +1964,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2054,7 +2097,7 @@ dependencies = [ "fastrand", "redox_syscall", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2132,9 +2175,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.34.0" +version = "1.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" +checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" dependencies = [ "backtrace", "bytes", @@ -2142,7 +2185,7 @@ dependencies = [ "mio", "pin-project-lite", "socket2 0.5.5", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2242,9 +2285,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" @@ -2254,9 +2297,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" [[package]] name = "unicode-ident" @@ -2287,9 +2330,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "2.8.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5ccd538d4a604753ebc2f17cd9946e89b77bf87f6a8e2309667c6f2e87855e3" +checksum = "f8cdd25c339e200129fe4de81451814e5228c9b771d57378817d6117cc2b3f97" dependencies = [ "base64", "flate2", @@ -2306,9 +2349,9 @@ dependencies = [ [[package]] name = "url" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", @@ -2350,9 +2393,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2360,9 +2403,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", "log", @@ -2375,9 +2418,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" dependencies = [ "cfg-if", "js-sys", @@ -2387,9 +2430,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2397,9 +2440,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", @@ -2410,15 +2453,40 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + +[[package]] +name = "wasm-bindgen-test" +version = "0.3.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf9242c0d27999b831eae4767b2a146feb0b27d332d553e605864acd2afd403" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "794645f5408c9a039fd09f4d113cdfb2e7eba5ff1956b07bcf701cf4b394fe89" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] [[package]] name = "web-sys" -version = "0.3.65" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" dependencies = [ "js-sys", "wasm-bindgen", @@ -2426,9 +2494,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.2" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" +checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" [[package]] name = "winapi" @@ -2467,7 +2535,7 @@ version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -2476,7 +2544,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", ] [[package]] @@ -2485,13 +2562,28 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] @@ -2500,47 +2592,89 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + [[package]] name = "winnow" -version = "0.5.19" +version = "0.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" +checksum = "b67b5f0a4e7a27a64c651977932b9dc5667ca7fc31ac44b03ed37a0cf42fdfff" dependencies = [ "memchr", ] @@ -2552,5 +2686,5 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if", - "windows-sys", + "windows-sys 0.48.0", ] diff --git a/Cargo.toml b/Cargo.toml index b7327f1..8ece532 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,12 @@ [workspace] members = [ + "psbt", "cli", "fs", "." ] default-members = [ + "psbt", "cli", "fs", "." @@ -84,3 +86,8 @@ serde = ["serde_crate", "serde_yaml", "bp-std/serde", "bp-wallet/serde",] [package.metadata.docs.rs] features = [ "all" ] + +[patch.crates-io] +bp-invoice = { git = "https://github.com/BP-WG/bp-std", branch = "v0.11" } +rgb-std = { git = "https://github.com/RGB-WG/rgb-std", branch = "v0.11" } +rgb-invoice = { git = "https://github.com/RGB-WG/rgb-std", branch = "v0.11" } diff --git a/cli/Cargo.toml b/cli/Cargo.toml index b0c334f..a2e7e71 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -28,7 +28,6 @@ bp-esplora = { workspace = true } bp-util = { workspace = true } psbt = { workspace = true } rgb-std = { workspace = true, features = ["serde"] } -rgb-invoice = { version = "0.11.0-alpha.2", path = "../invoice" } rgb-runtime = { version = "0.11.0-alpha.2", path = "..", features = ["log", "serde"] } log = { workspace = true } env_logger = "0.10.1" diff --git a/psbt/Cargo.toml b/psbt/Cargo.toml index 921b443..1aeebb8 100644 --- a/psbt/Cargo.toml +++ b/psbt/Cargo.toml @@ -1,8 +1,8 @@ [package] -name = "psbt-" -version = "0.10.10" -description = "RGB smart contract invoicing library" -keywords = ["bitcoin", "invoices", "rgb", "smart-contracts", "lnp-bp"] +name = "psbt-rgb" +version = { workspace = true } +description = "Partially signed bitcoin transaction RGB extensions" +keywords = ["bitcoin", "invoices", "rgb", "smart-contracts", "psbt"] categories = ["cryptography::cryptocurrencies"] authors = { workspace = true } repository = { workspace = true } @@ -13,15 +13,15 @@ rust-version = { workspace = true } readme = "../README.md" [lib] -name = "rgbpsbt" +name = "psbt" crate-type = ["cdylib", "rlib"] # We need this for WASM [dependencies] amplify = { workspace = true } baid58 = { workspace = true } bp-std = { workspace = true } -psbt = "=0.10.0-BP-beta.1" -rgb-std = { version = "0.10.10", path = "../std" } +psbt = { workspace = true } +rgb-std = { workspace = true } [target.'cfg(target_arch = "wasm32")'.dependencies] wasm-bindgen = "0.2" diff --git a/psbt/src/lib.rs b/psbt/src/lib.rs index 7d12d9a..78b51e2 100644 --- a/psbt/src/lib.rs +++ b/psbt/src/lib.rs @@ -1,14 +1,70 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right +// Partially signed bitcoin transaction RGB extensions +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2020-2023 by +// Dr Maxim Orlovsky +// +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::collections::HashMap; + +use bpstd::secp256k1::serde::{Deserialize, Serialize}; +use psbt::Psbt; +use rgbstd::{AnchoredBundle, ContractId, Outpoint, Transition}; + +/// A batch of state transitions under different contracts which are associated +/// with some specific transfer and will be anchored within a single layer 1 +/// transaction. +#[derive(Clone, PartialEq, Eq, Hash, Debug, Default)] +pub struct Batch { + pub transitions: Vec, +} + +/// Structure exported from a PSBT for merging into the stash. It contains a set +/// of finalized state transitions, packed into bundles, and anchored to a +/// single layer 1 transaction. +#[derive(Clone, PartialEq, Eq, Hash, Debug, Default)] +pub struct Fascia { + pub bundles: Vec, } -#[cfg(test)] -mod tests { - use super::*; +pub enum EmbedError {} +pub enum ExtractError {} - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); +pub trait RgbPsbt { + fn rgb_embed(&mut self, batch: Batch) -> Result<(), EmbedError>; + fn rgb_extract(&mut self) -> Result; +} + +impl RgbPsbt for Psbt { + fn rgb_embed(&mut self, batch: Batch) -> Result<(), EmbedError> { + let mut contract_inputs = HashMap::>::new(); + + contract_inputs.entry(id).or_default().push(output); + for (op_id, transition) in batch { + for input in self.inputs_mut() { + let outpoint = input.prevout().outpoint(); + if inputs.contains(&outpoint) { + input.set_rgb_consumer(transition.contract_id, op_id)?; + } + } + self.push_rgb_transition(transition)?; + } + Ok(()) } + + fn rgb_extract(&mut self) -> Result {} } diff --git a/src/pay.rs b/src/pay.rs index dcf423f..e9af350 100644 --- a/src/pay.rs +++ b/src/pay.rs @@ -19,19 +19,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::cmp::Ordering; -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; use std::convert::Infallible; -use std::error::Error; use std::iter; use bp::seals::txout::CloseMethod; -use bp::{Outpoint, Txid}; -use chrono::Utc; +use bp::Vout; use psbt::Psbt; -use rgbinvoice::{Beneficiary, RgbInvoice}; use rgbstd::containers::{Bindle, BuilderSeal, Transfer}; use rgbstd::interface::{BuilderError, ContractSuppl, TypedState, VelocityHint}; +use rgbstd::invoice::{Beneficiary, RgbInvoice}; use rgbstd::persistence::{ConsignerError, Inventory, InventoryError, Stash}; use rgbstd::{ AssignmentType, ContractId, GraphSeal, Operation, Opout, SealDefinition, @@ -78,39 +75,18 @@ pub struct Payment { impl Runtime { pub fn pay(&mut self, invoice: RgbInvoice, method: CloseMethod) -> Result { - // 1. Prepare the data - if let Some(expiry) = invoice.expiry { - if expiry < Utc::now().timestamp() { - return Err(PayError::InvoiceExpired); - } - } - let contract_id = invoice.contract.ok_or(PayError::NoContract)?; - let iface = invoice.iface.ok_or(PayError::NoIface)?; - let mut main_builder = - self.transition_builder(contract_id, iface.clone(), invoice.operation)?; - // 2. Construct PSBT - - let (beneficiary_output, beneficiary) = match invoice.beneficiary { - Beneficiary::BlindedSeal(seal) => { - let seal = BuilderSeal::Concealed(seal); - (None, seal) - } - Beneficiary::WitnessUtxo(addr) => { - let vout = psbt - .outputs() - .position(|out| out.script == addr.script_pubkey()) - .ok_or(PayError::NoBeneficiaryOutput)?; - let seal = BuilderSeal::Revealed(SealDefinition::Bitcoin(GraphSeal::new_vout( - method, vout, - ))); - (Some(vout), seal) - } + let beneficiary_output = match invoice.beneficiary { + Beneficiary::BlindedSeal(seal) => None, + Beneficiary::WitnessUtxo(addr) => psbt + .outputs() + .position(|out| out.script == addr.script_pubkey()) + .ok_or(PayError::NoBeneficiaryOutput)?, }; let prev_outpoints = psbt.inputs().map(|inp| inp.prevout().outpoint()); // Classify PSBT outputs which can be used for assignments - let mut out_classes = HashMap::>::new(); + let mut out_classes = HashMap::>::new(); for (no, outp) in psbt.outputs().enumerate() { if beneficiary_output == Some(no) { continue; @@ -135,14 +111,11 @@ impl Runtime { .into_iter() .map(|(class, indexes)| (class, indexes.into_iter().cycle())) .collect::>(); - let mut output_for_assignment = |suppl: Option<&ContractSuppl>, - assignment_type: AssignmentType| - -> Result, PayError> { - let velocity = suppl - .and_then(|suppl| suppl.owned_state.get(&assignment_type)) - .map(|s| s.velocity) - .unwrap_or_default(); - let vout = out_classes + let allocator = |id: ContractId, + assignment_type: AssignmentType, + velocity: VelocityHint| + -> Option { + out_classes .get_mut(&velocity) .and_then(iter::Cycle::next) .or_else(|| { @@ -150,120 +123,15 @@ impl Runtime { .get_mut(&VelocityHint::default()) .and_then(iter::Cycle::next) }) - .ok_or(PayError::NoBlankOrChange(velocity, assignment_type))?; - let seal = GraphSeal::new_vout(method, vout); - Ok(BuilderSeal::Revealed(SealDefinition::Bitcoin(seal))) - }; - - // 2. Prepare and self-consume transition - let assignment_name = invoice - .assignment - .as_ref() - .or_else(|| main_builder.default_assignment().ok()) - .ok_or(BuilderError::NoDefaultAssignment)?; - let assignment_id = main_builder - .assignments_type(assignment_name) - .ok_or(BuilderError::InvalidStateField(assignment_name.clone()))?; - // TODO: select supplement basing on the signer trust level - let suppl = self - .contract_suppl(contract_id) - .and_then(|set| set.first()) - .cloned(); - let mut sum_inputs = 0u64; - for (opout, state) in self.state_for_outputs(contract_id, prev_outpoints)? { - main_builder = main_builder.add_input(opout)?; - if opout.ty != assignment_id { - let seal = output_for_assignment(suppl.as_ref(), opout.ty)?; - main_builder = main_builder.add_raw_state(opout.ty, seal, state)?; - } else if let TypedState::Amount(value, _) = state { - sum_inputs += value; - } - } - // Add change - let transition = match invoice.owned_state { - TypedState::Amount(amt) => { - match sum_inputs.cmp(&amt) { - Ordering::Greater => { - let seal = output_for_assignment(suppl.as_ref(), assignment_id)?; - let change = TypedState::Amount(sum_inputs - amt); - main_builder = main_builder.add_raw_state(assignment_id, seal, change)?; - } - Ordering::Less => return Err(PayError::InsufficientState), - Ordering::Equal => {} - } - main_builder - .add_raw_state(assignment_id, beneficiary, TypedState::Amount(amt))? - .complete_transition(contract_id)? - } - _ => { - todo!("only TypedState::Amount is currently supported") - } }; - // 3. Prepare other transitions - // Enumerate state - let mut contract_inputs = HashMap::>::new(); - let mut spent_state = HashMap::>::new(); - for outpoint in prev_outpoints { - for id in self.contracts_by_outpoints([outpoint])? { - contract_inputs.entry(id).or_default().push(outpoint); - if id == contract_id { - continue; - } - spent_state - .entry(id) - .or_default() - .extend(self.state_for_outpoints(id, [outpoint])?); - } - } - // Construct blank transitions - let mut other_transitions = HashMap::with_capacity(spent_state.len()); - for (id, opouts) in spent_state { - let mut blank_builder = self.blank_builder(id, iface.clone())?; - // TODO: select supplement basing on the signer trust level - let suppl = self.contract_suppl(id).and_then(|set| set.first()); - - for (opout, state) in opouts { - let seal = output_for_assignment(suppl, opout.ty)?; - blank_builder = blank_builder - .add_input(opout)? - .add_raw_state(opout.ty, seal, state)?; - } - - other_transitions.insert(id, blank_builder.complete_transition(contract_id)?); - } - // 4. Add transitions to PSBT - other_transitions.insert(contract_id, transition); - for (id, transition) in other_transitions { - let inputs = contract_inputs.remove(&id).unwrap_or_default(); - for input in psbt.inputs() { - let outpoint = input.prevout().outpoint(); - if inputs.contains(&outpoint) { - input.set_rgb_consumer(id, transition.id())?; - } - } - psbt.push_rgb_transition(transition)?; - } - // Here we assume the provided PSBT is final: its inputs and outputs will not be - // modified after calling this method. - let bundles = psbt.rgb_bundles()?; - // TODO: Make it two-staged, such that PSBT editing will be allowed by other - // participants as required for multiparty protocols like coinjoin. - psbt.rgb_bundle_to_lnpbp4()?; - let anchor = psbt.dbc_conclude(method)?; - // TODO: Ensure that with PSBTv2 we remove flag allowing PSBT modification. + psbt.rgb_embed(batch)?; - // 4. Prepare transfer + // 5. Prepare transfer let witness_txid = psbt.txid(); - self.consume_anchor(anchor)?; - for (id, bundle) in bundles { - self.consume_bundle(id, bundle, witness_txid.to_byte_array().into())?; - } let beneficiary = match beneficiary { - BuilderSeal::Revealed(seal) => { - BuilderSeal::Revealed(seal.resolve(witness_txid.to_byte_array())) - } + BuilderSeal::Revealed(seal) => BuilderSeal::Revealed(seal.resolve(witness_txid)), BuilderSeal::Concealed(seal) => BuilderSeal::Concealed(seal), }; let transfer = self.stock().transfer(contract_id, [beneficiary])?; From 8522c62cb1f58bf2247abaad3fcfe39335fece1e Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Wed, 13 Dec 2023 20:56:23 +0100 Subject: [PATCH 06/27] wallet: payment workflow draft --- Cargo.lock | 109 ++++++++++++++++++++---------- Cargo.toml | 2 + cli/src/command.rs | 3 +- src/pay.rs | 164 +++++++++++++++++++++++++++------------------ 4 files changed, 176 insertions(+), 102 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 299bcfc..b1ab4d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -354,6 +354,20 @@ dependencies = [ "strict_encoding", ] +[[package]] +name = "bp-derive" +version = "0.11.0-beta.2" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#664fa950efcb5bb653c9af4238c64e0610447200" +dependencies = [ + "amplify", + "bitcoin_hashes", + "bp-consensus", + "bp-invoice", + "commit_verify", + "indexmap 2.1.0", + "serde", +] + [[package]] name = "bp-esplora" version = "0.11.0-beta.1" @@ -378,6 +392,7 @@ dependencies = [ "bech32", "bitcoin_hashes", "bp-consensus", + "serde", ] [[package]] @@ -400,16 +415,14 @@ dependencies = [ [[package]] name = "bp-std" version = "0.11.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6674cfa814dee185eb49d791065448ae8a8dcf0ad20f23cdec49ba7341bce9ab" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#664fa950efcb5bb653c9af4238c64e0610447200" dependencies = [ "amplify", - "bech32", - "bitcoin_hashes", "bp-consensus", - "bp-core", - "commit_verify", - "indexmap 2.1.0", + "bp-derive", + "bp-invoice", + "descriptors 0.11.0-beta.2 (git+https://github.com/BP-WG/bp-std?branch=v0.11)", + "psbt 0.11.0-beta.2 (git+https://github.com/BP-WG/bp-std?branch=v0.11)", "serde", ] @@ -425,10 +438,10 @@ dependencies = [ "bp-std", "bp-wallet", "clap", - "descriptors", + "descriptors 0.11.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger", "log", - "psbt", + "psbt 0.11.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde", "serde_yaml", "shellexpand", @@ -439,14 +452,13 @@ dependencies = [ [[package]] name = "bp-wallet" version = "0.11.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9dbf251e691b0d45420e86116deafc4fefc7833c571c32e7fd318e2f4ea3f16" +source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#45e81f3bf646681a041423d4cab9c739dbc15dde" dependencies = [ "amplify", "bp-esplora", "bp-std", - "descriptors", - "psbt", + "descriptors 0.11.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "psbt 0.11.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde", "serde_yaml", "toml", @@ -531,7 +543,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -662,7 +674,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -673,7 +685,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -698,6 +710,17 @@ dependencies = [ "serde", ] +[[package]] +name = "descriptors" +version = "0.11.0-beta.2" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#664fa950efcb5bb653c9af4238c64e0610447200" +dependencies = [ + "amplify", + "bp-derive", + "indexmap 2.1.0", + "serde", +] + [[package]] name = "digest" version = "0.10.7" @@ -1138,9 +1161,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.150" +version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] name = "libredox" @@ -1272,7 +1295,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -1360,7 +1383,21 @@ dependencies = [ "base64", "bp-std", "chrono", - "descriptors", + "descriptors 0.11.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 2.1.0", + "serde", +] + +[[package]] +name = "psbt" +version = "0.11.0-beta.2" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#664fa950efcb5bb653c9af4238c64e0610447200" +dependencies = [ + "amplify", + "base64", + "bp-derive", + "chrono", + "descriptors 0.11.0-beta.2 (git+https://github.com/BP-WG/bp-std?branch=v0.11)", "indexmap 2.1.0", "serde", ] @@ -1373,7 +1410,7 @@ dependencies = [ "baid58", "bp-std", "getrandom", - "psbt", + "psbt 0.11.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand", "rgb-std", "wasm-bindgen", @@ -1531,7 +1568,7 @@ dependencies = [ [[package]] name = "rgb-invoice" version = "0.11.0-beta.2" -source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#a5baa8964039abb3ef639f5ba449fd3f5de043cf" +source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#eec60571a25c7347d0ffa2a524d14ce61d20adb3" dependencies = [ "amplify", "baid58", @@ -1566,10 +1603,10 @@ dependencies = [ "bp-std", "bp-wallet", "chrono", - "descriptors", + "descriptors 0.11.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 2.1.0", "log", - "psbt", + "psbt 0.11.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", "rgb-persist-fs", "rgb-std", "serde", @@ -1580,7 +1617,7 @@ dependencies = [ [[package]] name = "rgb-std" version = "0.11.0-beta.2" -source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#a5baa8964039abb3ef639f5ba449fd3f5de043cf" +source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#eec60571a25c7347d0ffa2a524d14ce61d20adb3" dependencies = [ "amplify", "baid58", @@ -1613,7 +1650,7 @@ dependencies = [ "commit_verify", "env_logger", "log", - "psbt", + "psbt 0.11.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", "rgb-runtime", "rgb-std", "serde", @@ -1822,7 +1859,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -1893,7 +1930,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -2057,9 +2094,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.39" +version = "2.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" dependencies = [ "proc-macro2", "quote", @@ -2126,7 +2163,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -2412,7 +2449,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", "wasm-bindgen-shared", ] @@ -2446,7 +2483,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2479,7 +2516,7 @@ checksum = "794645f5408c9a039fd09f4d113cdfb2e7eba5ff1956b07bcf701cf4b394fe89" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -2672,9 +2709,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.26" +version = "0.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67b5f0a4e7a27a64c651977932b9dc5667ca7fc31ac44b03ed37a0cf42fdfff" +checksum = "6c830786f7720c2fd27a1a0e27a709dbd3c4d009b56d098fc742d4f4eab91fe2" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 8ece532..d099b67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -89,5 +89,7 @@ features = [ "all" ] [patch.crates-io] bp-invoice = { git = "https://github.com/BP-WG/bp-std", branch = "v0.11" } +bp-std = { git = "https://github.com/BP-WG/bp-std", branch = "v0.11" } +bp-wallet = { git = "https://github.com/BP-WG/bp-wallet", branch = "v0.11" } rgb-std = { git = "https://github.com/RGB-WG/rgb-std", branch = "v0.11" } rgb-invoice = { git = "https://github.com/RGB-WG/rgb-std", branch = "v0.11" } diff --git a/cli/src/command.rs b/cli/src/command.rs index a8289fe..e55a873 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -27,12 +27,11 @@ use amplify::confinement::U16; use bp_util::{Config, Exec}; use bpstd::{Sats, Txid}; use bpwallet::{Invoice, TxParams}; -use psbt::Psbt; use rgb_rt::{DescriptorRgb, RgbDescr, RgbKeychain, RuntimeError}; -use rgbinvoice::{Beneficiary, InvoiceState, RgbInvoice, RgbTransport}; use rgbstd::containers::{Bindle, Transfer, UniversalBindle}; use rgbstd::contract::{ContractId, GenesisSeal, GraphSeal, StateType}; use rgbstd::interface::{ContractBuilder, FilterExclude, IfaceId, SchemaIfaces}; +use rgbstd::invoice::{Beneficiary, InvoiceState, RgbInvoice, RgbTransport}; use rgbstd::persistence::{Inventory, Stash}; use rgbstd::schema::SchemaId; use rgbstd::SealDefinition; diff --git a/src/pay.rs b/src/pay.rs index e9af350..1eb97fc 100644 --- a/src/pay.rs +++ b/src/pay.rs @@ -24,11 +24,12 @@ use std::convert::Infallible; use std::iter; use bp::seals::txout::CloseMethod; -use bp::Vout; +use bp::{Sats, Vout}; +use bpwallet::{Invoice, PsbtMeta, TxParams}; use psbt::Psbt; use rgbstd::containers::{Bindle, BuilderSeal, Transfer}; -use rgbstd::interface::{BuilderError, ContractSuppl, TypedState, VelocityHint}; -use rgbstd::invoice::{Beneficiary, RgbInvoice}; +use rgbstd::interface::{BuilderError, ContractSuppl, FilterIncludeAll, TypedState, VelocityHint}; +use rgbstd::invoice::{Beneficiary, InvoiceState, RgbInvoice}; use rgbstd::persistence::{ConsignerError, Inventory, InventoryError, Stash}; use rgbstd::{ AssignmentType, ContractId, GraphSeal, Operation, Opout, SealDefinition, @@ -38,99 +39,134 @@ use rgbstd::{ use crate::Runtime; #[derive(Debug, Display, Error, From)] -#[display(inner)] +#[display(doc_comments)] pub enum PayError { - /// unspecified contract - #[display(doc_comments)] + /// unspecified contract. NoContract, - /// unspecified interface - #[display(doc_comments)] + /// unspecified interface. NoIface, + /// invoice doesn't provide information about the operation, and the used + /// interface do not define default operation. + NoOperation, + + /// invoice doesn't provide information about the assignment type, and the + /// used interface do not define default assignment type. + NoAssignment, + /// state provided via PSBT inputs is not sufficient to cover invoice state /// requirements. - #[display(doc_comments)] InsufficientState, - /// the invoice has expired - #[display(doc_comments)] + /// the invoice has expired. InvoiceExpired, + /// non-fungible state is not yet supported by the invoices. + Unsupported, + #[from] + #[display(inner)] Inventory(InventoryError), #[from] + #[display(inner)] Builder(BuilderError), #[from] + #[display(inner)] Consigner(ConsignerError), } #[derive(Clone, Eq, PartialEq, Hash, Debug)] -pub struct Payment { - pub unsigned_psbt: Psbt, - pub transfer: Bindle, +pub struct TransferParams { + pub tx: TxParams, + pub min_amount: Sats, } impl Runtime { - pub fn pay(&mut self, invoice: RgbInvoice, method: CloseMethod) -> Result { - // 2. Construct PSBT - let beneficiary_output = match invoice.beneficiary { - Beneficiary::BlindedSeal(seal) => None, - Beneficiary::WitnessUtxo(addr) => psbt - .outputs() - .position(|out| out.script == addr.script_pubkey()) - .ok_or(PayError::NoBeneficiaryOutput)?, - }; - let prev_outpoints = psbt.inputs().map(|inp| inp.prevout().outpoint()); + pub fn pay( + &self, + invoice: &RgbInvoice, + method: CloseMethod, + params: TransferParams, + ) -> Result<(Psbt, PsbtMeta, Bindle), PayError> { + let (mut psbt, meta) = self.construct_psbt(invoice, method, params)?; + // ... here we pass PSBT around signers, if necessary + let transfer = self.transfer(invoice, &mut psbt)?; + Ok((psbt, meta, transfer)) + } - // Classify PSBT outputs which can be used for assignments - let mut out_classes = HashMap::>::new(); - for (no, outp) in psbt.outputs().enumerate() { - if beneficiary_output == Some(no) { - continue; + pub fn construct_psbt( + &self, + invoice: &RgbInvoice, + method: CloseMethod, + params: TransferParams, + ) -> Result<(Psbt, PsbtMeta), PayError> { + let contract_id = invoice.contract.ok_or(PayError::NoContract)?; + + let iface_name = invoice.iface.ok_or(PayError::NoIface)?; + let iface = self.stock().iface_by_name(&iface_name)?; + let contract = self.contract_iface_named(contract_id, iface_name)?; + let operation = invoice + .operation + .or_else(|| iface.default_operation) + .ok_or(PayError::NoOperation)?; + let assignment_name = invoice + .assignment + .or_else(|| { + iface + .transitions + .get(&operation) + .and_then(|t| t.default_assignment) + }) + .ok_or(PayError::NoAssignment)?; + let outputs = match invoice.owned_state { + InvoiceState::Amount(amount) => { + let mut state = contract.fungible(assignment_name, &FilterIncludeAll)?; + state.sort_by_key(|a| a.value); + let mut sum = 0u64; + state + .iter() + .rev() + .take_while(|a| { + if sum >= amount { + false + } else { + sum += amount; + true + } + }) + .map(|a| a.owner) + .collect::>() } - if outp - // NB: Here we assume that if output has derivation information it belongs to our wallet. - .bip32_derivation - .first() - .map(|(_, src)| src) - .or_else(|| outp.tap_bip32_derivation.first().map(|(_, d)| &d.origin)) - .and_then(|orig| orig.derivation().iter().rev().nth(1)) - .copied() - .map(u32::from) - .filter(|index| *index == RGB_NATIVE_DERIVATION_INDEX || *index == RGB_TAPRET_DERIVATION_INDEX) - .is_some() - { - let class = outp.rgb_velocity_hint().unwrap_or_default(); - out_classes.entry(class).or_default().push(no); - } - } - let mut out_classes = out_classes - .into_iter() - .map(|(class, indexes)| (class, indexes.into_iter().cycle())) - .collect::>(); - let allocator = |id: ContractId, - assignment_type: AssignmentType, - velocity: VelocityHint| - -> Option { - out_classes - .get_mut(&velocity) - .and_then(iter::Cycle::next) - .or_else(|| { - out_classes - .get_mut(&VelocityHint::default()) - .and_then(iter::Cycle::next) - }) + _ => return Err(PayError::Unsupported), + }; + let inv = match invoice.beneficiary { + Beneficiary::BlindedSeal(_) => Invoice::with_max(self.wallet().next_address()), + Beneficiary::WitnessVoutBitcoin(addr) => Invoice::new(addr, params.min_amount), }; + let (mut psbt, meta) = self.wallet().construct_psbt(&outputs, inv, params.tx)?; - // 4. Add transitions to PSBT + let batch = + self.compose(&invoice, outputs, method, meta.change_vout, |_, _, _| meta.change_vout)?; psbt.rgb_embed(batch)?; + Ok((psbt, meta)) + } + + pub fn transfer( + &self, + invoice: &RgbInvoice, + psbt: &mut Psbt, + ) -> Result, PayError> { + let contract_id = invoice.contract.ok_or(PayError::NoContract)?; + + psbt.dbc_finalize()?; + let fascia = psbt.rgb_extract()?; - // 5. Prepare transfer let witness_txid = psbt.txid(); - let beneficiary = match beneficiary { + self.stock().consume(fascia)?; + let beneficiary = match invoice.beneficiary { BuilderSeal::Revealed(seal) => BuilderSeal::Revealed(seal.resolve(witness_txid)), BuilderSeal::Concealed(seal) => BuilderSeal::Concealed(seal), }; From db31a8ed3e579818f14a9ca6abc1f788cb995c44 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 15 Dec 2023 21:03:18 +0100 Subject: [PATCH 07/27] descriptor: support for SpkClass --- Cargo.lock | 78 +++++++++++++++-------------------------------- Cargo.toml | 7 +++++ src/descriptor.rs | 10 +++++- 3 files changed, 40 insertions(+), 55 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b1ab4d4..a9d2d2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -310,8 +310,7 @@ dependencies = [ [[package]] name = "bp-consensus" version = "0.11.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b50dbcec1e75163cdd1ba1cc265f1bb67ae4319b00469c36b3972a2eb49b879" +source = "git+https://github.com/BP-WG/bp-core?branch=v0.11#31e9b11b96a8678afa3d39a774bfa686cb9c0def" dependencies = [ "amplify", "chrono", @@ -357,7 +356,7 @@ dependencies = [ [[package]] name = "bp-derive" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#664fa950efcb5bb653c9af4238c64e0610447200" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#04dc07f0c93359d5ceef26efc28365a7a507866f" dependencies = [ "amplify", "bitcoin_hashes", @@ -371,8 +370,7 @@ dependencies = [ [[package]] name = "bp-esplora" version = "0.11.0-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ae41858fe075fed93276e1399be6db314775adbf4470f7f9b056d78d37ebc56" +source = "git+https://github.com/BP-WG/bp-esplora-client?branch=master#a963c4bb6a86601e3a767f1a48eae58c041ba026" dependencies = [ "amplify", "bp-std", @@ -380,13 +378,14 @@ dependencies = [ "reqwest", "serde", "serde_with", + "sha2", "ureq", ] [[package]] name = "bp-invoice" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#664fa950efcb5bb653c9af4238c64e0610447200" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#04dc07f0c93359d5ceef26efc28365a7a507866f" dependencies = [ "amplify", "bech32", @@ -415,22 +414,21 @@ dependencies = [ [[package]] name = "bp-std" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#664fa950efcb5bb653c9af4238c64e0610447200" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#04dc07f0c93359d5ceef26efc28365a7a507866f" dependencies = [ "amplify", "bp-consensus", "bp-derive", "bp-invoice", - "descriptors 0.11.0-beta.2 (git+https://github.com/BP-WG/bp-std?branch=v0.11)", - "psbt 0.11.0-beta.2 (git+https://github.com/BP-WG/bp-std?branch=v0.11)", + "descriptors", + "psbt", "serde", ] [[package]] name = "bp-util" version = "0.11.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abdb986e5bf767576c8533563d7d4daa0667e3dc4acc12ddd7354ada5473b436" +source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#7a1ade93df870e769ce8bbf80014ba2195480e7c" dependencies = [ "amplify", "base64", @@ -438,10 +436,10 @@ dependencies = [ "bp-std", "bp-wallet", "clap", - "descriptors 0.11.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "descriptors", "env_logger", "log", - "psbt 0.11.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "psbt", "serde", "serde_yaml", "shellexpand", @@ -452,13 +450,13 @@ dependencies = [ [[package]] name = "bp-wallet" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#45e81f3bf646681a041423d4cab9c739dbc15dde" +source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#7a1ade93df870e769ce8bbf80014ba2195480e7c" dependencies = [ "amplify", "bp-esplora", "bp-std", - "descriptors 0.11.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", - "psbt 0.11.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "descriptors", + "psbt", "serde", "serde_yaml", "toml", @@ -701,19 +699,7 @@ dependencies = [ [[package]] name = "descriptors" version = "0.11.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b5433142e6b89ff9603f654e6725b9e97a1ce5f3783bb11dbedc93b3839296" -dependencies = [ - "amplify", - "bp-std", - "indexmap 2.1.0", - "serde", -] - -[[package]] -name = "descriptors" -version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#664fa950efcb5bb653c9af4238c64e0610447200" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#04dc07f0c93359d5ceef26efc28365a7a507866f" dependencies = [ "amplify", "bp-derive", @@ -1376,28 +1362,13 @@ dependencies = [ [[package]] name = "psbt" version = "0.11.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa2b08f79bc8546b6ad7d2b7e0da71ce84e7e690b33703fa4e5e3b1806012868" -dependencies = [ - "amplify", - "base64", - "bp-std", - "chrono", - "descriptors 0.11.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", - "indexmap 2.1.0", - "serde", -] - -[[package]] -name = "psbt" -version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#664fa950efcb5bb653c9af4238c64e0610447200" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#04dc07f0c93359d5ceef26efc28365a7a507866f" dependencies = [ "amplify", "base64", "bp-derive", "chrono", - "descriptors 0.11.0-beta.2 (git+https://github.com/BP-WG/bp-std?branch=v0.11)", + "descriptors", "indexmap 2.1.0", "serde", ] @@ -1410,7 +1381,7 @@ dependencies = [ "baid58", "bp-std", "getrandom", - "psbt 0.11.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "psbt", "rand", "rgb-std", "wasm-bindgen", @@ -1547,8 +1518,7 @@ dependencies = [ [[package]] name = "rgb-core" version = "0.11.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8572f1484191ad41598bc3c9b2a5aa3ed0a323ce1a56fc3916c8c4f7fa81d8eb" +source = "git+https://github.com/RGB-WG/rgb-core?branch=v0.11#1fc21032159d2b04443b823f6e6bfb3340ef0622" dependencies = [ "aluvm", "amplify", @@ -1568,7 +1538,7 @@ dependencies = [ [[package]] name = "rgb-invoice" version = "0.11.0-beta.2" -source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#eec60571a25c7347d0ffa2a524d14ce61d20adb3" +source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#49bd860a9cf61400debf059fecdb245b869526c4" dependencies = [ "amplify", "baid58", @@ -1603,10 +1573,10 @@ dependencies = [ "bp-std", "bp-wallet", "chrono", - "descriptors 0.11.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "descriptors", "indexmap 2.1.0", "log", - "psbt 0.11.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "psbt", "rgb-persist-fs", "rgb-std", "serde", @@ -1617,7 +1587,7 @@ dependencies = [ [[package]] name = "rgb-std" version = "0.11.0-beta.2" -source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#eec60571a25c7347d0ffa2a524d14ce61d20adb3" +source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#49bd860a9cf61400debf059fecdb245b869526c4" dependencies = [ "amplify", "baid58", @@ -1650,7 +1620,7 @@ dependencies = [ "commit_verify", "env_logger", "log", - "psbt 0.11.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "psbt", "rgb-runtime", "rgb-std", "serde", diff --git a/Cargo.toml b/Cargo.toml index d099b67..99cb63e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,8 +88,15 @@ serde = ["serde_crate", "serde_yaml", "bp-std/serde", "bp-wallet/serde",] features = [ "all" ] [patch.crates-io] +bp-consensus = { git = "https://github.com/BP-WG/bp-core", branch = "v0.11" } bp-invoice = { git = "https://github.com/BP-WG/bp-std", branch = "v0.11" } bp-std = { git = "https://github.com/BP-WG/bp-std", branch = "v0.11" } +descriptors = { git = "https://github.com/BP-WG/bp-std", branch = "v0.11" } +psbt = { git = "https://github.com/BP-WG/bp-std", branch = "v0.11" } +bp-derive = { git = "https://github.com/BP-WG/bp-std", branch = "v0.11" } bp-wallet = { git = "https://github.com/BP-WG/bp-wallet", branch = "v0.11" } +bp-util = { git = "https://github.com/BP-WG/bp-wallet", branch = "v0.11" } +bp-esplora = { git = "https://github.com/BP-WG/bp-esplora-client", branch = "master" } +rgb-core = { git = "https://github.com/RGB-WG/rgb-core", branch = "v0.11" } rgb-std = { git = "https://github.com/RGB-WG/rgb-std", branch = "v0.11" } rgb-invoice = { git = "https://github.com/RGB-WG/rgb-std", branch = "v0.11" } diff --git a/src/descriptor.rs b/src/descriptor.rs index 4eb5157..19cec01 100644 --- a/src/descriptor.rs +++ b/src/descriptor.rs @@ -31,7 +31,7 @@ use bpstd::{ IndexError, IndexParseError, KeyOrigin, Keychain, NormalIndex, TapDerivation, Terminal, XOnlyPk, XpubDerivable, XpubSpec, }; -use descriptors::{Descriptor, StdDescr, TrKey}; +use descriptors::{Descriptor, SpkClass, StdDescr, TrKey}; use indexmap::IndexMap; pub trait DescriptorRgb: Descriptor { @@ -148,6 +148,8 @@ impl Descriptor for TapretKey { type VarIter<'v> = iter::Empty<&'v ()> where Self: 'v, (): 'v; type XpubIter<'x> = iter::Once<&'x XpubSpec> where Self: 'x; + fn class(&self) -> SpkClass { SpkClass::P2tr } + fn keys(&self) -> Self::KeyIter<'_> { iter::once(&self.internal_key) } fn vars(&self) -> Self::VarIter<'_> { iter::empty() } fn xpubs(&self) -> Self::XpubIter<'_> { iter::once(self.internal_key.xpub_spec()) } @@ -216,6 +218,12 @@ where Self: Derive type VarIter<'v> = iter::Empty<&'v ()> where Self: 'v, (): 'v; type XpubIter<'x> = vec::IntoIter<&'x XpubSpec> where Self: 'x; + fn class(&self) -> SpkClass { + match self { + RgbDescr::TapretKey(d) => d.class(), + } + } + fn keys(&self) -> Self::KeyIter<'_> { match self { RgbDescr::TapretKey(d) => d.keys().collect::>(), From e03adc61588a4472fa41c77dddb4ee76ff2c6915 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sun, 17 Dec 2023 17:26:05 +0100 Subject: [PATCH 08/27] psbt: implement everything --- Cargo.lock | 63 ++++----- Cargo.toml | 12 +- psbt/Cargo.toml | 3 + psbt/src/lib.rs | 103 +++++++++++---- psbt/src/rgb.rs | 332 ++++++++++++++++++++++++++++++------------------ src/pay.rs | 46 ++++--- src/runtime.rs | 8 +- 7 files changed, 359 insertions(+), 208 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a9d2d2f..f0be30a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,8 +29,7 @@ dependencies = [ [[package]] name = "aluvm" version = "0.11.0-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134a93e142c6aabca39c69c501c7d34ec99b04a5cb25738e1c0d1382f14b02a1" +source = "git+https://github.com/AluVM/rust-aluvm?branch=v0.11#34894fe14cbbd3312122a7854072056756798a03" dependencies = [ "amplify", "baid58", @@ -89,11 +88,12 @@ dependencies = [ [[package]] name = "amplify_num" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddce3bc63e807ea02065e8d8b702695f3d302ae4158baddff8b0ce5c73947251" +checksum = "9681187211554ab98f138ba159e90861b136c20afc680dcff2ba82d020721e27" dependencies = [ "serde", + "wasm-bindgen", ] [[package]] @@ -310,7 +310,7 @@ dependencies = [ [[package]] name = "bp-consensus" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-core?branch=v0.11#31e9b11b96a8678afa3d39a774bfa686cb9c0def" +source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#d3a57c213f34d9012101b9969cdc8b0d60a6fe60" dependencies = [ "amplify", "chrono", @@ -324,8 +324,7 @@ dependencies = [ [[package]] name = "bp-core" version = "0.11.0-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece2f2e6f91bdbf5684f26a105c078e50394f98e37dc173c20b23eba53e7be09" +source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#d3a57c213f34d9012101b9969cdc8b0d60a6fe60" dependencies = [ "amplify", "bp-consensus", @@ -341,8 +340,7 @@ dependencies = [ [[package]] name = "bp-dbc" version = "0.11.0-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0d9d7bbc4dc2debd9c30469752963410b44adee55af807378589e88c5fd913a" +source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#d3a57c213f34d9012101b9969cdc8b0d60a6fe60" dependencies = [ "amplify", "base85", @@ -356,7 +354,7 @@ dependencies = [ [[package]] name = "bp-derive" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#04dc07f0c93359d5ceef26efc28365a7a507866f" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#64dc394e6b4d33d4f12a9b1eccd6429d6aa5bbe4" dependencies = [ "amplify", "bitcoin_hashes", @@ -385,7 +383,7 @@ dependencies = [ [[package]] name = "bp-invoice" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#04dc07f0c93359d5ceef26efc28365a7a507866f" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#64dc394e6b4d33d4f12a9b1eccd6429d6aa5bbe4" dependencies = [ "amplify", "bech32", @@ -397,8 +395,7 @@ dependencies = [ [[package]] name = "bp-seals" version = "0.11.0-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3661c2c7bddda0b6fd37dafa1a56c863ba0ff9787ebc06d05eb8ac676f1d469e" +source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#d3a57c213f34d9012101b9969cdc8b0d60a6fe60" dependencies = [ "amplify", "baid58", @@ -414,7 +411,7 @@ dependencies = [ [[package]] name = "bp-std" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#04dc07f0c93359d5ceef26efc28365a7a507866f" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#64dc394e6b4d33d4f12a9b1eccd6429d6aa5bbe4" dependencies = [ "amplify", "bp-consensus", @@ -428,7 +425,7 @@ dependencies = [ [[package]] name = "bp-util" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#7a1ade93df870e769ce8bbf80014ba2195480e7c" +source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#07dc6312f8aa80f4b8299d720fcdbd6858f08f82" dependencies = [ "amplify", "base64", @@ -450,7 +447,7 @@ dependencies = [ [[package]] name = "bp-wallet" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#7a1ade93df870e769ce8bbf80014ba2195480e7c" +source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#07dc6312f8aa80f4b8299d720fcdbd6858f08f82" dependencies = [ "amplify", "bp-esplora", @@ -559,8 +556,7 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "commit_encoding_derive" version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00033f14d67c4169d588f085ea2faeb7b610cf03a74d42ea09eeba31abef2047" +source = "git+https://github.com/LNP-BP/client_side_validation?branch=v0.11#6b634bccd96d2cb857a44ae48417f8e35ecb3de7" dependencies = [ "amplify", "amplify_syn", @@ -572,8 +568,7 @@ dependencies = [ [[package]] name = "commit_verify" version = "0.11.0-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82c1a7d13181451d927abcc477d4565f23eb06ab36820599cc75ed3161147daa" +source = "git+https://github.com/LNP-BP/client_side_validation?branch=v0.11#6b634bccd96d2cb857a44ae48417f8e35ecb3de7" dependencies = [ "amplify", "commit_encoding_derive", @@ -699,7 +694,7 @@ dependencies = [ [[package]] name = "descriptors" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#04dc07f0c93359d5ceef26efc28365a7a507866f" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#64dc394e6b4d33d4f12a9b1eccd6429d6aa5bbe4" dependencies = [ "amplify", "bp-derive", @@ -1362,15 +1357,18 @@ dependencies = [ [[package]] name = "psbt" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#04dc07f0c93359d5ceef26efc28365a7a507866f" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#64dc394e6b4d33d4f12a9b1eccd6429d6aa5bbe4" dependencies = [ "amplify", "base64", + "bp-core", "bp-derive", "chrono", + "commit_verify", "descriptors", "indexmap 2.1.0", "serde", + "strict_encoding", ] [[package]] @@ -1379,11 +1377,14 @@ version = "0.11.0-alpha.2" dependencies = [ "amplify", "baid58", + "bp-core", "bp-std", + "commit_verify", "getrandom", "psbt", "rand", "rgb-std", + "strict_encoding", "wasm-bindgen", "wasm-bindgen-test", ] @@ -1518,7 +1519,7 @@ dependencies = [ [[package]] name = "rgb-core" version = "0.11.0-beta.2" -source = "git+https://github.com/RGB-WG/rgb-core?branch=v0.11#1fc21032159d2b04443b823f6e6bfb3340ef0622" +source = "git+https://github.com/RGB-WG/rgb-core?branch=canary#f12b208ccec4a11de0de1ad0b0cd4048d5fd0e11" dependencies = [ "aluvm", "amplify", @@ -1538,7 +1539,7 @@ dependencies = [ [[package]] name = "rgb-invoice" version = "0.11.0-beta.2" -source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#49bd860a9cf61400debf059fecdb245b869526c4" +source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#b65cf9ec35a4748862dd02b59ca72fb6b7370692" dependencies = [ "amplify", "baid58", @@ -1587,7 +1588,7 @@ dependencies = [ [[package]] name = "rgb-std" version = "0.11.0-beta.2" -source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#49bd860a9cf61400debf059fecdb245b869526c4" +source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#b65cf9ec35a4748862dd02b59ca72fb6b7370692" dependencies = [ "amplify", "baid58", @@ -2118,18 +2119,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" dependencies = [ "proc-macro2", "quote", @@ -2325,9 +2326,9 @@ dependencies = [ [[package]] name = "unsafe-libyaml" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" +checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" [[package]] name = "untrusted" diff --git a/Cargo.toml b/Cargo.toml index 99cb63e..768213e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ license = "Apache-2.0" [workspace.dependencies] amplify = "4.5.0" baid58 = "0.4.4" +commit_verify = "0.11.0-beta.1" strict_encoding = "2.6.1" strict_types = "1.6.3" bp-core = "0.11.0-beta.1" @@ -35,7 +36,7 @@ bp-wallet = "0.11.0-beta.2" bp-util = "0.11.0-beta.2" bp-esplora = "0.11.0-beta.1" descriptors = "0.11.0-beta.1" -psbt = "0.11.0-beta.1" +psbt = { version = "0.11.0-beta.1", features = ["client-side-validation"] } rgb-std = { version = "0.11.0-beta.2", features = ["fs"] } indexmap = "2.0.2" chrono = "0.4.31" @@ -88,7 +89,11 @@ serde = ["serde_crate", "serde_yaml", "bp-std/serde", "bp-wallet/serde",] features = [ "all" ] [patch.crates-io] -bp-consensus = { git = "https://github.com/BP-WG/bp-core", branch = "v0.11" } +commit_verify = { git = "https://github.com/LNP-BP/client_side_validation", branch = "v0.11" } +bp-consensus = { git = "https://github.com/BP-WG/bp-core", branch = "doubleanchors" } +bp-dbc = { git = "https://github.com/BP-WG/bp-core", branch = "doubleanchors" } +bp-seals = { git = "https://github.com/BP-WG/bp-core", branch = "doubleanchors" } +bp-core = { git = "https://github.com/BP-WG/bp-core", branch = "doubleanchors" } bp-invoice = { git = "https://github.com/BP-WG/bp-std", branch = "v0.11" } bp-std = { git = "https://github.com/BP-WG/bp-std", branch = "v0.11" } descriptors = { git = "https://github.com/BP-WG/bp-std", branch = "v0.11" } @@ -97,6 +102,7 @@ bp-derive = { git = "https://github.com/BP-WG/bp-std", branch = "v0.11" } bp-wallet = { git = "https://github.com/BP-WG/bp-wallet", branch = "v0.11" } bp-util = { git = "https://github.com/BP-WG/bp-wallet", branch = "v0.11" } bp-esplora = { git = "https://github.com/BP-WG/bp-esplora-client", branch = "master" } -rgb-core = { git = "https://github.com/RGB-WG/rgb-core", branch = "v0.11" } +aluvm = { git = "https://github.com/AluVM/rust-aluvm", branch = "v0.11" } +rgb-core = { git = "https://github.com/RGB-WG/rgb-core", branch = "canary" } rgb-std = { git = "https://github.com/RGB-WG/rgb-std", branch = "v0.11" } rgb-invoice = { git = "https://github.com/RGB-WG/rgb-std", branch = "v0.11" } diff --git a/psbt/Cargo.toml b/psbt/Cargo.toml index 1aeebb8..97e6ffe 100644 --- a/psbt/Cargo.toml +++ b/psbt/Cargo.toml @@ -19,6 +19,9 @@ crate-type = ["cdylib", "rlib"] # We need this for WASM [dependencies] amplify = { workspace = true } baid58 = { workspace = true } +commit_verify = { workspace = true } +strict_encoding = { workspace = true } +bp-core = { workspace = true } bp-std = { workspace = true } psbt = { workspace = true } rgb-std = { workspace = true } diff --git a/psbt/src/lib.rs b/psbt/src/lib.rs index 78b51e2..3dd8750 100644 --- a/psbt/src/lib.rs +++ b/psbt/src/lib.rs @@ -19,52 +19,103 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::HashMap; +#[macro_use] +extern crate amplify; -use bpstd::secp256k1::serde::{Deserialize, Serialize}; -use psbt::Psbt; -use rgbstd::{AnchoredBundle, ContractId, Outpoint, Transition}; +mod rgb; -/// A batch of state transitions under different contracts which are associated -/// with some specific transfer and will be anchored within a single layer 1 -/// transaction. -#[derive(Clone, PartialEq, Eq, Hash, Debug, Default)] -pub struct Batch { - pub transitions: Vec, +use bp::dbc::opret::OpretProof; +use bp::dbc::tapret::TapretProof; +use psbt::{DbcPsbtError, Psbt}; +use rgbstd::containers::{Batch, Fascia, XchainOutpoint}; +use rgbstd::{AnchorSet, XAnchor}; + +pub use self::rgb::{ + ProprietaryKeyRgb, RgbExt, RgbInExt, RgbOutExt, RgbPsbtError, PSBT_GLOBAL_RGB_TRANSITION, + PSBT_IN_RGB_CONSUMED_BY, PSBT_OUT_RGB_VELOCITY_HINT, PSBT_RGB_PREFIX, +}; + +#[derive(Clone, Eq, PartialEq, Debug, Display, Error)] +#[display(doc_comments)] +pub enum EmbedError { + /// provided transaction batch references inputs which are absent from the + /// PSBT. Possible it was created for a different PSBT. + AbsentInputs, + + /// the provided PSBT is invalid since it doublespends on some of its + /// inputs. + PsbtRepeatedInputs, } -/// Structure exported from a PSBT for merging into the stash. It contains a set -/// of finalized state transitions, packed into bundles, and anchored to a -/// single layer 1 transaction. -#[derive(Clone, PartialEq, Eq, Hash, Debug, Default)] -pub struct Fascia { - pub bundles: Vec, +#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)] +#[display(inner)] +pub enum CommitError { + #[from] + Rgb(RgbPsbtError), + + #[from] + Dbc(DbcPsbtError), } -pub enum EmbedError {} +#[derive(Clone, Eq, PartialEq, Debug, Display, Error)] +#[display(doc_comments)] pub enum ExtractError {} +// TODO: Batch must be homomorphic by the outpoint type (chain) + pub trait RgbPsbt { fn rgb_embed(&mut self, batch: Batch) -> Result<(), EmbedError>; - fn rgb_extract(&mut self) -> Result; + fn rgb_commit(&mut self) -> Result; + fn rgb_extract(&self) -> Result; } impl RgbPsbt for Psbt { fn rgb_embed(&mut self, batch: Batch) -> Result<(), EmbedError> { - let mut contract_inputs = HashMap::>::new(); - - contract_inputs.entry(id).or_default().push(output); - for (op_id, transition) in batch { + for info in batch { + let contract_id = info.transition.contract_id; + let mut inputs = info.inputs.into_inner(); for input in self.inputs_mut() { let outpoint = input.prevout().outpoint(); - if inputs.contains(&outpoint) { - input.set_rgb_consumer(transition.contract_id, op_id)?; + if let Some(pos) = inputs + .iter() + .position(|i| i == &XchainOutpoint::Bitcoin(outpoint)) + { + inputs.remove(pos); + input + .set_rgb_consumer(contract_id, info.id) + .map_err(|_| EmbedError::PsbtRepeatedInputs)?; } } - self.push_rgb_transition(transition)?; + if !inputs.is_empty() { + return Err(EmbedError::AbsentInputs); + } + self.push_rgb_transition(info.transition, info.methods) + .expect("transitions are unique since they are in BTreeMap indexed by opid"); } Ok(()) } - fn rgb_extract(&mut self) -> Result {} + fn rgb_commit(&mut self) -> Result { + // Convert RGB data to MPCs? Or should we do it at the moment we add them... No, + // since we may require more DBC methods with each additional state transition + let (bundles, methods) = self.rgb_bundles_to_mpc()?; + // DBC commitment for the required methods + let (mut tapret_anchor, mut opret_anchor) = (None, None); + if methods.has_tapret_first() { + tapret_anchor = Some(self.dbc_commit::()?); + } + if methods.has_opret_first() { + opret_anchor = Some(self.dbc_commit::()?); + } + let anchor = AnchorSet::from_split(tapret_anchor, opret_anchor) + .expect("at least one of DBC are present due to CloseMethodSet type guarantees"); + Ok(Fascia { + anchor: XAnchor::Bitcoin(anchor), + bundles, + }) + } + + fn rgb_extract(&self) -> Result { + todo!("implement RGB PSBT fascia extraction for multi-party protocols") + } } diff --git a/psbt/src/rgb.rs b/psbt/src/rgb.rs index 8e6800d..ddb9934 100644 --- a/psbt/src/rgb.rs +++ b/psbt/src/rgb.rs @@ -19,24 +19,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -// TODO: Implement state transition ops for PSBT - -use std::collections::btree_map::Entry; use std::collections::{BTreeMap, BTreeSet}; -use amplify::confinement::{Confined, U24}; -use amplify::{confinement, Wrapper}; -use bitcoin::psbt::raw::ProprietaryKey; -use bitcoin::psbt::{self, PartiallySignedTransaction as Psbt}; +use amplify::confinement::{Confined, MediumOrdMap, SmallOrdMap, U24}; +use amplify::{confinement, FromSliceError}; +use bp::dbc::Method; use commit_verify::mpc; -use rgb::{BundleItem, ContractId, OpId, Operation, Transition, TransitionBundle}; +use psbt::{KeyAlreadyPresent, KeyMap, MpcPsbtError, PropKey, Psbt}; use rgbstd::accessors::{MergeReveal, MergeRevealError}; +use rgbstd::containers::CloseMethodSet; use rgbstd::interface::VelocityHint; -use strict_encoding::{SerializeError, StrictDeserialize, StrictSerialize}; - -use super::lnpbp4::OutputLnpbp4; -use super::opret::OutputOpret; -use super::tapret::OutputTapret; +use rgbstd::{ContractId, OpId, Operation, Transition, TransitionBundle, Vin}; +use strict_encoding::{DeserializeError, StrictDeserialize, StrictSerialize}; // TODO: Instead of storing whole RGB contract in PSBT create a shortened // contract version which skips all info not important for hardware @@ -46,47 +40,59 @@ use super::tapret::OutputTapret; // pub const PSBT_GLOBAL_RGB_CONTRACT: u8 = 0x00; /// PSBT proprietary key prefix used for RGB. -pub const PSBT_RGB_PREFIX: &[u8] = b"RGB"; +pub const PSBT_RGB_PREFIX: &str = "RGB"; /// Proprietary key subtype for storing RGB state transition in global map. -pub const PSBT_GLOBAL_RGB_TRANSITION: u8 = 0x01; +pub const PSBT_GLOBAL_RGB_TRANSITION: u64 = 0x01; +/// Proprietary key subtype for storing information on which closed methods +/// should be used for each of RGB state transitions. +pub const PSBT_GLOBAL_RGB_CLOSE_METHODS: u64 = 0x02; /// Proprietary key subtype for storing RGB state transition operation id which /// consumes this input. -pub const PSBT_IN_RGB_CONSUMED_BY: u8 = 0x03; +pub const PSBT_IN_RGB_CONSUMED_BY: u64 = 0x01; /// Proprietary key subtype for storing hint for the velocity of the state /// which can be assigned to the provided output. -pub const PSBT_OUT_RGB_VELOCITY_HINT: u8 = 0x10; +pub const PSBT_OUT_RGB_VELOCITY_HINT: u64 = 0x01; /// Extension trait for static functions returning RGB-related proprietary keys. pub trait ProprietaryKeyRgb { /// Constructs [`PSBT_GLOBAL_RGB_TRANSITION`] proprietary key. - fn rgb_transition(opid: OpId) -> ProprietaryKey { - ProprietaryKey { - prefix: PSBT_RGB_PREFIX.to_vec(), + fn rgb_transition(opid: OpId) -> PropKey { + PropKey { + identifier: PSBT_RGB_PREFIX.to_owned(), subtype: PSBT_GLOBAL_RGB_TRANSITION, - key: opid.to_vec(), + data: opid.to_vec().into(), + } + } + /// Constructs [`PSBT_GLOBAL_RGB_CLOSE_METHODS`] proprietary key. + fn rgb_closing_methods(opid: OpId) -> PropKey { + PropKey { + identifier: PSBT_RGB_PREFIX.to_owned(), + subtype: PSBT_GLOBAL_RGB_CLOSE_METHODS, + data: opid.to_vec().into(), } } /// Constructs [`PSBT_IN_RGB_CONSUMED_BY`] proprietary key. - fn rgb_in_consumed_by(contract_id: ContractId) -> ProprietaryKey { - ProprietaryKey { - prefix: PSBT_RGB_PREFIX.to_vec(), + fn rgb_in_consumed_by(contract_id: ContractId) -> PropKey { + PropKey { + identifier: PSBT_RGB_PREFIX.to_owned(), subtype: PSBT_IN_RGB_CONSUMED_BY, - key: contract_id.to_vec(), + data: contract_id.to_vec().into(), } } - fn rgb_out_velocity_hint() -> ProprietaryKey { - ProprietaryKey { - prefix: PSBT_RGB_PREFIX.to_vec(), + /// Constructs [`PSBT_OUT_RGB_VELOCITY_HINT`] proprietary key. + fn rgb_out_velocity_hint() -> PropKey { + PropKey { + identifier: PSBT_RGB_PREFIX.to_owned(), subtype: PSBT_OUT_RGB_VELOCITY_HINT, - key: vec![], + data: none!(), } } } -impl ProprietaryKeyRgb for ProprietaryKey {} +impl ProprietaryKeyRgb for PropKey {} /// Errors processing RGB-related proprietary PSBT keys and their values. #[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)] @@ -99,86 +105,118 @@ pub enum RgbPsbtError { /// transition {1} which has to be added to RGB UnrelatedTransitions(OpId, OpId, MergeRevealError), - /// PSBT doesn't specify an output which can host tapret or opret - /// commitment. - NoHostOutput, + /// PSBT contains no contract information + NoContracts, + + /// contract {0} listed in the PSBT has zero known transition information. + NoTransitions(ContractId), + + /// invalid contract id data. + #[from(FromSliceError)] + InvalidContractId, + + /// state transition {0} doesn't provide information about seal closing + /// methods used by its inputs. + NoCloseMethod(OpId), + + /// invalid close method data for opid {0} + InvalidCloseMethod(OpId), + + /// PSBT doesn't specify an output which can host {0} commitment. + NoHostOutput(Method), + + /// PSBT contains too many contracts (more than 16 million). + TooManyContracts, /// PSBT contains too many state transitions for a bundle. #[from(confinement::Error)] TooManyTransitionsInBundle, + /// the size of transition {0} exceeds 16 MB. + TransitionTooBig(OpId), + /// state transition data in PSBT are invalid. Details: {0} #[from] - InvalidTransition(SerializeError), + InvalidTransition(DeserializeError), + + #[from] + #[display(inner)] + Mpc(MpcPsbtError), } #[allow(clippy::result_large_err)] pub trait RgbExt { - fn rgb_contract_ids(&self) -> BTreeSet; + fn rgb_contract_ids(&self) -> Result, FromSliceError>; fn rgb_contract_consumers( &self, contract_id: ContractId, - ) -> Result, RgbPsbtError>; + ) -> Result, FromSliceError>; - fn rgb_op_ids(&self, contract_id: ContractId) -> BTreeSet; + fn rgb_op_ids(&self, contract_id: ContractId) -> Result, FromSliceError>; - fn rgb_transitions(&self, contract_id: ContractId) -> BTreeMap { - self.rgb_op_ids(contract_id) - .into_iter() - .filter_map(|opid| self.rgb_transition(opid).map(|ts| (opid, ts))) - .collect() - } + fn rgb_transition(&self, opid: OpId) -> Result, RgbPsbtError>; - fn rgb_transition(&self, opid: OpId) -> Option; + fn rgb_close_methods(&self, opid: OpId) -> Result, RgbPsbtError>; - fn push_rgb_transition(&mut self, transition: Transition) -> Result; + fn push_rgb_transition( + &mut self, + transition: Transition, + methods: CloseMethodSet, + ) -> Result; - fn rgb_bundles(&self) -> Result, RgbPsbtError> { + fn rgb_bundles( + &self, + ) -> Result, RgbPsbtError> { let mut map = BTreeMap::new(); - for contract_id in self.rgb_contract_ids() { - let mut items = BTreeMap::new(); - for (opid, no) in self.rgb_contract_consumers(contract_id)? { - let transition = self.rgb_transition(opid); - match items.entry(opid) { - Entry::Vacant(entry) => { - entry.insert(BundleItem { - inputs: tiny_bset!(no), - transition, - }); - } - Entry::Occupied(entry) => { - let item = entry.into_mut(); - if item.transition.is_none() { - item.transition = transition; - } - item.inputs.push(no)?; - } + for contract_id in self.rgb_contract_ids()? { + let mut input_map = SmallOrdMap::::new(); + let mut known_transitions = SmallOrdMap::::new(); + let mut method_set = None::; + for (opid, vin) in self.rgb_contract_consumers(contract_id)? { + let (transition, methods) = ( + self.rgb_transition(opid)?, + self.rgb_close_methods(opid)? + .ok_or(RgbPsbtError::NoCloseMethod(opid))?, + ); + method_set |= methods; + input_map.insert(vin, opid)?; + if let Some(transition) = transition { + known_transitions.insert(opid, transition)?; } } - let bundle = Confined::try_from(items).map(TransitionBundle::from_inner)?; - map.insert(contract_id, bundle); + let bundle = TransitionBundle { + input_map: Confined::try_from(input_map.into_inner()) + .map_err(|_| RgbPsbtError::NoTransitions(contract_id))?, + known_transitions: Confined::try_from(known_transitions.into_inner()) + .map_err(|_| RgbPsbtError::NoTransitions(contract_id))?, + }; + map.insert(contract_id, (bundle, method_set.expect("type guarantees"))); } Ok(map) } - fn rgb_bundle_to_lnpbp4(&mut self) -> Result; + fn rgb_bundles_to_mpc( + &mut self, + ) -> Result< + (Confined, 1, U24>, CloseMethodSet), + RgbPsbtError, + >; } impl RgbExt for Psbt { - fn rgb_contract_ids(&self) -> BTreeSet { - self.inputs - .iter() + fn rgb_contract_ids(&self) -> Result, FromSliceError> { + self.inputs() .flat_map(|input| { input .proprietary .keys() .filter(|prop_key| { - prop_key.prefix == PSBT_RGB_PREFIX && + prop_key.identifier == PSBT_RGB_PREFIX && prop_key.subtype == PSBT_IN_RGB_CONSUMED_BY }) - .map(|prop_key| &prop_key.key) - .filter_map(ContractId::from_slice) + .map(|prop_key| prop_key.data.as_slice()) + .map(ContractId::copy_from_slice) }) .collect() } @@ -186,34 +224,51 @@ impl RgbExt for Psbt { fn rgb_contract_consumers( &self, contract_id: ContractId, - ) -> Result, RgbPsbtError> { - let mut consumers: BTreeSet<(OpId, u16)> = bset! {}; - for (no, input) in self.inputs.iter().enumerate() { - if let Some(opid) = input.rgb_consumer(contract_id) { - consumers.insert((opid, no as u16)); + ) -> Result, FromSliceError> { + let mut consumers: BTreeSet<(OpId, Vin)> = bset! {}; + for (no, input) in self.inputs().enumerate() { + if let Some(opid) = input.rgb_consumer(contract_id)? { + consumers.insert((opid, Vin::from_u32(no as u32))); } } Ok(consumers) } - fn rgb_op_ids(&self, contract_id: ContractId) -> BTreeSet { - self.inputs - .iter() - .filter_map(|input| input.rgb_consumer(contract_id)) + fn rgb_op_ids(&self, contract_id: ContractId) -> Result, FromSliceError> { + self.inputs() + .filter_map(|input| input.rgb_consumer(contract_id).transpose()) .collect() } - fn rgb_transition(&self, opid: OpId) -> Option { - let data = self - .proprietary - .get(&ProprietaryKey::rgb_transition(opid))?; - let data = Confined::try_from_iter(data.iter().copied()).ok()?; - Transition::from_strict_serialized::(data).ok() + fn rgb_transition(&self, opid: OpId) -> Result, RgbPsbtError> { + let Some(data) = self.proprietary(&PropKey::rgb_transition(opid)) else { + return Ok(None); + }; + let data = Confined::try_from_iter(data.iter().copied())?; + let transition = Transition::from_strict_serialized::(data)?; + Ok(Some(transition)) } - fn push_rgb_transition(&mut self, mut transition: Transition) -> Result { + fn rgb_close_methods(&self, opid: OpId) -> Result, RgbPsbtError> { + let Some(m) = self.proprietary(&PropKey::rgb_closing_methods(opid)) else { + return Ok(None); + }; + if m.len() == 1 { + if let Ok(method) = CloseMethodSet::try_from(m[0]) { + return Ok(Some(method)); + } + } + return Err(RgbPsbtError::InvalidCloseMethod(opid)); + } + + fn push_rgb_transition( + &mut self, + mut transition: Transition, + mut methods: CloseMethodSet, + ) -> Result { let opid = transition.id(); - let prev_transition = self.rgb_transition(opid); + let prev_methods = self.rgb_close_methods(opid)?; + let prev_transition = self.rgb_transition(opid)?; if let Some(ref prev_transition) = prev_transition { transition = transition .merge_reveal(prev_transition.clone()) @@ -221,29 +276,59 @@ impl RgbExt for Psbt { RgbPsbtError::UnrelatedTransitions(prev_transition.id(), opid, err) })?; } - let serialized_transition = transition.to_strict_serialized::()?; - self.proprietary - .insert(ProprietaryKey::rgb_transition(opid), serialized_transition.into_inner()); + let serialized_transition = transition + .to_strict_serialized::() + .map_err(|_| RgbPsbtError::TransitionTooBig(opid))?; + // Since we update transition it's ok to ignore the fact that it previously + // existed + let _ = self + .push_proprietary(PropKey::rgb_transition(opid), serialized_transition.into_inner()); + methods |= prev_methods; + let _ = self.push_proprietary(PropKey::rgb_closing_methods(opid), vec![methods as u8]); Ok(prev_transition.is_none()) } - fn rgb_bundle_to_lnpbp4(&mut self) -> Result { + fn rgb_bundles_to_mpc( + &mut self, + ) -> Result< + (Confined, 1, U24>, CloseMethodSet), + RgbPsbtError, + > { let bundles = self.rgb_bundles()?; - let output = self - .outputs - .iter_mut() - .find(|output| output.is_tapret_host() | output.is_opret_host()) - .ok_or(RgbPsbtError::NoHostOutput)?; - - let len = bundles.len(); - for (contract_id, bundle) in bundles { - output - .set_lnpbp4_message(mpc::ProtocolId::from(contract_id), bundle.bundle_id().into()) - .map_err(|_| RgbPsbtError::AlreadySet)?; + let mut map = MediumOrdMap::new(); + let mut close_methods = None::; + for (contract_id, (bundle, methods)) in bundles { + let protocol_id = mpc::ProtocolId::from(contract_id); + let message = mpc::Message::from(bundle.bundle_id()); + if methods.has_tapret_first() { + // We need to do it each time due to Rust borrow checker + let tapret_host = self + .outputs_mut() + .find(|output| output.is_tapret_host()) + .ok_or(RgbPsbtError::NoHostOutput(Method::TapretFirst))?; + tapret_host.set_mpc_message(protocol_id, message)?; + } + if methods.has_opret_first() { + // We need to do it each time due to Rust borrow checker + let opret_host = self + .outputs_mut() + .find(|output| output.is_opret_host()) + .ok_or(RgbPsbtError::NoHostOutput(Method::OpretFirst))?; + opret_host.set_mpc_message(protocol_id, message)?; + } + map.insert(contract_id, bundle) + .map_err(|_| RgbPsbtError::TooManyContracts)?; + close_methods |= methods; } - Ok(len) + let Some(close_methods) = close_methods else { + return Err(RgbPsbtError::NoContracts); + }; + + let map = Confined::try_from(map.into_inner()).map_err(|_| RgbPsbtError::NoContracts)?; + + Ok((map, close_methods)) } } @@ -254,7 +339,7 @@ pub trait RgbInExt { /// this proprietary key to a standard one. In this case, the invalid /// data will be filtered at the moment of PSBT deserialization and this /// function will return `None` only in situations when the key is absent. - fn rgb_consumer(&self, contract_id: ContractId) -> Option; + fn rgb_consumer(&self, contract_id: ContractId) -> Result, FromSliceError>; /// Adds information about state transition consuming this PSBT input. /// @@ -273,30 +358,33 @@ pub trait RgbInExt { &mut self, contract_id: ContractId, opid: OpId, - ) -> Result; + ) -> Result; } impl RgbInExt for psbt::Input { - fn rgb_consumer(&self, contract_id: ContractId) -> Option { - let data = self + fn rgb_consumer(&self, contract_id: ContractId) -> Result, FromSliceError> { + let Some(data) = self .proprietary - .get(&ProprietaryKey::rgb_in_consumed_by(contract_id))?; - OpId::from_slice(data) + .get(&PropKey::rgb_in_consumed_by(contract_id)) + else { + return Ok(None); + }; + Ok(Some(OpId::copy_from_slice(data)?)) } fn set_rgb_consumer( &mut self, contract_id: ContractId, opid: OpId, - ) -> Result { + ) -> Result { + let key = PropKey::rgb_in_consumed_by(contract_id); match self.rgb_consumer(contract_id) { - None => { - self.proprietary - .insert(ProprietaryKey::rgb_in_consumed_by(contract_id), opid.to_vec()); + Ok(None) | Err(_) => { + let _ = self.push_proprietary(key, opid.to_vec()); Ok(true) } - Some(id) if id == opid => Ok(false), - Some(_) => Err(RgbPsbtError::AlreadySet), + Ok(Some(id)) if id == opid => Ok(false), + Ok(Some(_)) => Err(KeyAlreadyPresent(key)), } } } @@ -323,9 +411,7 @@ pub trait RgbOutExt { impl RgbOutExt for psbt::Output { fn rgb_velocity_hint(&self) -> Option { - let data = self - .proprietary - .get(&ProprietaryKey::rgb_out_velocity_hint())?; + let data = self.proprietary.get(&PropKey::rgb_out_velocity_hint())?; if data.len() != 1 { None } else { @@ -335,8 +421,8 @@ impl RgbOutExt for psbt::Output { fn set_rgb_velocity_hint(&mut self, hint: VelocityHint) -> bool { let prev = self.rgb_velocity_hint(); - self.proprietary - .insert(ProprietaryKey::rgb_out_velocity_hint(), vec![hint as u8]); + self.push_proprietary(PropKey::rgb_out_velocity_hint(), vec![hint as u8]) + .ok(); Some(hint) == prev } } diff --git a/src/pay.rs b/src/pay.rs index 1eb97fc..5dc6aff 100644 --- a/src/pay.rs +++ b/src/pay.rs @@ -19,22 +19,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::HashMap; use std::convert::Infallible; -use std::iter; use bp::seals::txout::CloseMethod; -use bp::{Sats, Vout}; -use bpwallet::{Invoice, PsbtMeta, TxParams}; +use bp::{Outpoint, Sats, Vout}; +use bpwallet::{Beneficiary as BpBeneficiary, PsbtMeta, TxParams}; use psbt::Psbt; use rgbstd::containers::{Bindle, BuilderSeal, Transfer}; -use rgbstd::interface::{BuilderError, ContractSuppl, FilterIncludeAll, TypedState, VelocityHint}; +use rgbstd::interface::{BuilderError, FilterIncludeAll}; use rgbstd::invoice::{Beneficiary, InvoiceState, RgbInvoice}; use rgbstd::persistence::{ConsignerError, Inventory, InventoryError, Stash}; -use rgbstd::{ - AssignmentType, ContractId, GraphSeal, Operation, Opout, SealDefinition, - RGB_NATIVE_DERIVATION_INDEX, RGB_TAPRET_DERIVATION_INDEX, -}; use crate::Runtime; @@ -78,7 +72,7 @@ pub enum PayError { Consigner(ConsignerError), } -#[derive(Clone, Eq, PartialEq, Hash, Debug)] +#[derive(Clone, PartialEq, Debug)] pub struct TransferParams { pub tx: TxParams, pub min_amount: Sats, @@ -142,11 +136,13 @@ impl Runtime { } _ => return Err(PayError::Unsupported), }; - let inv = match invoice.beneficiary { - Beneficiary::BlindedSeal(_) => Invoice::with_max(self.wallet().next_address()), - Beneficiary::WitnessVoutBitcoin(addr) => Invoice::new(addr, params.min_amount), + let beneficiary = match invoice.beneficiary { + Beneficiary::BlindedSeal(seal) => BpBeneficiary::with_max(self.wallet().next_address()), + Beneficiary::WitnessVoutBitcoin(addr) => BpBeneficiary::new(addr, params.min_amount), }; - let (mut psbt, meta) = self.wallet().construct_psbt(&outputs, inv, params.tx)?; + let (mut psbt, meta) = self + .wallet() + .construct_psbt(&outputs, [beneficiary], params.tx)?; let batch = self.compose(&invoice, outputs, method, meta.change_vout, |_, _, _| meta.change_vout)?; @@ -161,15 +157,23 @@ impl Runtime { ) -> Result, PayError> { let contract_id = invoice.contract.ok_or(PayError::NoContract)?; - psbt.dbc_finalize()?; - let fascia = psbt.rgb_extract()?; - - let witness_txid = psbt.txid(); - self.stock().consume(fascia)?; let beneficiary = match invoice.beneficiary { - BuilderSeal::Revealed(seal) => BuilderSeal::Revealed(seal.resolve(witness_txid)), - BuilderSeal::Concealed(seal) => BuilderSeal::Concealed(seal), + Beneficiary::WitnessVoutBitcoin(addr) => { + let s = addr.script_pubkey(); + let vout = psbt + .outputs() + .position(|output| output.script == s) + .ok_or(PayError::NoBeneficiaryOutput)?; + let witness_txid = psbt.txid(); + BuilderSeal::Revealed( + Outpoint::new(witness_txid, Vout::from_u32(vout as u32)).into(), + ) + } + Beneficiary::BlindedSeal(seal) => BuilderSeal::Concealed(seal), }; + + let fascia = psbt.rgb_commit()?; + self.stock().consume(fascia)?; let transfer = self.stock().transfer(contract_id, [beneficiary])?; Ok(transfer) diff --git a/src/runtime.rs b/src/runtime.rs index 80f1361..7b81d01 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -29,12 +29,11 @@ use std::{fs, io}; use bpstd::{AddressNetwork, Network, XpubDerivable}; use bpwallet::Wallet; use rgbfs::StockFs; -use rgbstd::containers::{Contract, LoadError, Transfer}; +use rgbstd::containers::{Contract, LoadError, Transfer, XchainOutpoint}; use rgbstd::interface::{BuilderError, OutpointFilter}; use rgbstd::persistence::{Inventory, InventoryDataError, InventoryError, StashError, Stock}; use rgbstd::resolvers::ResolveHeight; use rgbstd::validation::{self, ResolveTx}; -use rgbstd::Output; use strict_types::encoding::{DeserializeError, Ident, SerializeError}; use crate::{DescriptorRgb, RgbDescr}; @@ -118,10 +117,11 @@ impl, K> DerefMut for Runtime { } impl, K> OutpointFilter for Runtime { - fn include_output(&self, output: Output) -> bool { + fn include_output(&self, output: impl Into) -> bool { + let output = output.into(); self.wallet .coins() - .any(|utxo| Output::Bitcoin(utxo.outpoint) == output) + .any(|utxo| XchainOutpoint::Bitcoin(utxo.outpoint) == output) } } From 7ad91e6815a479e025e805c5ebc44fcff44a0a83 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Mon, 18 Dec 2023 21:15:56 +0100 Subject: [PATCH 09/27] runtime, cli: payment workflow --- Cargo.lock | 64 ++++++++--------- Cargo.toml | 3 +- cli/src/command.rs | 76 +++++++++++--------- cli/src/resolver.rs | 10 +-- psbt/Cargo.toml | 2 +- psbt/src/lib.rs | 2 +- src/descriptor.rs | 8 +++ src/lib.rs | 1 + src/pay.rs | 164 +++++++++++++++++++++++++++++++++++--------- src/runtime.rs | 5 +- 10 files changed, 223 insertions(+), 112 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f0be30a..7c1ae81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -425,7 +425,7 @@ dependencies = [ [[package]] name = "bp-util" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#07dc6312f8aa80f4b8299d720fcdbd6858f08f82" +source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#6418b4eaef62b78c092714582818046b6e2a2d03" dependencies = [ "amplify", "base64", @@ -447,7 +447,7 @@ dependencies = [ [[package]] name = "bp-wallet" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#07dc6312f8aa80f4b8299d720fcdbd6858f08f82" +source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#6418b4eaef62b78c092714582818046b6e2a2d03" dependencies = [ "amplify", "bp-esplora", @@ -1006,9 +1006,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", @@ -1021,7 +1021,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.10", + "socket2", "tokio", "tower-service", "tracing", @@ -1371,24 +1371,6 @@ dependencies = [ "strict_encoding", ] -[[package]] -name = "psbt-rgb" -version = "0.11.0-alpha.2" -dependencies = [ - "amplify", - "baid58", - "bp-core", - "bp-std", - "commit_verify", - "getrandom", - "psbt", - "rand", - "rgb-std", - "strict_encoding", - "wasm-bindgen", - "wasm-bindgen-test", -] - [[package]] name = "quote" version = "1.0.33" @@ -1539,7 +1521,7 @@ dependencies = [ [[package]] name = "rgb-invoice" version = "0.11.0-beta.2" -source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#b65cf9ec35a4748862dd02b59ca72fb6b7370692" +source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#f73f03bf8dca1953ae79d852e44f34255f876c7c" dependencies = [ "amplify", "baid58", @@ -1563,6 +1545,24 @@ dependencies = [ "strict_encoding", ] +[[package]] +name = "rgb-psbt" +version = "0.11.0-alpha.2" +dependencies = [ + "amplify", + "baid58", + "bp-core", + "bp-std", + "commit_verify", + "getrandom", + "psbt", + "rand", + "rgb-std", + "strict_encoding", + "wasm-bindgen", + "wasm-bindgen-test", +] + [[package]] name = "rgb-runtime" version = "0.11.0-alpha.2" @@ -1577,8 +1577,8 @@ dependencies = [ "descriptors", "indexmap 2.1.0", "log", - "psbt", "rgb-persist-fs", + "rgb-psbt", "rgb-std", "serde", "serde_yaml", @@ -1588,7 +1588,7 @@ dependencies = [ [[package]] name = "rgb-std" version = "0.11.0-beta.2" -source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#b65cf9ec35a4748862dd02b59ca72fb6b7370692" +source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#f73f03bf8dca1953ae79d852e44f34255f876c7c" dependencies = [ "amplify", "baid58", @@ -1955,16 +1955,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "socket2" version = "0.5.5" @@ -2192,7 +2182,7 @@ dependencies = [ "libc", "mio", "pin-project-lite", - "socket2 0.5.5", + "socket2", "windows-sys 0.48.0", ] diff --git a/Cargo.toml b/Cargo.toml index 768213e..3d06a57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ bp-esplora = "0.11.0-beta.1" descriptors = "0.11.0-beta.1" psbt = { version = "0.11.0-beta.1", features = ["client-side-validation"] } rgb-std = { version = "0.11.0-beta.2", features = ["fs"] } +rgb-psbt = { version = "0.11.0-alpha.2", path = "psbt" } indexmap = "2.0.2" chrono = "0.4.31" serde_crate = { package = "serde", version = "1", features = ["derive"] } @@ -70,8 +71,8 @@ bp-std = { workspace = true } bp-wallet = { workspace = true, features = ["fs"] } bp-esplora = { workspace = true, optional = true } descriptors = { workspace = true } -psbt = { workspace = true } rgb-std = { workspace = true } +rgb-psbt = { workspace = true } rgb-persist-fs = { version = "0.11.0-alpha", path = "fs" } indexmap = { workspace = true } chrono = { workspace = true } diff --git a/cli/src/command.rs b/cli/src/command.rs index e55a873..1a5a7ff 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -20,21 +20,22 @@ // limitations under the License. use std::fs; +use std::fs::File; use std::path::PathBuf; use std::str::FromStr; use amplify::confinement::U16; use bp_util::{Config, Exec}; use bpstd::{Sats, Txid}; -use bpwallet::{Invoice, TxParams}; -use rgb_rt::{DescriptorRgb, RgbDescr, RgbKeychain, RuntimeError}; +use psbt::PsbtVer; +use rgb_rt::{DescriptorRgb, RgbDescr, RgbKeychain, RuntimeError, TransferParams}; use rgbstd::containers::{Bindle, Transfer, UniversalBindle}; use rgbstd::contract::{ContractId, GenesisSeal, GraphSeal, StateType}; use rgbstd::interface::{ContractBuilder, FilterExclude, IfaceId, SchemaIfaces}; use rgbstd::invoice::{Beneficiary, InvoiceState, RgbInvoice, RgbTransport}; use rgbstd::persistence::{Inventory, Stash}; use rgbstd::schema::SchemaId; -use rgbstd::SealDefinition; +use rgbstd::XSeal; use seals::txout::{CloseMethod, ExplicitSeal}; use strict_types::encoding::{FieldName, TypeName}; use strict_types::StrictVal; @@ -139,10 +140,19 @@ pub enum Command { /// Transfer RGB assets #[display("transfer")] Transfer { + /// Encode PSBT as V2 + #[clap(short = '2')] + v2: bool, + /// Method for single-use-seals #[clap(long, default_value = "tapret1st")] method: CloseMethod, + /// Amount of satoshis which should be paid to the address-based + /// beneficiary + #[clap(long, default_value = "2000")] + sats: Sats, + /// Invoice data invoice: RgbInvoice, @@ -295,7 +305,7 @@ impl Exec for RgbArgs { contract, file, } => { - let mut runtime = self.rgb_runtime(&config)?; + let runtime = self.rgb_runtime(&config)?; let bindle = runtime .export_contract(*contract) .map_err(|err| err.to_string())?; @@ -471,7 +481,7 @@ impl Exec for RgbArgs { .expect("seal must be a string"); let seal = ExplicitSeal::::from_str(seal).expect("invalid seal definition"); - let seal = GenesisSeal::from(seal); + let seal = XSeal::Bitcoin(GenesisSeal::from(seal)); // Workaround for borrow checker: let field_name = @@ -537,7 +547,7 @@ impl Exec for RgbArgs { .next() .expect("no addresses left") .addr; - Beneficiary::WitnessUtxo(addr) + Beneficiary::WitnessVoutBitcoin(addr) } (_, Some(outpoint)) => { let seal = GraphSeal::new( @@ -545,7 +555,7 @@ impl Exec for RgbArgs { outpoint.txid, outpoint.vout, ); - runtime.store_seal_secret(SealDefinition::Bitcoin(seal))?; + runtime.store_seal_secret(XSeal::Bitcoin(seal))?; Beneficiary::BlindedSeal(seal.to_concealed_seal()) } }; @@ -565,40 +575,36 @@ impl Exec for RgbArgs { } #[allow(unused_variables)] Command::Transfer { + v2, method, invoice, fee, - psbt: psbt_filename, + sats, + psbt: psbt_file, consignment: out_file, } => { - // 1. BP Wallet: Do coin selection (using Layer2 components) - // 2. BP Wallet: Construct PSBT prototype (no state transitions) - // ... complete PSBT structure updates in multi-party protocols - // 3. RGB Std: Prepare stencil - main state transition and blank state - // transitions - // 4. RGB PSBT: Embed stencil into PSBT - // ... complete PSBT client-side updates in multi-party protocols - // 5. RGB PSBT: Anchorize PSBT, extract disclosure - // 6. RGB Std: Merge disclosure into the stash, cache and index - // 7. RGB Std: Prepare consignment - let mut runtime = self.rgb_runtime(&config)?; - // TODO: Support lock time and RBFs - let params = TxParams::with(*fee); + let params = TransferParams::with(*fee, *sats); - eprint!("Constructing PSBT ... "); - let mut psbt = runtime - .wallet_mut() - .construct_psbt(coins, Invoice, params)?; - eprintln!("success"); - - eprint!("Constructing transfer consignment ... "); - let transfer = runtime - .pay(invoice, &mut psbt, method) + let (psbt, meta, transfer) = runtime + .pay(invoice, *method, params) .map_err(|err| err.to_string())?; + transfer.save(&out_file)?; - eprintln!("success"); + + let ver = if *v2 { PsbtVer::V2 } else { PsbtVer::V0 }; + eprintln!("{}", serde_yaml::to_string(&psbt).unwrap()); + match psbt_file { + Some(file_name) => { + let mut psbt_file = File::create(file_name)?; + psbt.encode(ver, &mut psbt_file)?; + } + None => match ver { + PsbtVer::V0 => println!("{psbt}"), + PsbtVer::V2 => println!("{psbt:#}"), + }, + } } Command::Inspect { file, format } => { let bindle = UniversalBindle::load_file(file)?; @@ -651,7 +657,11 @@ impl Exec for RgbArgs { format!("{root_dir}/stash/geneses/{id}.yaml"), serde_yaml::to_string(runtime.genesis(id)?)?, )?; - for (no, suppl) in runtime.contract_suppl(id).into_iter().flatten().enumerate() + for (no, suppl) in runtime + .contract_suppl_all(id) + .into_iter() + .flatten() + .enumerate() { fs::write( format!("{root_dir}/stash/geneses/{id}.suppl.{no:03}.yaml"), @@ -665,7 +675,7 @@ impl Exec for RgbArgs { serde_yaml::to_string(runtime.bundle(id)?)?, )?; } - for id in runtime.anchor_ids()? { + for id in runtime.witness_ids()? { fs::write( format!("{root_dir}/stash/anchors/{id}.yaml"), serde_yaml::to_string(runtime.anchor(id)?)?, diff --git a/cli/src/resolver.rs b/cli/src/resolver.rs index 6634d77..cdc1f67 100644 --- a/cli/src/resolver.rs +++ b/cli/src/resolver.rs @@ -24,7 +24,7 @@ use std::convert::Infallible; use bpstd::{Tx, Txid}; use rgbstd::resolvers::ResolveHeight; use rgbstd::validation::{ResolveTx, TxResolverError}; -use rgbstd::{Anchor, Layer1, WitnessAnchor}; +use rgbstd::{Layer1, WitnessAnchor, XAnchor}; use crate::RgbArgs; @@ -32,12 +32,12 @@ use crate::RgbArgs; pub struct PanickingResolver; impl ResolveHeight for PanickingResolver { type Error = Infallible; - fn resolve_anchor(&mut self, _: &Anchor) -> Result { + fn resolve_anchor(&mut self, _: &XAnchor) -> Result { unreachable!("PanickingResolver must be used only for newly issued contract validation") } } impl ResolveTx for PanickingResolver { - fn resolve_tx(&self, _: Layer1, _: Txid) -> Result { + fn resolve_bp_tx(&self, _: Layer1, _: Txid) -> Result { unreachable!("PanickingResolver must be used only for newly issued contract validation") } } @@ -48,12 +48,12 @@ impl RgbArgs { struct DumbResolver(); impl ResolveHeight for DumbResolver { type Error = Infallible; - fn resolve_anchor(&mut self, _: &Anchor) -> Result { + fn resolve_anchor(&mut self, _: &XAnchor) -> Result { todo!() } } impl ResolveTx for DumbResolver { - fn resolve_tx(&self, _: Layer1, _: Txid) -> Result { todo!() } + fn resolve_bp_tx(&self, _: Layer1, _: Txid) -> Result { todo!() } } DumbResolver::default() } diff --git a/psbt/Cargo.toml b/psbt/Cargo.toml index 97e6ffe..6cee557 100644 --- a/psbt/Cargo.toml +++ b/psbt/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "psbt-rgb" +name = "rgb-psbt" version = { workspace = true } description = "Partially signed bitcoin transaction RGB extensions" keywords = ["bitcoin", "invoices", "rgb", "smart-contracts", "psbt"] diff --git a/psbt/src/lib.rs b/psbt/src/lib.rs index 3dd8750..1f04226 100644 --- a/psbt/src/lib.rs +++ b/psbt/src/lib.rs @@ -26,7 +26,7 @@ mod rgb; use bp::dbc::opret::OpretProof; use bp::dbc::tapret::TapretProof; -use psbt::{DbcPsbtError, Psbt}; +pub use psbt::*; use rgbstd::containers::{Batch, Fascia, XchainOutpoint}; use rgbstd::{AnchorSet, XAnchor}; diff --git a/src/descriptor.rs b/src/descriptor.rs index 19cec01..9b4e50b 100644 --- a/src/descriptor.rs +++ b/src/descriptor.rs @@ -25,6 +25,7 @@ use std::{iter, vec}; use amplify::Wrapper; use bp::dbc::tapret::TapretCommitment; +use bp::dbc::Method; use bp::seals::txout::CloseMethod; use bpstd::{ CompressedPk, Derive, DeriveCompr, DeriveSet, DeriveXOnly, DerivedScript, Idx, IdxBase, @@ -64,6 +65,13 @@ impl RgbKeychain { k == Self::Rgb as u8 || k == Self::Tapret as u8 } pub fn is_seal(self) -> bool { self == Self::Rgb || self == Self::Tapret } + + pub const fn for_method(method: Method) -> Self { + match method { + Method::OpretFirst => Self::Rgb, + Method::TapretFirst => Self::Tapret, + } + } } impl FromStr for RgbKeychain { diff --git a/src/lib.rs b/src/lib.rs index a3cc7b7..9aa6698 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,4 +33,5 @@ mod descriptor; mod pay; pub use descriptor::{DescriptorRgb, RgbDescr, RgbKeychain, TapretKey}; +pub use pay::{CompletionError, CompositionError, PayError, TransferParams}; pub use runtime::{Runtime, RuntimeError}; diff --git a/src/pay.rs b/src/pay.rs index 5dc6aff..1a5599e 100644 --- a/src/pay.rs +++ b/src/pay.rs @@ -22,19 +22,32 @@ use std::convert::Infallible; use bp::seals::txout::CloseMethod; -use bp::{Outpoint, Sats, Vout}; -use bpwallet::{Beneficiary as BpBeneficiary, PsbtMeta, TxParams}; -use psbt::Psbt; +use bp::{Outpoint, Sats, ScriptPubkey, Vout}; +use bpwallet::{Beneficiary as BpBeneficiary, ConstructionError, PsbtMeta, TxParams}; +use psbt::{CommitError, EmbedError, Psbt, RgbPsbt}; use rgbstd::containers::{Bindle, BuilderSeal, Transfer}; -use rgbstd::interface::{BuilderError, FilterIncludeAll}; +use rgbstd::interface::{ContractError, FilterIncludeAll}; use rgbstd::invoice::{Beneficiary, InvoiceState, RgbInvoice}; -use rgbstd::persistence::{ConsignerError, Inventory, InventoryError, Stash}; +use rgbstd::persistence::{ + ComposeError, ConsignerError, Inventory, InventoryError, Stash, StashError, +}; +use rgbstd::XSeal; -use crate::Runtime; +use crate::{RgbKeychain, Runtime}; #[derive(Debug, Display, Error, From)] -#[display(doc_comments)] +#[display(inner)] pub enum PayError { + #[from] + Composition(CompositionError), + + #[from] + Completion(CompletionError), +} + +#[derive(Debug, Display, Error, From)] +#[display(doc_comments)] +pub enum CompositionError { /// unspecified contract. NoContract, @@ -56,20 +69,59 @@ pub enum PayError { /// the invoice has expired. InvoiceExpired, + /// one of the RGB assignments spent require presence of tapret output - + /// even this is not a taproot wallet. Unable to create a valid PSBT, manual + /// work is needed. + TapretRequired, + /// non-fungible state is not yet supported by the invoices. Unsupported, + #[from] + #[display(inner)] + Construction(ConstructionError), + + #[from] + #[display(inner)] + Interface(ContractError), + #[from] #[display(inner)] Inventory(InventoryError), #[from] #[display(inner)] - Builder(BuilderError), + Stash(StashError), + + #[from] + #[display(inner)] + Compose(ComposeError), + + #[from] + #[display(inner)] + Embed(EmbedError), +} + +#[derive(Debug, Display, Error, From)] +#[display(doc_comments)] +pub enum CompletionError { + /// unspecified contract. + NoContract, + + /// the provided PSBT doesn't pay any sats to the RGB beneficiary address. + NoBeneficiaryOutput, + + #[from] + #[display(inner)] + Inventory(InventoryError), #[from] #[display(inner)] Consigner(ConsignerError), + + #[from] + #[display(inner)] + Commit(CommitError), } #[derive(Clone, PartialEq, Debug)] @@ -78,9 +130,18 @@ pub struct TransferParams { pub min_amount: Sats, } +impl TransferParams { + pub fn with(fee: Sats, min_amount: Sats) -> Self { + TransferParams { + tx: TxParams::with(fee), + min_amount, + } + } +} + impl Runtime { pub fn pay( - &self, + &mut self, invoice: &RgbInvoice, method: CloseMethod, params: TransferParams, @@ -92,32 +153,37 @@ impl Runtime { } pub fn construct_psbt( - &self, + &mut self, invoice: &RgbInvoice, method: CloseMethod, params: TransferParams, - ) -> Result<(Psbt, PsbtMeta), PayError> { - let contract_id = invoice.contract.ok_or(PayError::NoContract)?; + ) -> Result<(Psbt, PsbtMeta), CompositionError> { + let contract_id = invoice.contract.ok_or(CompositionError::NoContract)?; - let iface_name = invoice.iface.ok_or(PayError::NoIface)?; + let iface_name = invoice.iface.clone().ok_or(CompositionError::NoIface)?; let iface = self.stock().iface_by_name(&iface_name)?; let contract = self.contract_iface_named(contract_id, iface_name)?; let operation = invoice .operation - .or_else(|| iface.default_operation) - .ok_or(PayError::NoOperation)?; + .as_ref() + .or_else(|| iface.default_operation.as_ref()) + .ok_or(CompositionError::NoOperation)?; let assignment_name = invoice .assignment + .as_ref() .or_else(|| { iface .transitions - .get(&operation) - .and_then(|t| t.default_assignment) + .get(operation) + .and_then(|t| t.default_assignment.as_ref()) }) - .ok_or(PayError::NoAssignment)?; + .cloned() + .ok_or(CompositionError::NoAssignment)?; let outputs = match invoice.owned_state { InvoiceState::Amount(amount) => { - let mut state = contract.fungible(assignment_name, &FilterIncludeAll)?; + let mut state = contract + .fungible(assignment_name, &FilterIncludeAll)? + .into_inner(); state.sort_by_key(|a| a.value); let mut sum = 0u64; state @@ -127,35 +193,67 @@ impl Runtime { if sum >= amount { false } else { - sum += amount; + sum += a.value; true } }) .map(|a| a.owner) .collect::>() } - _ => return Err(PayError::Unsupported), + _ => return Err(CompositionError::Unsupported), }; let beneficiary = match invoice.beneficiary { - Beneficiary::BlindedSeal(seal) => BpBeneficiary::with_max(self.wallet().next_address()), + Beneficiary::BlindedSeal(_) => BpBeneficiary::with_max( + self.wallet_mut() + .next_address(RgbKeychain::for_method(method), true), + ), Beneficiary::WitnessVoutBitcoin(addr) => BpBeneficiary::new(addr, params.min_amount), }; - let (mut psbt, meta) = self - .wallet() - .construct_psbt(&outputs, [beneficiary], params.tx)?; + let outpoints = outputs + .iter() + .filter_map(|o| o.reduce_to_bp()) + .map(|o| Outpoint::new(o.txid, o.vout)); + let (mut psbt, meta) = + self.wallet_mut() + .construct_psbt(outpoints, &[beneficiary], params.tx)?; + let (beneficiary_vout, beneficiary_script) = match invoice.beneficiary { + Beneficiary::WitnessVoutBitcoin(addr) => { + let s = addr.script_pubkey(); + let vout = psbt + .outputs() + .position(|output| output.script == s) + .map(|vout| Vout::from_u32(vout as u32)); + (vout, s) + } + Beneficiary::BlindedSeal(_) => (None, none!()), + }; let batch = - self.compose(&invoice, outputs, method, meta.change_vout, |_, _, _| meta.change_vout)?; + self.compose(&invoice, outputs, method, beneficiary_vout, |_, _, _| meta.change_vout)?; + + let methods = batch.close_method_set(); + if methods.has_tapret_first() { + let output = psbt + .outputs_mut() + .find(|o| o.script.is_p2tr() && &o.script != &beneficiary_script) + .ok_or(CompositionError::TapretRequired)?; + output.set_tapret_host().expect("just created"); + } + if methods.has_opret_first() { + let output = psbt.construct_output_expect(ScriptPubkey::op_return(&[]), Sats::ZERO); + output.set_opret_host().expect("just created"); + } + psbt.rgb_embed(batch)?; Ok((psbt, meta)) } pub fn transfer( - &self, + &mut self, invoice: &RgbInvoice, psbt: &mut Psbt, - ) -> Result, PayError> { - let contract_id = invoice.contract.ok_or(PayError::NoContract)?; + ) -> Result, CompletionError> { + let contract_id = invoice.contract.ok_or(CompletionError::NoContract)?; let beneficiary = match invoice.beneficiary { Beneficiary::WitnessVoutBitcoin(addr) => { @@ -163,17 +261,17 @@ impl Runtime { let vout = psbt .outputs() .position(|output| output.script == s) - .ok_or(PayError::NoBeneficiaryOutput)?; + .ok_or(CompletionError::NoBeneficiaryOutput)?; let witness_txid = psbt.txid(); - BuilderSeal::Revealed( + BuilderSeal::Revealed(XSeal::Bitcoin( Outpoint::new(witness_txid, Vout::from_u32(vout as u32)).into(), - ) + )) } Beneficiary::BlindedSeal(seal) => BuilderSeal::Concealed(seal), }; let fascia = psbt.rgb_commit()?; - self.stock().consume(fascia)?; + self.stock_mut().consume(fascia)?; let transfer = self.stock().transfer(contract_id, [beneficiary])?; Ok(transfer) diff --git a/src/runtime.rs b/src/runtime.rs index 7b81d01..4b17b3f 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -26,6 +26,7 @@ use std::ops::{Deref, DerefMut}; use std::path::PathBuf; use std::{fs, io}; +use amplify::IoError; use bpstd::{AddressNetwork, Network, XpubDerivable}; use bpwallet::Wallet; use rgbfs::StockFs; @@ -42,7 +43,8 @@ use crate::{DescriptorRgb, RgbDescr}; #[display(inner)] pub enum RuntimeError { #[from] - Io(io::Error), + #[from(io::Error)] + Io(IoError), #[from] Serialize(SerializeError), @@ -99,6 +101,7 @@ impl From for RuntimeError { #[derive(Getters)] pub struct Runtime = RgbDescr, K = XpubDerivable> { stock_path: PathBuf, + #[getter(as_mut)] stock: Stock, #[getter(as_mut)] wallet: Wallet, From 62b5009146b363553493e2a3a9b516866f54c78c Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Mon, 18 Dec 2023 21:19:23 +0100 Subject: [PATCH 10/27] chore: fix clippy warnings --- cli/src/command.rs | 2 +- psbt/src/lib.rs | 1 + psbt/src/rgb.rs | 2 +- src/pay.rs | 9 ++++++--- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/cli/src/command.rs b/cli/src/command.rs index 1a5a7ff..36655bb 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -591,7 +591,7 @@ impl Exec for RgbArgs { .pay(invoice, *method, params) .map_err(|err| err.to_string())?; - transfer.save(&out_file)?; + transfer.save(out_file)?; let ver = if *v2 { PsbtVer::V2 } else { PsbtVer::V0 }; eprintln!("{}", serde_yaml::to_string(&psbt).unwrap()); diff --git a/psbt/src/lib.rs b/psbt/src/lib.rs index 1f04226..6643f72 100644 --- a/psbt/src/lib.rs +++ b/psbt/src/lib.rs @@ -65,6 +65,7 @@ pub enum ExtractError {} pub trait RgbPsbt { fn rgb_embed(&mut self, batch: Batch) -> Result<(), EmbedError>; + #[allow(clippy::result_large_err)] fn rgb_commit(&mut self) -> Result; fn rgb_extract(&self) -> Result; } diff --git a/psbt/src/rgb.rs b/psbt/src/rgb.rs index ddb9934..2b67da1 100644 --- a/psbt/src/rgb.rs +++ b/psbt/src/rgb.rs @@ -258,7 +258,7 @@ impl RgbExt for Psbt { return Ok(Some(method)); } } - return Err(RgbPsbtError::InvalidCloseMethod(opid)); + Err(RgbPsbtError::InvalidCloseMethod(opid)) } fn push_rgb_transition( diff --git a/src/pay.rs b/src/pay.rs index 1a5599e..37da18e 100644 --- a/src/pay.rs +++ b/src/pay.rs @@ -140,6 +140,7 @@ impl TransferParams { } impl Runtime { + #[allow(clippy::result_large_err)] pub fn pay( &mut self, invoice: &RgbInvoice, @@ -152,6 +153,7 @@ impl Runtime { Ok((psbt, meta, transfer)) } + #[allow(clippy::result_large_err)] pub fn construct_psbt( &mut self, invoice: &RgbInvoice, @@ -166,7 +168,7 @@ impl Runtime { let operation = invoice .operation .as_ref() - .or_else(|| iface.default_operation.as_ref()) + .or(iface.default_operation.as_ref()) .ok_or(CompositionError::NoOperation)?; let assignment_name = invoice .assignment @@ -229,13 +231,13 @@ impl Runtime { Beneficiary::BlindedSeal(_) => (None, none!()), }; let batch = - self.compose(&invoice, outputs, method, beneficiary_vout, |_, _, _| meta.change_vout)?; + self.compose(invoice, outputs, method, beneficiary_vout, |_, _, _| meta.change_vout)?; let methods = batch.close_method_set(); if methods.has_tapret_first() { let output = psbt .outputs_mut() - .find(|o| o.script.is_p2tr() && &o.script != &beneficiary_script) + .find(|o| o.script.is_p2tr() && o.script != beneficiary_script) .ok_or(CompositionError::TapretRequired)?; output.set_tapret_host().expect("just created"); } @@ -248,6 +250,7 @@ impl Runtime { Ok((psbt, meta)) } + #[allow(clippy::result_large_err)] pub fn transfer( &mut self, invoice: &RgbInvoice, From 721b55c7167e8d3c1089f91cca160d5645abb9f1 Mon Sep 17 00:00:00 2001 From: Armando Dutra Date: Sat, 23 Dec 2023 14:33:27 -0300 Subject: [PATCH 11/27] fix: select correct assignment name --- src/pay.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/pay.rs b/src/pay.rs index 37da18e..b0712f2 100644 --- a/src/pay.rs +++ b/src/pay.rs @@ -170,17 +170,20 @@ impl Runtime { .as_ref() .or(iface.default_operation.as_ref()) .ok_or(CompositionError::NoOperation)?; + let assignment_name = invoice .assignment .as_ref() .or_else(|| { - iface - .transitions - .get(operation) - .and_then(|t| t.default_assignment.as_ref()) + iface.transitions.get(operation).and_then(|t| { + t.default_assignment + .as_ref() + .and_then(|f| t.assignments.get(f).and_then(|arg| (&arg.name).into())) + }) }) .cloned() .ok_or(CompositionError::NoAssignment)?; + let outputs = match invoice.owned_state { InvoiceState::Amount(amount) => { let mut state = contract From 8d2280c0e34ca05edddd504774f7053025b60f68 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Mon, 25 Dec 2023 01:11:41 +0100 Subject: [PATCH 12/27] cli: remove unneeded set-host command --- cli/src/command.rs | 56 ---------------------------------------------- 1 file changed, 56 deletions(-) diff --git a/cli/src/command.rs b/cli/src/command.rs index 36655bb..44477d8 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -202,17 +202,6 @@ pub enum Command { /// File with the transfer consignment file: PathBuf, }, - - /// Set first opret/tapret output to host a commitment - #[display("set-host")] - SetHost { - /// Method for single-use-seals - #[arg(long, default_value = "tapret1st")] - method: CloseMethod, - - /// PSBT file - psbt_file: PathBuf, - }, } impl Exec for RgbArgs { @@ -745,51 +734,6 @@ impl Exec for RgbArgs { runtime.accept_transfer(transfer, &mut resolver, *force)?; eprintln!("Transfer accepted into the stash"); } - #[allow(unused_variables)] - Command::SetHost { method, psbt_file } => { - todo!(); - /* - let psbt_data = fs::read(&psbt_file)?; - let mut psbt = Psbt::deserialize(&psbt_data)?; - let mut psbt_modified = false; - match method { - CloseMethod::OpretFirst => { - psbt.unsigned_tx - .output - .iter() - .zip(&mut psbt.outputs) - .find(|(o, outp)| { - o.script_pubkey.is_op_return() && !outp.is_opret_host() - }) - .and_then(|(_, outp)| { - psbt_modified = true; - outp.set_opret_host().ok() - }); - } - CloseMethod::TapretFirst => { - psbt.unsigned_tx - .output - .iter() - .zip(&mut psbt.outputs) - .find(|(o, outp)| { - o.script_pubkey.is_v1_p2tr() && !outp.is_tapret_host() - }) - .and_then(|(_, outp)| { - psbt_modified = true; - outp.set_tapret_host().ok() - }); - } - _ => {} - }; - fs::write(&psbt_file, psbt.serialize())?; - if psbt_modified { - eprintln!( - "PSBT file '{}' is updated with {method} host now set.", - psbt_file.display() - ); - } - */ - } } println!(); From 3961b4de87f27acc8fca5cc2fb9e4864ecaec42a Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Mon, 25 Dec 2023 01:12:02 +0100 Subject: [PATCH 13/27] pay: fix payments --- Cargo.lock | 118 ++++++++++++++++++++++----------------------- cli/src/command.rs | 10 ++-- src/pay.rs | 6 ++- 3 files changed, 71 insertions(+), 63 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c1ae81..89a4231 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -310,7 +310,7 @@ dependencies = [ [[package]] name = "bp-consensus" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#d3a57c213f34d9012101b9969cdc8b0d60a6fe60" +source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#536e773c0eea4f1706aad4418921083bc75f6833" dependencies = [ "amplify", "chrono", @@ -324,7 +324,7 @@ dependencies = [ [[package]] name = "bp-core" version = "0.11.0-beta.1" -source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#d3a57c213f34d9012101b9969cdc8b0d60a6fe60" +source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#536e773c0eea4f1706aad4418921083bc75f6833" dependencies = [ "amplify", "bp-consensus", @@ -340,7 +340,7 @@ dependencies = [ [[package]] name = "bp-dbc" version = "0.11.0-beta.1" -source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#d3a57c213f34d9012101b9969cdc8b0d60a6fe60" +source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#536e773c0eea4f1706aad4418921083bc75f6833" dependencies = [ "amplify", "base85", @@ -354,7 +354,7 @@ dependencies = [ [[package]] name = "bp-derive" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#64dc394e6b4d33d4f12a9b1eccd6429d6aa5bbe4" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#358faf0f2229501eac848266c8d2235936ab3421" dependencies = [ "amplify", "bitcoin_hashes", @@ -383,7 +383,7 @@ dependencies = [ [[package]] name = "bp-invoice" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#64dc394e6b4d33d4f12a9b1eccd6429d6aa5bbe4" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#358faf0f2229501eac848266c8d2235936ab3421" dependencies = [ "amplify", "bech32", @@ -395,7 +395,7 @@ dependencies = [ [[package]] name = "bp-seals" version = "0.11.0-beta.1" -source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#d3a57c213f34d9012101b9969cdc8b0d60a6fe60" +source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#536e773c0eea4f1706aad4418921083bc75f6833" dependencies = [ "amplify", "baid58", @@ -411,7 +411,7 @@ dependencies = [ [[package]] name = "bp-std" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#64dc394e6b4d33d4f12a9b1eccd6429d6aa5bbe4" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#358faf0f2229501eac848266c8d2235936ab3421" dependencies = [ "amplify", "bp-consensus", @@ -425,7 +425,7 @@ dependencies = [ [[package]] name = "bp-util" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#6418b4eaef62b78c092714582818046b6e2a2d03" +source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#212490ac58970e703820d63bbdc59005a40f6987" dependencies = [ "amplify", "base64", @@ -447,7 +447,7 @@ dependencies = [ [[package]] name = "bp-wallet" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#6418b4eaef62b78c092714582818046b6e2a2d03" +source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#212490ac58970e703820d63bbdc59005a40f6987" dependencies = [ "amplify", "bp-esplora", @@ -538,7 +538,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", ] [[package]] @@ -667,7 +667,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.41", + "syn 2.0.42", ] [[package]] @@ -678,7 +678,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 2.0.41", + "syn 2.0.42", ] [[package]] @@ -694,7 +694,7 @@ dependencies = [ [[package]] name = "descriptors" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#64dc394e6b4d33d4f12a9b1eccd6429d6aa5bbe4" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#358faf0f2229501eac848266c8d2235936ab3421" dependencies = [ "amplify", "bp-derive", @@ -834,36 +834,36 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", "futures-task", @@ -1240,9 +1240,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] @@ -1255,9 +1255,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" -version = "0.10.61" +version = "0.10.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" +checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" dependencies = [ "bitflags 2.4.1", "cfg-if", @@ -1276,7 +1276,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", ] [[package]] @@ -1287,9 +1287,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.97" +version = "0.9.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" +checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" dependencies = [ "cc", "libc", @@ -1329,9 +1329,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" [[package]] name = "powerfmt" @@ -1347,9 +1347,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.70" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" dependencies = [ "unicode-ident", ] @@ -1357,7 +1357,7 @@ dependencies = [ [[package]] name = "psbt" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#64dc394e6b4d33d4f12a9b1eccd6429d6aa5bbe4" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#358faf0f2229501eac848266c8d2235936ab3421" dependencies = [ "amplify", "base64", @@ -1461,9 +1461,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.22" +version = "0.11.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" dependencies = [ "base64", "bytes", @@ -1521,7 +1521,7 @@ dependencies = [ [[package]] name = "rgb-invoice" version = "0.11.0-beta.2" -source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#f73f03bf8dca1953ae79d852e44f34255f876c7c" +source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#a251ad3ce06239629697665369c9a6070b02dffd" dependencies = [ "amplify", "baid58", @@ -1588,7 +1588,7 @@ dependencies = [ [[package]] name = "rgb-std" version = "0.11.0-beta.2" -source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#f73f03bf8dca1953ae79d852e44f34255f876c7c" +source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#a251ad3ce06239629697665369c9a6070b02dffd" dependencies = [ "amplify", "baid58", @@ -1830,7 +1830,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", ] [[package]] @@ -1846,9 +1846,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ "serde", ] @@ -1901,14 +1901,14 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", ] [[package]] name = "serde_yaml" -version = "0.9.27" +version = "0.9.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" +checksum = "a15e0ef66bf939a7c890a0bf6d5a733c70202225f9888a89ed5c62298b019129" dependencies = [ "indexmap 2.1.0", "itoa", @@ -2055,9 +2055,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.41" +version = "2.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" +checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8" dependencies = [ "proc-macro2", "quote", @@ -2124,14 +2124,14 @@ checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", ] [[package]] name = "time" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" dependencies = [ "deranged", "itoa", @@ -2149,9 +2149,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" dependencies = [ "time-core", ] @@ -2173,9 +2173,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.0" +version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ "backtrace", "bytes", @@ -2410,7 +2410,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", "wasm-bindgen-shared", ] @@ -2444,7 +2444,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2477,7 +2477,7 @@ checksum = "794645f5408c9a039fd09f4d113cdfb2e7eba5ff1956b07bcf701cf4b394fe89" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", ] [[package]] @@ -2670,9 +2670,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.28" +version = "0.5.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c830786f7720c2fd27a1a0e27a709dbd3c4d009b56d098fc742d4f4eab91fe2" +checksum = "9b5c3db89721d50d0e2a673f5043fc4722f76dcc352d7b1ab8b8288bed4ed2c5" dependencies = [ "memchr", ] diff --git a/cli/src/command.rs b/cli/src/command.rs index 44477d8..bcd0aa4 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -583,7 +583,6 @@ impl Exec for RgbArgs { transfer.save(out_file)?; let ver = if *v2 { PsbtVer::V2 } else { PsbtVer::V0 }; - eprintln!("{}", serde_yaml::to_string(&psbt).unwrap()); match psbt_file { Some(file_name) => { let mut psbt_file = File::create(file_name)?; @@ -657,6 +656,11 @@ impl Exec for RgbArgs { serde_yaml::to_string(suppl)?, )?; } + let tags = runtime.contract_asset_tags(id)?; + fs::write( + format!("{root_dir}/stash/geneses/{id}.tags.yaml"), + serde_yaml::to_string(tags)?, + )?; } for id in runtime.bundle_ids()? { fs::write( @@ -666,8 +670,8 @@ impl Exec for RgbArgs { } for id in runtime.witness_ids()? { fs::write( - format!("{root_dir}/stash/anchors/{id}.yaml"), - serde_yaml::to_string(runtime.anchor(id)?)?, + format!("{root_dir}/stash/anchors/{id}.debug"), + format!("{:#?}", runtime.anchor(id)?), )?; } for id in runtime.extension_ids()? { diff --git a/src/pay.rs b/src/pay.rs index b0712f2..92ae3e1 100644 --- a/src/pay.rs +++ b/src/pay.rs @@ -249,6 +249,9 @@ impl Runtime { output.set_opret_host().expect("just created"); } + psbt.sort_outputs_by(|output| !output.is_tapret_host() && !output.is_opret_host()) + .expect("psbt must be modifiable at this stage"); + psbt.complete_construction(); psbt.rgb_embed(batch)?; Ok((psbt, meta)) } @@ -261,6 +264,8 @@ impl Runtime { ) -> Result, CompletionError> { let contract_id = invoice.contract.ok_or(CompletionError::NoContract)?; + let fascia = psbt.rgb_commit()?; + let beneficiary = match invoice.beneficiary { Beneficiary::WitnessVoutBitcoin(addr) => { let s = addr.script_pubkey(); @@ -276,7 +281,6 @@ impl Runtime { Beneficiary::BlindedSeal(seal) => BuilderSeal::Concealed(seal), }; - let fascia = psbt.rgb_commit()?; self.stock_mut().consume(fascia)?; let transfer = self.stock().transfer(contract_id, [beneficiary])?; From d47871ff8ad2faf92ffe4e1228658efe5bb2b1e3 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Mon, 25 Dec 2023 01:55:49 +0100 Subject: [PATCH 14/27] cli: implement resolver --- Cargo.lock | 17 +++++++++-------- Cargo.toml | 2 +- cli/src/command.rs | 6 +++--- cli/src/resolver.rs | 15 ++------------- src/runtime.rs | 3 +++ 5 files changed, 18 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 89a4231..ef13097 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -354,7 +354,7 @@ dependencies = [ [[package]] name = "bp-derive" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#358faf0f2229501eac848266c8d2235936ab3421" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#6649ca23a397d8197d13046f429dfbc0c5a9bdfb" dependencies = [ "amplify", "bitcoin_hashes", @@ -368,12 +368,13 @@ dependencies = [ [[package]] name = "bp-esplora" version = "0.11.0-beta.1" -source = "git+https://github.com/BP-WG/bp-esplora-client?branch=master#a963c4bb6a86601e3a767f1a48eae58c041ba026" +source = "git+https://github.com/BP-WG/bp-esplora-client?branch=master#bc5ff6bdd74214f877dcb9639ec33a1f17e13079" dependencies = [ "amplify", "bp-std", "log", "reqwest", + "rgb-std", "serde", "serde_with", "sha2", @@ -383,7 +384,7 @@ dependencies = [ [[package]] name = "bp-invoice" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#358faf0f2229501eac848266c8d2235936ab3421" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#6649ca23a397d8197d13046f429dfbc0c5a9bdfb" dependencies = [ "amplify", "bech32", @@ -411,7 +412,7 @@ dependencies = [ [[package]] name = "bp-std" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#358faf0f2229501eac848266c8d2235936ab3421" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#6649ca23a397d8197d13046f429dfbc0c5a9bdfb" dependencies = [ "amplify", "bp-consensus", @@ -425,7 +426,7 @@ dependencies = [ [[package]] name = "bp-util" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#212490ac58970e703820d63bbdc59005a40f6987" +source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#82e318df91cddf0a61155c926dd27b4e92d78dab" dependencies = [ "amplify", "base64", @@ -447,7 +448,7 @@ dependencies = [ [[package]] name = "bp-wallet" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#212490ac58970e703820d63bbdc59005a40f6987" +source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#82e318df91cddf0a61155c926dd27b4e92d78dab" dependencies = [ "amplify", "bp-esplora", @@ -694,7 +695,7 @@ dependencies = [ [[package]] name = "descriptors" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#358faf0f2229501eac848266c8d2235936ab3421" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#6649ca23a397d8197d13046f429dfbc0c5a9bdfb" dependencies = [ "amplify", "bp-derive", @@ -1357,7 +1358,7 @@ dependencies = [ [[package]] name = "psbt" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#358faf0f2229501eac848266c8d2235936ab3421" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#6649ca23a397d8197d13046f429dfbc0c5a9bdfb" dependencies = [ "amplify", "base64", diff --git a/Cargo.toml b/Cargo.toml index 3d06a57..5ee2c25 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,7 +69,7 @@ strict_types = { workspace = true } bp-core = { workspace = true } bp-std = { workspace = true } bp-wallet = { workspace = true, features = ["fs"] } -bp-esplora = { workspace = true, optional = true } +bp-esplora = { workspace = true, features = ["rgb"], optional = true } descriptors = { workspace = true } rgb-std = { workspace = true } rgb-psbt = { workspace = true } diff --git a/cli/src/command.rs b/cli/src/command.rs index bcd0aa4..3405ddf 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -270,7 +270,7 @@ impl Exec for RgbArgs { ); } UniversalBindle::Contract(bindle) => { - let mut resolver = self.resolver(); + let mut resolver = self.resolver()?; let id = bindle.id(); let contract = bindle .unbindle() @@ -714,7 +714,7 @@ impl Exec for RgbArgs { eprintln!("Dump is successfully generated and saved to '{root_dir}'"); } Command::Validate { file } => { - let mut resolver = self.resolver(); + let mut resolver = self.resolver()?; let bindle = Bindle::::load_file(file)?; let status = match bindle .unbindle() @@ -728,7 +728,7 @@ impl Exec for RgbArgs { } Command::Accept { force, file } => { let mut runtime = self.rgb_runtime(&config)?; - let mut resolver = self.resolver(); + let mut resolver = self.resolver()?; let bindle = Bindle::::load_file(file)?; let transfer = bindle .unbindle() diff --git a/cli/src/resolver.rs b/cli/src/resolver.rs index cdc1f67..9515c44 100644 --- a/cli/src/resolver.rs +++ b/cli/src/resolver.rs @@ -43,18 +43,7 @@ impl ResolveTx for PanickingResolver { } impl RgbArgs { - pub fn resolver(&self) -> impl ResolveTx + ResolveHeight { - #[derive(Default)] - struct DumbResolver(); - impl ResolveHeight for DumbResolver { - type Error = Infallible; - fn resolve_anchor(&mut self, _: &XAnchor) -> Result { - todo!() - } - } - impl ResolveTx for DumbResolver { - fn resolve_bp_tx(&self, _: Layer1, _: Txid) -> Result { todo!() } - } - DumbResolver::default() + pub fn resolver(&self) -> Result { + esplora::Builder::new(&self.resolver.esplora).build_blocking() } } diff --git a/src/runtime.rs b/src/runtime.rs index 4b17b3f..e58e4c5 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -87,6 +87,9 @@ pub enum RuntimeError { #[from(bpwallet::LoadError)] Bp(bpwallet::RuntimeError), + #[from] + Esplora(esplora::Error), + #[from] Yaml(serde_yaml::Error), From 639d402a58331f4a9fb999896922d44288801dd1 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Mon, 25 Dec 2023 13:17:46 +0100 Subject: [PATCH 15/27] cli: remove outdated PanickingResolver --- cli/src/args.rs | 6 ++++++ cli/src/command.rs | 3 +-- cli/src/main.rs | 2 -- cli/src/resolver.rs | 49 --------------------------------------------- 4 files changed, 7 insertions(+), 53 deletions(-) delete mode 100644 cli/src/resolver.rs diff --git a/cli/src/args.rs b/cli/src/args.rs index 1e841de..306d096 100644 --- a/cli/src/args.rs +++ b/cli/src/args.rs @@ -24,6 +24,8 @@ use bp_util::{Config, DescriptorOpts}; use bpstd::XpubDerivable; use rgb_rt::{RgbDescr, Runtime, RuntimeError, TapretKey}; +use rgbstd::resolvers::ResolveHeight; +use rgbstd::validation::ResolveTx; use crate::Command; @@ -75,4 +77,8 @@ impl RgbArgs { Ok(runtime) } + + pub fn resolver(&self) -> Result { + esplora::Builder::new(&self.resolver.esplora).build_blocking() + } } diff --git a/cli/src/command.rs b/cli/src/command.rs index 3405ddf..bec3226 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -40,7 +40,6 @@ use seals::txout::{CloseMethod, ExplicitSeal}; use strict_types::encoding::{FieldName, TypeName}; use strict_types::StrictVal; -use crate::resolver::PanickingResolver; use crate::RgbArgs; // TODO: For now, serde implementation doesn't work for consignments due to @@ -495,7 +494,7 @@ impl Exec for RgbArgs { let contract = builder.issue_contract().expect("failure issuing contract"); let id = contract.contract_id(); - let mut resolver = PanickingResolver; + let mut resolver = self.resolver()?; let validated_contract = contract .validate(&mut resolver, self.general.network.is_testnet()) .map_err(|consignment| { diff --git a/cli/src/main.rs b/cli/src/main.rs index fc13ac6..47030a2 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -31,7 +31,6 @@ extern crate serde_crate as serde; mod command; mod args; -mod resolver; use std::process::ExitCode; @@ -41,7 +40,6 @@ use rgb_rt::RuntimeError; pub use crate::args::RgbArgs; pub use crate::command::Command; -pub use crate::resolver::PanickingResolver; fn main() -> ExitCode { if let Err(err) = run() { diff --git a/cli/src/resolver.rs b/cli/src/resolver.rs deleted file mode 100644 index 9515c44..0000000 --- a/cli/src/resolver.rs +++ /dev/null @@ -1,49 +0,0 @@ -// RGB smart contract wallet runtime -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2019-2023 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::convert::Infallible; - -use bpstd::{Tx, Txid}; -use rgbstd::resolvers::ResolveHeight; -use rgbstd::validation::{ResolveTx, TxResolverError}; -use rgbstd::{Layer1, WitnessAnchor, XAnchor}; - -use crate::RgbArgs; - -// TODO: Embed in contract issuance builder -pub struct PanickingResolver; -impl ResolveHeight for PanickingResolver { - type Error = Infallible; - fn resolve_anchor(&mut self, _: &XAnchor) -> Result { - unreachable!("PanickingResolver must be used only for newly issued contract validation") - } -} -impl ResolveTx for PanickingResolver { - fn resolve_bp_tx(&self, _: Layer1, _: Txid) -> Result { - unreachable!("PanickingResolver must be used only for newly issued contract validation") - } -} - -impl RgbArgs { - pub fn resolver(&self) -> Result { - esplora::Builder::new(&self.resolver.esplora).build_blocking() - } -} From 330cfd25b51ac19950c85da26b00a096071d6980 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Mon, 25 Dec 2023 14:03:52 +0100 Subject: [PATCH 16/27] wallet: make resolver to use terminal transactions from consignment --- Cargo.lock | 32 ++++++------- Cargo.toml | 2 +- cli/Cargo.toml | 1 - cli/src/args.rs | 8 ++-- cli/src/command.rs | 21 +++++---- src/lib.rs | 2 + src/resolver.rs | 115 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 147 insertions(+), 34 deletions(-) create mode 100644 src/resolver.rs diff --git a/Cargo.lock b/Cargo.lock index ef13097..43af99a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -368,13 +368,12 @@ dependencies = [ [[package]] name = "bp-esplora" version = "0.11.0-beta.1" -source = "git+https://github.com/BP-WG/bp-esplora-client?branch=master#bc5ff6bdd74214f877dcb9639ec33a1f17e13079" +source = "git+https://github.com/BP-WG/bp-esplora-client?branch=master#49d9a56638eaecd7652bc046d4550b1446daf1ec" dependencies = [ "amplify", "bp-std", "log", "reqwest", - "rgb-std", "serde", "serde_with", "sha2", @@ -539,7 +538,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.43", ] [[package]] @@ -668,7 +667,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.42", + "syn 2.0.43", ] [[package]] @@ -679,7 +678,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 2.0.42", + "syn 2.0.43", ] [[package]] @@ -1277,7 +1276,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.43", ] [[package]] @@ -1522,7 +1521,7 @@ dependencies = [ [[package]] name = "rgb-invoice" version = "0.11.0-beta.2" -source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#a251ad3ce06239629697665369c9a6070b02dffd" +source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#dbd5f511c093f11d64250363dd690ee6bf32acfa" dependencies = [ "amplify", "baid58", @@ -1589,7 +1588,7 @@ dependencies = [ [[package]] name = "rgb-std" version = "0.11.0-beta.2" -source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#a251ad3ce06239629697665369c9a6070b02dffd" +source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#dbd5f511c093f11d64250363dd690ee6bf32acfa" dependencies = [ "amplify", "baid58", @@ -1613,7 +1612,6 @@ version = "0.11.0-alpha.2" dependencies = [ "amplify", "baid58", - "bp-esplora", "bp-seals", "bp-std", "bp-util", @@ -1831,7 +1829,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.43", ] [[package]] @@ -1902,7 +1900,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.43", ] [[package]] @@ -2056,9 +2054,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.42" +version = "2.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8" +checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" dependencies = [ "proc-macro2", "quote", @@ -2125,7 +2123,7 @@ checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.43", ] [[package]] @@ -2411,7 +2409,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.43", "wasm-bindgen-shared", ] @@ -2445,7 +2443,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.43", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2478,7 +2476,7 @@ checksum = "794645f5408c9a039fd09f4d113cdfb2e7eba5ff1956b07bcf701cf4b394fe89" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.43", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 5ee2c25..3d06a57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,7 +69,7 @@ strict_types = { workspace = true } bp-core = { workspace = true } bp-std = { workspace = true } bp-wallet = { workspace = true, features = ["fs"] } -bp-esplora = { workspace = true, features = ["rgb"], optional = true } +bp-esplora = { workspace = true, optional = true } descriptors = { workspace = true } rgb-std = { workspace = true } rgb-psbt = { workspace = true } diff --git a/cli/Cargo.toml b/cli/Cargo.toml index a2e7e71..ab4a689 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -24,7 +24,6 @@ commit_verify = "0.11.0-beta.1" bp-seals = { workspace = true } bp-std = { workspace = true, features = ["serde"] } bp-wallet = { workspace = true } -bp-esplora = { workspace = true } bp-util = { workspace = true } psbt = { workspace = true } rgb-std = { workspace = true, features = ["serde"] } diff --git a/cli/src/args.rs b/cli/src/args.rs index 306d096..9c8bbb2 100644 --- a/cli/src/args.rs +++ b/cli/src/args.rs @@ -23,9 +23,7 @@ use bp_util::{Config, DescriptorOpts}; use bpstd::XpubDerivable; -use rgb_rt::{RgbDescr, Runtime, RuntimeError, TapretKey}; -use rgbstd::resolvers::ResolveHeight; -use rgbstd::validation::ResolveTx; +use rgb_rt::{Resolver, ResolverError, RgbDescr, Runtime, RuntimeError, TapretKey}; use crate::Command; @@ -78,7 +76,7 @@ impl RgbArgs { Ok(runtime) } - pub fn resolver(&self) -> Result { - esplora::Builder::new(&self.resolver.esplora).build_blocking() + pub fn resolver(&self) -> Result { + Resolver::new(&self.resolver.esplora) } } diff --git a/cli/src/command.rs b/cli/src/command.rs index bec3226..04a80db 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -715,22 +715,23 @@ impl Exec for RgbArgs { Command::Validate { file } => { let mut resolver = self.resolver()?; let bindle = Bindle::::load_file(file)?; - let status = match bindle - .unbindle() - .validate(&mut resolver, self.general.network.is_testnet()) - { - Ok(consignment) => consignment.into_validation_status(), - Err(consignment) => consignment.into_validation_status(), - } - .expect("just validated"); + let consignment = bindle.unbindle(); + resolver.add_terminals(&consignment); + let status = + match consignment.validate(&mut resolver, self.general.network.is_testnet()) { + Ok(consignment) => consignment.into_validation_status(), + Err(consignment) => consignment.into_validation_status(), + } + .expect("just validated"); eprintln!("{status}"); } Command::Accept { force, file } => { let mut runtime = self.rgb_runtime(&config)?; let mut resolver = self.resolver()?; let bindle = Bindle::::load_file(file)?; - let transfer = bindle - .unbindle() + let consignment = bindle.unbindle(); + resolver.add_terminals(&consignment); + let transfer = consignment .validate(&mut resolver, self.general.network.is_testnet()) .unwrap_or_else(|c| c); eprintln!("{}", transfer.validation_status().expect("just validated")); diff --git a/src/lib.rs b/src/lib.rs index 9aa6698..665f4e4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,7 +31,9 @@ extern crate serde_crate as serde; mod runtime; mod descriptor; mod pay; +mod resolver; pub use descriptor::{DescriptorRgb, RgbDescr, RgbKeychain, TapretKey}; pub use pay::{CompletionError, CompositionError, PayError, TransferParams}; +pub use resolver::{AnchorResolverError, Resolver, ResolverError}; pub use runtime::{Runtime, RuntimeError}; diff --git a/src/resolver.rs b/src/resolver.rs new file mode 100644 index 0000000..5b9f9ae --- /dev/null +++ b/src/resolver.rs @@ -0,0 +1,115 @@ +// RGB smart contracts for Bitcoin & Lightning +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2019-2023 by +// Dr Maxim Orlovsky +// +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::collections::HashMap; + +use bpstd::{Tx, Txid}; +pub use esplora::Error as ResolverError; +use rgbstd::containers::Consignment; +use rgbstd::resolvers::ResolveHeight; +use rgbstd::validation::{ResolveTx, TxResolverError}; +use rgbstd::{Layer1, WitnessAnchor, WitnessId, WitnessOrd, WitnessPos, XAnchor}; + +pub struct Resolver { + esplora_client: esplora::BlockingClient, + terminal_txes: HashMap, +} + +#[derive(Debug, Display, Error, From)] +#[display(doc_comments)] +pub enum AnchorResolverError { + #[from] + #[display(inner)] + Error(esplora::Error), + + /// invalid anchor {0} + InvalidAnchor(String), +} + +impl Resolver { + pub fn new(url: &str) -> Result { + let esplora_client = esplora::Builder::new(url).build_blocking()?; + Ok(Self { + esplora_client, + terminal_txes: none!(), + }) + } + + pub fn add_terminals(&mut self, consignment: &Consignment) { + self.terminal_txes.extend( + consignment + .terminals + .values() + .filter_map(|t| t.tx.as_ref()) + .map(|tx| (tx.txid(), tx.clone())), + ); + } +} + +impl ResolveHeight for Resolver { + type Error = AnchorResolverError; + + fn resolve_anchor(&mut self, anchor: &XAnchor) -> Result { + let XAnchor::Bitcoin(anchor) = anchor else { + panic!("Liquid is not yet supported") + }; + let txid = anchor + .txid() + .ok_or(AnchorResolverError::InvalidAnchor(format!("{:#?}", anchor)))?; + + if self.terminal_txes.contains_key(&txid) { + return Ok(WitnessAnchor { + witness_ord: WitnessOrd::OffChain, + witness_id: WitnessId::Bitcoin(txid), + }); + } + + let status = self.esplora_client.tx_status(&txid)?; + let ord = match status + .block_height + .and_then(|h| status.block_time.map(|t| (h, t))) + { + Some((h, t)) => WitnessOrd::OnChain( + WitnessPos::new(h, t as i64).ok_or(esplora::Error::InvalidServerData)?, + ), + None => WitnessOrd::OffChain, + }; + Ok(WitnessAnchor { + witness_ord: ord, + witness_id: WitnessId::Bitcoin(txid), + }) + } +} + +impl ResolveTx for Resolver { + fn resolve_bp_tx(&self, layer1: Layer1, txid: Txid) -> Result { + assert_eq!(layer1, Layer1::Bitcoin, "Liquid is not yet supported"); + + if let Some(tx) = self.terminal_txes.get(&txid) { + return Ok(tx.clone()); + } + + self.esplora_client + .tx(&txid) + .map_err(|err| TxResolverError::Other(txid, err.to_string()))? + .ok_or(TxResolverError::Unknown(txid)) + } +} From 5e80961c1586bd75afa4ff2e487efe5f8a80ad59 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Mon, 25 Dec 2023 17:21:51 +0100 Subject: [PATCH 17/27] store and apply tapret tweaks --- Cargo.lock | 15 +++++++------ Cargo.toml | 1 + src/descriptor.rs | 55 +++++++++++++++++++++++++++++++++++++++++------ src/lib.rs | 2 +- src/pay.rs | 44 +++++++++++++++++++++++++++---------- 5 files changed, 91 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 43af99a..c1eea72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -354,7 +354,7 @@ dependencies = [ [[package]] name = "bp-derive" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#6649ca23a397d8197d13046f429dfbc0c5a9bdfb" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#5d093349fd5f874fdd68d1181ad97256716c8671" dependencies = [ "amplify", "bitcoin_hashes", @@ -383,7 +383,7 @@ dependencies = [ [[package]] name = "bp-invoice" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#6649ca23a397d8197d13046f429dfbc0c5a9bdfb" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#5d093349fd5f874fdd68d1181ad97256716c8671" dependencies = [ "amplify", "bech32", @@ -411,7 +411,7 @@ dependencies = [ [[package]] name = "bp-std" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#6649ca23a397d8197d13046f429dfbc0c5a9bdfb" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#5d093349fd5f874fdd68d1181ad97256716c8671" dependencies = [ "amplify", "bp-consensus", @@ -425,7 +425,7 @@ dependencies = [ [[package]] name = "bp-util" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#82e318df91cddf0a61155c926dd27b4e92d78dab" +source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#cb25ca35b8b0b797eb57b12a6565fd948fdf6d2b" dependencies = [ "amplify", "base64", @@ -447,7 +447,7 @@ dependencies = [ [[package]] name = "bp-wallet" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#82e318df91cddf0a61155c926dd27b4e92d78dab" +source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#cb25ca35b8b0b797eb57b12a6565fd948fdf6d2b" dependencies = [ "amplify", "bp-esplora", @@ -694,7 +694,7 @@ dependencies = [ [[package]] name = "descriptors" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#6649ca23a397d8197d13046f429dfbc0c5a9bdfb" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#5d093349fd5f874fdd68d1181ad97256716c8671" dependencies = [ "amplify", "bp-derive", @@ -1357,7 +1357,7 @@ dependencies = [ [[package]] name = "psbt" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#6649ca23a397d8197d13046f429dfbc0c5a9bdfb" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#5d093349fd5f874fdd68d1181ad97256716c8671" dependencies = [ "amplify", "base64", @@ -1574,6 +1574,7 @@ dependencies = [ "bp-std", "bp-wallet", "chrono", + "commit_verify", "descriptors", "indexmap 2.1.0", "log", diff --git a/Cargo.toml b/Cargo.toml index 3d06a57..4808c05 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,6 +65,7 @@ name = "rgb_rt" [dependencies] amplify = { workspace = true } baid58 = { workspace = true } +commit_verify = { workspace = true } strict_types = { workspace = true } bp-core = { workspace = true } bp-std = { workspace = true } diff --git a/src/descriptor.rs b/src/descriptor.rs index 9b4e50b..dc0e8cd 100644 --- a/src/descriptor.rs +++ b/src/descriptor.rs @@ -19,7 +19,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::{BTreeSet, HashMap}; use std::str::FromStr; use std::{iter, vec}; @@ -29,14 +29,24 @@ use bp::dbc::Method; use bp::seals::txout::CloseMethod; use bpstd::{ CompressedPk, Derive, DeriveCompr, DeriveSet, DeriveXOnly, DerivedScript, Idx, IdxBase, - IndexError, IndexParseError, KeyOrigin, Keychain, NormalIndex, TapDerivation, Terminal, - XOnlyPk, XpubDerivable, XpubSpec, + IndexError, IndexParseError, KeyOrigin, Keychain, NormalIndex, TapDerivation, TapScript, + TapTree, Terminal, XOnlyPk, XpubDerivable, XpubSpec, }; +use commit_verify::CommitVerify; use descriptors::{Descriptor, SpkClass, StdDescr, TrKey}; use indexmap::IndexMap; +#[derive(Copy, Clone, Eq, PartialEq, Debug, Display, Error)] +#[display("terminal derivation /10/{0} already has a taptweak assigned")] +pub struct TapTweakAlreadyAssigned(pub NormalIndex); + pub trait DescriptorRgb: Descriptor { fn seal_close_method(&self) -> CloseMethod; + fn add_tapret_tweak( + &mut self, + index: NormalIndex, + tweak: TapretCommitment, + ) -> Result<(), TapTweakAlreadyAssigned>; } #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] @@ -96,12 +106,13 @@ impl From for Keychain { fn from(keychain: RgbKeychain) -> Self { Keychain::from(keychain as u8) } } -#[derive(Clone, Eq, PartialEq, Hash, Debug)] +#[derive(Clone, Eq, PartialEq, Debug)] #[derive(Serialize, Deserialize)] #[serde(crate = "serde_crate", rename_all = "camelCase")] pub struct TapretKey { pub internal_key: K, - pub tweaks: BTreeMap, + // TODO: Allow multiple tweaks per index by introducing derivation using new Terminal trait + pub tweaks: HashMap, } impl TapretKey { @@ -127,8 +138,16 @@ impl Derive for TapretKey { } fn derive(&self, change: impl Into, index: impl Into) -> DerivedScript { - // TODO: Apply tweaks + let change = change.into(); + let index = index.into(); let internal_key = self.internal_key.derive(change, index); + if change.into_inner() == RgbKeychain::Tapret as u8 { + if let Some(tweak) = self.tweaks.get(&index) { + let script_commitment = TapScript::commit(tweak); + let tap_tree = TapTree::with_single_leaf(script_commitment); + return DerivedScript::TaprootScript(internal_key.into(), tap_tree); + } + } DerivedScript::TaprootKeyOnly(internal_key.into()) } } @@ -182,9 +201,21 @@ impl Descriptor for TapretKey { impl DescriptorRgb for TapretKey { fn seal_close_method(&self) -> CloseMethod { CloseMethod::TapretFirst } + + fn add_tapret_tweak( + &mut self, + index: NormalIndex, + tweak: TapretCommitment, + ) -> Result<(), TapTweakAlreadyAssigned> { + if self.tweaks.contains_key(&index) { + return Err(TapTweakAlreadyAssigned(index)); + } + self.tweaks.insert(index, tweak); + Ok(()) + } } -#[derive(Clone, Eq, PartialEq, Hash, Debug, From)] +#[derive(Clone, Eq, PartialEq, Debug, From)] #[derive(Serialize, Deserialize)] #[serde( crate = "serde_crate", @@ -274,6 +305,16 @@ where Self: Derive RgbDescr::TapretKey(d) => d.seal_close_method(), } } + + fn add_tapret_tweak( + &mut self, + index: NormalIndex, + tweak: TapretCommitment, + ) -> Result<(), TapTweakAlreadyAssigned> { + match self { + RgbDescr::TapretKey(d) => d.add_tapret_tweak(index, tweak), + } + } } impl From for RgbDescr { diff --git a/src/lib.rs b/src/lib.rs index 665f4e4..8f9723c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,7 +33,7 @@ mod descriptor; mod pay; mod resolver; -pub use descriptor::{DescriptorRgb, RgbDescr, RgbKeychain, TapretKey}; +pub use descriptor::{DescriptorRgb, RgbDescr, RgbKeychain, TapTweakAlreadyAssigned, TapretKey}; pub use pay::{CompletionError, CompositionError, PayError, TransferParams}; pub use resolver::{AnchorResolverError, Resolver, ResolverError}; pub use runtime::{Runtime, RuntimeError}; diff --git a/src/pay.rs b/src/pay.rs index 92ae3e1..afe4426 100644 --- a/src/pay.rs +++ b/src/pay.rs @@ -21,10 +21,11 @@ use std::convert::Infallible; +use bp::dbc::tapret::TapretProof; use bp::seals::txout::CloseMethod; use bp::{Outpoint, Sats, ScriptPubkey, Vout}; use bpwallet::{Beneficiary as BpBeneficiary, ConstructionError, PsbtMeta, TxParams}; -use psbt::{CommitError, EmbedError, Psbt, RgbPsbt}; +use psbt::{CommitError, EmbedError, Psbt, RgbPsbt, TapretKeyError}; use rgbstd::containers::{Bindle, BuilderSeal, Transfer}; use rgbstd::interface::{ContractError, FilterIncludeAll}; use rgbstd::invoice::{Beneficiary, InvoiceState, RgbInvoice}; @@ -33,7 +34,7 @@ use rgbstd::persistence::{ }; use rgbstd::XSeal; -use crate::{RgbKeychain, Runtime}; +use crate::{DescriptorRgb, RgbKeychain, Runtime, TapTweakAlreadyAssigned}; #[derive(Debug, Display, Error, From)] #[display(inner)] @@ -111,6 +112,17 @@ pub enum CompletionError { /// the provided PSBT doesn't pay any sats to the RGB beneficiary address. NoBeneficiaryOutput, + /// the provided PSBT has conflicting descriptor in the taptweak output. + InconclusiveDerivation, + + #[from] + #[display(inner)] + MultipleTweaks(TapTweakAlreadyAssigned), + + #[from] + #[display(inner)] + TapretKey(TapretKeyError), + #[from] #[display(inner)] Inventory(InventoryError), @@ -158,7 +170,7 @@ impl Runtime { &mut self, invoice: &RgbInvoice, method: CloseMethod, - params: TransferParams, + mut params: TransferParams, ) -> Result<(Psbt, PsbtMeta), CompositionError> { let contract_id = invoice.contract.ok_or(CompositionError::NoContract)?; @@ -207,20 +219,21 @@ impl Runtime { } _ => return Err(CompositionError::Unsupported), }; - let beneficiary = match invoice.beneficiary { - Beneficiary::BlindedSeal(_) => BpBeneficiary::with_max( - self.wallet_mut() - .next_address(RgbKeychain::for_method(method), true), - ), - Beneficiary::WitnessVoutBitcoin(addr) => BpBeneficiary::new(addr, params.min_amount), + let beneficiaries = match invoice.beneficiary { + Beneficiary::BlindedSeal(_) => vec![], + Beneficiary::WitnessVoutBitcoin(addr) => { + vec![BpBeneficiary::new(addr, params.min_amount)] + } }; let outpoints = outputs .iter() .filter_map(|o| o.reduce_to_bp()) .map(|o| Outpoint::new(o.txid, o.vout)); + params.tx.change_keychain = RgbKeychain::for_method(method).into(); let (mut psbt, meta) = - self.wallet_mut() - .construct_psbt(outpoints, &[beneficiary], params.tx)?; + self.wallet() + .construct_psbt(outpoints, &beneficiaries, params.tx)?; + // TODO: Increase change index let (beneficiary_vout, beneficiary_script) = match invoice.beneficiary { Beneficiary::WitnessVoutBitcoin(addr) => { @@ -242,6 +255,7 @@ impl Runtime { .outputs_mut() .find(|o| o.script.is_p2tr() && o.script != beneficiary_script) .ok_or(CompositionError::TapretRequired)?; + // TODO: Add descriptor id to the tapret host data output.set_tapret_host().expect("just created"); } if methods.has_opret_first() { @@ -265,6 +279,14 @@ impl Runtime { let contract_id = invoice.contract.ok_or(CompletionError::NoContract)?; let fascia = psbt.rgb_commit()?; + if let Some(output) = psbt.dbc_output::() { + let terminal = output + .terminal_derivation() + .ok_or(CompletionError::InconclusiveDerivation)?; + let tapret_commitment = output.tapret_commitment()?; + self.wallet_mut() + .add_tapret_tweak(terminal.index, tapret_commitment)?; + } let beneficiary = match invoice.beneficiary { Beneficiary::WitnessVoutBitcoin(addr) => { From 44826ac937709376777cd2ae7a3015f2c4544e5d Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Mon, 25 Dec 2023 18:00:24 +0100 Subject: [PATCH 18/27] pay: add unsigned transaction to consignment terminals --- Cargo.lock | 8 ++++---- src/pay.rs | 29 ++++++++++++++++++++++------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c1eea72..e32d701 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -425,7 +425,7 @@ dependencies = [ [[package]] name = "bp-util" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#cb25ca35b8b0b797eb57b12a6565fd948fdf6d2b" +source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#e197ebf9d9d84230715d1c96521ecb8aed5cb92c" dependencies = [ "amplify", "base64", @@ -447,7 +447,7 @@ dependencies = [ [[package]] name = "bp-wallet" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#cb25ca35b8b0b797eb57b12a6565fd948fdf6d2b" +source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#e197ebf9d9d84230715d1c96521ecb8aed5cb92c" dependencies = [ "amplify", "bp-esplora", @@ -1521,7 +1521,7 @@ dependencies = [ [[package]] name = "rgb-invoice" version = "0.11.0-beta.2" -source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#dbd5f511c093f11d64250363dd690ee6bf32acfa" +source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#d8a1161b68f4294797ce0721ebd87469d40b3198" dependencies = [ "amplify", "baid58", @@ -1589,7 +1589,7 @@ dependencies = [ [[package]] name = "rgb-std" version = "0.11.0-beta.2" -source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#dbd5f511c093f11d64250363dd690ee6bf32acfa" +source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#d8a1161b68f4294797ce0721ebd87469d40b3198" dependencies = [ "amplify", "baid58", diff --git a/src/pay.rs b/src/pay.rs index afe4426..19fae13 100644 --- a/src/pay.rs +++ b/src/pay.rs @@ -21,12 +21,13 @@ use std::convert::Infallible; +use amplify::confinement::Confined; use bp::dbc::tapret::TapretProof; use bp::seals::txout::CloseMethod; use bp::{Outpoint, Sats, ScriptPubkey, Vout}; use bpwallet::{Beneficiary as BpBeneficiary, ConstructionError, PsbtMeta, TxParams}; use psbt::{CommitError, EmbedError, Psbt, RgbPsbt, TapretKeyError}; -use rgbstd::containers::{Bindle, BuilderSeal, Transfer}; +use rgbstd::containers::{Bindle, BuilderSeal, TerminalSeal, Transfer, VoutSeal}; use rgbstd::interface::{ContractError, FilterIncludeAll}; use rgbstd::invoice::{Beneficiary, InvoiceState, RgbInvoice}; use rgbstd::persistence::{ @@ -288,23 +289,37 @@ impl Runtime { .add_tapret_tweak(terminal.index, tapret_commitment)?; } - let beneficiary = match invoice.beneficiary { + let (beneficiary, terminal_seal) = match invoice.beneficiary { Beneficiary::WitnessVoutBitcoin(addr) => { let s = addr.script_pubkey(); let vout = psbt .outputs() .position(|output| output.script == s) .ok_or(CompletionError::NoBeneficiaryOutput)?; + let vout = Vout::from_u32(vout as u32); let witness_txid = psbt.txid(); - BuilderSeal::Revealed(XSeal::Bitcoin( - Outpoint::new(witness_txid, Vout::from_u32(vout as u32)).into(), - )) + let method = self.wallet().seal_close_method(); + let seal = XSeal::Bitcoin(Outpoint::new(witness_txid, vout).into()); + ( + BuilderSeal::Revealed(seal), + TerminalSeal::BitcoinWitnessVout(VoutSeal::new(method, vout)), + ) + } + Beneficiary::BlindedSeal(seal) => { + (BuilderSeal::Concealed(seal), TerminalSeal::ConcealedUtxo(seal)) } - Beneficiary::BlindedSeal(seal) => BuilderSeal::Concealed(seal), }; self.stock_mut().consume(fascia)?; - let transfer = self.stock().transfer(contract_id, [beneficiary])?; + let mut transfer = self.stock().transfer(contract_id, [beneficiary])?; + let mut terminals = transfer.terminals.to_inner(); + for terminal in terminals.values_mut() { + if terminal.seals.contains(&terminal_seal) { + // TODO: Store unsigned tx + terminal.tx = Some(psbt.to_unsigned_tx().into()) + } + } + transfer.terminals = Confined::from_collection_unsafe(terminals); Ok(transfer) } From fa311655c6ada00c80d3f8322f9f440c5d7e843d Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Mon, 25 Dec 2023 18:10:31 +0100 Subject: [PATCH 19/27] cli: add addicional Prepare and Consign commands --- cli/src/command.rs | 89 ++++++++++++++++++++++++++++++++++++++++++++-- src/runtime.rs | 3 ++ 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/cli/src/command.rs b/cli/src/command.rs index 04a80db..574ae75 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -27,7 +27,7 @@ use std::str::FromStr; use amplify::confinement::U16; use bp_util::{Config, Exec}; use bpstd::{Sats, Txid}; -use psbt::PsbtVer; +use psbt::{Psbt, PsbtVer}; use rgb_rt::{DescriptorRgb, RgbDescr, RgbKeychain, RuntimeError, TransferParams}; use rgbstd::containers::{Bindle, Transfer, UniversalBindle}; use rgbstd::contract::{ContractId, GenesisSeal, GraphSeal, StateType}; @@ -136,6 +136,47 @@ 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`. + #[display("prepare")] + Prepare { + /// Encode PSBT as V2 + #[clap(short = '2')] + v2: bool, + + /// Method for single-use-seals + #[clap(long, default_value = "tapret1st")] + method: CloseMethod, + + /// Amount of satoshis which should be paid to the address-based + /// beneficiary + #[clap(long, default_value = "2000")] + sats: Sats, + + /// Invoice data + invoice: RgbInvoice, + + /// Fee + fee: Sats, + + /// Name of PSBT file to save. If not given, prints PSBT to STDOUT + psbt: Option, + }, + + /// Prepare consignment for transferring RGB assets. In the most of cases + /// you need to use `transfer` command instead of `prepare` and `consign`. + #[display("prepare")] + Consign { + /// Invoice data + invoice: RgbInvoice, + + /// Name of PSBT file containing prepared transfer data + psbt: PathBuf, + + /// File for generated transfer consignment + consignment: PathBuf, + }, + /// Transfer RGB assets #[display("transfer")] Transfer { @@ -561,7 +602,49 @@ impl Exec for RgbArgs { }; println!("{invoice}"); } - #[allow(unused_variables)] + Command::Prepare { + v2, + method, + invoice, + fee, + sats, + psbt: psbt_file, + } => { + let mut runtime = self.rgb_runtime(&config)?; + // TODO: Support lock time and RBFs + let params = TransferParams::with(*fee, *sats); + + let (psbt, _) = runtime + .construct_psbt(invoice, *method, params) + .map_err(|err| err.to_string())?; + + let ver = if *v2 { PsbtVer::V2 } else { PsbtVer::V0 }; + match psbt_file { + Some(file_name) => { + let mut psbt_file = File::create(file_name)?; + psbt.encode(ver, &mut psbt_file)?; + } + None => match ver { + PsbtVer::V0 => println!("{psbt}"), + PsbtVer::V2 => println!("{psbt:#}"), + }, + } + } + Command::Consign { + invoice, + psbt: psbt_name, + consignment: out_file, + } => { + let mut runtime = self.rgb_runtime(&config)?; + let mut psbt_file = File::open(psbt_name)?; + let mut psbt = Psbt::decode(&mut psbt_file)?; + let transfer = runtime + .transfer(invoice, &mut psbt) + .map_err(|err| err.to_string())?; + let mut psbt_file = File::create(psbt_name)?; + psbt.encode(psbt.version, &mut psbt_file)?; + transfer.save(out_file)?; + } Command::Transfer { v2, method, @@ -575,7 +658,7 @@ impl Exec for RgbArgs { // TODO: Support lock time and RBFs let params = TransferParams::with(*fee, *sats); - let (psbt, meta, transfer) = runtime + let (psbt, _, transfer) = runtime .pay(invoice, *method, params) .map_err(|err| err.to_string())?; diff --git a/src/runtime.rs b/src/runtime.rs index e58e4c5..92bf31e 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -65,6 +65,9 @@ pub enum RuntimeError { #[from] Builder(BuilderError), + #[from] + PsbtDecode(psbt::DecodeError), + /// wallet with id '{0}' is not known to the system. #[display(doc_comments)] WalletUnknown(Ident), From acf9369c190bccb7e3b957f19d434d4257cd9d08 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Mon, 25 Dec 2023 19:35:46 +0100 Subject: [PATCH 20/27] cli: add armor command --- cli/src/command.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/cli/src/command.rs b/cli/src/command.rs index 574ae75..290a588 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -100,6 +100,15 @@ pub enum Command { file: Option, }, + /// Convert binary RGB file into a text armored version + #[display("convert")] + Armor { + /// File with RGB data + /// + /// If not provided, assumes `-a` and reads the data from STDIN + file: PathBuf, + }, + /// Reports information about state of a contract #[display("state")] State { @@ -347,6 +356,11 @@ impl Exec for RgbArgs { } } + Command::Armor { file } => { + let bindle = UniversalBindle::load_file(file)?; + println!("{bindle}"); + } + Command::State { contract_id, iface } => { let mut runtime = self.rgb_runtime(&config)?; let bp_runtime = self.bp_runtime::(&config)?; From f0b54903b15ee058446ef7f91e8c14f281de94be Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Mon, 25 Dec 2023 20:00:39 +0100 Subject: [PATCH 21/27] chore: update deps with fix on tapret validation --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e32d701..644173b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -310,7 +310,7 @@ dependencies = [ [[package]] name = "bp-consensus" version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#536e773c0eea4f1706aad4418921083bc75f6833" +source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#241eee511a7c584716b31eb5d897a94a9d8ce56e" dependencies = [ "amplify", "chrono", @@ -324,7 +324,7 @@ dependencies = [ [[package]] name = "bp-core" version = "0.11.0-beta.1" -source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#536e773c0eea4f1706aad4418921083bc75f6833" +source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#241eee511a7c584716b31eb5d897a94a9d8ce56e" dependencies = [ "amplify", "bp-consensus", @@ -340,7 +340,7 @@ dependencies = [ [[package]] name = "bp-dbc" version = "0.11.0-beta.1" -source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#536e773c0eea4f1706aad4418921083bc75f6833" +source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#241eee511a7c584716b31eb5d897a94a9d8ce56e" dependencies = [ "amplify", "base85", @@ -395,7 +395,7 @@ dependencies = [ [[package]] name = "bp-seals" version = "0.11.0-beta.1" -source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#536e773c0eea4f1706aad4418921083bc75f6833" +source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#241eee511a7c584716b31eb5d897a94a9d8ce56e" dependencies = [ "amplify", "baid58", @@ -556,7 +556,7 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "commit_encoding_derive" version = "0.10.0" -source = "git+https://github.com/LNP-BP/client_side_validation?branch=v0.11#6b634bccd96d2cb857a44ae48417f8e35ecb3de7" +source = "git+https://github.com/LNP-BP/client_side_validation?branch=v0.11#2ec2bca60028d12af5657580a350e292f9ff13eb" dependencies = [ "amplify", "amplify_syn", @@ -568,7 +568,7 @@ dependencies = [ [[package]] name = "commit_verify" version = "0.11.0-beta.1" -source = "git+https://github.com/LNP-BP/client_side_validation?branch=v0.11#6b634bccd96d2cb857a44ae48417f8e35ecb3de7" +source = "git+https://github.com/LNP-BP/client_side_validation?branch=v0.11#2ec2bca60028d12af5657580a350e292f9ff13eb" dependencies = [ "amplify", "commit_encoding_derive", From 773dea0ad73791eb86bde78e6c3a027aa5d3cf32 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Mon, 25 Dec 2023 20:20:09 +0100 Subject: [PATCH 22/27] chore: update dependencies with fixes --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 644173b..26e69ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,7 +29,7 @@ dependencies = [ [[package]] name = "aluvm" version = "0.11.0-beta.1" -source = "git+https://github.com/AluVM/rust-aluvm?branch=v0.11#34894fe14cbbd3312122a7854072056756798a03" +source = "git+https://github.com/AluVM/rust-aluvm?branch=v0.11#60036fb666e8d4c5ddab16c1a6aa6b8305d0b749" dependencies = [ "amplify", "baid58", @@ -1501,7 +1501,7 @@ dependencies = [ [[package]] name = "rgb-core" version = "0.11.0-beta.2" -source = "git+https://github.com/RGB-WG/rgb-core?branch=canary#f12b208ccec4a11de0de1ad0b0cd4048d5fd0e11" +source = "git+https://github.com/RGB-WG/rgb-core?branch=canary#49e78b3f16e7844fde44446717b0c3272027b6f5" dependencies = [ "aluvm", "amplify", From b1a9c0751d81b90dbc4b6e3c3f5a4c589bd19eee Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Mon, 25 Dec 2023 20:20:23 +0100 Subject: [PATCH 23/27] cli: report successful validation --- cli/src/command.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cli/src/command.rs b/cli/src/command.rs index 290a588..98d91c0 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -35,6 +35,7 @@ use rgbstd::interface::{ContractBuilder, FilterExclude, IfaceId, SchemaIfaces}; use rgbstd::invoice::{Beneficiary, InvoiceState, RgbInvoice, RgbTransport}; use rgbstd::persistence::{Inventory, Stash}; use rgbstd::schema::SchemaId; +use rgbstd::validation::Validity; use rgbstd::XSeal; use seals::txout::{CloseMethod, ExplicitSeal}; use strict_types::encoding::{FieldName, TypeName}; @@ -820,7 +821,11 @@ impl Exec for RgbArgs { Err(consignment) => consignment.into_validation_status(), } .expect("just validated"); - eprintln!("{status}"); + if status.validity() == Validity::Valid { + eprintln!("The provided consignment is valid") + } else { + eprintln!("{status}"); + } } Command::Accept { force, file } => { let mut runtime = self.rgb_runtime(&config)?; From 7e24f780cda2cf5dd94940aa94a88109f353d141 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Mon, 25 Dec 2023 20:22:11 +0100 Subject: [PATCH 24/27] chore: fix clippy lints --- cli/src/args.rs | 1 + src/resolver.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/cli/src/args.rs b/cli/src/args.rs index 9c8bbb2..370bd1b 100644 --- a/cli/src/args.rs +++ b/cli/src/args.rs @@ -76,6 +76,7 @@ impl RgbArgs { Ok(runtime) } + #[allow(clippy::result_large_err)] pub fn resolver(&self) -> Result { Resolver::new(&self.resolver.esplora) } diff --git a/src/resolver.rs b/src/resolver.rs index 5b9f9ae..16dcf4b 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -33,6 +33,7 @@ pub struct Resolver { terminal_txes: HashMap, } +#[allow(clippy::large_enum_variant)] #[derive(Debug, Display, Error, From)] #[display(doc_comments)] pub enum AnchorResolverError { @@ -45,6 +46,7 @@ pub enum AnchorResolverError { } impl Resolver { + #[allow(clippy::result_large_err)] pub fn new(url: &str) -> Result { let esplora_client = esplora::Builder::new(url).build_blocking()?; Ok(Self { From af586841a2d1ebc2d8fa7c5480d942a0b805352a Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Mon, 25 Dec 2023 20:29:17 +0100 Subject: [PATCH 25/27] cli: filter wallet/all state with a flag --- cli/src/command.rs | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/cli/src/command.rs b/cli/src/command.rs index 98d91c0..b7584e0 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -113,8 +113,13 @@ pub enum Command { /// Reports information about state of a contract #[display("state")] State { + /// Show all state - not just the one owned by the wallet + #[clap(short, long)] + all: bool, + /// Contract identifier contract_id: ContractId, + /// Interface to interpret the state data iface: String, }, @@ -362,7 +367,11 @@ impl Exec for RgbArgs { println!("{bindle}"); } - Command::State { contract_id, iface } => { + Command::State { + contract_id, + iface, + all, + } => { let mut runtime = self.rgb_runtime(&config)?; let bp_runtime = self.bp_runtime::(&config)?; runtime.attach(bp_runtime.detach()); @@ -384,20 +393,22 @@ impl Exec for RgbArgs { println!(" {}:", owned.name); if let Ok(allocations) = contract.fungible(owned.name.clone(), &runtime) { for allocation in allocations { - print!( + println!( " amount={}, utxo={}, witness={} # owned by the wallet", allocation.value, allocation.owner, allocation.witness ); } } - if let Ok(allocations) = - contract.fungible(owned.name.clone(), &FilterExclude(&runtime)) - { - for allocation in allocations { - print!( - " amount={}, utxo={}, witness={} # owner unknown", - allocation.value, allocation.owner, allocation.witness - ); + if *all { + if let Ok(allocations) = + contract.fungible(owned.name.clone(), &FilterExclude(&runtime)) + { + for allocation in allocations { + println!( + " amount={}, utxo={}, witness={} # owner unknown", + allocation.value, allocation.owner, allocation.witness + ); + } } } // TODO: Print out other types of state From 065fb5eeaf238327955857637fb4414702a639e4 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Mon, 25 Dec 2023 20:32:13 +0100 Subject: [PATCH 26/27] ci: fix default feature build --- Cargo.toml | 2 +- cli/Cargo.toml | 2 +- src/lib.rs | 2 ++ src/runtime.rs | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4808c05..25c5253 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,7 +82,7 @@ serde_yaml = { workspace = true, optional = true } log = { workspace = true, optional = true } [features] -default = [] +default = ["esplora"] all = ["esplora", "serde", "log"] esplora = ["bp-esplora", "bp-wallet/esplora"] serde = ["serde_crate", "serde_yaml", "bp-std/serde", "bp-wallet/serde",] diff --git a/cli/Cargo.toml b/cli/Cargo.toml index ab4a689..5c18b0a 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -27,7 +27,7 @@ bp-wallet = { workspace = true } bp-util = { workspace = true } psbt = { workspace = true } rgb-std = { workspace = true, features = ["serde"] } -rgb-runtime = { version = "0.11.0-alpha.2", path = "..", features = ["log", "serde"] } +rgb-runtime = { version = "0.11.0-alpha.2", path = "..", features = ["esplora", "log", "serde"] } log = { workspace = true } env_logger = "0.10.1" clap = { version = "4.4.8", features = ["derive", "env"] } diff --git a/src/lib.rs b/src/lib.rs index 8f9723c..7c22480 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,9 +31,11 @@ extern crate serde_crate as serde; mod runtime; mod descriptor; mod pay; +#[cfg(feature = "esplora")] mod resolver; pub use descriptor::{DescriptorRgb, RgbDescr, RgbKeychain, TapTweakAlreadyAssigned, TapretKey}; pub use pay::{CompletionError, CompositionError, PayError, TransferParams}; +#[cfg(feature = "esplora")] pub use resolver::{AnchorResolverError, Resolver, ResolverError}; pub use runtime::{Runtime, RuntimeError}; diff --git a/src/runtime.rs b/src/runtime.rs index 92bf31e..da90033 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -90,6 +90,7 @@ pub enum RuntimeError { #[from(bpwallet::LoadError)] Bp(bpwallet::RuntimeError), + #[cfg(feature = "esplora")] #[from] Esplora(esplora::Error), From 69377f1856c1e34de9f1bf646bc35c2c460316d3 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Mon, 25 Dec 2023 23:28:05 +0100 Subject: [PATCH 27/27] release v0.11.0-beta.1 --- Cargo.lock | 106 +++++++++++++++++++++++++++++-------------------- Cargo.toml | 43 ++++++-------------- cli/Cargo.toml | 4 +- 3 files changed, 76 insertions(+), 77 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 26e69ca..acf143d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,8 +28,9 @@ dependencies = [ [[package]] name = "aluvm" -version = "0.11.0-beta.1" -source = "git+https://github.com/AluVM/rust-aluvm?branch=v0.11#60036fb666e8d4c5ddab16c1a6aa6b8305d0b749" +version = "0.11.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10be187b383247e1902aa5a415f76ffc9a04f197829c46b9ccb6da3582e394f" dependencies = [ "amplify", "baid58", @@ -309,8 +310,9 @@ dependencies = [ [[package]] name = "bp-consensus" -version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#241eee511a7c584716b31eb5d897a94a9d8ce56e" +version = "0.11.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190ac89a2a3c79d5bfb677f48e5393691832c540973341831572edaffdce0881" dependencies = [ "amplify", "chrono", @@ -323,8 +325,9 @@ dependencies = [ [[package]] name = "bp-core" -version = "0.11.0-beta.1" -source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#241eee511a7c584716b31eb5d897a94a9d8ce56e" +version = "0.11.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0143f6c7399cb6d0003407e6de197a03b3b42b34380fab02c8fecaf4431b061" dependencies = [ "amplify", "bp-consensus", @@ -339,8 +342,9 @@ dependencies = [ [[package]] name = "bp-dbc" -version = "0.11.0-beta.1" -source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#241eee511a7c584716b31eb5d897a94a9d8ce56e" +version = "0.11.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e3e04649c77079cfd1466ba14a62c83585053bb61d294578a39fcfe38f402db" dependencies = [ "amplify", "base85", @@ -353,8 +357,9 @@ dependencies = [ [[package]] name = "bp-derive" -version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#5d093349fd5f874fdd68d1181ad97256716c8671" +version = "0.11.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497e2b7324e0c7a65bdaabccd0fc7d05b003cca4ae3f3a1520e023aab3a9a24f" dependencies = [ "amplify", "bitcoin_hashes", @@ -367,8 +372,9 @@ dependencies = [ [[package]] name = "bp-esplora" -version = "0.11.0-beta.1" -source = "git+https://github.com/BP-WG/bp-esplora-client?branch=master#49d9a56638eaecd7652bc046d4550b1446daf1ec" +version = "0.11.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d642e2e6d699edd884e0359668750df1590d209f2ff956d3c5fbf58b5eba730" dependencies = [ "amplify", "bp-std", @@ -382,8 +388,9 @@ dependencies = [ [[package]] name = "bp-invoice" -version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#5d093349fd5f874fdd68d1181ad97256716c8671" +version = "0.11.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a40b7fb87b58b2371b7b3b010568baa9ba463007707f9af9bb9b77a63d77e8f" dependencies = [ "amplify", "bech32", @@ -394,8 +401,9 @@ dependencies = [ [[package]] name = "bp-seals" -version = "0.11.0-beta.1" -source = "git+https://github.com/BP-WG/bp-core?branch=doubleanchors#241eee511a7c584716b31eb5d897a94a9d8ce56e" +version = "0.11.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e4955315fad858472320ee5a7fba8a35c61c3928bdacb581931e23d4f25c7b" dependencies = [ "amplify", "baid58", @@ -410,8 +418,9 @@ dependencies = [ [[package]] name = "bp-std" -version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#5d093349fd5f874fdd68d1181ad97256716c8671" +version = "0.11.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d6847d1e7359ed49947092fd319433c82b9cced1a2d780a9415d212770cf93c" dependencies = [ "amplify", "bp-consensus", @@ -424,8 +433,9 @@ dependencies = [ [[package]] name = "bp-util" -version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#e197ebf9d9d84230715d1c96521ecb8aed5cb92c" +version = "0.11.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26f19a4670648894ce6e3dc46d1c2fe31c6ac168fe83c66575b1f2bcbf24b49f" dependencies = [ "amplify", "base64", @@ -446,8 +456,9 @@ dependencies = [ [[package]] name = "bp-wallet" -version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#e197ebf9d9d84230715d1c96521ecb8aed5cb92c" +version = "0.11.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a947aa0c73c5097e30e6be62939e8a6f230aa076710817000fb1cd15b408e8" dependencies = [ "amplify", "bp-esplora", @@ -556,7 +567,8 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "commit_encoding_derive" version = "0.10.0" -source = "git+https://github.com/LNP-BP/client_side_validation?branch=v0.11#2ec2bca60028d12af5657580a350e292f9ff13eb" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00033f14d67c4169d588f085ea2faeb7b610cf03a74d42ea09eeba31abef2047" dependencies = [ "amplify", "amplify_syn", @@ -567,8 +579,9 @@ dependencies = [ [[package]] name = "commit_verify" -version = "0.11.0-beta.1" -source = "git+https://github.com/LNP-BP/client_side_validation?branch=v0.11#2ec2bca60028d12af5657580a350e292f9ff13eb" +version = "0.11.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5598661b1d90b149f0b944faef8c1d1d131f847678a6c450b9540b629ac11291" dependencies = [ "amplify", "commit_encoding_derive", @@ -693,8 +706,9 @@ dependencies = [ [[package]] name = "descriptors" -version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#5d093349fd5f874fdd68d1181ad97256716c8671" +version = "0.11.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58cfb07301f4982a5470167155c186586871d21693a791078cfa15c173657626" dependencies = [ "amplify", "bp-derive", @@ -1356,8 +1370,9 @@ dependencies = [ [[package]] name = "psbt" -version = "0.11.0-beta.2" -source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#5d093349fd5f874fdd68d1181ad97256716c8671" +version = "0.11.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "010207105ec57c07146a30363074b71fa204c7515b3b413280d0e5986c658359" dependencies = [ "amplify", "base64", @@ -1500,8 +1515,9 @@ dependencies = [ [[package]] name = "rgb-core" -version = "0.11.0-beta.2" -source = "git+https://github.com/RGB-WG/rgb-core?branch=canary#49e78b3f16e7844fde44446717b0c3272027b6f5" +version = "0.11.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd6b8a6d616997c670ea40a5a9badc9744f3e1fa29213affabdd2d2c192ef3b" dependencies = [ "aluvm", "amplify", @@ -1520,8 +1536,9 @@ dependencies = [ [[package]] name = "rgb-invoice" -version = "0.11.0-beta.2" -source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#d8a1161b68f4294797ce0721ebd87469d40b3198" +version = "0.11.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba62ce12f4677d821ed5877bcc4125248cc3b82c8aeb0bd01a3924ae3c477284" dependencies = [ "amplify", "baid58", @@ -1547,7 +1564,7 @@ dependencies = [ [[package]] name = "rgb-psbt" -version = "0.11.0-alpha.2" +version = "0.11.0-beta.1" dependencies = [ "amplify", "baid58", @@ -1565,7 +1582,7 @@ dependencies = [ [[package]] name = "rgb-runtime" -version = "0.11.0-alpha.2" +version = "0.11.0-beta.1" dependencies = [ "amplify", "baid58", @@ -1588,8 +1605,9 @@ dependencies = [ [[package]] name = "rgb-std" -version = "0.11.0-beta.2" -source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#d8a1161b68f4294797ce0721ebd87469d40b3198" +version = "0.11.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138a9d7eea58593bb756e2effd991fcf42fe55503e311abc9b1d22c5088dd71f" dependencies = [ "amplify", "baid58", @@ -1609,7 +1627,7 @@ dependencies = [ [[package]] name = "rgb-wallet" -version = "0.11.0-alpha.2" +version = "0.11.0-beta.1" dependencies = [ "amplify", "baid58", @@ -1939,9 +1957,9 @@ dependencies = [ [[package]] name = "single_use_seals" -version = "0.11.0-beta.1" +version = "0.11.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a46b0f289450dc06e7b94f048a55ac437177532f2c6c9bed12c1de7ac948f5a5" +checksum = "c30647a1342641c45ca7c1dcd5ae7db16533b86744e827c84cfed875db2de3fe" dependencies = [ "amplify_derive", ] @@ -2109,18 +2127,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.51" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +checksum = "83a48fd946b02c0a526b2e9481c8e2a17755e47039164a86c4070446e3a4614d" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.51" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +checksum = "e7fbe9b594d6568a6a1443250a7e67d80b74e1e96f6d1715e1e21cc1888291d3" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 25c5253..806fcf1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ default-members = [ ] [workspace.package] -version = "0.11.0-alpha.2" +version = "0.11.0-beta.1" keywords = ["bitcoin", "lightning", "rgb", "smart-contracts", "lnp-bp"] categories = ["cryptography::cryptocurrencies"] authors = ["Dr Maxim Orlovsky "] @@ -26,19 +26,19 @@ license = "Apache-2.0" [workspace.dependencies] amplify = "4.5.0" baid58 = "0.4.4" -commit_verify = "0.11.0-beta.1" +commit_verify = "0.11.0-beta.2" strict_encoding = "2.6.1" strict_types = "1.6.3" -bp-core = "0.11.0-beta.1" -bp-seals = "0.11.0-beta.1" -bp-std = "0.11.0-beta.2" -bp-wallet = "0.11.0-beta.2" -bp-util = "0.11.0-beta.2" -bp-esplora = "0.11.0-beta.1" -descriptors = "0.11.0-beta.1" -psbt = { version = "0.11.0-beta.1", features = ["client-side-validation"] } -rgb-std = { version = "0.11.0-beta.2", features = ["fs"] } -rgb-psbt = { version = "0.11.0-alpha.2", path = "psbt" } +bp-core = "0.11.0-beta.2" +bp-seals = "0.11.0-beta.2" +bp-std = "0.11.0-beta.3" +bp-wallet = "0.11.0-beta.3" +bp-util = "0.11.0-beta.3" +bp-esplora = "0.11.0-beta.2" +descriptors = "0.11.0-beta.2" +psbt = { version = "0.11.0-beta.2", features = ["client-side-validation"] } +rgb-std = { version = "0.11.0-beta.3", features = ["fs"] } +rgb-psbt = { version = "0.11.0-beta.1", path = "psbt" } indexmap = "2.0.2" chrono = "0.4.31" serde_crate = { package = "serde", version = "1", features = ["derive"] } @@ -89,22 +89,3 @@ serde = ["serde_crate", "serde_yaml", "bp-std/serde", "bp-wallet/serde",] [package.metadata.docs.rs] features = [ "all" ] - -[patch.crates-io] -commit_verify = { git = "https://github.com/LNP-BP/client_side_validation", branch = "v0.11" } -bp-consensus = { git = "https://github.com/BP-WG/bp-core", branch = "doubleanchors" } -bp-dbc = { git = "https://github.com/BP-WG/bp-core", branch = "doubleanchors" } -bp-seals = { git = "https://github.com/BP-WG/bp-core", branch = "doubleanchors" } -bp-core = { git = "https://github.com/BP-WG/bp-core", branch = "doubleanchors" } -bp-invoice = { git = "https://github.com/BP-WG/bp-std", branch = "v0.11" } -bp-std = { git = "https://github.com/BP-WG/bp-std", branch = "v0.11" } -descriptors = { git = "https://github.com/BP-WG/bp-std", branch = "v0.11" } -psbt = { git = "https://github.com/BP-WG/bp-std", branch = "v0.11" } -bp-derive = { git = "https://github.com/BP-WG/bp-std", branch = "v0.11" } -bp-wallet = { git = "https://github.com/BP-WG/bp-wallet", branch = "v0.11" } -bp-util = { git = "https://github.com/BP-WG/bp-wallet", branch = "v0.11" } -bp-esplora = { git = "https://github.com/BP-WG/bp-esplora-client", branch = "master" } -aluvm = { git = "https://github.com/AluVM/rust-aluvm", branch = "v0.11" } -rgb-core = { git = "https://github.com/RGB-WG/rgb-core", branch = "canary" } -rgb-std = { git = "https://github.com/RGB-WG/rgb-std", branch = "v0.11" } -rgb-invoice = { git = "https://github.com/RGB-WG/rgb-std", branch = "v0.11" } diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 5c18b0a..2354bc1 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -20,14 +20,14 @@ path = "src/main.rs" amplify = { workspace = true } baid58 = { workspace = true } strict_types = { workspace = true, features = ["serde"] } -commit_verify = "0.11.0-beta.1" +commit_verify = { workspace = true } bp-seals = { workspace = true } bp-std = { workspace = true, features = ["serde"] } bp-wallet = { workspace = true } bp-util = { workspace = true } psbt = { workspace = true } rgb-std = { workspace = true, features = ["serde"] } -rgb-runtime = { version = "0.11.0-alpha.2", path = "..", features = ["esplora", "log", "serde"] } +rgb-runtime = { version = "0.11.0-beta.1", path = "..", features = ["esplora", "log", "serde"] } log = { workspace = true } env_logger = "0.10.1" clap = { version = "4.4.8", features = ["derive", "env"] }