-
Notifications
You must be signed in to change notification settings - Fork 80
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add
OutputSweeper
and persistence utils
We add an `OutputSweeper` object that will keep track of sweepable outputs. To this end, we start by adding the general structures and the required utilities to persist the `SpendableOutputInfo` to our `KVStore`.
- Loading branch information
Showing
4 changed files
with
170 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
use crate::hex_utils; | ||
use crate::io::{KVStore, SPENDABLE_OUTPUT_INFO_PERSISTENCE_NAMESPACE}; | ||
use crate::logger::{log_debug, log_error, Logger}; | ||
use crate::wallet::Wallet; | ||
use crate::{Error, KeysManager}; | ||
|
||
use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator}; | ||
use lightning::chain::BestBlock; | ||
use lightning::impl_writeable_tlv_based; | ||
use lightning::sign::{EntropySource, SpendableOutputDescriptor}; | ||
use lightning::util::ser::Writeable; | ||
|
||
use bitcoin::secp256k1::Secp256k1; | ||
use bitcoin::{BlockHash, LockTime, PackedLockTime, Transaction}; | ||
|
||
use std::ops::Deref; | ||
use std::sync::{Arc, Mutex}; | ||
|
||
#[derive(Clone, Debug, PartialEq, Eq)] | ||
pub(crate) struct SpendableOutputInfo { | ||
id: [u8; 32], | ||
descriptor: SpendableOutputDescriptor, | ||
spending_tx: Option<Transaction>, | ||
broadcast_height: Option<u32>, | ||
confirmed_in_block: Option<(u32, BlockHash)>, | ||
} | ||
|
||
impl_writeable_tlv_based!(SpendableOutputInfo, { | ||
(0, id, required), | ||
(2, descriptor, required), | ||
(4, spending_tx, option), | ||
(6, broadcast_height, option), | ||
(8, confirmed_in_block, option), | ||
}); | ||
|
||
pub(crate) struct OutputSweeper<K: KVStore + Sync + Send, L: Deref> | ||
where | ||
L::Target: Logger, | ||
{ | ||
outputs: Mutex<Vec<SpendableOutputInfo>>, | ||
wallet: Arc<Wallet<bdk::database::SqliteDatabase, L>>, | ||
keys_manager: Arc<KeysManager>, | ||
kv_store: Arc<K>, | ||
best_block: Mutex<BestBlock>, | ||
logger: L, | ||
} | ||
|
||
impl<K: KVStore + Sync + Send, L: Deref> OutputSweeper<K, L> | ||
where | ||
L::Target: Logger, | ||
{ | ||
pub(crate) fn new( | ||
outputs: Vec<SpendableOutputInfo>, wallet: Arc<Wallet<bdk::database::SqliteDatabase, L>>, | ||
keys_manager: Arc<KeysManager>, kv_store: Arc<K>, best_block: BestBlock, logger: L, | ||
) -> Self { | ||
let outputs = Mutex::new(outputs); | ||
let best_block = Mutex::new(best_block); | ||
Self { outputs, wallet, keys_manager, kv_store, best_block, logger } | ||
} | ||
|
||
pub(crate) fn add_outputs(&self, output_descriptors: Vec<SpendableOutputDescriptor>) { | ||
let mut locked_outputs = self.outputs.lock().unwrap(); | ||
|
||
let (spending_tx, broadcast_height) = match self.get_spending_tx(&output_descriptors) { | ||
Ok(Some(spending_tx)) => { | ||
self.wallet.broadcast_transactions(&[&spending_tx]); | ||
(Some(spending_tx), Some(self.best_block.lock().unwrap().height())) | ||
} | ||
Ok(None) => { | ||
log_debug!( | ||
self.logger, | ||
"Omitted spending static outputs: {:?}", | ||
output_descriptors | ||
); | ||
(None, None) | ||
} | ||
Err(e) => { | ||
log_error!(self.logger, "Error spending outputs: {:?}", e); | ||
(None, None) | ||
} | ||
}; | ||
|
||
for descriptor in output_descriptors { | ||
let id = self.keys_manager.get_secure_random_bytes(); | ||
let output_info = SpendableOutputInfo { | ||
id, | ||
descriptor, | ||
spending_tx: spending_tx.clone(), | ||
broadcast_height, | ||
confirmed_in_block: None, | ||
}; | ||
|
||
locked_outputs.push(output_info.clone()); | ||
match self.persist_info(&output_info) { | ||
Ok(()) => {} | ||
Err(e) => { | ||
log_error!(self.logger, "Error persisting spendable output info: {:?}", e) | ||
} | ||
} | ||
} | ||
} | ||
|
||
fn get_spending_tx( | ||
&self, output_descriptors: &Vec<SpendableOutputDescriptor>, | ||
) -> Result<Option<Transaction>, ()> { | ||
let tx_feerate = self.wallet.get_est_sat_per_1000_weight(ConfirmationTarget::Normal); | ||
|
||
let destination_address = self.wallet.get_new_address().map_err(|e| { | ||
log_error!(self.logger, "Failed to get destination address from wallet: {}", e); | ||
})?; | ||
|
||
let cur_height = self.best_block.lock().unwrap().height(); | ||
let locktime: PackedLockTime = | ||
LockTime::from_height(cur_height).map_or(PackedLockTime::ZERO, |l| l.into()); | ||
|
||
self.keys_manager.spend_spendable_outputs( | ||
&output_descriptors.iter().collect::<Vec<_>>(), | ||
Vec::new(), | ||
destination_address.script_pubkey(), | ||
tx_feerate, | ||
Some(locktime), | ||
&Secp256k1::new(), | ||
) | ||
} | ||
|
||
fn persist_info(&self, output: &SpendableOutputInfo) -> Result<(), Error> { | ||
let key = hex_utils::to_string(&output.id); | ||
let data = output.encode(); | ||
self.kv_store.write(SPENDABLE_OUTPUT_INFO_PERSISTENCE_NAMESPACE, &key, &data).map_err(|e| { | ||
log_error!( | ||
self.logger, | ||
"Write for key {}/{} failed due to: {}", | ||
SPENDABLE_OUTPUT_INFO_PERSISTENCE_NAMESPACE, | ||
key, | ||
e | ||
); | ||
Error::PersistenceFailed | ||
}) | ||
} | ||
} |