Experimenting with two-party PSBT signing.
This commit is contained in:
@@ -39,17 +39,45 @@ impl EsploraWallet {
|
|||||||
recipient: Address,
|
recipient: Address,
|
||||||
amount: Amount,
|
amount: Amount,
|
||||||
) -> Result<bitcoin::Transaction, anyhow::Error> {
|
) -> Result<bitcoin::Transaction, anyhow::Error> {
|
||||||
let mut tx_builder = self.wallet.build_tx();
|
let mut tx_builder = self.build_tx()?;
|
||||||
tx_builder
|
tx_builder
|
||||||
.add_recipient(recipient.script_pubkey(), amount)
|
.add_recipient(recipient.script_pubkey(), amount)
|
||||||
.enable_rbf();
|
.enable_rbf();
|
||||||
let mut psbt = tx_builder.finish()?;
|
let mut psbt = tx_builder.finish()?;
|
||||||
let finalized = self.wallet.sign(&mut psbt, SignOptions::default())?;
|
let tx = self.sign(&mut psbt, true)?.extract_tx()?;
|
||||||
assert!(finalized);
|
|
||||||
let tx = psbt.extract_tx()?;
|
|
||||||
Ok(tx)
|
Ok(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn build_tx(
|
||||||
|
&mut self,
|
||||||
|
) -> Result<
|
||||||
|
bdk_wallet::TxBuilder<bdk_wallet::wallet::coin_selection::BranchAndBoundCoinSelection>,
|
||||||
|
anyhow::Error,
|
||||||
|
> {
|
||||||
|
Ok(self.wallet.build_tx())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn sign(
|
||||||
|
&self,
|
||||||
|
psbt: &mut bitcoin::Psbt,
|
||||||
|
finalize: bool,
|
||||||
|
) -> Result<bitcoin::Psbt, anyhow::Error> {
|
||||||
|
tracing::info!("{} signing PSBT", self.name);
|
||||||
|
|
||||||
|
let options = SignOptions {
|
||||||
|
try_finalize: finalize,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let finalized = self.wallet.sign(psbt, options)?;
|
||||||
|
|
||||||
|
// make sure the PSBT is finalized if we asked for it
|
||||||
|
if finalize {
|
||||||
|
assert!(finalized)
|
||||||
|
}
|
||||||
|
Ok(psbt.to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
/// Syncs the wallet with the latest state of the Bitcoin blockchain
|
/// Syncs the wallet with the latest state of the Bitcoin blockchain
|
||||||
pub(crate) async fn sync(&mut self) -> Result<(), anyhow::Error> {
|
pub(crate) async fn sync(&mut self) -> Result<(), anyhow::Error> {
|
||||||
tracing::info!("{} full scan sync start", self.name);
|
tracing::info!("{} full scan sync start", self.name);
|
||||||
@@ -105,6 +133,10 @@ impl EsploraWallet {
|
|||||||
);
|
);
|
||||||
self.client.broadcast(tx).await
|
self.client.broadcast(tx).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn wallet(&mut self) -> &Wallet {
|
||||||
|
&self.wallet
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a Bitcoin descriptor wallet with the mnemonic in the given user directory.
|
/// Creates a Bitcoin descriptor wallet with the mnemonic in the given user directory.
|
||||||
|
|||||||
@@ -4,25 +4,13 @@ use tracing_subscriber::filter::EnvFilter;
|
|||||||
use tracing_subscriber::util::SubscriberInitExt;
|
use tracing_subscriber::util::SubscriberInitExt;
|
||||||
use tracing_subscriber::{fmt, layer::SubscriberExt};
|
use tracing_subscriber::{fmt, layer::SubscriberExt};
|
||||||
|
|
||||||
|
use super::clients::esplora::EsploraWallet;
|
||||||
|
|
||||||
pub(crate) async fn simple_transfer() -> Result<(), anyhow::Error> {
|
pub(crate) async fn simple_transfer() -> Result<(), anyhow::Error> {
|
||||||
tracing_setup();
|
let (mut dave, mut sammy) = setup().await?;
|
||||||
|
|
||||||
let mut dave = clients::esplora::create_wallet("dave", Network::Signet)?;
|
|
||||||
let mut sammy = clients::esplora::create_wallet("sammy", Network::Signet)?;
|
|
||||||
|
|
||||||
let _next_address = dave.next_unused_address()?;
|
|
||||||
|
|
||||||
let _ = dave.balance();
|
|
||||||
dave.sync().await?;
|
|
||||||
let _ = dave.balance();
|
|
||||||
|
|
||||||
let _sammy = sammy.balance();
|
|
||||||
sammy.sync().await?;
|
|
||||||
let _ = sammy.balance();
|
|
||||||
|
|
||||||
|
|
||||||
let send_amount = Amount::from_sat(500);
|
let send_amount = Amount::from_sat(500);
|
||||||
ensure_enough_sats(dave, send_amount);
|
ensure_enough_sats(&dave, send_amount);
|
||||||
|
|
||||||
let sammy_address = sammy.next_unused_address()?.address;
|
let sammy_address = sammy.next_unused_address()?.address;
|
||||||
let tx = dave.build_and_sign_send_tx(sammy_address, send_amount)?;
|
let tx = dave.build_and_sign_send_tx(sammy_address, send_amount)?;
|
||||||
@@ -31,8 +19,21 @@ pub(crate) async fn simple_transfer() -> Result<(), anyhow::Error> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn setup() -> Result<(EsploraWallet, EsploraWallet), anyhow::Error> {
|
||||||
|
tracing_setup();
|
||||||
|
let mut dave = clients::esplora::create_wallet("dave", Network::Signet)?;
|
||||||
|
let mut sammy = clients::esplora::create_wallet("sammy", Network::Signet)?;
|
||||||
|
let _ = dave.balance();
|
||||||
|
dave.sync().await?;
|
||||||
|
let _ = dave.balance();
|
||||||
|
let _sammy = sammy.balance();
|
||||||
|
sammy.sync().await?;
|
||||||
|
let _ = sammy.balance();
|
||||||
|
Ok((dave, sammy))
|
||||||
|
}
|
||||||
|
|
||||||
/// Exit if the wallet does not have enough sats to send.
|
/// Exit if the wallet does not have enough sats to send.
|
||||||
fn ensure_enough_sats(wallet: EsploraWallet, send_amount: bitcoin::Amount) -> _ {
|
fn ensure_enough_sats(wallet: &EsploraWallet, send_amount: bitcoin::Amount) {
|
||||||
if wallet.balance().total() < send_amount {
|
if wallet.balance().total() < send_amount {
|
||||||
tracing::error!(
|
tracing::error!(
|
||||||
"Please send at least {} sats to the receiving address. Exiting.",
|
"Please send at least {} sats to the receiving address. Exiting.",
|
||||||
@@ -43,12 +44,38 @@ fn ensure_enough_sats(wallet: EsploraWallet, send_amount: bitcoin::Amount) -> _
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn htlc() -> anyhow::Result<()> {
|
pub(crate) async fn htlc() -> anyhow::Result<()> {
|
||||||
// dave will be our sender
|
let (mut dave, mut sammy) = setup().await?;
|
||||||
let mut dave = clients::esplora::create_wallet("dave", Network::Signet)?;
|
|
||||||
|
|
||||||
// sammy will be our liquidity router
|
// format a new commitment transaction like in Lightning
|
||||||
let mut sammy = clients::esplora::create_wallet("sammy", Network::Signet)?;
|
let mut commitment_builder = dave.build_tx()?;
|
||||||
|
let amount = Amount::from_sat(500);
|
||||||
|
let recipient = sammy.next_unused_address()?.script_pubkey();
|
||||||
|
commitment_builder.add_recipient(recipient, amount);
|
||||||
|
commitment_builder.enable_rbf();
|
||||||
|
let psbt = commitment_builder
|
||||||
|
.finish()
|
||||||
|
.expect("unable to build commitment");
|
||||||
|
|
||||||
|
// sign the commitment transaction
|
||||||
|
let mut dave_psbt = dave.sign(&mut psbt.clone(), false)?;
|
||||||
|
let sammy_psbt = sammy.sign(&mut psbt.clone(), false)?;
|
||||||
|
|
||||||
|
dave_psbt
|
||||||
|
.combine(sammy_psbt)
|
||||||
|
.expect("problem combining bitcoin PSBTs"); // these guys love mutability
|
||||||
|
|
||||||
|
let finalized = dave
|
||||||
|
.wallet()
|
||||||
|
.finalize_psbt(&mut dave_psbt, bdk_wallet::SignOptions::default())
|
||||||
|
.expect("couldn't finalize");
|
||||||
|
|
||||||
|
assert!(finalized);
|
||||||
|
let tx = dave_psbt.extract_tx()?;
|
||||||
|
|
||||||
|
let _ = dave.broadcast(&tx).await.expect("couldn't broadcast");
|
||||||
|
|
||||||
|
let _ = sammy.sync();
|
||||||
|
sammy.balance();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user