Skip to content

Commit

Permalink
Merge pull request #335 from chenyukang/yukang-add-send-payment-dry-run
Browse files Browse the repository at this point in the history
Add dry_run option for send_payment
  • Loading branch information
quake authored Nov 23, 2024
2 parents 2160827 + abfd71f commit 07174b5
Show file tree
Hide file tree
Showing 8 changed files with 177 additions and 4 deletions.
13 changes: 13 additions & 0 deletions src/fiber/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,13 @@ impl SessionRoute {
.collect();
Self { nodes }
}

pub fn fee(&self) -> u128 {
let first_amount = self.nodes.first().map_or(0, |s| s.amount);
let last_amount = self.nodes.last().map_or(0, |s| s.amount);
assert!(first_amount >= last_amount);
first_amount - last_amount
}
}

#[serde_as]
Expand Down Expand Up @@ -978,16 +985,22 @@ impl PaymentSession {
pub fn can_retry(&self) -> bool {
self.retried_times < self.try_limit
}

pub fn fee(&self) -> u128 {
self.route.fee()
}
}

impl From<PaymentSession> for SendPaymentResponse {
fn from(session: PaymentSession) -> Self {
let fee = session.fee();
Self {
payment_hash: session.request.payment_hash,
status: session.status,
failed_error: session.last_error,
created_at: session.created_at,
last_updated_at: session.last_updated_at,
fee,
}
}
}
19 changes: 18 additions & 1 deletion src/fiber/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ pub struct SendPaymentResponse {
pub created_at: u64,
pub last_updated_at: u64,
pub failed_error: Option<String>,
pub fee: u128,
}

/// What kind of local information should be broadcasted to the network.
Expand Down Expand Up @@ -302,6 +303,8 @@ pub struct SendPaymentCommand {
pub udt_type_script: Option<Script>,
// allow self payment, default is false
pub allow_self_payment: bool,
// dry_run only used for checking, default is false
pub dry_run: bool,
}

#[serde_as]
Expand All @@ -320,6 +323,7 @@ pub struct SendPaymentData {
pub udt_type_script: Option<Script>,
pub preimage: Option<Hash256>,
pub allow_self_payment: bool,
pub dry_run: bool,
}

impl SendPaymentData {
Expand Down Expand Up @@ -423,6 +427,7 @@ impl SendPaymentData {
udt_type_script,
preimage,
allow_self_payment: command.allow_self_payment,
dry_run: command.dry_run,
})
}
}
Expand Down Expand Up @@ -2450,6 +2455,18 @@ where
Error::InvalidParameter(format!("Failed to validate payment request: {:?}", e))
})?;

// for dry run, we only build the route and return the hops info,
// will not store the payment session and send the onion packet
if payment_data.dry_run {
let mut payment_session = PaymentSession::new(payment_data.clone(), 0);
let hops = self
.build_payment_route(&mut payment_session, &payment_data)
.await?;
payment_session.route =
SessionRoute::new(state.get_public_key(), payment_data.target_pubkey, &hops);
return Ok(payment_session.into());
}

// initialize the payment session in db and begin the payment process lifecycle
if let Some(payment_session) = self.store.get_payment_session(payment_data.payment_hash) {
// we only allow retrying payment session with status failed
Expand All @@ -2462,7 +2479,7 @@ where
}
}

let payment_session = PaymentSession::new(payment_data.clone(), 5);
let payment_session = PaymentSession::new(payment_data, 5);
self.store.insert_payment_session(payment_session.clone());
let session = self.try_payment_session(state, payment_session).await?;
return Ok(session.into());
Expand Down
98 changes: 97 additions & 1 deletion src/fiber/tests/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,6 @@ async fn test_network_send_payment_normal_keysend_workflow() {
tokio::time::sleep(tokio::time::Duration::from_millis(5000)).await;

let node_b_pubkey = node_b.pubkey.clone();
// This payment request is without an invoice, the receiver will return an error `IncorrectOrUnknownPaymentDetails`
let message = |rpc_reply| -> NetworkActorMessage {
NetworkActorMessage::Command(NetworkActorCommand::SendPayment(
SendPaymentCommand {
Expand All @@ -323,6 +322,7 @@ async fn test_network_send_payment_normal_keysend_workflow() {
keysend: Some(true),
udt_type_script: None,
allow_self_payment: false,
dry_run: false,
},
rpc_reply,
))
Expand All @@ -344,6 +344,32 @@ async fn test_network_send_payment_normal_keysend_workflow() {
.unwrap();
assert_eq!(res.status, PaymentSessionStatus::Success);
assert_eq!(res.failed_error, None);

// we can make the same payment again, since payment_hash will be generated randomly
let message = |rpc_reply| -> NetworkActorMessage {
NetworkActorMessage::Command(NetworkActorCommand::SendPayment(
SendPaymentCommand {
target_pubkey: Some(node_b_pubkey),
amount: Some(10000),
payment_hash: None,
final_htlc_expiry_delta: None,
invoice: None,
timeout: None,
max_fee_amount: None,
max_parts: None,
keysend: Some(true),
udt_type_script: None,
allow_self_payment: false,
dry_run: false,
},
rpc_reply,
))
};
let res = call!(node_a.network_actor, message)
.expect("node_a alive")
.unwrap();
// this is the payment_hash generated by keysend
assert_eq!(res.status, PaymentSessionStatus::Inflight);
}

#[tokio::test]
Expand Down Expand Up @@ -378,6 +404,7 @@ async fn test_network_send_payment_keysend_with_payment_hash() {
keysend: Some(true),
udt_type_script: None,
allow_self_payment: false,
dry_run: false,
},
rpc_reply,
))
Expand Down Expand Up @@ -421,6 +448,7 @@ async fn test_network_send_payment_final_incorrect_hash() {
keysend: None,
udt_type_script: None,
allow_self_payment: false,
dry_run: false,
},
rpc_reply,
))
Expand Down Expand Up @@ -478,6 +506,7 @@ async fn test_network_send_payment_target_not_found() {
keysend: None,
udt_type_script: None,
allow_self_payment: false,
dry_run: false,
},
rpc_reply,
))
Expand Down Expand Up @@ -516,6 +545,7 @@ async fn test_network_send_payment_amount_is_too_large() {
keysend: None,
udt_type_script: None,
allow_self_payment: false,
dry_run: false,
},
rpc_reply,
))
Expand All @@ -528,6 +558,72 @@ async fn test_network_send_payment_amount_is_too_large() {
.contains("IncorrectOrUnknownPaymentDetails"));
}

// FIXME: this is the case send_payment with direct channels, we should handle this case
#[tokio::test]
async fn test_network_send_payment_with_dry_run() {
init_tracing();

let _span = tracing::info_span!("node", node = "test").entered();
let node_a_funding_amount = 100000000000;
let node_b_funding_amount = 6200000000;

let (node_a, node_b, _new_channel_id) =
create_nodes_with_established_channel(node_a_funding_amount, node_b_funding_amount, true)
.await;
// Wait for the channel announcement to be broadcasted
tokio::time::sleep(tokio::time::Duration::from_millis(5000)).await;

let node_b_pubkey = node_b.pubkey.clone();
let message = |rpc_reply| -> NetworkActorMessage {
NetworkActorMessage::Command(NetworkActorCommand::SendPayment(
SendPaymentCommand {
target_pubkey: Some(node_b_pubkey),
amount: Some(100000000000 + 5),
payment_hash: Some(gen_sha256_hash()),
final_htlc_expiry_delta: None,
invoice: None,
timeout: None,
max_fee_amount: None,
max_parts: None,
keysend: None,
udt_type_script: None,
allow_self_payment: false,
dry_run: true,
},
rpc_reply,
))
};
let res = call!(node_a.network_actor, message).expect("node_a alive");
assert!(res.is_ok());
let res = res.unwrap();
assert_eq!(res.status, PaymentSessionStatus::Created);
// since there are only sender and receiver in the router, fee will be 0
assert_eq!(res.fee, 0);

let message = |rpc_reply| -> NetworkActorMessage {
NetworkActorMessage::Command(NetworkActorCommand::SendPayment(
SendPaymentCommand {
target_pubkey: Some(gen_rand_public_key()),
amount: Some(1000 + 5),
payment_hash: Some(gen_sha256_hash()),
final_htlc_expiry_delta: None,
invoice: None,
timeout: None,
max_fee_amount: None,
max_parts: None,
keysend: None,
udt_type_script: None,
allow_self_payment: false,
dry_run: true,
},
rpc_reply,
))
};
let res = call!(node_a.network_actor, message).expect("node_a alive");
// since the target is not valid, the payment check will fail
assert!(res.is_err());
}

#[tokio::test]
async fn test_stash_broadcast_messages() {
init_tracing();
Expand Down
Loading

0 comments on commit 07174b5

Please sign in to comment.