From 7560cad237bbd516adc05085242550eff9410f3c Mon Sep 17 00:00:00 2001 From: Tibo-lg Date: Thu, 29 Jun 2023 15:00:28 +0900 Subject: [PATCH] Fix protocol of channel renew and sub channel establish --- .github/workflows/rust.yml | 2 +- dlc-manager/src/channel/ser.rs | 5 +- dlc-manager/src/channel/signed_channel.rs | 29 +- dlc-manager/src/channel_updater.rs | 249 +++++++++----- dlc-manager/src/manager.rs | 191 +++++++---- dlc-manager/src/sub_channel_manager.rs | 315 +++++++++++++----- dlc-manager/src/subchannel/mod.rs | 61 +++- dlc-manager/src/subchannel/ser.rs | 38 ++- dlc-manager/tests/channel_execution_tests.rs | 45 ++- .../tests/ln_dlc_channel_execution_tests.rs | 57 +++- dlc-messages/src/channel.rs | 39 ++- dlc-messages/src/lib.rs | 12 +- dlc-messages/src/sub_channel.rs | 40 ++- dlc-sled-storage-provider/src/lib.rs | 2 + dlc-sled-storage-provider/test_files/Accepted | Bin 3430 -> 8551 bytes .../test_files/AcceptedChannel | Bin 908 -> 908 bytes .../test_files/AcceptedSubChannel | Bin 3159 -> 2997 bytes dlc-sled-storage-provider/test_files/Closed | Bin 1108 -> 1109 bytes .../test_files/Confirmed | Bin 5983 -> 12140 bytes .../test_files/Confirmed1 | Bin 5983 -> 10069 bytes dlc-sled-storage-provider/test_files/Offered | Bin 1107 -> 2706 bytes .../test_files/OfferedChannel | Bin 235 -> 235 bytes .../test_files/OfferedSubChannel | Bin 2424 -> 2424 bytes .../test_files/OfferedSubChannel1 | Bin 2424 -> 2424 bytes .../test_files/PreClosed | Bin 6874 -> 10960 bytes dlc-sled-storage-provider/test_files/Signed | Bin 5873 -> 9367 bytes dlc-sled-storage-provider/test_files/Signed1 | Bin 5873 -> 9959 bytes .../test_files/SignedChannelEstablished | Bin 3663 -> 3663 bytes .../test_files/SignedChannelSettled | Bin 3465 -> 3465 bytes .../test_files/SignedSubChannel | Bin 3385 -> 3385 bytes scripts/generate_serialized_contract_files.sh | 6 +- scripts/run_integration_tests.sh | 2 +- 32 files changed, 787 insertions(+), 306 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f7330aa5..9901268e 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -58,6 +58,6 @@ jobs: - name: Wait for electrs to be ready run: ./scripts/wait_for_electrs.sh - name: Run test - run: RUST_BACKTRACE=1 ${{ matrix.tests }} --ignored --exact + run: RUST_MIN_STACK=104857600 RUST_BACKTRACE=1 ${{ matrix.tests }} --ignored --exact - name: Stop bitcoin node run: ./scripts/stop_node.sh diff --git a/dlc-manager/src/channel/ser.rs b/dlc-manager/src/channel/ser.rs index a4e1fcf7..c3efabc9 100644 --- a/dlc-manager/src/channel/ser.rs +++ b/dlc-manager/src/channel/ser.rs @@ -58,8 +58,9 @@ impl_dlc_writeable_enum!( (4, SettledConfirmed, {(settle_tx, writeable), (counter_settle_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (own_settle_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (counter_next_per_update_point, writeable), (own_next_per_update_point, writeable), (timeout, writeable), (own_payout, writeable), (counter_payout, writeable) }), (5, Settled, {(settle_tx, writeable), (counter_settle_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (own_settle_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (own_payout, writeable), (counter_payout, writeable)}), (6, RenewOffered, {(offered_contract_id, writeable), (counter_payout, writeable), (is_offer, writeable), (offer_next_per_update_point, writeable), (timeout, writeable)}), - (7, RenewAccepted, {(contract_id, writeable), (offer_per_update_point, writeable), (accept_per_update_point, writeable), (buffer_transaction, writeable), (buffer_script_pubkey, writeable), (accept_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (timeout, writeable), (own_payout, writeable)}), - (8, RenewConfirmed, {(contract_id, writeable), (offer_per_update_point, writeable), (accept_per_update_point, writeable), (buffer_transaction, writeable), (buffer_script_pubkey, writeable), (offer_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (accept_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (timeout, writeable), (own_payout, writeable), (total_collateral, writeable)}), + (7, RenewAccepted, {(contract_id, writeable), (offer_per_update_point, writeable), (accept_per_update_point, writeable), (buffer_transaction, writeable), (buffer_script_pubkey, writeable), (timeout, writeable), (own_payout, writeable)}), + (8, RenewConfirmed, {(contract_id, writeable), (offer_per_update_point, writeable), (accept_per_update_point, writeable), (buffer_transaction, writeable), (buffer_script_pubkey, writeable), (offer_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (timeout, writeable), (own_payout, writeable), (total_collateral, writeable)}), + (15, RenewFinalized, {(contract_id, writeable), (offer_per_update_point, writeable), (accept_per_update_point, writeable), (buffer_transaction, writeable), (buffer_script_pubkey, writeable), (offer_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (accept_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (timeout, writeable), (own_payout, writeable), (total_collateral, writeable)}), (9, Closing, {(buffer_transaction, writeable), (contract_id, writeable), (is_initiator, writeable)}), (10, ClosedPunished, { (punishment_txid, writeable) }), (11, CollaborativeCloseOffered, { (counter_payout, writeable), (offer_signature, writeable), (close_tx, writeable), (timeout, writeable) }) diff --git a/dlc-manager/src/channel/signed_channel.rs b/dlc-manager/src/channel/signed_channel.rs index 42861a23..1146ca99 100644 --- a/dlc-manager/src/channel/signed_channel.rs +++ b/dlc-manager/src/channel/signed_channel.rs @@ -228,9 +228,6 @@ typed_enum!( buffer_transaction: Transaction, /// The buffer transaction script pubkey. buffer_script_pubkey: Script, - /// The adaptor signature for the buffer transaction generated by - /// the accept party. - accept_buffer_adaptor_signature: EcdsaAdaptorSignature, /// The UNIX epoch at which the counter party will be considered /// unresponsive and the channel will be forced closed. timeout: u64, @@ -240,6 +237,31 @@ typed_enum!( /// A [`SignedChannel`] is in `RenewConfirmed` state when the local party /// has sent a [`dlc_messages::channel::RenewConfirm`] message. RenewConfirmed { + /// The [`crate::ContractId`] of the offered contract. + contract_id: ContractId, + /// The per update point to be used by the offer party for the setup + /// of the next channel state. + offer_per_update_point: PublicKey, + /// The per update point to be used by the accept party for the setup + /// of the next channel state. + accept_per_update_point: PublicKey, + /// The buffer transaction. + buffer_transaction: Transaction, + /// The buffer transaction script pubkey. + buffer_script_pubkey: Script, + /// The adaptor signature for the buffer transaction generated by + /// the offer party. + offer_buffer_adaptor_signature: EcdsaAdaptorSignature, + /// The UNIX epoch at which the counter party will be considered + /// unresponsive and the channel will be forced closed. + timeout: u64, + /// The payout to the local party attributed for closing the previous state. + own_payout: u64, + /// The total amount of collateral in the channel. + total_collateral: u64, + }, + /// Finalize the renewal of the contract within a DLC channel. + RenewFinalized { /// The [`crate::ContractId`] of the offered contract. contract_id: ContractId, /// The per update point to be used by the offer party for the setup @@ -334,6 +356,7 @@ impl SignedChannelState { } => Some(*offered_contract_id), SignedChannelState::RenewAccepted { contract_id, .. } => Some(*contract_id), SignedChannelState::RenewConfirmed { contract_id, .. } => Some(*contract_id), + SignedChannelState::RenewFinalized { contract_id, .. } => Some(*contract_id), SignedChannelState::Closing { contract_id, .. } => Some(*contract_id), _ => None, } diff --git a/dlc-manager/src/channel_updater.rs b/dlc-manager/src/channel_updater.rs index 65e6bde4..c2c5bec7 100644 --- a/dlc-manager/src/channel_updater.rs +++ b/dlc-manager/src/channel_updater.rs @@ -31,7 +31,8 @@ use dlc::{ use dlc_messages::{ channel::{ AcceptChannel, CollaborativeCloseOffer, Reject, RenewAccept, RenewConfirm, RenewFinalize, - RenewOffer, SettleAccept, SettleConfirm, SettleFinalize, SettleOffer, SignChannel, + RenewOffer, RenewRevoke, SettleAccept, SettleConfirm, SettleFinalize, SettleOffer, + SignChannel, }, oracle_msgs::{OracleAnnouncement, OracleAttestation}, FundingSignatures, @@ -353,8 +354,8 @@ where temporary_channel_id: offered_channel.temporary_channel_id, channel_id, accept_per_update_seed: PublicKey::from_secret_key(secp, &per_update_seed), - accept_buffer_adaptor_signature: buffer_adaptor_signature, counter_party: offered_contract.counter_party, + accept_buffer_adaptor_signature: buffer_adaptor_signature, }; let accept_channel = accepted_channel.get_accept_channel_msg( @@ -1549,7 +1550,6 @@ where peer_timeout, signer, time, - None, ) } @@ -1561,7 +1561,6 @@ pub(crate) fn accept_channel_renewal_internal( peer_timeout: u64, signer: &S, time: &T, - own_buffer_adaptor_sk: Option, ) -> Result<(AcceptedContract, RenewAccept), Error> where S::Target: Signer, @@ -1580,7 +1579,6 @@ where } }; - let own_fund_sk = signer.get_secret_key_for_pubkey(&signed_channel.own_params.fund_pubkey)?; let own_base_secret_key = signer.get_secret_key_for_pubkey(&signed_channel.own_points.own_basepoint)?; let per_update_seed = signer.get_secret_key_for_pubkey(&signed_channel.own_per_update_seed)?; @@ -1632,23 +1630,6 @@ where buffer_nsequence, )?; - let own_buffer_adaptor_sk = own_buffer_adaptor_sk.as_ref().unwrap_or(&own_fund_sk); - - let buffer_input_value = if signed_channel.is_sub_channel() { - signed_channel.fund_tx.output[1].value - } else { - signed_channel.fund_tx.output[signed_channel.fund_output_index].value - }; - - let buffer_adaptor_signature = get_tx_adaptor_signature( - secp, - &buffer_transaction, - buffer_input_value, - &dlc_transactions.funding_script_pubkey, - own_buffer_adaptor_sk, - &offer_revoke_params.publish_pk.inner, - )?; - let own_secret_key = derive_private_key(secp, &accept_per_update_point, &own_base_secret_key); let (accepted_contract, adaptor_sigs) = accept_contract_internal( @@ -1668,7 +1649,6 @@ where accept_per_update_point, buffer_transaction, buffer_script_pubkey, - accept_buffer_adaptor_signature: buffer_adaptor_signature, timeout: time.unix_time_now() + peer_timeout, own_payout, }; @@ -1678,7 +1658,6 @@ where let renew_accept = RenewAccept { channel_id: signed_channel.channel_id, next_per_update_point: accept_per_update_point, - buffer_adaptor_signature, cet_adaptor_signatures: (&adaptor_sigs as &[_]).into(), refund_signature: accepted_contract.accept_refund_signature, }; @@ -1714,7 +1693,6 @@ where signer, time, None, - None, ) } @@ -1728,7 +1706,6 @@ pub(crate) fn verify_renew_accept_and_confirm_internal( signer: &S, time: &T, own_buffer_adaptor_sk: Option, - counter_buffer_own_pk: Option, ) -> Result<(SignedContract, RenewConfirm), Error> where S::Target: Signer, @@ -1739,13 +1716,6 @@ where let own_base_secret_key = signer.get_secret_key_for_pubkey(&signed_channel.own_points.own_basepoint)?; - let per_update_seed = signer.get_secret_key_for_pubkey(&signed_channel.own_per_update_seed)?; - - let prev_per_update_secret = SecretKey::from_slice(&build_commitment_secret( - per_update_seed.as_ref(), - signed_channel.update_idx, - ))?; - let offer_per_update_point = get_signed_channel_state!(signed_channel, RenewOffered, offer_next_per_update_point)?; @@ -1815,19 +1785,6 @@ where signed_channel.fund_tx.output[signed_channel.fund_output_index].value }; let own_buffer_adaptor_sk = own_buffer_adaptor_sk.as_ref().unwrap_or(&own_fund_sk); - let counter_buffer_own_pk = counter_buffer_own_pk - .as_ref() - .unwrap_or(&signed_contract.accepted_contract.accept_params.fund_pubkey); - - verify_tx_adaptor_signature( - secp, - &buffer_transaction, - buffer_input_value, - &signed_channel.fund_script_pubkey, - counter_buffer_own_pk, - &offer_revoke_params.publish_pk.inner, - &renew_accept.buffer_adaptor_signature, - )?; let own_buffer_adaptor_signature = get_tx_adaptor_signature( secp, @@ -1845,7 +1802,6 @@ where buffer_transaction, buffer_script_pubkey, offer_buffer_adaptor_signature: own_buffer_adaptor_signature, - accept_buffer_adaptor_signature: renew_accept.buffer_adaptor_signature, timeout: time.unix_time_now() + peer_timeout, own_payout, total_collateral: offered_contract.total_collateral, @@ -1855,7 +1811,6 @@ where let renew_confirm = RenewConfirm { channel_id: signed_channel.channel_id, - per_update_secret: prev_per_update_secret, buffer_adaptor_signature: own_buffer_adaptor_signature, cet_adaptor_signatures: (&cet_adaptor_signatures as &[_]).into(), refund_signature: signed_contract.offer_refund_signature, @@ -1868,14 +1823,18 @@ where /// [`RenewAccept`] message, verifying the message and updating the state of the /// channel and associated contract the same time. Expects the channel to be in /// [`SignedChannelState::RenewAccepted`] state. -pub fn verify_renew_confirm_and_finalize( +pub fn verify_renew_confirm_and_finalize( secp: &Secp256k1, signed_channel: &mut SignedChannel, accepted_contract: &AcceptedContract, renew_confirm: &RenewConfirm, + peer_timeout: u64, + time: &T, signer: &S, + chain_monitor: &Mutex, ) -> Result<(SignedContract, RenewFinalize), Error> where + T::Target: Time, S::Target: Signer, { verify_renew_confirm_and_finalize_internal( @@ -1883,26 +1842,35 @@ where signed_channel, accepted_contract, renew_confirm, + peer_timeout, + time, signer, None, + None, + chain_monitor, ) } -pub(crate) fn verify_renew_confirm_and_finalize_internal( +pub(crate) fn verify_renew_confirm_and_finalize_internal( secp: &Secp256k1, signed_channel: &mut SignedChannel, accepted_contract: &AcceptedContract, renew_confirm: &RenewConfirm, + peer_timeout: u64, + time: &T, signer: &S, counter_buffer_own_pk: Option, + own_buffer_adaptor_sk: Option, + chain_monitor: &Mutex, ) -> Result<(SignedContract, RenewFinalize), Error> where + T::Target: Time, S::Target: Signer, { let ( &offer_per_update_point, &accept_per_update_point, - &accept_buffer_adaptor_signature, + own_payout, buffer_transaction, buffer_script_pubkey, ) = get_signed_channel_state!( @@ -1910,7 +1878,7 @@ where RenewAccepted, offer_per_update_point, accept_per_update_point, - accept_buffer_adaptor_signature | buffer_transaction, + own_payout | buffer_transaction, buffer_script_pubkey )?; @@ -1951,28 +1919,6 @@ where Some(signed_channel.channel_id), )?; - signed_channel.state = SignedChannelState::Established { - signed_contract_id: signed_contract.accepted_contract.get_contract_id(), - own_buffer_adaptor_signature: accept_buffer_adaptor_signature, - counter_buffer_adaptor_signature: renew_confirm.buffer_adaptor_signature, - buffer_transaction: buffer_transaction.clone(), - is_offer: false, - total_collateral: signed_contract - .accepted_contract - .offered_contract - .total_collateral, - }; - - signed_channel.update_idx -= 1; - - signed_channel - .counter_party_commitment_secrets - .provide_secret( - signed_channel.update_idx + 1, - *renew_confirm.per_update_secret.as_ref(), - ) - .map_err(|_| Error::InvalidParameters("Provided secret was invalid".to_string()))?; - signed_channel.counter_per_update_point = offer_per_update_point; signed_channel.own_per_update_point = accept_per_update_point; @@ -1980,29 +1926,82 @@ where let prev_per_update_secret = SecretKey::from_slice(&build_commitment_secret( per_update_seed.as_ref(), - signed_channel.update_idx + 1, + signed_channel.update_idx, ))?; + let offer_revoke_params = signed_channel.counter_points.get_revokable_params( + secp, + &signed_channel.own_points.revocation_basepoint, + &signed_channel.counter_per_update_point, + ); + + let own_fund_sk = signer.get_secret_key_for_pubkey(&signed_channel.own_params.fund_pubkey)?; + let own_buffer_adaptor_sk = own_buffer_adaptor_sk.as_ref().unwrap_or(&own_fund_sk); + + let buffer_input_value = if signed_channel.is_sub_channel() { + signed_channel.fund_tx.output[1].value + } else { + signed_channel.fund_tx.output[signed_channel.fund_output_index].value + }; + + let buffer_adaptor_signature = get_tx_adaptor_signature( + secp, + buffer_transaction, + buffer_input_value, + &signed_channel.fund_script_pubkey, + own_buffer_adaptor_sk, + &offer_revoke_params.publish_pk.inner, + )?; + let total_collateral = + signed_channel.own_params.collateral + signed_channel.counter_params.collateral; + + chain_monitor.lock().unwrap().add_tx( + buffer_transaction.txid(), + ChannelInfo { + channel_id: signed_channel.channel_id, + tx_type: TxType::BufferTx, + }, + ); + + signed_channel.state = SignedChannelState::RenewFinalized { + contract_id: signed_contract.accepted_contract.get_contract_id(), + offer_per_update_point, + accept_per_update_point, + buffer_transaction: buffer_transaction.clone(), + buffer_script_pubkey: buffer_script_pubkey.clone(), + offer_buffer_adaptor_signature: renew_confirm.buffer_adaptor_signature, + accept_buffer_adaptor_signature: buffer_adaptor_signature, + timeout: time.unix_time_now() + peer_timeout, + own_payout: *own_payout, + total_collateral, + }; + let renew_finalize = RenewFinalize { channel_id: signed_channel.channel_id, per_update_secret: prev_per_update_secret, + buffer_adaptor_signature, }; Ok((signed_contract, renew_finalize)) } /// Verify the given [`RenewFinalize`] and update the state of the channel. -pub fn renew_channel_on_finalize( +pub fn renew_channel_on_finalize( + secp: &Secp256k1, signed_channel: &mut SignedChannel, renew_finalize: &RenewFinalize, -) -> Result<(), Error> { + counter_buffer_own_pk: Option, + signer: &S, +) -> Result +where + S::Target: Signer, +{ let ( contract_id, total_collateral, offer_per_update_point, accept_per_update_point, offer_buffer_adaptor_signature, - accept_buffer_adaptor_signature, buffer_transaction, ) = get_signed_channel_state!( signed_channel, @@ -2011,13 +2010,37 @@ pub fn renew_channel_on_finalize( total_collateral, offer_per_update_point, accept_per_update_point, - offer_buffer_adaptor_signature, - accept_buffer_adaptor_signature | buffer_transaction + offer_buffer_adaptor_signature | buffer_transaction + )?; + + let offer_revoke_params = signed_channel.own_points.get_revokable_params( + secp, + &signed_channel.counter_points.revocation_basepoint, + offer_per_update_point, + ); + + let buffer_input_value = if signed_channel.is_sub_channel() { + signed_channel.fund_tx.output[1].value + } else { + signed_channel.fund_tx.output[signed_channel.fund_output_index].value + }; + let counter_buffer_own_pk = counter_buffer_own_pk + .as_ref() + .unwrap_or(&signed_channel.counter_params.fund_pubkey); + + verify_tx_adaptor_signature( + secp, + buffer_transaction, + buffer_input_value, + &signed_channel.fund_script_pubkey, + counter_buffer_own_pk, + &offer_revoke_params.publish_pk.inner, + &renew_finalize.buffer_adaptor_signature, )?; let state = SignedChannelState::Established { signed_contract_id: *contract_id, - counter_buffer_adaptor_signature: *accept_buffer_adaptor_signature, + counter_buffer_adaptor_signature: renew_finalize.buffer_adaptor_signature, own_buffer_adaptor_signature: *offer_buffer_adaptor_signature, buffer_transaction: buffer_transaction.clone(), is_offer: true, @@ -2032,6 +2055,13 @@ pub fn renew_channel_on_finalize( ) .map_err(|_| Error::InvalidParameters("Provided secret was invalid".to_string()))?; + let per_update_seed = signer.get_secret_key_for_pubkey(&signed_channel.own_per_update_seed)?; + + let prev_per_update_secret = SecretKey::from_slice(&build_commitment_secret( + per_update_seed.as_ref(), + signed_channel.update_idx, + ))?; + signed_channel.own_per_update_point = *offer_per_update_point; signed_channel.counter_per_update_point = *accept_per_update_point; @@ -2039,6 +2069,53 @@ pub fn renew_channel_on_finalize( signed_channel.roll_back_state = None; signed_channel.update_idx -= 1; + let msg = RenewRevoke { + channel_id: signed_channel.channel_id, + per_update_secret: prev_per_update_secret, + }; + + Ok(msg) +} + +/// Verify the given [`RenewRevoke`] and update the state of the channel. +pub fn renew_channel_on_revoke( + signed_channel: &mut SignedChannel, + renew_revoke: &RenewRevoke, +) -> Result<(), Error> { + let ( + contract_id, + total_collateral, + offer_buffer_adaptor_signature, + accept_buffer_adaptor_signature, + buffer_transaction, + ) = get_signed_channel_state!( + signed_channel, + RenewFinalized, + contract_id, + total_collateral, + offer_buffer_adaptor_signature, + accept_buffer_adaptor_signature | buffer_transaction + )?; + + signed_channel + .counter_party_commitment_secrets + .provide_secret( + signed_channel.update_idx, + *renew_revoke.per_update_secret.as_ref(), + ) + .map_err(|_| Error::InvalidParameters("Provided secret was invalid".to_string()))?; + + signed_channel.update_idx -= 1; + + signed_channel.state = SignedChannelState::Established { + signed_contract_id: *contract_id, + counter_buffer_adaptor_signature: *accept_buffer_adaptor_signature, + own_buffer_adaptor_signature: *offer_buffer_adaptor_signature, + buffer_transaction: buffer_transaction.clone(), + is_offer: true, + total_collateral: *total_collateral, + }; + Ok(()) } @@ -2335,6 +2412,8 @@ pub fn on_reject(signed_channel: &mut SignedChannel) -> Result<(), Error> { pub fn initiate_unilateral_close_established_channel( secp: &Secp256k1, signed_channel: &mut SignedChannel, + buffer_adaptor_signature: EcdsaAdaptorSignature, + mut buffer_transaction: Transaction, signer: &S, sub_channel: Option<(SubChannel, &ClosingSubChannel)>, is_initiator: bool, @@ -2342,14 +2421,6 @@ pub fn initiate_unilateral_close_established_channel( where S::Target: Signer, { - let (buffer_adaptor_signature, buffer_transaction) = get_signed_channel_state!( - signed_channel, - Established, - counter_buffer_adaptor_signature | buffer_transaction - )?; - - let mut buffer_transaction = buffer_transaction.clone(); - let publish_base_secret = signer.get_secret_key_for_pubkey(&signed_channel.own_points.publish_basepoint)?; diff --git a/dlc-manager/src/manager.rs b/dlc-manager/src/manager.rs index 7a5edd0e..393d08f7 100644 --- a/dlc-manager/src/manager.rs +++ b/dlc-manager/src/manager.rs @@ -23,8 +23,8 @@ use bitcoin::Address; use bitcoin::Transaction; use dlc_messages::channel::{ AcceptChannel, CollaborativeCloseOffer, OfferChannel, Reject, RenewAccept, RenewConfirm, - RenewFinalize, RenewOffer, SettleAccept, SettleConfirm, SettleFinalize, SettleOffer, - SignChannel, + RenewFinalize, RenewOffer, RenewRevoke, SettleAccept, SettleConfirm, SettleFinalize, + SettleOffer, SignChannel, }; use dlc_messages::oracle_msgs::{OracleAnnouncement, OracleAttestation}; use dlc_messages::{ @@ -35,8 +35,8 @@ use lightning::ln::chan_utils::{ build_commitment_secret, derive_private_key, derive_private_revocation_key, }; use log::{error, warn}; -use secp256k1_zkp::XOnlyPublicKey; use secp256k1_zkp::{ecdsa::Signature, All, PublicKey, Secp256k1, SecretKey}; +use secp256k1_zkp::{EcdsaAdaptorSignature, XOnlyPublicKey}; use std::collections::HashMap; use std::ops::Deref; use std::string::ToString; @@ -264,8 +264,10 @@ where ChannelMessage::RenewFinalize(self.on_renew_confirm(r, &counter_party)?), ))), ChannelMessage::RenewFinalize(r) => { - self.on_renew_finalize(r, &counter_party)?; - Ok(None) + let revoke = self.on_renew_finalize(r, &counter_party)?; + Ok(Some(DlcMessage::Channel(ChannelMessage::RenewRevoke( + revoke, + )))) } ChannelMessage::CollaborativeCloseOffer(c) => { self.on_collaborative_close_offer(c, &counter_party)?; @@ -275,6 +277,10 @@ where self.on_reject(r, &counter_party)?; Ok(None) } + ChannelMessage::RenewRevoke(r) => { + self.on_renew_revoke(r, &counter_party)?; + Ok(None) + } }, DlcMessage::SubChannel(_) => Err(Error::InvalidParameters( "SubChannel messages not supported".to_string(), @@ -954,21 +960,6 @@ where None as Option )?; - let own_buffer_adaptor_sk = if let Some(sub_channel_id) = - signed_channel.sub_channel_id.as_ref() - { - let (signed_sub_channel, state) = - get_sub_channel_in_state!(self, *sub_channel_id, Signed, None::)?; - let own_base_secret_key = self - .wallet - .get_secret_key_for_pubkey(&signed_sub_channel.own_base_points.own_basepoint)?; - let own_secret_key = - derive_private_key(&self.secp, &state.own_per_split_point, &own_base_secret_key); - Some(own_secret_key) - } else { - None - }; - let (accepted_contract, msg) = crate::channel_updater::accept_channel_renewal_internal( &self.secp, &mut signed_channel, @@ -977,7 +968,6 @@ where PEER_TIMEOUT, &self.wallet, &self.time, - own_buffer_adaptor_sk, )?; let counter_party = signed_channel.counter_party; @@ -1578,7 +1568,7 @@ where let offered_contract = get_contract_in_state!(self, &offered_contract_id, Offered, Some(*peer_id))?; - let (own_buffer_adaptor_sk, counter_buffer_adaptor_pk) = if let Some(sub_channel_id) = + let own_buffer_adaptor_sk = if let Some(sub_channel_id) = signed_channel.sub_channel_id.as_ref() { let (signed_sub_channel, state) = @@ -1588,20 +1578,10 @@ where .get_secret_key_for_pubkey(&signed_sub_channel.own_base_points.own_basepoint)?; let own_secret_key = derive_private_key(&self.secp, &state.own_per_split_point, &own_base_secret_key); - let accept_revoke_params = signed_sub_channel - .counter_base_points - .expect("to have counter base points") - .get_revokable_params( - &self.secp, - &signed_sub_channel.own_base_points.revocation_basepoint, - &state.counter_per_split_point, - ); - ( - Some(own_secret_key), - Some(accept_revoke_params.own_pk.inner), - ) + + Some(own_secret_key) } else { - (None, None) + None }; let (signed_contract, msg) = @@ -1615,7 +1595,6 @@ where &self.wallet, &self.time, own_buffer_adaptor_sk, - counter_buffer_adaptor_pk, )?; // Directly confirmed as we're in a channel the fund tx is already confirmed. @@ -1691,23 +1670,34 @@ where let accepted_contract = get_contract_in_state!(self, &contract_id, Accepted, Some(*peer_id))?; - let counter_buffer_adaptor_pk = - if let Some(sub_channel_id) = signed_channel.sub_channel_id.as_ref() { - let (signed_sub_channel, state) = - get_sub_channel_in_state!(self, *sub_channel_id, Signed, None::)?; - let accept_revoke_params = signed_sub_channel - .counter_base_points - .expect("to have counter base points") - .get_revokable_params( - &self.secp, - &signed_sub_channel.own_base_points.revocation_basepoint, - &state.counter_per_split_point, - ); + let (counter_buffer_adaptor_pk, own_buffer_adaptor_sk) = if let Some(sub_channel_id) = + signed_channel.sub_channel_id.as_ref() + { + let (signed_sub_channel, state) = + get_sub_channel_in_state!(self, *sub_channel_id, Signed, None::)?; + let accept_revoke_params = signed_sub_channel + .counter_base_points + .expect("to have counter base points") + .get_revokable_params( + &self.secp, + &signed_sub_channel.own_base_points.revocation_basepoint, + &state.counter_per_split_point, + ); + let (signed_sub_channel, state) = + get_sub_channel_in_state!(self, *sub_channel_id, Signed, None::)?; + let own_base_secret_key = self + .wallet + .get_secret_key_for_pubkey(&signed_sub_channel.own_base_points.own_basepoint)?; + let own_secret_key = + derive_private_key(&self.secp, &state.own_per_split_point, &own_base_secret_key); - Some(accept_revoke_params.own_pk.inner) - } else { - None - }; + ( + Some(accept_revoke_params.own_pk.inner), + Some(own_secret_key), + ) + } else { + (None, None) + }; let (signed_contract, msg) = crate::channel_updater::verify_renew_confirm_and_finalize_internal( @@ -1715,8 +1705,12 @@ where &mut signed_channel, &accepted_contract, renew_confirm, + PEER_TIMEOUT, + &self.time, &self.wallet, counter_buffer_adaptor_pk, + own_buffer_adaptor_sk, + &self.chain_monitor, )?; self.chain_monitor.lock().unwrap().add_tx( @@ -1727,17 +1721,6 @@ where }, ); - let buffer_tx = - get_signed_channel_state!(signed_channel, Established, ref buffer_transaction)?; - - self.chain_monitor.lock().unwrap().add_tx( - buffer_tx.txid(), - ChannelInfo { - channel_id: signed_channel.channel_id, - tx_type: TxType::BufferTx, - }, - ); - // Directly confirmed as we're in a channel the fund tx is already confirmed. self.store.upsert_channel( Channel::Signed(signed_channel), @@ -1758,7 +1741,7 @@ where &self, renew_finalize: &RenewFinalize, peer_id: &PublicKey, - ) -> Result<(), Error> { + ) -> Result { let mut signed_channel = get_channel_in_state!(self, &renew_finalize.channel_id, Signed, Some(*peer_id))?; let own_payout = get_signed_channel_state!(signed_channel, RenewConfirmed, own_payout)?; @@ -1811,7 +1794,30 @@ where } }; - crate::channel_updater::renew_channel_on_finalize(&mut signed_channel, renew_finalize)?; + let counter_buffer_adaptor_pk = + if let Some(sub_channel_id) = signed_channel.sub_channel_id.as_ref() { + let (signed_sub_channel, state) = + get_sub_channel_in_state!(self, *sub_channel_id, Signed, None::)?; + let accept_revoke_params = signed_sub_channel + .counter_base_points + .expect("to have counter base points") + .get_revokable_params( + &self.secp, + &signed_sub_channel.own_base_points.revocation_basepoint, + &state.counter_per_split_point, + ); + Some(accept_revoke_params.own_pk.inner) + } else { + None + }; + + let msg = crate::channel_updater::renew_channel_on_finalize( + &self.secp, + &mut signed_channel, + renew_finalize, + counter_buffer_adaptor_pk, + &self.wallet, + )?; self.chain_monitor.lock().unwrap().add_tx( prev_tx_id, @@ -1841,7 +1847,21 @@ where self.store.update_contract(&closed_contract)?; } - Ok(()) + Ok(msg) + } + + fn on_renew_revoke( + &self, + renew_revoke: &RenewRevoke, + peer_id: &PublicKey, + ) -> Result<(), Error> { + let mut signed_channel = + get_channel_in_state!(self, &renew_revoke.channel_id, Signed, Some(*peer_id))?; + + crate::channel_updater::renew_channel_on_revoke(&mut signed_channel, renew_revoke)?; + + self.store + .upsert_channel(Channel::Signed(signed_channel), None) } fn on_collaborative_close_offer( @@ -1897,6 +1917,7 @@ where check_for_timed_out_channels!(self, RenewOffered); check_for_timed_out_channels!(self, RenewAccepted); check_for_timed_out_channels!(self, RenewConfirmed); + check_for_timed_out_channels!(self, RenewFinalized); check_for_timed_out_channels!(self, SettledOffered); check_for_timed_out_channels!(self, SettledAccepted); check_for_timed_out_channels!(self, SettledConfirmed); @@ -2244,9 +2265,37 @@ where sub_channel: Option<(SubChannel, &ClosingSubChannel)>, is_initiator: bool, ) -> Result<(), Error> { - match channel.state { - SignedChannelState::Established { .. } => self - .initiate_unilateral_close_established_channel(channel, sub_channel, is_initiator), + match &channel.state { + SignedChannelState::Established { + counter_buffer_adaptor_signature, + buffer_transaction, + .. + } => { + let counter_buffer_adaptor_signature = *counter_buffer_adaptor_signature; + let buffer_transaction = buffer_transaction.clone(); + self.initiate_unilateral_close_established_channel( + channel, + sub_channel, + is_initiator, + counter_buffer_adaptor_signature, + buffer_transaction, + ) + } + SignedChannelState::RenewFinalized { + buffer_transaction, + offer_buffer_adaptor_signature, + .. + } => { + let offer_buffer_adaptor_signature = *offer_buffer_adaptor_signature; + let buffer_transaction = buffer_transaction.clone(); + self.initiate_unilateral_close_established_channel( + channel, + sub_channel, + is_initiator, + offer_buffer_adaptor_signature, + buffer_transaction, + ) + } SignedChannelState::Settled { .. } => { self.close_settled_channel(channel, sub_channel, is_initiator) } @@ -2282,10 +2331,14 @@ where mut signed_channel: SignedChannel, sub_channel: Option<(SubChannel, &ClosingSubChannel)>, is_initiator: bool, + buffer_adaptor_signature: EcdsaAdaptorSignature, + buffer_transaction: Transaction, ) -> Result<(), Error> { crate::channel_updater::initiate_unilateral_close_established_channel( &self.secp, &mut signed_channel, + buffer_adaptor_signature, + buffer_transaction, &self.wallet, sub_channel, is_initiator, diff --git a/dlc-manager/src/sub_channel_manager.rs b/dlc-manager/src/sub_channel_manager.rs index 9af2a246..128fe87c 100644 --- a/dlc-manager/src/sub_channel_manager.rs +++ b/dlc-manager/src/sub_channel_manager.rs @@ -14,7 +14,7 @@ use dlc_messages::{ sub_channel::{ Reject, SubChannelAccept, SubChannelCloseAccept, SubChannelCloseConfirm, SubChannelCloseFinalize, SubChannelCloseOffer, SubChannelConfirm, SubChannelFinalize, - SubChannelOffer, + SubChannelOffer, SubChannelRevoke, }, FundingSignatures, SubChannelMessage, }; @@ -31,7 +31,7 @@ use lightning::{ util::ser::{Readable, Writeable, Writer}, util::{errors::APIError, events::MessageSendEventsProvider}, }; -use log::{error, trace, warn}; +use log::{error, info, trace, warn}; use secp256k1_zkp::{ecdsa::Signature, PublicKey, SecretKey}; use crate::{ @@ -48,8 +48,9 @@ use crate::{ manager::{get_channel_in_state, get_contract_in_state, Manager, CET_NSEQUENCE}, subchannel::{ self, generate_temporary_channel_id, AcceptedSubChannel, CloseAcceptedSubChannel, - CloseConfirmedSubChannel, CloseOfferedSubChannel, ClosingSubChannel, LNChannelManager, - OfferedSubChannel, ReestablishFlag, SignedSubChannel, SubChannel, SubChannelState, + CloseConfirmedSubChannel, CloseOfferedSubChannel, ClosingSubChannel, ConfirmedSubChannel, + LNChannelManager, OfferedSubChannel, ReestablishFlag, SignedSubChannel, SubChannel, + SubChannelState, }, Blockchain, ChannelId, ContractId, Oracle, Signer, Storage, Time, Wallet, }; @@ -234,7 +235,11 @@ where Ok(Some(SubChannelMessage::Finalize(res))) } SubChannelMessage::Finalize(f) => { - self.on_sub_channel_finalize(f, sender)?; + let res = self.on_sub_channel_finalize(f, sender)?; + Ok(Some(SubChannelMessage::Revoke(res))) + } + SubChannelMessage::Revoke(r) => { + self.on_sub_channel_revoke(r, sender)?; Ok(None) } SubChannelMessage::CloseOffer(o) => { @@ -578,24 +583,6 @@ where glue_tx_output_value, ); - let mut split_tx_adaptor_signature = None; - self.ln_channel_manager - .sign_with_fund_key_cb(channel_lock, &mut |sk| { - split_tx_adaptor_signature = Some( - get_tx_adaptor_signature( - self.dlc_channel_manager.get_secp(), - &split_tx.transaction, - channel_details.channel_value_satoshis, - &funding_redeemscript, - sk, - &offer_revoke_params.publish_pk.inner, - ) - .unwrap(), - ); - }); - - let split_tx_adaptor_signature = split_tx_adaptor_signature.unwrap(); - let commitment_signed = self .ln_channel_manager .get_updated_funding_outpoint_commitment_signed( @@ -644,7 +631,6 @@ where let msg = SubChannelAccept { channel_id: *channel_id, - split_adaptor_signature: split_tx_adaptor_signature, first_per_split_point: next_per_split_point, revocation_basepoint: offered_sub_channel.own_base_points.revocation_basepoint, publish_basepoint: offered_sub_channel.own_base_points.publish_basepoint, @@ -678,7 +664,6 @@ where let accepted_sub_channel = AcceptedSubChannel { offer_per_split_point: state.per_split_point, accept_per_split_point: next_per_split_point, - accept_split_adaptor_signature: split_tx_adaptor_signature, split_tx, ln_glue_transaction: ln_glue_tx, ln_rollback: (&channel_details).into(), @@ -1118,14 +1103,14 @@ where Ok(Reject { channel_id }) } - /// Marks the channel as finalized when a disconnection happens while the peer is waiting for - /// the RAA from the remote node (the RAA must have been given by the remote node during the + /// Moves the sub channel to the `[SubChannelState::Signed]` state when a disconnection happens while the peer is waiting for + /// the revocation message from the remote node (the revocation secret must have been given by the remote node during the /// reestablishment protocol). - fn mark_channel_finalized(&self, channel_id: ChannelId) -> Result<(), Error> { + fn mark_channel_signed(&self, channel_id: ChannelId) -> Result<(), Error> { let (mut signed_sub_channel, state) = get_sub_channel_in_state!( self.dlc_channel_manager, channel_id, - Confirmed, + Finalized, None:: )?; @@ -1148,7 +1133,7 @@ where .ok_or_else(|| Error::InvalidState( "No contract id in on_sub_channel_finalize".to_string() ))?, - Signed, + Confirmed, None:: )?; @@ -1315,6 +1300,7 @@ where })?; let ln_rollback = (&channel_details).into(); + let offer_revoke_params = offered_sub_channel.own_base_points.get_revokable_params( self.dlc_channel_manager.get_secp(), &sub_channel_accept.revocation_basepoint, @@ -1384,16 +1370,6 @@ where let ln_output_value = split_tx.transaction.output[0].value; - dlc::channel::verify_tx_adaptor_signature( - self.dlc_channel_manager.get_secp(), - &split_tx.transaction, - channel_details.channel_value_satoshis, - &funding_redeemscript, - &channel_details.counter_funding_pubkey, - &offer_revoke_params.publish_pk.inner, - &sub_channel_accept.split_adaptor_signature, - )?; - let channel_id = &channel_details.channel_id; let own_base_secret_key = self @@ -1531,9 +1507,6 @@ where let msg = SubChannelConfirm { channel_id: sub_channel_accept.channel_id, - per_commitment_secret: SecretKey::from_slice(&revoke_and_ack.per_commitment_secret) - .expect("a valid secret key"), - next_per_commitment_point: revoke_and_ack.next_per_commitment_point, split_adaptor_signature: split_tx_adaptor_signature, commit_signature: commitment_signed.signature, htlc_signatures: commitment_signed.htlc_signatures, @@ -1555,20 +1528,22 @@ where }, ); - let signed_sub_channel = SignedSubChannel { + let confirmed_sub_channel = ConfirmedSubChannel { own_per_split_point: state.per_split_point, counter_per_split_point: sub_channel_accept.first_per_split_point, own_split_adaptor_signature: split_tx_adaptor_signature, - counter_split_adaptor_signature: sub_channel_accept.split_adaptor_signature, split_tx, counter_glue_signature: sub_channel_accept.ln_glue_signature, ln_glue_transaction: ln_glue_tx, ln_rollback, + prev_commitment_secret: SecretKey::from_slice(&revoke_and_ack.per_commitment_secret) + .expect("a valid secret key"), + next_per_commitment_point: revoke_and_ack.next_per_commitment_point, }; offered_sub_channel.counter_base_points = Some(accept_points); - offered_sub_channel.state = SubChannelState::Confirmed(signed_sub_channel); + offered_sub_channel.state = SubChannelState::Confirmed(confirmed_sub_channel); self.dlc_channel_manager.get_store().upsert_channel( Channel::Signed(signed_channel), @@ -1610,12 +1585,6 @@ where Some(*counter_party) )?; - let raa = RevokeAndACK { - channel_id: sub_channel_confirm.channel_id, - per_commitment_secret: *sub_channel_confirm.per_commitment_secret.as_ref(), - next_per_commitment_point: sub_channel_confirm.next_per_commitment_point, - }; - let accept_revoke_params = accepted_sub_channel.own_base_points.get_revokable_params( self.dlc_channel_manager.get_secp(), @@ -1661,8 +1630,6 @@ where Some(*counter_party) )?; - self.ln_channel_manager.revoke_and_ack(channel_lock, &raa)?; - let revoke_and_ack = self.ln_channel_manager.on_commitment_signed_get_raa( channel_lock, &sub_channel_confirm.commit_signature, @@ -1710,10 +1677,28 @@ where self.dlc_channel_manager.get_chain_monitor(), )?; + let mut split_tx_adaptor_signature = None; + self.ln_channel_manager + .sign_with_fund_key_cb(channel_lock, &mut |sk| { + split_tx_adaptor_signature = Some( + get_tx_adaptor_signature( + self.dlc_channel_manager.get_secp(), + &state.split_tx.transaction, + accepted_sub_channel.fund_value_satoshis, + funding_redeemscript, + sk, + &offer_revoke_params.publish_pk.inner, + ) + .unwrap(), + ); + }); + + let split_adaptor_signature = split_tx_adaptor_signature.unwrap(); + let signed_sub_channel = SignedSubChannel { own_per_split_point: state.accept_per_split_point, counter_per_split_point: state.offer_per_split_point, - own_split_adaptor_signature: state.accept_split_adaptor_signature, + own_split_adaptor_signature: split_adaptor_signature, counter_split_adaptor_signature: sub_channel_confirm.split_adaptor_signature, split_tx: state.split_tx.clone(), counter_glue_signature: sub_channel_confirm.ln_glue_signature, @@ -1728,9 +1713,10 @@ where ) .expect("a valid secret key"), next_per_commitment_point: revoke_and_ack.next_per_commitment_point, + split_adaptor_signature, }; - accepted_sub_channel.state = SubChannelState::Signed(signed_sub_channel); + accepted_sub_channel.state = SubChannelState::Finalized(signed_sub_channel); self.dlc_channel_manager.get_store().upsert_channel( Channel::Signed(signed_channel), @@ -1753,20 +1739,53 @@ where &self, sub_channel_finalize: &SubChannelFinalize, counter_party: &PublicKey, - ) -> Result<(), Error> { - self.ln_channel_manager.with_useable_channel_lock( + ) -> Result { + let channel_details = self + .ln_channel_manager + .get_channel_details(&sub_channel_finalize.channel_id) + .ok_or_else(|| { + Error::InvalidParameters(format!( + "Unknown LN channel {:02x?}", + sub_channel_finalize.channel_id + )) + })?; + let msg = self.ln_channel_manager.with_useable_channel_lock( &sub_channel_finalize.channel_id, counter_party, |channel_lock| { - let (mut signed_sub_channel, state) = get_sub_channel_in_state!( + let (mut confirmed_sub_channel, state) = get_sub_channel_in_state!( self.dlc_channel_manager, sub_channel_finalize.channel_id, Confirmed, Some(*counter_party) )?; + let funding_redeemscript = &confirmed_sub_channel.original_funding_redeemscript; + + let offer_revoke_params = + confirmed_sub_channel.own_base_points.get_revokable_params( + self.dlc_channel_manager.get_secp(), + &confirmed_sub_channel + .counter_base_points + .as_ref() + .expect("to have counter base points") + .revocation_basepoint, + &state.own_per_split_point, + ); + + dlc::channel::verify_tx_adaptor_signature( + self.dlc_channel_manager.get_secp(), + &state.split_tx.transaction, + confirmed_sub_channel.fund_value_satoshis, + funding_redeemscript, + &channel_details.counter_funding_pubkey, + &offer_revoke_params.publish_pk.inner, + &sub_channel_finalize.split_adaptor_signature, + ) + .map_err(|e| APIError::ExternalError { err: e.to_string() })?; + let dlc_channel_id = - signed_sub_channel + confirmed_sub_channel .get_dlc_channel_id(0) .ok_or(Error::InvalidState( "Could not get dlc channel id".to_string(), @@ -1802,11 +1821,75 @@ where Some(Contract::Confirmed(contract)), )?; - signed_sub_channel.state = SubChannelState::Signed(state); + let signed_sub_channel = SignedSubChannel { + own_per_split_point: state.own_per_split_point, + counter_per_split_point: state.counter_per_split_point, + own_split_adaptor_signature: state.own_split_adaptor_signature, + counter_split_adaptor_signature: sub_channel_finalize.split_adaptor_signature, + split_tx: state.split_tx, + ln_glue_transaction: state.ln_glue_transaction, + counter_glue_signature: state.counter_glue_signature, + ln_rollback: state.ln_rollback, + }; + + let msg = SubChannelRevoke { + channel_id: confirmed_sub_channel.channel_id, + per_commitment_secret: state.prev_commitment_secret, + next_per_commitment_point: state.next_per_commitment_point, + }; + + confirmed_sub_channel.state = SubChannelState::Signed(signed_sub_channel); + + self.dlc_channel_manager + .get_store() + .upsert_sub_channel(&confirmed_sub_channel)?; + Ok(msg) + }, + )?; + + Ok(msg) + } + + fn on_sub_channel_revoke( + &self, + sub_channel_revoke: &SubChannelRevoke, + counter_party: &PublicKey, + ) -> Result<(), Error> { + self.ln_channel_manager.with_useable_channel_lock( + &sub_channel_revoke.channel_id, + counter_party, + |channel_lock| { + let (mut confirmed_sub_channel, state) = get_sub_channel_in_state!( + self.dlc_channel_manager, + sub_channel_revoke.channel_id, + Finalized, + Some(*counter_party) + )?; + + let raa = RevokeAndACK { + channel_id: sub_channel_revoke.channel_id, + per_commitment_secret: sub_channel_revoke.per_commitment_secret.secret_bytes(), + next_per_commitment_point: sub_channel_revoke.next_per_commitment_point, + }; + + self.ln_channel_manager.revoke_and_ack(channel_lock, &raa)?; + + let signed_sub_channel = SignedSubChannel { + own_per_split_point: state.own_per_split_point, + counter_per_split_point: state.counter_per_split_point, + own_split_adaptor_signature: state.own_split_adaptor_signature, + counter_split_adaptor_signature: state.counter_split_adaptor_signature, + split_tx: state.split_tx, + ln_glue_transaction: state.ln_glue_transaction, + counter_glue_signature: state.counter_glue_signature, + ln_rollback: state.ln_rollback, + }; + + confirmed_sub_channel.state = SubChannelState::Signed(signed_sub_channel); self.dlc_channel_manager .get_store() - .upsert_sub_channel(&signed_sub_channel)?; + .upsert_sub_channel(&confirmed_sub_channel)?; Ok(()) }, )?; @@ -2263,11 +2346,12 @@ where Action::ForceSign(id) => { if let Some(details) = self.ln_channel_manager.get_channel_details(&id) { if details.is_usable { - if let Err(e) = self.mark_channel_finalized(id) { - error!("Unexpected error {} making channel {:?} as finalized, keeping the action to retry.", e, id); + if let Err(e) = self.mark_channel_signed(id) { + error!("Unexpected error {} marking channel {:?} as signed, keeping the action to retry.", e, id); retain.push(Action::ForceSign(id)); } } else { + info!("Could not mark channel {:?} as signed as it was not usable, keeping the action to retry.", id); retain.push(Action::ForceSign(id)); } } else { @@ -2741,8 +2825,64 @@ where .map_err(|e| Error::InvalidState(format!("{:?}", e)))?; } SubChannelState::Confirmed(a) => { + self.ln_channel_manager + .with_useable_channel_lock(&channel.channel_id, peer_id, |channel_lock| { + let dlc_channel_id = + channel.get_dlc_channel_id(0).ok_or_else(|| { + Error::InvalidState("Could not get dlc channel id".to_string()) + })?; + let dlc_channel = get_channel_in_state!( + self.dlc_channel_manager, + &dlc_channel_id, + Signed, + None:: + )?; + let contract = get_contract_in_state!( + self.dlc_channel_manager, + &dlc_channel + .get_contract_id() + .expect("Signed contract should have a contract id"), + Signed, + None:: + )?; + let offered_channel = OfferedChannel { + offered_contract_id: contract.accepted_contract.offered_contract.id, + temporary_channel_id: dlc_channel.temporary_channel_id, + party_points: dlc_channel.own_points, + per_update_point: dlc_channel.own_per_update_point, + offer_per_update_seed: channel.per_split_seed, + is_offer_party: true, + counter_party: dlc_channel.counter_party, + // TODO(tibo): use value from original offer + cet_nsequence: CET_NSEQUENCE, + }; + self.ln_channel_manager.set_funding_outpoint( + channel_lock, + &a.ln_rollback.funding_outpoint, + a.ln_rollback.channel_value_satoshis, + a.ln_rollback.value_to_self_msat, + ); + self.dlc_channel_manager.get_store().upsert_channel( + Channel::Offered(offered_channel), + Some(Contract::Offered( + contract.accepted_contract.offered_contract, + )), + )?; + updated_state = Some(SubChannelState::Offered(OfferedSubChannel { + per_split_point: a.own_per_split_point, + })); + Ok(()) + }) + .map_err(|e| Error::InvalidState(format!("{:?}", e)))?; + } + SubChannelState::Finalized(signed) => { if let Some(counter_state) = peer_state { - if counter_state == ReestablishFlag::Accepted as u8 { + if counter_state == ReestablishFlag::Signed as u8 { + self.actions + .lock() + .unwrap() + .push(Action::ForceSign(channel_id)); + } else { self.ln_channel_manager .with_useable_channel_lock( &channel.channel_id, @@ -2765,7 +2905,7 @@ where &dlc_channel.get_contract_id().expect( "Signed contract should have a contract id" ), - Signed, + Confirmed, None:: )?; let offered_channel = OfferedChannel { @@ -2774,39 +2914,50 @@ where .offered_contract .id, temporary_channel_id: dlc_channel.temporary_channel_id, - party_points: dlc_channel.own_points, - per_update_point: dlc_channel.own_per_update_point, - offer_per_update_seed: channel.per_split_seed, + party_points: dlc_channel.counter_points, + per_update_point: dlc_channel.counter_per_update_point, + offer_per_update_seed: None, is_offer_party: false, counter_party: dlc_channel.counter_party, - // TODO(tibo): use value from original offer cet_nsequence: CET_NSEQUENCE, }; - self.ln_channel_manager.set_funding_outpoint( - channel_lock, - &a.ln_rollback.funding_outpoint, - a.ln_rollback.channel_value_satoshis, - a.ln_rollback.value_to_self_msat, - ); + self.dlc_channel_manager + .get_store() + .upsert_sub_channel(&channel)?; self.dlc_channel_manager.get_store().upsert_channel( Channel::Offered(offered_channel), Some(Contract::Offered( contract.accepted_contract.offered_contract, )), )?; + let party_params = + contract.accepted_contract.accept_params.clone(); + let funding_inputs_info = + contract.accepted_contract.funding_inputs; + let accept_points = dlc_channel.own_points.clone(); + let per_update_seed_pk = dlc_channel.own_per_update_seed; + self.actions.lock().unwrap().push(Action::ReAccept { + channel_id, + party_params, + funding_inputs_info, + accept_points, + per_update_seed_pk, + }); + + self.ln_channel_manager.set_funding_outpoint( + channel_lock, + &signed.ln_rollback.funding_outpoint, + signed.ln_rollback.channel_value_satoshis, + signed.ln_rollback.value_to_self_msat, + ); updated_state = Some(SubChannelState::Offered(OfferedSubChannel { - per_split_point: a.own_per_split_point, + per_split_point: signed.counter_per_split_point, })); Ok(()) }, ) .map_err(|e| Error::InvalidState(format!("{:?}", e)))?; - } else { - self.actions - .lock() - .unwrap() - .push(Action::ForceSign(channel_id)); } } } diff --git a/dlc-manager/src/subchannel/mod.rs b/dlc-manager/src/subchannel/mod.rs index 0376ad7a..647ec268 100644 --- a/dlc-manager/src/subchannel/mod.rs +++ b/dlc-manager/src/subchannel/mod.rs @@ -76,7 +76,10 @@ impl SubChannel { match &self.state { SubChannelState::Offered(_) => Some(temporary_channel_id), SubChannelState::Accepted(a) => Some(a.get_dlc_channel_id(temporary_channel_id, index)), - SubChannelState::Signed(s) | SubChannelState::Confirmed(s) => { + SubChannelState::Confirmed(s) => { + Some(s.get_dlc_channel_id(temporary_channel_id, index)) + } + SubChannelState::Signed(s) | SubChannelState::Finalized(s) => { Some(s.get_dlc_channel_id(temporary_channel_id, index)) } SubChannelState::Closing(c) => Some( @@ -106,6 +109,7 @@ impl SubChannel { SubChannelState::Offered(_) => Some(ReestablishFlag::Offered as u8), SubChannelState::Accepted(_) => Some(ReestablishFlag::Accepted as u8), SubChannelState::Confirmed(_) => Some(ReestablishFlag::Confirmed as u8), + SubChannelState::Finalized(_) => Some(ReestablishFlag::Finalized as u8), SubChannelState::Signed(_) => Some(ReestablishFlag::Signed as u8), SubChannelState::CloseOffered(_) => Some(ReestablishFlag::CloseOffered as u8), SubChannelState::CloseAccepted(_) => Some(ReestablishFlag::CloseAccepted as u8), @@ -124,8 +128,12 @@ pub enum SubChannelState { /// The sub channel was accepted. Accepted(AcceptedSubChannel), /// The sub channel was confirmed. - Confirmed(SignedSubChannel), - /// The sub channel transactions have been signed. + Confirmed(ConfirmedSubChannel), + /// The sub channel transactions have been signed, awaiting revocation of the previous + /// commitment transaction. + Finalized(SignedSubChannel), + /// The sub channel transactions have been signed and the previous commitment transaction + /// revoked. Signed(SignedSubChannel), /// The sub channel is closing. Closing(ClosingSubChannel), @@ -154,11 +162,12 @@ pub(crate) enum ReestablishFlag { Offered = 1, Accepted = 2, Confirmed = 3, - Signed = 4, - CloseOffered = 5, - CloseAccepted = 6, - CloseConfirmed = 7, - OffChainClosed = 8, + Finalized = 4, + Signed = 5, + CloseOffered = 6, + CloseAccepted = 7, + CloseConfirmed = 8, + OffChainClosed = 9, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -175,8 +184,6 @@ pub struct AcceptedSubChannel { pub offer_per_split_point: PublicKey, /// The current per split point of the accept party. pub accept_per_split_point: PublicKey, - /// The adaptor signature of the accepting party for the split transaction. - pub accept_split_adaptor_signature: EcdsaAdaptorSignature, /// Information about the split transaction for the sub channel. pub split_tx: SplitTx, /// Glue transaction that bridges the split transaction to the Lightning sub channel. @@ -218,6 +225,40 @@ impl AcceptedSubChannel { } } +#[derive(Debug, Clone, PartialEq, Eq)] +/// Information about a sub channel offered by the local party whose transactions have been signed, +/// but whose previous commitment transaction has not been revoked yet. +pub struct ConfirmedSubChannel { + /// The current per split point of the local party. + pub own_per_split_point: PublicKey, + /// The current per split point of the remote party. + pub counter_per_split_point: PublicKey, + /// Adaptor signature of the local party for the split transaction. + pub own_split_adaptor_signature: EcdsaAdaptorSignature, + /// Information about the split transaction for the sub channel. + pub split_tx: SplitTx, + /// Glue transaction that bridges the split transaction to the Lightning sub channel. + pub ln_glue_transaction: Transaction, + /// Signature of the remote party for the glue transaction. + pub counter_glue_signature: Signature, + /// The secret to revoke the previous commitment transaction of the LN channel. + pub prev_commitment_secret: SecretKey, + /// The image of the next commitment point to be used to build a commitment transaction. + pub next_per_commitment_point: PublicKey, + /// Information used to facilitate the rollback of a channel split. + pub ln_rollback: LnRollBackInfo, +} + +impl ConfirmedSubChannel { + fn get_dlc_channel_id(&self, temporary_channel_id: ChannelId, channel_idx: u8) -> ChannelId { + crate::utils::compute_id( + self.split_tx.transaction.txid(), + channel_idx as u16 + 1, + &temporary_channel_id, + ) + } +} + #[derive(Debug, Clone, PartialEq, Eq)] /// Information about a sub channel whose transactions have been signed. pub struct SignedSubChannel { diff --git a/dlc-manager/src/subchannel/ser.rs b/dlc-manager/src/subchannel/ser.rs index 29017120..99519d71 100644 --- a/dlc-manager/src/subchannel/ser.rs +++ b/dlc-manager/src/subchannel/ser.rs @@ -6,8 +6,8 @@ use lightning::util::ser::{Readable, Writeable, Writer}; use super::{ AcceptedSubChannel, CloseAcceptedSubChannel, CloseConfirmedSubChannel, CloseOfferedSubChannel, - ClosingSubChannel, LnRollBackInfo, OfferedSubChannel, SignedSubChannel, SubChannel, - SubChannelState, + ClosingSubChannel, ConfirmedSubChannel, LnRollBackInfo, OfferedSubChannel, SignedSubChannel, + SubChannel, SubChannelState, }; impl_dlc_writeable!(SubChannel, { @@ -31,17 +31,18 @@ impl_dlc_writeable_enum!(SubChannelState, (0, Offered), (1, Accepted), (2, Confirmed), - (3, Signed), - (4, Closing), - (5, CloseOffered), - (6, CloseAccepted), - (7, CloseConfirmed), - (8, ClosedPunished) + (3, Finalized), + (4, Signed), + (5, Closing), + (6, CloseOffered), + (7, CloseAccepted), + (8, CloseConfirmed), + (9, ClosedPunished) ;;; - (9, OnChainClosed), - (10, CounterOnChainClosed), - (11, OffChainClosed), - (12, Rejected) + (10, OnChainClosed), + (11, CounterOnChainClosed), + (12, OffChainClosed), + (13, Rejected) ); impl_dlc_writeable!(OfferedSubChannel, { (per_split_point, writeable) }); @@ -53,12 +54,23 @@ impl_dlc_writeable!(LnRollBackInfo, { (channel_value_satoshis, writeable), (valu impl_dlc_writeable!(AcceptedSubChannel, { (offer_per_split_point, writeable), (accept_per_split_point, writeable), - (accept_split_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (split_tx, {cb_writeable, split_tx::write, split_tx::read}), (ln_glue_transaction, writeable), (ln_rollback, writeable) }); +impl_dlc_writeable!(ConfirmedSubChannel, { + (own_per_split_point, writeable), + (counter_per_split_point, writeable), + (own_split_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), + (split_tx, {cb_writeable, split_tx::write, split_tx::read}), + (ln_glue_transaction, writeable), + (counter_glue_signature, writeable), + (ln_rollback, writeable), + (prev_commitment_secret, writeable), + (next_per_commitment_point, writeable) +}); + impl_dlc_writeable!(SignedSubChannel, { (own_per_split_point, writeable), (counter_per_split_point, writeable), diff --git a/dlc-manager/tests/channel_execution_tests.rs b/dlc-manager/tests/channel_execution_tests.rs index 7f9cd429..4e1e6a11 100644 --- a/dlc-manager/tests/channel_execution_tests.rs +++ b/dlc-manager/tests/channel_execution_tests.rs @@ -88,6 +88,7 @@ enum TestPath { RenewOfferTimeout, RenewAcceptTimeout, RenewConfirmTimeout, + RenewFinalizeTimeout, RenewReject, RenewRace, RenewEstablishedClose, @@ -234,6 +235,15 @@ fn channel_renew_confirm_timeout_test() { ); } +#[test] +#[ignore] +fn channel_renew_finalize_timeout_test() { + channel_execution_test( + get_enum_test_params(1, 1, None), + TestPath::RenewFinalizeTimeout, + ); +} + #[test] #[ignore] fn channel_renew_reject_test() { @@ -395,6 +405,11 @@ fn channel_execution_test(test_params: TestParams, path: TestPath) { return None; } } + if let TestPath::RenewFinalizeTimeout = path_copy { + if let Message::Channel(ChannelMessage::RenewRevoke(_)) = msg { + return None; + } + } Some(msg) }; @@ -642,7 +657,8 @@ fn channel_execution_test(test_params: TestParams, path: TestPath) { } TestPath::RenewOfferTimeout | TestPath::RenewAcceptTimeout - | TestPath::RenewConfirmTimeout => { + | TestPath::RenewConfirmTimeout + | TestPath::RenewFinalizeTimeout => { renew_timeout( first, first_send, @@ -653,6 +669,7 @@ fn channel_execution_test(test_params: TestParams, path: TestPath) { channel_id, &test_params.contract_input, path, + &generate_blocks, ); } TestPath::RenewReject => { @@ -1064,6 +1081,8 @@ fn renew_channel( second_receive.recv().expect("Error synchronizing"); // Process Renew Finalize first_receive.recv().expect("Error synchronizing"); + // Process Renew Revoke + second_receive.recv().expect("Error synchronizing"); if let Some(prev_contract_id) = prev_contract_id { assert_contract_state!(first, prev_contract_id, Closed); @@ -1223,7 +1242,7 @@ fn collaborative_close( assert_contract_state!(first, contract_id, Closed); } -fn renew_timeout( +fn renew_timeout( first: DlcParty, first_send: &Sender>, first_receive: &Receiver<()>, @@ -1233,6 +1252,7 @@ fn renew_timeout( channel_id: ChannelId, contract_input: &ContractInput, path: TestPath, + generate_blocks: &F, ) { { let (renew_offer, _) = first @@ -1300,6 +1320,27 @@ fn renew_timeout( .expect("not to error"); assert_channel_state!(first, channel_id, Signed, Closed); + } else if let TestPath::RenewFinalizeTimeout = path { + //Process confirm + second_receive.recv().expect("Error synchronizing"); + // Process Finalize + first_receive.recv().expect("Error synchronizing"); + mocks::mock_time::set_time( + (EVENT_MATURITY as u64) + dlc_manager::manager::PEER_TIMEOUT + 2, + ); + second + .lock() + .unwrap() + .periodic_check() + .expect("not to error"); + generate_blocks(289); + second + .lock() + .unwrap() + .periodic_check() + .expect("not to error"); + + assert_channel_state!(second, channel_id, Signed, Closed); } } } diff --git a/dlc-manager/tests/ln_dlc_channel_execution_tests.rs b/dlc-manager/tests/ln_dlc_channel_execution_tests.rs index af1e0366..46ad01ed 100644 --- a/dlc-manager/tests/ln_dlc_channel_execution_tests.rs +++ b/dlc-manager/tests/ln_dlc_channel_execution_tests.rs @@ -1569,34 +1569,75 @@ fn offer_sub_channel( } alice_node.process_events(); - let finalize = bob_node + let mut finalize = bob_node .sub_channel_manager .on_sub_channel_message(&confirm, &alice_node.channel_manager.get_our_node_id()) .unwrap() .unwrap(); - assert_sub_channel_state!(bob_node.sub_channel_manager, channel_id, Signed); + assert_sub_channel_state!(bob_node.sub_channel_manager, channel_id, Finalized); bob_node.process_events(); assert_sub_channel_state!(alice_node.sub_channel_manager, channel_id, Confirmed); + if let TestPath::Reconnect = test_path { + reconnect( + alice_node, + bob_node, + alice_descriptor.clone(), + bob_descriptor.clone(), + ); + assert_sub_channel_state!(alice_node.sub_channel_manager, channel_id, Offered); + assert_sub_channel_state!(bob_node.sub_channel_manager, channel_id, Offered); + + // Bob should re-send the accept message + let mut msgs = bob_node.sub_channel_manager.periodic_check(); + assert_eq!(1, msgs.len()); + assert_eq!(0, alice_node.sub_channel_manager.periodic_check().len()); + if let (SubChannelMessage::Accept(a), p) = msgs.pop().unwrap() { + assert_eq!(p, alice_node.channel_manager.get_our_node_id()); + let confirm = alice_node + .sub_channel_manager + .on_sub_channel_message( + &SubChannelMessage::Accept(a), + &bob_node.channel_manager.get_our_node_id(), + ) + .unwrap() + .unwrap(); + finalize = bob_node + .sub_channel_manager + .on_sub_channel_message(&confirm, &alice_node.channel_manager.get_our_node_id()) + .unwrap() + .unwrap(); + } else { + panic!("Expected an accept message"); + } + } + + let revoke = alice_node + .sub_channel_manager + .on_sub_channel_message(&finalize, &bob_node.channel_manager.get_our_node_id()) + .unwrap() + .unwrap(); + assert_sub_channel_state!(alice_node.sub_channel_manager, channel_id, Signed); + assert_sub_channel_state!(bob_node.sub_channel_manager, channel_id, Finalized); + if let TestPath::Reconnect = test_path { reconnect(alice_node, bob_node, alice_descriptor, bob_descriptor); - // For some weird reason uncommenting this triggers a stack overflow... - // assert_sub_channel_state!(alice_node.sub_channel_manager, channel_id, Confirmed); - // assert_sub_channel_state!(bob_node.sub_channel_manager, channel_id, Signed); + assert_sub_channel_state!(alice_node.sub_channel_manager, channel_id, Signed); + assert_sub_channel_state!(bob_node.sub_channel_manager, channel_id, Finalized); assert_eq!(0, alice_node.sub_channel_manager.periodic_check().len()); assert_eq!(0, bob_node.sub_channel_manager.periodic_check().len()); } else { - alice_node + bob_node .sub_channel_manager - .on_sub_channel_message(&finalize, &bob_node.channel_manager.get_our_node_id()) + .on_sub_channel_message(&revoke, &alice_node.channel_manager.get_our_node_id()) .unwrap(); } - assert_sub_channel_state!(alice_node.sub_channel_manager, channel_id, Signed); + assert_sub_channel_state!(bob_node.sub_channel_manager, channel_id, Signed); alice_node.process_events(); } diff --git a/dlc-messages/src/channel.rs b/dlc-messages/src/channel.rs index 3da5366f..c5981b3f 100644 --- a/dlc-messages/src/channel.rs +++ b/dlc-messages/src/channel.rs @@ -430,9 +430,6 @@ pub struct RenewAccept { /// The per update point to be used by the sending party to setup the next /// channel state. pub next_per_update_point: PublicKey, - /// The adaptor signature for the buffer transaction generated by the offer - /// party. - pub buffer_adaptor_signature: EcdsaAdaptorSignature, /// The adaptor signatures for all CETs generated by the offer party. pub cet_adaptor_signatures: CetAdaptorSignatures, /// The refund signature generated by the offer party. @@ -442,7 +439,6 @@ pub struct RenewAccept { impl_dlc_writeable!(RenewAccept, { (channel_id, writeable), (next_per_update_point, writeable), - (buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (cet_adaptor_signatures, writeable), (refund_signature, writeable) }); @@ -464,9 +460,6 @@ pub struct RenewConfirm { )] /// The id of the channel referred to by the message. pub channel_id: [u8; 32], - /// The pre image of the per update point used by the sending party to setup - /// the previous channel state. - pub per_update_secret: SecretKey, /// The adaptor signature for the buffer transaction generated by the offer /// party. pub buffer_adaptor_signature: EcdsaAdaptorSignature, @@ -478,7 +471,6 @@ pub struct RenewConfirm { impl_dlc_writeable!(RenewConfirm, { (channel_id, writeable), - (per_update_secret, writeable), (buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (cet_adaptor_signatures, writeable), (refund_signature, writeable) @@ -504,9 +496,40 @@ pub struct RenewFinalize { /// The pre image of the per update point used by the sending party to setup /// the previous channel state. pub per_update_secret: SecretKey, + /// The adaptor signature for the buffer transaction generated by the accept + /// party. + pub buffer_adaptor_signature: EcdsaAdaptorSignature, } impl_dlc_writeable!(RenewFinalize, { + (channel_id, writeable), + (per_update_secret, writeable), + (buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}) +}); + +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] +/// Message used to finalize the establishment of a new contract within a channel. +pub struct RenewRevoke { + #[cfg_attr( + feature = "serde", + serde( + serialize_with = "crate::serde_utils::serialize_hex", + deserialize_with = "crate::serde_utils::deserialize_hex_array" + ) + )] + /// The id of the channel referred to by the message. + pub channel_id: [u8; 32], + /// The pre image of the per update point used by the sending party to setup + /// the previous channel state. + pub per_update_secret: SecretKey, +} + +impl_dlc_writeable!(RenewRevoke, { (channel_id, writeable), (per_update_secret, writeable) }); diff --git a/dlc-messages/src/lib.rs b/dlc-messages/src/lib.rs index d8eb713b..b27a237e 100644 --- a/dlc-messages/src/lib.rs +++ b/dlc-messages/src/lib.rs @@ -42,8 +42,8 @@ use crate::ser_impls::{read_ecdsa_adaptor_signature, write_ecdsa_adaptor_signatu use bitcoin::{consensus::Decodable, OutPoint, Script, Transaction}; use channel::{ AcceptChannel, CollaborativeCloseOffer, OfferChannel, Reject, RenewAccept, RenewConfirm, - RenewFinalize, RenewOffer, SettleAccept, SettleConfirm, SettleFinalize, SettleOffer, - SignChannel, + RenewFinalize, RenewOffer, RenewRevoke, SettleAccept, SettleConfirm, SettleFinalize, + SettleOffer, SignChannel, }; use contract_msgs::ContractInfo; use dlc::{Error, TxInputInfo}; @@ -56,7 +56,7 @@ use segmentation::{SegmentChunk, SegmentStart}; use sub_channel::{ Reject as SubChannelReject, SubChannelAccept, SubChannelCloseAccept, SubChannelCloseConfirm, SubChannelCloseFinalize, SubChannelCloseOffer, SubChannelConfirm, SubChannelFinalize, - SubChannelOffer, + SubChannelOffer, SubChannelRevoke, }; macro_rules! impl_type { @@ -86,6 +86,7 @@ impl_type!(RENEW_CHANNEL_OFFER_TYPE, RenewOffer, 43014); impl_type!(RENEW_CHANNEL_ACCEPT_TYPE, RenewAccept, 43016); impl_type!(RENEW_CHANNEL_CONFIRM_TYPE, RenewConfirm, 43018); impl_type!(RENEW_CHANNEL_FINALIZE_TYPE, RenewFinalize, 43020); +impl_type!(RENEW_CHANNEL_REVOKE_TYPE, RenewRevoke, 43026); impl_type!( COLLABORATIVE_CLOSE_OFFER_TYPE, CollaborativeCloseOffer, @@ -96,6 +97,7 @@ impl_type!(SUB_CHANNEL_OFFER, SubChannelOffer, 43034); impl_type!(SUB_CHANNEL_ACCEPT, SubChannelAccept, 43036); impl_type!(SUB_CHANNEL_CONFIRM, SubChannelConfirm, 43038); impl_type!(SUB_CHANNEL_FINALIZE, SubChannelFinalize, 43040); +impl_type!(SUB_CHANNEL_REVOKE, SubChannelRevoke, 43052); impl_type!(SUB_CHANNEL_CLOSE_OFFER, SubChannelCloseOffer, 43042); impl_type!(SUB_CHANNEL_CLOSE_ACCEPT, SubChannelCloseAccept, 43044); impl_type!(SUB_CHANNEL_CLOSE_CONFIRM, SubChannelCloseConfirm, 43046); @@ -545,6 +547,7 @@ pub enum ChannelMessage { RenewAccept(RenewAccept), RenewConfirm(RenewConfirm), RenewFinalize(RenewFinalize), + RenewRevoke(RenewRevoke), CollaborativeCloseOffer(CollaborativeCloseOffer), Reject(Reject), } @@ -556,6 +559,7 @@ pub enum SubChannelMessage { Accept(SubChannelAccept), Confirm(SubChannelConfirm), Finalize(SubChannelFinalize), + Revoke(SubChannelRevoke), CloseOffer(SubChannelCloseOffer), CloseAccept(SubChannelCloseAccept), CloseConfirm(SubChannelCloseConfirm), @@ -610,6 +614,7 @@ impl_type_writeable_for_enum!(ChannelMessage, RenewAccept, RenewConfirm, RenewFinalize, + RenewRevoke, CollaborativeCloseOffer, Reject }); @@ -620,6 +625,7 @@ impl_type_writeable_for_enum!(SubChannelMessage, Accept, Confirm, Finalize, + Revoke, CloseOffer, CloseAccept, CloseConfirm, diff --git a/dlc-messages/src/sub_channel.rs b/dlc-messages/src/sub_channel.rs index 97083a43..e19387fe 100644 --- a/dlc-messages/src/sub_channel.rs +++ b/dlc-messages/src/sub_channel.rs @@ -100,8 +100,6 @@ pub struct SubChannelAccept { /// The base point that will be used by the offer party in the 2 of 2 output /// of split transactions. pub own_basepoint: PublicKey, - /// The adaptor signature for the split transaction. - pub split_adaptor_signature: EcdsaAdaptorSignature, /// The signature for the new commit transaction of the Lightning channel. pub commit_signature: Signature, /// The htlc signatures for the new commit transaction of the Lightning channel. @@ -140,7 +138,6 @@ impl_dlc_writeable!( (revocation_basepoint, writeable), (publish_basepoint, writeable), (own_basepoint, writeable), - (split_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (commit_signature, writeable), (htlc_signatures, writeable), (first_per_split_point, writeable), @@ -157,7 +154,7 @@ impl_dlc_writeable!( } ); -/// A message to confirm the establishment of a DLC channel within an existing Lightning channel. +/// A message sent by the offer party to confirm the establishment of a DLC channel within an existing Lightning channel. #[derive(Clone, Debug)] #[cfg_attr( feature = "serde", @@ -167,10 +164,6 @@ impl_dlc_writeable!( pub struct SubChannelConfirm { /// The id of the Lightning channel the message relates to. pub channel_id: [u8; 32], - /// The pre-image of the revocation point used for the old commitment transaction. - pub per_commitment_secret: SecretKey, - /// The commitment point for the next Lightning commitment transaction. - pub next_per_commitment_point: PublicKey, /// The adaptor signature used for revocation of the split transaction. pub split_adaptor_signature: EcdsaAdaptorSignature, /// The signature for the new commitment transaction. @@ -190,8 +183,6 @@ pub struct SubChannelConfirm { impl_dlc_writeable!(SubChannelConfirm, { (channel_id, writeable), - (per_commitment_secret, writeable), - (next_per_commitment_point, writeable), (split_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (commit_signature, writeable), (htlc_signatures, writeable), @@ -201,7 +192,8 @@ impl_dlc_writeable!(SubChannelConfirm, { (ln_glue_signature, writeable) }); -/// A message to finalize the establishment of a DLC channel within an existing Lightning channel. +/// A message sent by the accept party to finalize the establishment of a DLC channel within an +/// existing Lightning channel and revoking the previous commitment transaction. #[derive(Clone, Debug)] #[cfg_attr( feature = "serde", @@ -215,13 +207,37 @@ pub struct SubChannelFinalize { pub per_commitment_secret: SecretKey, /// The commitment point for the next Lightning commitment transaction. pub next_per_commitment_point: PublicKey, + /// The adaptor signature for the split transaction. + pub split_adaptor_signature: EcdsaAdaptorSignature, } impl_dlc_writeable!(SubChannelFinalize, { (channel_id, writeable), (per_commitment_secret, writeable), - (next_per_commitment_point, writeable) + (next_per_commitment_point, writeable), + (split_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}) +}); +/// A message sent by the offer party to revoke the previous commitment transaction. +#[derive(Clone, Debug)] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] +pub struct SubChannelRevoke { + /// The id of the Lightning channel the message relates to. + pub channel_id: [u8; 32], + /// The pre-image of the revocation point used for the old commitment transaction. + pub per_commitment_secret: SecretKey, + /// The commitment point for the next Lightning commitment transaction. + pub next_per_commitment_point: PublicKey, +} + +impl_dlc_writeable!(SubChannelRevoke, { + (channel_id, writeable), + (per_commitment_secret, writeable), + (next_per_commitment_point, writeable) }); /// A message to offer the collaborative (off-chain) closing of a DLC channel embedded within a diff --git a/dlc-sled-storage-provider/src/lib.rs b/dlc-sled-storage-provider/src/lib.rs index c6ceb4e4..90e82cf5 100644 --- a/dlc-sled-storage-provider/src/lib.rs +++ b/dlc-sled-storage-provider/src/lib.rs @@ -145,6 +145,7 @@ convertible_enum!( RenewAccepted, RenewOffered, RenewConfirmed, + RenewFinalized, }, SignedChannelStateType ); @@ -154,6 +155,7 @@ convertible_enum!( Offered = 1, Accepted, Confirmed, + Finalized, Signed, Closing, OnChainClosed, diff --git a/dlc-sled-storage-provider/test_files/Accepted b/dlc-sled-storage-provider/test_files/Accepted index 2f38c3beb0a27281b52d203c268cb47c0b88a402..779ef465a80b312df05945bb329715f4ffe91ae1 100644 GIT binary patch literal 8551 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 literal 3430 zcmdnQ(=&N(mGl+Aurp475(Q`4T0GI78Girnb9J5<*4#JOF)%VRF)%aW0$>@|Sg!`S z0#*=WvoMn7y(nF1oeT#gw z+a+K4`Ls^@!L)D*>+#LkwrcMvx4gVuGfZg9>8Z=qUuzofbPKMR;@@tPt!&Qk?)7(_ zfpUM_Sq-nA)kptU*td5G2&=a6MRHF2_-pgx!t~z(6JX=eN^v~(Xi9iXL>_PMu03${zn6n>TF(ckzoO77B?kJiCx~k<>ftxVW{*AAy76x4Io;)X0*cZuU$P4&uR8s)KN`zF@8N2x+;<74qBlf$vF%y- z>6>)M6zy|TO!NCEKlf+fVQ@yXj)gy`AUk1wVr#ebbeRK_uNqg>T76#;*?G+%=4`^^ z`43+6d|R`BtrPeBpT_BRkZ0b#D*lAvsps=<*nNsOH`{o`fwQxM18l;-|Ns9pF&ui$ z4dMYE-1JRC@4%Gu?jh)vm)Sg(6|EB}En$L58IBeQiEzrTFAeDO0U2T>3 z+3|E`O3bg_+x>E4{#V`WivW9#2L^rUjxu54)TVc=Y(>|Hf>` z4--v<_81G#i4VL3b~6J5(9!=vVZv}e@!&DLsK_f&aYkU=u1`4OX22xT65jaD@JPSR zbk0Wxlh|t6n(GZ#@mvV;I??`b;@yjB%s;Q5{qTs*Au-G=s3)QAz`_};r~O@XznHIm z!N#ns?1H)=-(D~d6XAKfgzx5I*Vra;%ZJLfqK3TLN4yvPD&7y0|9SPohh6D+7;aiM zY`Yz>O{3-Vui)Bp4aNfS6ZKZjf(D5Bw%GXlXa%H@^CiK&+Q@*vlm`TC( zbs2sI3FRY*GF(rj>@u=)q+NVvP;OoW|qX;!#S7N`amm~tQ=&P3>hOY^{W zg6k}xYF3zXAPr+8xt1L!#)+X5oCHAjBk2UzrZ5d$Fa|evP63#-Acju3N3iP@g6R~-(8&#x!md*UCM}AglLsb+U8fjKS{y?s zFH8zVBVt1WCM*eKNP(gb2w=$;CXR?e28OyRTY?n&*6{+`;AcX_7cRX2ZqixsRU zg))o#TZ7&ktgpIr{e{u)<~fa8jbVDn=BLbdjwvxNx@Yh&>0;!WFp$|yM3rWsVwtE) z4Wy8n?}an0*1=ZL`jz#ufxHGR3$WF)MJlXdX(a}Q{fmFa&Q?}lzxYGup+6OAtj z|CN+}&$MANoVAkePXWjjHjpnEB|ZsE-|@EYvdq5>PQ z76^fZ5mb0GIw(Vo$5G0!=iZ+{nu$ye*Or2d2plFJ@QDZ84wS~$XduHv2H`v4?e diff --git a/dlc-sled-storage-provider/test_files/AcceptedChannel b/dlc-sled-storage-provider/test_files/AcceptedChannel index 3bd3d7419b3a138cd053b14f656059513919e623..d15fa1b66d5d181f301fa4fbc6f5228c8dad90c5 100644 GIT binary patch delta 904 zcmV;319$w42aE@QZw--=wsZnTwXlf&&=A!DS;$$*;kJ+rMkleq6xLxt0^fEO_8h)B zd`%KSm7*!O4Joz%6s>(eNElsslE5+5q5^d{D3r`{nB6s7&cYL*e9Io+K^2JOJvNu) z{LJfhH5~%d{)!zx=G-MD`q|fzc)5;D#uz*#S5#|Ri*l=fN2wnt0yjYYSa$lgRy1|v zuNsol6c%+dTV!_brf00000{{R2~0dgb-1^@s6 z03rY&BY!Y6wPPGB#8dRz3C8Ltb!U;yAD0tL-I|1@_< z0UuYOu-5&k0j7gRK^;FiI)>|lr(X2h=B#96cBvGuG=1SkDgssBB=|A?iuwZJq@ePM zt#+vt9|Z4dh6tt;f#Y;ifLS>l%ym9+h^=<16nyJ)XaKZ}^IKj+LJ#Ow2~$KlIEZI< zsT41NI7)6FY^u)y|E#tHAmr2asRNdXt#+vt`xwaz8Xo&rnSW~2_>)5=z z$Atq`UtCrLpTNc899~dVB6@5RVDp38km_<;a=f*WqwOUEAwsMh{$fQ3;k@cl`-tX$ z$P*gLQr7#ktWPU((8MhU5MFmCa}WrnuenEE&J@ zwV)ku8n|)1+e_(=EY6PX(odV0?{=c1-cL5pkt%&7Aq)^j>kW8lVnVrcdE;;7>=#{u e{#KYXN*o-Y@B$cwSAq61#E%@83F0u5c>@z4r?4de delta 904 zcmV;319$w42aE@QShvwc7vt_B``>(I;$6|}BNiF>tIYQN-_2J#p%ezt0_&WEmasfW z&?S8fkcA!_n%d0)KNQY-VSEYS7Ep28SkpCs33V%?0y?9}NKR=B+%Rn_ zxjyD^QT|B-NJOx7($Q|m8T=$b3IgNRO@8#Ap~cp{oOby|Fyi5$A?D9_HJiQuGO<## zj-vT(#T8S;7JU z0004;EZ&mAaj&1)xMy1gQSS$42)-(~%@cV4V^@H;zI4C>00000{{R2~0dgb-1^@s6 z03rY&jk`~HeZUSAYQq*bZj72gm7s3YqrMCwZ}zT#Q8k?C6R!)uU;yAD0)xfb2@cvI zE3llx&f{tri4I1@e8V<80$%O0=#`MA_N-)McBvHRLz1Xrm^AaI2MRuRc{5X1Gp&_~ zt#+vtFKQk?t{dd~jd=3>WOD!5eyo0Hh^=<16hsDd1QQqwlwzE068|*;8y+mtponL7 zsT9$FJ4k6L0wwg*sHXt#+vt2810-`D{ViyU2^J(+}&>Q?;I3h^=<1 z6dn&_G1Z^~L7^o$!s68Se{uhNJBVm!Ap>aDF_??xb_r2YAr-jhu-Hs&*{yWsYnH<2 z`5RRQUyo?4arO4o0Prl|V$Psuz?k|fN)}6h8k0JXNkD~wy`rbk>4uue9ie|CIfkhYPiCzRCyEf{IbmihIvQ z5pDxQ<&9$CAkccf9!TL+D^cW)FRFZo2oP}CHos|`@?k{-)t<*=3JnT83+ksyq+f)8 zJ*fp_Sqw5m^pe^oTqDL^j8x2~TE9j<@1)dA-LB-&D+5Ha@b(<%d1Y!3i~)mEwm~4o zeMuL=z&GYA59UbKSs|72c46<7tGirbCq+@#Ps;uw; diff --git a/dlc-sled-storage-provider/test_files/AcceptedSubChannel b/dlc-sled-storage-provider/test_files/AcceptedSubChannel index 5dec51aed231e42ce7965c17afb901dc02dbc1e5..a7c755bfe30cf50e96e4ec2be0b01e112994a9f6 100644 GIT binary patch literal 2997 zcmY!to_phje~`-AqfQ^5?e2JdUE5O8O#RB0^#TF&*D2Q@WMaG;p2DvBdf%aW+v>g7 z-0CpC5>+y#!|}?@lKsxMxBLqk82*C+BXiDnw`fm}{~H8F+oO6f?Ra)NUv5oC<0Z}2 ze^hLoYU*w=RVqt`S$~%e=1w=)Oe}j-S-iRY{gdRDZ!=zH?>7<@tYl(fU|>`w;2@wH z2w?hU$_QpDF(^zG_RWkrxrs5}z|p>X5yS2uvRmYzrgjjm6l~-J zMdq65ivhR0Pw_BTwLA~7%V&9P8spaJ<8mbDn`g;B{;dbrq$HQE6ghG8bmx&2(}WF5 zLDzO(+?fBL@lHs`TA+k^d8|?Gp603ryLT+rdb~YhVvxWQu*AOuNi($N)t)o#I3&hV zw7Z#aS$1ScI#7qU^8SJeX`PIXVKd%#FrSL6%~ehW>*(N!3)u4a>)~kC_QU2I)=J*v z6MP1i&=7Dbo{(>s9-nudp;&BV0>iSf=#GqZMdqW^eSa1gC~SP?`88itGK273cGa*OjH&DeA}n8SD$pPf&ShidJu%GrL_SLMM$ zp#TgyMtek1%YRn9(YWn7w}jslql1dL33QoMS< zZrr=v$121*=Jyot&y43%f4o?g))}y-Nbw=lc4nK0y!>p9OBVY7a#rY>-`>%wbF5;o zux7*gYy0Qe^vBL)%9OR)m(R0`<5rQ`$zwk*W!*aRG$tc_QC?LR=Uu1Y-+36BZR&z| zt#7~9zb#Jw;Uu<$9pa0vxO$JpY~3dj^p2%t#d4+-!M2Y-9lGtc<+xJ*s@vhL%Ee*k z`?_;#^7Hp3-`&o2fw|&l_OyvlZ-0&DHF;ucXL+TpEnD|QdG6Od$)!6&zqlBIJp&3N z_aH@PA=`;dD)VdpP5LOUTq*n7Y*KU5{*$5i1E#Q?nY}MKOk9yUVCxn&)eB#$v~`oB ze(>a^c+aZQ_%&6@Rjt7^`OR&YdqL|M2%19B5U`u@5s*xTPX$D7R2}v(8V#4xZ~=wD UXgU~82czi#dm0!G7sBBJ087Ke_W%F@ literal 3159 zcmaF+<=CQQvTv#*bJ|KwUp4p2*zdM^5bx?|w^(AkZt(Lw<_8-pE;GEZsu#EQ&X3}` z%lzL_-k70c+oIXgduHr^(C*5>@E;5qnVTJTCflyRwj(d}&f%-2=Xp-?J*!xns=F`j z;1`ZJMJ<|~e??>utAzYqYzMK%s{J%lJI50XD(WERw0-BPQ9V*{hJKG#Ivv9pilsY9HTuVsEzg~|K^u_?e}ZxVyI>8 zamHo&Yt3e0n=mbxk%_NP=HhW?PVP@_IqR23)M#{_J-?SFe9iJ$e{QzZFY;$GdA*jt zDPFUt@=yI4hrmlQ3}0@ur7qmS!TdBkRB_H(b3-N*)8NoOALZ{4eX& z>+@Q7W(Lyq diff --git a/dlc-sled-storage-provider/test_files/Closed b/dlc-sled-storage-provider/test_files/Closed index 2ed6b4d942ef452eaa8e65e8cb82eb4212cfb120..5a8e38c0cadc5ce3857e56969539f504b0b34fb7 100644 GIT binary patch delta 1094 zcmV-M1iAav2-OIG0Rblb7s(#pZb zkJao5_j&6_yr4Vp1UzK=b2H%GQ1O0-;sPq-vL&}iBciy^S690A(kmmRfn3**|#yMS-CVDE2lw>!gwXiGL( zEoRnlPJ>9d+Aq=w<%%OV0*p8C{fQ(LHREJ4Qrmn*)jOiems#QIiwQFERq#-u+*w-$ zg9tq?)7v~~Cp+8s?z=Q$S3!5vd)Qg z5^Iw0{)oQk672d_>Bu7X)#|c`lpz?Ou$Mu@k*V9OP!%#v`T7zlu*~vT!V{AZchXyx z9^cb{cQq@-)bx>o%HQ&Vyx1oLk_$7pDE+&-F&I1ph(3DS?fC2F_#On(^We-)2V8gE zf0*o8CnO*;Vq+X+cfwuC(Nm0R1R$xhFqRdyy;$y77TkcPsOHywg3tNFaa?EF##|EF##|EF#$0FF#$3C0Wbmp000010ol<< z(niP-B~113fyY6Fj_2~IJk;-f(LQ8?hZ^B9!~g&Q0092~|Nj9%BLxNk0000M02GOT zF4YGb9W9{PxPeZdz}{FgwgBS<07x)J0wDnBuR!kwosoHK&es8*RknGoQJl1@PT)zt zV!OrTGKVLDY)QfMx_1VFgzF%xulytPzF({(B<1Z2?CxL;@gEiOjP3 znbqql5;GE+c{`gP@_NTg5@AdD0j-^X9`USg0w5D}1iOUf9>;I~Z@TRjAI=ZANs;o0 zMaghN>QXvA$fn3Yq=bu*6PpI%cM5|z`1sNN*SKnPhj^feCb~%J00~i14 zlHzoU-vIie&Sm?N0nOhey_5*Fj^Ww27-Fe^5>l?O3%_6|F)Gz%-1f9DS9?A*K311a zSbam18T&avhQjo#h2Y%Z;{nUh;$D)9{wkE}0{&&H*7*WemSEmgIlPd%v M2><{8|NmGgKzDT!9{>OV delta 1093 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 Q$Zl?A?UGsm0Fz@1FCHSQ)&Kwi diff --git a/dlc-sled-storage-provider/test_files/OfferedChannel b/dlc-sled-storage-provider/test_files/OfferedChannel index 0e8003f4a6164416f0fbe4241e4e71790aa666af..eed80072ce993c4e49f9e4305e571bf7e6591d04 100644 GIT binary patch delta 205 zcmV;;05bpU0qX&OM|++i(@6!1)@O&X|7^JaF`0*Y8Q`%32INcF*~#t+yl=i7aM)GM z2t&PT{|dD6FN44%bu$AH0Z z%VGwrwgOasWmLTCLxp2P7pdtSFa#h5wHx-%D;NHEbvAZI*$>SEU{#tofa2zpm5n*JADyLpmVwc$cUtEUCHnR zw2!dH0tkW!s*}^#Zq$RD`g+26i?g+W1F!4>&qRZLLY{?p;}q)rW#z{`_KFU3gxS^l zBe$Q}hy#IED(s&i9hF&AO{=jhqhu{oO11AsOj&;{VcC#HCx#gVoj+bv?lsg!WoXn^ zd#`qtqt}bM6(ahnGHOXJThNOm0$(eJ>4?}MHEEQS6Is52Wdbfu5_80Qped*@WurR7 HS&_sioatfB diff --git a/dlc-sled-storage-provider/test_files/OfferedSubChannel b/dlc-sled-storage-provider/test_files/OfferedSubChannel index 41e986b95cc7cd28e910a64571416738bceccbf3..e1194009f1c98bb922a9d5ce6783458ccf2faa64 100644 GIT binary patch literal 2424 zcmd;I+LPb#+Hvp9bl-hSn^e9BFi-t9>(0}Cj}Ch~ChxeB!_=m^k0H0_&gUQPacOrq zy_8g~zs2nQwmVbNAT{Di{m0V`4FAD^f%(q-f(PGp3LTRTZqL{^D|L@`(AHnYlR6*& z`JrGKzszS50~9ba8^m^_ltXujY9g z<>mKS-bxAe-Cj45_ee@$Sc$W&$xAKG#XF8&XEKSNdv0e^y6g4rS}q-VUrk;cigc$} zPB#8}$|yoR;S4t;^MV!f@oc`05=qO8HY$5xRQKBFC#tn(w~dkC@kc^_ee;<;Sj}%2 zXpFZ_`q@^i1L(X8S08=>I&njt|E$L4e91wrp^FeHWk;L5d zKLwjMfBGrc~d|&)`?Bn^rlPS_r$29o-zq7sPFG}UK zf}DaiSAgGDw2!+$VfUr7cK+d27Zd!P)VXe;NtKy-s}5z2;!?Iinxh zGfm8=eb^?-xUrLw$@0R)+eWNkrq{ps$TSbI-no75tjUR)R}#+N_OClNbyW=0qcby< zZLXeK$usw0*HrmJ4hwaNb0s$RoLZU2^4y+Ug= zgh~qTDl*TQ+Hicj?9MrxdbT&%`jl=@fAcfzU}#+S<5m0aHoRD88MKappc;bKz`__O z&4^P5Rbo^fYIwkSqu~Mz2~>JCTu{RU#v2V6SV*AKqv3)Y9x&c$xWGaJl^$;40stZZ B-?IP! diff --git a/dlc-sled-storage-provider/test_files/OfferedSubChannel1 b/dlc-sled-storage-provider/test_files/OfferedSubChannel1 index d3c516593515b9e1da880533e28a402ada406ad5..9e58df8797cdd1a5caa492534144a07221d9a410 100644 GIT binary patch literal 2424 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 P1uzKkzZGwtM~nXhZ+H{o literal 6874 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 z zs|jK;)1<9W<0)@*W3dXcn_T@OHxu_Qkv_NGWrgK6t&k_!k?~yA5QWvibv!C@_h)4$ zQf!~NJveQuWwv%NrFsaRul!(cIU3!?Jx6B{9Y3?|5i1v^v?+Ay)oLQ&0>V0~Eu5IU zBq!w>Mr2w#S^jD$mj;_}f9wYrgmRNzG@#a|WTBzj-b8&km0BzWhQI%&{B0GDnr>z> zU$)#cGe0j2TQFVZtu~!dYB;&7_L(IqC1+Z@_{|7o?;0cIf{h;Q@EwrJ8GV4I9b!7r z8cu#&>k$Va%Te|P@i^&vx^Ya6wbfVq(RmwLnB$_rh#$k%k76pGMOprE{WUsdtc9!k za5W9#Eoju^jZX^Z_2c39;xX@%($m8xnW@FxMF4w=`<|U%k~ZNVT6R!Y{APOFij@(R zXUElHO&8`VC%HjmdX6>Vs*u64ebSb@)+wXg_1@l@Xqi|<@N#b)U#%oRyMO3)aOie| zK)O51Ayn|p3N__}4k7qdGBJM8M@ zInk$tjJ2-EB@GVg`#kkD{A%59X*S;A=t~lQfiGIS&-N6JUh5~gpH+LMMy>39!75Z( zoxpaTxNk1uUotC}ybK1aE!*W+p3+}@S`%>%LRYkBJr7=vQ4E@`VSwr*lBMaPr@C5h zsXo0hAV$W#XsKlI%eSJmWs>R5YIUEI4OIaCoeq{DDHSTd zef#Pv3fGTrk{~b6^=^o-Uik!wnT}k*JVNGU!|u$Yg|aKzn;Y$3X+A93DGo+dnYta@ z6bEUp;$fLG>ICmg_wbCE5;VLogX{s&=$1*k2xS}^a8h3MK_cU%WbnypV92hnT7#e3 zJNclNxMExGbs#^rJv*gRuECX>#_QtZ;U}eu#gk*iwsdI;hKFf`l2c>M_T^n79g zWnIb1;!n0REjlSp{VbzLY@0Q2-n)eeOhJ}5J61NVXn8d;Ly}Q$t|O>W{iHN@5ai~g zO=0o49PiQR^!IHHTRk1B!mcXLzk}H=^YgBIos{PG$5dS?YoPwDQG&aMaoclplL#oP z@2RHZE>^iM*G&vRDGkodWo@h`C|X0lgrI$H=gRv!t&I=OQovFO2c}&zexH9*nw%^B zV)FWU-_##C!2l=4DG)OtGfgVc+(1{P+-U^wjkzH>;arYd XyQG^`@&oi1CP5eYZ{4>JZ;Sr{=+M5Q diff --git a/dlc-sled-storage-provider/test_files/Signed b/dlc-sled-storage-provider/test_files/Signed index cdb74e399138ae0bdd7e0a4c88849ef996d9a9e8..04e9b3c49e967ffc75004e295b708ae25a169758 100644 GIT binary patch literal 9367 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 QXUg_gd7uvF^JTz)0n08%Gynhq literal 5873 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 ZkeH+4f)p4?%+YW`3JfIXFbx+*1^`)S=kWjl literal 3663 zcmb22o~owwbJ;v2RjEbS-Z6+gdfC1uI6yhvVsD+O%XTLwCW)5t#&3p4`emkbJ~EiZ zR?F60Z?KBzLWtLi_J0%aUQAomvfT8G(KCTX0_(jt=2_kRS8Ox?)_j%52cAKDHwpCf zF+c(H=83C!Eiw7>EXK!hYPljy)c1{MJnuJDWVi%o&Dd#`?-(jxs&C)-a(xiJ@V?mmQ zSYRvjN~PT^eu-RvHsR{?a;a65FInyM@%?mnbpz+sbG#GV7IHI)F^FX5MeHtc^i7lI zeR}Tm-v631PhDdcRV`TedtIv>NdA`1#CO6+B3^3!oE-Fu>*hSZ+bd=4H@2V4WHl0D zoV}i*bw(Ap%imIWlr z{rV6@a`}w|OvlUxRP_$UrpB~b%f*^ne$BXGZ@6RKoI9-HU#$ZSC1x_6`+R-(Q@ymP zUn~)l9v4e)H|dLt{;E5s+ioFK^ToWafT_7@g@EnsZL|LGy?1Jk&?VbrOStVO{(&G#N8w;j!eY>*SVS8bM{wY?iqt-&mL{lNVEo@LBIdyURf+=p#e`(zDZ(g&H9VCA}B3oBKHNv1# z&^BCT&)+obzlQ(Kr)S5>?$hpI{9y6JtSjeC>ZachZHmpJ=1P~`T_jq0#yaWO1~XOX z_3t3z1tx)kd8kQBe3|wMAPW(gtg&7V41c2oUu|KvkN&lLYt#7`Q_e3_`0UnkxHspB zwqElM>zs>BOnVJqd&?HfO>dcHAZwQLe9PUe*u`amDo^Sc=wA8m_m64zzpF3295P#* z|GA(3v6!W^^UR~Sp)B8YV*Xd&H{^8mIKMPI?O8^TZSS3{n^yA56u9k*eGs>$r0}M^ z@T*J3DM5#JCM{jJIQ6CA%lB{i_j{dwt-X&W`NrgH58Mo9PX4H#_L?w$j4gG8t!mBE6BF03oy#<7&b_Y3 z<`vdUY=URjOqI^RB-)k!JkEB5lmDieZH(D98O;oJ|w&s98>@P|NqZeq{7Mu;wdpG1Ql!98ef#m`L^=P znsmW6izW2`&(&T0+c93`qSn0mY@60|?@s`0WMfr!U|<011_F?E42(=fB@mE$qOv7O zA#;N&+-3XKeom~^5%~5zP4~!~9k)xfmc)e~OSvxp*=Bj=t@n?IaOD~)DINCUw^F;QPjdEwbx_SRtGcze0ah6^JD02a0Y9{>OV diff --git a/dlc-sled-storage-provider/test_files/SignedChannelSettled b/dlc-sled-storage-provider/test_files/SignedChannelSettled index 18566520bf56271160ebab6d2a8edd65ec8aceb4..092c6099dd33948eca4ad73e178426232c676986 100644 GIT binary patch literal 3465 zcmb19+xVAbr|0yg3e%R`JDqE{jc<4zV!^zxl^WJn7h=ibo<%l zj|@=2^l|r-7dtByym@(*%7V+AtlHMC%-g0a@u)cLg2Ih=Jtj=tx@zaovv0INn4t5j zbb8}lGvOTz9?v-I_tcTvTjhvJBw&U*7I0y=t=Ck-aKGJe>S(^CX@>ohQN|#vn3x!jDrz)6Gpb zznQIIRdky*cFzpy%&xE>H+b(^WsKv4+HZSBVFPjd`vp_nVfk9St(*85CMH4`x z+^-LTC0S#=8kjT81e!V~1V(nAb6$CaZz*e*^MsyaKQWH4xpQkc__lI0Z}9j3{UhS{ zzHdiYc(F!ZTp=J^dnNn+moIbkk2E!_^<8GFO#Hli*!0cnS0M7t&^Z5Q$ik{QhdSq6 z+OR4!3mje$0vMQE9%#+&i;H#u3BUrAVfl>%tV}=w#+)b1=6>^4Q@T5~)I@r|Zr4jc zN$phTV5iUP-|o>Vn&c1C#qb{pm~_2kSwS2n28H=MSAO_A$-GN2y}EDO^uDP|hxoee zxI#PD>Xj_jH2M20_vUhtN|4fuXupDbA#QZ1n#Zj zAZ1MN)-c_;X_GId^`M(`e!#Yg2mbZPtdY)s+avD8c;I*bdZvoPH6r^QI(L{R*4z(q zn6i(1X>z0byoD{EYQcvu+G$8~ET~(xd-a0UZ#zTYv96uQ(Np{B>S49XGekNziFdBP zBUO9oGV8aGZ$DouZrwOjYcKm`=NZS$pIXsM(zbC>7S?A1@TuUJ1? z^UQhfQ(b4fd7HN_uTEDEZDacQVEZ-4=aC#PYuXP8XH7a{x@GIWna5VWIkkUTj9q7% zA=AoPZ7sz@af}?Dl~wLfB`r7&M4$MqowLHBr+z)lyO4Lwin_Y*rHEe-nf&cwFu(Zo zV^d9YKc`K$o0!>gt}|#tk%(+6N8gS!P10f~5C6Pbx9oHB(>31;#bemrl9ps}e^d+O z&wg5bc}`=%ni=u`3to%)@t0UCzY(%XFS_>8^U#G{P&k5O6-0ru3MA70{{uxb6H&Pj z6v;%TERaIx231xzkX^tiw$1&QdD?(OyO&?H&M9nxWZbF36IxE6KdrS&+%K|NT$ACs zGdD;nj%4pp+6OiXD2=U%0qJCL4^kwUtrQ8CVnOQ|n2vi*i(C2o@a+{ne2jbkcQ0pA zHGZyr@yHAN4LfpNMfl#m`gTx9@O1R<7yBi`U))GNapdq}73(SQ(gJpM73Y76Iu8xk z|6pg~BN*{XBFT-aLkbKe=4iMe1qKpxG+dAZ1Bp2rE=Yla#2gJ5q`*L8j)n_TU?4Gv JShz4U008IRuMq$M literal 3465 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