Skip to content

Commit

Permalink
Temp changes for open channel test
Browse files Browse the repository at this point in the history
  • Loading branch information
alecchendev committed Jun 10, 2024
1 parent 5590bc5 commit c336da6
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 1 deletion.
52 changes: 52 additions & 0 deletions lightning/src/ln/async_signer_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,58 @@ use crate::events::{Event, MessageSendEvent, MessageSendEventsProvider, ClosureR
use crate::ln::functional_test_utils::*;
use crate::ln::msgs::ChannelMessageHandler;
use crate::ln::channelmanager::{PaymentId, RecipientOnionFields};
use crate::util::test_channel_signer::{EnforcementState, ops};


#[cfg(feature = "std")]
#[test]
fn test_open_channel() {
// Simulate acquiring the signature for `funding_created` asynchronously.
let chanmon_cfgs = create_chanmon_cfgs(2);
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);

// Open an outbound channel simulating an async signer.
let channel_id_0 = EnforcementState::with_default_unavailable(
ops::GET_PER_COMMITMENT_POINT,
|| nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100000, 10001, 42, None, None)
).expect("Failed to create channel");

{
let msgs = nodes[0].node.get_and_clear_pending_msg_events();
assert!(msgs.is_empty(), "Expected no message events; got {:?}", msgs);
}

nodes[0].set_channel_signer_ops_available(&nodes[1].node.get_our_node_id(), &channel_id_0, ops::GET_PER_COMMITMENT_POINT, true);
nodes[0].node.signer_unblocked(None);

// nodes[0] --- open_channel --> nodes[1]
let mut open_chan_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());

// Handle an inbound channel simulating an async signer.
EnforcementState::with_default_unavailable(
ops::GET_PER_COMMITMENT_POINT,
|| nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), &open_chan_msg)
);

{
let msgs = nodes[1].node.get_and_clear_pending_msg_events();
assert!(msgs.is_empty(), "Expected no message events; got {:?}", msgs);
}

let channel_id_1 = {
let channels = nodes[1].node.list_channels();
assert_eq!(channels.len(), 1, "expected one channel, not {}", channels.len());
channels[0].channel_id
};

nodes[1].set_channel_signer_ops_available(&nodes[0].node.get_our_node_id(), &channel_id_1, ops::GET_PER_COMMITMENT_POINT, true);
nodes[1].node.signer_unblocked(None);

// nodes[0] <-- accept_channel --- nodes[1]
get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id());
}

#[test]
fn test_async_commitment_signature_for_funding_created() {
Expand Down
24 changes: 23 additions & 1 deletion lightning/src/ln/functional_test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use crate::util::errors::APIError;
#[cfg(test)]
use crate::util::logger::Logger;
use crate::util::scid_utils;
use crate::util::test_channel_signer::TestChannelSigner;
use crate::util::test_channel_signer::{TestChannelSigner, ops};
use crate::util::test_utils;
use crate::util::test_utils::{panicking, TestChainMonitor, TestScorer, TestKeysInterface};
use crate::util::ser::{ReadableArgs, Writeable};
Expand Down Expand Up @@ -523,6 +523,28 @@ impl<'a, 'b, 'c> Node<'a, 'b, 'c> {
.insert(channel_keys_id.unwrap());
}
}

/// Changes the channel signer's availability for the specified peer and channel.
///
/// When `available` is set to `true`, the channel signer will behave normally. When set to
/// `false`, the channel signer will act like an off-line remote signer and will return `Err` for
/// several of the signing methods. Currently, only `get_per_commitment_point` and
/// `release_commitment_secret` are affected by this setting.
/// several of the signing methods.
#[cfg(test)]
pub fn set_channel_signer_ops_available(&self, peer_id: &PublicKey, chan_id: &ChannelId, mask: u32, available: bool) {
let per_peer_state = self.node.per_peer_state.read().unwrap();
let chan_lock = per_peer_state.get(peer_id).unwrap().lock().unwrap();
let signer = (|| {
match chan_lock.channel_by_id.get(chan_id) {
Some(phase) => phase.context().get_signer(),
None => panic!("Couldn't find a channel with id {}", chan_id),
}
})();
log_debug!(self.logger, "Setting channel signer for {} as {}available for {} (mask={})",
chan_id, if available { "" } else { "un" }, ops::string_from(mask), mask);
signer.as_ecdsa().unwrap().set_ops_available(mask, available);
}
}

/// If we need an unsafe pointer to a `Node` (ie to reference it in a thread
Expand Down
98 changes: 98 additions & 0 deletions lightning/src/util/test_channel_signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::sign::ecdsa::EcdsaChannelSigner;
#[allow(unused_imports)]
use crate::prelude::*;

use core::cell::RefCell;
use core::cmp;
use crate::sync::{Mutex, Arc};
#[cfg(test)] use crate::sync::MutexGuard;
Expand Down Expand Up @@ -76,6 +77,51 @@ pub struct TestChannelSigner {
pub available: Arc<Mutex<bool>>,
}

/// Channel signer operations that can be individually enabled and disabled. If a particular value
/// is set in the `TestChannelSigner::unavailable` bitmask, then that operation will return an
/// error.
pub mod ops {
pub const GET_PER_COMMITMENT_POINT: u32 = 1 << 0;
pub const RELEASE_COMMITMENT_SECRET: u32 = 1 << 1;
pub const VALIDATE_HOLDER_COMMITMENT: u32 = 1 << 2;
pub const SIGN_COUNTERPARTY_COMMITMENT: u32 = 1 << 3;
pub const VALIDATE_COUNTERPARTY_REVOCATION: u32 = 1 << 4;
pub const SIGN_HOLDER_COMMITMENT_AND_HTLCS: u32 = 1 << 5;
pub const SIGN_JUSTICE_REVOKED_OUTPUT: u32 = 1 << 6;
pub const SIGN_JUSTICE_REVOKED_HTLC: u32 = 1 << 7;
pub const SIGN_HOLDER_HTLC_TRANSACTION: u32 = 1 << 8;
pub const SIGN_COUNTERPARTY_HTLC_TRANSATION: u32 = 1 << 9;
pub const SIGN_CLOSING_TRANSACTION: u32 = 1 << 10;
pub const SIGN_HOLDER_ANCHOR_INPUT: u32 = 1 << 11;
pub const SIGN_CHANNEL_ANNOUNCMENT_WITH_FUNDING_KEY: u32 = 1 << 12;

#[cfg(test)]
pub fn string_from(mask: u32) -> String {
if mask == 0 {
return "nothing".to_owned();
}
if mask == !(0 as u32) {
return "everything".to_owned();
}

vec![
if (mask & GET_PER_COMMITMENT_POINT) != 0 { Some("get_per_commitment_point") } else { None },
if (mask & RELEASE_COMMITMENT_SECRET) != 0 { Some("release_commitment_secret") } else { None },
if (mask & VALIDATE_HOLDER_COMMITMENT) != 0 { Some("validate_holder_commitment") } else { None },
if (mask & SIGN_COUNTERPARTY_COMMITMENT) != 0 { Some("sign_counterparty_commitment") } else { None },
if (mask & VALIDATE_COUNTERPARTY_REVOCATION) != 0 { Some("validate_counterparty_revocation") } else { None },
if (mask & SIGN_HOLDER_COMMITMENT_AND_HTLCS) != 0 { Some("sign_holder_commitment_and_htlcs") } else { None },
if (mask & SIGN_JUSTICE_REVOKED_OUTPUT) != 0 { Some("sign_justice_revoked_output") } else { None },
if (mask & SIGN_JUSTICE_REVOKED_HTLC) != 0 { Some("sign_justice_revoked_htlc") } else { None },
if (mask & SIGN_HOLDER_HTLC_TRANSACTION) != 0 { Some("sign_holder_htlc_transaction") } else { None },
if (mask & SIGN_COUNTERPARTY_HTLC_TRANSATION) != 0 { Some("sign_counterparty_htlc_transation") } else { None },
if (mask & SIGN_CLOSING_TRANSACTION) != 0 { Some("sign_closing_transaction") } else { None },
if (mask & SIGN_HOLDER_ANCHOR_INPUT) != 0 { Some("sign_holder_anchor_input") } else { None },
if (mask & SIGN_CHANNEL_ANNOUNCMENT_WITH_FUNDING_KEY) != 0 { Some("sign_channel_announcment_with_funding_key") } else { None },
].iter().flatten().map(|s| s.to_string()).collect::<Vec<_>>().join(", ")
}
}

impl PartialEq for TestChannelSigner {
fn eq(&self, o: &Self) -> bool {
Arc::ptr_eq(&self.state, &o.state)
Expand Down Expand Up @@ -123,12 +169,29 @@ impl TestChannelSigner {
pub fn set_available(&self, available: bool) {
*self.available.lock().unwrap() = available;
}

#[cfg(test)]
pub fn set_ops_available(&self, mask: u32, available: bool) {
let mut state = self.get_enforcement_state();
if available {
state.unavailable_signer_ops &= !mask; // clear the bits that are now available
} else {
state.unavailable_signer_ops |= mask; // set the bits that are now unavailable
}
}

fn is_signer_available(&self, ops_mask: u32) -> bool {
self.state.lock().unwrap().is_signer_available(ops_mask)
}
}

impl ChannelSigner for TestChannelSigner {
fn get_per_commitment_point(&self, idx: u64, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<PublicKey, ()> {
// TODO: implement a mask in EnforcementState to let you test signatures being
// unavailable
if !self.is_signer_available(ops::GET_PER_COMMITMENT_POINT) {
return Err(());
}
self.inner.get_per_commitment_point(idx, secp_ctx)
}

Expand Down Expand Up @@ -379,16 +442,51 @@ pub struct EnforcementState {
pub last_holder_revoked_commitment: u64,
/// The last validated holder commitment number, backwards counting
pub last_holder_commitment: u64,
/// A flag array that indicates which signing operations are currently *not* available in the
/// channel. When a method's bit is set, then the signer will act as if the signature is
/// unavailable and return an error result.
pub unavailable_signer_ops: u32,
}

impl EnforcementState {
#[cfg(feature = "std")]
thread_local! {
static DEFAULT_UNAVAILABLE_SIGNER_OPS: RefCell<u32> = RefCell::new(0);
}

/// Enforcement state for a new channel
pub fn new() -> Self {
EnforcementState {
last_counterparty_commitment: INITIAL_REVOKED_COMMITMENT_NUMBER,
last_counterparty_revoked_commitment: INITIAL_REVOKED_COMMITMENT_NUMBER,
last_holder_revoked_commitment: INITIAL_REVOKED_COMMITMENT_NUMBER,
last_holder_commitment: INITIAL_REVOKED_COMMITMENT_NUMBER,
unavailable_signer_ops: {
#[cfg(feature = "std")]
{
EnforcementState::DEFAULT_UNAVAILABLE_SIGNER_OPS.with(|ops| *ops.borrow())
}
#[cfg(not(feature = "std"))]
{
0
}
}
}
}

pub fn is_signer_available(&self, ops_mask: u32) -> bool {
(self.unavailable_signer_ops & ops_mask) == 0
}

#[cfg(feature = "std")]
pub fn with_default_unavailable<F, R>(ops: u32, f: F) -> R
where F: FnOnce() -> R
{
EnforcementState::DEFAULT_UNAVAILABLE_SIGNER_OPS.with(|unavailable_ops| {
unavailable_ops.replace(ops);
let res = f();
unavailable_ops.replace(0);
res
})
}
}

0 comments on commit c336da6

Please sign in to comment.