Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Let consumers provide TX and TXO confirmations #14

Merged
merged 1 commit into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions bitcoin-rpc-provider/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,12 @@ impl Blockchain for BitcoinCoreProvider {
},
}
}

fn get_txo_confirmations(&self, _txo: &OutPoint) -> Result<Option<(u32, Txid)>, ManagerError> {
// TODO: We might need to get rid of this method if we want to keep supporting Bitcoin core
// as a `Blockchain`. See https://bitcoin.stackexchange.com/questions/48504/how-to-check-if-an-output-has-been-spent.
unimplemented!()
}
}

impl FeeEstimator for BitcoinCoreProvider {
Expand Down
62 changes: 47 additions & 15 deletions dlc-manager/src/chain_monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@
//! transactions of interest in the context of DLC.

use std::collections::HashMap;
use std::ops::Deref;

use bitcoin::{Block, OutPoint, Transaction, Txid};
use bitcoin::{OutPoint, Transaction, Txid};
use dlc_messages::ser_impls::{
read_ecdsa_adaptor_signature, read_hash_map, write_ecdsa_adaptor_signature, write_hash_map,
};
use lightning::ln::msgs::DecodeError;
use lightning::util::ser::{Readable, Writeable, Writer};
use secp256k1_zkp::EcdsaAdaptorSignature;

use crate::Blockchain;

/// A `ChainMonitor` keeps a list of transaction ids to watch for in the blockchain,
/// and some associated information used to apply an action when the id is seen.
#[derive(Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -125,27 +128,56 @@ impl ChainMonitor {
self.watched_tx.remove(txid);
}

/// Check if any watched transactions are part of the block, confirming them if so.
///
/// # Panics
///
/// Panics if the new block's height is not exactly one more than the last processed height.
pub(crate) fn process_block(&mut self, block: &Block, height: u64) {
assert_eq!(self.last_height + 1, height);
/// Check if any watched transactions have been confirmed.
pub(crate) fn check_transactions<B>(&mut self, blockchain: &B)
where
B: Deref,
B::Target: Blockchain,
{
for (txid, state) in self.watched_tx.iter_mut() {
let confirmations = match blockchain.get_transaction_confirmations(txid) {
Ok(confirmations) => confirmations,
Err(e) => {
log::error!("Failed to get transaction confirmations for {txid}: {e}");
continue;
}
};

if confirmations > 0 {
let tx = match blockchain.get_transaction(txid) {
Ok(tx) => tx,
Err(e) => {
log::error!("Failed to get transaction for {txid}: {e}");
continue;
}
};

for tx in block.txdata.iter() {
if let Some(state) = self.watched_tx.get_mut(&tx.txid()) {
state.confirm(tx.clone());
}
}

for txin in tx.input.iter() {
if let Some(state) = self.watched_txo.get_mut(&txin.previous_output) {
state.confirm(tx.clone())
for (txo, state) in self.watched_txo.iter_mut() {
let (confirmations, txid) = match blockchain.get_txo_confirmations(txo) {
Ok(Some((confirmations, txid))) => (confirmations, txid),
Ok(None) => continue,
Err(e) => {
log::error!("Failed to get transaction confirmations for {txo}: {e}");
continue;
}
};

if confirmations > 0 {
let tx = match blockchain.get_transaction(&txid) {
Ok(tx) => tx,
Err(e) => {
log::error!("Failed to get transaction for {txid}: {e}");
continue;
}
};

state.confirm(tx.clone());
}
}

self.last_height += 1;
}

/// All the currently watched transactions which have been confirmed.
Expand Down
7 changes: 6 additions & 1 deletion dlc-manager/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,13 @@ pub trait Blockchain {
fn get_block_at_height(&self, height: u64) -> Result<Block, Error>;
/// Get the transaction with given id.
fn get_transaction(&self, tx_id: &Txid) -> Result<Transaction, Error>;
/// Get the number of confirmation for the transaction with given id.
/// Get the number of confirmations for the transaction with given id.
fn get_transaction_confirmations(&self, tx_id: &Txid) -> Result<u32, Error>;
/// Get the number of confirmations for the given transaction output.
///
/// Also returns the [`Txid`] of the transaction where the transaction output is used as an
/// input.
fn get_txo_confirmations(&self, txo: &OutPoint) -> Result<Option<(u32, Txid)>, Error>;
}

/// Storage trait provides functionalities to store and retrieve DLCs.
Expand Down
34 changes: 5 additions & 29 deletions dlc-manager/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,38 +374,14 @@ where
Ok((contract_id, counter_party, accept_msg))
}

/// Function to update the state of the [`ChainMonitor`] with new
/// blocks.
///
/// Consumers **MUST** call this periodically in order to
/// determine when pending transactions reach confirmation.
pub fn periodic_chain_monitor(&self) -> Result<(), Error> {
let cur_height = self.blockchain.get_blockchain_height()?;
let last_height = self.chain_monitor.lock().unwrap().last_height;

// TODO(luckysori): We could end up reprocessing a block at
// the same height if there is a reorg.
if cur_height < last_height {
return Err(Error::InvalidState(
"Current height is lower than last height.".to_string(),
));
}

for height in last_height + 1..=cur_height {
let block = self.blockchain.get_block_at_height(height)?;

self.chain_monitor
.lock()
.unwrap()
.process_block(&block, height);
}

Ok(())
}

/// Function to call to check the state of the currently executing DLCs and
/// update them if possible.
pub fn periodic_check(&self) -> Result<(), Error> {
{
let mut chain_monitor = self.chain_monitor.lock().unwrap();
chain_monitor.check_transactions(&self.blockchain);
}

self.check_signed_contracts()?;
self.check_confirmed_contracts()?;
self.check_preclosed_contracts()?;
Expand Down
1 change: 0 additions & 1 deletion dlc-manager/tests/channel_execution_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ fn alter_adaptor_sig(input: &EcdsaAdaptorSignature) -> EcdsaAdaptorSignature {
fn periodic_check(dlc_party: DlcParty) {
let dlc_manager = dlc_party.lock().unwrap();

dlc_manager.periodic_chain_monitor().unwrap();
dlc_manager.periodic_check().unwrap();
}

Expand Down
1 change: 0 additions & 1 deletion dlc-manager/tests/ln_dlc_channel_execution_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ impl LnDlcParty {
];

self.esplora_sync.sync(confirmables).unwrap();
self.dlc_manager.periodic_chain_monitor().unwrap();

self.sub_channel_manager.periodic_check();
self.dlc_manager.periodic_check().unwrap();
Expand Down
6 changes: 6 additions & 0 deletions electrs-blockchain-provider/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,12 @@ impl Blockchain for ElectrsBlockchainProvider {

Ok(0)
}

fn get_txo_confirmations(&self, _txo: &OutPoint) -> Result<Option<(u32, Txid)>, Error> {
// TODO: We might need to get rid of this method if we want to keep supporting Electrs
// as a `Blockchain`. See https://bitcoin.stackexchange.com/questions/48504/how-to-check-if-an-output-has-been-spent.
unimplemented!()
}
}

impl simple_wallet::WalletBlockchainProvider for ElectrsBlockchainProvider {
Expand Down
5 changes: 4 additions & 1 deletion mocks/src/mock_blockchain.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{ops::Deref, sync::Mutex};

use bitcoin::{Transaction, Txid};
use bitcoin::{hashes::Hash, OutPoint, Transaction, Txid};
use dlc_manager::error::Error;
use lightning::chain::chaininterface::BroadcasterInterface;
use simple_wallet::WalletBlockchainProvider;
Expand Down Expand Up @@ -123,4 +123,7 @@ where
fn get_transaction_confirmations(&self, _tx_id: &Txid) -> Result<u32, Error> {
Ok(6)
}
fn get_txo_confirmations(&self, _txo: &OutPoint) -> Result<Option<(u32, Txid)>, Error> {
Ok(Some((6, Txid::all_zeros())))
}
}
Loading