use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};
#[allow(unused_imports)]
use crate::prelude::*;
use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode, NodeIdLookUp};
use crate::blinded_path::utils;
use crate::io;
use crate::io::Cursor;
use crate::ln::onion_utils;
use crate::onion_message::packet::ControlTlvs;
use crate::sign::{NodeSigner, Recipient};
use crate::crypto::streams::ChaChaPolyReadAdapter;
use crate::util::ser::{FixedLengthReader, LengthReadableArgs, Writeable, Writer};
use core::mem;
use core::ops::Deref;
pub(crate) struct ForwardTlvs {
pub(crate) next_hop: NextHop,
pub(crate) next_blinding_override: Option<PublicKey>,
}
pub(crate) struct ReceiveTlvs {
pub(crate) path_id: Option<[u8; 32]>,
}
#[derive(Debug)]
pub enum NextHop {
NodeId(PublicKey),
ShortChannelId(u64),
}
impl Writeable for ForwardTlvs {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
let (next_node_id, short_channel_id) = match self.next_hop {
NextHop::NodeId(pubkey) => (Some(pubkey), None),
NextHop::ShortChannelId(scid) => (None, Some(scid)),
};
encode_tlv_stream!(writer, {
(2, short_channel_id, option),
(4, next_node_id, option),
(8, self.next_blinding_override, option)
});
Ok(())
}
}
impl Writeable for ReceiveTlvs {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
encode_tlv_stream!(writer, {
(6, self.path_id, option),
});
Ok(())
}
}
pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
secp_ctx: &Secp256k1<T>, unblinded_path: &[PublicKey], session_priv: &SecretKey
) -> Result<Vec<BlindedHop>, secp256k1::Error> {
let blinded_tlvs = unblinded_path.iter()
.skip(1) .map(|pk| ForwardTlvs { next_hop: NextHop::NodeId(*pk), next_blinding_override: None })
.map(|tlvs| ControlTlvs::Forward(tlvs))
.chain(core::iter::once(ControlTlvs::Receive(ReceiveTlvs { path_id: None })));
utils::construct_blinded_hops(secp_ctx, unblinded_path.iter(), blinded_tlvs, session_priv)
}
pub(crate) fn advance_path_by_one<NS: Deref, NL: Deref, T>(
path: &mut BlindedPath, node_signer: &NS, node_id_lookup: &NL, secp_ctx: &Secp256k1<T>
) -> Result<(), ()>
where
NS::Target: NodeSigner,
NL::Target: NodeIdLookUp,
T: secp256k1::Signing + secp256k1::Verification,
{
let control_tlvs_ss = node_signer.ecdh(Recipient::Node, &path.blinding_point, None)?;
let rho = onion_utils::gen_rho_from_shared_secret(&control_tlvs_ss.secret_bytes());
let encrypted_control_tlvs = path.blinded_hops.remove(0).encrypted_payload;
let mut s = Cursor::new(&encrypted_control_tlvs);
let mut reader = FixedLengthReader::new(&mut s, encrypted_control_tlvs.len() as u64);
match ChaChaPolyReadAdapter::read(&mut reader, rho) {
Ok(ChaChaPolyReadAdapter {
readable: ControlTlvs::Forward(ForwardTlvs { next_hop, next_blinding_override })
}) => {
let next_node_id = match next_hop {
NextHop::NodeId(pubkey) => pubkey,
NextHop::ShortChannelId(scid) => match node_id_lookup.next_node_id(scid) {
Some(pubkey) => pubkey,
None => return Err(()),
},
};
let mut new_blinding_point = match next_blinding_override {
Some(blinding_point) => blinding_point,
None => {
onion_utils::next_hop_pubkey(secp_ctx, path.blinding_point,
control_tlvs_ss.as_ref()).map_err(|_| ())?
}
};
mem::swap(&mut path.blinding_point, &mut new_blinding_point);
path.introduction_node = IntroductionNode::NodeId(next_node_id);
Ok(())
},
_ => Err(())
}
}