latest_update_id: u64,
commitment_transaction_number_obscure_factor: u64,
+ destination_script: Script,
key_storage: Storage<ChanSigner>,
their_htlc_base_key: Option<PublicKey>,
their_delayed_payment_base_key: Option<PublicKey>,
fn eq(&self, other: &Self) -> bool {
if self.latest_update_id != other.latest_update_id ||
self.commitment_transaction_number_obscure_factor != other.commitment_transaction_number_obscure_factor ||
+ self.destination_script != other.destination_script ||
self.key_storage != other.key_storage ||
self.their_htlc_base_key != other.their_htlc_base_key ||
self.their_delayed_payment_base_key != other.their_delayed_payment_base_key ||
// Set in initial Channel-object creation, so should always be set by now:
U48(self.commitment_transaction_number_obscure_factor).write(writer)?;
+ self.destination_script.write(writer)?;
match self.key_storage {
Storage::Local { ref keys, ref funding_key, ref revocation_base_key, ref htlc_base_key, ref delayed_payment_base_key, ref payment_base_key, ref shutdown_pubkey, ref funding_info, ref current_remote_commitment_txid, ref prev_remote_commitment_txid } => {
writer.write_all(&[0; 1])?;
latest_update_id: 0,
commitment_transaction_number_obscure_factor,
+ destination_script: destination_script.clone(),
key_storage: Storage::Local {
keys,
funding_key,
// can also be resolved in a few other ways which can have more than one output. Thus,
// we call is_resolving_htlc_output here outside of the tx.input.len() == 1 check.
self.is_resolving_htlc_output(&tx, height);
+
+ if let Some(spendable_output) = self.is_paying_spendable_output(&tx) {
+ spendable_outputs.push(spendable_output);
+ }
}
let should_broadcast = if let Some(_) = self.current_local_signed_commitment_tx {
self.would_broadcast_at_height(height)
}
}
}
- let mut spendable_output = self.onchain_tx_handler.block_connected(txn_matched, claimable_outpoints, height, &*broadcaster, &*fee_estimator);
- spendable_outputs.append(&mut spendable_output);
+ self.onchain_tx_handler.block_connected(txn_matched, claimable_outpoints, height, &*broadcaster, &*fee_estimator);
self.last_block_hash = block_hash.clone();
for &(ref txid, ref output_scripts) in watch_outputs.iter() {
}
}
}
+
+ /// Check if any transaction broadcasted is paying fund back to some address we can assume to own
+ fn is_paying_spendable_output(&self, tx: &Transaction) -> Option<SpendableOutputDescriptor> {
+ for (i, outp) in tx.output.iter().enumerate() { // There is max one spendable output for any channel tx, including ones generated by us
+ if outp.script_pubkey == self.destination_script {
+ return Some(SpendableOutputDescriptor::StaticOutput {
+ outpoint: BitcoinOutPoint { txid: tx.txid(), vout: i as u32 },
+ output: outp.clone(),
+ });
+ }
+ }
+ None
+ }
}
const MAX_ALLOC_SIZE: usize = 64*1024;
let latest_update_id: u64 = Readable::read(reader)?;
let commitment_transaction_number_obscure_factor = <U48 as Readable>::read(reader)?.0;
+ let destination_script = Readable::read(reader)?;
+
let key_storage = match <u8 as Readable>::read(reader)? {
0 => {
let keys = Readable::read(reader)?;
latest_update_id,
commitment_transaction_number_obscure_factor,
+ destination_script,
+
key_storage,
their_htlc_base_key,
their_delayed_payment_base_key,
assert_eq!(revoked_local_txn[0].input[0].previous_output.txid, chan.3.txid());
claim_payment(&nodes[0], &vec!(&nodes[1])[..], payment_preimage, 3_000_000);
- let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
+ let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 1);
check_closed_broadcast!(nodes[1], false);
check_added_monitors!(nodes[1], 1);
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
+ let header_1 = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
+ nodes[1].block_notifier.block_connected(&Block { header: header_1, txdata: vec![node_txn[0].clone()] }, 2);
+
let spend_txn = check_spendable_outputs!(nodes[1], 1);
assert_eq!(spend_txn.len(), 3);
- assert_eq!(spend_txn[0], spend_txn[2]); // to_remote output on revoked remote commitment_tx
+ assert_eq!(spend_txn[0], spend_txn[1]); // to_remote output on revoked remote commitment_tx
check_spends!(spend_txn[0], revoked_local_txn[0]);
- check_spends!(spend_txn[1], node_txn[0]);
+ check_spends!(spend_txn[2], node_txn[0]);
}
#[test]
assert_eq!(node_txn.len(), 3);
check_spends!(node_txn[0], commitment_tx[0]);
assert_eq!(node_txn[0].input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
-eprintln!("{:?}", node_txn[1]);
check_spends!(node_txn[1], chan_1.3);
check_spends!(node_txn[2], node_txn[1]);
- let spend_txn = check_spendable_outputs!(nodes[1], 1); // , 0, 0, 1, 1);
+ let header_1 = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
+ nodes[1].block_notifier.block_connected(&Block { header: header_1, txdata: vec![node_txn[0].clone()] }, 1);
+
+ let spend_txn = check_spendable_outputs!(nodes[1], 1);
assert_eq!(spend_txn.len(), 1);
check_spends!(spend_txn[0], node_txn[0]);
}
assert_eq!(node_txn[0].input.len(), 2);
check_spends!(node_txn[0], revoked_local_txn[0]);
+ let header_1 = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
+ nodes[1].block_notifier.block_connected(&Block { header: header_1, txdata: vec![node_txn[0].clone()] }, 2);
+
let spend_txn = check_spendable_outputs!(nodes[1], 1);
assert_eq!(spend_txn.len(), 1);
check_spends!(spend_txn[0], node_txn[0]);
check_spends!(revoked_htlc_txn[1], chan_1.3);
// B will generate justice tx from A's revoked commitment/HTLC tx
- nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![revoked_local_txn[0].clone(), revoked_htlc_txn[0].clone()] }, 1);
+ nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![revoked_local_txn[0].clone(), revoked_htlc_txn[0].clone()] }, 0);
check_closed_broadcast!(nodes[1], false);
check_added_monitors!(nodes[1], 1);
assert_eq!(node_txn[3].input.len(), 1);
check_spends!(node_txn[3], revoked_local_txn[0]);
+ let header_1 = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
+ nodes[1].block_notifier.block_connected(&Block { header: header_1, txdata: vec![node_txn[0].clone(), node_txn[2].clone()] }, 1);
+
// Check B's ChannelMonitor was able to generate the right spendable output descriptor
let spend_txn = check_spendable_outputs!(nodes[1], 1);
assert_eq!(spend_txn.len(), 2);
assert_eq!(node_txn[2].input.len(), 1);
check_spends!(node_txn[2], revoked_htlc_txn[0]);
+ let header_1 = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
+ nodes[0].block_notifier.block_connected(&Block { header: header_1, txdata: vec![node_txn[0].clone(), node_txn[2].clone()] }, 1);
+
// Check A's ChannelMonitor was able to generate the right spendable output descriptor
let spend_txn = check_spendable_outputs!(nodes[0], 1);
assert_eq!(spend_txn.len(), 5); // Duplicated SpendableOutput due to block rescan after revoked htlc output tracking
+ assert_eq!(spend_txn[0], spend_txn[1]);
assert_eq!(spend_txn[0], spend_txn[2]);
check_spends!(spend_txn[0], revoked_local_txn[0]); // spending to_remote output from revoked local tx
- check_spends!(spend_txn[1], node_txn[0]); // spending justice tx output from revoked local tx htlc received output
- check_spends!(spend_txn[3], node_txn[2]); // spending justice tx output on htlc success tx
+ check_spends!(spend_txn[3], node_txn[0]); // spending justice tx output from revoked local tx htlc received output
+ check_spends!(spend_txn[4], node_txn[2]); // spending justice tx output on htlc success tx
}
#[test]
use ln::channelmonitor::{ANTI_REORG_DELAY, CLTV_SHARED_CLAIM_BUFFER, InputMaterial, ClaimRequest};
use ln::chan_utils::HTLCType;
use chain::chaininterface::{FeeEstimator, BroadcasterInterface, ConfirmationTarget, MIN_RELAY_FEE_SAT_PER_1000_WEIGHT};
-use chain::keysinterface::SpendableOutputDescriptor;
use util::logger::Logger;
use util::ser::{ReadableArgs, Readable, Writer, Writeable};
use util::byte_utils;
Some((new_timer, new_feerate, bumped_tx))
}
- pub(super) fn block_connected<B: Deref, F: Deref>(&mut self, txn_matched: &[&Transaction], claimable_outpoints: Vec<ClaimRequest>, height: u32, broadcaster: B, fee_estimator: F) -> Vec<SpendableOutputDescriptor>
+ pub(super) fn block_connected<B: Deref, F: Deref>(&mut self, txn_matched: &[&Transaction], claimable_outpoints: Vec<ClaimRequest>, height: u32, broadcaster: B, fee_estimator: F)
where B::Target: BroadcasterInterface,
F::Target: FeeEstimator
{
let mut new_claims = Vec::new();
let mut aggregated_claim = HashMap::new();
let mut aggregated_soonest = ::std::u32::MAX;
- let mut spendable_outputs = Vec::new();
// Try to aggregate outputs if their timelock expiration isn't imminent (absolute_timelock
// <= CLTV_SHARED_CLAIM_BUFFER) and they don't require an immediate nLockTime (aggregable).
self.claimable_outpoints.insert(k.clone(), (txid, height));
}
log_trace!(self, "Broadcast onchain {}", log_tx!(tx));
- spendable_outputs.push(SpendableOutputDescriptor::StaticOutput {
- outpoint: BitcoinOutPoint { txid: tx.txid(), vout: 0 },
- output: tx.output[0].clone(),
- });
broadcaster.broadcast_transaction(&tx);
}
}
} else { unreachable!(); }
}
}
-
- spendable_outputs
}
pub(super) fn block_disconnected<B: Deref, F: Deref>(&mut self, height: u32, broadcaster: B, fee_estimator: F)