Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lightning/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ inventory = { version = "0.3", optional = true }
# RGB and related
amplify = "4.8"
bincode = "1.3"
serde_json = "1.0"
rgb-strict-encoding = "1.0.1"
futures = "0.3"
rgb-lib = { git = "https://github.com/UTEXO-Protocol/rgb-lib.git", tag = "v0.3.0-beta.50-bfa", default-features = false }
Expand Down
32 changes: 20 additions & 12 deletions lightning/src/chain/channelmonitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ use crate::types::features::ChannelTypeFeatures;
use crate::types::payment::{PaymentHash, PaymentPreimage};
use crate::util::byte_utils;
use crate::util::logger::{Logger, WithContext};
use crate::util::persist::MonitorName;
use crate::util::persist::{KVStoreSync, MonitorName};
use crate::util::ser::{
MaybeReadable, Readable, ReadableArgs, RequiredWrapper, UpgradableRequired, Writeable, Writer,
U48,
Expand All @@ -79,6 +79,7 @@ use crate::util::ser::{
use crate::prelude::*;

use crate::io::{self, Error};
use crate::sync::Arc;
use crate::sync::Mutex;
use core::ops::Deref;
use core::{cmp, mem};
Expand Down Expand Up @@ -1872,7 +1873,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
commitment_transaction_number_obscure_factor: u64,
initial_holder_commitment_tx: HolderCommitmentTransaction, best_block: BestBlock,
counterparty_node_id: PublicKey, channel_id: ChannelId,
is_manual_broadcast: bool,
is_manual_broadcast: bool, ldk_data_dir: Option<String>,
rgb_kv_store: Option<Arc<dyn KVStoreSync + Send + Sync>>,
) -> ChannelMonitor<Signer> {

assert!(commitment_transaction_number_obscure_factor <= (1 << 48));
Expand All @@ -1895,6 +1897,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
let onchain_tx_handler = OnchainTxHandler::new(
channel_parameters.channel_value_satoshis, channel_keys_id, destination_script.into(),
keys, channel_parameters.clone(), initial_holder_commitment_tx.clone(), secp_ctx,
ldk_data_dir, rgb_kv_store,
);

let funding_outpoint = channel_parameters.funding_outpoint
Expand Down Expand Up @@ -6419,10 +6422,14 @@ where

const MAX_ALLOC_SIZE: usize = 64 * 1024;

impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP)>
impl<'a, 'b, ES: EntropySource, SP: SignerProvider>
ReadableArgs<(&'a ES, &'b SP, Option<String>, Option<Arc<dyn KVStoreSync + Send + Sync>>)>
for (BlockHash, ChannelMonitor<SP::EcdsaSigner>)
{
fn read<R: io::Read>(reader: &mut R, args: (&'a ES, &'b SP)) -> Result<Self, DecodeError> {
fn read<R: io::Read>(
reader: &mut R,
args: (&'a ES, &'b SP, Option<String>, Option<Arc<dyn KVStoreSync + Send + Sync>>),
) -> Result<Self, DecodeError> {
match <Option<Self>>::read(reader, args) {
Ok(Some(res)) => Ok(res),
Ok(None) => Err(DecodeError::UnknownRequiredFeature),
Expand All @@ -6431,11 +6438,12 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
}
}

impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP)>
impl<'a, 'b, ES: EntropySource, SP: SignerProvider>
ReadableArgs<(&'a ES, &'b SP, Option<String>, Option<Arc<dyn KVStoreSync + Send + Sync>>)>
for Option<(BlockHash, ChannelMonitor<SP::EcdsaSigner>)>
{
#[rustfmt::skip]
fn read<R: io::Read>(reader: &mut R, args: (&'a ES, &'b SP)) -> Result<Self, DecodeError> {
fn read<R: io::Read>(reader: &mut R, args: (&'a ES, &'b SP, Option<String>, Option<Arc<dyn KVStoreSync + Send + Sync>>)) -> Result<Self, DecodeError> {
macro_rules! unwrap_obj {
($key: expr) => {
match $key {
Expand All @@ -6445,7 +6453,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
}
}

let (entropy_source, signer_provider) = args;
let (entropy_source, signer_provider, ldk_data_dir_arg, rgb_kv_store_arg) = args;

let _ver = read_ver_prefix!(reader, SERIALIZATION_VERSION);

Expand Down Expand Up @@ -6620,7 +6628,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
}
}
let onchain_tx_handler: OnchainTxHandler<SP::EcdsaSigner> = ReadableArgs::read(
reader, (entropy_source, signer_provider, channel_value_satoshis, channel_keys_id)
reader, (entropy_source, signer_provider, channel_value_satoshis, channel_keys_id, ldk_data_dir_arg, rgb_kv_store_arg)
)?;

let lockdown_from_offchain = Readable::read(reader)?;
Expand Down Expand Up @@ -6966,8 +6974,8 @@ mod tests {
&[(0, broadcast_tx)], conf_height);

let (_, pre_update_monitor) = <(BlockHash, ChannelMonitor<_>)>::read(
&mut io::Cursor::new(&get_monitor!(nodes[1], channel.2).encode()),
(&nodes[1].keys_manager.backing, &nodes[1].keys_manager.backing)).unwrap();
&mut io::Cursor::new(&get_monitor!(nodes[1], channel.2).encode()),
(&nodes[1].keys_manager.backing, &nodes[1].keys_manager.backing, None)).unwrap();

// If the ChannelManager tries to update the channel, however, the ChainMonitor will pass
// the update through to the ChannelMonitor which will refuse it (as the channel is closed).
Expand Down Expand Up @@ -7121,7 +7129,7 @@ mod tests {
let monitor = ChannelMonitor::new(
Secp256k1::new(), keys, Some(shutdown_script.into_inner()), 0, &ScriptBuf::new(),
&channel_parameters, true, 0, HolderCommitmentTransaction::dummy(0, funding_outpoint, Vec::new()),
best_block, dummy_key, channel_id, false,
best_block, dummy_key, channel_id, false, None, None,
);

let nondust_htlcs = preimages_slice_to_htlcs!(preimages[0..10]);
Expand Down Expand Up @@ -7382,7 +7390,7 @@ mod tests {
let monitor = ChannelMonitor::new(
Secp256k1::new(), keys, Some(shutdown_script.into_inner()), 0, &ScriptBuf::new(),
&channel_parameters, true, 0, HolderCommitmentTransaction::dummy(0, funding_outpoint, Vec::new()),
best_block, dummy_key, channel_id, false,
best_block, dummy_key, channel_id, false, None, None,
);

let chan_id = monitor.inner.lock().unwrap().channel_id();
Expand Down
42 changes: 35 additions & 7 deletions lightning/src/chain/onchaintx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ use crate::ln::chan_utils::{
};
use crate::ln::msgs::DecodeError;
use crate::sign::{ecdsa::EcdsaChannelSigner, EntropySource, HTLCDescriptor, SignerProvider};
use crate::sync::Arc;
use crate::util::logger::Logger;
use crate::util::persist::KVStoreSync;
use crate::util::ser::{
MaybeReadable, Readable, ReadableArgs, UpgradableRequired, Writeable, Writer,
};
Expand Down Expand Up @@ -277,6 +279,11 @@ pub struct OnchainTxHandler<ChannelSigner: EcdsaChannelSigner> {
onchain_events_awaiting_threshold_conf: Vec<OnchainEventEntry>,

pub(super) secp_ctx: Secp256k1<secp256k1::All>,

/// Runtime `ldk_data_dir` + RGB KVStore used by `color_claim` to reach RGB transfer
/// info. `None` means no RGB coloring should be attempted.
ldk_data_dir: Option<String>,
rgb_kv_store: Option<Arc<dyn KVStoreSync + Send + Sync>>,
}

impl<ChannelSigner: EcdsaChannelSigner> PartialEq for OnchainTxHandler<ChannelSigner> {
Expand Down Expand Up @@ -348,15 +355,20 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
}
}

impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP, u64, [u8; 32])>
for OnchainTxHandler<SP::EcdsaSigner>
impl<'a, 'b, ES: EntropySource, SP: SignerProvider>
ReadableArgs<(
&'a ES,
&'b SP,
u64,
[u8; 32],
Option<String>,
Option<Arc<dyn KVStoreSync + Send + Sync>>,
)> for OnchainTxHandler<SP::EcdsaSigner>
{
#[rustfmt::skip]
fn read<R: io::Read>(reader: &mut R, args: (&'a ES, &'b SP, u64, [u8; 32])) -> Result<Self, DecodeError> {
let entropy_source = args.0;
let signer_provider = args.1;
let channel_value_satoshis = args.2;
let channel_keys_id = args.3;
fn read<R: io::Read>(reader: &mut R, args: (&'a ES, &'b SP, u64, [u8; 32], Option<String>, Option<Arc<dyn KVStoreSync + Send + Sync>>)) -> Result<Self, DecodeError> {
let (entropy_source, signer_provider, channel_value_satoshis, channel_keys_id, ldk_data_dir_arg, rgb_kv_store_arg) =
args;

let _ver = read_ver_prefix!(reader, SERIALIZATION_VERSION);

Expand Down Expand Up @@ -438,6 +450,8 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
onchain_events_awaiting_threshold_conf,
pending_claim_events: Vec::new(),
secp_ctx,
ldk_data_dir: ldk_data_dir_arg,
rgb_kv_store: rgb_kv_store_arg,
})
}
}
Expand All @@ -447,6 +461,7 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
channel_value_satoshis: u64, channel_keys_id: [u8; 32], destination_script: ScriptBuf,
signer: ChannelSigner, channel_parameters: ChannelTransactionParameters,
holder_commitment: HolderCommitmentTransaction, secp_ctx: Secp256k1<secp256k1::All>,
ldk_data_dir: Option<String>, rgb_kv_store: Option<Arc<dyn KVStoreSync + Send + Sync>>,
) -> Self {
OnchainTxHandler {
channel_value_satoshis,
Expand All @@ -462,6 +477,8 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
onchain_events_awaiting_threshold_conf: Vec::new(),
pending_claim_events: Vec::new(),
secp_ctx,
ldk_data_dir,
rgb_kv_store,
}
}

Expand All @@ -479,6 +496,16 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
events
}

/// Returns the runtime `ldk_data_dir` used by `color_claim`.
pub(crate) fn ldk_data_dir(&self) -> Option<&str> {
self.ldk_data_dir.as_deref()
}

/// Returns the runtime RGB KVStore used by `color_claim`.
pub(crate) fn rgb_kv_store(&self) -> Option<&Arc<dyn KVStoreSync + Send + Sync>> {
self.rgb_kv_store.as_ref()
}

/// Triggers rebroadcasts/fee-bumps of pending claims from a force-closed channel. This is
/// crucial in preventing certain classes of pinning attacks, detecting substantial mempool
/// feerate changes between blocks, and ensuring reliability if broadcasting fails. We recommend
Expand Down Expand Up @@ -1372,6 +1399,7 @@ mod tests {
chan_params,
holder_commit,
secp_ctx,
None,
);

// Create a broadcaster with current block height 1.
Expand Down
48 changes: 45 additions & 3 deletions lightning/src/chain/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ use crate::ln::chan_utils::{
use crate::ln::channel_keys::{DelayedPaymentBasepoint, HtlcBasepoint};
use crate::ln::channelmanager::MIN_CLTV_EXPIRY_DELTA;
use crate::ln::msgs::DecodeError;
use crate::rgb_utils::color_claim;
use crate::sign::ecdsa::EcdsaChannelSigner;
use crate::sign::{ChannelDerivationParameters, HTLCDescriptor};
use crate::types::features::ChannelTypeFeatures;
Expand All @@ -47,6 +48,7 @@ use crate::util::ser::{Readable, ReadableArgs, RequiredWrapper, Writeable, Write
use crate::io;
use core::cmp;
use core::ops::Deref;
use std::path::Path;

#[allow(unused_imports)]
use crate::prelude::*;
Expand Down Expand Up @@ -116,6 +118,10 @@ pub(crate) fn verify_channel_type_features(channel_type_features: &Option<Channe
// number_of_witness_elements + sig_length + revocation_sig + true_length + op_true + witness_script_length + witness_script
pub(crate) const WEIGHT_REVOKED_OUTPUT: u64 = 1 + 1 + 73 + 1 + 1 + 1 + 77;

// Weight of the RGB OP_RETURN output appended by `color_claim` to justice transactions.
// value(8) + varint(1) + script(OP_RETURN(1) + OP_PUSHBYTES_32(1) + 32-byte opret commitment) = 43 bytes serialized × 4 (non-witness) = 172 WU.
const RGB_OPRETURN_OUTPUT_WEIGHT: u64 = 172;

#[cfg(not(any(test, feature = "_test_utils")))]
/// Height delay at which transactions are fee-bumped/rebroadcasted with a low priority.
const LOW_FREQUENCY_BUMP_INTERVAL: u32 = 15;
Expand Down Expand Up @@ -1355,6 +1361,18 @@ impl PackageTemplate {
core::cmp::max(current_height, minimum_locktime.unwrap_or(0))
}
}
/// Returns whether every input spends a revoked counterparty output (a justice/penalty
/// package claiming a revoked commitment transaction).
fn is_justice_package(&self) -> bool {
!self.inputs.is_empty()
&& self.inputs.iter().all(|(_, d)| {
matches!(
d,
PackageSolvingData::RevokedOutput(..)
| PackageSolvingData::RevokedHTLCOutput(..)
)
})
}
pub(crate) fn package_weight(&self, destination_script: &Script) -> u64 {
let mut inputs_weight = 0;
let mut witnesses_weight = 2; // count segwit flags
Expand All @@ -1366,7 +1384,12 @@ impl PackageTemplate {
// version: 4 bytes ; count_tx_in: 1 byte ; count_tx_out: 1 byte ; lock_time: 4 bytes
let transaction_weight = 10 * WITNESS_SCALE_FACTOR;
// value: 8 bytes ; var_int: 1 byte ; pk_script: `destination_script.len()`
let output_weight = (8 + 1 + destination_script.len()) * WITNESS_SCALE_FACTOR;
let mut output_weight = (8 + 1 + destination_script.len()) * WITNESS_SCALE_FACTOR;
// Justice (penalty) transactions get an RGB OP_RETURN output appended by `color_claim`.
// Account for its weight here so the feerate computation and downstream assert are accurate.
if self.is_justice_package() {
output_weight += RGB_OPRETURN_OUTPUT_WEIGHT as usize;
}
(inputs_weight + witnesses_weight + transaction_weight + output_weight) as u64
}
#[rustfmt::skip]
Expand Down Expand Up @@ -1409,6 +1432,21 @@ impl PackageTemplate {
for (outpoint, outp) in self.inputs.iter() {
bumped_tx.input.push(outp.as_tx_input(*outpoint));
}
// Only color justice (penalty) claims: all inputs must be revoked counterparty outputs,
// possibly spending more than one revoked transaction (e.g. a revoked commitment and
// HTLC transactions spending it). `color_claim` groups the inputs by spent transaction
// and loads the matching `{txid}_transfer_info` for each. Coloring needs to happen
// before `finalize_input` signs over all outputs so that the RGB OP_RETURN is part of
// what gets signed.
if self.is_justice_package() {
if let Some(ldk_data_dir) = onchain_handler.ldk_data_dir() {
if let Some(kv_store) = onchain_handler.rgb_kv_store() {
let _ = color_claim(
&mut bumped_tx, Path::new(ldk_data_dir), kv_store.as_ref(), logger,
);
}
}
}
for (i, (outpoint, out)) in self.inputs.iter().enumerate() {
log_debug!(logger, "Adding claiming input for outpoint {}:{}", outpoint.txid, outpoint.vout);
if !out.finalize_input(&mut bumped_tx, i, onchain_handler) { continue; }
Expand Down Expand Up @@ -1776,7 +1814,8 @@ mod tests {
use crate::chain::package::{
feerate_bump, weight_offered_htlc, weight_received_htlc, CounterpartyOfferedHTLCOutput,
CounterpartyReceivedHTLCOutput, HolderFundingOutput, HolderHTLCOutput, PackageSolvingData,
PackageTemplate, RevokedHTLCOutput, RevokedOutput, WEIGHT_REVOKED_OUTPUT,
PackageTemplate, RevokedHTLCOutput, RevokedOutput, RGB_OPRETURN_OUTPUT_WEIGHT,
WEIGHT_REVOKED_OUTPUT,
};
use crate::chain::Txid;
use crate::ln::chan_utils::{
Expand Down Expand Up @@ -2187,7 +2226,10 @@ mod tests {
{
let revk_outp = dumb_revk_output!();
let package = PackageTemplate::build_package(fake_txid(1), 0, revk_outp, 0);
assert_eq!(package.package_weight(&ScriptBuf::new()), weight_sans_output + WEIGHT_REVOKED_OUTPUT);
assert_eq!(
package.package_weight(&ScriptBuf::new()),
weight_sans_output + WEIGHT_REVOKED_OUTPUT + RGB_OPRETURN_OUTPUT_WEIGHT
);
}

{
Expand Down
4 changes: 3 additions & 1 deletion lightning/src/ln/chanmon_update_fail_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ fn test_monitor_and_persister_update_fail() {
let monitor = nodes[0].chain_monitor.chain_monitor.get_monitor(chan.2).unwrap();
let (_, new_monitor) = <(BlockHash, ChannelMonitor<TestChannelSigner>)>::read(
&mut &monitor.encode()[..],
(nodes[0].keys_manager, nodes[0].keys_manager),
(nodes[0].keys_manager, nodes[0].keys_manager, None, None),
)
.unwrap();
assert!(new_monitor == *monitor);
Expand Down Expand Up @@ -4919,6 +4919,8 @@ fn native_async_persist() {
Arc::clone(&keys_manager),
Arc::clone(&tx_broadcaster),
Arc::clone(&fee_estimator),
None,
None,
);
let chain_source = test_utils::TestChainSource::new(Network::Testnet);
let async_chain_monitor = ChainMonitor::new_async_beta(
Expand Down
Loading
Loading