Skip to content

Commit

Permalink
payments workflows
Browse files Browse the repository at this point in the history
  • Loading branch information
dr-orlovsky committed Jan 24, 2025
1 parent 8ffe11c commit 74be135
Show file tree
Hide file tree
Showing 9 changed files with 224 additions and 48 deletions.
12 changes: 7 additions & 5 deletions Cargo.lock

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

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ license = "Apache-2.0"
amplify = "4.8.0"
nonasync = "0.1.2"
strict_encoding = "2.8.1"
strict_types = "2.8.1"
commit_verify = "=0.12.0-beta.4"
bp-std = { version = "=0.12.0-beta.4", features = ["client-side-validation"] }
bp-electrum = "=0.12.0-beta.4"
Expand Down Expand Up @@ -51,10 +52,12 @@ crate-type = ["cdylib", "rlib"]
[dependencies]
amplify = { workspace = true }
commit_verify = { workspace = true }
strict_types = { workspace = true }
nonasync = { workspace = true }
bp-std = { workspace = true }
bp-wallet = { workspace = true }
rgb-std = { workspace = true, features = ["bitcoin"] }
rgb-psbt.workspace = true
indexmap = { workspace = true }
serde = { workspace = true, optional = true }
serde_yaml = { workspace = true, optional = true }
Expand Down
36 changes: 32 additions & 4 deletions cli/src/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,17 @@ pub enum Cmd {
#[clap(short, long, default_value = "aggregate", env = RGB_COINSELECT_STRATEGY_ENV)]
strategy: CoinselectStrategy,

/// Invoice to fylfill
invoice: RgbInvoice,
/// Invoice to fulfill
invoice: RgbInvoice<ContractId>,

/// Fees for PSBT
fee: Sats,

/// File to save the produced PSBT
///
/// If not provided, prints PSBT to standard output.
#[clap(value_hint = ValueHint::FilePath)]
psbt: Option<PathBuf>,
},

/// Execute a script, producing prefabricated operation bundle and PSBT
Expand Down Expand Up @@ -492,6 +501,23 @@ impl Args {
}
}

Cmd::Pay { wallet, strategy, invoice, fee, psbt: psbt_filename } => {
let mut runtime = self.runtime(wallet.wallet.as_deref());
// TODO: sync wallet if needed
// TODO: Add params and giveway to arguments
let params = TxParams::with(*fee);
let giveaway = Some(Sats::from(500u16));
let psbt = runtime.pay_invoice(invoice, *strategy, params, giveaway)?;
if let Some(psbt_filename) = psbt_filename {
psbt.encode(
psbt.version,
&mut File::create(psbt_filename).expect("Unable to write PSBT"),
)?;
} else {
println!("{psbt}");
}
}

Cmd::Exec {
wallet,
script,
Expand All @@ -506,7 +532,8 @@ impl Args {
serde_yaml::from_reader::<_, OpRequestSet<Option<WoutAssignment>>>(src)?;

let (mut psbt, meta) = runtime
.construct_psbt(&items, TxParams::with(*fee))
.wallet
.compose_psbt(&items, TxParams::with(*fee))
.expect("Unable to construct PSBT");
let mut psbt_file = File::create_new(
psbt_filename
Expand All @@ -519,13 +546,14 @@ impl Args {
// Here we send PSBT to other payjoin parties so they add their inputs and outputs,
// or even re-order existing ones

// TODO: Replace this with `color` function
let items = items.resolve_seals(psbt.script_resolver(), meta.change_vout)?;
let bundle = runtime.bundle(items, meta.change_vout)?;
bundle
.strict_serialize_to_file::<{ usize::MAX }>(&bundle_filename)
.expect("Unable to write output file");

psbt.rgb_fill_csv(bundle)
psbt.rgb_fill_csv(&bundle)
.expect("Unable to embed RGB information to PSBT");
psbt.encode(psbt.version, &mut psbt_file)
.expect("Unable to write PSBT");
Expand Down
8 changes: 4 additions & 4 deletions psbt/src/bp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,16 @@ use rgb::popls::bp::PrefabBundle;
use crate::{RgbPsbt, RgbPsbtError, ScriptResolver};

impl RgbPsbt for Psbt {
fn rgb_fill_csv(&mut self, bundle: PrefabBundle) -> Result<(), RgbPsbtError> {
fn rgb_fill_csv(&mut self, bundle: &PrefabBundle) -> Result<(), RgbPsbtError> {
for prefab in bundle {
let id = mpc::ProtocolId::from_byte_array(prefab.operation.contract_id.to_byte_array());
let opid = prefab.operation.opid();
let msg = mmb::Message::from_byte_array(opid.to_byte_array());
for outpoint in prefab.closes {
for outpoint in &prefab.closes {
let input = self
.inputs_mut()
.find(|inp| inp.previous_outpoint == outpoint)
.ok_or(RgbPsbtError::InputAbsent(outpoint))?;
.find(|inp| inp.previous_outpoint == *outpoint)
.ok_or(RgbPsbtError::InputAbsent(*outpoint))?;
input.set_mmb_message(id, msg).map_err(|_| {
RgbPsbtError::InputAlreadyUsed(input.index(), prefab.operation.contract_id)
})?;
Expand Down
2 changes: 1 addition & 1 deletion psbt/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use rgb::{ContractId, Outpoint};

pub trait RgbPsbt {
// TODO: Add rgb_embed to embed operations for hardware signers
fn rgb_fill_csv(&mut self, bundle: PrefabBundle) -> Result<(), RgbPsbtError>;
fn rgb_fill_csv(&mut self, bundle: &PrefabBundle) -> Result<(), RgbPsbtError>;

fn rgb_complete(&mut self) -> Result<(), Unmodifiable>;
}
Expand Down
51 changes: 48 additions & 3 deletions src/coinselect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,22 @@
// or implied. See the License for the specific language governing permissions and limitations under
// the License.

use std::str::FromStr;
use core::str::FromStr;

use rgb::popls::bp::Coinselect;
use rgb::{CellAddr, StateCalc};
use strict_types::StrictVal;

#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Display, Default)]
#[display(lowercase)]
pub enum CoinselectStrategy {
/// Collect them most small outputs unless the invoiced value if reached
#[default]
Aggregate,
Size,

/// Collect the minimum number of outputs (with the large value) to reduce the resulting input
/// count
SmallSize,
}

impl FromStr for CoinselectStrategy {
Expand All @@ -38,8 +46,45 @@ impl FromStr for CoinselectStrategy {
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"aggregate" => Ok(CoinselectStrategy::Aggregate),
"size" => Ok(CoinselectStrategy::Size),
"smallsize" => Ok(CoinselectStrategy::SmallSize),
s => Err(s.to_string()),
}
}
}

impl Coinselect for CoinselectStrategy {
fn coinselect(
&mut self,
invoiced_state: &StrictVal,
calc: &mut (impl StateCalc + ?Sized),
owned_state: Vec<(CellAddr, &StrictVal)>,
) -> Option<Vec<CellAddr>> {
let res = match self {
CoinselectStrategy::Aggregate => owned_state
.into_iter()
.take_while(|(_, val)| {
if calc.is_satisfied(invoiced_state) {
return false;
}
calc.accumulate(*val).is_ok()
})
.map(|(addr, _)| addr)
.collect(),
CoinselectStrategy::SmallSize => owned_state
.into_iter()
.rev()
.take_while(|(_, val)| {
if calc.is_satisfied(invoiced_state) {
return false;
}
calc.accumulate(*val).is_ok()
})
.map(|(addr, _)| addr)
.collect(),
};
if !calc.is_satisfied(invoiced_state) {
return None;
};
Some(res)
}
}
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,6 @@ mod runtime;

pub use coinselect::CoinselectStrategy;
#[cfg(feature = "fs")]
pub use runtime::file::RgbDirRuntime;
pub use runtime::RgbRuntime;
pub use runtime::file::{ConsignmentStream, RgbDirRuntime, Transfer};
pub use runtime::{PayError, RgbRuntime, TransferError};
pub use wallet::RgbWallet;
Loading

0 comments on commit 74be135

Please sign in to comment.