use bitcoin::block::{Block, Header};
use bitcoin::constants::genesis_block;
use bitcoin::hash_types::{BlockHash, Txid};
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::{Hash, HashEngine};
use bitcoin::network::Network;
use bitcoin::script::{Script, ScriptBuf};
use bitcoin::secp256k1::PublicKey;
use crate::chain::channelmonitor::{
ChannelMonitor, ChannelMonitorUpdate, MonitorEvent, ANTI_REORG_DELAY,
};
use crate::chain::transaction::{OutPoint, TransactionData};
use crate::ln::types::ChannelId;
use crate::sign::ecdsa::EcdsaChannelSigner;
use crate::sign::HTLCDescriptor;
use core::ops::Deref;
#[allow(unused_imports)]
use crate::prelude::*;
pub mod chaininterface;
pub mod chainmonitor;
pub mod channelmonitor;
pub(crate) mod onchaintx;
pub(crate) mod package;
pub mod transaction;
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct BlockLocator {
pub block_hash: BlockHash,
pub height: u32,
pub previous_blocks: [Option<BlockHash>; ANTI_REORG_DELAY as usize * 2],
}
impl BlockLocator {
pub fn from_network(network: Network) -> Self {
let block_hash = genesis_block(network).header.block_hash();
let previous_blocks = [None; ANTI_REORG_DELAY as usize * 2];
BlockLocator { block_hash, height: 0, previous_blocks }
}
pub fn new(block_hash: BlockHash, height: u32) -> Self {
let previous_blocks = [None; ANTI_REORG_DELAY as usize * 2];
BlockLocator { block_hash, height, previous_blocks }
}
pub fn advance(&mut self, new_hash: BlockHash) {
for i in (1..self.previous_blocks.len()).rev() {
self.previous_blocks[i] = self.previous_blocks[i - 1];
}
self.previous_blocks[0] = Some(self.block_hash);
self.block_hash = new_hash;
self.height += 1;
}
pub fn update_for_new_tip(&mut self, new_tip_hash: BlockHash, new_tip_height: u32) {
if new_tip_height == self.height + 1 {
self.advance(new_tip_hash);
} else {
*self = BlockLocator::new(new_tip_hash, new_tip_height);
}
}
pub fn get_hash_at_height(&self, height: u32) -> Option<BlockHash> {
if height > self.height {
return None;
}
if height == self.height {
return Some(self.block_hash);
}
let offset = self.height.saturating_sub(height) as usize;
if offset >= 1 && offset <= self.previous_blocks.len() {
self.previous_blocks[offset - 1]
} else {
None
}
}
pub fn find_common_ancestor(&self, other: &BlockLocator) -> Option<(BlockHash, u32)> {
if self.block_hash == other.block_hash && self.height == other.height {
return Some((self.block_hash, self.height));
}
let min_height = self.height.saturating_sub(self.previous_blocks.len() as u32);
for check_height in (min_height..=self.height).rev() {
if let Some(self_hash) = self.get_hash_at_height(check_height) {
if let Some(other_hash) = other.get_hash_at_height(check_height) {
if self_hash == other_hash {
return Some((self_hash, check_height));
}
}
}
}
None
}
}
impl_writeable_tlv_based!(BlockLocator, {
(0, block_hash, required),
(1, previous_blocks_read, (legacy, [Option<BlockHash>; 6 * 2], |_| Ok(()), |us: &BlockLocator| Some(us.previous_blocks))),
(2, height, required),
(unused, previous_blocks, (static_value, previous_blocks_read.unwrap_or([None; 6 * 2]))),
});
pub trait Listen {
fn filtered_block_connected(&self, header: &Header, txdata: &TransactionData, height: u32);
fn block_connected(&self, block: &Block, height: u32) {
let txdata: Vec<_> = block.txdata.iter().enumerate().collect();
self.filtered_block_connected(&block.header, &txdata, height);
}
fn blocks_disconnected(&self, fork_point_block: BlockLocator);
}
pub trait Confirm {
fn transactions_confirmed(&self, header: &Header, txdata: &TransactionData, height: u32);
fn transaction_unconfirmed(&self, txid: &Txid);
fn best_block_updated(&self, header: &Header, height: u32);
fn get_relevant_txids(&self) -> Vec<(Txid, u32, Option<BlockHash>)>;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ChannelMonitorUpdateStatus {
Completed,
InProgress,
UnrecoverableError,
}
pub trait Watch<ChannelSigner: EcdsaChannelSigner> {
fn watch_channel(
&self, channel_id: ChannelId, monitor: ChannelMonitor<ChannelSigner>,
) -> Result<ChannelMonitorUpdateStatus, ()>;
fn update_channel(
&self, channel_id: ChannelId, update: &ChannelMonitorUpdate,
) -> ChannelMonitorUpdateStatus;
fn release_pending_monitor_events(
&self,
) -> Vec<(OutPoint, ChannelId, Vec<MonitorEvent>, PublicKey)>;
}
impl<ChannelSigner: EcdsaChannelSigner, T: Watch<ChannelSigner> + ?Sized, W: Deref<Target = T>>
Watch<ChannelSigner> for W
{
fn watch_channel(
&self, channel_id: ChannelId, monitor: ChannelMonitor<ChannelSigner>,
) -> Result<ChannelMonitorUpdateStatus, ()> {
self.deref().watch_channel(channel_id, monitor)
}
fn update_channel(
&self, channel_id: ChannelId, update: &ChannelMonitorUpdate,
) -> ChannelMonitorUpdateStatus {
self.deref().update_channel(channel_id, update)
}
fn release_pending_monitor_events(
&self,
) -> Vec<(OutPoint, ChannelId, Vec<MonitorEvent>, PublicKey)> {
self.deref().release_pending_monitor_events()
}
}
pub trait Filter {
fn register_tx(&self, txid: &Txid, script_pubkey: &Script);
fn register_output(&self, output: WatchedOutput);
}
impl<T: Filter + ?Sized, F: Deref<Target = T>> Filter for F {
fn register_tx(&self, txid: &Txid, script_pubkey: &Script) {
self.deref().register_tx(txid, script_pubkey)
}
fn register_output(&self, output: WatchedOutput) {
self.deref().register_output(output)
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct WatchedOutput {
pub block_hash: Option<BlockHash>,
pub outpoint: OutPoint,
pub script_pubkey: ScriptBuf,
}
impl<T: Listen> Listen for dyn core::ops::Deref<Target = T> {
fn filtered_block_connected(&self, header: &Header, txdata: &TransactionData, height: u32) {
(**self).filtered_block_connected(header, txdata, height);
}
fn blocks_disconnected(&self, fork_point: BlockLocator) {
(**self).blocks_disconnected(fork_point);
}
}
impl<T: core::ops::Deref, U: core::ops::Deref> Listen for (T, U)
where
T::Target: Listen,
U::Target: Listen,
{
fn filtered_block_connected(&self, header: &Header, txdata: &TransactionData, height: u32) {
self.0.filtered_block_connected(header, txdata, height);
self.1.filtered_block_connected(header, txdata, height);
}
fn blocks_disconnected(&self, fork_point: BlockLocator) {
self.0.blocks_disconnected(fork_point);
self.1.blocks_disconnected(fork_point);
}
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub struct ClaimId(pub [u8; 32]);
impl ClaimId {
pub(crate) fn from_htlcs(htlcs: &[HTLCDescriptor]) -> ClaimId {
let mut engine = Sha256::engine();
for htlc in htlcs {
engine.input(&htlc.commitment_txid.to_byte_array());
engine.input(&htlc.htlc.transaction_output_index.unwrap().to_be_bytes());
}
ClaimId(Sha256::from_engine(engine).to_byte_array())
}
pub(crate) fn step_with_bytes(&self, bytes: &[u8]) -> ClaimId {
let mut engine = Sha256::engine();
engine.input(&self.0);
engine.input(bytes);
ClaimId(Sha256::from_engine(engine).to_byte_array())
}
}
#[cfg(test)]
mod tests {
use super::*;
use bitcoin::hashes::Hash;
#[test]
fn test_best_block() {
let hash1 = BlockHash::from_slice(&[1; 32]).unwrap();
let mut chain_a = BlockLocator::new(hash1, 100);
let mut chain_b = BlockLocator::new(hash1, 100);
assert_eq!(chain_a.get_hash_at_height(100), Some(hash1));
assert_eq!(chain_a.get_hash_at_height(101), None);
assert_eq!(chain_a.get_hash_at_height(99), None);
assert_eq!(chain_a.find_common_ancestor(&chain_b), Some((hash1, 100)));
let hash2 = BlockHash::from_slice(&[2; 32]).unwrap();
chain_a.advance(hash2);
assert_eq!(chain_a.height, 101);
assert_eq!(chain_a.block_hash, hash2);
assert_eq!(chain_a.previous_blocks[0], Some(hash1));
assert_eq!(chain_a.get_hash_at_height(101), Some(hash2));
assert_eq!(chain_a.get_hash_at_height(100), Some(hash1));
assert_eq!(chain_a.find_common_ancestor(&chain_b), Some((hash1, 100)));
let hash_b3 = BlockHash::from_slice(&[33; 32]).unwrap();
chain_b.advance(hash_b3);
assert_eq!(chain_a.find_common_ancestor(&chain_b), Some((hash1, 100)));
let hash_other = BlockHash::from_slice(&[99; 32]).unwrap();
let chain_c = BlockLocator::new(hash_other, 200);
assert_eq!(chain_a.find_common_ancestor(&chain_c), None);
}
}