Fix bug when doing multiple payments

This commit is contained in:
Steven Roose
2024-02-02 14:19:51 +00:00
parent 81416747f3
commit e67540a6d3
4 changed files with 74 additions and 48 deletions

View File

@@ -225,14 +225,14 @@ pub async fn run_round_scheduler(
b.ordering(bdk::wallet::tx_builder::TxOrdering::Untouched);
b.add_recipient(vtxos_spec.cosign_spk(), vtxos_spec.total_required_value().to_sat());
b.add_recipient(connector_output.script_pubkey, connector_output.value);
b.fee_rate(bdk::FeeRate::from_sat_per_vb(100.0)); //TODO(stevenroose) fix
b.fee_rate(bdk::FeeRate::from_sat_per_vb(10.0)); //TODO(stevenroose) fix
b.finish().context("bdk failed to create round tx")?
};
let round_tx = round_tx_psbt.clone().extract_tx();
let vtxos_utxo = OutPoint::new(round_tx.txid(), 0);
let conns_utxo = OutPoint::new(round_tx.txid(), 1);
// Generate nonces and combine with user's nonces.
// Generate vtxo nonces and combine with user's nonces.
let (sec_vtxo_nonces, pub_vtxo_nonces) = {
let mut secs = Vec::with_capacity(nb_nodes);
let mut pubs = Vec::with_capacity(nb_nodes);

View File

@@ -4,7 +4,8 @@
use std::fs;
use std::path::PathBuf;
use bitcoin::Amount;
use anyhow::Context;
use bitcoin::{address, Address, Amount};
use bitcoin::secp256k1::PublicKey;
use clap::Parser;
@@ -45,6 +46,11 @@ enum Command {
amount: Amount,
},
#[command()]
SendOnchain {
address: Address<address::NetworkUnchecked>,
amount: Amount,
},
#[command()]
Send {
pubkey: PublicKey,
amount: Amount,
@@ -59,8 +65,7 @@ enum Command {
DropVtxos {},
}
#[tokio::main]
async fn main() {
async fn inner_main() -> anyhow::Result<()> {
env_logger::builder()
.filter_module("sled", log::LevelFilter::Off)
.filter_module("bitcoincore_rpc", log::LevelFilter::Trace)
@@ -72,42 +77,44 @@ async fn main() {
//TODO(stevenroose) somehow pass this in
let mut cfg = Config {
network: bitcoin::Network::Regtest,
datadir: cli.datadir.canonicalize().expect("canonicalizing path"),
datadir: cli.datadir.canonicalize().context("canonicalizing path")?,
asp_address: "http://[::1]:35035".parse().unwrap(),
..Default::default()
};
// Handle create command differently.
if let Command::Create { ref datadir, force } = cli.command {
let datadir = if let Some(datadir) = datadir {
fs::create_dir_all(&datadir).context("failed to create datadir")?;
datadir
} else {
&cli.datadir
}.canonicalize().context("error canonicalizing datadir")?;
if force {
fs::remove_dir_all(&datadir)?;
}
fs::create_dir_all(&datadir).context("failed to create datadir")?;
cfg.datadir = datadir;
let mut w = Wallet::create(cfg).await.context("error creating wallet")?;
info!("Onchain address: {}", w.get_new_onchain_address()?);
return Ok(());
}
let mut w = Wallet::open(cfg.clone()).await.context("error opening wallet")?;
match cli.command {
Command::Create { datadir, force } => {
let datadir = if let Some(datadir) = datadir {
fs::create_dir_all(&datadir).expect("failed to create datadir");
datadir
} else {
cli.datadir
}.canonicalize().expect("error canonicalizing datadir");
if force {
fs::remove_dir_all(&datadir).unwrap();
}
fs::create_dir_all(&datadir).expect("failed to create datadir");
cfg.datadir = datadir;
let mut w = Wallet::create(cfg).await.expect("error creating wallet");
info!("Onchain address: {}", w.get_new_onchain_address().unwrap());
},
Command::Create { .. } => unreachable!(),
Command::GetAddress { } => {
let mut w = Wallet::open(cfg).await.unwrap();
info!("Onchain address: {}", w.get_new_onchain_address().unwrap());
info!("Onchain address: {}", w.get_new_onchain_address()?);
},
Command::GetVtxoPubkey { } => {
let w = Wallet::open(cfg).await.unwrap();
info!("Vtxo pubkey: {}", w.vtxo_pubkey());
}
Command::Balance { } => {
let mut w = Wallet::open(cfg).await.unwrap();
info!("Onchain balance: {}", w.onchain_balance().unwrap());
info!("Offchain balance: {}", w.offchain_balance().await.unwrap());
let (claimable, unclaimable) = w.unclaimed_exits().await.unwrap();
info!("Onchain balance: {}", w.onchain_balance()?);
info!("Offchain balance: {}", w.offchain_balance().await?);
let (claimable, unclaimable) = w.unclaimed_exits().await?;
if !claimable.is_empty() {
let sum = claimable.iter().map(|i| i.spec.amount).sum::<Amount>();
info!("Got {} claimable exits with total value of {}", claimable.len(), sum);
@@ -118,29 +125,39 @@ async fn main() {
}
},
Command::Onboard { amount } => {
let mut w = Wallet::open(cfg).await.unwrap();
w.onboard(amount).await.unwrap();
w.onboard(amount).await?;
},
Command::SendOnchain { address, amount } => {
let addr = address.require_network(cfg.network).with_context(|| {
format!("address is not valid for configured network {}", cfg.network)
})?;
w.send_onchain(addr, amount)?;
},
Command::Send { pubkey, amount } => {
let mut w = Wallet::open(cfg).await.unwrap();
let dest = Destination { pubkey, amount };
w.send_payment(dest).await.unwrap();
w.send_payment(dest).await?;
},
Command::StartExit { } => {
let mut w = Wallet::open(cfg).await.unwrap();
w.start_unilateral_exit().await.unwrap();
w.start_unilateral_exit().await?;
},
Command::ClaimExit { } => {
let mut w = Wallet::open(cfg).await.unwrap();
w.claim_unilateral_exit().await.unwrap();
w.claim_unilateral_exit().await?;
},
// dev commands
Command::DropVtxos { } => {
let w = Wallet::open(cfg).await.unwrap();
w.drop_vtxos().await.unwrap();
w.drop_vtxos().await?;
info!("Dropped all vtxos");
},
}
Ok(())
}
#[tokio::main]
async fn main() {
if let Err(e) = inner_main().await {
eprintln!("An error occurred: {}", e);
eprintln!("Backtrace: {:?}", e.backtrace());
}
}

View File

@@ -3,7 +3,7 @@ use std::io;
use std::collections::HashSet;
use anyhow::Context;
use bitcoin::{secp256k1, sighash, taproot, OutPoint, Witness};
use bitcoin::{secp256k1, sighash, taproot, Amount, OutPoint, Witness};
use ark::{Vtxo, VtxoSpec};
@@ -170,8 +170,9 @@ impl Wallet {
info!("No inputs we can claim.");
return Ok(());
}
debug!("Claiming the following exits: {:?}",
inputs.iter().map(|i| i.utxo.to_string()).collect::<Vec<_>>(),
let total_amount = inputs.iter().map(|i| i.spec.amount).sum::<Amount>();
debug!("Claiming the following exits with total value of {}: {:?}",
total_amount, inputs.iter().map(|i| i.utxo.to_string()).collect::<Vec<_>>(),
);
let mut psbt = self.onchain.create_exit_claim_tx(&inputs)?;
@@ -223,6 +224,8 @@ impl Wallet {
// Then update the database and only set the remaining inputs as to do.
self.db.store_claim_inputs(&remaining).context("failed db update")?;
info!("Successfully claimed total value of {}", total_amount);
Ok(())
}
}

View File

@@ -38,6 +38,7 @@ pub struct ArkInfo {
pub vtxo_exit_delta: u16,
}
#[derive(Debug, Clone)]
pub struct Config {
pub network: Network,
pub datadir: PathBuf,
@@ -181,7 +182,6 @@ impl Wallet {
// We ask the ASP to cosign our onboard unlock tx.
let (user_part, priv_user_part) = ark::onboard::new_user(spec, utxo);
trace!("User part for onboard: {:#?}", user_part);
let asp_part = {
let res = self.asp.request_onboard_cosign(arkd_rpc_client::OnboardCosignRequest {
user_part: {
@@ -284,6 +284,10 @@ impl Wallet {
Ok(())
}
pub fn send_onchain(&mut self, addr: Address, amount: Amount) -> anyhow::Result<Txid> {
Ok(self.onchain.send_money(addr, amount)?)
}
pub async fn send_payment(&mut self, destination: Destination) -> anyhow::Result<()> {
self.sync_ark().await.context("ark sync error")?;
@@ -340,7 +344,8 @@ impl Wallet {
};
// The round has now started. We can submit our payment.
trace!("Submitting payment request");
trace!("Submitting payment request with {} inputs and {} outputs",
input_vtxos.len(), 1 + change.is_some() as usize);
self.asp.submit_payment(rpc::SubmitPaymentRequest {
cosign_pubkey: cosign_key.public_key().serialize().to_vec(),
input_vtxos: input_vtxos.iter().map(|v| v.encode()).collect(),
@@ -405,7 +410,9 @@ impl Wallet {
// Make forfeit signatures.
let connectors = ConnectorChain::new(
forfeit_nonces.len(), conns_utxo, self.ark_info.asp_pubkey,
forfeit_nonces.values().next().unwrap().len(),
conns_utxo,
self.ark_info.asp_pubkey,
);
let forfeit_signatures = input_vtxos.iter().map(|v| {
let sigs = connectors.connectors().enumerate().map(|(i, conn)| {
@@ -429,7 +436,7 @@ impl Wallet {
// Make vtxo signatures from top to bottom, just like sighashes are returned.
let sighashes = vtxo_tree.sighashes(vtxos_utxo);
trace!("sighashes: {:?}", sighashes);
trace!("vtxo sighashes: {:?}", sighashes);
assert_eq!(sighashes.len(), vtxo_agg_nonces.len());
let signatures = iter::zip(sec_nonces.into_iter(), iter::zip(sighashes, vtxo_agg_nonces))
.map(|(sec_nonce, (sighash, agg_nonce))| {
@@ -443,7 +450,6 @@ impl Wallet {
None,
).0
}).collect::<Vec<_>>();
trace!("Sending signatures to ASP");
self.asp.provide_signatures(rpc::RoundSignatures {
forfeit: forfeit_signatures.into_iter().map(|(id, sigs)| {
rpc::ForfeitSignatures {