Skip to content

Commit

Permalink
merge onion packet implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
chenyukang committed Sep 29, 2024
2 parents 602c2e0 + 832a403 commit c71e4d8
Show file tree
Hide file tree
Showing 7 changed files with 418 additions and 183 deletions.
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
socket2 = "0.5.7"
lnd-grpc-tonic-client = "0.3.0"
git-version = "0.3.9"
fiber-sphinx = "1.0.1"

[profile.release]
panic = "abort"
Expand Down
2 changes: 2 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ pub enum Error {
NetworkGraphError(#[from] GraphError),
#[error("Invalid peer message: {0}")]
InvalidPeerMessage(String),
#[error("Onion packet error: {0}")]
InvalidOnionPacket(crate::fiber::types::Error),
}

pub type Result<T> = std::result::Result<T, Error>;
95 changes: 55 additions & 40 deletions src/fiber/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use tracing::{debug, error, info, warn};
use crate::{
fiber::{
network::{get_chain_hash, SendOnionPacketCommand},
types::{ChannelUpdate, OnionPacket, RemoveTlcFail},
types::{ChannelUpdate, RemoveTlcFail},
},
invoice::InvoiceStore,
};
Expand All @@ -26,8 +26,8 @@ use musig2::{
PubNonce, SecNonce,
};
use ractor::{
async_trait as rasync_trait, Actor, ActorProcessingErr, ActorRef, OutputPort, RpcReplyPort,
SpawnErr,
async_trait as rasync_trait, call, Actor, ActorProcessingErr, ActorRef, OutputPort,
RpcReplyPort, SpawnErr,
};

use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -454,11 +454,11 @@ where
.handle_add_tlc_peer_message(state, add_tlc.clone())
.await
{
Ok((added_tlc_id, forward_to_next_hop)) => {
if forward_to_next_hop {
Ok((added_tlc_id, peeled_packet_bytes)) => {
if let Some(forward_packet_bytes) = peeled_packet_bytes {
self.handle_forward_onion_packet(
state,
add_tlc.onion_packet,
forward_packet_bytes,
added_tlc_id.into(),
)
.await?;
Expand Down Expand Up @@ -677,48 +677,62 @@ where
&self,
state: &mut ChannelActorState,
add_tlc: AddTlc,
) -> Result<(TLCId, bool), ProcessingChannelError> {
) -> Result<(TLCId, Option<Vec<u8>>), ProcessingChannelError> {
state.check_for_tlc_update(Some(add_tlc.amount))?;

// check the onion_packet is valid or not, check the payment hash and amount.
// check time lock is valid or not.
// check the onion_packet is valid or not, if not, we should return an error.
// If there is a next hop, we should send the AddTlc message to the next hop.
// If this is the last hop, try to fulfill the payment,
// find the corresponding payment preimage from payment hash, this is done in try_to_settle_down_tlc.
let mut forward_to_next_hop = false;
// If this is the last hop, we should check the payment hash and amount and then
// try to fulfill the payment, find the corresponding payment preimage from payment hash.
let mut preimage = None;
if let Ok(onion_packet) = OnionPacket::deserialize(&add_tlc.onion_packet) {
if let Some(onion_info) = onion_packet.peek() {
forward_to_next_hop = onion_info.next_hop.is_some();
let mut peeled_packet_bytes: Option<Vec<u8>> = None;
let mut forward_to_next_hop = false;

// check the payment hash and amount
if onion_info.payment_hash != add_tlc.payment_hash {
return Err(ProcessingChannelError::InvalidParameter(
"Payment hash or amount mismatch".to_string(),
));
}
if forward_to_next_hop && onion_info.amount > add_tlc.amount {
return Err(ProcessingChannelError::InvalidParameter(
format!("Payment amount is too large, expect next hop amount [{}] less than received amount [{}]",
onion_info.amount, add_tlc.amount)
));
}
if !forward_to_next_hop && onion_info.amount != add_tlc.amount {
return Err(ProcessingChannelError::InvalidParameter(
"Payment amount mismatch".to_string(),
));
}
if !add_tlc.onion_packet.is_empty() {
// TODO: Here we call network actor to peel the onion packet. Indeed, this message is forwarded from
// the network actor when it handles `FiberMessage::ChannelNormalOperation`. A better alternative is
// peeling the onion packet there before forwarding the message to the channel actor.
let peeled_packet = call!(self.network, |tx| NetworkActorMessage::Command(
NetworkActorCommand::PeelPaymentOnionPacket(
add_tlc.onion_packet.clone(),
add_tlc.payment_hash.clone(),
tx
)
))
.expect("call network")
.map_err(|err| ProcessingChannelError::PeelingOnionPacketError(err))?;

// TODO: check the expiry time, if expired, we should return an error.
// check the payment hash and amount
if peeled_packet.current.payment_hash != add_tlc.payment_hash {
return Err(ProcessingChannelError::InvalidParameter(
"Payment hash mismatch".to_string(),
));
}
// TODO: check the expiry time, if expired, we should return an error.

if peeled_packet.is_last() {
// if this is the last hop, store the preimage.
preimage = onion_info.preimage;
preimage = peeled_packet.current.preimage;
} else {
peeled_packet_bytes = Some(peeled_packet.serialize());
forward_to_next_hop = true;
}

if forward_to_next_hop && peeled_packet.current.amount > add_tlc.amount {
return Err(ProcessingChannelError::InvalidParameter(
format!("Payment amount is too large, expect next hop amount [{}] less than received amount [{}]",
peeled_packet.current.amount , add_tlc.amount)
));
}
if !forward_to_next_hop && peeled_packet.current.amount != add_tlc.amount {
return Err(ProcessingChannelError::InvalidParameter(
"Payment amount mismatch".to_string(),
));
}
}

let tlc = state.create_inbounding_tlc(add_tlc.clone(), preimage)?;
state.insert_tlc(tlc.clone())?;

if let Some(ref udt_type_script) = state.funding_udt_type_script {
self.subscribers
.pending_received_tlcs_subscribers
Expand All @@ -734,7 +748,7 @@ where
// The peer may falsely believe that we have already processed this message,
// while we have crashed. We need a way to make sure that the peer will resend
// this message, and our processing of this message is idempotent.
Ok((tlc.id, forward_to_next_hop))
Ok((tlc.id, peeled_packet_bytes))
}

async fn handle_forward_onion_packet(
Expand All @@ -747,7 +761,7 @@ where
let rpc_reply = RpcReplyPort::from(send);
self.network
.send_message(NetworkActorMessage::Command(
NetworkActorCommand::SendOnionPacket(
NetworkActorCommand::SendPaymentOnionPacket(
SendOnionPacketCommand {
packet: onion_packet,
previous_tlc: Some((state.get_id(), added_tlc_id)),
Expand All @@ -756,9 +770,8 @@ where
),
))
.expect("network actor is alive");
let res = match recv.await {
Ok(Ok(tlc_id)) => Ok(tlc_id),
Ok(Err(err)) => Err(err),
let res = match recv.await.expect("expect command replied") {
Ok(tlc_id) => Ok(tlc_id),
Err(e) => Err(e.to_string()),
};
// If we failed to forward the onion packet, we should remove the tlc.
Expand Down Expand Up @@ -2013,6 +2026,8 @@ pub enum ProcessingChannelError {
Musig2VerifyError(#[from] VerifyError),
#[error("Musig2 SigningError: {0}")]
Musig2SigningError(#[from] SigningError),
#[error("Failed to peel onion packet: {0}")]
PeelingOnionPacketError(String),
}

bitflags! {
Expand Down
11 changes: 6 additions & 5 deletions src/fiber/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use super::path::NodeHeap;
use super::types::Pubkey;
use super::types::{ChannelAnnouncement, ChannelUpdate, Hash256, NodeAnnouncement};
use crate::fiber::path::{NodeHeapElement, ProbabilityEvaluator};
use crate::fiber::types::OnionInfo;
use crate::fiber::types::PaymentHopData;
use crate::invoice::CkbInvoice;
use ckb_jsonrpc_types::JsonBytes;
use ckb_types::packed::{OutPoint, Script};
Expand Down Expand Up @@ -455,10 +455,11 @@ where
self.connected_peer_addresses.clear();
}

/// Returns a list of `PaymentHopData` for all nodes in the route, including the origin and the target node.
pub fn build_route(
&self,
payment_request: SendPaymentData,
) -> Result<Vec<OnionInfo>, GraphError> {
) -> Result<Vec<PaymentHopData>, GraphError> {
let source = self.get_source_pubkey();
let target = payment_request.target_pubkey;
let amount = payment_request.amount;
Expand Down Expand Up @@ -520,7 +521,7 @@ where

// make sure the final hop's amount is the same as the payment amount
// the last hop will check the amount from TLC and the amount from the onion packet
onion_infos.push(OnionInfo {
onion_infos.push(PaymentHopData {
amount: current_amount,
payment_hash,
next_hop,
Expand All @@ -532,13 +533,13 @@ where
current_amount += fee;
current_expiry += expiry;
}
// add the first hop so that the logic for send HTLC can be reused
// Add the first hop as the instruction for the current node, so the logic for send HTLC can be reused.
let next_hop = if !route.is_empty() {
Some(route[0].target)
} else {
None
};
onion_infos.push(OnionInfo {
onion_infos.push(PaymentHopData {
amount: current_amount,
payment_hash,
next_hop,
Expand Down
Loading

0 comments on commit c71e4d8

Please sign in to comment.