Skip to content

Commit 1fd69ee

Browse files
committed
Decrypt Trampoline onions
In this commit, we start decrypting the inner onion we receive, and lay some groundwork towards handling the subsequent routing or payment receipt.
1 parent 72ce1d2 commit 1fd69ee

File tree

5 files changed

+486
-38
lines changed

5 files changed

+486
-38
lines changed

lightning/src/ln/blinded_payment_tests.rs

+144
Original file line numberDiff line numberDiff line change
@@ -1814,3 +1814,147 @@ fn test_combined_trampoline_onion_creation_vectors() {
18141814
assert_eq!(htlc_msat, 150_156_000);
18151815
assert_eq!(htlc_cltv, 800_060);
18161816
}
1817+
1818+
#[test]
1819+
#[cfg(trampoline)]
1820+
fn test_trampoline_inbound_payment_decoding() {
1821+
let secp_ctx = Secp256k1::new();
1822+
let session_priv = secret_from_hex("0303030303030303030303030303030303030303030303030303030303030303");
1823+
1824+
let bob_secret = secret_from_hex("4242424242424242424242424242424242424242424242424242424242424242");
1825+
let bob_node_id = PublicKey::from_secret_key(&secp_ctx, &bob_secret);
1826+
let _bob_unblinded_tlvs = bytes_from_hex("011a0000000000000000000000000000000000000000000000000000020800000000000006c10a0800240000009627100c06000b69e505dc0e00fd023103123456");
1827+
let carol_secret = secret_from_hex("4343434343434343434343434343434343434343434343434343434343434343");
1828+
let carol_node_id = PublicKey::from_secret_key(&secp_ctx, &carol_secret);
1829+
let _carol_unblinded_tlvs = bytes_from_hex("020800000000000004510821031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f0a0800300000006401f40c06000b69c105dc0e00");
1830+
let dave_secret = secret_from_hex("4444444444444444444444444444444444444444444444444444444444444444");
1831+
let dave_node_id = PublicKey::from_secret_key(&secp_ctx, &dave_secret);
1832+
let _dave_unblinded_tlvs = bytes_from_hex("01230000000000000000000000000000000000000000000000000000000000000000000000020800000000000002310a060090000000fa0c06000b699105dc0e00");
1833+
let eve_secret = secret_from_hex("4545454545454545454545454545454545454545454545454545454545454545");
1834+
let _eve_node_id = PublicKey::from_secret_key(&secp_ctx, &eve_secret);
1835+
let _eve_unblinded_tlvs = bytes_from_hex("011a00000000000000000000000000000000000000000000000000000604deadbeef0c06000b690105dc0e0f020000000000000000000000000000fdffff0206c1");
1836+
1837+
let path = Path {
1838+
hops: vec![
1839+
// Bob
1840+
RouteHop {
1841+
pubkey: bob_node_id,
1842+
node_features: NodeFeatures::empty(),
1843+
short_channel_id: 0,
1844+
channel_features: ChannelFeatures::empty(),
1845+
fee_msat: 0,
1846+
cltv_expiry_delta: 0,
1847+
maybe_announced_channel: false,
1848+
},
1849+
1850+
// Carol
1851+
RouteHop {
1852+
pubkey: carol_node_id,
1853+
node_features: NodeFeatures::empty(),
1854+
short_channel_id: (572330 << 40) + (42 << 16) + 2821,
1855+
channel_features: ChannelFeatures::empty(),
1856+
fee_msat: 150_153_000,
1857+
cltv_expiry_delta: 0,
1858+
maybe_announced_channel: false,
1859+
},
1860+
],
1861+
blinded_tail: Some(BlindedTail {
1862+
trampoline_hops: vec![
1863+
// Carol's pubkey
1864+
TrampolineHop {
1865+
pubkey: carol_node_id,
1866+
node_features: Features::empty(),
1867+
fee_msat: 2_500,
1868+
cltv_expiry_delta: 24,
1869+
},
1870+
// Dave's pubkey (the intro node needs to be duplicated)
1871+
TrampolineHop {
1872+
pubkey: dave_node_id,
1873+
node_features: Features::empty(),
1874+
fee_msat: 150_500, // incorporate both base and proportional fee
1875+
cltv_expiry_delta: 36,
1876+
}
1877+
],
1878+
hops: vec![
1879+
// Dave's blinded node id
1880+
BlindedHop {
1881+
blinded_node_id: pubkey_from_hex("0295d40514096a8be54859e7dfe947b376eaafea8afe5cb4eb2c13ff857ed0b4be"),
1882+
encrypted_payload: bytes_from_hex("0ccf3c8a58deaa603f657ee2a5ed9d604eb5c8ca1e5f801989afa8f3ea6d789bbdde2c7e7a1ef9ca8c38d2c54760febad8446d3f273ddb537569ef56613846ccd3aba78a"),
1883+
},
1884+
// Eve's blinded node id
1885+
BlindedHop {
1886+
blinded_node_id: pubkey_from_hex("020e2dbadcc2005e859819ddebbe88a834ae8a6d2b049233c07335f15cd1dc5f22"),
1887+
encrypted_payload: bytes_from_hex("bcd747394fbd4d99588da075a623316e15a576df5bc785cccc7cd6ec7b398acce6faf520175f9ec920f2ef261cdb83dc28cc3a0eeb970107b3306489bf771ef5b1213bca811d345285405861d08a655b6c237fa247a8b4491beee20c878a60e9816492026d8feb9dafa84585b253978db6a0aa2945df5ef445c61e801fb82f43d5f00716baf9fc9b3de50bc22950a36bda8fc27bfb1242e5860c7e687438d4133e058770361a19b6c271a2a07788d34dccc27e39b9829b061a4d960eac4a2c2b0f4de506c24f9af3868c0aff6dda27281c"),
1888+
}
1889+
],
1890+
blinding_point: pubkey_from_hex("02988face71e92c345a068f740191fd8e53be14f0bb957ef730d3c5f76087b960e"),
1891+
excess_final_cltv_expiry_delta: 0,
1892+
final_value_msat: 150_000_000
1893+
})
1894+
};
1895+
1896+
let payment_secret = PaymentSecret(secret_from_hex("7494b65bc092b48a75465e43e29be807eb2cc535ce8aaba31012b8ff1ceac5da").secret_bytes());
1897+
1898+
let amt_msat = 150_000_001;
1899+
let cur_height = 800_001;
1900+
let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret);
1901+
let (bob_onion, _, _) = onion_utils::create_payment_onion(&secp_ctx, &path, &session_priv, amt_msat, &recipient_onion_fields, cur_height, &PaymentHash([0; 32]), &None, None, [0; 32]).unwrap();
1902+
1903+
struct TestEcdhSigner {
1904+
node_secret: SecretKey,
1905+
}
1906+
impl NodeSigner for TestEcdhSigner {
1907+
fn ecdh(
1908+
&self, _recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>,
1909+
) -> Result<SharedSecret, ()> {
1910+
let mut node_secret = self.node_secret.clone();
1911+
if let Some(tweak) = tweak {
1912+
node_secret = self.node_secret.mul_tweak(tweak).map_err(|_| ())?;
1913+
}
1914+
Ok(SharedSecret::new(other_key, &node_secret))
1915+
}
1916+
fn get_inbound_payment_key(&self) -> ExpandedKey { unreachable!() }
1917+
fn get_node_id(&self, _recipient: Recipient) -> Result<PublicKey, ()> { unreachable!() }
1918+
fn sign_invoice(
1919+
&self, _invoice: &RawBolt11Invoice, _recipient: Recipient,
1920+
) -> Result<RecoverableSignature, ()> { unreachable!() }
1921+
fn sign_bolt12_invoice(
1922+
&self, _invoice: &UnsignedBolt12Invoice,
1923+
) -> Result<schnorr::Signature, ()> { unreachable!() }
1924+
fn sign_gossip_message(&self, _msg: UnsignedGossipMessage) -> Result<Signature, ()> { unreachable!() }
1925+
}
1926+
let logger = test_utils::TestLogger::with_id("".to_owned());
1927+
1928+
let bob_update_add = update_add_msg(111_000, 747_501, None, bob_onion);
1929+
let bob_node_signer = TestEcdhSigner { node_secret: bob_secret };
1930+
1931+
let (bob_peeled_onion, next_packet_details_opt) = onion_payment::decode_incoming_update_add_htlc_onion(
1932+
&bob_update_add, &bob_node_signer, &logger, &secp_ctx
1933+
).unwrap_or_else(|_| panic!());
1934+
1935+
let (carol_packet_bytes, carol_hmac) = if let onion_utils::Hop::Forward {
1936+
next_hop_data: msgs::InboundOnionForwardPayload {..}, next_hop_hmac, new_packet_bytes, ..
1937+
} = bob_peeled_onion {
1938+
(new_packet_bytes, next_hop_hmac)
1939+
} else { panic!() };
1940+
1941+
let carol_packet_details = next_packet_details_opt.unwrap();
1942+
let carol_onion = msgs::OnionPacket {
1943+
version: 0,
1944+
public_key: carol_packet_details.next_packet_pubkey,
1945+
hop_data: carol_packet_bytes,
1946+
hmac: carol_hmac,
1947+
};
1948+
let carol_update_add = update_add_msg(carol_packet_details.outgoing_amt_msat, carol_packet_details.outgoing_cltv_value, None, carol_onion);
1949+
1950+
let carol_node_signer = TestEcdhSigner { node_secret: carol_secret };
1951+
let (carol_peeled_onion, _) = onion_payment::decode_incoming_update_add_htlc_onion(
1952+
&carol_update_add, &carol_node_signer, &logger, &secp_ctx
1953+
).unwrap_or_else(|_| panic!());
1954+
1955+
let _carol_trampoline_update_add = if let onion_utils::Hop::TrampolineForward { next_trampoline_hop_data, .. } = carol_peeled_onion {
1956+
assert_eq!(next_trampoline_hop_data.next_trampoline, dave_node_id);
1957+
} else {
1958+
panic!();
1959+
};
1960+
}

lightning/src/ln/channelmanager.rs

+27-4
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,6 @@ use crate::ln::channel_state::ChannelDetails;
5454
use crate::types::features::{Bolt12InvoiceFeatures, ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
5555
#[cfg(any(feature = "_test_utils", test))]
5656
use crate::types::features::Bolt11InvoiceFeatures;
57-
#[cfg(trampoline)]
58-
use crate::routing::gossip::NodeId;
5957
use crate::routing::router::{BlindedTail, InFlightHtlcs, Path, Payee, PaymentParameters, RouteParameters, RouteParametersConfig, Router, FixedRouter, Route};
6058
use crate::ln::onion_payment::{check_incoming_htlc_cltv, create_recv_pending_htlc_info, create_fwd_pending_htlc_info, decode_incoming_update_add_htlc_onion, HopConnector, InboundHTLCErr, NextPacketDetails};
6159
use crate::ln::msgs;
@@ -181,7 +179,7 @@ pub enum PendingHTLCRouting {
181179
/// do with the HTLC.
182180
onion_packet: msgs::TrampolineOnionPacket,
183181
/// The node ID of the Trampoline node which we need to route this HTLC to.
184-
node_id: NodeId,
182+
node_id: PublicKey,
185183
/// Set if this HTLC is being forwarded within a blinded path.
186184
blinded: Option<BlindedForward>,
187185
/// The absolute CLTV of the inbound HTLC
@@ -4492,11 +4490,36 @@ where
44924490
Err(InboundHTLCErr { err_code, err_data, msg }) => return_err!(msg, err_code, &err_data)
44934491
}
44944492
},
4493+
#[cfg(trampoline)]
4494+
onion_utils::Hop::TrampolineReceive { .. } | onion_utils::Hop::TrampolineBlindedReceive { .. } => {
4495+
// OUR PAYMENT!
4496+
let current_height: u32 = self.best_block.read().unwrap().height;
4497+
match create_recv_pending_htlc_info(decoded_hop, shared_secret, msg.payment_hash,
4498+
msg.amount_msat, msg.cltv_expiry, None, allow_underpay, msg.skimmed_fee_msat,
4499+
current_height)
4500+
{
4501+
Ok(info) => {
4502+
// Note that we could obviously respond immediately with an update_fulfill_htlc
4503+
// message, however that would leak that we are the recipient of this payment, so
4504+
// instead we stay symmetric with the forwarding case, only responding (after a
4505+
// delay) once they've sent us a commitment_signed!
4506+
PendingHTLCStatus::Forward(info)
4507+
},
4508+
Err(InboundHTLCErr { err_code, err_data, msg }) => return_err!(msg, err_code, &err_data)
4509+
}
4510+
},
44954511
onion_utils::Hop::Forward { .. } | onion_utils::Hop::BlindedForward { .. } => {
44964512
match create_fwd_pending_htlc_info(msg, decoded_hop, shared_secret, next_packet_pubkey_opt) {
44974513
Ok(info) => PendingHTLCStatus::Forward(info),
44984514
Err(InboundHTLCErr { err_code, err_data, msg }) => return_err!(msg, err_code, &err_data)
44994515
}
4516+
},
4517+
#[cfg(trampoline)]
4518+
onion_utils::Hop::TrampolineForward { .. } | onion_utils::Hop::TrampolineBlindedForward { .. } => {
4519+
match create_fwd_pending_htlc_info(msg, decoded_hop, shared_secret, next_packet_pubkey_opt) {
4520+
Ok(info) => PendingHTLCStatus::Forward(info),
4521+
Err(InboundHTLCErr { err_code, err_data, msg }) => return_err!(msg, err_code, &err_data)
4522+
}
45004523
}
45014524
}
45024525
}
@@ -5908,7 +5931,7 @@ where
59085931
// of the onion.
59095932
failed_payment!(err_msg, err_code, sha256_of_onion.to_vec(), None);
59105933
},
5911-
Err(onion_utils::OnionDecodeErr::Relay { err_msg, err_code, shared_secret }) => {
5934+
Err(onion_utils::OnionDecodeErr::Relay { err_msg, err_code, shared_secret, .. }) => {
59125935
let phantom_shared_secret = shared_secret.secret_bytes();
59135936
failed_payment!(err_msg, err_code, Vec::new(), Some(phantom_shared_secret));
59145937
},

lightning/src/ln/msgs.rs

-3
Original file line numberDiff line numberDiff line change
@@ -2127,7 +2127,6 @@ mod fuzzy_internal_msgs {
21272127
}
21282128

21292129
#[cfg(trampoline)]
2130-
#[cfg_attr(trampoline, allow(unused))]
21312130
pub struct InboundTrampolineForwardPayload {
21322131
pub next_trampoline: PublicKey,
21332132
/// The value, in msat, of the payment after this hop's fee is deducted.
@@ -2136,7 +2135,6 @@ mod fuzzy_internal_msgs {
21362135
}
21372136

21382137
#[cfg(trampoline)]
2139-
#[cfg_attr(trampoline, allow(unused))]
21402138
pub struct InboundTrampolineBlindedForwardPayload {
21412139
pub next_trampoline: PublicKey,
21422140
pub payment_relay: PaymentRelay,
@@ -2147,7 +2145,6 @@ mod fuzzy_internal_msgs {
21472145
}
21482146

21492147
#[cfg(trampoline)]
2150-
#[cfg_attr(trampoline, allow(unused))]
21512148
pub enum InboundTrampolinePayload {
21522149
Forward(InboundTrampolineForwardPayload),
21532150
BlindedForward(InboundTrampolineBlindedForwardPayload),

0 commit comments

Comments
 (0)