|
9 | 9 |
|
10 | 10 | //! Utitilies for bumping transactions originating from [`super::Event`]s.
|
11 | 11 |
|
| 12 | +use core::convert::TryInto; |
| 13 | +use core::ops::Deref; |
| 14 | + |
| 15 | +use crate::chain::chaininterface::BroadcasterInterface; |
| 16 | +use crate::chain::keysinterface::{ChannelSigner, EcdsaChannelSigner, SignerProvider}; |
| 17 | +use crate::io_extras::sink; |
12 | 18 | use crate::ln::PaymentPreimage;
|
13 | 19 | use crate::ln::chan_utils;
|
14 |
| -use crate::ln::chan_utils::{ChannelTransactionParameters, HTLCOutputInCommitment}; |
| 20 | +use crate::ln::chan_utils::{ |
| 21 | + ANCHOR_INPUT_WITNESS_WEIGHT, HTLC_SUCCESS_INPUT_ANCHOR_WITNESS_WEIGHT, |
| 22 | + HTLC_TIMEOUT_INPUT_ANCHOR_WITNESS_WEIGHT, ChannelTransactionParameters, HTLCOutputInCommitment |
| 23 | +}; |
| 24 | +use crate::events::Event; |
| 25 | +use crate::prelude::HashMap; |
| 26 | +use crate::util::logger::Logger; |
| 27 | +use crate::util::ser::Writeable; |
15 | 28 |
|
16 |
| -use bitcoin::{OutPoint, PackedLockTime, Script, Transaction, Txid, TxIn, TxOut, Witness}; |
| 29 | +use bitcoin::{OutPoint, PackedLockTime, Sequence, Script, Transaction, Txid, TxIn, TxOut, Witness}; |
| 30 | +use bitcoin::blockdata::constants::WITNESS_SCALE_FACTOR; |
| 31 | +use bitcoin::consensus::Encodable; |
| 32 | +use bitcoin::hashes::{Hash, HashEngine}; |
| 33 | +use bitcoin::hashes::sha256::Hash as Sha256Hash; |
17 | 34 | use bitcoin::secp256k1;
|
18 | 35 | use bitcoin::secp256k1::{PublicKey, Secp256k1};
|
19 | 36 | use bitcoin::secp256k1::ecdsa::Signature;
|
@@ -231,3 +248,365 @@ pub enum BumpTransactionEvent {
|
231 | 248 | tx_lock_time: PackedLockTime,
|
232 | 249 | },
|
233 | 250 | }
|
| 251 | + |
| 252 | +/// A unique identifier used to track bumps for a transaction. The same identifier is always used |
| 253 | +/// for all bumps of the same transaction. |
| 254 | +#[derive(Copy, Clone, Hash, PartialEq, Eq)] |
| 255 | +pub struct BumpId([u8; 32]); |
| 256 | + |
| 257 | +impl BumpId { |
| 258 | + fn from_txid(txid: Txid) -> Self { |
| 259 | + Self(txid.into_inner()) |
| 260 | + } |
| 261 | + |
| 262 | + fn from_htlc_descriptors(htlc_descriptors: &[HTLCDescriptor]) -> Self { |
| 263 | + let mut engine = Sha256Hash::engine(); |
| 264 | + for htlc_descriptor in htlc_descriptors { |
| 265 | + let htlc_outpoint = OutPoint { |
| 266 | + txid: htlc_descriptor.commitment_txid, |
| 267 | + vout: htlc_descriptor.htlc.transaction_output_index.unwrap() |
| 268 | + }; |
| 269 | + engine.input(&htlc_outpoint.encode()); |
| 270 | + } |
| 271 | + Self(Sha256Hash::from_engine(engine).into_inner()) |
| 272 | + } |
| 273 | +} |
| 274 | + |
| 275 | +/// An input that must be included in a transaction when performing coin selection through |
| 276 | +/// [`CoinSelectionSource::select_confirmed_utxos`]. |
| 277 | +pub struct Input { |
| 278 | + /// The unique identifier of the input. |
| 279 | + pub outpoint: OutPoint, |
| 280 | + /// The upper-bound weight consumed by the input's full witness required to satisfy its |
| 281 | + /// corresponding output's script. |
| 282 | + pub witness_weight: u64, |
| 283 | +} |
| 284 | + |
| 285 | +/// An unspent transaction output that is available to spend resulting from a successful |
| 286 | +/// [`CoinSelection`] attempt. |
| 287 | +#[derive(Clone, Debug)] |
| 288 | +pub struct Utxo { |
| 289 | + /// The unique identifier of the output. |
| 290 | + pub outpoint: OutPoint, |
| 291 | + /// The output to spend. |
| 292 | + pub output: TxOut, |
| 293 | + /// The upper-bound weight consumed by the corresponding input's full witness required to |
| 294 | + /// satisfy the output's script. |
| 295 | + pub witness_weight: u64, |
| 296 | +} |
| 297 | + |
| 298 | +/// The result of a successful coin selection attempt for a transaction requiring additional UTXOs |
| 299 | +/// to cover its fees. |
| 300 | +pub struct CoinSelection { |
| 301 | + /// The set of UTXOs (with at least 1 confirmation) to spend and use within a transaction |
| 302 | + /// requiring additional fees. |
| 303 | + confirmed_utxos: Vec<Utxo>, |
| 304 | + /// An additional output tracking whether any change remained after coin selection. This output |
| 305 | + /// should always have a value above dust for its given `script_pubkey`. |
| 306 | + change_output: Option<TxOut>, |
| 307 | +} |
| 308 | + |
| 309 | +/// An abstraction over a bitcoin wallet that can perform coin selection over a set of UTXOs and can |
| 310 | +/// sign for them. The coin selection method aims to mimic Bitcoin Core's `fundrawtransaction` RPC, |
| 311 | +/// which most wallets should be able to satisfy. |
| 312 | +pub trait CoinSelectionSource { |
| 313 | + /// Performs coin selection of a set of UTXOs, with at least 1 confirmation each, that are |
| 314 | + /// available to spend. Implementations are free to pick their coin selection algorithm of |
| 315 | + /// choice, as long as the following requirements are met: |
| 316 | + /// |
| 317 | + /// 1. `must_spend` contains a set of [`Input`]s that must be included in the transaction |
| 318 | + /// throughout coin selection. |
| 319 | + /// 2. `must_pay_to` contains a set of [`TxOut`]s that must be included in the transaction |
| 320 | + /// throughout coin selection. |
| 321 | + /// 3. Enough inputs must be selected/contributed for the resulting transaction (including the |
| 322 | + /// inputs and outputs noted above) to meet `target_feerate_sat_per_1000_weight`. |
| 323 | + /// |
| 324 | + /// Implementations must realize that [`Input::witness_weight`] only tracks the weight of the |
| 325 | + /// input's witness. Some wallets, like Bitcoin Core's, may require providing the full input |
| 326 | + /// weight. Failing to do so may lead to underestimating fee bumps and delaying block inclusion. |
| 327 | + fn select_confirmed_utxos( |
| 328 | + &self, bump_id: BumpId, must_spend: Vec<Input>, must_pay_to: Vec<TxOut>, |
| 329 | + target_feerate_sat_per_1000_weight: u32, |
| 330 | + ) -> Result<CoinSelection, ()>; |
| 331 | + /// Returns a script to use for change above dust resulting from a successful coin selection |
| 332 | + /// attempt. |
| 333 | + fn change_script(&self) -> Result<Script, ()>; |
| 334 | + /// Signs and provides the full witness for all inputs within the transaction known to the |
| 335 | + /// trait (i.e., any provided via [`CoinSelectionSource::select_confirmed_utxos`]). |
| 336 | + fn sign_tx(&self, tx: &mut Transaction) -> Result<(), ()>; |
| 337 | +} |
| 338 | + |
| 339 | +/// A handler for [`Event::BumpTransaction`] events that sources confirmed UTXOs from a |
| 340 | +/// [`CoinSelectionSource`] to fee bump transactions via Child-Pays-For-Parent (CPFP) or |
| 341 | +/// Replace-By-Fee (RBF). |
| 342 | +pub struct BumpTransactionEventHandler<B: Deref, C: Deref, SP: Deref, L: Deref> |
| 343 | +where |
| 344 | + B::Target: BroadcasterInterface, |
| 345 | + C::Target: CoinSelectionSource, |
| 346 | + SP::Target: SignerProvider, |
| 347 | + L::Target: Logger, |
| 348 | +{ |
| 349 | + broadcaster: B, |
| 350 | + utxo_source: C, |
| 351 | + signer_provider: SP, |
| 352 | + logger: L, |
| 353 | + secp: Secp256k1<secp256k1::All>, |
| 354 | +} |
| 355 | + |
| 356 | +impl<B: Deref, C: Deref, SP: Deref, L: Deref> BumpTransactionEventHandler<B, C, SP, L> |
| 357 | +where |
| 358 | + B::Target: BroadcasterInterface, |
| 359 | + C::Target: CoinSelectionSource, |
| 360 | + SP::Target: SignerProvider, |
| 361 | + L::Target: Logger, |
| 362 | +{ |
| 363 | + /// Returns a new instance capable of handling [`Event::BumpTransaction`] events. |
| 364 | + pub fn new(broadcaster: B, utxo_source: C, signer_provider: SP, logger: L) -> Self { |
| 365 | + Self { |
| 366 | + broadcaster, |
| 367 | + utxo_source, |
| 368 | + signer_provider, |
| 369 | + logger, |
| 370 | + secp: Secp256k1::new(), |
| 371 | + } |
| 372 | + } |
| 373 | + |
| 374 | + /// Updates a transaction with the result of a successful coin selection attempt. |
| 375 | + fn process_coin_selection( |
| 376 | + &self, tx: &mut Transaction, mut coin_selection: CoinSelection, |
| 377 | + mut override_change_output: Option<impl FnOnce(&mut Transaction, &mut CoinSelection)>, |
| 378 | + ) { |
| 379 | + for utxo in coin_selection.confirmed_utxos.drain(..) { |
| 380 | + tx.input.push(TxIn { |
| 381 | + previous_output: utxo.outpoint, |
| 382 | + script_sig: Script::new(), |
| 383 | + sequence: Sequence::ZERO, |
| 384 | + witness: Witness::new(), |
| 385 | + }); |
| 386 | + } |
| 387 | + if let Some(override_change_output) = override_change_output.take() { |
| 388 | + override_change_output(tx, &mut coin_selection) |
| 389 | + } else if let Some(change_output) = coin_selection.change_output.take() { |
| 390 | + tx.output.push(change_output); |
| 391 | + } |
| 392 | + } |
| 393 | + |
| 394 | + /// Returns an unsigned transaction spending an anchor output of the commitment transaction, and |
| 395 | + /// any additional UTXOs sourced, to bump the commitment transaction's fee. |
| 396 | + fn build_anchor_tx( |
| 397 | + &self, bump_id: BumpId, target_feerate_sat_per_1000_weight: u32, |
| 398 | + commitment_tx: &Transaction, anchor_descriptor: &AnchorDescriptor, |
| 399 | + ) -> Result<Transaction, ()> { |
| 400 | + // Most wallets that support funding a transaction also require an output, e.g. see |
| 401 | + // bitcoind's `fundrawtransaction`. Since we're just interested in spending the anchor |
| 402 | + // input, without caring where the change goes, we use an output just above dust backed by |
| 403 | + // the wallet's change script. If the wallet ends up producing its own change output when |
| 404 | + // funding the transaction, we'll join them into one, saving the user a few satoshis. |
| 405 | + // TODO: Prevent change address inflation. |
| 406 | + let change_script = self.utxo_source.change_script()?; |
| 407 | + let dust_change_output = TxOut { |
| 408 | + value: change_script.dust_value().to_sat(), |
| 409 | + script_pubkey: change_script, |
| 410 | + }; |
| 411 | + |
| 412 | + let must_spend = vec![Input { |
| 413 | + outpoint: anchor_descriptor.outpoint, |
| 414 | + witness_weight: commitment_tx.weight() as u64 + ANCHOR_INPUT_WITNESS_WEIGHT, |
| 415 | + }]; |
| 416 | + let must_pay_to = vec![dust_change_output.clone()]; |
| 417 | + let coin_selection = self.utxo_source.select_confirmed_utxos( |
| 418 | + bump_id, must_spend, must_pay_to, target_feerate_sat_per_1000_weight, |
| 419 | + )?; |
| 420 | + let override_change_output = |tx: &mut Transaction, coin_selection: &mut CoinSelection| { |
| 421 | + if let Some(mut change_output) = coin_selection.change_output.take() { |
| 422 | + // Replace the change output we initially added to `must_spend` with the one given |
| 423 | + // to us by the user. |
| 424 | + let dust_change_output_weight = dust_change_output.consensus_encode(&mut sink()) |
| 425 | + .unwrap() as u64; |
| 426 | + let dust_change_output_fee = dust_change_output_weight * |
| 427 | + target_feerate_sat_per_1000_weight as u64; |
| 428 | + change_output.value += dust_change_output_fee + dust_change_output.value; |
| 429 | + tx.output.push(change_output); |
| 430 | + } else { |
| 431 | + tx.output.push(dust_change_output); |
| 432 | + } |
| 433 | + }; |
| 434 | + |
| 435 | + let mut tx = Transaction { |
| 436 | + version: 2, |
| 437 | + lock_time: PackedLockTime::ZERO, // TODO: Use next best height. |
| 438 | + input: vec![TxIn { |
| 439 | + previous_output: anchor_descriptor.outpoint, |
| 440 | + script_sig: Script::new(), |
| 441 | + sequence: Sequence::ZERO, |
| 442 | + witness: Witness::new(), |
| 443 | + }], |
| 444 | + output: vec![], |
| 445 | + }; |
| 446 | + self.process_coin_selection(&mut tx, coin_selection, Some(override_change_output)); |
| 447 | + Ok(tx) |
| 448 | + } |
| 449 | + |
| 450 | + /// Handles a [`BumpTransactionEvent::ChannelClose`] event variant by producing a fully-signed |
| 451 | + /// transaction spending an anchor output of the commitment transaction to bump its fee and |
| 452 | + /// broadcasts them to the network as a package. |
| 453 | + fn handle_channel_close( |
| 454 | + &self, package_target_feerate_sat_per_1000_weight: u32, commitment_tx: &Transaction, |
| 455 | + commitment_tx_fee_sat: u64, anchor_descriptor: &AnchorDescriptor, |
| 456 | + ) -> Result<(), ()> { |
| 457 | + // Compute the feerate the anchor transaction must meet to meet the overall feerate for the |
| 458 | + // package (commitment + anchor transactions). |
| 459 | + let commitment_tx_feerate: u32 = (commitment_tx_fee_sat * 1000 / commitment_tx.weight() as u64) |
| 460 | + .try_into().unwrap_or(u32::max_value()); |
| 461 | + let feerate_diff = package_target_feerate_sat_per_1000_weight.saturating_sub(commitment_tx_feerate); |
| 462 | + if feerate_diff == 0 { |
| 463 | + // If the commitment transaction already has a feerate high enough on its own, broadcast |
| 464 | + // it as is without a child. |
| 465 | + self.broadcaster.broadcast_transaction(commitment_tx); |
| 466 | + return Ok(()); |
| 467 | + } |
| 468 | + |
| 469 | + let bump_id = BumpId::from_txid(commitment_tx.txid()); |
| 470 | + // TODO: Use the one in `crate::chain::chaininterface` once it's correct. |
| 471 | + const MIN_RELAY_FEERATE: u32 = bitcoin::policy::DEFAULT_MIN_RELAY_TX_FEE / |
| 472 | + WITNESS_SCALE_FACTOR as u32; |
| 473 | + let target_anchor_tx_feerate = if feerate_diff < MIN_RELAY_FEERATE { |
| 474 | + // Transactions generally won't propagate if the minimum feerate is not met, so use it |
| 475 | + // as a lower bound. |
| 476 | + MIN_RELAY_FEERATE |
| 477 | + } else { |
| 478 | + feerate_diff |
| 479 | + }; |
| 480 | + let mut anchor_tx = self.build_anchor_tx( |
| 481 | + bump_id, target_anchor_tx_feerate, commitment_tx, anchor_descriptor, |
| 482 | + )?; |
| 483 | + |
| 484 | + debug_assert_eq!(anchor_tx.output.len(), 1); |
| 485 | + self.utxo_source.sign_tx(&mut anchor_tx)?; |
| 486 | + let signer = self.signer_provider.derive_channel_signer( |
| 487 | + anchor_descriptor.channel_value_satoshis, anchor_descriptor.channel_keys_id, |
| 488 | + ); |
| 489 | + let anchor_sig = signer.sign_holder_anchor_input(&anchor_tx, 0, &self.secp)?; |
| 490 | + anchor_tx.input[0].witness = |
| 491 | + chan_utils::build_anchor_input_witness(&signer.pubkeys().funding_pubkey, &anchor_sig); |
| 492 | + |
| 493 | + // TODO: Broadcast as transaction package once supported. |
| 494 | + self.broadcaster.broadcast_transaction(commitment_tx); |
| 495 | + self.broadcaster.broadcast_transaction(&anchor_tx); |
| 496 | + Ok(()) |
| 497 | + } |
| 498 | + |
| 499 | + /// Returns an unsigned, fee-bumped HTLC transaction, along with the set of signers required to |
| 500 | + /// fulfill the witness for each HTLC input within it. |
| 501 | + fn build_htlc_tx( |
| 502 | + &self, bump_id: BumpId, target_feerate_sat_per_1000_weight: u32, |
| 503 | + htlc_descriptors: &[HTLCDescriptor], tx_lock_time: PackedLockTime, |
| 504 | + ) -> Result<(Transaction, HashMap<[u8; 32], <SP::Target as SignerProvider>::Signer>), ()> { |
| 505 | + let mut tx = Transaction { |
| 506 | + version: 2, |
| 507 | + lock_time: tx_lock_time, |
| 508 | + input: vec![], |
| 509 | + output: vec![], |
| 510 | + }; |
| 511 | + // Unfortunately, we need to derive the signer for each HTLC ahead of time to obtain its |
| 512 | + // input. |
| 513 | + let mut signers = HashMap::new(); |
| 514 | + for htlc_descriptor in htlc_descriptors { |
| 515 | + let signer = signers.entry(htlc_descriptor.channel_keys_id) |
| 516 | + .or_insert_with(|| |
| 517 | + self.signer_provider.derive_channel_signer( |
| 518 | + htlc_descriptor.channel_value_satoshis, htlc_descriptor.channel_keys_id, |
| 519 | + ) |
| 520 | + ); |
| 521 | + let per_commitment_point = signer.get_per_commitment_point( |
| 522 | + htlc_descriptor.per_commitment_number, &self.secp |
| 523 | + ); |
| 524 | + tx.input.push(htlc_descriptor.unsigned_tx_input()); |
| 525 | + let htlc_output = htlc_descriptor.tx_output(&per_commitment_point, &self.secp); |
| 526 | + tx.output.push(htlc_output); |
| 527 | + } |
| 528 | + |
| 529 | + let must_spend = htlc_descriptors.iter().map(|htlc_descriptor| Input { |
| 530 | + outpoint: OutPoint { |
| 531 | + txid: htlc_descriptor.commitment_txid, |
| 532 | + vout: htlc_descriptor.htlc.transaction_output_index.unwrap(), |
| 533 | + }, |
| 534 | + witness_weight: if htlc_descriptor.preimage.is_some() { |
| 535 | + HTLC_SUCCESS_INPUT_ANCHOR_WITNESS_WEIGHT |
| 536 | + } else { |
| 537 | + HTLC_TIMEOUT_INPUT_ANCHOR_WITNESS_WEIGHT |
| 538 | + }, |
| 539 | + }).collect(); |
| 540 | + let must_pay_to = tx.output.clone(); |
| 541 | + let coin_selection = self.utxo_source.select_confirmed_utxos( |
| 542 | + bump_id, must_spend, must_pay_to, target_feerate_sat_per_1000_weight, |
| 543 | + )?; |
| 544 | + |
| 545 | + self.process_coin_selection( |
| 546 | + &mut tx, coin_selection, None::<fn(&mut Transaction, &mut CoinSelection)> |
| 547 | + ); |
| 548 | + Ok((tx, signers)) |
| 549 | + } |
| 550 | + |
| 551 | + /// Handles a [`BumpTransactionEvent::HTLCResolution`] event variant by producing a |
| 552 | + /// fully-signed, fee-bumped HTLC transaction that is broadcast to the network. |
| 553 | + fn handle_htlc_resolution( |
| 554 | + &self, target_feerate_sat_per_1000_weight: u32, htlc_descriptors: &[HTLCDescriptor], |
| 555 | + tx_lock_time: PackedLockTime, |
| 556 | + ) -> Result<(), ()> { |
| 557 | + let bump_id = BumpId::from_htlc_descriptors(htlc_descriptors); |
| 558 | + let (mut htlc_tx, signers) = self.build_htlc_tx( |
| 559 | + bump_id, target_feerate_sat_per_1000_weight, htlc_descriptors, tx_lock_time, |
| 560 | + )?; |
| 561 | + |
| 562 | + debug_assert_eq!(htlc_tx.output.len(), htlc_descriptors.len() + 1); |
| 563 | + self.utxo_source.sign_tx(&mut htlc_tx)?; |
| 564 | + for (idx, htlc_descriptor) in htlc_descriptors.iter().enumerate() { |
| 565 | + let signer = signers.get(&htlc_descriptor.channel_keys_id).unwrap(); |
| 566 | + let htlc_sig = signer.sign_holder_htlc_transaction( |
| 567 | + &htlc_tx, idx, htlc_descriptor, &self.secp |
| 568 | + )?; |
| 569 | + let per_commitment_point = signer.get_per_commitment_point( |
| 570 | + htlc_descriptor.per_commitment_number, &self.secp |
| 571 | + ); |
| 572 | + let witness_script = htlc_descriptor.witness_script(&per_commitment_point, &self.secp); |
| 573 | + htlc_tx.input[idx].witness = htlc_descriptor.tx_input_witness(&htlc_sig, &witness_script); |
| 574 | + } |
| 575 | + |
| 576 | + self.broadcaster.broadcast_transaction(&htlc_tx); |
| 577 | + Ok(()) |
| 578 | + } |
| 579 | + |
| 580 | + /// Handles all variants of [`BumpTransactionEvent`], immediately returning otherwise. |
| 581 | + pub fn handle_event(&self, event: Event) { |
| 582 | + let event = if let Event::BumpTransaction(event) = event { |
| 583 | + event |
| 584 | + } else { |
| 585 | + return; |
| 586 | + }; |
| 587 | + match event { |
| 588 | + BumpTransactionEvent::ChannelClose { |
| 589 | + package_target_feerate_sat_per_1000_weight, commitment_tx, anchor_descriptor, |
| 590 | + commitment_tx_fee_satoshis, .. |
| 591 | + } => { |
| 592 | + if let Err(_) = self.handle_channel_close( |
| 593 | + package_target_feerate_sat_per_1000_weight, &commitment_tx, |
| 594 | + commitment_tx_fee_satoshis, &anchor_descriptor, |
| 595 | + ) { |
| 596 | + log_error!(self.logger, "Failed bumping commitment transaction fee for {}", |
| 597 | + commitment_tx.txid()); |
| 598 | + } |
| 599 | + } |
| 600 | + BumpTransactionEvent::HTLCResolution { |
| 601 | + target_feerate_sat_per_1000_weight, htlc_descriptors, tx_lock_time, |
| 602 | + } => { |
| 603 | + if let Err(_) = self.handle_htlc_resolution( |
| 604 | + target_feerate_sat_per_1000_weight, &htlc_descriptors, tx_lock_time, |
| 605 | + ) { |
| 606 | + log_error!(self.logger, "Failed bumping HTLC transaction fee for commitment {}", |
| 607 | + htlc_descriptors[0].commitment_txid); |
| 608 | + } |
| 609 | + } |
| 610 | + } |
| 611 | + } |
| 612 | +} |
0 commit comments