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