}
fn block_connected(&mut self, txn_matched: &[&Transaction], height: u32, block_hash: &Sha256dHash, broadcaster: &BroadcasterInterface, fee_estimator: &FeeEstimator)-> (Vec<(Sha256dHash, Vec<TxOut>)>, Vec<SpendableOutputDescriptor>, Vec<(HTLCSource, Option<PaymentPreimage>, PaymentHash)>) {
+ log_trace!(self, "Block {} at height {} connected with {} txn matched", block_hash, height, txn_matched.len());
let mut watch_outputs = Vec::new();
let mut spendable_outputs = Vec::new();
let mut htlc_updated = Vec::new();
- let mut bump_candidates = Vec::new();
+ let mut bump_candidates = HashMap::new();
for tx in txn_matched {
if tx.input.len() == 1 {
// Assuming our keys were not leaked (in which case we're screwed no matter what),
}
// Scan all input to verify is one of the outpoint spent is of interest for us
- let mut claimed_outpoints = Vec::new();
- let mut claimed_input_material = Vec::new();
+ let mut claimed_outputs_material = Vec::new();
for inp in &tx.input {
if let Some(ancestor_claimable_txid) = self.claimable_outpoints.get(&inp.previous_output) {
// If outpoint has claim request pending on it...
//... we need to verify equality between transaction outpoints and claim request
// outpoints to know if transaction is the original claim or a bumped one issued
// by us.
- for claim_inp in claim_material.per_input_material.keys() {
- if *claim_inp == inp.previous_output {
- claimed_outpoints.push(inp.previous_output.clone());
+ let mut set_equality = true;
+ if claim_material.per_input_material.len() != tx.input.len() {
+ set_equality = false;
+ } else {
+ for (claim_inp, tx_inp) in claim_material.per_input_material.keys().zip(tx.input.iter()) {
+ if *claim_inp != tx_inp.previous_output {
+ set_equality = false;
+ }
}
}
- if claimed_outpoints.len() == 0 && claim_material.per_input_material.len() == tx.input.len() { // If true, register claim request to be removed after reaching a block security height
+
+ // If this is our transaction (or our counterparty spent all the outputs
+ // before we could anyway), wait for ANTI_REORG_DELAY and clean the RBF
+ // tracking map.
+ if set_equality {
+ let new_event = OnchainEvent::Claim { claim_request: ancestor_claimable_txid.0.clone() };
match self.onchain_events_waiting_threshold_conf.entry(height + ANTI_REORG_DELAY - 1) {
- hash_map::Entry::Occupied(_) => {},
+ hash_map::Entry::Occupied(mut entry) => {
+ if !entry.get().contains(&new_event) {
+ entry.get_mut().push(new_event);
+ }
+ },
hash_map::Entry::Vacant(entry) => {
- entry.insert(vec![OnchainEvent::Claim { claim_request: ancestor_claimable_txid.0.clone()}]);
+ entry.insert(vec![new_event]);
}
}
} else { // If false, generate new claim request with update outpoint set
- for already_claimed in claimed_outpoints.iter() {
- if let Some(input_material) = claim_material.per_input_material.remove(&already_claimed) {
- claimed_input_material.push(input_material);
+ for input in tx.input.iter() {
+ if let Some(input_material) = claim_material.per_input_material.remove(&input.previous_output) {
+ claimed_outputs_material.push((input.previous_output, input_material));
}
}
- // Avoid bump engine using inaccurate feerate due to new transaction size
- claim_material.feerate_previous = 0;
//TODO: recompute soonest_timelock to avoid wasting a bit on fees
- bump_candidates.push((ancestor_claimable_txid.0.clone(), claim_material.clone()));
+ bump_candidates.insert(ancestor_claimable_txid.0.clone(), claim_material.clone());
}
break; //No need to iterate further, either tx is our or their
} else {
}
}
}
- for (outpoint, input_material) in claimed_outpoints.iter().zip(claimed_input_material.drain(..)) {
+ for (outpoint, input_material) in claimed_outputs_material.drain(..) {
+ let new_event = OnchainEvent::ContentiousOutpoint { outpoint, input_material };
match self.onchain_events_waiting_threshold_conf.entry(height + ANTI_REORG_DELAY - 1) {
- hash_map::Entry::Occupied(_) => {},
+ hash_map::Entry::Occupied(mut entry) => {
+ if !entry.get().contains(&new_event) {
+ entry.get_mut().push(new_event);
+ }
+ },
hash_map::Entry::Vacant(entry) => {
- entry.insert(vec![OnchainEvent::ContentiousOutpoint { outpoint: *outpoint, input_material: input_material }]);
+ entry.insert(vec![new_event]);
}
}
}
for ev in events {
match ev {
OnchainEvent::Claim { claim_request } => {
- // We may remove a whole set of claim outpoints here, as these one may have been aggregated in a single tx and claimed so atomically
- self.pending_claim_requests.remove(&claim_request);
+ // We may remove a whole set of claim outpoints here, as these one may have
+ // been aggregated in a single tx and claimed so atomically
+ if let Some(bump_material) = self.pending_claim_requests.remove(&claim_request) {
+ for outpoint in bump_material.per_input_material.keys() {
+ self.claimable_outpoints.remove(&outpoint);
+ }
+ }
},
OnchainEvent::HTLCUpdate { htlc_update } => {
log_trace!(self, "HTLC {} failure update has got enough confirmations to be passed upstream", log_bytes!((htlc_update.1).0));
}
for (ancestor_claim_txid, ref mut cached_claim_datas) in self.pending_claim_requests.iter_mut() {
if cached_claim_datas.height_timer == height {
- bump_candidates.push((ancestor_claim_txid.clone(), cached_claim_datas.clone()));
+ if let hash_map::Entry::Vacant(entry) = bump_candidates.entry(ancestor_claim_txid.clone()) {
+ entry.insert(cached_claim_datas.clone());
+ }
}
}
- for &mut (_, ref mut cached_claim_datas) in bump_candidates.iter_mut() {
+ for ref mut cached_claim_datas in bump_candidates.values_mut() {
if let Some((new_timer, new_feerate, bump_tx)) = self.bump_claim_tx(height, &cached_claim_datas, fee_estimator) {
cached_claim_datas.height_timer = new_timer;
cached_claim_datas.feerate_previous = new_feerate;
broadcaster.broadcast_transaction(&bump_tx);
}
}
- for (ancestor_claim_txid, cached_claim_datas) in bump_candidates.drain(..) {
+ for (ancestor_claim_txid, cached_claim_datas) in bump_candidates.drain() {
self.pending_claim_requests.insert(ancestor_claim_txid, cached_claim_datas);
}
self.last_block_hash = block_hash.clone();
OnchainEvent::ContentiousOutpoint { outpoint, input_material } => {
if let Some(ancestor_claimable_txid) = self.claimable_outpoints.get(&outpoint) {
if let Some(claim_material) = self.pending_claim_requests.get_mut(&ancestor_claimable_txid.0) {
- // Avoid bump engine using inaccurate feerate due to new transaction size
- claim_material.feerate_previous = 0;
claim_material.per_input_material.insert(outpoint, input_material);
// Using a HashMap guarantee us than if we have multiple outpoints getting
// resurrected only one bump claim tx is going to be broadcast