#[cfg(test)]
mod tests {
use bitcoin;
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
use lightning::ln::chan_utils;
use test_log::test;
use crate::channel::{Channel, CommitmentType};
use crate::util::status::{Code, Status};
use crate::util::test_utils::key::*;
use crate::util::test_utils::*;
use crate::util::INITIAL_COMMITMENT_NUMBER;
const REV_COMMIT_NUM: u64 = 23;
fn make_per_commitment(idx: u64) -> (PublicKey, SecretKey) {
let commitment_seed = [55u8; 32];
let secp_ctx = Secp256k1::new();
let secret = SecretKey::from_slice(&chan_utils::build_commitment_secret(
&commitment_seed,
INITIAL_COMMITMENT_NUMBER - idx,
))
.unwrap();
let point = PublicKey::from_secret_key(&secp_ctx, &secret);
(point, secret)
}
fn validate_counterparty_revocation_with_mutator<RevocationMutator, ChannelStateValidator>(
mutate_revocation_input: RevocationMutator,
validate_channel_state: ChannelStateValidator,
) -> Result<(), Status>
where
RevocationMutator: Fn(&mut Channel, &mut SecretKey),
ChannelStateValidator: Fn(&Channel),
{
let (node, _setup, channel_id, offered_htlcs, received_htlcs) =
sign_commitment_tx_with_mutators_setup(CommitmentType::StaticRemoteKey);
node.with_channel(&channel_id, |chan| {
let channel_parameters = chan.make_channel_parameters();
let feerate_per_kw = 0;
let to_broadcaster = 1_979_997;
let to_countersignatory = 1_000_000;
for idx in 0..REV_COMMIT_NUM {
let (_, secret) = make_per_commitment(idx);
let secrets = chan.enforcement_state.counterparty_secrets.as_mut().unwrap();
secrets
.provide_secret(INITIAL_COMMITMENT_NUMBER - idx, secret.secret_bytes())
.unwrap();
}
let (remote_percommit_point, mut remote_percommit_secret) =
make_per_commitment(REV_COMMIT_NUM);
chan.enforcement_state.set_next_counterparty_revoke_num_for_testing(REV_COMMIT_NUM - 1);
chan.enforcement_state.set_next_counterparty_commit_num_for_testing(
REV_COMMIT_NUM,
remote_percommit_point,
);
for idx in 0..REV_COMMIT_NUM - 1 {
let (expected_point, _) = make_per_commitment(idx);
let point = chan.get_counterparty_commitment_point(idx);
assert_eq!(point, Some(expected_point), "idx={}", idx);
}
assert!(chan.get_counterparty_commitment_point(REV_COMMIT_NUM).is_none());
let parameters = channel_parameters.as_counterparty_broadcastable();
let keys = chan.make_counterparty_tx_keys(&remote_percommit_point);
let htlcs = Channel::htlcs_info2_to_oic(&offered_htlcs, &received_htlcs);
let redeem_scripts = build_tx_scripts(
&keys,
to_countersignatory,
to_broadcaster,
&htlcs,
¶meters,
&chan.keys.pubkeys(&chan.secp_ctx).funding_pubkey,
&chan.setup.counterparty_points.funding_pubkey,
)
.expect("scripts");
let output_witscripts: Vec<_> =
redeem_scripts.iter().map(|s| s.as_bytes().to_vec()).collect();
let commitment_tx = chan.make_counterparty_commitment_tx_with_keys(
&remote_percommit_point,
REV_COMMIT_NUM,
feerate_per_kw,
to_broadcaster,
to_countersignatory,
htlcs.clone(),
);
let trusted_tx = commitment_tx.trust();
let tx = trusted_tx.built_transaction().clone();
for received_htlc in received_htlcs.clone() {
node.add_keysend(
make_test_pubkey(1),
received_htlc.payment_hash,
received_htlc.value_sat * 1000,
)?;
}
let _sig = chan.sign_counterparty_commitment_tx(
&tx.transaction,
&output_witscripts,
&remote_percommit_point,
REV_COMMIT_NUM,
feerate_per_kw,
offered_htlcs.clone(),
received_htlcs.clone(),
)?;
chan.set_next_counterparty_revoke_num_for_testing(REV_COMMIT_NUM);
chan.set_next_counterparty_commit_num_for_testing(
REV_COMMIT_NUM + 2,
make_test_pubkey(0x10),
);
mutate_revocation_input(chan, &mut remote_percommit_secret);
let deferred_rv =
chan.validate_counterparty_revocation(REV_COMMIT_NUM, &remote_percommit_secret);
validate_channel_state(chan);
deferred_rv?;
assert_eq!(
tx.txid.to_string(),
"325f6443f45049da5a4d99cb54407ff8eaabfd8324bea18933a0ff40983275a3"
);
Ok(())
})
}
#[test]
fn validate_counterparty_revocation_success() {
assert!(validate_counterparty_revocation_with_mutator(
|_chan, _old_secret| {
},
|chan| {
assert_eq!(chan.enforcement_state.next_counterparty_revoke_num, REV_COMMIT_NUM + 1);
}
)
.is_ok());
}
#[test]
fn validate_counterparty_revocation_can_retry() {
assert!(validate_counterparty_revocation_with_mutator(
|chan, _old_secret| {
chan.enforcement_state
.set_next_counterparty_revoke_num_for_testing(REV_COMMIT_NUM + 1);
},
|chan| {
assert_eq!(chan.enforcement_state.next_counterparty_revoke_num, REV_COMMIT_NUM + 1);
}
)
.is_ok());
}
#[test]
fn validate_counterparty_revocation_not_ahead() {
assert_failed_precondition_err!(
validate_counterparty_revocation_with_mutator(
|chan, _old_secret| {
chan.enforcement_state
.set_next_counterparty_revoke_num_for_testing(REV_COMMIT_NUM + 2);
},
|chan| {
assert_eq!(
chan.enforcement_state.next_counterparty_revoke_num,
REV_COMMIT_NUM + 2
);
}
),
"policy failure: validate_counterparty_revocation: \
invalid counterparty revoke_num 23 with next_counterparty_revoke_num 25"
);
}
#[test]
fn validate_counterparty_revocation_not_behind() {
assert_failed_precondition_err!(
validate_counterparty_revocation_with_mutator(
|chan, _old_secret| {
chan.enforcement_state
.set_next_counterparty_revoke_num_for_testing(REV_COMMIT_NUM - 1);
},
|chan| {
assert_eq!(
chan.enforcement_state.next_counterparty_revoke_num,
REV_COMMIT_NUM - 1
);
}
),
"policy failure: validate_counterparty_revocation: \
invalid counterparty revoke_num 23 with next_counterparty_revoke_num 22"
);
}
#[test]
fn validate_counterparty_revocation_with_bad_secret() {
assert_failed_precondition_err!(
validate_counterparty_revocation_with_mutator(
|_chan, old_secret| {
*old_secret = make_test_privkey(42);
},
|chan| {
assert_eq!(chan.enforcement_state.next_counterparty_revoke_num, REV_COMMIT_NUM);
}
),
"policy failure: validate_counterparty_revocation: \
revocation commit point mismatch for commit_num 23: \
supplied 035be5e9478209674a96e60f1f037f6176540fd001fa1d64694770c56a7709c42c, \
previous 0393b4f3cd135b4f71fdb7ded8aad92cf22212a6893e89a2068a71e15a5ba560a3"
);
}
#[test]
fn validate_counterparty_revocation_with_retry() {
let (node, _setup, channel_id, offered_htlcs, received_htlcs) =
sign_commitment_tx_with_mutators_setup(CommitmentType::StaticRemoteKey);
assert_status_ok!(node.with_channel(&channel_id, |chan| {
for idx in 0..REV_COMMIT_NUM {
let (_, secret) = make_per_commitment(idx);
let secrets = chan.enforcement_state.counterparty_secrets.as_mut().unwrap();
secrets
.provide_secret(INITIAL_COMMITMENT_NUMBER - idx, secret.secret_bytes())
.unwrap();
}
chan.enforcement_state.set_next_counterparty_revoke_num_for_testing(REV_COMMIT_NUM - 1);
chan.enforcement_state.set_next_counterparty_commit_num_for_testing(
REV_COMMIT_NUM,
make_per_commitment(REV_COMMIT_NUM - 1).0,
);
Ok(())
}));
assert_status_ok!(node.with_channel(&channel_id, |chan| {
let channel_parameters = chan.make_channel_parameters();
let (remote_percommit_point, _) = make_per_commitment(REV_COMMIT_NUM);
let feerate_per_kw = 0;
let to_broadcaster = 1_979_997;
let to_countersignatory = 1_000_000;
let parameters = channel_parameters.as_counterparty_broadcastable();
let keys = chan.make_counterparty_tx_keys(&remote_percommit_point);
let htlcs = Channel::htlcs_info2_to_oic(&offered_htlcs, &received_htlcs);
let redeem_scripts = build_tx_scripts(
&keys,
to_countersignatory,
to_broadcaster,
&htlcs,
¶meters,
&chan.keys.pubkeys(&chan.secp_ctx).funding_pubkey,
&chan.setup.counterparty_points.funding_pubkey,
)
.expect("scripts");
let output_witscripts: Vec<_> =
redeem_scripts.iter().map(|s| s.as_bytes().to_vec()).collect();
let commitment_tx = chan.make_counterparty_commitment_tx_with_keys(
&remote_percommit_point,
REV_COMMIT_NUM,
feerate_per_kw,
to_broadcaster,
to_countersignatory,
htlcs.clone(),
);
let trusted_tx = commitment_tx.trust();
let tx = trusted_tx.built_transaction().clone();
for received_htlc in received_htlcs.clone() {
node.add_keysend(
make_test_pubkey(1),
received_htlc.payment_hash,
received_htlc.value_sat * 1000,
)?;
}
let _sig = chan.sign_counterparty_commitment_tx(
&tx.transaction,
&output_witscripts,
&remote_percommit_point,
REV_COMMIT_NUM,
feerate_per_kw,
offered_htlcs.clone(),
received_htlcs.clone(),
)?;
Ok(())
}));
assert_status_ok!(node.with_channel(&channel_id, |chan| {
assert_status_ok!(chan.validate_counterparty_revocation(
REV_COMMIT_NUM - 1,
&make_per_commitment(REV_COMMIT_NUM - 1).1,
));
Ok(())
}));
assert_status_ok!(node.with_channel(&channel_id, |chan| {
let channel_parameters = chan.make_channel_parameters();
let (remote_percommit_point, _) = make_per_commitment(REV_COMMIT_NUM + 1);
let feerate_per_kw = 0;
let to_broadcaster = 1_979_097; let to_countersignatory = 1_000_900;
let parameters = channel_parameters.as_counterparty_broadcastable();
let keys = chan.make_counterparty_tx_keys(&remote_percommit_point);
let htlcs = Channel::htlcs_info2_to_oic(&offered_htlcs, &received_htlcs);
let redeem_scripts = build_tx_scripts(
&keys,
to_countersignatory,
to_broadcaster,
&htlcs,
¶meters,
&chan.keys.pubkeys(&chan.secp_ctx).funding_pubkey,
&chan.setup.counterparty_points.funding_pubkey,
)
.expect("scripts");
let output_witscripts: Vec<_> =
redeem_scripts.iter().map(|s| s.as_bytes().to_vec()).collect();
let commitment_tx = chan.make_counterparty_commitment_tx_with_keys(
&remote_percommit_point,
REV_COMMIT_NUM + 1,
feerate_per_kw,
to_broadcaster,
to_countersignatory,
htlcs.clone(),
);
let trusted_tx = commitment_tx.trust();
let tx = trusted_tx.built_transaction().clone();
let _sig = chan.sign_counterparty_commitment_tx(
&tx.transaction,
&output_witscripts,
&remote_percommit_point,
REV_COMMIT_NUM + 1,
feerate_per_kw,
offered_htlcs.clone(),
received_htlcs.clone(),
)?;
Ok(())
}));
assert_status_ok!(node.with_channel(&channel_id, |chan| {
assert_eq!(chan.enforcement_state.next_counterparty_revoke_num, REV_COMMIT_NUM);
assert!(chan.enforcement_state.previous_counterparty_commit_info.is_some());
assert_failed_precondition_err!(
chan.validate_counterparty_revocation(
REV_COMMIT_NUM - 2,
&make_per_commitment(REV_COMMIT_NUM - 2).1
),
"policy failure: validate_counterparty_revocation: \
invalid counterparty revoke_num 21 with next_counterparty_revoke_num 23"
);
assert_eq!(chan.enforcement_state.next_counterparty_revoke_num, REV_COMMIT_NUM);
assert!(chan.enforcement_state.previous_counterparty_commit_info.is_some());
assert_failed_precondition_err!(
chan.validate_counterparty_revocation(
REV_COMMIT_NUM + 1,
&make_per_commitment(REV_COMMIT_NUM + 1).1
),
"policy failure: validate_counterparty_revocation: \
invalid counterparty revoke_num 24 with next_counterparty_revoke_num 23"
);
assert_eq!(chan.enforcement_state.next_counterparty_revoke_num, REV_COMMIT_NUM);
assert!(chan.enforcement_state.previous_counterparty_commit_info.is_some());
assert_status_ok!(chan.validate_counterparty_revocation(
REV_COMMIT_NUM,
&make_per_commitment(REV_COMMIT_NUM).1
));
assert_eq!(chan.enforcement_state.next_counterparty_revoke_num, REV_COMMIT_NUM + 1);
assert!(chan.enforcement_state.previous_counterparty_commit_info.is_none());
assert_status_ok!(chan.validate_counterparty_revocation(
REV_COMMIT_NUM,
&make_per_commitment(REV_COMMIT_NUM).1
));
assert_eq!(chan.enforcement_state.next_counterparty_revoke_num, REV_COMMIT_NUM + 1);
assert!(chan.enforcement_state.previous_counterparty_commit_info.is_none());
assert_failed_precondition_err!(
chan.validate_counterparty_revocation(
REV_COMMIT_NUM - 1,
&make_per_commitment(REV_COMMIT_NUM - 1).1
),
"policy failure: validate_counterparty_revocation: \
invalid counterparty revoke_num 22 with next_counterparty_revoke_num 24"
);
assert_eq!(chan.enforcement_state.next_counterparty_revoke_num, REV_COMMIT_NUM + 1);
assert!(chan.enforcement_state.previous_counterparty_commit_info.is_none());
assert_failed_precondition_err!(
chan.validate_counterparty_revocation(
REV_COMMIT_NUM + 2,
&make_per_commitment(REV_COMMIT_NUM + 2).1
),
"policy failure: validate_counterparty_revocation: \
invalid counterparty revoke_num 25 with next_counterparty_revoke_num 24"
);
assert_eq!(chan.enforcement_state.next_counterparty_revoke_num, REV_COMMIT_NUM + 1);
assert!(chan.enforcement_state.previous_counterparty_commit_info.is_none());
Ok(())
}))
}
}