mirror of
https://github.com/aljazceru/clArk.git
synced 2025-12-17 13:14:20 +01:00
Fix bug when doing multiple payments
This commit is contained in:
@@ -225,14 +225,14 @@ pub async fn run_round_scheduler(
|
|||||||
b.ordering(bdk::wallet::tx_builder::TxOrdering::Untouched);
|
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(vtxos_spec.cosign_spk(), vtxos_spec.total_required_value().to_sat());
|
||||||
b.add_recipient(connector_output.script_pubkey, connector_output.value);
|
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")?
|
b.finish().context("bdk failed to create round tx")?
|
||||||
};
|
};
|
||||||
let round_tx = round_tx_psbt.clone().extract_tx();
|
let round_tx = round_tx_psbt.clone().extract_tx();
|
||||||
let vtxos_utxo = OutPoint::new(round_tx.txid(), 0);
|
let vtxos_utxo = OutPoint::new(round_tx.txid(), 0);
|
||||||
let conns_utxo = OutPoint::new(round_tx.txid(), 1);
|
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 (sec_vtxo_nonces, pub_vtxo_nonces) = {
|
||||||
let mut secs = Vec::with_capacity(nb_nodes);
|
let mut secs = Vec::with_capacity(nb_nodes);
|
||||||
let mut pubs = Vec::with_capacity(nb_nodes);
|
let mut pubs = Vec::with_capacity(nb_nodes);
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use bitcoin::Amount;
|
use anyhow::Context;
|
||||||
|
use bitcoin::{address, Address, Amount};
|
||||||
use bitcoin::secp256k1::PublicKey;
|
use bitcoin::secp256k1::PublicKey;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
@@ -45,6 +46,11 @@ enum Command {
|
|||||||
amount: Amount,
|
amount: Amount,
|
||||||
},
|
},
|
||||||
#[command()]
|
#[command()]
|
||||||
|
SendOnchain {
|
||||||
|
address: Address<address::NetworkUnchecked>,
|
||||||
|
amount: Amount,
|
||||||
|
},
|
||||||
|
#[command()]
|
||||||
Send {
|
Send {
|
||||||
pubkey: PublicKey,
|
pubkey: PublicKey,
|
||||||
amount: Amount,
|
amount: Amount,
|
||||||
@@ -59,8 +65,7 @@ enum Command {
|
|||||||
DropVtxos {},
|
DropVtxos {},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
async fn inner_main() -> anyhow::Result<()> {
|
||||||
async fn main() {
|
|
||||||
env_logger::builder()
|
env_logger::builder()
|
||||||
.filter_module("sled", log::LevelFilter::Off)
|
.filter_module("sled", log::LevelFilter::Off)
|
||||||
.filter_module("bitcoincore_rpc", log::LevelFilter::Trace)
|
.filter_module("bitcoincore_rpc", log::LevelFilter::Trace)
|
||||||
@@ -72,42 +77,44 @@ async fn main() {
|
|||||||
//TODO(stevenroose) somehow pass this in
|
//TODO(stevenroose) somehow pass this in
|
||||||
let mut cfg = Config {
|
let mut cfg = Config {
|
||||||
network: bitcoin::Network::Regtest,
|
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(),
|
asp_address: "http://[::1]:35035".parse().unwrap(),
|
||||||
..Default::default()
|
..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 {
|
match cli.command {
|
||||||
Command::Create { datadir, force } => {
|
Command::Create { .. } => unreachable!(),
|
||||||
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::GetAddress { } => {
|
Command::GetAddress { } => {
|
||||||
let mut w = Wallet::open(cfg).await.unwrap();
|
info!("Onchain address: {}", w.get_new_onchain_address()?);
|
||||||
info!("Onchain address: {}", w.get_new_onchain_address().unwrap());
|
|
||||||
},
|
},
|
||||||
Command::GetVtxoPubkey { } => {
|
Command::GetVtxoPubkey { } => {
|
||||||
let w = Wallet::open(cfg).await.unwrap();
|
|
||||||
info!("Vtxo pubkey: {}", w.vtxo_pubkey());
|
info!("Vtxo pubkey: {}", w.vtxo_pubkey());
|
||||||
}
|
}
|
||||||
Command::Balance { } => {
|
Command::Balance { } => {
|
||||||
let mut w = Wallet::open(cfg).await.unwrap();
|
info!("Onchain balance: {}", w.onchain_balance()?);
|
||||||
info!("Onchain balance: {}", w.onchain_balance().unwrap());
|
info!("Offchain balance: {}", w.offchain_balance().await?);
|
||||||
info!("Offchain balance: {}", w.offchain_balance().await.unwrap());
|
let (claimable, unclaimable) = w.unclaimed_exits().await?;
|
||||||
let (claimable, unclaimable) = w.unclaimed_exits().await.unwrap();
|
|
||||||
if !claimable.is_empty() {
|
if !claimable.is_empty() {
|
||||||
let sum = claimable.iter().map(|i| i.spec.amount).sum::<Amount>();
|
let sum = claimable.iter().map(|i| i.spec.amount).sum::<Amount>();
|
||||||
info!("Got {} claimable exits with total value of {}", claimable.len(), sum);
|
info!("Got {} claimable exits with total value of {}", claimable.len(), sum);
|
||||||
@@ -118,29 +125,39 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
Command::Onboard { amount } => {
|
Command::Onboard { amount } => {
|
||||||
let mut w = Wallet::open(cfg).await.unwrap();
|
w.onboard(amount).await?;
|
||||||
w.onboard(amount).await.unwrap();
|
},
|
||||||
|
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 } => {
|
Command::Send { pubkey, amount } => {
|
||||||
let mut w = Wallet::open(cfg).await.unwrap();
|
|
||||||
let dest = Destination { pubkey, amount };
|
let dest = Destination { pubkey, amount };
|
||||||
w.send_payment(dest).await.unwrap();
|
w.send_payment(dest).await?;
|
||||||
},
|
},
|
||||||
Command::StartExit { } => {
|
Command::StartExit { } => {
|
||||||
let mut w = Wallet::open(cfg).await.unwrap();
|
w.start_unilateral_exit().await?;
|
||||||
w.start_unilateral_exit().await.unwrap();
|
|
||||||
},
|
},
|
||||||
Command::ClaimExit { } => {
|
Command::ClaimExit { } => {
|
||||||
let mut w = Wallet::open(cfg).await.unwrap();
|
w.claim_unilateral_exit().await?;
|
||||||
w.claim_unilateral_exit().await.unwrap();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// dev commands
|
// dev commands
|
||||||
|
|
||||||
Command::DropVtxos { } => {
|
Command::DropVtxos { } => {
|
||||||
let w = Wallet::open(cfg).await.unwrap();
|
w.drop_vtxos().await?;
|
||||||
w.drop_vtxos().await.unwrap();
|
|
||||||
info!("Dropped all vtxos");
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::io;
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use bitcoin::{secp256k1, sighash, taproot, OutPoint, Witness};
|
use bitcoin::{secp256k1, sighash, taproot, Amount, OutPoint, Witness};
|
||||||
|
|
||||||
use ark::{Vtxo, VtxoSpec};
|
use ark::{Vtxo, VtxoSpec};
|
||||||
|
|
||||||
@@ -170,8 +170,9 @@ impl Wallet {
|
|||||||
info!("No inputs we can claim.");
|
info!("No inputs we can claim.");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
debug!("Claiming the following exits: {:?}",
|
let total_amount = inputs.iter().map(|i| i.spec.amount).sum::<Amount>();
|
||||||
inputs.iter().map(|i| i.utxo.to_string()).collect::<Vec<_>>(),
|
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)?;
|
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.
|
// Then update the database and only set the remaining inputs as to do.
|
||||||
self.db.store_claim_inputs(&remaining).context("failed db update")?;
|
self.db.store_claim_inputs(&remaining).context("failed db update")?;
|
||||||
|
|
||||||
|
info!("Successfully claimed total value of {}", total_amount);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ pub struct ArkInfo {
|
|||||||
pub vtxo_exit_delta: u16,
|
pub vtxo_exit_delta: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub network: Network,
|
pub network: Network,
|
||||||
pub datadir: PathBuf,
|
pub datadir: PathBuf,
|
||||||
@@ -181,7 +182,6 @@ impl Wallet {
|
|||||||
|
|
||||||
// We ask the ASP to cosign our onboard unlock tx.
|
// We ask the ASP to cosign our onboard unlock tx.
|
||||||
let (user_part, priv_user_part) = ark::onboard::new_user(spec, utxo);
|
let (user_part, priv_user_part) = ark::onboard::new_user(spec, utxo);
|
||||||
trace!("User part for onboard: {:#?}", user_part);
|
|
||||||
let asp_part = {
|
let asp_part = {
|
||||||
let res = self.asp.request_onboard_cosign(arkd_rpc_client::OnboardCosignRequest {
|
let res = self.asp.request_onboard_cosign(arkd_rpc_client::OnboardCosignRequest {
|
||||||
user_part: {
|
user_part: {
|
||||||
@@ -284,6 +284,10 @@ impl Wallet {
|
|||||||
Ok(())
|
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<()> {
|
pub async fn send_payment(&mut self, destination: Destination) -> anyhow::Result<()> {
|
||||||
self.sync_ark().await.context("ark sync error")?;
|
self.sync_ark().await.context("ark sync error")?;
|
||||||
|
|
||||||
@@ -340,7 +344,8 @@ impl Wallet {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// The round has now started. We can submit our payment.
|
// 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 {
|
self.asp.submit_payment(rpc::SubmitPaymentRequest {
|
||||||
cosign_pubkey: cosign_key.public_key().serialize().to_vec(),
|
cosign_pubkey: cosign_key.public_key().serialize().to_vec(),
|
||||||
input_vtxos: input_vtxos.iter().map(|v| v.encode()).collect(),
|
input_vtxos: input_vtxos.iter().map(|v| v.encode()).collect(),
|
||||||
@@ -405,7 +410,9 @@ impl Wallet {
|
|||||||
|
|
||||||
// Make forfeit signatures.
|
// Make forfeit signatures.
|
||||||
let connectors = ConnectorChain::new(
|
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 forfeit_signatures = input_vtxos.iter().map(|v| {
|
||||||
let sigs = connectors.connectors().enumerate().map(|(i, conn)| {
|
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.
|
// Make vtxo signatures from top to bottom, just like sighashes are returned.
|
||||||
let sighashes = vtxo_tree.sighashes(vtxos_utxo);
|
let sighashes = vtxo_tree.sighashes(vtxos_utxo);
|
||||||
trace!("sighashes: {:?}", sighashes);
|
trace!("vtxo sighashes: {:?}", sighashes);
|
||||||
assert_eq!(sighashes.len(), vtxo_agg_nonces.len());
|
assert_eq!(sighashes.len(), vtxo_agg_nonces.len());
|
||||||
let signatures = iter::zip(sec_nonces.into_iter(), iter::zip(sighashes, vtxo_agg_nonces))
|
let signatures = iter::zip(sec_nonces.into_iter(), iter::zip(sighashes, vtxo_agg_nonces))
|
||||||
.map(|(sec_nonce, (sighash, agg_nonce))| {
|
.map(|(sec_nonce, (sighash, agg_nonce))| {
|
||||||
@@ -443,7 +450,6 @@ impl Wallet {
|
|||||||
None,
|
None,
|
||||||
).0
|
).0
|
||||||
}).collect::<Vec<_>>();
|
}).collect::<Vec<_>>();
|
||||||
trace!("Sending signatures to ASP");
|
|
||||||
self.asp.provide_signatures(rpc::RoundSignatures {
|
self.asp.provide_signatures(rpc::RoundSignatures {
|
||||||
forfeit: forfeit_signatures.into_iter().map(|(id, sigs)| {
|
forfeit: forfeit_signatures.into_iter().map(|(id, sigs)| {
|
||||||
rpc::ForfeitSignatures {
|
rpc::ForfeitSignatures {
|
||||||
|
|||||||
Reference in New Issue
Block a user