From e8e2814b74f530c8df73e2cac608f3445b64b77e Mon Sep 17 00:00:00 2001 From: yukang Date: Fri, 20 Dec 2024 18:25:07 +0800 Subject: [PATCH 001/119] fix a bug in direct channel --- src/fiber/graph.rs | 9 ++- src/fiber/tests/channel.rs | 74 +++++++++---------- src/fiber/tests/payment.rs | 131 +++++++++++++++++++++++++++++++++- src/fiber/tests/test_utils.rs | 1 + 4 files changed, 172 insertions(+), 43 deletions(-) diff --git a/src/fiber/graph.rs b/src/fiber/graph.rs index b9d30de8b..68212826a 100644 --- a/src/fiber/graph.rs +++ b/src/fiber/graph.rs @@ -900,12 +900,17 @@ where } // if this is a direct channel, try to load the channel actor state for balance - if from == self.source { + if from == self.source || to == self.source { if let Some(state) = self .store .get_channel_state_by_outpoint(&channel_info.out_point()) { - if amount_to_send > state.to_local_amount { + let balance = if from == self.source { + state.to_local_amount + } else { + state.to_remote_amount + }; + if amount_to_send > balance { continue; } } diff --git a/src/fiber/tests/channel.rs b/src/fiber/tests/channel.rs index 858a59ab0..a051e98c0 100644 --- a/src/fiber/tests/channel.rs +++ b/src/fiber/tests/channel.rs @@ -4,11 +4,7 @@ use crate::fiber::channel::{ use crate::fiber::config::MAX_PAYMENT_TLC_EXPIRY_LIMIT; use crate::fiber::graph::PaymentSessionStatus; use crate::fiber::network::{DebugEvent, SendPaymentCommand}; -use crate::fiber::tests::test_utils::create_n_nodes_with_index_and_amounts_with_established_channel; -use crate::fiber::tests::test_utils::{ - create_3_nodes_with_established_channel, create_n_nodes_with_established_channel, - create_nodes_with_established_channel, NetworkNodeConfigBuilder, -}; +use crate::fiber::tests::test_utils::*; use crate::fiber::types::{ Hash256, PaymentHopData, PeeledOnionPacket, TlcErrorCode, NO_SHARED_SECRET, }; @@ -1134,8 +1130,8 @@ async fn test_network_send_payment_amount_is_too_large() { init_tracing(); let _span = tracing::info_span!("node", node = "test").entered(); - let node_a_funding_amount = 100000000000 + 4200000000; - let node_b_funding_amount = 4200000000 + 2; + let node_a_funding_amount = 100000000000 + MIN_RESERVED_CKB; + let node_b_funding_amount = MIN_RESERVED_CKB + 2; let (node_a, node_b, _new_channel_id) = create_nodes_with_established_channel(node_a_funding_amount, node_b_funding_amount, true) @@ -4189,8 +4185,8 @@ async fn test_send_payment_with_multiple_edges_in_middle_hops() { let (nodes, _channels) = create_n_nodes_with_index_and_amounts_with_established_channel( &[ ((0, 1), (100000000000, 100000000000)), - ((1, 2), (4200000000 + 900, 5200000000)), - ((1, 2), (4200000000 + 1000, 5200000000)), + ((1, 2), (MIN_RESERVED_CKB + 900, 5200000000)), + ((1, 2), (MIN_RESERVED_CKB + 1000, 5200000000)), ((2, 3), (100000000000, 100000000000)), ], 4, @@ -4256,8 +4252,8 @@ async fn test_send_payment_with_all_failed_middle_hops() { let (nodes, _channels) = create_n_nodes_with_index_and_amounts_with_established_channel( &[ ((0, 1), (100000000000, 100000000000)), - ((1, 2), (4200000000 + 900, 4200000000 + 1000)), - ((1, 2), (4200000000 + 910, 4200000000 + 1000)), + ((1, 2), (MIN_RESERVED_CKB + 900, MIN_RESERVED_CKB + 1000)), + ((1, 2), (MIN_RESERVED_CKB + 910, MIN_RESERVED_CKB + 1000)), ((2, 3), (100000000000, 100000000000)), ], 4, @@ -4323,8 +4319,8 @@ async fn test_send_payment_with_multiple_edges_can_succeed_in_retry() { let (nodes, _channels) = create_n_nodes_with_index_and_amounts_with_established_channel( &[ ((0, 1), (100000000000, 100000000000)), - ((1, 2), (4200000000 + 1000, 5200000000)), - ((1, 2), (4200000000 + 900, 6200000000)), + ((1, 2), (MIN_RESERVED_CKB + 1000, 5200000000)), + ((1, 2), (MIN_RESERVED_CKB + 900, 6200000000)), ((2, 3), (100000000000, 100000000000)), ], 4, @@ -4391,8 +4387,8 @@ async fn test_send_payment_with_final_hop_multiple_edges_in_middle_hops() { &[ ((0, 1), (100000000000, 100000000000)), ((1, 2), (100000000000, 100000000000)), - ((2, 3), (4200000000 + 900, 5200000000)), - ((2, 3), (4200000000 + 1000, 5200000000)), + ((2, 3), (MIN_RESERVED_CKB + 900, 5200000000)), + ((2, 3), (MIN_RESERVED_CKB + 1000, 5200000000)), ], 4, true, @@ -4458,8 +4454,8 @@ async fn test_send_payment_with_final_all_failed_middle_hops() { &[ ((0, 1), (100000000000, 100000000000)), ((1, 2), (100000000000, 100000000000)), - ((2, 3), (4200000000 + 900, 4200000000 + 1000)), - ((2, 3), (4200000000 + 910, 4200000000 + 1000)), + ((2, 3), (MIN_RESERVED_CKB + 900, MIN_RESERVED_CKB + 1000)), + ((2, 3), (MIN_RESERVED_CKB + 910, MIN_RESERVED_CKB + 1000)), ], 4, true, @@ -4516,8 +4512,8 @@ async fn test_send_payment_with_final_multiple_edges_can_succeed_in_retry() { &[ ((0, 1), (100000000000, 100000000000)), ((1, 2), (100000000000, 100000000000)), - ((2, 3), (4200000000 + 1000, 5200000000)), - ((2, 3), (4200000000 + 900, 6200000000)), + ((2, 3), (MIN_RESERVED_CKB + 1000, 5200000000)), + ((2, 3), (MIN_RESERVED_CKB + 900, 6200000000)), ], 4, true, @@ -4571,7 +4567,7 @@ async fn test_send_payment_with_first_hop_failed_with_fee() { // even 1000 > 999, but it's not enough for fee, and this is the direct channel // so we can check the actual balance of channel // the payment will fail - ((0, 1), (4200000000 + 1000, 5200000000)), + ((0, 1), (MIN_RESERVED_CKB + 1000, 5200000000)), ((1, 2), (100000000000, 100000000000)), ((2, 3), (100000000000, 100000000000)), ], @@ -4622,8 +4618,8 @@ async fn test_send_payment_succeed_with_multiple_edges_in_first_hop() { // the send payment should be succeed let (nodes, _channels) = create_n_nodes_with_index_and_amounts_with_established_channel( &[ - ((0, 1), (4200000000 + 900, 5200000000)), - ((0, 1), (4200000000 + 1001, 5200000000)), + ((0, 1), (MIN_RESERVED_CKB + 900, 5200000000)), + ((0, 1), (MIN_RESERVED_CKB + 1001, 5200000000)), ((1, 2), (100000000000, 100000000000)), ((2, 3), (100000000000, 100000000000)), ], @@ -4679,8 +4675,8 @@ async fn test_send_payment_with_first_hop_all_failed() { // path finding will fail in the first time of send payment let (nodes, _channels) = create_n_nodes_with_index_and_amounts_with_established_channel( &[ - ((0, 1), (4200000000 + 900, 4200000000 + 1000)), - ((0, 1), (4200000000 + 910, 4200000000 + 1000)), + ((0, 1), (MIN_RESERVED_CKB + 900, MIN_RESERVED_CKB + 1000)), + ((0, 1), (MIN_RESERVED_CKB + 910, MIN_RESERVED_CKB + 1000)), ((1, 2), (100000000000, 100000000000)), ((2, 3), (100000000000, 100000000000)), ], @@ -4733,8 +4729,8 @@ async fn test_send_payment_will_succeed_with_direct_channel_info_first_hop() { // so it will try the channel with smaller capacity and the payment will succeed let (nodes, channels) = create_n_nodes_with_index_and_amounts_with_established_channel( &[ - ((0, 1), (4200000000 + 2000, 4200000000 + 1000)), - ((0, 1), (4200000000 + 1005, 4200000000 + 1000)), + ((0, 1), (MIN_RESERVED_CKB + 2000, MIN_RESERVED_CKB + 1000)), + ((0, 1), (MIN_RESERVED_CKB + 1005, MIN_RESERVED_CKB + 1000)), ((1, 2), (100000000000, 100000000000)), ((2, 3), (100000000000, 100000000000)), ], @@ -4801,8 +4797,8 @@ async fn test_send_payment_will_succeed_with_retry_in_middle_hops() { &[ ((0, 1), (100000000000, 100000000000)), ((1, 2), (100000000000, 100000000000)), - ((2, 3), (4200000000 + 2000, 4200000000 + 1000)), - ((2, 3), (4200000000 + 1005, 4200000000 + 1000)), + ((2, 3), (MIN_RESERVED_CKB + 2000, MIN_RESERVED_CKB + 1000)), + ((2, 3), (MIN_RESERVED_CKB + 1005, MIN_RESERVED_CKB + 1000)), ], 4, true, @@ -4865,8 +4861,8 @@ async fn test_send_payment_will_fail_with_last_hop_info_in_add_tlc_peer() { &[ ((0, 1), (100000000000, 100000000000)), ((1, 2), (100000000000, 100000000000)), - ((2, 3), (4200000000 + 2000, 4200000000 + 1000)), - ((2, 3), (4200000000 + 1005, 4200000000 + 1000)), + ((2, 3), (MIN_RESERVED_CKB + 2000, MIN_RESERVED_CKB + 1000)), + ((2, 3), (MIN_RESERVED_CKB + 1005, MIN_RESERVED_CKB + 1000)), ], 4, true, @@ -4933,8 +4929,8 @@ async fn test_send_payment_will_fail_with_invoice_not_generated_by_target() { &[ ((0, 1), (100000000000, 100000000000)), ((1, 2), (100000000000, 100000000000)), - ((2, 3), (4200000000 + 2000, 4200000000 + 1000)), - ((2, 3), (4200000000 + 1005, 4200000000 + 1000)), + ((2, 3), (MIN_RESERVED_CKB + 2000, MIN_RESERVED_CKB + 1000)), + ((2, 3), (MIN_RESERVED_CKB + 1005, MIN_RESERVED_CKB + 1000)), ], 4, true, @@ -4994,8 +4990,8 @@ async fn test_send_payment_will_succeed_with_valid_invoice() { &[ ((0, 1), (100000000000, 100000000000)), ((1, 2), (100000000000, 100000000000)), - ((2, 3), (4200000000 + 2000, 4200000000 + 1000)), - ((2, 3), (4200000000 + 1005, 4200000000 + 1000)), + ((2, 3), (MIN_RESERVED_CKB + 2000, MIN_RESERVED_CKB + 1000)), + ((2, 3), (MIN_RESERVED_CKB + 1005, MIN_RESERVED_CKB + 1000)), ], 4, true, @@ -5064,8 +5060,8 @@ async fn test_send_payment_will_fail_with_no_invoice_preimage() { &[ ((0, 1), (100000000000, 100000000000)), ((1, 2), (100000000000, 100000000000)), - ((2, 3), (4200000000 + 2000, 4200000000 + 1000)), - ((2, 3), (4200000000 + 1005, 4200000000 + 1000)), + ((2, 3), (MIN_RESERVED_CKB + 2000, MIN_RESERVED_CKB + 1000)), + ((2, 3), (MIN_RESERVED_CKB + 1005, MIN_RESERVED_CKB + 1000)), ], 4, true, @@ -5136,8 +5132,8 @@ async fn test_send_payment_will_fail_with_cancelled_invoice() { &[ ((0, 1), (100000000000, 100000000000)), ((1, 2), (100000000000, 100000000000)), - ((2, 3), (4200000000 + 2000, 4200000000 + 1000)), - ((2, 3), (4200000000 + 1005, 4200000000 + 1000)), + ((2, 3), (MIN_RESERVED_CKB + 2000, MIN_RESERVED_CKB + 1000)), + ((2, 3), (MIN_RESERVED_CKB + 1005, MIN_RESERVED_CKB + 1000)), ], 4, true, @@ -5207,7 +5203,7 @@ async fn test_send_payment_will_succeed_with_large_tlc_expiry_limit() { let (nodes, _channels) = create_n_nodes_with_index_and_amounts_with_established_channel( &[ - ((0, 1), (4200000000 + 2000, 4200000000 + 1000)), + ((0, 1), (MIN_RESERVED_CKB + 2000, MIN_RESERVED_CKB + 1000)), ((1, 2), (100000000000, 100000000000)), ((2, 3), (100000000000, 100000000000)), ], diff --git a/src/fiber/tests/payment.rs b/src/fiber/tests/payment.rs index 692c0fba8..c8270ffe7 100644 --- a/src/fiber/tests/payment.rs +++ b/src/fiber/tests/payment.rs @@ -11,8 +11,8 @@ async fn test_send_payment_for_direct_channel_and_dry_run() { let (nodes, channels) = create_n_nodes_with_index_and_amounts_with_established_channel( &[ - ((0, 1), (4200000000 + 10000000000, 4200000000)), - ((0, 1), (4200000000, 4200000000 + 10000000000)), + ((0, 1), (MIN_RESERVED_CKB + 10000000000, MIN_RESERVED_CKB)), + ((0, 1), (MIN_RESERVED_CKB, MIN_RESERVED_CKB + 10000000000)), ], 2, true, @@ -115,3 +115,130 @@ async fn test_send_payment_for_direct_channel_and_dry_run() { assert_eq!(node_0_balance, 10000000000); assert_eq!(node_1_balance, 0); } + +#[tokio::test] +async fn test_send_payment_for_pay_self() { + init_tracing(); + let _span = tracing::info_span!("node", node = "test").entered(); + // from https://github.com/nervosnetwork/fiber/issues/362 + + let (nodes, channels) = create_n_nodes_with_index_and_amounts_with_established_channel( + &[ + ((0, 1), (MIN_RESERVED_CKB + 10000000000, MIN_RESERVED_CKB)), + ((1, 2), (MIN_RESERVED_CKB + 10000000000, MIN_RESERVED_CKB)), + ((2, 0), (MIN_RESERVED_CKB + 10000000000, MIN_RESERVED_CKB)), + ], + 3, + true, + ) + .await; + let [mut node_0, node_1, node_2] = nodes.try_into().expect("3 nodes"); + + let node_1_channel0_balance = node_1.get_local_balance_from_channel(channels[0]); + let node_1_channel1_balance = node_1.get_local_balance_from_channel(channels[1]); + let node_2_channel1_balance = node_2.get_local_balance_from_channel(channels[1]); + let node_2_channel2_balance = node_2.get_local_balance_from_channel(channels[2]); + + // sleep for a while + tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + + // now node_0 -> node_2 will be ok only with node_1, so the fee is larger than 0 + let res = node_0 + .send_payment(SendPaymentCommand { + target_pubkey: Some(node_2.pubkey.clone()), + amount: Some(60000000), + payment_hash: None, + final_tlc_expiry_delta: None, + tlc_expiry_limit: 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: true, + }) + .await; + + assert!(res.unwrap().fee > 0); + + // node_0 -> node_0 will be ok for dry_run if `allow_self_payment` is true + let res = node_0 + .send_payment(SendPaymentCommand { + target_pubkey: Some(node_0.pubkey.clone()), + amount: Some(60000000), + payment_hash: None, + final_tlc_expiry_delta: None, + tlc_expiry_limit: None, + invoice: None, + timeout: None, + max_fee_amount: None, + max_parts: None, + keysend: Some(true), + udt_type_script: None, + allow_self_payment: true, + dry_run: false, + }) + .await; + + eprintln!("res: {:?}", res); + assert!(res.is_ok()); + + // sleep for a while + tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + let res = res.unwrap(); + let payment_hash = res.payment_hash; + node_0 + .assert_payment_status(payment_hash, PaymentSessionStatus::Success, Some(1)) + .await; + + let node_0_balance1 = node_0.get_local_balance_from_channel(channels[0]); + let node_0_balance2 = node_0.get_local_balance_from_channel(channels[2]); + + assert_eq!(node_0_balance1, 10000000000 - 60000000 - res.fee); + assert_eq!(node_0_balance2, 60000000); + + eprintln!( + "node1 left: {:?}, right: {:?}", + node_1.get_local_balance_from_channel(channels[0]), + node_1.get_local_balance_from_channel(channels[1]) + ); + + let node_1_new_channel0_balance = node_1.get_local_balance_from_channel(channels[0]); + let node_1_new_channel1_balance = node_1.get_local_balance_from_channel(channels[1]); + let node_2_new_channel1_balance = node_2.get_local_balance_from_channel(channels[1]); + let node_2_new_channel2_balance = node_2.get_local_balance_from_channel(channels[2]); + + let node1_fee = (node_1_new_channel0_balance - node_1_channel0_balance) + - (node_1_channel1_balance - node_1_new_channel1_balance); + assert!(node1_fee > 0); + + let node2_fee = (node_2_new_channel1_balance - node_2_channel1_balance) + - (node_2_channel2_balance - node_2_new_channel2_balance); + assert!(node2_fee > 0); + assert_eq!(node1_fee + node2_fee, res.fee); + + // node_0 -> node_2 will be ok with direct channel2, + // since after payself this channel now have enough balance, so the fee is 0 + let res = node_0 + .send_payment(SendPaymentCommand { + target_pubkey: Some(node_2.pubkey.clone()), + amount: Some(60000000), + payment_hash: None, + final_tlc_expiry_delta: None, + tlc_expiry_limit: 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: true, + }) + .await; + + eprintln!("res: {:?}", res); + assert_eq!(res.unwrap().fee, 0); +} diff --git a/src/fiber/tests/test_utils.rs b/src/fiber/tests/test_utils.rs index e359deb1f..e18cec75d 100644 --- a/src/fiber/tests/test_utils.rs +++ b/src/fiber/tests/test_utils.rs @@ -56,6 +56,7 @@ use crate::{ }; static RETAIN_VAR: &str = "TEST_TEMP_RETAIN"; +pub(crate) const MIN_RESERVED_CKB: u128 = 4200000000; #[derive(Debug)] pub struct TempDir(ManuallyDrop); From 72db0eed270cdc1e7f4a3c8286143fc78504b9df Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 21 Dec 2024 10:08:59 +0800 Subject: [PATCH 002/119] Network graph should sync up when channel closed --- src/fiber/graph.rs | 8 +++- src/fiber/network.rs | 12 ++--- src/fiber/tests/channel.rs | 84 +++++++++++++++++++++++++++++++++++ src/fiber/tests/test_utils.rs | 14 ++++++ 4 files changed, 110 insertions(+), 8 deletions(-) diff --git a/src/fiber/graph.rs b/src/fiber/graph.rs index b9d30de8b..a501c4e96 100644 --- a/src/fiber/graph.rs +++ b/src/fiber/graph.rs @@ -523,7 +523,11 @@ where ) => { let mut channel_info = ChannelInfo::from((timestamp, channel_announcement)); self.load_channel_updates_from_store(&mut channel_info); - Some(channel_info) + if channel_info.is_explicitly_disabled() { + None + } else { + Some(channel_info) + } } _ => None, }) @@ -550,7 +554,7 @@ where &self, node_id: Pubkey, ) -> impl Iterator { - let mut channels: Vec<(Pubkey, Pubkey, &ChannelInfo, &ChannelUpdateInfo)> = self + let mut channels: Vec<_> = self .channels .values() .filter_map(move |channel| { diff --git a/src/fiber/network.rs b/src/fiber/network.rs index c4a037a39..0972b6b6b 100644 --- a/src/fiber/network.rs +++ b/src/fiber/network.rs @@ -2566,18 +2566,18 @@ where channel_id: &Hash256, tx_hash: Byte32, ) { - self.remove_channel(channel_id); - if let Some(session) = self.get_peer_session(peer_id) { - if let Some(set) = self.session_channels_map.get_mut(&session) { - set.remove(channel_id); - } - } self.send_message_to_channel_actor( *channel_id, None, ChannelActorMessage::Event(ChannelEvent::ClosingTransactionConfirmed), ) .await; + self.remove_channel(channel_id); + if let Some(session) = self.get_peer_session(peer_id) { + if let Some(set) = self.session_channels_map.get_mut(&session) { + set.remove(channel_id); + } + } // Notify outside observers. self.network .send_message(NetworkActorMessage::new_notification( diff --git a/src/fiber/tests/channel.rs b/src/fiber/tests/channel.rs index 858a59ab0..81b7f8478 100644 --- a/src/fiber/tests/channel.rs +++ b/src/fiber/tests/channel.rs @@ -4023,6 +4023,90 @@ async fn test_shutdown_channel_with_different_size_shutdown_script() { ); } +#[tokio::test] +async fn test_shutdown_channel_network_graph_will_not_sync_private_channel() { + let node_a_funding_amount = 100000000000; + let node_b_funding_amount = 6200000000; + + let (node_a, node_b, _channel_id) = + create_nodes_with_established_channel(node_a_funding_amount, node_b_funding_amount, false) + .await; + + // sleep for 1 second + tokio::time::sleep(tokio::time::Duration::from_millis(2000)).await; + + let network_nodes = node_a.get_network_nodes().await; + assert_eq!(network_nodes.len(), 2); + + let network_nodes = node_b.get_network_nodes().await; + assert_eq!(network_nodes.len(), 2); + + let network_channels = node_a.get_network_channels().await; + assert_eq!(network_channels.len(), 0); + + let network_channels = node_b.get_network_channels().await; + assert_eq!(network_channels.len(), 0); +} + +#[tokio::test] +async fn test_shutdown_channel_network_graph_with_sync_up() { + let node_a_funding_amount = 100000000000; + let node_b_funding_amount = 6200000000; + + let (node_a, node_b, channel_id) = + create_nodes_with_established_channel(node_a_funding_amount, node_b_funding_amount, true) + .await; + + // sleep for 1 second + tokio::time::sleep(tokio::time::Duration::from_millis(2000)).await; + + let network_nodes = node_a.get_network_nodes().await; + assert_eq!(network_nodes.len(), 2); + + let network_nodes = node_b.get_network_nodes().await; + assert_eq!(network_nodes.len(), 2); + + let network_channels = node_a.get_network_channels().await; + assert_eq!(network_channels.len(), 1); + + let network_channels = node_b.get_network_channels().await; + assert_eq!(network_channels.len(), 1); + + let message = |rpc_reply| { + NetworkActorMessage::Command(NetworkActorCommand::ControlFiberChannel( + ChannelCommandWithId { + channel_id: channel_id, + command: ChannelCommand::Shutdown( + ShutdownCommand { + close_script: Script::new_builder().args(vec![0u8; 19].pack()).build(), + fee_rate: FeeRate::from_u64(DEFAULT_COMMITMENT_FEE_RATE), + force: false, + }, + rpc_reply, + ), + }, + )) + }; + + call!(node_b.network_actor, message) + .expect("node_b alive") + .expect("successfully shutdown channel"); + + tokio::time::sleep(tokio::time::Duration::from_millis(2000)).await; + + let network_nodes = node_a.get_network_nodes().await; + assert_eq!(network_nodes.len(), 2); + + let network_nodes = node_b.get_network_nodes().await; + assert_eq!(network_nodes.len(), 2); + + let network_channels = node_a.get_network_channels().await; + assert_eq!(network_channels.len(), 0); + + let network_channels = node_b.get_network_channels().await; + assert_eq!(network_channels.len(), 0); +} + #[tokio::test] async fn test_send_payment_with_channel_balance_error() { init_tracing(); diff --git a/src/fiber/tests/test_utils.rs b/src/fiber/tests/test_utils.rs index e359deb1f..b6ef83ea0 100644 --- a/src/fiber/tests/test_utils.rs +++ b/src/fiber/tests/test_utils.rs @@ -714,6 +714,20 @@ impl NetworkNode { } } + pub async fn get_network_channels(&self) -> Vec { + self.network_graph + .read() + .await + .get_channels_with_params(1000, None) + } + + pub async fn get_network_nodes(&self) -> Vec { + self.network_graph + .read() + .await + .get_nodes_with_params(1000, None) + } + pub async fn start(&mut self) { let config = self.get_node_config(); let new = Self::new_with_config(config).await; From 4f8fed77ebdafac3a6b67759a786c9aca4c10aa6 Mon Sep 17 00:00:00 2001 From: quake Date: Sat, 21 Dec 2024 16:09:46 +0900 Subject: [PATCH 003/119] chore: revert #401 --- .github/workflows/e2e.yml | 2 +- tests/nodes/start.sh | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index effee321b..f9d55b490 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -63,7 +63,7 @@ jobs: fi # Prebuild the program so that we can run the following script faster - export FNN="$(cargo build --message-format=json-render-diagnostics | jq -js '[.[] | select(.reason == "compiler-artifact") | select(.executable != null)] | last | .executable')" + cargo build cd tests/deploy/udt-init && cargo build && cd - if [ ${{ matrix.workflow }} = "router-pay" ]; then export START_BOOTNODE=y diff --git a/tests/nodes/start.sh b/tests/nodes/start.sh index c79b82e8a..f41ceaa28 100755 --- a/tests/nodes/start.sh +++ b/tests/nodes/start.sh @@ -32,16 +32,13 @@ echo "Initializing finished, begin to start services ...." sleep 1 ckb run -C "$deploy_dir/node-data" --indexer & +cargo build # Start the dev node in the background. cd "$nodes_dir" || exit 1 start() { - if [ -n "$FNN" ]; then - "$FNN" "$@" - else - cargo run -- "$@" - fi + ../../target/debug/fnn "$@" } if [ "$#" -ne 1 ]; then From e08d786dffbaa7c16ddbdf007e067bc4eff8c44c Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 21 Dec 2024 17:27:15 +0800 Subject: [PATCH 004/119] begin the next version of tcl operations --- src/fiber/channel.rs | 268 ++++++++++++++++++++++++++++++++++--- src/fiber/tests/channel.rs | 56 +++++++- 2 files changed, 307 insertions(+), 17 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 35a74662d..0780136c0 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -2160,6 +2160,69 @@ impl TLCId { } } +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +pub enum OutboundTlcStatus { + LocalAnnounced, + Committed, + RemoteRemoved, + AwaitingRemoteRevokeToRemove, + AwaitingRemovedRemoteRevoke, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +pub enum InboundTlctatus { + RemoteAnnounced, + AwaitingRemoteRevokeToAnnounce, + AwaitingAnnouncedRemoteRevoke, + Committed, + LocalRemoved, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +pub enum TlcStatus { + Outbound(OutboundTlcStatus), + Inbound(InboundTlctatus), +} + +impl TlcStatus { + fn as_outbound_status(&self) -> OutboundTlcStatus { + match self { + TlcStatus::Outbound(status) => status.clone(), + _ => { + unreachable!("unexpected status") + } + } + } + + fn as_inbound_status(&self) -> InboundTlctatus { + match self { + TlcStatus::Inbound(status) => status.clone(), + _ => { + unreachable!("unexpected status ") + } + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +pub enum TlcKindV2 { + AddTlc(AddTlcInfoV2), + RemoveTlc(RemoveTlcInfo), +} + +impl TlcKindV2 { + pub fn log(&self) -> String { + match self { + TlcKindV2::AddTlc(add_tlc) => { + format!("{:?}", &add_tlc.tlc_id) + } + TlcKindV2::RemoveTlc(remove_tlc) => { + format!("RemoveTlc({:?})", &remove_tlc.tlc_id) + } + } + } +} + #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub enum TlcKind { AddTlc(AddTlcInfo), @@ -2179,6 +2242,63 @@ impl TlcKind { } } +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +pub struct AddTlcInfoV2 { + pub channel_id: Hash256, + pub status: TlcStatus, + pub tlc_id: TLCId, + pub amount: u128, + pub payment_hash: Hash256, + pub expiry: u64, + pub hash_algorithm: HashAlgorithm, + // the onion packet for multi-hop payment + pub onion_packet: Option, + /// Shared secret used in forwarding. + /// + /// Save it to backward errors. Use all zeros when no shared secrets are available. + pub shared_secret: [u8; 32], + pub created_at: CommitmentNumbers, + pub removed_at: Option<(CommitmentNumbers, RemoveTlcReason)>, + pub payment_preimage: Option, + + /// Note: `previous_tlc` is used to track the tlc chain for a multi-tlc payment, + /// we need to know previous when removing tlc backwardly. + /// + /// Node A ---------> Node B ------------> Node C ----------> Node D + /// tlc_1 <---> (tlc_1) (tlc_2) <---> (tlc_2) (tlc_3) <----> tlc_3 + /// ^^^^ ^^^^ + /// + pub previous_tlc: Option<(Hash256, TLCId)>, +} + +impl AddTlcInfoV2 { + pub fn is_offered(&self) -> bool { + self.tlc_id.is_offered() + } + + pub fn is_received(&self) -> bool { + !self.is_offered() + } + + pub fn get_commitment_numbers(&self) -> CommitmentNumbers { + self.created_at + } + + pub fn flip_mut(&mut self) { + self.tlc_id.flip_mut(); + } + + /// Get the value for the field `htlc_type` in commitment lock witness. + /// - Lowest 1 bit: 0 if the tlc is offered by the remote party, 1 otherwise. + /// - High 7 bits: + /// - 0: ckb hash + /// - 1: sha256 + pub fn get_htlc_type(&self) -> u8 { + let offered_flag = if self.is_offered() { 0u8 } else { 1u8 }; + ((self.hash_algorithm as u8) << 1) + offered_flag + } +} + #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub struct AddTlcInfo { pub channel_id: Hash256, @@ -2383,6 +2503,138 @@ pub enum RetryableRemoveTlc { RelayRemoveTlc(Hash256, u64, RemoveTlcReason), } +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Default)] +pub struct PendingTlcsV2 { + pub tlcs: Vec, + pub next_tlc_id: u64, +} + +impl PendingTlcsV2 { + pub fn get_next_id(&self) -> u64 { + self.next_tlc_id + } + + pub fn increment_next_id(&mut self) { + self.next_tlc_id += 1; + } + + pub fn add_tlc(&mut self, tlc: TlcKindV2) { + self.tlcs.push(tlc); + } +} + +#[derive(Default, Clone, Debug, Serialize, Deserialize)] +pub struct TlcStateV2 { + offered_tlcs: PendingTlcsV2, + received_tlcs: PendingTlcsV2, + // if the tlc is pending to be removed, the reason will be stored here + // this will only used for retrying remove TLC + retryable_remove_tlcs: Vec, + waiting_ack: bool, +} + +impl TlcStateV2 { + pub fn get_next_offering(&self) -> u64 { + self.offered_tlcs.get_next_id() + } + + pub fn get_next_received(&self) -> u64 { + self.received_tlcs.get_next_id() + } + + pub fn increment_offering(&mut self) { + self.offered_tlcs.increment_next_id(); + } + + pub fn increment_received(&mut self) { + self.received_tlcs.increment_next_id(); + } + + pub fn set_waiting_ack(&mut self, waiting_ack: bool) { + self.waiting_ack = waiting_ack; + } + + pub fn insert_relay_tlc_remove( + &mut self, + channel_id: Hash256, + tlc_id: u64, + reason: RemoveTlcReason, + ) { + self.retryable_remove_tlcs + .push(RetryableRemoveTlc::RelayRemoveTlc( + channel_id, tlc_id, reason, + )); + } + + pub fn get_pending_remove(&self) -> Vec { + self.retryable_remove_tlcs.clone() + } + + pub fn remove_pending_remove_tlc(&mut self, retryable_remove: &RetryableRemoveTlc) { + self.retryable_remove_tlcs + .retain(|remove| remove != retryable_remove); + } + + pub fn add_offered_tlc(&mut self, tlc: TlcKindV2) { + self.offered_tlcs.add_tlc(tlc); + } + + pub fn add_received_tlc(&mut self, tlc: TlcKindV2) { + self.received_tlcs.add_tlc(tlc); + } + + pub fn handle_commitment_signed(&self, local: bool) -> Hash256 { + let mut active_tls = vec![]; + for tlc in self.offered_tlcs.tlcs.iter() { + if let TlcKindV2::AddTlc(add_info) = tlc { + let status = add_info.status.as_outbound_status(); + let include = match status { + OutboundTlcStatus::LocalAnnounced => local, + OutboundTlcStatus::Committed => true, + OutboundTlcStatus::RemoteRemoved => local, + OutboundTlcStatus::AwaitingRemoteRevokeToRemove => local, + OutboundTlcStatus::AwaitingRemovedRemoteRevoke => false, + }; + if include { + active_tls.push(add_info.clone()); + } + } + } + for tlc in self.received_tlcs.tlcs.iter() { + if let TlcKindV2::AddTlc(add_info) = tlc { + let status = add_info.status.as_inbound_status(); + let include = match status { + InboundTlctatus::RemoteAnnounced => !local, + InboundTlctatus::AwaitingRemoteRevokeToAnnounce => !local, + InboundTlctatus::AwaitingAnnouncedRemoteRevoke => true, + InboundTlctatus::Committed => true, + InboundTlctatus::LocalRemoved => !local, + }; + if include { + active_tls.push(add_info.clone()); + } + } + } + + // serialize active_tls to ge a hash + let keyparts = active_tls + .iter() + .map(|tlc| (tlc.amount, tlc.payment_hash)) + .collect::>(); + + eprintln!("keyparts: {:?}", keyparts); + let serialized = serde_json::to_string(&keyparts).expect("Failed to serialize active_tls"); + + // Hash the serialized data using SHA-256 + let mut hasher = new_blake2b(); + hasher.update(serialized.to_string().as_bytes()); + let mut result = [0u8; 32]; + hasher.finalize(&mut result); + + result.into() + } +} + #[derive(Default, Clone, Debug, Serialize, Deserialize)] pub struct TlcState { local_pending_tlcs: PendingTlcs, @@ -2494,22 +2746,6 @@ impl TlcState { self.remote_pending_tlcs.push(tlc_info); } - pub fn next_local_tlc_id(&self) -> u64 { - self.local_pending_tlcs.next_tlc_id() - } - - pub fn increment_local_tlc_id(&mut self) { - self.local_pending_tlcs.increment_next_tlc_id(); - } - - pub fn next_remote_tlc_id(&self) -> u64 { - self.remote_pending_tlcs.next_tlc_id() - } - - pub fn increment_remote_tlc_id(&mut self) { - self.remote_pending_tlcs.increment_next_tlc_id(); - } - fn unify_tlcs<'a>( &self, staging_tlcs: impl Iterator, diff --git a/src/fiber/tests/channel.rs b/src/fiber/tests/channel.rs index 858a59ab0..d3e834cf2 100644 --- a/src/fiber/tests/channel.rs +++ b/src/fiber/tests/channel.rs @@ -1,6 +1,8 @@ use crate::fiber::channel::{ - AddTlcInfo, CommitmentNumbers, RemoveTlcInfo, TLCId, TlcKind, TlcState, UpdateCommand, + AddTlcInfo, CommitmentNumbers, InboundTlctatus, RemoveTlcInfo, TLCId, TlcKind, TlcState, + TlcStateV2, TlcStatus, UpdateCommand, }; +use crate::fiber::channel::{AddTlcInfoV2, TlcKindV2}; use crate::fiber::config::MAX_PAYMENT_TLC_EXPIRY_LIMIT; use crate::fiber::graph::PaymentSessionStatus; use crate::fiber::network::{DebugEvent, SendPaymentCommand}; @@ -147,6 +149,58 @@ fn test_pending_tlcs() { assert_eq!(tlcs2.len(), 0); } +#[test] +fn test_tlc_state_v2() { + let mut tlc_state = TlcStateV2::default(); + let mut add_tlc1 = AddTlcInfoV2 { + amount: 10000, + status: TlcStatus::Outbound(crate::fiber::channel::OutboundTlcStatus::LocalAnnounced), + channel_id: gen_rand_sha256_hash(), + payment_hash: gen_rand_sha256_hash(), + expiry: now_timestamp_as_millis_u64() + 1000, + hash_algorithm: HashAlgorithm::Sha256, + onion_packet: None, + shared_secret: NO_SHARED_SECRET.clone(), + tlc_id: TLCId::Offered(0), + created_at: CommitmentNumbers::default(), + removed_at: None, + payment_preimage: None, + previous_tlc: None, + }; + let mut add_tlc2 = AddTlcInfoV2 { + amount: 20000, + status: TlcStatus::Outbound(crate::fiber::channel::OutboundTlcStatus::LocalAnnounced), + channel_id: gen_rand_sha256_hash(), + payment_hash: gen_rand_sha256_hash(), + expiry: now_timestamp_as_millis_u64() + 2000, + hash_algorithm: HashAlgorithm::Sha256, + onion_packet: None, + shared_secret: NO_SHARED_SECRET.clone(), + tlc_id: TLCId::Offered(1), + created_at: CommitmentNumbers::default(), + removed_at: None, + payment_preimage: None, + previous_tlc: None, + }; + tlc_state.add_offered_tlc(TlcKindV2::AddTlc(add_tlc1.clone())); + tlc_state.add_offered_tlc(TlcKindV2::AddTlc(add_tlc2.clone())); + + let mut tlc_state_2 = TlcStateV2::default(); + add_tlc1.flip_mut(); + add_tlc2.flip_mut(); + add_tlc1.status = TlcStatus::Inbound(InboundTlctatus::RemoteAnnounced); + add_tlc2.status = TlcStatus::Inbound(InboundTlctatus::RemoteAnnounced); + tlc_state_2.add_received_tlc(TlcKindV2::AddTlc(add_tlc1)); + tlc_state_2.add_received_tlc(TlcKindV2::AddTlc(add_tlc2)); + + let hash1 = tlc_state.handle_commitment_signed(true); + eprintln!("hash1: {:?}", hash1); + + let hash2 = tlc_state_2.handle_commitment_signed(false); + eprintln!("hash2: {:?}", hash2); + assert_eq!(hash1, hash2); +} + #[test] fn test_pending_tlcs_duplicated_tlcs() { let mut tlc_state = TlcState::default(); From 8f9e9a005fca2d303c98bb48c138ef0d7b036235 Mon Sep 17 00:00:00 2001 From: quake Date: Sat, 21 Dec 2024 20:18:11 +0900 Subject: [PATCH 005/119] fix: deterministically hash should use molecule --- src/fiber/types.rs | 111 +++++++++++++++++++++------------------------ 1 file changed, 51 insertions(+), 60 deletions(-) diff --git a/src/fiber/types.rs b/src/fiber/types.rs index bb58bc404..f8d7f0a3a 100644 --- a/src/fiber/types.rs +++ b/src/fiber/types.rs @@ -1633,7 +1633,9 @@ impl NodeAnnouncement { auto_accept_min_ckb_funding_amount: self.auto_accept_min_ckb_funding_amount, udt_cfg_infos: get_udt_whitelist(), }; - deterministically_hash(&unsigned_announcement) + deterministically_hash(&molecule_gossip::NodeAnnouncement::from( + unsigned_announcement, + )) } pub fn peer_id(&self) -> PeerId { @@ -1764,13 +1766,7 @@ impl From for UdtCfgInfos { impl From for molecule_gossip::NodeAnnouncement { fn from(node_announcement: NodeAnnouncement) -> Self { - molecule_gossip::NodeAnnouncement::new_builder() - .signature( - node_announcement - .signature - .expect("node announcement signed") - .into(), - ) + let builder = molecule_gossip::NodeAnnouncement::new_builder() .features(node_announcement.features.pack()) .timestamp(node_announcement.timestamp.pack()) .node_id(node_announcement.node_id.into()) @@ -1790,8 +1786,15 @@ impl From for molecule_gossip::NodeAnnouncement { .collect(), ) .build(), - ) - .build() + ); + + let builder = if let Some(signature) = node_announcement.signature { + builder.signature(signature.into()) + } else { + builder + }; + + builder.build() } } @@ -1888,7 +1891,9 @@ impl ChannelAnnouncement { capacity: self.capacity, udt_type_script: self.udt_type_script.clone(), }; - deterministically_hash(&unsigned_announcement) + deterministically_hash(&molecule_gossip::ChannelAnnouncement::from( + unsigned_announcement, + )) } pub fn out_point(&self) -> &OutPoint { @@ -1898,25 +1903,7 @@ impl ChannelAnnouncement { impl From for molecule_gossip::ChannelAnnouncement { fn from(channel_announcement: ChannelAnnouncement) -> Self { - molecule_gossip::ChannelAnnouncement::new_builder() - .node1_signature( - channel_announcement - .node1_signature - .expect("channel announcement signed") - .into(), - ) - .node2_signature( - channel_announcement - .node2_signature - .expect("channel announcement signed") - .into(), - ) - .ckb_signature( - channel_announcement - .ckb_signature - .expect("channel announcement signed") - .into(), - ) + let builder = molecule_gossip::ChannelAnnouncement::new_builder() .features(channel_announcement.features.pack()) .chain_hash(channel_announcement.chain_hash.into()) .channel_outpoint(channel_announcement.channel_outpoint) @@ -1924,8 +1911,27 @@ impl From for molecule_gossip::ChannelAnnouncement { .node2_id(channel_announcement.node2_id.into()) .capacity(channel_announcement.capacity.pack()) .udt_type_script(channel_announcement.udt_type_script.pack()) - .ckb_key(channel_announcement.ckb_key.into()) - .build() + .ckb_key(channel_announcement.ckb_key.into()); + + let builder = if let Some(signature) = channel_announcement.node1_signature { + builder.node1_signature(signature.into()) + } else { + builder + }; + + let builder = if let Some(signature) = channel_announcement.node2_signature { + builder.node2_signature(signature.into()) + } else { + builder + }; + + let builder = if let Some(signature) = channel_announcement.ckb_signature { + builder.ckb_signature(signature.into()) + } else { + builder + }; + + builder.build() } } @@ -2014,7 +2020,7 @@ impl ChannelUpdate { tlc_minimum_value: self.tlc_minimum_value, tlc_fee_proportional_millionths: self.tlc_fee_proportional_millionths, }; - deterministically_hash(&unsigned_update) + deterministically_hash(&molecule_gossip::ChannelUpdate::from(unsigned_update)) } pub fn is_update_of_node_1(&self) -> bool { @@ -2039,13 +2045,7 @@ impl ChannelUpdate { impl From for molecule_gossip::ChannelUpdate { fn from(channel_update: ChannelUpdate) -> Self { - molecule_gossip::ChannelUpdate::new_builder() - .signature( - channel_update - .signature - .expect("channel update signed") - .into(), - ) + let builder = molecule_gossip::ChannelUpdate::new_builder() .chain_hash(channel_update.chain_hash.into()) .channel_outpoint(channel_update.channel_outpoint) .timestamp(channel_update.timestamp.pack()) @@ -2053,8 +2053,15 @@ impl From for molecule_gossip::ChannelUpdate { .channel_flags(channel_update.channel_flags.pack()) .tlc_expiry_delta(channel_update.tlc_expiry_delta.pack()) .tlc_minimum_value(channel_update.tlc_minimum_value.pack()) - .tlc_fee_proportional_millionths(channel_update.tlc_fee_proportional_millionths.pack()) - .build() + .tlc_fee_proportional_millionths(channel_update.tlc_fee_proportional_millionths.pack()); + + let builder = if let Some(signature) = channel_update.signature { + builder.signature(signature.into()) + } else { + builder + }; + + builder.build() } } @@ -2523,22 +2530,6 @@ impl TryFrom for BroadcastMessage { } } -impl BroadcastMessage { - pub fn id(&self) -> Hash256 { - match self { - BroadcastMessage::NodeAnnouncement(node_announcement) => { - deterministically_hash(node_announcement).into() - } - BroadcastMessage::ChannelAnnouncement(channel_announcement) => { - deterministically_hash(channel_announcement).into() - } - BroadcastMessage::ChannelUpdate(channel_update) => { - deterministically_hash(channel_update).into() - } - } - } -} - /// Note that currently we only allow querying for one type of broadcast message at a time. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum BroadcastMessageQueryFlags { @@ -3269,8 +3260,8 @@ pub(crate) fn deterministically_serialize(v: &T) -> Vec { serde_json::to_vec_pretty(v).expect("serialize value") } -pub(crate) fn deterministically_hash(v: &T) -> [u8; 32] { - ckb_hash::blake2b_256(deterministically_serialize(v)) +pub(crate) fn deterministically_hash(v: &T) -> [u8; 32] { + ckb_hash::blake2b_256(v.as_slice()).into() } #[serde_as] From 9380f2e2230a133902830aa582c110813acc3ff6 Mon Sep 17 00:00:00 2001 From: yukang Date: Sun, 22 Dec 2024 11:00:50 +0800 Subject: [PATCH 006/119] add unit test for tlc operations --- src/fiber/channel.rs | 20 +-- src/fiber/tests/channel.rs | 56 +------ src/fiber/tests/mod.rs | 1 + src/fiber/tests/tlc_op.rs | 302 +++++++++++++++++++++++++++++++++++++ 4 files changed, 306 insertions(+), 73 deletions(-) create mode 100644 src/fiber/tests/tlc_op.rs diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 0780136c0..96146e9cc 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -2583,7 +2583,7 @@ impl TlcStateV2 { self.received_tlcs.add_tlc(tlc); } - pub fn handle_commitment_signed(&self, local: bool) -> Hash256 { + pub fn commitment_signed(&self, local: bool) -> Vec { let mut active_tls = vec![]; for tlc in self.offered_tlcs.tlcs.iter() { if let TlcKindV2::AddTlc(add_info) = tlc { @@ -2615,23 +2615,7 @@ impl TlcStateV2 { } } } - - // serialize active_tls to ge a hash - let keyparts = active_tls - .iter() - .map(|tlc| (tlc.amount, tlc.payment_hash)) - .collect::>(); - - eprintln!("keyparts: {:?}", keyparts); - let serialized = serde_json::to_string(&keyparts).expect("Failed to serialize active_tls"); - - // Hash the serialized data using SHA-256 - let mut hasher = new_blake2b(); - hasher.update(serialized.to_string().as_bytes()); - let mut result = [0u8; 32]; - hasher.finalize(&mut result); - - result.into() + return active_tls; } } diff --git a/src/fiber/tests/channel.rs b/src/fiber/tests/channel.rs index d3e834cf2..858a59ab0 100644 --- a/src/fiber/tests/channel.rs +++ b/src/fiber/tests/channel.rs @@ -1,8 +1,6 @@ use crate::fiber::channel::{ - AddTlcInfo, CommitmentNumbers, InboundTlctatus, RemoveTlcInfo, TLCId, TlcKind, TlcState, - TlcStateV2, TlcStatus, UpdateCommand, + AddTlcInfo, CommitmentNumbers, RemoveTlcInfo, TLCId, TlcKind, TlcState, UpdateCommand, }; -use crate::fiber::channel::{AddTlcInfoV2, TlcKindV2}; use crate::fiber::config::MAX_PAYMENT_TLC_EXPIRY_LIMIT; use crate::fiber::graph::PaymentSessionStatus; use crate::fiber::network::{DebugEvent, SendPaymentCommand}; @@ -149,58 +147,6 @@ fn test_pending_tlcs() { assert_eq!(tlcs2.len(), 0); } -#[test] -fn test_tlc_state_v2() { - let mut tlc_state = TlcStateV2::default(); - let mut add_tlc1 = AddTlcInfoV2 { - amount: 10000, - status: TlcStatus::Outbound(crate::fiber::channel::OutboundTlcStatus::LocalAnnounced), - channel_id: gen_rand_sha256_hash(), - payment_hash: gen_rand_sha256_hash(), - expiry: now_timestamp_as_millis_u64() + 1000, - hash_algorithm: HashAlgorithm::Sha256, - onion_packet: None, - shared_secret: NO_SHARED_SECRET.clone(), - tlc_id: TLCId::Offered(0), - created_at: CommitmentNumbers::default(), - removed_at: None, - payment_preimage: None, - previous_tlc: None, - }; - let mut add_tlc2 = AddTlcInfoV2 { - amount: 20000, - status: TlcStatus::Outbound(crate::fiber::channel::OutboundTlcStatus::LocalAnnounced), - channel_id: gen_rand_sha256_hash(), - payment_hash: gen_rand_sha256_hash(), - expiry: now_timestamp_as_millis_u64() + 2000, - hash_algorithm: HashAlgorithm::Sha256, - onion_packet: None, - shared_secret: NO_SHARED_SECRET.clone(), - tlc_id: TLCId::Offered(1), - created_at: CommitmentNumbers::default(), - removed_at: None, - payment_preimage: None, - previous_tlc: None, - }; - tlc_state.add_offered_tlc(TlcKindV2::AddTlc(add_tlc1.clone())); - tlc_state.add_offered_tlc(TlcKindV2::AddTlc(add_tlc2.clone())); - - let mut tlc_state_2 = TlcStateV2::default(); - add_tlc1.flip_mut(); - add_tlc2.flip_mut(); - add_tlc1.status = TlcStatus::Inbound(InboundTlctatus::RemoteAnnounced); - add_tlc2.status = TlcStatus::Inbound(InboundTlctatus::RemoteAnnounced); - tlc_state_2.add_received_tlc(TlcKindV2::AddTlc(add_tlc1)); - tlc_state_2.add_received_tlc(TlcKindV2::AddTlc(add_tlc2)); - - let hash1 = tlc_state.handle_commitment_signed(true); - eprintln!("hash1: {:?}", hash1); - - let hash2 = tlc_state_2.handle_commitment_signed(false); - eprintln!("hash2: {:?}", hash2); - assert_eq!(hash1, hash2); -} - #[test] fn test_pending_tlcs_duplicated_tlcs() { let mut tlc_state = TlcState::default(); diff --git a/src/fiber/tests/mod.rs b/src/fiber/tests/mod.rs index ad7d7c7af..c236022df 100644 --- a/src/fiber/tests/mod.rs +++ b/src/fiber/tests/mod.rs @@ -8,4 +8,5 @@ mod path; mod payment; mod serde_utils; pub mod test_utils; +mod tlc_op; mod types; diff --git a/src/fiber/tests/tlc_op.rs b/src/fiber/tests/tlc_op.rs new file mode 100644 index 000000000..8696208f6 --- /dev/null +++ b/src/fiber/tests/tlc_op.rs @@ -0,0 +1,302 @@ +use crate::fiber::channel::AddTlcInfoV2; +use crate::fiber::channel::{ + CommitmentNumbers, InboundTlctatus, OutboundTlcStatus, TLCId, TlcKindV2, TlcStateV2, TlcStatus, +}; +use crate::fiber::hash_algorithm::HashAlgorithm; +use crate::fiber::types::{Hash256, NO_SHARED_SECRET}; +use crate::gen_rand_sha256_hash; +use crate::now_timestamp_as_millis_u64; +use ckb_hash::new_blake2b; +use ractor::{async_trait as rasync_trait, Actor, ActorProcessingErr, ActorRef}; +use std::collections::HashMap; + +fn sign_tlcs(tlcs: &Vec) -> Hash256 { + // serialize active_tls to ge a hash + let keyparts = tlcs + .iter() + .map(|tlc| (tlc.amount, tlc.payment_hash)) + .collect::>(); + + eprintln!("keyparts: {:?}", keyparts); + let serialized = serde_json::to_string(&keyparts).expect("Failed to serialize tls"); + + // Hash the serialized data using SHA-256 + let mut hasher = new_blake2b(); + hasher.update(serialized.to_string().as_bytes()); + let mut result = [0u8; 32]; + hasher.finalize(&mut result); + + result.into() +} + +pub struct TlcActorState { + pub tlc_state: TlcStateV2, + pub peer_id: String, +} + +impl TlcActorState { + pub fn get_peer(&self) -> String { + if self.peer_id == "peer_a" { + "peer_b".to_string() + } else { + "peer_a".to_string() + } + } +} + +pub struct NetworkActorState { + network: ActorRef, + pub peers: HashMap>, +} + +impl NetworkActorState { + pub async fn add_peer(&mut self, peer_id: String) { + let network = self.network.clone(); + let actor = Actor::spawn_linked( + Some(peer_id.clone()), + TlcActor::new(network.clone()), + peer_id.clone(), + network.clone().get_cell(), + ) + .await + .expect("Failed to start tlc actor") + .0; + self.peers.insert(peer_id.clone(), actor); + eprintln!("add_peer: {:?} added successfully ...", peer_id); + } +} + +pub struct TlcActor { + network: ActorRef, +} + +impl TlcActor { + pub fn new(network: ActorRef) -> Self { + Self { network } + } +} + +pub struct NetworkActor {} + +#[derive(Debug)] +pub enum TlcActorMessage { + CommandAddTlc(AddTlcInfoV2), + //CommandRemoveTlc, + PeerAddTlc(AddTlcInfoV2), + PeerCommitmentSigned(Hash256), + //PeerRemoveTlc, +} + +#[derive(Debug)] +pub enum NetworkActorMessage { + AddPeer(String), + AddTlc(String, AddTlcInfoV2), + PeerMsg(String, TlcActorMessage), +} + +#[rasync_trait] +impl Actor for NetworkActor { + type Msg = NetworkActorMessage; + type State = NetworkActorState; + type Arguments = (); + + async fn handle( + &self, + _myself: ActorRef, + message: Self::Msg, + state: &mut Self::State, + ) -> Result<(), ActorProcessingErr> { + match message { + NetworkActorMessage::AddPeer(peer_id) => { + state.add_peer(peer_id).await; + } + NetworkActorMessage::AddTlc(peer_id, add_tlc) => { + eprintln!("NetworkActorMessage::AddTlc"); + if let Some(actor) = state.peers.get(&peer_id) { + actor + .send_message(TlcActorMessage::CommandAddTlc(add_tlc)) + .expect("send ok"); + } + } + NetworkActorMessage::PeerMsg(peer_id, peer_msg) => { + if let Some(actor) = state.peers.get(&peer_id) { + eprintln!("NetworkActorMessage::PeerMsg: {:?}", peer_msg); + actor.send_message(peer_msg).expect("send ok"); + } + } + } + Ok(()) + } + + async fn pre_start( + &self, + myself: ActorRef, + _args: Self::Arguments, + ) -> Result { + eprintln!("NetworkActor pre_start"); + Ok(NetworkActorState { + peers: Default::default(), + network: myself.clone(), + }) + } +} + +#[rasync_trait] +impl Actor for TlcActor { + type Msg = TlcActorMessage; + type State = TlcActorState; + type Arguments = String; + + async fn handle( + &self, + _myself: ActorRef, + message: Self::Msg, + state: &mut Self::State, + ) -> Result<(), ActorProcessingErr> { + match message { + TlcActorMessage::CommandAddTlc(add_tlc) => { + eprintln!("TlcActorMessage::Command_AddTlc: {:?}", add_tlc); + state + .tlc_state + .add_offered_tlc(TlcKindV2::AddTlc(add_tlc.clone())); + let peer = state.get_peer(); + self.network + .send_message(NetworkActorMessage::PeerMsg( + peer.clone(), + TlcActorMessage::PeerAddTlc(add_tlc), + )) + .expect("send ok"); + + // send commitment signed + let tlcs = state.tlc_state.commitment_signed(true); + let hash = sign_tlcs(&tlcs); + eprintln!("got hash: {:?}", hash); + self.network + .send_message(NetworkActorMessage::PeerMsg( + peer, + TlcActorMessage::PeerCommitmentSigned(hash), + )) + .expect("send ok"); + } + TlcActorMessage::PeerAddTlc(add_tlc) => { + let mut tlc = add_tlc.clone(); + tlc.flip_mut(); + tlc.status = TlcStatus::Inbound(InboundTlctatus::RemoteAnnounced); + state.tlc_state.add_received_tlc(TlcKindV2::AddTlc(tlc)); + eprintln!("add peer tlc successfully: {:?}", add_tlc); + } + TlcActorMessage::PeerCommitmentSigned(peer_hash) => { + let tlcs = state.tlc_state.commitment_signed(false); + let hash = sign_tlcs(&tlcs); + assert_eq!(hash, peer_hash); + eprintln!("processed peer commitment_signed ...."); + } + } + Ok(()) + } + + async fn pre_start( + &self, + _myself: ActorRef, + args: Self::Arguments, + ) -> Result { + eprintln!("TlcActor pre_start"); + match args { + peer_id => { + eprintln!("peer_id: {:?}", peer_id); + Ok(TlcActorState { + tlc_state: Default::default(), + peer_id, + }) + } + } + } +} + +#[tokio::test] +async fn test_tlc_actor() { + let (network_actor, _handle) = Actor::spawn(None, NetworkActor {}, ()) + .await + .expect("Failed to start tlc actor"); + network_actor + .send_message(NetworkActorMessage::AddPeer("peer_a".to_string())) + .unwrap(); + network_actor + .send_message(NetworkActorMessage::AddPeer("peer_b".to_string())) + .unwrap(); + + network_actor + .send_message(NetworkActorMessage::AddTlc( + "peer_a".to_string(), + AddTlcInfoV2 { + amount: 10000, + status: TlcStatus::Outbound(OutboundTlcStatus::LocalAnnounced), + channel_id: gen_rand_sha256_hash(), + payment_hash: gen_rand_sha256_hash(), + expiry: now_timestamp_as_millis_u64() + 1000, + hash_algorithm: HashAlgorithm::Sha256, + onion_packet: None, + shared_secret: NO_SHARED_SECRET.clone(), + tlc_id: TLCId::Offered(0), + created_at: CommitmentNumbers::default(), + removed_at: None, + payment_preimage: None, + previous_tlc: None, + }, + )) + .unwrap(); + + tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; +} + +#[test] +fn test_tlc_state_v2() { + let mut tlc_state = TlcStateV2::default(); + let mut add_tlc1 = AddTlcInfoV2 { + amount: 10000, + status: TlcStatus::Outbound(OutboundTlcStatus::LocalAnnounced), + channel_id: gen_rand_sha256_hash(), + payment_hash: gen_rand_sha256_hash(), + expiry: now_timestamp_as_millis_u64() + 1000, + hash_algorithm: HashAlgorithm::Sha256, + onion_packet: None, + shared_secret: NO_SHARED_SECRET.clone(), + tlc_id: TLCId::Offered(0), + created_at: CommitmentNumbers::default(), + removed_at: None, + payment_preimage: None, + previous_tlc: None, + }; + let mut add_tlc2 = AddTlcInfoV2 { + amount: 20000, + status: TlcStatus::Outbound(OutboundTlcStatus::LocalAnnounced), + channel_id: gen_rand_sha256_hash(), + payment_hash: gen_rand_sha256_hash(), + expiry: now_timestamp_as_millis_u64() + 2000, + hash_algorithm: HashAlgorithm::Sha256, + onion_packet: None, + shared_secret: NO_SHARED_SECRET.clone(), + tlc_id: TLCId::Offered(1), + created_at: CommitmentNumbers::default(), + removed_at: None, + payment_preimage: None, + previous_tlc: None, + }; + tlc_state.add_offered_tlc(TlcKindV2::AddTlc(add_tlc1.clone())); + tlc_state.add_offered_tlc(TlcKindV2::AddTlc(add_tlc2.clone())); + + let mut tlc_state_2 = TlcStateV2::default(); + add_tlc1.flip_mut(); + add_tlc2.flip_mut(); + add_tlc1.status = TlcStatus::Inbound(InboundTlctatus::RemoteAnnounced); + add_tlc2.status = TlcStatus::Inbound(InboundTlctatus::RemoteAnnounced); + tlc_state_2.add_received_tlc(TlcKindV2::AddTlc(add_tlc1)); + tlc_state_2.add_received_tlc(TlcKindV2::AddTlc(add_tlc2)); + + let hash1 = sign_tlcs(&tlc_state.commitment_signed(true)); + eprintln!("hash1: {:?}", hash1); + + let hash2 = sign_tlcs(&tlc_state_2.commitment_signed(false)); + eprintln!("hash2: {:?}", hash2); + assert_eq!(hash1, hash2); +} From 30828508f13f5da4c853774fb5d1e502a5013f3f Mon Sep 17 00:00:00 2001 From: quake Date: Sun, 22 Dec 2024 21:00:08 +0900 Subject: [PATCH 007/119] chore: code cleanup --- src/fiber/channel.rs | 3 +-- src/fiber/gossip.rs | 3 --- src/fiber/network.rs | 23 ++++++++++------------- src/fiber/types.rs | 30 +++--------------------------- 4 files changed, 14 insertions(+), 45 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 35a74662d..6dee499ec 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -1795,8 +1795,7 @@ where // TODO: we may reject the channel opening request here // if the peer want to open a public channel, but we don't want to. - if public && channel_announcement_nonce.is_none() - || public && public_channel_info.is_none() + if public && (channel_announcement_nonce.is_none() || public_channel_info.is_none()) { return Err(Box::new(ProcessingChannelError::InvalidParameter( "Public channel should have channel announcement nonce and public channel info".to_string(), diff --git a/src/fiber/gossip.rs b/src/fiber/gossip.rs index 62952fdc8..37e0e69e7 100644 --- a/src/fiber/gossip.rs +++ b/src/fiber/gossip.rs @@ -2262,9 +2262,6 @@ where .expect("receive control"); debug!("Gossip actor received service control"); - let _ = myself.send_after(Duration::from_millis(500), || { - GossipActorMessage::TickNetworkMaintenance - }); let _ = myself.send_interval(network_maintenance_interval, || { GossipActorMessage::TickNetworkMaintenance }); diff --git a/src/fiber/network.rs b/src/fiber/network.rs index f5b9cc709..408eecf21 100644 --- a/src/fiber/network.rs +++ b/src/fiber/network.rs @@ -1481,19 +1481,16 @@ where // https://github.com/lightning/bolts/blob/master/04-onion-routing.md#rationale-6 // we now still update the graph, maybe we need to remove it later? if error_code.is_update() { - if let Some(extra_data) = &tcl_error_detail.extra_data { - match extra_data { - TlcErrData::ChannelFailed { channel_update, .. } => { - if let Some(channel_update) = channel_update { - let _ = network.send_message(NetworkActorMessage::new_command( - NetworkActorCommand::ProcessBroadcastMessage( - BroadcastMessage::ChannelUpdate(channel_update.clone()), - ), - )); - } - } - _ => {} - } + if let Some(TlcErrData::ChannelFailed { + channel_update: Some(channel_update), + .. + }) = &tcl_error_detail.extra_data + { + let _ = network.send_message(NetworkActorMessage::new_command( + NetworkActorCommand::ProcessBroadcastMessage(BroadcastMessage::ChannelUpdate( + channel_update.clone(), + )), + )); } } match tcl_error_detail.error_code() { diff --git a/src/fiber/types.rs b/src/fiber/types.rs index bb58bc404..03ee618e1 100644 --- a/src/fiber/types.rs +++ b/src/fiber/types.rs @@ -142,16 +142,7 @@ impl AsRef<[u8]> for Hash256 { impl From<&Hash256> for MByte32 { fn from(hash: &Hash256) -> Self { - MByte32::new_builder() - .set( - hash.0 - .into_iter() - .map(Byte::new) - .collect::>() - .try_into() - .expect("Byte32 from Hash256"), - ) - .build() + MByte32::from_slice(hash.0.as_ref()).expect("Byte32 from Hash256") } } @@ -163,13 +154,7 @@ impl From for MByte32 { impl From<&MByte32> for Hash256 { fn from(value: &MByte32) -> Self { - Hash256( - value - .as_bytes() - .to_vec() - .try_into() - .expect("Hash256 from Byte32"), - ) + Hash256(value.as_slice().try_into().expect("Hash256 from Byte32")) } } @@ -180,16 +165,7 @@ impl From for Hash256 { } fn u8_32_as_byte_32(value: &[u8; 32]) -> MByte32 { - MByte32::new_builder() - .set( - value - .iter() - .map(|v| Byte::new(*v)) - .collect::>() - .try_into() - .expect("[u8; 32] to Byte32"), - ) - .build() + MByte32::from_slice(value.as_slice()).expect("[u8; 32] to Byte32") } impl ::core::fmt::LowerHex for Hash256 { From 7fc5932811cef2e99acd2ecd3d9cfd68d5be207f Mon Sep 17 00:00:00 2001 From: YI Date: Sun, 22 Dec 2024 20:13:16 +0800 Subject: [PATCH 008/119] Fix creating incorrect funding tx in test --- src/fiber/tests/gossip.rs | 2 +- src/tests/mod.rs | 14 ++------------ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/fiber/tests/gossip.rs b/src/fiber/tests/gossip.rs index 8d02f57f9..eab5055ac 100644 --- a/src/fiber/tests/gossip.rs +++ b/src/fiber/tests/gossip.rs @@ -245,7 +245,7 @@ async fn test_saving_confirmed_channel_announcement() { let new_announcement = context .get_store() .get_latest_channel_announcement(&announcement.channel_outpoint); - assert_eq!(new_announcement, None); + assert_ne!(new_announcement, None); } #[tokio::test] diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 63e44c98d..2a1907a7b 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,5 +1,4 @@ use ckb_hash::blake2b_256; -use ckb_sdk::{Since, SinceType}; use ckb_types::core::TransactionView; use ckb_types::packed::CellOutput; use ckb_types::prelude::{Builder, Entity}; @@ -67,20 +66,11 @@ pub fn gen_node_announcement_from_privkey(sk: &Privkey) -> NodeAnnouncement { } pub fn create_funding_tx(x_only: &XOnlyPublicKey) -> TransactionView { - let version = 0u64; - let delay_epoch = 42; let capacity = 100u64; - let commitment_lock_script_args = [ - &blake2b_256(x_only.serialize())[0..20], - (Since::new(SinceType::EpochNumberWithFraction, delay_epoch, true).value()) - .to_le_bytes() - .as_slice(), - version.to_be_bytes().as_slice(), - ] - .concat(); + let commitment_lock_script_args = [&blake2b_256(x_only.serialize())[0..20]].concat(); TransactionView::new_advanced_builder() - .cell_deps(get_cell_deps_by_contracts(vec![Contract::CommitmentLock])) + .cell_deps(get_cell_deps_by_contracts(vec![Contract::Secp256k1Lock])) .output( CellOutput::new_builder() .capacity(capacity.pack()) From 08bd7bdcc0822352a2737f987eb9e6f1d303a467 Mon Sep 17 00:00:00 2001 From: YI Date: Sun, 22 Dec 2024 20:50:11 +0800 Subject: [PATCH 009/119] Add tests to save channel announcement and update --- src/fiber/tests/gossip.rs | 68 +++++++++++++++++++++++++++- src/tests/mod.rs | 94 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 159 insertions(+), 3 deletions(-) diff --git a/src/fiber/tests/gossip.rs b/src/fiber/tests/gossip.rs index eab5055ac..5b0a42b96 100644 --- a/src/fiber/tests/gossip.rs +++ b/src/fiber/tests/gossip.rs @@ -16,7 +16,6 @@ use tentacle::{ }; use tokio::{spawn, sync::RwLock}; -use crate::create_invalid_ecdsa_signature; use crate::fiber::tests::test_utils::{establish_channel_between_nodes, NetworkNode}; use crate::fiber::types::NodeAnnouncement; use crate::{ @@ -34,6 +33,7 @@ use crate::{ gen_node_announcement_from_privkey, gen_rand_channel_announcement, gen_rand_node_announcement, store::Store, }; +use crate::{create_invalid_ecdsa_signature, ChannelTestContext}; use super::test_utils::{get_test_root_actor, TempDir}; @@ -282,6 +282,72 @@ async fn test_saving_invalid_channel_announcement() { assert_eq!(new_announcement, None); } +#[tokio::test] +async fn test_saving_channel_update_after_saving_channel_announcement() { + let context = GossipTestingContext::new().await; + let channel_context = ChannelTestContext::gen(); + context.save_message(BroadcastMessage::ChannelAnnouncement( + channel_context.channel_announcement.clone(), + )); + let status = context.submit_tx(channel_context.funding_tx.clone()).await; + assert_eq!(status, Status::Committed); + tokio::time::sleep(Duration::from_millis(200).into()).await; + let new_announcement = context + .get_store() + .get_latest_channel_announcement(channel_context.channel_outpoint()); + assert_ne!(new_announcement, None); + for channel_update in [ + channel_context.create_channel_update_of_node1(0, 42, 42, 42), + channel_context.create_channel_update_of_node2(0, 42, 42, 42), + ] { + context.save_message(BroadcastMessage::ChannelUpdate(channel_update.clone())); + } + tokio::time::sleep(Duration::from_millis(200).into()).await; + for b in [true, false] { + let channel_update = context + .get_store() + .get_latest_channel_update(channel_context.channel_outpoint(), b); + assert_ne!(channel_update, None); + } +} + +#[tokio::test] +async fn test_saving_channel_update_before_saving_channel_announcement() { + let context = GossipTestingContext::new().await; + let channel_context = ChannelTestContext::gen(); + + for channel_update in [ + channel_context.create_channel_update_of_node1(0, 42, 42, 42), + channel_context.create_channel_update_of_node2(0, 42, 42, 42), + ] { + context.save_message(BroadcastMessage::ChannelUpdate(channel_update.clone())); + } + tokio::time::sleep(Duration::from_millis(200).into()).await; + for b in [true, false] { + let channel_update = context + .get_store() + .get_latest_channel_update(channel_context.channel_outpoint(), b); + assert_eq!(channel_update, None); + } + + context.save_message(BroadcastMessage::ChannelAnnouncement( + channel_context.channel_announcement.clone(), + )); + let status = context.submit_tx(channel_context.funding_tx.clone()).await; + assert_eq!(status, Status::Committed); + tokio::time::sleep(Duration::from_millis(200).into()).await; + let new_announcement = context + .get_store() + .get_latest_channel_announcement(channel_context.channel_outpoint()); + assert_ne!(new_announcement, None); + for b in [true, false] { + let channel_update = context + .get_store() + .get_latest_channel_update(channel_context.channel_outpoint(), b); + assert_ne!(channel_update, None); + } +} + #[tokio::test] async fn test_save_outdated_gossip_message() { let context = GossipTestingContext::new().await; diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 2a1907a7b..d52ca7291 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,12 +1,13 @@ use ckb_hash::blake2b_256; use ckb_types::core::TransactionView; use ckb_types::packed::CellOutput; -use ckb_types::prelude::{Builder, Entity}; +use ckb_types::prelude::{Builder, Entity, Unpack}; use ckb_types::{packed::OutPoint, prelude::Pack}; use secp256k1::{Keypair, PublicKey, Secp256k1, SecretKey, XOnlyPublicKey}; use crate::ckb::contracts::{get_cell_deps_by_contracts, get_script_by_contract, Contract}; -use crate::fiber::types::EcdsaSignature; +use crate::fiber::channel::{MESSAGE_OF_NODE1_FLAG, MESSAGE_OF_NODE2_FLAG}; +use crate::fiber::types::{ChannelUpdate, EcdsaSignature}; use crate::{ fiber::{ config::AnnouncedNodeName, @@ -113,6 +114,95 @@ pub fn gen_rand_channel_announcement() -> ( (sk, channel_announcement, tx, sk1, sk2) } +pub struct ChannelTestContext { + pub funding_tx_sk: Privkey, + pub node1_sk: Privkey, + pub node2_sk: Privkey, + pub funding_tx: TransactionView, + pub channel_announcement: ChannelAnnouncement, +} + +impl ChannelTestContext { + pub fn gen() -> ChannelTestContext { + let funding_tx_sk = gen_rand_fiber_private_key(); + let node1_sk = gen_rand_fiber_private_key(); + let node2_sk = gen_rand_fiber_private_key(); + let xonly = funding_tx_sk.x_only_pub_key(); + let funding_tx = create_funding_tx(&xonly); + let outpoint = funding_tx.output_pts_iter().next().unwrap(); + let capacity: u64 = funding_tx.output(0).unwrap().capacity().unpack(); + let mut channel_announcement = ChannelAnnouncement::new_unsigned( + &node1_sk.pubkey(), + &node2_sk.pubkey(), + outpoint.clone(), + &xonly, + capacity as u128, + None, + ); + let message = channel_announcement.message_to_sign(); + + channel_announcement.ckb_signature = Some(funding_tx_sk.sign_schnorr(message)); + channel_announcement.node1_signature = Some(node1_sk.sign(message)); + channel_announcement.node2_signature = Some(node2_sk.sign(message)); + + ChannelTestContext { + funding_tx_sk, + node1_sk, + node2_sk, + funding_tx, + channel_announcement, + } + } + + pub fn channel_outpoint(&self) -> &OutPoint { + &self.channel_announcement.channel_outpoint + } + + pub fn create_channel_update_of_node1( + &self, + channel_flags: u32, + tlc_expiry_delta: u64, + tlc_minimum_value: u128, + tlc_fee_proportional_millionths: u128, + ) -> ChannelUpdate { + let mut unsigned_channel_update = ChannelUpdate::new_unsigned( + self.channel_announcement.channel_outpoint.clone(), + now_timestamp_as_millis_u64(), + MESSAGE_OF_NODE1_FLAG, + channel_flags, + tlc_expiry_delta, + tlc_minimum_value, + tlc_fee_proportional_millionths, + ); + let message = unsigned_channel_update.message_to_sign(); + let signature = self.node1_sk.sign(message); + unsigned_channel_update.signature = Some(signature); + unsigned_channel_update + } + + pub fn create_channel_update_of_node2( + &self, + channel_flags: u32, + tlc_expiry_delta: u64, + tlc_minimum_value: u128, + tlc_fee_proportional_millionths: u128, + ) -> ChannelUpdate { + let mut unsigned_channel_update = ChannelUpdate::new_unsigned( + self.channel_announcement.channel_outpoint.clone(), + now_timestamp_as_millis_u64(), + MESSAGE_OF_NODE2_FLAG, + channel_flags, + tlc_expiry_delta, + tlc_minimum_value, + tlc_fee_proportional_millionths, + ); + let message = unsigned_channel_update.message_to_sign(); + let signature = self.node2_sk.sign(message); + unsigned_channel_update.signature = Some(signature); + unsigned_channel_update + } +} + pub fn create_invalid_ecdsa_signature() -> EcdsaSignature { let sk = Privkey::from([42u8; 32]); sk.sign([0u8; 32]) From bbbe1b9cfdc77881232396ac1342e47f0f0f6357 Mon Sep 17 00:00:00 2001 From: YI Date: Sun, 22 Dec 2024 20:55:07 +0800 Subject: [PATCH 010/119] Refactor channel announcements gossip tests --- src/fiber/tests/gossip.rs | 29 ++++++++++++++++++----------- src/tests/mod.rs | 29 ----------------------------- 2 files changed, 18 insertions(+), 40 deletions(-) diff --git a/src/fiber/tests/gossip.rs b/src/fiber/tests/gossip.rs index 5b0a42b96..f39be11cd 100644 --- a/src/fiber/tests/gossip.rs +++ b/src/fiber/tests/gossip.rs @@ -30,7 +30,7 @@ use crate::{ }, types::{BroadcastMessage, BroadcastMessageWithTimestamp, Cursor}, }, - gen_node_announcement_from_privkey, gen_rand_channel_announcement, gen_rand_node_announcement, + gen_node_announcement_from_privkey, gen_rand_node_announcement, store::Store, }; use crate::{create_invalid_ecdsa_signature, ChannelTestContext}; @@ -225,34 +225,41 @@ async fn test_save_gossip_message() { #[tokio::test] async fn test_saving_unconfirmed_channel_announcement() { let context = GossipTestingContext::new().await; - let (_, announcement, _, _, _) = gen_rand_channel_announcement(); - context.save_message(BroadcastMessage::ChannelAnnouncement(announcement.clone())); + let channel_context = ChannelTestContext::gen(); + context.save_message(BroadcastMessage::ChannelAnnouncement( + channel_context.channel_announcement.clone(), + )); tokio::time::sleep(Duration::from_millis(200).into()).await; let new_announcement = context .get_store() - .get_latest_channel_announcement(&announcement.channel_outpoint); + .get_latest_channel_announcement(channel_context.channel_outpoint()); assert_eq!(new_announcement, None); } #[tokio::test] async fn test_saving_confirmed_channel_announcement() { let context = GossipTestingContext::new().await; - let (_, announcement, tx, _, _) = gen_rand_channel_announcement(); - context.save_message(BroadcastMessage::ChannelAnnouncement(announcement.clone())); - let status = context.submit_tx(tx).await; + let channel_context = ChannelTestContext::gen(); + context.save_message(BroadcastMessage::ChannelAnnouncement( + channel_context.channel_announcement.clone(), + )); + let status = context.submit_tx(channel_context.funding_tx.clone()).await; assert_eq!(status, Status::Committed); tokio::time::sleep(Duration::from_millis(200).into()).await; let new_announcement = context .get_store() - .get_latest_channel_announcement(&announcement.channel_outpoint); + .get_latest_channel_announcement(channel_context.channel_outpoint()); assert_ne!(new_announcement, None); } #[tokio::test] async fn test_saving_invalid_channel_announcement() { let context = GossipTestingContext::new().await; - let (_, announcement, tx, _, _) = gen_rand_channel_announcement(); - context.save_message(BroadcastMessage::ChannelAnnouncement(announcement.clone())); + let channel_context = ChannelTestContext::gen(); + let tx = channel_context.funding_tx.clone(); + context.save_message(BroadcastMessage::ChannelAnnouncement( + channel_context.channel_announcement.clone(), + )); let output = tx.output(0).expect("get output").clone(); let invalid_lock = output .lock() @@ -278,7 +285,7 @@ async fn test_saving_invalid_channel_announcement() { tokio::time::sleep(Duration::from_millis(200).into()).await; let new_announcement = context .get_store() - .get_latest_channel_announcement(&announcement.channel_outpoint); + .get_latest_channel_announcement(channel_context.channel_outpoint()); assert_eq!(new_announcement, None); } diff --git a/src/tests/mod.rs b/src/tests/mod.rs index d52ca7291..7f2f760d9 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -85,35 +85,6 @@ pub fn create_funding_tx(x_only: &XOnlyPublicKey) -> TransactionView { .build() } -pub fn gen_rand_channel_announcement() -> ( - Privkey, - ChannelAnnouncement, - TransactionView, - Privkey, - Privkey, -) { - let sk1: Privkey = gen_rand_fiber_private_key(); - let sk2: Privkey = gen_rand_fiber_private_key(); - let sk = gen_rand_fiber_private_key(); - let xonly = sk.x_only_pub_key(); - let tx = create_funding_tx(&xonly); - let outpoint = tx.output_pts_iter().next().unwrap(); - let mut channel_announcement = ChannelAnnouncement::new_unsigned( - &sk1.pubkey(), - &sk2.pubkey(), - outpoint.clone(), - &xonly, - 0, - None, - ); - let message = channel_announcement.message_to_sign(); - - channel_announcement.ckb_signature = Some(sk.sign_schnorr(message)); - channel_announcement.node1_signature = Some(sk1.sign(message)); - channel_announcement.node2_signature = Some(sk2.sign(message)); - (sk, channel_announcement, tx, sk1, sk2) -} - pub struct ChannelTestContext { pub funding_tx_sk: Privkey, pub node1_sk: Privkey, From a100c959670c07035b3afa7f02499925b793bb65 Mon Sep 17 00:00:00 2001 From: YI Date: Sun, 22 Dec 2024 21:03:51 +0800 Subject: [PATCH 011/119] Add unit tests for channel update with invalid channel announcement --- src/fiber/tests/gossip.rs | 53 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/fiber/tests/gossip.rs b/src/fiber/tests/gossip.rs index f39be11cd..157344aad 100644 --- a/src/fiber/tests/gossip.rs +++ b/src/fiber/tests/gossip.rs @@ -355,6 +355,59 @@ async fn test_saving_channel_update_before_saving_channel_announcement() { } } +#[tokio::test] +async fn test_saving_channel_update_with_invalid_channel_announcement() { + let context = GossipTestingContext::new().await; + let channel_context = ChannelTestContext::gen(); + context.save_message(BroadcastMessage::ChannelAnnouncement( + channel_context.channel_announcement.clone(), + )); + let tx = channel_context.funding_tx.clone(); + context.save_message(BroadcastMessage::ChannelAnnouncement( + channel_context.channel_announcement.clone(), + )); + let output = tx.output(0).expect("get output").clone(); + let invalid_lock = output + .lock() + .as_builder() + .args( + Bytes::new_builder() + .set( + b"wrong lock args" + .into_iter() + .map(|b| Byte::new(*b)) + .collect(), + ) + .build(), + ) + .build(); + let invalid_output = output.as_builder().lock(invalid_lock).build(); + let invalid_tx = tx + .as_advanced_builder() + .set_outputs(vec![invalid_output]) + .build(); + let status = context.submit_tx(invalid_tx).await; + assert_eq!(status, Status::Committed); + tokio::time::sleep(Duration::from_millis(200).into()).await; + let new_announcement = context + .get_store() + .get_latest_channel_announcement(channel_context.channel_outpoint()); + assert_eq!(new_announcement, None); + for channel_update in [ + channel_context.create_channel_update_of_node1(0, 42, 42, 42), + channel_context.create_channel_update_of_node2(0, 42, 42, 42), + ] { + context.save_message(BroadcastMessage::ChannelUpdate(channel_update.clone())); + } + tokio::time::sleep(Duration::from_millis(200).into()).await; + for b in [true, false] { + let channel_update = context + .get_store() + .get_latest_channel_update(channel_context.channel_outpoint(), b); + assert_eq!(channel_update, None); + } +} + #[tokio::test] async fn test_save_outdated_gossip_message() { let context = GossipTestingContext::new().await; From fb4373dbb9d010ef26f07b4d8bc22757a26919a9 Mon Sep 17 00:00:00 2001 From: YI Date: Sun, 22 Dec 2024 21:18:18 +0800 Subject: [PATCH 012/119] Add unit tests for saving invalid channel updates --- src/fiber/tests/gossip.rs | 86 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/src/fiber/tests/gossip.rs b/src/fiber/tests/gossip.rs index 157344aad..a2f81179f 100644 --- a/src/fiber/tests/gossip.rs +++ b/src/fiber/tests/gossip.rs @@ -355,6 +355,92 @@ async fn test_saving_channel_update_before_saving_channel_announcement() { } } +#[tokio::test] +async fn test_saving_invalid_channel_update() { + let context = GossipTestingContext::new().await; + let channel_context = ChannelTestContext::gen(); + context.save_message(BroadcastMessage::ChannelAnnouncement( + channel_context.channel_announcement.clone(), + )); + let status = context.submit_tx(channel_context.funding_tx.clone()).await; + assert_eq!(status, Status::Committed); + tokio::time::sleep(Duration::from_millis(200).into()).await; + let new_announcement = context + .get_store() + .get_latest_channel_announcement(channel_context.channel_outpoint()); + assert_ne!(new_announcement, None); + for mut channel_update in [ + channel_context.create_channel_update_of_node1(0, 42, 42, 42), + channel_context.create_channel_update_of_node2(0, 42, 42, 42), + ] { + channel_update.signature = Some(create_invalid_ecdsa_signature()); + context.save_message(BroadcastMessage::ChannelUpdate(channel_update.clone())); + } + tokio::time::sleep(Duration::from_millis(200).into()).await; + for b in [true, false] { + let channel_update = context + .get_store() + .get_latest_channel_update(channel_context.channel_outpoint(), b); + assert_eq!(channel_update, None); + } +} + +#[tokio::test] +async fn test_saving_channel_update_independency() { + async fn test(node1_has_invalid_signature: bool, node2_has_invalid_signature: bool) { + let context = GossipTestingContext::new().await; + let channel_context = ChannelTestContext::gen(); + context.save_message(BroadcastMessage::ChannelAnnouncement( + channel_context.channel_announcement.clone(), + )); + let status = context.submit_tx(channel_context.funding_tx.clone()).await; + assert_eq!(status, Status::Committed); + tokio::time::sleep(Duration::from_millis(200).into()).await; + let new_announcement = context + .get_store() + .get_latest_channel_announcement(channel_context.channel_outpoint()); + assert_ne!(new_announcement, None); + for mut channel_update in [ + channel_context.create_channel_update_of_node1(0, 42, 42, 42), + channel_context.create_channel_update_of_node2(0, 42, 42, 42), + ] { + if channel_update.is_update_of_node_1() && node1_has_invalid_signature { + channel_update.signature = Some(create_invalid_ecdsa_signature()); + } + if channel_update.is_update_of_node_2() && node2_has_invalid_signature { + channel_update.signature = Some(create_invalid_ecdsa_signature()); + } + context.save_message(BroadcastMessage::ChannelUpdate(channel_update.clone())); + } + tokio::time::sleep(Duration::from_millis(200).into()).await; + for is_channel_update_of_node1 in [true, false] { + let channel_update = context.get_store().get_latest_channel_update( + channel_context.channel_outpoint(), + is_channel_update_of_node1, + ); + if is_channel_update_of_node1 { + if node1_has_invalid_signature { + assert_eq!(channel_update, None); + } else { + assert_ne!(channel_update, None); + } + } else { + if node2_has_invalid_signature { + assert_eq!(channel_update, None); + } else { + assert_ne!(channel_update, None); + } + } + } + } + + for node1_has_invalid_signature in [true, false] { + for node2_has_invalid_signature in [true, false] { + test(node1_has_invalid_signature, node2_has_invalid_signature).await; + } + } +} + #[tokio::test] async fn test_saving_channel_update_with_invalid_channel_announcement() { let context = GossipTestingContext::new().await; From 260953accabff5b389ec8b1693aafddc37c0b70d Mon Sep 17 00:00:00 2001 From: yukang Date: Mon, 23 Dec 2024 00:31:14 +0800 Subject: [PATCH 013/119] more unit test for tlc operations --- src/fiber/channel.rs | 59 ++++++++-- src/fiber/tests/tlc_op.rs | 222 +++++++++++++++++++++++++++++++++++--- 2 files changed, 259 insertions(+), 22 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 96146e9cc..887a74b09 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -2185,7 +2185,7 @@ pub enum TlcStatus { } impl TlcStatus { - fn as_outbound_status(&self) -> OutboundTlcStatus { + pub fn as_outbound_status(&self) -> OutboundTlcStatus { match self { TlcStatus::Outbound(status) => status.clone(), _ => { @@ -2194,7 +2194,7 @@ impl TlcStatus { } } - fn as_inbound_status(&self) -> InboundTlctatus { + pub fn as_inbound_status(&self) -> InboundTlctatus { match self { TlcStatus::Inbound(status) => status.clone(), _ => { @@ -2214,7 +2214,10 @@ impl TlcKindV2 { pub fn log(&self) -> String { match self { TlcKindV2::AddTlc(add_tlc) => { - format!("{:?}", &add_tlc.tlc_id) + format!( + "id: {:?} amount: {:?}, status: {:?}", + &add_tlc.tlc_id, &add_tlc.amount, &add_tlc.status + ) } TlcKindV2::RemoveTlc(remove_tlc) => { format!("RemoveTlc({:?})", &remove_tlc.tlc_id) @@ -2510,6 +2513,10 @@ pub struct PendingTlcsV2 { } impl PendingTlcsV2 { + pub fn iter_mut(&mut self) -> impl Iterator { + self.tlcs.iter_mut() + } + pub fn get_next_id(&self) -> u64 { self.next_tlc_id } @@ -2525,12 +2532,12 @@ impl PendingTlcsV2 { #[derive(Default, Clone, Debug, Serialize, Deserialize)] pub struct TlcStateV2 { - offered_tlcs: PendingTlcsV2, - received_tlcs: PendingTlcsV2, + pub offered_tlcs: PendingTlcsV2, + pub received_tlcs: PendingTlcsV2, // if the tlc is pending to be removed, the reason will be stored here // this will only used for retrying remove TLC - retryable_remove_tlcs: Vec, - waiting_ack: bool, + pub retryable_remove_tlcs: Vec, + pub waiting_ack: bool, } impl TlcStateV2 { @@ -2617,6 +2624,44 @@ impl TlcStateV2 { } return active_tls; } + + pub fn build_ack_transaction(&self, _for_remote: bool) -> Vec { + let mut active_tls = vec![]; + for tlc in self.offered_tlcs.tlcs.iter() { + if let TlcKindV2::AddTlc(add_info) = tlc { + active_tls.push(add_info.clone()); + } + } + for tlc in self.received_tlcs.tlcs.iter() { + if let TlcKindV2::AddTlc(add_info) = tlc { + active_tls.push(add_info.clone()); + } + } + return active_tls; + } + + pub fn need_another_commitment_signed(&self) -> bool { + self.offered_tlcs.tlcs.iter().any(|tlc| { + if let TlcKindV2::AddTlc(add_info) = tlc { + let status = add_info.status.as_outbound_status(); + matches!(status, OutboundTlcStatus::LocalAnnounced) + } else { + false + } + }) || self.received_tlcs.tlcs.iter().any(|tlc| { + if let TlcKindV2::AddTlc(add_info) = tlc { + let status = add_info.status.as_inbound_status(); + matches!( + status, + InboundTlctatus::RemoteAnnounced + | InboundTlctatus::AwaitingRemoteRevokeToAnnounce + | InboundTlctatus::AwaitingAnnouncedRemoteRevoke + ) + } else { + false + } + }) + } } #[derive(Default, Clone, Debug, Serialize, Deserialize)] diff --git a/src/fiber/tests/tlc_op.rs b/src/fiber/tests/tlc_op.rs index 8696208f6..d51bf626a 100644 --- a/src/fiber/tests/tlc_op.rs +++ b/src/fiber/tests/tlc_op.rs @@ -3,20 +3,28 @@ use crate::fiber::channel::{ CommitmentNumbers, InboundTlctatus, OutboundTlcStatus, TLCId, TlcKindV2, TlcStateV2, TlcStatus, }; use crate::fiber::hash_algorithm::HashAlgorithm; +use crate::fiber::types::PaymentOnionPacket; use crate::fiber::types::{Hash256, NO_SHARED_SECRET}; use crate::gen_rand_sha256_hash; use crate::now_timestamp_as_millis_u64; use ckb_hash::new_blake2b; +use ckb_types::packed::Byte32; use ractor::{async_trait as rasync_trait, Actor, ActorProcessingErr, ActorRef}; use std::collections::HashMap; fn sign_tlcs(tlcs: &Vec) -> Hash256 { // serialize active_tls to ge a hash - let keyparts = tlcs + let mut keyparts = tlcs .iter() .map(|tlc| (tlc.amount, tlc.payment_hash)) .collect::>(); + keyparts.sort_by(|a, b| { + let a: Byte32 = a.1.into(); + let b: Byte32 = b.1.into(); + a.cmp(&b) + }); + eprintln!("keyparts: {:?}", keyparts); let serialized = serde_json::to_string(&keyparts).expect("Failed to serialize tls"); @@ -76,21 +84,35 @@ impl TlcActor { } } +#[derive(Debug, Clone)] +pub struct AddTlcCommand { + pub amount: u128, + pub payment_hash: Hash256, + pub expiry: u64, + pub hash_algorithm: HashAlgorithm, + pub onion_packet: Option, + pub shared_secret: [u8; 32], + #[allow(dead_code)] + pub previous_tlc: Option<(Hash256, u64)>, +} + pub struct NetworkActor {} #[derive(Debug)] pub enum TlcActorMessage { - CommandAddTlc(AddTlcInfoV2), + Debug, + CommandAddTlc(AddTlcCommand), //CommandRemoveTlc, PeerAddTlc(AddTlcInfoV2), PeerCommitmentSigned(Hash256), + PeerRevokeAndAck(Hash256), //PeerRemoveTlc, } #[derive(Debug)] pub enum NetworkActorMessage { AddPeer(String), - AddTlc(String, AddTlcInfoV2), + AddTlc(String, AddTlcCommand), PeerMsg(String, TlcActorMessage), } @@ -154,11 +176,40 @@ impl Actor for TlcActor { state: &mut Self::State, ) -> Result<(), ActorProcessingErr> { match message { - TlcActorMessage::CommandAddTlc(add_tlc) => { - eprintln!("TlcActorMessage::Command_AddTlc: {:?}", add_tlc); + TlcActorMessage::Debug => { + eprintln!("Peer {} Debug", state.peer_id); + for tlc in state.tlc_state.offered_tlcs.tlcs.iter() { + eprintln!("offered_tlc: {:?}", tlc.log()); + } + for tlc in state.tlc_state.received_tlcs.tlcs.iter() { + eprintln!("received_tlc: {:?}", tlc.log()); + } + } + TlcActorMessage::CommandAddTlc(command) => { + eprintln!( + "Peer {} TlcActorMessage::Command_AddTlc: {:?}", + state.peer_id, command + ); + let next_offer_id = state.tlc_state.get_next_offering(); + let add_tlc = AddTlcInfoV2 { + channel_id: gen_rand_sha256_hash(), + tlc_id: TLCId::Offered(next_offer_id), + amount: command.amount, + payment_hash: command.payment_hash, + expiry: command.expiry, + hash_algorithm: command.hash_algorithm, + created_at: CommitmentNumbers::default(), + payment_preimage: None, + removed_at: None, + onion_packet: command.onion_packet, + shared_secret: command.shared_secret, + previous_tlc: None, + status: TlcStatus::Outbound(OutboundTlcStatus::LocalAnnounced), + }; state .tlc_state .add_offered_tlc(TlcKindV2::AddTlc(add_tlc.clone())); + state.tlc_state.increment_offering(); let peer = state.get_peer(); self.network .send_message(NetworkActorMessage::PeerMsg( @@ -168,7 +219,7 @@ impl Actor for TlcActor { .expect("send ok"); // send commitment signed - let tlcs = state.tlc_state.commitment_signed(true); + let tlcs = state.tlc_state.commitment_signed(false); let hash = sign_tlcs(&tlcs); eprintln!("got hash: {:?}", hash); self.network @@ -179,6 +230,7 @@ impl Actor for TlcActor { .expect("send ok"); } TlcActorMessage::PeerAddTlc(add_tlc) => { + eprintln!("Peer {} process peer add_tlc ....", state.peer_id); let mut tlc = add_tlc.clone(); tlc.flip_mut(); tlc.status = TlcStatus::Inbound(InboundTlctatus::RemoteAnnounced); @@ -186,10 +238,92 @@ impl Actor for TlcActor { eprintln!("add peer tlc successfully: {:?}", add_tlc); } TlcActorMessage::PeerCommitmentSigned(peer_hash) => { - let tlcs = state.tlc_state.commitment_signed(false); + eprintln!( + "\nPeer {} processed peer commitment_signed ....", + state.peer_id + ); + let tlcs = state.tlc_state.commitment_signed(true); + let hash = sign_tlcs(&tlcs); + assert_eq!(hash, peer_hash); + + let peer = state.get_peer(); + + for tlc in state.tlc_state.received_tlcs.tlcs.iter_mut() { + if let TlcKindV2::AddTlc(tlc) = tlc { + let out_status = tlc.status.as_inbound_status(); + match out_status { + InboundTlctatus::RemoteAnnounced => { + tlc.status = TlcStatus::Inbound( + InboundTlctatus::AwaitingRemoteRevokeToAnnounce, + ) + } + _ => {} + } + } + } + + eprintln!("sending peer revoke and ack ...."); + let tlcs = state.tlc_state.build_ack_transaction(true); + let hash = sign_tlcs(&tlcs); + self.network + .send_message(NetworkActorMessage::PeerMsg( + peer.clone(), + TlcActorMessage::PeerRevokeAndAck(hash), + )) + .expect("send ok"); + + // send commitment signed from our side if necessary + if state.tlc_state.need_another_commitment_signed() { + eprintln!("sending another commitment signed ...."); + let tlcs = state.tlc_state.commitment_signed(false); + let hash = sign_tlcs(&tlcs); + self.network + .send_message(NetworkActorMessage::PeerMsg( + peer, + TlcActorMessage::PeerCommitmentSigned(hash), + )) + .expect("send ok"); + } + } + TlcActorMessage::PeerRevokeAndAck(peer_hash) => { + eprintln!("Peer {} processed peer revoke and ack ....", state.peer_id); + let tlcs = state.tlc_state.build_ack_transaction(false); let hash = sign_tlcs(&tlcs); assert_eq!(hash, peer_hash); - eprintln!("processed peer commitment_signed ...."); + + for tlc in state.tlc_state.offered_tlcs.tlcs.iter_mut() { + if let TlcKindV2::AddTlc(tlc) = tlc { + let out_status = tlc.status.as_outbound_status(); + match out_status { + OutboundTlcStatus::LocalAnnounced => { + tlc.status = TlcStatus::Outbound(OutboundTlcStatus::Committed); + } + OutboundTlcStatus::AwaitingRemoteRevokeToRemove => { + tlc.status = TlcStatus::Outbound( + OutboundTlcStatus::AwaitingRemovedRemoteRevoke, + ); + } + _ => {} + } + } + } + + for tlc in state.tlc_state.received_tlcs.tlcs.iter_mut() { + if let TlcKindV2::AddTlc(tlc) = tlc { + let in_status = tlc.status.as_inbound_status(); + match in_status { + InboundTlctatus::AwaitingRemoteRevokeToAnnounce => { + tlc.status = TlcStatus::Inbound( + InboundTlctatus::AwaitingAnnouncedRemoteRevoke, + ); + } + InboundTlctatus::AwaitingAnnouncedRemoteRevoke => { + tlc.status = TlcStatus::Inbound(InboundTlctatus::Committed); + } + _ => {} + } + } + } } } Ok(()) @@ -228,25 +362,83 @@ async fn test_tlc_actor() { network_actor .send_message(NetworkActorMessage::AddTlc( "peer_a".to_string(), - AddTlcInfoV2 { + AddTlcCommand { amount: 10000, - status: TlcStatus::Outbound(OutboundTlcStatus::LocalAnnounced), - channel_id: gen_rand_sha256_hash(), payment_hash: gen_rand_sha256_hash(), expiry: now_timestamp_as_millis_u64() + 1000, hash_algorithm: HashAlgorithm::Sha256, onion_packet: None, shared_secret: NO_SHARED_SECRET.clone(), - tlc_id: TLCId::Offered(0), - created_at: CommitmentNumbers::default(), - removed_at: None, - payment_preimage: None, previous_tlc: None, }, )) .unwrap(); tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + network_actor + .send_message(NetworkActorMessage::AddTlc( + "peer_a".to_string(), + AddTlcCommand { + amount: 20000, + payment_hash: gen_rand_sha256_hash(), + expiry: now_timestamp_as_millis_u64() + 1000, + hash_algorithm: HashAlgorithm::Sha256, + onion_packet: None, + shared_secret: NO_SHARED_SECRET.clone(), + previous_tlc: None, + }, + )) + .unwrap(); + + tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + network_actor + .send_message(NetworkActorMessage::AddTlc( + "peer_b".to_string(), + AddTlcCommand { + amount: 30000, + payment_hash: gen_rand_sha256_hash(), + expiry: now_timestamp_as_millis_u64() + 1000, + hash_algorithm: HashAlgorithm::Sha256, + onion_packet: None, + shared_secret: NO_SHARED_SECRET.clone(), + previous_tlc: None, + }, + )) + .unwrap(); + + tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + network_actor + .send_message(NetworkActorMessage::AddTlc( + "peer_b".to_string(), + AddTlcCommand { + amount: 50000, + payment_hash: gen_rand_sha256_hash(), + expiry: now_timestamp_as_millis_u64() + 1000, + hash_algorithm: HashAlgorithm::Sha256, + onion_packet: None, + shared_secret: NO_SHARED_SECRET.clone(), + previous_tlc: None, + }, + )) + .unwrap(); + + tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + network_actor + .send_message(NetworkActorMessage::PeerMsg( + "peer_a".to_string(), + TlcActorMessage::Debug, + )) + .unwrap(); + + tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; + network_actor + .send_message(NetworkActorMessage::PeerMsg( + "peer_b".to_string(), + TlcActorMessage::Debug, + )) + .unwrap(); + + tokio::time::sleep(tokio::time::Duration::from_millis(2000)).await; } #[test] From c950ba192fc6026697c86c9c8db3b6a91e19e57a Mon Sep 17 00:00:00 2001 From: quake Date: Sat, 21 Dec 2024 19:39:59 +0900 Subject: [PATCH 014/119] chore: simplify fn prune_messages_to_be_saved --- src/fiber/gossip.rs | 92 ++++++++++----------------------------------- src/fiber/types.rs | 55 ++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 74 deletions(-) diff --git a/src/fiber/gossip.rs b/src/fiber/gossip.rs index 37e0e69e7..0f9154e5b 100644 --- a/src/fiber/gossip.rs +++ b/src/fiber/gossip.rs @@ -1,6 +1,7 @@ use std::{ - collections::{HashMap, HashSet, VecDeque}, + collections::{HashMap, HashSet}, marker::PhantomData, + mem::take, sync::Arc, time::Duration, }; @@ -1017,83 +1018,30 @@ impl ExtendedGossipMessageStoreState { // We will also change the relevant state (e.g. update the latest cursor). // The returned list may be sent to the subscribers. async fn prune_messages_to_be_saved(&mut self) -> Vec { - let complete_messages = self - .messages_to_be_saved - .iter() - .filter(|m| self.has_dependencies_available(m)) - .cloned() - .collect::>(); - self.messages_to_be_saved - .retain(|v| !complete_messages.contains(v)); - - let mut sorted_messages = Vec::with_capacity(complete_messages.len()); - - // Save all the messages to a map so that we can easily order messages by their dependencies. - let mut messages_map: HashMap< - (BroadcastMessageID, bool), - VecDeque, - > = HashMap::new(); - - for new_message in complete_messages { - let key = ( - new_message.message_id(), - match &new_message { - // Message id alone is not enough to differentiate channel updates. - // We need a flag to indicate if the message is an update of node 1. - BroadcastMessageWithTimestamp::ChannelUpdate(channel_update) => { - channel_update.is_update_of_node_1() - } - _ => true, - }, - ); - let messages = messages_map.entry(key).or_default(); - let index = messages.partition_point(|m| m.cursor() < new_message.cursor()); - match messages.get(index + 1) { - Some(message) if message == &new_message => { - // The same message is already saved. - continue; - } - _ => { - messages.insert(index, new_message); - } - } - } + let messages_to_be_saved = take(&mut self.messages_to_be_saved); + let (complete_messages, uncomplete_messages) = messages_to_be_saved + .into_iter() + .partition(|m| self.has_dependencies_available(m)); + self.messages_to_be_saved = uncomplete_messages; - loop { - let key = match messages_map.keys().next() { - None => break, - Some(key) => key.clone(), - }; - let messages = messages_map.remove(&key).expect("key exists"); - if let BroadcastMessageWithTimestamp::ChannelUpdate(channel_update) = &messages[0] { - let outpoint = channel_update.channel_outpoint.clone(); - if let Some(message) = - messages_map.remove(&(BroadcastMessageID::ChannelAnnouncement(outpoint), true)) - { - for message in message { - sorted_messages.push(message); - } - } - } - for message in messages { - sorted_messages.push(message); - } - } + let mut sorted_messages = complete_messages.into_iter().collect::>(); + sorted_messages.sort_unstable(); let mut verified_sorted_messages = Vec::with_capacity(sorted_messages.len()); - for message in sorted_messages { - if let Err(error) = - verify_and_save_broadcast_message(&message, &self.store, &self.chain_actor).await + match verify_and_save_broadcast_message(&message, &self.store, &self.chain_actor).await { - warn!( - "Failed to verify and save message {:?}: {:?}", - message, error - ); - continue; + Ok(_) => { + self.update_last_cursor(message.cursor()); + verified_sorted_messages.push(message); + } + Err(error) => { + warn!( + "Failed to verify and save message {:?}: {:?}", + message, error + ); + } } - self.update_last_cursor(message.cursor()); - verified_sorted_messages.push(message); } verified_sorted_messages diff --git a/src/fiber/types.rs b/src/fiber/types.rs index 03ee618e1..d35122eef 100644 --- a/src/fiber/types.rs +++ b/src/fiber/types.rs @@ -30,6 +30,7 @@ use secp256k1::{ use secp256k1::{Verification, XOnlyPublicKey}; use serde::{Deserialize, Serialize}; use serde_with::serde_as; +use std::cmp::Ordering; use std::fmt::Display; use std::marker::PhantomData; use std::str::FromStr; @@ -2407,6 +2408,20 @@ impl BroadcastMessageWithTimestamp { } } +impl Ord for BroadcastMessageWithTimestamp { + fn cmp(&self, other: &Self) -> Ordering { + self.message_id() + .cmp(&other.message_id()) + .then(self.timestamp().cmp(&other.timestamp())) + } +} + +impl PartialOrd for BroadcastMessageWithTimestamp { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + impl From for BroadcastMessage { fn from(broadcast_message_with_timestamp: BroadcastMessageWithTimestamp) -> Self { match broadcast_message_with_timestamp { @@ -2592,6 +2607,42 @@ pub enum BroadcastMessageID { NodeAnnouncement(Pubkey), } +// We need to implement Ord for BroadcastMessageID to make sure that a ChannelUpdate message is always ordered after ChannelAnnouncement, +// so that we can use it as the sorting key in fn prune_messages_to_be_saved to simplify the logic. +impl Ord for BroadcastMessageID { + fn cmp(&self, other: &Self) -> Ordering { + match (self, other) { + ( + BroadcastMessageID::ChannelAnnouncement(outpoint1), + BroadcastMessageID::ChannelAnnouncement(outpoint2), + ) => outpoint1.cmp(outpoint2), + ( + BroadcastMessageID::ChannelUpdate(outpoint1), + BroadcastMessageID::ChannelUpdate(outpoint2), + ) => outpoint1.cmp(outpoint2), + ( + BroadcastMessageID::NodeAnnouncement(pubkey1), + BroadcastMessageID::NodeAnnouncement(pubkey2), + ) => pubkey1.cmp(pubkey2), + (BroadcastMessageID::ChannelUpdate(_), _) => Ordering::Less, + (BroadcastMessageID::NodeAnnouncement(_), _) => Ordering::Greater, + ( + BroadcastMessageID::ChannelAnnouncement(_), + BroadcastMessageID::NodeAnnouncement(_), + ) => Ordering::Less, + (BroadcastMessageID::ChannelAnnouncement(_), BroadcastMessageID::ChannelUpdate(_)) => { + Ordering::Greater + } + } + } +} + +impl PartialOrd for BroadcastMessageID { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + // 1 byte for message type, 36 bytes for message id const MESSAGE_ID_SIZE: usize = 1 + 36; // 8 bytes for timestamp, MESSAGE_ID_SIZE bytes for message id @@ -2717,13 +2768,13 @@ impl Cursor { } impl Ord for Cursor { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { + fn cmp(&self, other: &Self) -> Ordering { self.to_bytes().cmp(&other.to_bytes()) } } impl PartialOrd for Cursor { - fn partial_cmp(&self, other: &Self) -> Option { + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } From 06ecaa3838da5c63608bb7d20bc2e2376ce2a170 Mon Sep 17 00:00:00 2001 From: yukang Date: Mon, 23 Dec 2024 11:31:53 +0800 Subject: [PATCH 015/119] set waiting_ack --- src/fiber/channel.rs | 59 ++++++++++++++++++++++++++++++++++++++- src/fiber/tests/tlc_op.rs | 48 ++----------------------------- 2 files changed, 60 insertions(+), 47 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 887a74b09..d72e4d8ee 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -2590,7 +2590,7 @@ impl TlcStateV2 { self.received_tlcs.add_tlc(tlc); } - pub fn commitment_signed(&self, local: bool) -> Vec { + pub fn commitment_signed(&mut self, local: bool) -> Vec { let mut active_tls = vec![]; for tlc in self.offered_tlcs.tlcs.iter() { if let TlcKindV2::AddTlc(add_info) = tlc { @@ -2622,9 +2622,31 @@ impl TlcStateV2 { } } } + if !local { + self.set_waiting_ack(true); + } return active_tls; } + pub fn update_for_peer_commitment_signed(&mut self) { + for tlc in self.received_tlcs.tlcs.iter_mut() { + if let TlcKindV2::AddTlc(tlc) = tlc { + let out_status = tlc.status.as_inbound_status(); + match out_status { + InboundTlctatus::RemoteAnnounced => { + let status = if self.waiting_ack { + InboundTlctatus::AwaitingRemoteRevokeToAnnounce + } else { + InboundTlctatus::AwaitingAnnouncedRemoteRevoke + }; + tlc.status = TlcStatus::Inbound(status) + } + _ => {} + } + } + } + } + pub fn build_ack_transaction(&self, _for_remote: bool) -> Vec { let mut active_tls = vec![]; for tlc in self.offered_tlcs.tlcs.iter() { @@ -2640,6 +2662,41 @@ impl TlcStateV2 { return active_tls; } + pub fn handle_reovke_and_ack(&mut self) { + self.set_waiting_ack(false); + for tlc in self.offered_tlcs.tlcs.iter_mut() { + if let TlcKindV2::AddTlc(tlc) = tlc { + let out_status = tlc.status.as_outbound_status(); + match out_status { + OutboundTlcStatus::LocalAnnounced => { + tlc.status = TlcStatus::Outbound(OutboundTlcStatus::Committed); + } + OutboundTlcStatus::AwaitingRemoteRevokeToRemove => { + tlc.status = + TlcStatus::Outbound(OutboundTlcStatus::AwaitingRemovedRemoteRevoke); + } + _ => {} + } + } + } + + for tlc in self.received_tlcs.tlcs.iter_mut() { + if let TlcKindV2::AddTlc(tlc) = tlc { + let in_status = tlc.status.as_inbound_status(); + match in_status { + InboundTlctatus::AwaitingRemoteRevokeToAnnounce => { + tlc.status = + TlcStatus::Inbound(InboundTlctatus::AwaitingAnnouncedRemoteRevoke); + } + InboundTlctatus::AwaitingAnnouncedRemoteRevoke => { + tlc.status = TlcStatus::Inbound(InboundTlctatus::Committed); + } + _ => {} + } + } + } + } + pub fn need_another_commitment_signed(&self) -> bool { self.offered_tlcs.tlcs.iter().any(|tlc| { if let TlcKindV2::AddTlc(add_info) = tlc { diff --git a/src/fiber/tests/tlc_op.rs b/src/fiber/tests/tlc_op.rs index d51bf626a..a04c65591 100644 --- a/src/fiber/tests/tlc_op.rs +++ b/src/fiber/tests/tlc_op.rs @@ -248,19 +248,7 @@ impl Actor for TlcActor { let peer = state.get_peer(); - for tlc in state.tlc_state.received_tlcs.tlcs.iter_mut() { - if let TlcKindV2::AddTlc(tlc) = tlc { - let out_status = tlc.status.as_inbound_status(); - match out_status { - InboundTlctatus::RemoteAnnounced => { - tlc.status = TlcStatus::Inbound( - InboundTlctatus::AwaitingRemoteRevokeToAnnounce, - ) - } - _ => {} - } - } - } + state.tlc_state.update_for_peer_commitment_signed(); eprintln!("sending peer revoke and ack ...."); let tlcs = state.tlc_state.build_ack_transaction(true); @@ -291,39 +279,7 @@ impl Actor for TlcActor { let hash = sign_tlcs(&tlcs); assert_eq!(hash, peer_hash); - for tlc in state.tlc_state.offered_tlcs.tlcs.iter_mut() { - if let TlcKindV2::AddTlc(tlc) = tlc { - let out_status = tlc.status.as_outbound_status(); - match out_status { - OutboundTlcStatus::LocalAnnounced => { - tlc.status = TlcStatus::Outbound(OutboundTlcStatus::Committed); - } - OutboundTlcStatus::AwaitingRemoteRevokeToRemove => { - tlc.status = TlcStatus::Outbound( - OutboundTlcStatus::AwaitingRemovedRemoteRevoke, - ); - } - _ => {} - } - } - } - - for tlc in state.tlc_state.received_tlcs.tlcs.iter_mut() { - if let TlcKindV2::AddTlc(tlc) = tlc { - let in_status = tlc.status.as_inbound_status(); - match in_status { - InboundTlctatus::AwaitingRemoteRevokeToAnnounce => { - tlc.status = TlcStatus::Inbound( - InboundTlctatus::AwaitingAnnouncedRemoteRevoke, - ); - } - InboundTlctatus::AwaitingAnnouncedRemoteRevoke => { - tlc.status = TlcStatus::Inbound(InboundTlctatus::Committed); - } - _ => {} - } - } - } + state.tlc_state.handle_reovke_and_ack(); } } Ok(()) From 0eac14911dffabc650f1e2e8e656bf1b33e7c63d Mon Sep 17 00:00:00 2001 From: yukang Date: Mon, 23 Dec 2024 12:09:34 +0800 Subject: [PATCH 016/119] add remove tlc --- src/fiber/channel.rs | 54 +++++++++++++++++++++++++++++++++- src/fiber/tests/tlc_op.rs | 61 ++++++++++++++++++++++++++++++++++----- 2 files changed, 107 insertions(+), 8 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index d72e4d8ee..5586d0a90 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -2590,6 +2590,38 @@ impl TlcStateV2 { self.received_tlcs.add_tlc(tlc); } + pub fn set_received_tlc_removed(&mut self, tlc_id: u64) { + for tlc in self.received_tlcs.iter_mut() { + if let TlcKindV2::AddTlc(add_tlc) = tlc { + if Into::::into(add_tlc.tlc_id) == tlc_id { + add_tlc.removed_at = Some(( + add_tlc.created_at, + RemoveTlcReason::RemoveTlcFulfill(RemoveTlcFulfill { + payment_preimage: Default::default(), + }), + )); + add_tlc.status = TlcStatus::Inbound(InboundTlctatus::LocalRemoved); + } + } + } + } + + pub fn set_offered_tlc_removed(&mut self, tlc_id: u64) { + for tlc in self.offered_tlcs.iter_mut() { + if let TlcKindV2::AddTlc(add_tlc) = tlc { + if Into::::into(add_tlc.tlc_id) == tlc_id { + add_tlc.removed_at = Some(( + add_tlc.created_at, + RemoveTlcReason::RemoveTlcFulfill(RemoveTlcFulfill { + payment_preimage: Default::default(), + }), + )); + add_tlc.status = TlcStatus::Outbound(OutboundTlcStatus::RemoteRemoved); + } + } + } + } + pub fn commitment_signed(&mut self, local: bool) -> Vec { let mut active_tls = vec![]; for tlc in self.offered_tlcs.tlcs.iter() { @@ -2629,6 +2661,26 @@ impl TlcStateV2 { } pub fn update_for_peer_commitment_signed(&mut self) { + for tlc in self.offered_tlcs.tlcs.iter_mut() { + if let TlcKindV2::AddTlc(tlc) = tlc { + let out_status = tlc.status.as_outbound_status(); + match out_status { + OutboundTlcStatus::RemoteRemoved => { + let status = if self.waiting_ack { + OutboundTlcStatus::AwaitingRemoteRevokeToRemove + } else { + OutboundTlcStatus::AwaitingRemovedRemoteRevoke + }; + tlc.status = TlcStatus::Outbound(status); + } + OutboundTlcStatus::AwaitingRemoteRevokeToRemove => { + tlc.status = + TlcStatus::Outbound(OutboundTlcStatus::AwaitingRemovedRemoteRevoke); + } + _ => {} + } + } + } for tlc in self.received_tlcs.tlcs.iter_mut() { if let TlcKindV2::AddTlc(tlc) = tlc { let out_status = tlc.status.as_inbound_status(); @@ -2662,7 +2714,7 @@ impl TlcStateV2 { return active_tls; } - pub fn handle_reovke_and_ack(&mut self) { + pub fn update_for_revoke_and_ack(&mut self) { self.set_waiting_ack(false); for tlc in self.offered_tlcs.tlcs.iter_mut() { if let TlcKindV2::AddTlc(tlc) = tlc { diff --git a/src/fiber/tests/tlc_op.rs b/src/fiber/tests/tlc_op.rs index a04c65591..567753fc9 100644 --- a/src/fiber/tests/tlc_op.rs +++ b/src/fiber/tests/tlc_op.rs @@ -102,8 +102,9 @@ pub struct NetworkActor {} pub enum TlcActorMessage { Debug, CommandAddTlc(AddTlcCommand), - //CommandRemoveTlc, + CommandRemoveTlc(u64), PeerAddTlc(AddTlcInfoV2), + PeerRemoveTlc(u64), PeerCommitmentSigned(Hash256), PeerRevokeAndAck(Hash256), //PeerRemoveTlc, @@ -111,8 +112,9 @@ pub enum TlcActorMessage { #[derive(Debug)] pub enum NetworkActorMessage { - AddPeer(String), + RegisterPeer(String), AddTlc(String, AddTlcCommand), + RemoveTlc(String, u64), PeerMsg(String, TlcActorMessage), } @@ -129,7 +131,7 @@ impl Actor for NetworkActor { state: &mut Self::State, ) -> Result<(), ActorProcessingErr> { match message { - NetworkActorMessage::AddPeer(peer_id) => { + NetworkActorMessage::RegisterPeer(peer_id) => { state.add_peer(peer_id).await; } NetworkActorMessage::AddTlc(peer_id, add_tlc) => { @@ -140,6 +142,13 @@ impl Actor for NetworkActor { .expect("send ok"); } } + NetworkActorMessage::RemoveTlc(peer_id, tlc_id) => { + if let Some(actor) = state.peers.get(&peer_id) { + actor + .send_message(TlcActorMessage::CommandRemoveTlc(tlc_id)) + .expect("send ok"); + } + } NetworkActorMessage::PeerMsg(peer_id, peer_msg) => { if let Some(actor) = state.peers.get(&peer_id) { eprintln!("NetworkActorMessage::PeerMsg: {:?}", peer_msg); @@ -229,14 +238,46 @@ impl Actor for TlcActor { )) .expect("send ok"); } + TlcActorMessage::CommandRemoveTlc(tlc_id) => { + eprintln!("Peer {} process remove tlc ....", state.peer_id); + state.tlc_state.set_received_tlc_removed(tlc_id); + let peer = state.get_peer(); + self.network + .send_message(NetworkActorMessage::PeerMsg( + peer.clone(), + TlcActorMessage::PeerRemoveTlc(tlc_id), + )) + .expect("send ok"); + + // send commitment signed + let tlcs = state.tlc_state.commitment_signed(false); + let hash = sign_tlcs(&tlcs); + eprintln!("got hash: {:?}", hash); + self.network + .send_message(NetworkActorMessage::PeerMsg( + peer, + TlcActorMessage::PeerCommitmentSigned(hash), + )) + .expect("send ok"); + } TlcActorMessage::PeerAddTlc(add_tlc) => { - eprintln!("Peer {} process peer add_tlc ....", state.peer_id); + eprintln!( + "Peer {} process peer add_tlc .... with tlc_id: {:?}", + state.peer_id, add_tlc.tlc_id + ); let mut tlc = add_tlc.clone(); tlc.flip_mut(); tlc.status = TlcStatus::Inbound(InboundTlctatus::RemoteAnnounced); state.tlc_state.add_received_tlc(TlcKindV2::AddTlc(tlc)); eprintln!("add peer tlc successfully: {:?}", add_tlc); } + TlcActorMessage::PeerRemoveTlc(tlc_id) => { + eprintln!( + "Peer {} process peer remove tlc .... with tlc_id: {}", + state.peer_id, tlc_id + ); + state.tlc_state.set_offered_tlc_removed(tlc_id); + } TlcActorMessage::PeerCommitmentSigned(peer_hash) => { eprintln!( "\nPeer {} processed peer commitment_signed ....", @@ -279,7 +320,7 @@ impl Actor for TlcActor { let hash = sign_tlcs(&tlcs); assert_eq!(hash, peer_hash); - state.tlc_state.handle_reovke_and_ack(); + state.tlc_state.update_for_revoke_and_ack(); } } Ok(()) @@ -309,10 +350,10 @@ async fn test_tlc_actor() { .await .expect("Failed to start tlc actor"); network_actor - .send_message(NetworkActorMessage::AddPeer("peer_a".to_string())) + .send_message(NetworkActorMessage::RegisterPeer("peer_a".to_string())) .unwrap(); network_actor - .send_message(NetworkActorMessage::AddPeer("peer_b".to_string())) + .send_message(NetworkActorMessage::RegisterPeer("peer_b".to_string())) .unwrap(); network_actor @@ -378,6 +419,12 @@ async fn test_tlc_actor() { )) .unwrap(); + tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + // remove tlc from peer_b + network_actor + .send_message(NetworkActorMessage::RemoveTlc("peer_b".to_string(), 0)) + .unwrap(); + tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; network_actor .send_message(NetworkActorMessage::PeerMsg( From d5b7ddedb47443ae64ccef89e36394f5810a62d5 Mon Sep 17 00:00:00 2001 From: yukang Date: Mon, 23 Dec 2024 16:11:54 +0800 Subject: [PATCH 017/119] begin new tlc operation --- src/fiber/channel.rs | 1392 +++++++++++++++--------------------- src/fiber/tests/channel.rs | 433 ++++++----- src/fiber/tests/tlc_op.rs | 53 +- 3 files changed, 811 insertions(+), 1067 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 5586d0a90..ce6f74fe3 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -67,7 +67,6 @@ use thiserror::Error; use tokio::sync::oneshot; use std::{ - collections::{BTreeMap, HashMap}, fmt::Debug, sync::Arc, time::{SystemTime, UNIX_EPOCH}, @@ -123,7 +122,7 @@ pub struct AddTlcResponse { #[derive(Clone)] pub struct TlcNotification { pub channel_id: Hash256, - pub tlc: AddTlcInfo, + pub tlc: TlcInfo, pub script: Script, } @@ -650,77 +649,78 @@ where async fn handle_commitment_signed_peer_message( &self, - myself: &ActorRef, + _myself: &ActorRef, state: &mut ChannelActorState, commitment_signed: CommitmentSigned, ) -> Result<(), ProcessingChannelError> { // build commitment tx and verify signature from remote, if passed send ACK for partner state.verify_commitment_signed_and_send_ack(commitment_signed, &self.network)?; - self.flush_staging_tlc_operations(myself, state).await; + state.tlc_state.update_for_peer_commitment_signed(); + //self.flush_staging_tlc_operations(myself, state).await; Ok(()) } - async fn flush_staging_tlc_operations( - &self, - myself: &ActorRef, - state: &mut ChannelActorState, - ) { - let pending_apply_tlcs = state.tlc_state.commit_remote_tlcs(); - for tlc_info in pending_apply_tlcs { - match tlc_info { - TlcKind::AddTlc(add_tlc) => { - assert!(add_tlc.is_received()); - if let Err(e) = self.apply_add_tlc_operation(myself, state, &add_tlc).await { - let tlc_err = match e.source { - // If we already have TlcErr, we can directly use it to send back to the peer. - ProcessingChannelError::TlcForwardingError(tlc_err) => tlc_err, - _ => { - let error_detail = self.get_tlc_error(state, &e.source).await; - #[cfg(debug_assertions)] - self.network - .clone() - .send_message(NetworkActorMessage::new_notification( - NetworkServiceEvent::DebugEvent(DebugEvent::AddTlcFailed( - state.get_local_peer_id(), - add_tlc.payment_hash, - error_detail.clone(), - )), - )) - .expect(ASSUME_NETWORK_ACTOR_ALIVE); - error_detail - } - }; - let error_packet = TlcErrPacket::new( - tlc_err, - // There's no shared secret stored in the received TLC, use the one found in the peeled onion packet. - &e.shared_secret, - ); - self.register_retryable_tlc_remove( - myself, - state, - add_tlc.tlc_id, - RemoveTlcReason::RemoveTlcFail(error_packet), - ) - .await; - } - } - TlcKind::RemoveTlc(remove_tlc) => { - let _ = self - .apply_remove_tlc_operation(myself, state, remove_tlc) - .await - .map_err(|e| { - error!("Error handling apply_remove_tlc_operation: {:?}", e); - }); - } - } - } - } + // async fn flush_staging_tlc_operations( + // &self, + // myself: &ActorRef, + // state: &mut ChannelActorState, + // ) { + // let pending_apply_tlcs = state.tlc_state.commit_remote_tlcs(); + // for tlc_info in pending_apply_tlcs { + // match tlc_info { + // TlcKind::AddTlc(add_tlc) => { + // assert!(add_tlc.is_received()); + // if let Err(e) = self.apply_add_tlc_operation(myself, state, &add_tlc).await { + // let tlc_err = match e.source { + // // If we already have TlcErr, we can directly use it to send back to the peer. + // ProcessingChannelError::TlcForwardingError(tlc_err) => tlc_err, + // _ => { + // let error_detail = self.get_tlc_error(state, &e.source).await; + // #[cfg(debug_assertions)] + // self.network + // .clone() + // .send_message(NetworkActorMessage::new_notification( + // NetworkServiceEvent::DebugEvent(DebugEvent::AddTlcFailed( + // state.get_local_peer_id(), + // add_tlc.payment_hash, + // error_detail.clone(), + // )), + // )) + // .expect(ASSUME_NETWORK_ACTOR_ALIVE); + // error_detail + // } + // }; + // let error_packet = TlcErrPacket::new( + // tlc_err, + // // There's no shared secret stored in the received TLC, use the one found in the peeled onion packet. + // &e.shared_secret, + // ); + // self.register_retryable_tlc_remove( + // myself, + // state, + // add_tlc.tlc_id, + // RemoveTlcReason::RemoveTlcFail(error_packet), + // ) + // .await; + // } + // } + // TlcKind::RemoveTlc(remove_tlc) => { + // let _ = self + // .apply_remove_tlc_operation(myself, state, remove_tlc) + // .await + // .map_err(|e| { + // error!("Error handling apply_remove_tlc_operation: {:?}", e); + // }); + // } + // } + // } + // } async fn try_to_relay_remove_tlc( &self, myself: &ActorRef, state: &mut ChannelActorState, - tlc_info: &AddTlcInfo, + tlc_info: &TlcInfo, remove_reason: RemoveTlcReason, ) { assert!(tlc_info.is_offered()); @@ -798,7 +798,7 @@ where &self, myself: &ActorRef, state: &mut ChannelActorState, - add_tlc: &AddTlcInfo, + add_tlc: &TlcInfo, ) -> Result<(), ProcessingChannelErrorWithSharedSecret> { // If needed, shared secret also get be extracted from the encrypted onion packet: // - Extract public key from onion_packet[1..34] @@ -841,7 +841,7 @@ where async fn try_add_tlc_peel_onion_packet( &self, state: &mut ChannelActorState, - add_tlc: &AddTlcInfo, + add_tlc: &TlcInfo, ) -> Result, ProcessingChannelError> { state.check_tlc_expiry(add_tlc.expiry)?; @@ -859,7 +859,7 @@ where async fn apply_add_tlc_operation_with_peeled_onion_packet( &self, state: &mut ChannelActorState, - add_tlc: &AddTlcInfo, + add_tlc: &TlcInfo, peeled_onion_packet: PeeledPaymentOnionPacket, ) -> Result<(), ProcessingChannelError> { let payment_hash = add_tlc.payment_hash; @@ -964,9 +964,7 @@ where state.check_for_tlc_update(Some(add_tlc.amount), false, false)?; let tlc_info = state.create_inbounding_tlc(add_tlc.clone())?; state.check_insert_tlc(&tlc_info)?; - state - .tlc_state - .add_remote_tlc(TlcKind::AddTlc(tlc_info.clone())); + state.tlc_state.add_received_tlc(tlc_info); state.increment_next_received_tlc_id(); Ok(()) } @@ -981,59 +979,58 @@ where // maybe we need to go through shutdown process for this error state .check_remove_tlc_with_reason(TLCId::Offered(remove_tlc.tlc_id), &remove_tlc.reason)?; - let tlc_kind = TlcKind::RemoveTlc(RemoveTlcInfo { - tlc_id: TLCId::Offered(remove_tlc.tlc_id), - channel_id: remove_tlc.channel_id, - reason: remove_tlc.reason.clone(), - }); - state.tlc_state.add_remote_tlc(tlc_kind.clone()); + state.tlc_state.set_offered_tlc_removed( + remove_tlc.tlc_id, + state.get_current_commitment_numbers(), + remove_tlc.reason, + ); Ok(()) } - async fn apply_remove_tlc_operation( - &self, - myself: &ActorRef, - state: &mut ChannelActorState, - remove_tlc: RemoveTlcInfo, - ) -> Result<(), ProcessingChannelError> { - let channel_id = state.get_id(); - let remove_reason = remove_tlc.reason.clone(); - let tlc_info = state - .remove_tlc_with_reason(remove_tlc.tlc_id, &remove_reason) - .expect("expect remove tlc successfully"); - if let ( - Some(ref udt_type_script), - RemoveTlcReason::RemoveTlcFulfill(RemoveTlcFulfill { payment_preimage }), - ) = (state.funding_udt_type_script.clone(), &remove_reason) - { - let mut tlc = tlc_info.clone(); - tlc.payment_preimage = Some(*payment_preimage); - self.subscribers - .settled_tlcs_subscribers - .send(TlcNotification { - tlc, - channel_id, - script: udt_type_script.clone(), - }); - } - if tlc_info.previous_tlc.is_none() { - // only the original sender of the TLC should send `TlcRemoveReceived` event - // because only the original sender cares about the TLC event to settle the payment - self.network - .send_message(NetworkActorMessage::new_event( - NetworkActorEvent::TlcRemoveReceived( - tlc_info.payment_hash, - remove_reason.clone(), - ), - )) - .expect("myself alive"); - } else { - // relay RemoveTlc to previous channel if needed - self.try_to_relay_remove_tlc(myself, state, &tlc_info, remove_reason) - .await; - } - Ok(()) - } + // async fn apply_remove_tlc_operation( + // &self, + // myself: &ActorRef, + // state: &mut ChannelActorState, + // remove_tlc: RemoveTlcInfo, + // ) -> Result<(), ProcessingChannelError> { + // let channel_id = state.get_id(); + // let remove_reason = remove_tlc.reason.clone(); + // let tlc_info = state + // .remove_tlc_with_reason(remove_tlc.tlc_id, &remove_reason) + // .expect("expect remove tlc successfully"); + // if let ( + // Some(ref udt_type_script), + // RemoveTlcReason::RemoveTlcFulfill(RemoveTlcFulfill { payment_preimage }), + // ) = (state.funding_udt_type_script.clone(), &remove_reason) + // { + // let mut tlc = tlc_info.clone(); + // tlc.payment_preimage = Some(*payment_preimage); + // self.subscribers + // .settled_tlcs_subscribers + // .send(TlcNotification { + // tlc, + // channel_id, + // script: udt_type_script.clone(), + // }); + // } + // if tlc_info.previous_tlc.is_none() { + // // only the original sender of the TLC should send `TlcRemoveReceived` event + // // because only the original sender cares about the TLC event to settle the payment + // self.network + // .send_message(NetworkActorMessage::new_event( + // NetworkActorEvent::TlcRemoveReceived( + // tlc_info.payment_hash, + // remove_reason.clone(), + // ), + // )) + // .expect("myself alive"); + // } else { + // // relay RemoveTlc to previous channel if needed + // self.try_to_relay_remove_tlc(myself, state, &tlc_info, remove_reason) + // .await; + // } + // Ok(()) + // } async fn handle_forward_onion_packet( &self, @@ -1150,7 +1147,7 @@ where state.check_tlc_expiry(command.expiry)?; let tlc = state.create_outbounding_tlc(command.clone()); state.check_insert_tlc(&tlc)?; - state.tlc_state.add_local_tlc(TlcKind::AddTlc(tlc.clone())); + state.tlc_state.add_offered_tlc(tlc.clone()); state.increment_next_offered_tlc_id(); let add_tlc = AddTlc { @@ -1185,12 +1182,11 @@ where ) -> ProcessingChannelResult { state.check_for_tlc_update(None, true, false)?; state.check_remove_tlc_with_reason(TLCId::Received(command.id), &command.reason)?; - let tlc_kind = TlcKind::RemoveTlc(RemoveTlcInfo { - channel_id: state.get_id(), - tlc_id: TLCId::Received(command.id), - reason: command.reason.clone(), - }); - state.tlc_state.add_local_tlc(tlc_kind.clone()); + state.tlc_state.set_received_tlc_removed( + command.id, + state.get_current_commitment_numbers(), + command.reason.clone(), + ); let msg = FiberMessageWithPeerId::new( state.get_remote_peer_id(), FiberMessage::remove_tlc(RemoveTlc { @@ -2204,49 +2200,49 @@ impl TlcStatus { } } -#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -pub enum TlcKindV2 { - AddTlc(AddTlcInfoV2), - RemoveTlc(RemoveTlcInfo), -} - -impl TlcKindV2 { - pub fn log(&self) -> String { - match self { - TlcKindV2::AddTlc(add_tlc) => { - format!( - "id: {:?} amount: {:?}, status: {:?}", - &add_tlc.tlc_id, &add_tlc.amount, &add_tlc.status - ) - } - TlcKindV2::RemoveTlc(remove_tlc) => { - format!("RemoveTlc({:?})", &remove_tlc.tlc_id) - } - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -pub enum TlcKind { - AddTlc(AddTlcInfo), - RemoveTlc(RemoveTlcInfo), -} - -impl TlcKind { - pub fn log(&self) -> String { - match self { - TlcKind::AddTlc(add_tlc) => { - format!("{:?}", &add_tlc.tlc_id) - } - TlcKind::RemoveTlc(remove_tlc) => { - format!("RemoveTlc({:?})", &remove_tlc.tlc_id) - } - } - } -} +// #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +// pub enum TlcKindV2 { +// AddTlc(TlcInfo), +// RemoveTlc(RemoveTlcInfo), +// } + +// impl TlcKindV2 { +// pub fn log(&self) -> String { +// match self { +// TlcKindV2::AddTlc(add_tlc) => { +// format!( +// "id: {:?} amount: {:?}, status: {:?}", +// &add_tlc.tlc_id, &add_tlc.amount, &add_tlc.status +// ) +// } +// TlcKindV2::RemoveTlc(remove_tlc) => { +// format!("RemoveTlc({:?})", &remove_tlc.tlc_id) +// } +// } +// } +// } + +// #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +// pub enum TlcKind { +// AddTlc(AddTlcInfo), +// RemoveTlc(RemoveTlcInfo), +// } + +// impl TlcKind { +// pub fn log(&self) -> String { +// match self { +// TlcKind::AddTlc(add_tlc) => { +// format!("{:?}", &add_tlc.tlc_id) +// } +// TlcKind::RemoveTlc(remove_tlc) => { +// format!("RemoveTlc({:?})", &remove_tlc.tlc_id) +// } +// } +// } +// } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -pub struct AddTlcInfoV2 { +pub struct TlcInfo { pub channel_id: Hash256, pub status: TlcStatus, pub tlc_id: TLCId, @@ -2274,63 +2270,11 @@ pub struct AddTlcInfoV2 { pub previous_tlc: Option<(Hash256, TLCId)>, } -impl AddTlcInfoV2 { - pub fn is_offered(&self) -> bool { - self.tlc_id.is_offered() - } - - pub fn is_received(&self) -> bool { - !self.is_offered() - } - - pub fn get_commitment_numbers(&self) -> CommitmentNumbers { - self.created_at - } - - pub fn flip_mut(&mut self) { - self.tlc_id.flip_mut(); - } - - /// Get the value for the field `htlc_type` in commitment lock witness. - /// - Lowest 1 bit: 0 if the tlc is offered by the remote party, 1 otherwise. - /// - High 7 bits: - /// - 0: ckb hash - /// - 1: sha256 - pub fn get_htlc_type(&self) -> u8 { - let offered_flag = if self.is_offered() { 0u8 } else { 1u8 }; - ((self.hash_algorithm as u8) << 1) + offered_flag +impl TlcInfo { + pub fn log(&self) -> String { + format!("id: {:?} status: {:?}", &self.tlc_id, self.status) } -} -#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -pub struct AddTlcInfo { - pub channel_id: Hash256, - pub tlc_id: TLCId, - pub amount: u128, - pub payment_hash: Hash256, - pub expiry: u64, - pub hash_algorithm: HashAlgorithm, - // the onion packet for multi-hop payment - pub onion_packet: Option, - /// Shared secret used in forwarding. - /// - /// Save it to backward errors. Use all zeros when no shared secrets are available. - pub shared_secret: [u8; 32], - pub created_at: CommitmentNumbers, - pub removed_at: Option<(CommitmentNumbers, RemoveTlcReason)>, - pub payment_preimage: Option, - - /// Note: `previous_tlc` is used to track the tlc chain for a multi-tlc payment, - /// we need to know previous when removing tlc backwardly. - /// - /// Node A ---------> Node B ------------> Node C ----------> Node D - /// tlc_1 <---> (tlc_1) (tlc_2) <---> (tlc_2) (tlc_3) <----> tlc_3 - /// ^^^^ ^^^^ - /// - pub previous_tlc: Option<(Hash256, TLCId)>, -} - -impl AddTlcInfo { pub fn is_offered(&self) -> bool { self.tlc_id.is_offered() } @@ -2347,6 +2291,12 @@ impl AddTlcInfo { self.tlc_id.flip_mut(); } + fn get_hash(&self) -> ShortHash { + self.payment_hash.as_ref()[..20] + .try_into() + .expect("short hash from payment hash") + } + /// Get the value for the field `htlc_type` in commitment lock witness. /// - Lowest 1 bit: 0 if the tlc is offered by the remote party, 1 otherwise. /// - High 7 bits: @@ -2356,191 +2306,228 @@ impl AddTlcInfo { let offered_flag = if self.is_offered() { 0u8 } else { 1u8 }; ((self.hash_algorithm as u8) << 1) + offered_flag } - - fn get_hash(&self) -> ShortHash { - self.payment_hash.as_ref()[..20] - .try_into() - .expect("short hash from payment hash") - } -} - -#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] -pub struct RemoveTlcInfo { - pub channel_id: Hash256, - pub tlc_id: TLCId, - pub reason: RemoveTlcReason, } -impl TlcKind { - pub fn tlc_id_u64(&self) -> u64 { - match self { - TlcKind::AddTlc(add_tlc) => add_tlc.tlc_id.into(), - TlcKind::RemoveTlc(remove_tlc) => remove_tlc.tlc_id.into(), - } - } - - pub fn tlc_id(&self) -> TLCId { - match self { - TlcKind::AddTlc(info) => info.tlc_id, - TlcKind::RemoveTlc(remove_tlc) => remove_tlc.tlc_id, - } - } +// #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +// pub struct AddTlcInfo { +// pub channel_id: Hash256, +// pub tlc_id: TLCId, +// pub amount: u128, +// pub payment_hash: Hash256, +// pub expiry: u64, +// pub hash_algorithm: HashAlgorithm, +// // the onion packet for multi-hop payment +// pub onion_packet: Option, +// /// Shared secret used in forwarding. +// /// +// /// Save it to backward errors. Use all zeros when no shared secrets are available. +// pub shared_secret: [u8; 32], +// pub created_at: CommitmentNumbers, +// pub removed_at: Option<(CommitmentNumbers, RemoveTlcReason)>, +// pub payment_preimage: Option, + +// /// Note: `previous_tlc` is used to track the tlc chain for a multi-tlc payment, +// /// we need to know previous when removing tlc backwardly. +// /// +// /// Node A ---------> Node B ------------> Node C ----------> Node D +// /// tlc_1 <---> (tlc_1) (tlc_2) <---> (tlc_2) (tlc_3) <----> tlc_3 +// /// ^^^^ ^^^^ +// /// +// pub previous_tlc: Option<(Hash256, TLCId)>, +// } + +// impl AddTlcInfo { +// pub fn is_offered(&self) -> bool { +// self.tlc_id.is_offered() +// } + +// pub fn is_received(&self) -> bool { +// !self.is_offered() +// } + +// pub fn get_commitment_numbers(&self) -> CommitmentNumbers { +// self.created_at +// } + +// pub fn flip_mut(&mut self) { +// self.tlc_id.flip_mut(); +// } + +// /// Get the value for the field `htlc_type` in commitment lock witness. +// /// - Lowest 1 bit: 0 if the tlc is offered by the remote party, 1 otherwise. +// /// - High 7 bits: +// /// - 0: ckb hash +// /// - 1: sha256 +// pub fn get_htlc_type(&self) -> u8 { +// let offered_flag = if self.is_offered() { 0u8 } else { 1u8 }; +// ((self.hash_algorithm as u8) << 1) + offered_flag +// } + +// fn get_hash(&self) -> ShortHash { +// self.payment_hash.as_ref()[..20] +// .try_into() +// .expect("short hash from payment hash") +// } +// } + +// #[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +// pub struct RemoveTlcInfo { +// pub channel_id: Hash256, +// pub tlc_id: TLCId, +// pub reason: RemoveTlcReason, +// } + +// impl TlcKind { +// pub fn tlc_id_u64(&self) -> u64 { +// match self { +// TlcKind::AddTlc(add_tlc) => add_tlc.tlc_id.into(), +// TlcKind::RemoveTlc(remove_tlc) => remove_tlc.tlc_id.into(), +// } +// } + +// pub fn tlc_id(&self) -> TLCId { +// match self { +// TlcKind::AddTlc(info) => info.tlc_id, +// TlcKind::RemoveTlc(remove_tlc) => remove_tlc.tlc_id, +// } +// } + +// pub fn is_offered(&self) -> bool { +// self.tlc_id().is_offered() +// } + +// pub fn is_received(&self) -> bool { +// !self.is_offered() +// } +// } - pub fn is_offered(&self) -> bool { - self.tlc_id().is_offered() - } - - pub fn is_received(&self) -> bool { - !self.is_offered() - } +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +pub enum RetryableRemoveTlc { + RemoveTlc(TLCId, RemoveTlcReason), + RelayRemoveTlc(Hash256, u64, RemoveTlcReason), } -#[derive(Default, Clone, Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Default)] pub struct PendingTlcs { - tlcs: Vec, - committed_index: usize, - next_tlc_id: u64, + pub tlcs: Vec, + pub next_tlc_id: u64, } impl PendingTlcs { - pub fn new() -> Self { - Self { - tlcs: Vec::new(), - committed_index: 0, - next_tlc_id: 0, - } + pub fn iter_mut(&mut self) -> impl Iterator { + self.tlcs.iter_mut() } - pub fn next_tlc_id(&self) -> u64 { + pub fn get_next_id(&self) -> u64 { self.next_tlc_id } - pub fn increment_next_tlc_id(&mut self) { + pub fn increment_next_id(&mut self) { self.next_tlc_id += 1; } - pub fn add_tlc_operation(&mut self, tlc_op: TlcKind) { - self.tlcs.push(tlc_op); - } - - pub fn get_staging_tlcs(&self) -> &[TlcKind] { - &self.tlcs[self.committed_index..] - } - - pub fn get_committed_tlcs(&self) -> &[TlcKind] { - &self.tlcs[..self.committed_index] - } - - pub fn get_committed_tlcs_mut(&mut self) -> &mut [TlcKind] { - &mut self.tlcs[..self.committed_index] - } - - pub fn tlcs(&self) -> &[TlcKind] { - &self.tlcs - } - - pub fn tlcs_mut(&mut self) -> &mut Vec { - &mut self.tlcs - } - - pub fn push(&mut self, tlc: TlcKind) { - assert!(!self.is_tlc_present(&tlc)); + pub fn add_tlc(&mut self, tlc: TlcInfo) { self.tlcs.push(tlc); } - fn is_tlc_present(&self, tlc: &TlcKind) -> bool { - self.tlcs.iter().any(|t| match (t, tlc) { - (TlcKind::AddTlc(info1), TlcKind::AddTlc(info2)) => info1.tlc_id == info2.tlc_id, - (TlcKind::RemoveTlc(info1), TlcKind::RemoveTlc(info2)) => info1.tlc_id == info2.tlc_id, - _ => false, - }) - } - - pub fn commit_tlcs(&mut self, committed_tlcs: &[TlcKind]) -> Vec { - let staging_tlcs = self.get_staging_tlcs().to_vec(); - for tlc in committed_tlcs { - if !self.is_tlc_present(tlc) { - self.tlcs.push(tlc.clone()); - } - } - self.committed_index = self.tlcs.len(); - return staging_tlcs; - } - - pub fn get_mut(&mut self, tlc_id: &TLCId) -> Option<&mut AddTlcInfo> { - self.tlcs.iter_mut().find_map(|tlc| match tlc { - TlcKind::AddTlc(info) if info.tlc_id == *tlc_id => Some(info), - _ => None, - }) - } - - pub fn drop_remove_tlc(&mut self, tlc_id: &TLCId) { - self.tlcs.retain(|tlc| match tlc { - TlcKind::RemoveTlc(info) => info.tlc_id != *tlc_id, - _ => true, - }); - self.committed_index = self.tlcs.len(); - } - - pub fn shrink_removed_tlc(&mut self) { - assert_eq!(self.committed_index, self.tlcs.len()); - let new_committed_index = self - .get_committed_tlcs() + pub fn get_committed_tlcs(&self) -> Vec { + self.tlcs .iter() - .filter(|tlc| match tlc { - TlcKind::AddTlc(info) => info.removed_at.is_none(), - _ => true, + .filter(|tlc| { + if tlc.is_offered() { + match tlc.status.as_outbound_status() { + OutboundTlcStatus::Committed => true, + _ => false, + } + } else { + match tlc.status.as_inbound_status() { + InboundTlctatus::Committed => true, + _ => false, + } + } }) - .count(); - self.tlcs.retain(|tlc| match tlc { - TlcKind::AddTlc(info) => info.removed_at.is_none(), - _ => true, - }); - self.committed_index = new_committed_index; + .cloned() + .collect() } } -#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -pub enum RetryableRemoveTlc { - RemoveTlc(TLCId, RemoveTlcReason), - RelayRemoveTlc(Hash256, u64, RemoveTlcReason), +#[derive(Default, Clone, Debug, Serialize, Deserialize)] +pub struct TlcState { + pub offered_tlcs: PendingTlcs, + pub received_tlcs: PendingTlcs, + // if the tlc is pending to be removed, the reason will be stored here + // this will only used for retrying remove TLC + pub retryable_remove_tlcs: Vec, + pub waiting_ack: bool, } -#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Default)] -pub struct PendingTlcsV2 { - pub tlcs: Vec, - pub next_tlc_id: u64, -} +impl TlcState { + pub fn get_mut(&mut self, tlc_id: &TLCId) -> Option<&mut TlcInfo> { + self.offered_tlcs + .tlcs + .iter_mut() + .find(|tlc| tlc.tlc_id == *tlc_id) + .or_else(|| { + self.received_tlcs + .tlcs + .iter_mut() + .find(|tlc| tlc.tlc_id == *tlc_id) + }) + } -impl PendingTlcsV2 { - pub fn iter_mut(&mut self) -> impl Iterator { - self.tlcs.iter_mut() + pub fn get(&self, tlc_id: &TLCId) -> Option<&TlcInfo> { + self.offered_tlcs + .tlcs + .iter() + .find(|tlc| tlc.tlc_id == *tlc_id) + .or_else(|| { + self.received_tlcs + .tlcs + .iter() + .find(|tlc| tlc.tlc_id == *tlc_id) + }) } - pub fn get_next_id(&self) -> u64 { - self.next_tlc_id + pub fn get_local_active_offered_tlcs(&self) -> impl Iterator + '_ { + self.offered_tlcs.tlcs.iter().filter_map(move |tlc| { + if tlc.removed_at.is_none() { + Some(tlc.clone()) + } else { + None + } + }) } - pub fn increment_next_id(&mut self) { - self.next_tlc_id += 1; + pub fn get_remote_active_offered_tlcs(&self) -> impl Iterator + '_ { + self.received_tlcs.tlcs.iter().filter_map(move |tlc| { + if tlc.removed_at.is_none() { + Some(tlc.clone()) + } else { + None + } + }) } - pub fn add_tlc(&mut self, tlc: TlcKindV2) { - self.tlcs.push(tlc); + pub fn get_active_received_tlcs(&self) -> impl Iterator + '_ { + self.received_tlcs.tlcs.iter().filter_map(move |tlc| { + if tlc.removed_at.is_none() { + Some(tlc.clone()) + } else { + None + } + }) } -} -#[derive(Default, Clone, Debug, Serialize, Deserialize)] -pub struct TlcStateV2 { - pub offered_tlcs: PendingTlcsV2, - pub received_tlcs: PendingTlcsV2, - // if the tlc is pending to be removed, the reason will be stored here - // this will only used for retrying remove TLC - pub retryable_remove_tlcs: Vec, - pub waiting_ack: bool, -} + pub fn get_remote_received_tlcs(&self) -> impl Iterator + '_ { + self.offered_tlcs.tlcs.iter().filter_map(move |tlc| { + if tlc.removed_at.is_none() { + Some(tlc.clone()) + } else { + None + } + }) + } -impl TlcStateV2 { pub fn get_next_offering(&self) -> u64 { self.offered_tlcs.get_next_id() } @@ -2561,6 +2548,62 @@ impl TlcStateV2 { self.waiting_ack = waiting_ack; } + pub fn set_tlc_pending_remove(&mut self, tlc_id: TLCId, reason: RemoveTlcReason) { + self.retryable_remove_tlcs + .push(RetryableRemoveTlc::RemoveTlc(tlc_id, reason)); + } + + pub fn all_tlcs(&self) -> impl Iterator + '_ { + self.offered_tlcs + .tlcs + .iter() + .chain(self.received_tlcs.tlcs.iter()) + } + + pub fn all_commited_tlcs(&self) -> impl Iterator + '_ { + self.offered_tlcs + .tlcs + .iter() + .chain(self.received_tlcs.tlcs.iter()) + .filter(|tlc| { + if tlc.is_offered() { + match tlc.status.as_outbound_status() { + OutboundTlcStatus::Committed => true, + _ => false, + } + } else { + match tlc.status.as_inbound_status() { + InboundTlctatus::Committed => true, + _ => false, + } + } + }) + } + + pub fn apply_remove_tlc( + &mut self, + tlc_id: TLCId, + remove_at: CommitmentNumbers, + reason: RemoveTlcReason, + ) { + self.offered_tlcs + .tlcs + .iter_mut() + .find(|tlc| tlc.tlc_id == tlc_id) + .map(|tlc| { + tlc.removed_at = Some((remove_at, reason.clone())); + tlc.status = TlcStatus::Outbound(OutboundTlcStatus::RemoteRemoved); + }); + self.received_tlcs + .tlcs + .iter_mut() + .find(|tlc| tlc.tlc_id == tlc_id) + .map(|tlc| { + tlc.removed_at = Some((remove_at, reason.clone())); + tlc.status = TlcStatus::Inbound(InboundTlctatus::LocalRemoved); + }); + } + pub fn insert_relay_tlc_remove( &mut self, channel_id: Hash256, @@ -2582,76 +2625,68 @@ impl TlcStateV2 { .retain(|remove| remove != retryable_remove); } - pub fn add_offered_tlc(&mut self, tlc: TlcKindV2) { + pub fn add_offered_tlc(&mut self, tlc: TlcInfo) { self.offered_tlcs.add_tlc(tlc); } - pub fn add_received_tlc(&mut self, tlc: TlcKindV2) { + pub fn add_received_tlc(&mut self, tlc: TlcInfo) { self.received_tlcs.add_tlc(tlc); } - pub fn set_received_tlc_removed(&mut self, tlc_id: u64) { + pub fn set_received_tlc_removed( + &mut self, + tlc_id: u64, + remove_at: CommitmentNumbers, + reason: RemoveTlcReason, + ) { for tlc in self.received_tlcs.iter_mut() { - if let TlcKindV2::AddTlc(add_tlc) = tlc { - if Into::::into(add_tlc.tlc_id) == tlc_id { - add_tlc.removed_at = Some(( - add_tlc.created_at, - RemoveTlcReason::RemoveTlcFulfill(RemoveTlcFulfill { - payment_preimage: Default::default(), - }), - )); - add_tlc.status = TlcStatus::Inbound(InboundTlctatus::LocalRemoved); - } + if Into::::into(tlc.tlc_id) == tlc_id { + tlc.removed_at = Some((remove_at, reason.clone())); + tlc.status = TlcStatus::Inbound(InboundTlctatus::LocalRemoved); } } } - pub fn set_offered_tlc_removed(&mut self, tlc_id: u64) { + pub fn set_offered_tlc_removed( + &mut self, + tlc_id: u64, + remove_at: CommitmentNumbers, + reason: RemoveTlcReason, + ) { for tlc in self.offered_tlcs.iter_mut() { - if let TlcKindV2::AddTlc(add_tlc) = tlc { - if Into::::into(add_tlc.tlc_id) == tlc_id { - add_tlc.removed_at = Some(( - add_tlc.created_at, - RemoveTlcReason::RemoveTlcFulfill(RemoveTlcFulfill { - payment_preimage: Default::default(), - }), - )); - add_tlc.status = TlcStatus::Outbound(OutboundTlcStatus::RemoteRemoved); - } + if Into::::into(tlc.tlc_id) == tlc_id { + tlc.removed_at = Some((remove_at, reason.clone())); + tlc.status = TlcStatus::Outbound(OutboundTlcStatus::RemoteRemoved); } } } - pub fn commitment_signed(&mut self, local: bool) -> Vec { + pub fn commitment_signed(&mut self, local: bool) -> Vec { let mut active_tls = vec![]; for tlc in self.offered_tlcs.tlcs.iter() { - if let TlcKindV2::AddTlc(add_info) = tlc { - let status = add_info.status.as_outbound_status(); - let include = match status { - OutboundTlcStatus::LocalAnnounced => local, - OutboundTlcStatus::Committed => true, - OutboundTlcStatus::RemoteRemoved => local, - OutboundTlcStatus::AwaitingRemoteRevokeToRemove => local, - OutboundTlcStatus::AwaitingRemovedRemoteRevoke => false, - }; - if include { - active_tls.push(add_info.clone()); - } + let status = tlc.status.as_outbound_status(); + let include = match status { + OutboundTlcStatus::LocalAnnounced => local, + OutboundTlcStatus::Committed => true, + OutboundTlcStatus::RemoteRemoved => local, + OutboundTlcStatus::AwaitingRemoteRevokeToRemove => local, + OutboundTlcStatus::AwaitingRemovedRemoteRevoke => false, + }; + if include { + active_tls.push(tlc.clone()); } } for tlc in self.received_tlcs.tlcs.iter() { - if let TlcKindV2::AddTlc(add_info) = tlc { - let status = add_info.status.as_inbound_status(); - let include = match status { - InboundTlctatus::RemoteAnnounced => !local, - InboundTlctatus::AwaitingRemoteRevokeToAnnounce => !local, - InboundTlctatus::AwaitingAnnouncedRemoteRevoke => true, - InboundTlctatus::Committed => true, - InboundTlctatus::LocalRemoved => !local, - }; - if include { - active_tls.push(add_info.clone()); - } + let status = tlc.status.as_inbound_status(); + let include = match status { + InboundTlctatus::RemoteAnnounced => !local, + InboundTlctatus::AwaitingRemoteRevokeToAnnounce => !local, + InboundTlctatus::AwaitingAnnouncedRemoteRevoke => true, + InboundTlctatus::Committed => true, + InboundTlctatus::LocalRemoved => !local, + }; + if include { + active_tls.push(tlc.clone()); } } if !local { @@ -2662,54 +2697,46 @@ impl TlcStateV2 { pub fn update_for_peer_commitment_signed(&mut self) { for tlc in self.offered_tlcs.tlcs.iter_mut() { - if let TlcKindV2::AddTlc(tlc) = tlc { - let out_status = tlc.status.as_outbound_status(); - match out_status { - OutboundTlcStatus::RemoteRemoved => { - let status = if self.waiting_ack { - OutboundTlcStatus::AwaitingRemoteRevokeToRemove - } else { - OutboundTlcStatus::AwaitingRemovedRemoteRevoke - }; - tlc.status = TlcStatus::Outbound(status); - } - OutboundTlcStatus::AwaitingRemoteRevokeToRemove => { - tlc.status = - TlcStatus::Outbound(OutboundTlcStatus::AwaitingRemovedRemoteRevoke); - } - _ => {} + let out_status = tlc.status.as_outbound_status(); + match out_status { + OutboundTlcStatus::RemoteRemoved => { + let status = if self.waiting_ack { + OutboundTlcStatus::AwaitingRemoteRevokeToRemove + } else { + OutboundTlcStatus::AwaitingRemovedRemoteRevoke + }; + tlc.status = TlcStatus::Outbound(status); } + OutboundTlcStatus::AwaitingRemoteRevokeToRemove => { + tlc.status = + TlcStatus::Outbound(OutboundTlcStatus::AwaitingRemovedRemoteRevoke); + } + _ => {} } } for tlc in self.received_tlcs.tlcs.iter_mut() { - if let TlcKindV2::AddTlc(tlc) = tlc { - let out_status = tlc.status.as_inbound_status(); - match out_status { - InboundTlctatus::RemoteAnnounced => { - let status = if self.waiting_ack { - InboundTlctatus::AwaitingRemoteRevokeToAnnounce - } else { - InboundTlctatus::AwaitingAnnouncedRemoteRevoke - }; - tlc.status = TlcStatus::Inbound(status) - } - _ => {} + let out_status = tlc.status.as_inbound_status(); + match out_status { + InboundTlctatus::RemoteAnnounced => { + let status = if self.waiting_ack { + InboundTlctatus::AwaitingRemoteRevokeToAnnounce + } else { + InboundTlctatus::AwaitingAnnouncedRemoteRevoke + }; + tlc.status = TlcStatus::Inbound(status) } + _ => {} } } } - pub fn build_ack_transaction(&self, _for_remote: bool) -> Vec { + pub fn build_ack_transaction(&self, _for_remote: bool) -> Vec { let mut active_tls = vec![]; for tlc in self.offered_tlcs.tlcs.iter() { - if let TlcKindV2::AddTlc(add_info) = tlc { - active_tls.push(add_info.clone()); - } + active_tls.push(tlc.clone()); } for tlc in self.received_tlcs.tlcs.iter() { - if let TlcKindV2::AddTlc(add_info) = tlc { - active_tls.push(add_info.clone()); - } + active_tls.push(tlc.clone()); } return active_tls; } @@ -2717,321 +2744,46 @@ impl TlcStateV2 { pub fn update_for_revoke_and_ack(&mut self) { self.set_waiting_ack(false); for tlc in self.offered_tlcs.tlcs.iter_mut() { - if let TlcKindV2::AddTlc(tlc) = tlc { - let out_status = tlc.status.as_outbound_status(); - match out_status { - OutboundTlcStatus::LocalAnnounced => { - tlc.status = TlcStatus::Outbound(OutboundTlcStatus::Committed); - } - OutboundTlcStatus::AwaitingRemoteRevokeToRemove => { - tlc.status = - TlcStatus::Outbound(OutboundTlcStatus::AwaitingRemovedRemoteRevoke); - } - _ => {} + let out_status = tlc.status.as_outbound_status(); + match out_status { + OutboundTlcStatus::LocalAnnounced => { + tlc.status = TlcStatus::Outbound(OutboundTlcStatus::Committed); } + OutboundTlcStatus::AwaitingRemoteRevokeToRemove => { + tlc.status = + TlcStatus::Outbound(OutboundTlcStatus::AwaitingRemovedRemoteRevoke); + } + _ => {} } } for tlc in self.received_tlcs.tlcs.iter_mut() { - if let TlcKindV2::AddTlc(tlc) = tlc { - let in_status = tlc.status.as_inbound_status(); - match in_status { - InboundTlctatus::AwaitingRemoteRevokeToAnnounce => { - tlc.status = - TlcStatus::Inbound(InboundTlctatus::AwaitingAnnouncedRemoteRevoke); - } - InboundTlctatus::AwaitingAnnouncedRemoteRevoke => { - tlc.status = TlcStatus::Inbound(InboundTlctatus::Committed); - } - _ => {} + let in_status = tlc.status.as_inbound_status(); + match in_status { + InboundTlctatus::AwaitingRemoteRevokeToAnnounce => { + tlc.status = TlcStatus::Inbound(InboundTlctatus::AwaitingAnnouncedRemoteRevoke); + } + InboundTlctatus::AwaitingAnnouncedRemoteRevoke => { + tlc.status = TlcStatus::Inbound(InboundTlctatus::Committed); } + _ => {} } } } pub fn need_another_commitment_signed(&self) -> bool { self.offered_tlcs.tlcs.iter().any(|tlc| { - if let TlcKindV2::AddTlc(add_info) = tlc { - let status = add_info.status.as_outbound_status(); - matches!(status, OutboundTlcStatus::LocalAnnounced) - } else { - false - } + let status = tlc.status.as_outbound_status(); + matches!(status, OutboundTlcStatus::LocalAnnounced) }) || self.received_tlcs.tlcs.iter().any(|tlc| { - if let TlcKindV2::AddTlc(add_info) = tlc { - let status = add_info.status.as_inbound_status(); - matches!( - status, - InboundTlctatus::RemoteAnnounced - | InboundTlctatus::AwaitingRemoteRevokeToAnnounce - | InboundTlctatus::AwaitingAnnouncedRemoteRevoke - ) - } else { - false - } - }) - } -} - -#[derive(Default, Clone, Debug, Serialize, Deserialize)] -pub struct TlcState { - local_pending_tlcs: PendingTlcs, - remote_pending_tlcs: PendingTlcs, - // if the tlc is pending to be removed, the reason will be stored here - // this will only used for retrying remove TLC - retryable_remove_tlcs: Vec, - waiting_ack: bool, -} - -impl TlcState { - pub fn get_next_offering(&self) -> u64 { - self.local_pending_tlcs.next_tlc_id() - } - - pub fn get_next_received(&self) -> u64 { - self.remote_pending_tlcs.next_tlc_id() - } - - pub fn increment_offering(&mut self) { - self.local_pending_tlcs.increment_next_tlc_id(); - } - - pub fn increment_received(&mut self) { - self.remote_pending_tlcs.increment_next_tlc_id(); - } - - pub fn set_waiting_ack(&mut self, waiting_ack: bool) { - self.waiting_ack = waiting_ack; - } - - pub fn set_tlc_pending_remove(&mut self, tlc_id: TLCId, reason: RemoveTlcReason) { - self.retryable_remove_tlcs - .push(RetryableRemoveTlc::RemoveTlc(tlc_id, reason)); - } - - pub fn insert_relay_tlc_remove( - &mut self, - channel_id: Hash256, - tlc_id: u64, - reason: RemoveTlcReason, - ) { - self.retryable_remove_tlcs - .push(RetryableRemoveTlc::RelayRemoveTlc( - channel_id, tlc_id, reason, - )); - } - - pub fn get_pending_remove(&self) -> Vec { - self.retryable_remove_tlcs.clone() - } - - pub fn remove_pending_remove_tlc(&mut self, retryable_remove: &RetryableRemoveTlc) { - self.retryable_remove_tlcs - .retain(|remove| remove != retryable_remove); - } - - pub fn get(&self, id: &TLCId) -> Option<&AddTlcInfo> { - match id { - TLCId::Offered(_id) => { - self.local_pending_tlcs - .tlcs() - .iter() - .find_map(|tlc| match tlc { - TlcKind::AddTlc(info) if info.tlc_id == *id => Some(info), - _ => None, - }) - } - TLCId::Received(_id) => { - self.remote_pending_tlcs - .tlcs() - .iter() - .find_map(|tlc| match tlc { - TlcKind::AddTlc(info) if info.tlc_id == *id => Some(info), - _ => None, - }) - } - } - } - - pub fn get_mut(&mut self, id: &TLCId) -> Option<&mut AddTlcInfo> { - match id { - TLCId::Offered(_id) => { - self.local_pending_tlcs - .tlcs - .iter_mut() - .find_map(|tlc| match tlc { - TlcKind::AddTlc(info) if info.tlc_id == *id => Some(info), - _ => None, - }) - } - TLCId::Received(_id) => { - self.remote_pending_tlcs - .tlcs - .iter_mut() - .find_map(|tlc| match tlc { - TlcKind::AddTlc(info) if info.tlc_id == *id => Some(info), - _ => None, - }) - } - } - } - - pub fn add_local_tlc(&mut self, tlc_info: TlcKind) { - self.local_pending_tlcs.push(tlc_info); - } - - pub fn add_remote_tlc(&mut self, tlc_info: TlcKind) { - self.remote_pending_tlcs.push(tlc_info); - } - - fn unify_tlcs<'a>( - &self, - staging_tlcs: impl Iterator, - committed_tlcs: impl Iterator, - ) -> Vec { - let mut add_tlcs: BTreeMap = Default::default(); - let mut remove_tlcs = vec![]; - for tlc in committed_tlcs { - match tlc { - TlcKind::AddTlc(info) => { - if info.removed_at.is_none() { - let _ = add_tlcs.insert(tlc.tlc_id(), tlc.clone()); - } - } - TlcKind::RemoveTlc(..) => { - unreachable!("RemoveTlc should not be in committed tlcs") - } - } - } - for tlc in staging_tlcs { - match tlc { - TlcKind::AddTlc(_info) => { - // If the tlc is already in the committed tlcs, we should not add it again. - // committed tlcs always have the latest tlc information - if add_tlcs.contains_key(&tlc.tlc_id()) { - continue; - } - let _ = add_tlcs.insert(tlc.tlc_id(), tlc.clone()); - } - TlcKind::RemoveTlc(..) => remove_tlcs.push(tlc.clone()), - } - } - for tlc in remove_tlcs { - let tlc_id = tlc.tlc_id(); - let _ = add_tlcs.remove(&tlc_id); - } - add_tlcs.values().map(|tlc| tlc.clone()).collect() - } - - pub fn get_tlcs_for_local(&self) -> Vec { - self.unify_tlcs( - self.local_pending_tlcs.get_staging_tlcs().into_iter(), - self.local_pending_tlcs - .get_committed_tlcs() - .into_iter() - .chain(self.remote_pending_tlcs.get_committed_tlcs().into_iter()), - ) - } - - pub fn get_tlcs_for_remote(&self) -> Vec { - self.unify_tlcs( - self.remote_pending_tlcs.get_staging_tlcs().into_iter(), - self.remote_pending_tlcs - .get_committed_tlcs() - .into_iter() - .chain(self.local_pending_tlcs.get_committed_tlcs().into_iter()), - ) - } - - pub fn get_tlcs_with(&self, local_commitment: bool) -> Vec { - if local_commitment { - self.get_tlcs_for_local() - } else { - self.get_tlcs_for_remote() - } - } - - pub fn commit_local_tlcs(&mut self) -> Vec { - self.local_pending_tlcs - .commit_tlcs(self.remote_pending_tlcs.get_committed_tlcs()) - } - - pub fn commit_remote_tlcs(&mut self) -> Vec { - self.remote_pending_tlcs - .commit_tlcs(self.local_pending_tlcs.get_committed_tlcs()) - } - - fn filter_add_tlcs<'a, I>(tlcs: I) -> impl Iterator - where - I: Iterator, - { - tlcs.filter_map(|tlc| match tlc { - TlcKind::AddTlc(info) => { - if info.removed_at.is_some() { - None - } else { - Some((info.tlc_id, info)) - } - } - TlcKind::RemoveTlc(..) => None, + let status = tlc.status.as_inbound_status(); + matches!( + status, + InboundTlctatus::RemoteAnnounced + | InboundTlctatus::AwaitingRemoteRevokeToAnnounce + | InboundTlctatus::AwaitingAnnouncedRemoteRevoke + ) }) - .collect::>() - .into_iter() - .map(|(_, v)| v) - } - - pub fn all_commited_tlcs(&self) -> impl Iterator { - Self::filter_add_tlcs( - self.local_pending_tlcs - .get_committed_tlcs() - .into_iter() - .chain(self.remote_pending_tlcs.get_committed_tlcs().into_iter()), - ) - } - - pub fn all_tlcs(&self) -> impl Iterator { - Self::filter_add_tlcs( - self.local_pending_tlcs - .tlcs() - .into_iter() - .chain(self.remote_pending_tlcs.tlcs().into_iter()), - ) - } - - pub fn mark_tlc_remove( - &mut self, - tlc_id: TLCId, - removed_at: CommitmentNumbers, - reason: RemoveTlcReason, - ) { - // we don't consider RemoveTLC in the pending TLCS when build commitment signature - // so it's safe to remove them all from remote and local pending TLCS - self.local_pending_tlcs.drop_remove_tlc(&tlc_id); - self.remote_pending_tlcs.drop_remove_tlc(&tlc_id); - if let Some(tlc) = self.local_pending_tlcs.get_mut(&tlc_id) { - tlc.removed_at = Some((removed_at, reason.clone())); - } - if let Some(tlc) = self.remote_pending_tlcs.get_mut(&tlc_id) { - tlc.removed_at = Some((removed_at, reason)); - } - } - - pub fn apply_remove_tlc( - &mut self, - tlc_id: TLCId, - removed_at: CommitmentNumbers, - reason: RemoveTlcReason, - ) { - self.mark_tlc_remove(tlc_id, removed_at, reason); - // it's safe to remove multiple removed tlcs from pending TLCS, - // just make sure the two partners are operating on correct pending list, - // in other words, when one is remove from local TLCS, - // the peer should remove it from remote TLCS - if tlc_id.is_offered() { - self.local_pending_tlcs.shrink_removed_tlc(); - } else { - self.remote_pending_tlcs.shrink_removed_tlc(); - } } } @@ -4593,11 +4345,11 @@ impl ChannelActorState { self.tlc_state.increment_received(); } - pub fn get_offered_tlc(&self, tlc_id: u64) -> Option<&AddTlcInfo> { + pub fn get_offered_tlc(&self, tlc_id: u64) -> Option<&TlcInfo> { self.tlc_state.get(&TLCId::Offered(tlc_id)) } - pub fn get_received_tlc(&self, tlc_id: u64) -> Option<&AddTlcInfo> { + pub fn get_received_tlc(&self, tlc_id: u64) -> Option<&TlcInfo> { self.tlc_state.get(&TLCId::Received(tlc_id)) } @@ -4607,7 +4359,7 @@ impl ChannelActorState { } } - pub fn check_insert_tlc(&mut self, tlc: &AddTlcInfo) -> Result<(), ProcessingChannelError> { + pub fn check_insert_tlc(&mut self, tlc: &TlcInfo) -> Result<(), ProcessingChannelError> { let payment_hash = tlc.payment_hash; if let Some(tlc) = self .tlc_state @@ -4648,7 +4400,7 @@ impl ChannelActorState { &mut self, tlc_id: TLCId, reason: &RemoveTlcReason, - ) -> Result { + ) -> Result { let removed_at = self.get_current_commitment_numbers(); let current = self.tlc_state.get(&tlc_id).expect("TLC exists").clone(); @@ -4840,77 +4592,62 @@ impl ChannelActorState { AggNonce::sum(nonces) } - fn get_active_received_tlcs(&self, local_commitment: bool) -> impl Iterator { - self.tlc_state - .get_tlcs_with(local_commitment) - .into_iter() - .filter_map(|tlc| match tlc { - TlcKind::AddTlc(tlc) if tlc.is_received() => Some(tlc), - _ => None, - }) + fn get_active_received_tlcs(&self, local_commitment: bool) -> Vec { + if local_commitment { + self.tlc_state + .get_local_active_offered_tlcs() + .filter(|tlc| tlc.is_received()) + .collect() + } else { + self.tlc_state + .get_remote_active_offered_tlcs() + .filter(|tlc| tlc.is_received()) + .collect() + } } - fn get_active_offered_tlcs(&self, local_commitment: bool) -> impl Iterator { - self.tlc_state - .get_tlcs_with(local_commitment) - .into_iter() - .filter_map(|tlc| match tlc { - TlcKind::AddTlc(tlc) if tlc.is_offered() => Some(tlc), - _ => None, - }) + fn get_active_offered_tlcs(&self, local_commitment: bool) -> Vec { + if local_commitment { + self.tlc_state + .get_local_active_offered_tlcs() + .filter(|tlc| tlc.is_offered()) + .collect() + } else { + self.tlc_state + .get_remote_active_offered_tlcs() + .filter(|tlc| tlc.is_offered()) + .collect() + } } // Get the total amount of pending tlcs that are fulfilled - fn get_pending_fulfilled_tlcs_amount(&self, for_remote: bool, offered: bool) -> u128 { + fn get_pending_fulfilled_tlcs_amount(&self, for_remote: bool, _offered: bool) -> u128 { let (local_pending_tlcs, remote_pending_tlcs) = if for_remote { - ( - &self.tlc_state.local_pending_tlcs, - &self.tlc_state.remote_pending_tlcs, - ) + (&self.tlc_state.offered_tlcs, &self.tlc_state.received_tlcs) } else { - ( - &self.tlc_state.remote_pending_tlcs, - &self.tlc_state.local_pending_tlcs, - ) + (&self.tlc_state.received_tlcs, &self.tlc_state.offered_tlcs) }; - let mut pending = local_pending_tlcs + let mut fulfilled = 0; + local_pending_tlcs .get_committed_tlcs() .into_iter() .chain(remote_pending_tlcs.get_committed_tlcs().into_iter()) - .filter_map(|tlc| match tlc { - TlcKind::AddTlc(info) - if info.removed_at.is_none() && info.is_offered() == offered => - { - Some((tlc.tlc_id(), info.amount)) - } - _ => None, - }) - .collect::>(); - let mut fulfilled = 0; - - for tlc in local_pending_tlcs.get_staging_tlcs() { - match tlc { - TlcKind::AddTlc(info) => { - pending.insert(tlc.tlc_id(), info.amount); - } - TlcKind::RemoveTlc(remove_tlc) => { - if let Some(amount) = pending.remove(&remove_tlc.tlc_id) { - if matches!(remove_tlc.reason, RemoveTlcReason::RemoveTlcFulfill(_)) { - fulfilled += amount; - } + .for_each(|tlc| { + if let Some(remove_at) = tlc.removed_at { + if matches!(remove_at.1, RemoveTlcReason::RemoveTlcFulfill(_)) { + fulfilled += tlc.amount; } } - } - } + }); fulfilled } - pub fn get_all_received_tlcs(&self) -> impl Iterator { + pub fn get_all_received_tlcs(&self) -> impl Iterator { self.tlc_state.all_tlcs().filter(|tlc| tlc.is_received()) } - pub fn get_all_offer_tlcs(&self) -> impl Iterator { + pub fn get_all_offer_tlcs(&self) -> impl Iterator { self.tlc_state.all_tlcs().filter(|tlc| tlc.is_offered()) } @@ -4920,7 +4657,7 @@ impl ChannelActorState { // The offerer who offered this tlc will have the first pubkey, and the receiver // will have the second pubkey. // This tlc must have valid local_committed_at and remote_committed_at fields. - pub fn get_tlc_pubkeys(&self, tlc: &AddTlcInfo) -> (Pubkey, Pubkey) { + pub fn get_tlc_pubkeys(&self, tlc: &TlcInfo) -> (Pubkey, Pubkey) { let is_offered = tlc.is_offered(); let CommitmentNumbers { local: local_commitment_number, @@ -4946,11 +4683,9 @@ impl ChannelActorState { } } - fn get_active_received_tlc_with_pubkeys( - &self, - local: bool, - ) -> Vec<(AddTlcInfo, Pubkey, Pubkey)> { + fn get_active_received_tlc_with_pubkeys(&self, local: bool) -> Vec<(TlcInfo, Pubkey, Pubkey)> { self.get_active_received_tlcs(local) + .into_iter() .map(move |tlc| { let (k1, k2) = self.get_tlc_pubkeys(&tlc); (tlc, k1, k2) @@ -4958,11 +4693,9 @@ impl ChannelActorState { .collect() } - fn get_active_offered_tlc_with_pubkeys( - &self, - local: bool, - ) -> Vec<(AddTlcInfo, Pubkey, Pubkey)> { + fn get_active_offered_tlc_with_pubkeys(&self, local: bool) -> Vec<(TlcInfo, Pubkey, Pubkey)> { self.get_active_offered_tlcs(local) + .into_iter() .map(move |tlc| { let (k1, k2) = self.get_tlc_pubkeys(&tlc); (tlc, k1, k2) @@ -5173,15 +4906,16 @@ impl ChannelActorState { Ok(()) } - fn create_outbounding_tlc(&self, command: AddTlcCommand) -> AddTlcInfo { + fn create_outbounding_tlc(&self, command: AddTlcCommand) -> TlcInfo { let id = self.get_next_offering_tlc_id(); assert!( self.get_offered_tlc(id).is_none(), "Must not have the same id in pending offered tlcs" ); - AddTlcInfo { + TlcInfo { channel_id: self.get_id(), + status: TlcStatus::Outbound(OutboundTlcStatus::LocalAnnounced), tlc_id: TLCId::Offered(id), amount: command.amount, payment_hash: command.payment_hash, @@ -5198,9 +4932,10 @@ impl ChannelActorState { } } - fn create_inbounding_tlc(&self, message: AddTlc) -> Result { - let tlc_info = AddTlcInfo { + fn create_inbounding_tlc(&self, message: AddTlc) -> Result { + let tlc_info = TlcInfo { tlc_id: TLCId::Received(message.tlc_id), + status: TlcStatus::Inbound(InboundTlctatus::RemoteAnnounced), channel_id: self.get_id(), amount: message.amount, payment_hash: message.payment_hash, @@ -6014,14 +5749,15 @@ impl ChannelActorState { self.increment_local_commitment_number(); self.append_remote_commitment_point(next_per_commitment_point); - let staging_tlcs = self.tlc_state.commit_local_tlcs(); - for tlc in staging_tlcs { - if let TlcKind::RemoveTlc(remove_tlc) = tlc { - self.remove_tlc_with_reason(remove_tlc.tlc_id, &remove_tlc.reason) - .expect("expect remove tlc successfully"); - } - } - self.tlc_state.set_waiting_ack(false); + self.tlc_state.update_for_revoke_and_ack(); + // let staging_tlcs = self.tlc_state.commit_local_tlcs(); + // for tlc in staging_tlcs { + // if let TlcKind::RemoveTlc(remove_tlc) = tlc { + // self.remove_tlc_with_reason(remove_tlc.tlc_id, &remove_tlc.reason) + // .expect("expect remove tlc successfully"); + // } + // } + // self.tlc_state.set_waiting_ack(false); network .send_message(NetworkActorMessage::new_notification( diff --git a/src/fiber/tests/channel.rs b/src/fiber/tests/channel.rs index 858a59ab0..ea3b9364b 100644 --- a/src/fiber/tests/channel.rs +++ b/src/fiber/tests/channel.rs @@ -1,6 +1,4 @@ -use crate::fiber::channel::{ - AddTlcInfo, CommitmentNumbers, RemoveTlcInfo, TLCId, TlcKind, TlcState, UpdateCommand, -}; +use crate::fiber::channel::UpdateCommand; use crate::fiber::config::MAX_PAYMENT_TLC_EXPIRY_LIMIT; use crate::fiber::graph::PaymentSessionStatus; use crate::fiber::network::{DebugEvent, SendPaymentCommand}; @@ -66,221 +64,220 @@ fn test_derive_private_and_public_tlc_keys() { assert_eq!(derived_privkey.pubkey(), derived_pubkey); } -#[test] -fn test_pending_tlcs() { - let mut tlc_state = TlcState::default(); - let add_tlc1 = AddTlcInfo { - amount: 10000, - channel_id: gen_rand_sha256_hash(), - payment_hash: gen_rand_sha256_hash(), - expiry: now_timestamp_as_millis_u64() + 1000, - hash_algorithm: HashAlgorithm::Sha256, - onion_packet: None, - shared_secret: NO_SHARED_SECRET.clone(), - tlc_id: TLCId::Offered(0), - created_at: CommitmentNumbers::default(), - removed_at: None, - payment_preimage: None, - previous_tlc: None, - }; - let add_tlc2 = AddTlcInfo { - amount: 20000, - channel_id: gen_rand_sha256_hash(), - payment_hash: gen_rand_sha256_hash(), - expiry: now_timestamp_as_millis_u64() + 2000, - hash_algorithm: HashAlgorithm::Sha256, - onion_packet: None, - shared_secret: NO_SHARED_SECRET.clone(), - tlc_id: TLCId::Offered(1), - created_at: CommitmentNumbers::default(), - removed_at: None, - payment_preimage: None, - previous_tlc: None, - }; - tlc_state.add_local_tlc(TlcKind::AddTlc(add_tlc1.clone())); - tlc_state.add_local_tlc(TlcKind::AddTlc(add_tlc2.clone())); - - let mut tlc_state_2 = TlcState::default(); - tlc_state_2.add_remote_tlc(TlcKind::AddTlc(add_tlc1.clone())); - tlc_state_2.add_remote_tlc(TlcKind::AddTlc(add_tlc2.clone())); - - let tx1 = tlc_state.get_tlcs_for_local(); - let tx2 = tlc_state_2.get_tlcs_for_remote(); - - assert_eq!(tx1, tx2); - - let tlcs = tlc_state.commit_local_tlcs(); - assert_eq!(tlcs.len(), 2); - - let tlcs2 = tlc_state_2.commit_remote_tlcs(); - assert_eq!(tlcs2.len(), 2); - - assert_eq!(tx1, tx2); - - let tlcs = tlc_state.commit_local_tlcs(); - assert_eq!(tlcs.len(), 0); - - let tlcs2 = tlc_state_2.commit_remote_tlcs(); - assert_eq!(tlcs2.len(), 0); - - tlc_state_2.add_local_tlc(TlcKind::AddTlc(add_tlc1.clone())); - tlc_state_2.add_local_tlc(TlcKind::AddTlc(add_tlc2.clone())); - - tlc_state.add_remote_tlc(TlcKind::AddTlc(add_tlc1.clone())); - tlc_state.add_remote_tlc(TlcKind::AddTlc(add_tlc2.clone())); - - let tx1 = tlc_state.get_tlcs_for_remote(); - let tx2 = tlc_state_2.get_tlcs_for_local(); - - assert_eq!(tx1, tx2); - - let tlcs = tlc_state.commit_remote_tlcs(); - assert_eq!(tlcs.len(), 2); - let tlcs2 = tlc_state_2.commit_local_tlcs(); - assert_eq!(tlcs2.len(), 2); - - assert_eq!(tx1, tx2); - - let tlcs = tlc_state.commit_remote_tlcs(); - assert_eq!(tlcs.len(), 0); - let tlcs2 = tlc_state_2.commit_local_tlcs(); - assert_eq!(tlcs2.len(), 0); -} - -#[test] -fn test_pending_tlcs_duplicated_tlcs() { - let mut tlc_state = TlcState::default(); - let add_tlc1 = AddTlcInfo { - amount: 10000, - channel_id: gen_rand_sha256_hash(), - payment_hash: gen_rand_sha256_hash(), - expiry: now_timestamp_as_millis_u64() + 1000, - hash_algorithm: HashAlgorithm::Sha256, - onion_packet: None, - shared_secret: NO_SHARED_SECRET.clone(), - tlc_id: TLCId::Offered(0), - created_at: CommitmentNumbers::default(), - removed_at: None, - payment_preimage: None, - previous_tlc: None, - }; - tlc_state.add_local_tlc(TlcKind::AddTlc(add_tlc1.clone())); - - let mut tlc_state_2 = TlcState::default(); - tlc_state_2.add_remote_tlc(TlcKind::AddTlc(add_tlc1.clone())); - - let tx1 = tlc_state.get_tlcs_for_local(); - let tx2 = tlc_state_2.get_tlcs_for_remote(); - - assert_eq!(tx1, tx2); - - let tlcs = tlc_state.commit_local_tlcs(); - assert_eq!(tlcs.len(), 1); - - let tlcs2 = tlc_state_2.commit_remote_tlcs(); - assert_eq!(tlcs2.len(), 1); - - assert_eq!(tx1, tx2); - - let tlcs = tlc_state.commit_local_tlcs(); - assert_eq!(tlcs.len(), 0); - - let tlcs2 = tlc_state_2.commit_remote_tlcs(); - assert_eq!(tlcs2.len(), 0); - - let add_tlc2 = AddTlcInfo { - amount: 20000, - channel_id: gen_rand_sha256_hash(), - payment_hash: gen_rand_sha256_hash(), - expiry: now_timestamp_as_millis_u64() + 2000, - hash_algorithm: HashAlgorithm::Sha256, - onion_packet: None, - shared_secret: NO_SHARED_SECRET.clone(), - tlc_id: TLCId::Offered(1), - created_at: CommitmentNumbers::default(), - removed_at: None, - payment_preimage: None, - previous_tlc: None, - }; - - tlc_state_2.add_local_tlc(TlcKind::AddTlc(add_tlc2.clone())); - tlc_state.add_remote_tlc(TlcKind::AddTlc(add_tlc2.clone())); - - let tx1 = tlc_state.get_tlcs_for_remote(); - let tx2 = tlc_state_2.get_tlcs_for_local(); - - assert_eq!(tx1, tx2); - - let tlcs = tlc_state.commit_remote_tlcs(); - assert_eq!(tlcs.len(), 1); - let tlcs2 = tlc_state_2.commit_local_tlcs(); - assert_eq!(tlcs2.len(), 1); - assert_eq!(tx1, tx2); - - let tlcs = tlc_state.commit_remote_tlcs(); - assert_eq!(tlcs.len(), 0); - let tlcs2 = tlc_state_2.commit_local_tlcs(); - assert_eq!(tlcs2.len(), 0); - - let committed_tlcs1 = tlc_state.all_commited_tlcs().collect::>(); - let committed_tlcs2 = tlc_state_2.all_commited_tlcs().collect::>(); - assert_eq!(committed_tlcs1, committed_tlcs2); -} - -#[test] -fn test_pending_tlcs_with_remove_tlc() { - let mut tlc_state = TlcState::default(); - let add_tlc1 = AddTlcInfo { - amount: 10000, - channel_id: gen_rand_sha256_hash(), - payment_hash: gen_rand_sha256_hash(), - expiry: now_timestamp_as_millis_u64() + 1000, - hash_algorithm: HashAlgorithm::Sha256, - onion_packet: None, - shared_secret: NO_SHARED_SECRET.clone(), - tlc_id: TLCId::Offered(0), - created_at: CommitmentNumbers::default(), - removed_at: None, - payment_preimage: None, - previous_tlc: None, - }; - let add_tlc2 = AddTlcInfo { - amount: 20000, - channel_id: gen_rand_sha256_hash(), - payment_hash: gen_rand_sha256_hash(), - expiry: now_timestamp_as_millis_u64() + 2000, - hash_algorithm: HashAlgorithm::Sha256, - onion_packet: None, - shared_secret: NO_SHARED_SECRET.clone(), - tlc_id: TLCId::Offered(1), - created_at: CommitmentNumbers::default(), - removed_at: None, - payment_preimage: None, - previous_tlc: None, - }; - let remote_tlc = RemoveTlcInfo { - channel_id: gen_rand_sha256_hash(), - tlc_id: TLCId::Offered(0), - reason: RemoveTlcReason::RemoveTlcFulfill(RemoveTlcFulfill { - payment_preimage: gen_rand_sha256_hash(), - }), - }; - - tlc_state.add_local_tlc(TlcKind::AddTlc(add_tlc1.clone())); - tlc_state.add_local_tlc(TlcKind::AddTlc(add_tlc2.clone())); - tlc_state.add_local_tlc(TlcKind::RemoveTlc(remote_tlc.clone())); - - let tx1 = tlc_state.get_tlcs_for_local(); - assert_eq!(tx1.len(), 1); - let first = &tx1[0]; - assert_eq!(first.tlc_id(), TLCId::Offered(1)); - - let tx1 = tlc_state.commit_local_tlcs(); - assert_eq!(tx1.len(), 3); - - let all_tlcs: Vec<&AddTlcInfo> = tlc_state.all_commited_tlcs().collect(); - assert_eq!(all_tlcs.len(), 2); -} +// #[test] +// fn test_pending_tlcs() { +// let mut tlc_state = TlcState::default(); +// let add_tlc1 = AddTlcInfo { +// amount: 10000, +// status: TlcStatus::Outbound(OutboundTlcStatus::LocalAnnounced), + +// channel_id: gen_rand_sha256_hash(), +// payment_hash: gen_rand_sha256_hash(), +// expiry: now_timestamp_as_millis_u64() + 1000, +// hash_algorithm: HashAlgorithm::Sha256, +// onion_packet: None, +// shared_secret: NO_SHARED_SECRET.clone(), +// tlc_id: TLCId::Offered(0), +// created_at: CommitmentNumbers::default(), +// removed_at: None, +// payment_preimage: None, +// previous_tlc: None, +// }; +// let add_tlc2 = AddTlcInfo { +// amount: 20000, +// status: TlcStatus::Outbound(OutboundTlcStatus::LocalAnnounced), + +// channel_id: gen_rand_sha256_hash(), +// payment_hash: gen_rand_sha256_hash(), +// expiry: now_timestamp_as_millis_u64() + 2000, +// hash_algorithm: HashAlgorithm::Sha256, +// onion_packet: None, +// shared_secret: NO_SHARED_SECRET.clone(), +// tlc_id: TLCId::Offered(1), +// created_at: CommitmentNumbers::default(), +// removed_at: None, +// payment_preimage: None, +// previous_tlc: None, +// }; +// tlc_state.add_local_tlc(TlcKind::AddTlc(add_tlc1.clone())); +// tlc_state.add_local_tlc(TlcKind::AddTlc(add_tlc2.clone())); + +// let mut tlc_state_2 = TlcState::default(); +// tlc_state_2.add_remote_tlc(TlcKind::AddTlc(add_tlc1.clone())); +// tlc_state_2.add_remote_tlc(TlcKind::AddTlc(add_tlc2.clone())); + +// let tx1 = tlc_state.get_tlcs_for_local(); +// let tx2 = tlc_state_2.get_tlcs_for_remote(); + +// assert_eq!(tx1, tx2); + +// let tlcs = tlc_state.commit_local_tlcs(); +// assert_eq!(tlcs.len(), 2); + +// let tlcs2 = tlc_state_2.commit_remote_tlcs(); +// assert_eq!(tlcs2.len(), 2); + +// assert_eq!(tx1, tx2); + +// let tlcs = tlc_state.commit_local_tlcs(); +// assert_eq!(tlcs.len(), 0); + +// let tlcs2 = tlc_state_2.commit_remote_tlcs(); +// assert_eq!(tlcs2.len(), 0); + +// tlc_state_2.add_local_tlc(TlcKind::AddTlc(add_tlc1.clone())); +// tlc_state_2.add_local_tlc(TlcKind::AddTlc(add_tlc2.clone())); + +// tlc_state.add_remote_tlc(TlcKind::AddTlc(add_tlc1.clone())); +// tlc_state.add_remote_tlc(TlcKind::AddTlc(add_tlc2.clone())); + +// let tx1 = tlc_state.get_tlcs_for_remote(); +// let tx2 = tlc_state_2.get_tlcs_for_local(); + +// assert_eq!(tx1, tx2); + +// let tlcs = tlc_state.commit_remote_tlcs(); +// assert_eq!(tlcs.len(), 2); +// let tlcs2 = tlc_state_2.commit_local_tlcs(); +// assert_eq!(tlcs2.len(), 2); + +// assert_eq!(tx1, tx2); + +// let tlcs = tlc_state.commit_remote_tlcs(); +// assert_eq!(tlcs.len(), 0); +// let tlcs2 = tlc_state_2.commit_local_tlcs(); +// assert_eq!(tlcs2.len(), 0); +// } + +// #[test] +// fn test_pending_tlcs_duplicated_tlcs() { +// let mut tlc_state = TlcState::default(); +// let add_tlc1 = AddTlcInfo { +// amount: 10000, +// status: TlcStatus::Outbound(OutboundTlcStatus::LocalAnnounced), +// channel_id: gen_rand_sha256_hash(), +// payment_hash: gen_rand_sha256_hash(), +// expiry: now_timestamp_as_millis_u64() + 1000, +// hash_algorithm: HashAlgorithm::Sha256, +// onion_packet: None, +// shared_secret: NO_SHARED_SECRET.clone(), +// tlc_id: TLCId::Offered(0), +// created_at: CommitmentNumbers::default(), +// removed_at: None, +// payment_preimage: None, +// previous_tlc: None, +// }; +// tlc_state.add_local_tlc(TlcKind::AddTlc(add_tlc1.clone())); + +// let mut tlc_state_2 = TlcState::default(); +// tlc_state_2.add_remote_tlc(TlcKind::AddTlc(add_tlc1.clone())); + +// let tx1 = tlc_state.get_tlcs_for_local(); +// let tx2 = tlc_state_2.get_tlcs_for_remote(); + +// assert_eq!(tx1, tx2); + +// let tlcs = tlc_state.commit_local_tlcs(); +// assert_eq!(tlcs.len(), 1); + +// let tlcs2 = tlc_state_2.commit_remote_tlcs(); +// assert_eq!(tlcs2.len(), 1); + +// assert_eq!(tx1, tx2); + +// let tlcs = tlc_state.commit_local_tlcs(); +// assert_eq!(tlcs.len(), 0); + +// let tlcs2 = tlc_state_2.commit_remote_tlcs(); +// assert_eq!(tlcs2.len(), 0); + +// let add_tlc2 = AddTlcInfo { +// amount: 20000, +// status: TlcStatus::Outbound(OutboundTlcStatus::LocalAnnounced), +// channel_id: gen_rand_sha256_hash(), +// payment_hash: gen_rand_sha256_hash(), +// expiry: now_timestamp_as_millis_u64() + 2000, +// hash_algorithm: HashAlgorithm::Sha256, +// onion_packet: None, +// shared_secret: NO_SHARED_SECRET.clone(), +// tlc_id: TLCId::Offered(1), +// created_at: CommitmentNumbers::default(), +// removed_at: None, +// payment_preimage: None, +// previous_tlc: None, +// }; + +// tlc_state_2.add_local_tlc(TlcKind::AddTlc(add_tlc2.clone())); +// tlc_state.add_remote_tlc(TlcKind::AddTlc(add_tlc2.clone())); + +// let tx1 = tlc_state.get_tlcs_for_remote(); +// let tx2 = tlc_state_2.get_tlcs_for_local(); + +// assert_eq!(tx1, tx2); + +// let tlcs = tlc_state.commit_remote_tlcs(); +// assert_eq!(tlcs.len(), 1); +// let tlcs2 = tlc_state_2.commit_local_tlcs(); +// assert_eq!(tlcs2.len(), 1); +// assert_eq!(tx1, tx2); + +// let tlcs = tlc_state.commit_remote_tlcs(); +// assert_eq!(tlcs.len(), 0); +// let tlcs2 = tlc_state_2.commit_local_tlcs(); +// assert_eq!(tlcs2.len(), 0); + +// let committed_tlcs1 = tlc_state.all_commited_tlcs().collect::>(); +// let committed_tlcs2 = tlc_state_2.all_commited_tlcs().collect::>(); +// assert_eq!(committed_tlcs1, committed_tlcs2); +// } + +// #[test] +// fn test_pending_tlcs_with_remove_tlc() { +// let mut tlc_state = TlcState::default(); +// let add_tlc1 = TlcInfo { +// amount: 10000, +// status: TlcStatus::Outbound(OutboundTlcStatus::LocalAnnounced), +// channel_id: gen_rand_sha256_hash(), +// payment_hash: gen_rand_sha256_hash(), +// expiry: now_timestamp_as_millis_u64() + 1000, +// hash_algorithm: HashAlgorithm::Sha256, +// onion_packet: None, +// shared_secret: NO_SHARED_SECRET.clone(), +// tlc_id: TLCId::Offered(0), +// created_at: CommitmentNumbers::default(), +// removed_at: None, +// payment_preimage: None, +// previous_tlc: None, +// }; +// let add_tlc2 = AddTlcInfo { +// amount: 20000, +// status: TlcStatus::Outbound(OutboundTlcStatus::LocalAnnounced), +// channel_id: gen_rand_sha256_hash(), +// payment_hash: gen_rand_sha256_hash(), +// expiry: now_timestamp_as_millis_u64() + 2000, +// hash_algorithm: HashAlgorithm::Sha256, +// onion_packet: None, +// shared_secret: NO_SHARED_SECRET.clone(), +// tlc_id: TLCId::Offered(1), +// created_at: CommitmentNumbers::default(), +// removed_at: None, +// payment_preimage: None, +// previous_tlc: None, +// }; +// let remote_tlc = RemoveTlcInfo { +// channel_id: gen_rand_sha256_hash(), +// tlc_id: TLCId::Offered(0), +// reason: RemoveTlcReason::RemoveTlcFulfill(RemoveTlcFulfill { +// payment_preimage: gen_rand_sha256_hash(), +// }), +// }; + +// tlc_state.add_offered_tlc(add_tlc1.clone()); +// tlc_state.add_offered_tlc(add_tlc2.clone()); + +// let all_tlcs: Vec<&AddTlcInfo> = tlc_state.all_commited_tlcs().collect(); +// assert_eq!(all_tlcs.len(), 2); +// } #[tokio::test] async fn test_open_channel_to_peer() { diff --git a/src/fiber/tests/tlc_op.rs b/src/fiber/tests/tlc_op.rs index 567753fc9..bf0776847 100644 --- a/src/fiber/tests/tlc_op.rs +++ b/src/fiber/tests/tlc_op.rs @@ -1,10 +1,11 @@ -use crate::fiber::channel::AddTlcInfoV2; +use crate::fiber::channel::TlcInfo; use crate::fiber::channel::{ - CommitmentNumbers, InboundTlctatus, OutboundTlcStatus, TLCId, TlcKindV2, TlcStateV2, TlcStatus, + CommitmentNumbers, InboundTlctatus, OutboundTlcStatus, TLCId, TlcState, TlcStatus, }; use crate::fiber::hash_algorithm::HashAlgorithm; -use crate::fiber::types::PaymentOnionPacket; +use crate::fiber::types::RemoveTlcFulfill; use crate::fiber::types::{Hash256, NO_SHARED_SECRET}; +use crate::fiber::types::{PaymentOnionPacket, RemoveTlcReason}; use crate::gen_rand_sha256_hash; use crate::now_timestamp_as_millis_u64; use ckb_hash::new_blake2b; @@ -12,7 +13,7 @@ use ckb_types::packed::Byte32; use ractor::{async_trait as rasync_trait, Actor, ActorProcessingErr, ActorRef}; use std::collections::HashMap; -fn sign_tlcs(tlcs: &Vec) -> Hash256 { +fn sign_tlcs(tlcs: &Vec) -> Hash256 { // serialize active_tls to ge a hash let mut keyparts = tlcs .iter() @@ -38,7 +39,7 @@ fn sign_tlcs(tlcs: &Vec) -> Hash256 { } pub struct TlcActorState { - pub tlc_state: TlcStateV2, + pub tlc_state: TlcState, pub peer_id: String, } @@ -103,7 +104,7 @@ pub enum TlcActorMessage { Debug, CommandAddTlc(AddTlcCommand), CommandRemoveTlc(u64), - PeerAddTlc(AddTlcInfoV2), + PeerAddTlc(TlcInfo), PeerRemoveTlc(u64), PeerCommitmentSigned(Hash256), PeerRevokeAndAck(Hash256), @@ -200,7 +201,7 @@ impl Actor for TlcActor { state.peer_id, command ); let next_offer_id = state.tlc_state.get_next_offering(); - let add_tlc = AddTlcInfoV2 { + let add_tlc = TlcInfo { channel_id: gen_rand_sha256_hash(), tlc_id: TLCId::Offered(next_offer_id), amount: command.amount, @@ -215,9 +216,7 @@ impl Actor for TlcActor { previous_tlc: None, status: TlcStatus::Outbound(OutboundTlcStatus::LocalAnnounced), }; - state - .tlc_state - .add_offered_tlc(TlcKindV2::AddTlc(add_tlc.clone())); + state.tlc_state.add_offered_tlc(add_tlc.clone()); state.tlc_state.increment_offering(); let peer = state.get_peer(); self.network @@ -240,7 +239,13 @@ impl Actor for TlcActor { } TlcActorMessage::CommandRemoveTlc(tlc_id) => { eprintln!("Peer {} process remove tlc ....", state.peer_id); - state.tlc_state.set_received_tlc_removed(tlc_id); + state.tlc_state.set_received_tlc_removed( + tlc_id, + CommitmentNumbers::default(), + RemoveTlcReason::RemoveTlcFulfill(RemoveTlcFulfill { + payment_preimage: Default::default(), + }), + ); let peer = state.get_peer(); self.network .send_message(NetworkActorMessage::PeerMsg( @@ -268,7 +273,7 @@ impl Actor for TlcActor { let mut tlc = add_tlc.clone(); tlc.flip_mut(); tlc.status = TlcStatus::Inbound(InboundTlctatus::RemoteAnnounced); - state.tlc_state.add_received_tlc(TlcKindV2::AddTlc(tlc)); + state.tlc_state.add_received_tlc(tlc); eprintln!("add peer tlc successfully: {:?}", add_tlc); } TlcActorMessage::PeerRemoveTlc(tlc_id) => { @@ -276,7 +281,13 @@ impl Actor for TlcActor { "Peer {} process peer remove tlc .... with tlc_id: {}", state.peer_id, tlc_id ); - state.tlc_state.set_offered_tlc_removed(tlc_id); + state.tlc_state.set_offered_tlc_removed( + tlc_id, + CommitmentNumbers::default(), + RemoveTlcReason::RemoveTlcFulfill(RemoveTlcFulfill { + payment_preimage: Default::default(), + }), + ); } TlcActorMessage::PeerCommitmentSigned(peer_hash) => { eprintln!( @@ -446,8 +457,8 @@ async fn test_tlc_actor() { #[test] fn test_tlc_state_v2() { - let mut tlc_state = TlcStateV2::default(); - let mut add_tlc1 = AddTlcInfoV2 { + let mut tlc_state = TlcState::default(); + let mut add_tlc1 = TlcInfo { amount: 10000, status: TlcStatus::Outbound(OutboundTlcStatus::LocalAnnounced), channel_id: gen_rand_sha256_hash(), @@ -462,7 +473,7 @@ fn test_tlc_state_v2() { payment_preimage: None, previous_tlc: None, }; - let mut add_tlc2 = AddTlcInfoV2 { + let mut add_tlc2 = TlcInfo { amount: 20000, status: TlcStatus::Outbound(OutboundTlcStatus::LocalAnnounced), channel_id: gen_rand_sha256_hash(), @@ -477,16 +488,16 @@ fn test_tlc_state_v2() { payment_preimage: None, previous_tlc: None, }; - tlc_state.add_offered_tlc(TlcKindV2::AddTlc(add_tlc1.clone())); - tlc_state.add_offered_tlc(TlcKindV2::AddTlc(add_tlc2.clone())); + tlc_state.add_offered_tlc(add_tlc1.clone()); + tlc_state.add_offered_tlc(add_tlc2.clone()); - let mut tlc_state_2 = TlcStateV2::default(); + let mut tlc_state_2 = TlcState::default(); add_tlc1.flip_mut(); add_tlc2.flip_mut(); add_tlc1.status = TlcStatus::Inbound(InboundTlctatus::RemoteAnnounced); add_tlc2.status = TlcStatus::Inbound(InboundTlctatus::RemoteAnnounced); - tlc_state_2.add_received_tlc(TlcKindV2::AddTlc(add_tlc1)); - tlc_state_2.add_received_tlc(TlcKindV2::AddTlc(add_tlc2)); + tlc_state_2.add_received_tlc(add_tlc1); + tlc_state_2.add_received_tlc(add_tlc2); let hash1 = sign_tlcs(&tlc_state.commitment_signed(true)); eprintln!("hash1: {:?}", hash1); From 375147ca1fb48c6945fb0ae0f1c91097f07a7ddc Mon Sep 17 00:00:00 2001 From: yukang Date: Mon, 23 Dec 2024 20:55:40 +0800 Subject: [PATCH 018/119] implement tlc wait ack --- src/fiber/channel.rs | 512 ++++++++++++++++--------------------- src/fiber/network.rs | 10 +- src/fiber/tests/payment.rs | 92 ++++--- src/store/store.rs | 8 +- 4 files changed, 294 insertions(+), 328 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index ce6f74fe3..0867d92ce 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -439,6 +439,7 @@ where } FiberChannelMessage::RevokeAndAck(revoke_and_ack) => { state.handle_revoke_and_ack_peer_message(&self.network, revoke_and_ack)?; + self.flush_tlc_operations(myself, state).await; Ok(()) } FiberChannelMessage::ChannelReady(_channel_ready) => { @@ -655,66 +656,101 @@ where ) -> Result<(), ProcessingChannelError> { // build commitment tx and verify signature from remote, if passed send ACK for partner state.verify_commitment_signed_and_send_ack(commitment_signed, &self.network)?; - state.tlc_state.update_for_peer_commitment_signed(); - //self.flush_staging_tlc_operations(myself, state).await; + let need_commitment_signed = state.tlc_state.update_for_peer_commitment_signed(); + + if need_commitment_signed { + eprintln!("sending commitment_signed after first ack"); + self.handle_commitment_signed_command(state)?; + } else { + eprintln!("not sending commitment_signed after first ack"); + } Ok(()) } - // async fn flush_staging_tlc_operations( - // &self, - // myself: &ActorRef, - // state: &mut ChannelActorState, - // ) { - // let pending_apply_tlcs = state.tlc_state.commit_remote_tlcs(); - // for tlc_info in pending_apply_tlcs { - // match tlc_info { - // TlcKind::AddTlc(add_tlc) => { - // assert!(add_tlc.is_received()); - // if let Err(e) = self.apply_add_tlc_operation(myself, state, &add_tlc).await { - // let tlc_err = match e.source { - // // If we already have TlcErr, we can directly use it to send back to the peer. - // ProcessingChannelError::TlcForwardingError(tlc_err) => tlc_err, - // _ => { - // let error_detail = self.get_tlc_error(state, &e.source).await; - // #[cfg(debug_assertions)] - // self.network - // .clone() - // .send_message(NetworkActorMessage::new_notification( - // NetworkServiceEvent::DebugEvent(DebugEvent::AddTlcFailed( - // state.get_local_peer_id(), - // add_tlc.payment_hash, - // error_detail.clone(), - // )), - // )) - // .expect(ASSUME_NETWORK_ACTOR_ALIVE); - // error_detail - // } - // }; - // let error_packet = TlcErrPacket::new( - // tlc_err, - // // There's no shared secret stored in the received TLC, use the one found in the peeled onion packet. - // &e.shared_secret, - // ); - // self.register_retryable_tlc_remove( - // myself, - // state, - // add_tlc.tlc_id, - // RemoveTlcReason::RemoveTlcFail(error_packet), - // ) - // .await; - // } - // } - // TlcKind::RemoveTlc(remove_tlc) => { - // let _ = self - // .apply_remove_tlc_operation(myself, state, remove_tlc) - // .await - // .map_err(|e| { - // error!("Error handling apply_remove_tlc_operation: {:?}", e); - // }); - // } - // } - // } - // } + async fn flush_tlc_operations( + &self, + myself: &ActorRef, + state: &mut ChannelActorState, + ) { + state.tlc_state.debug(); + + let mut settled_tlcs = vec![]; + for tlc in state.tlc_state.received_tlcs.tlcs.iter_mut() { + if let Some((_removed_at, reason)) = &tlc.removed_at { + if matches!( + tlc.status, + TlcStatus::Inbound(InboundTlctatus::LocalRemoved) + ) { + settled_tlcs.push((tlc.tlc_id, reason.clone())); + } + } + } + for (tlc_id, reason) in settled_tlcs.iter() { + eprintln!("apply offered tlc remove: {:?}", tlc_id); + let _ = self + .apply_remove_tlc_operation(myself, state, *tlc_id, reason.clone()) + .await; + } + + // flush outbound tlcs + let mut settled_tlcs = vec![]; + for tlc in state.tlc_state.offered_tlcs.tlcs.iter_mut() { + if let Some((_removed_at, reason)) = &tlc.removed_at { + if matches!( + tlc.status, + TlcStatus::Outbound(OutboundTlcStatus::AwaitingRemovedRemoteRevoke) + ) { + settled_tlcs.push((tlc.tlc_id, reason.clone())); + } + } + } + + let apply_tlcs = state.tlc_state.get_committed_received_tlcs(); + eprintln!("flushing {} tlcs", apply_tlcs.len()); + for add_tlc in apply_tlcs { + if add_tlc.removed_at.is_some() { + eprintln!("tlc already removed, skip"); + continue; + } + + assert!(add_tlc.is_received()); + if let Err(e) = self.apply_add_tlc_operation(myself, state, &add_tlc).await { + let tlc_err = match e.source { + // If we already have TlcErr, we can directly use it to send back to the peer. + ProcessingChannelError::TlcForwardingError(tlc_err) => tlc_err, + _ => { + let error_detail = self.get_tlc_error(state, &e.source).await; + #[cfg(debug_assertions)] + self.network + .clone() + .send_message(NetworkActorMessage::new_notification( + NetworkServiceEvent::DebugEvent(DebugEvent::AddTlcFailed( + state.get_local_peer_id(), + add_tlc.payment_hash, + error_detail.clone(), + )), + )) + .expect(ASSUME_NETWORK_ACTOR_ALIVE); + error_detail + } + }; + let error_packet = TlcErrPacket::new( + tlc_err, + // There's no shared secret stored in the received TLC, use the one found in the peeled onion packet. + &e.shared_secret, + ); + self.register_retryable_tlc_remove( + myself, + state, + add_tlc.tlc_id, + RemoveTlcReason::RemoveTlcFail(error_packet), + ) + .await; + } + } + + eprintln!("end flusing now ..................."); + } async fn try_to_relay_remove_tlc( &self, @@ -790,6 +826,10 @@ where } } + eprintln!( + "settle down tlc: {:?} with reason: {:?}", + tlc_id, remove_reason + ); self.register_retryable_tlc_remove(myself, state, tlc.tlc_id, remove_reason) .await; } @@ -987,50 +1027,52 @@ where Ok(()) } - // async fn apply_remove_tlc_operation( - // &self, - // myself: &ActorRef, - // state: &mut ChannelActorState, - // remove_tlc: RemoveTlcInfo, - // ) -> Result<(), ProcessingChannelError> { - // let channel_id = state.get_id(); - // let remove_reason = remove_tlc.reason.clone(); - // let tlc_info = state - // .remove_tlc_with_reason(remove_tlc.tlc_id, &remove_reason) - // .expect("expect remove tlc successfully"); - // if let ( - // Some(ref udt_type_script), - // RemoveTlcReason::RemoveTlcFulfill(RemoveTlcFulfill { payment_preimage }), - // ) = (state.funding_udt_type_script.clone(), &remove_reason) - // { - // let mut tlc = tlc_info.clone(); - // tlc.payment_preimage = Some(*payment_preimage); - // self.subscribers - // .settled_tlcs_subscribers - // .send(TlcNotification { - // tlc, - // channel_id, - // script: udt_type_script.clone(), - // }); - // } - // if tlc_info.previous_tlc.is_none() { - // // only the original sender of the TLC should send `TlcRemoveReceived` event - // // because only the original sender cares about the TLC event to settle the payment - // self.network - // .send_message(NetworkActorMessage::new_event( - // NetworkActorEvent::TlcRemoveReceived( - // tlc_info.payment_hash, - // remove_reason.clone(), - // ), - // )) - // .expect("myself alive"); - // } else { - // // relay RemoveTlc to previous channel if needed - // self.try_to_relay_remove_tlc(myself, state, &tlc_info, remove_reason) - // .await; - // } - // Ok(()) - // } + async fn apply_remove_tlc_operation( + &self, + myself: &ActorRef, + state: &mut ChannelActorState, + tlc_id: TLCId, + reason: RemoveTlcReason, + ) -> Result<(), ProcessingChannelError> { + let channel_id = state.get_id(); + let remove_reason = reason.clone(); + let tlc_info = state + .remove_tlc_with_reason(tlc_id, &remove_reason) + .expect("expect remove tlc successfully"); + if let ( + Some(ref udt_type_script), + RemoveTlcReason::RemoveTlcFulfill(RemoveTlcFulfill { payment_preimage }), + ) = (state.funding_udt_type_script.clone(), &remove_reason) + { + let mut tlc = tlc_info.clone(); + tlc.payment_preimage = Some(*payment_preimage); + self.subscribers + .settled_tlcs_subscribers + .send(TlcNotification { + tlc, + channel_id, + script: udt_type_script.clone(), + }); + } + if tlc_info.previous_tlc.is_none() { + // only the original sender of the TLC should send `TlcRemoveReceived` event + // because only the original sender cares about the TLC event to settle the payment + eprintln!("sending TlcRemoveReceived ...: {:?}", tlc_info.payment_hash); + self.network + .send_message(NetworkActorMessage::new_event( + NetworkActorEvent::TlcRemoveReceived( + tlc_info.payment_hash, + remove_reason.clone(), + ), + )) + .expect("myself alive"); + } else { + // relay RemoveTlc to previous channel if needed + self.try_to_relay_remove_tlc(myself, state, &tlc_info, remove_reason) + .await; + } + Ok(()) + } async fn handle_forward_onion_packet( &self, @@ -1114,6 +1156,7 @@ where commitment_tx_partial_signature, next_local_nonce: state.get_next_local_nonce(), }; + eprintln!("sending commitment_signed ..."); self.network .send_message(NetworkActorMessage::new_command( NetworkActorCommand::SendFiberMessage(FiberMessageWithPeerId::new( @@ -1123,6 +1166,7 @@ where )) .expect(ASSUME_NETWORK_ACTOR_ALIVE); state.save_remote_nonce_for_raa(); + eprintln!("finished sent commitment_signed"); match flags { CommitmentSignedFlags::SigningCommitment(flags) => { @@ -1180,6 +1224,7 @@ where state: &mut ChannelActorState, command: RemoveTlcCommand, ) -> ProcessingChannelResult { + eprintln!("removing tlc: {:?}", command); state.check_for_tlc_update(None, true, false)?; state.check_remove_tlc_with_reason(TLCId::Received(command.id), &command.reason)?; state.tlc_state.set_received_tlc_removed( @@ -2045,8 +2090,14 @@ where match message { ChannelActorMessage::PeerMessage(message) => { - if let Err(error) = self.handle_peer_message(&myself, state, message).await { - error!("Error while processing channel message: {:?}", error); + if let Err(error) = self + .handle_peer_message(&myself, state, message.clone()) + .await + { + error!( + "Error while processing channel message: {:?} with message: {:?}", + error, message + ); #[cfg(debug_assertions)] self.network .clone() @@ -2172,6 +2223,7 @@ pub enum InboundTlctatus { AwaitingAnnouncedRemoteRevoke, Committed, LocalRemoved, + RemoveSettled, } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] @@ -2200,47 +2252,6 @@ impl TlcStatus { } } -// #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -// pub enum TlcKindV2 { -// AddTlc(TlcInfo), -// RemoveTlc(RemoveTlcInfo), -// } - -// impl TlcKindV2 { -// pub fn log(&self) -> String { -// match self { -// TlcKindV2::AddTlc(add_tlc) => { -// format!( -// "id: {:?} amount: {:?}, status: {:?}", -// &add_tlc.tlc_id, &add_tlc.amount, &add_tlc.status -// ) -// } -// TlcKindV2::RemoveTlc(remove_tlc) => { -// format!("RemoveTlc({:?})", &remove_tlc.tlc_id) -// } -// } -// } -// } - -// #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -// pub enum TlcKind { -// AddTlc(AddTlcInfo), -// RemoveTlc(RemoveTlcInfo), -// } - -// impl TlcKind { -// pub fn log(&self) -> String { -// match self { -// TlcKind::AddTlc(add_tlc) => { -// format!("{:?}", &add_tlc.tlc_id) -// } -// TlcKind::RemoveTlc(remove_tlc) => { -// format!("RemoveTlc({:?})", &remove_tlc.tlc_id) -// } -// } -// } -// } - #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub struct TlcInfo { pub channel_id: Hash256, @@ -2308,99 +2319,6 @@ impl TlcInfo { } } -// #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -// pub struct AddTlcInfo { -// pub channel_id: Hash256, -// pub tlc_id: TLCId, -// pub amount: u128, -// pub payment_hash: Hash256, -// pub expiry: u64, -// pub hash_algorithm: HashAlgorithm, -// // the onion packet for multi-hop payment -// pub onion_packet: Option, -// /// Shared secret used in forwarding. -// /// -// /// Save it to backward errors. Use all zeros when no shared secrets are available. -// pub shared_secret: [u8; 32], -// pub created_at: CommitmentNumbers, -// pub removed_at: Option<(CommitmentNumbers, RemoveTlcReason)>, -// pub payment_preimage: Option, - -// /// Note: `previous_tlc` is used to track the tlc chain for a multi-tlc payment, -// /// we need to know previous when removing tlc backwardly. -// /// -// /// Node A ---------> Node B ------------> Node C ----------> Node D -// /// tlc_1 <---> (tlc_1) (tlc_2) <---> (tlc_2) (tlc_3) <----> tlc_3 -// /// ^^^^ ^^^^ -// /// -// pub previous_tlc: Option<(Hash256, TLCId)>, -// } - -// impl AddTlcInfo { -// pub fn is_offered(&self) -> bool { -// self.tlc_id.is_offered() -// } - -// pub fn is_received(&self) -> bool { -// !self.is_offered() -// } - -// pub fn get_commitment_numbers(&self) -> CommitmentNumbers { -// self.created_at -// } - -// pub fn flip_mut(&mut self) { -// self.tlc_id.flip_mut(); -// } - -// /// Get the value for the field `htlc_type` in commitment lock witness. -// /// - Lowest 1 bit: 0 if the tlc is offered by the remote party, 1 otherwise. -// /// - High 7 bits: -// /// - 0: ckb hash -// /// - 1: sha256 -// pub fn get_htlc_type(&self) -> u8 { -// let offered_flag = if self.is_offered() { 0u8 } else { 1u8 }; -// ((self.hash_algorithm as u8) << 1) + offered_flag -// } - -// fn get_hash(&self) -> ShortHash { -// self.payment_hash.as_ref()[..20] -// .try_into() -// .expect("short hash from payment hash") -// } -// } - -// #[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] -// pub struct RemoveTlcInfo { -// pub channel_id: Hash256, -// pub tlc_id: TLCId, -// pub reason: RemoveTlcReason, -// } - -// impl TlcKind { -// pub fn tlc_id_u64(&self) -> u64 { -// match self { -// TlcKind::AddTlc(add_tlc) => add_tlc.tlc_id.into(), -// TlcKind::RemoveTlc(remove_tlc) => remove_tlc.tlc_id.into(), -// } -// } - -// pub fn tlc_id(&self) -> TLCId { -// match self { -// TlcKind::AddTlc(info) => info.tlc_id, -// TlcKind::RemoveTlc(remove_tlc) => remove_tlc.tlc_id, -// } -// } - -// pub fn is_offered(&self) -> bool { -// self.tlc_id().is_offered() -// } - -// pub fn is_received(&self) -> bool { -// !self.is_offered() -// } -// } - #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub enum RetryableRemoveTlc { RemoveTlc(TLCId, RemoveTlcReason), @@ -2462,6 +2380,15 @@ pub struct TlcState { } impl TlcState { + pub fn debug(&self) { + for tlc in self.offered_tlcs.tlcs.iter() { + eprintln!("offered_tlc: {:?}", tlc.log()); + } + for tlc in self.received_tlcs.tlcs.iter() { + eprintln!("received_tlc: {:?}", tlc.log()); + } + } + pub fn get_mut(&mut self, tlc_id: &TLCId) -> Option<&mut TlcInfo> { self.offered_tlcs .tlcs @@ -2528,6 +2455,10 @@ impl TlcState { }) } + pub fn get_committed_received_tlcs(&self) -> Vec { + self.received_tlcs.get_committed_tlcs() + } + pub fn get_next_offering(&self) -> u64 { self.offered_tlcs.get_next_id() } @@ -2591,16 +2522,14 @@ impl TlcState { .iter_mut() .find(|tlc| tlc.tlc_id == tlc_id) .map(|tlc| { - tlc.removed_at = Some((remove_at, reason.clone())); - tlc.status = TlcStatus::Outbound(OutboundTlcStatus::RemoteRemoved); + tlc.status = TlcStatus::Inbound(InboundTlctatus::RemoveSettled); }); self.received_tlcs .tlcs .iter_mut() .find(|tlc| tlc.tlc_id == tlc_id) .map(|tlc| { - tlc.removed_at = Some((remove_at, reason.clone())); - tlc.status = TlcStatus::Inbound(InboundTlctatus::LocalRemoved); + tlc.status = TlcStatus::Inbound(InboundTlctatus::RemoveSettled); }); } @@ -2684,6 +2613,7 @@ impl TlcState { InboundTlctatus::AwaitingAnnouncedRemoteRevoke => true, InboundTlctatus::Committed => true, InboundTlctatus::LocalRemoved => !local, + InboundTlctatus::RemoveSettled => false, }; if include { active_tls.push(tlc.clone()); @@ -2695,10 +2625,9 @@ impl TlcState { return active_tls; } - pub fn update_for_peer_commitment_signed(&mut self) { + pub fn update_for_peer_commitment_signed(&mut self) -> bool { for tlc in self.offered_tlcs.tlcs.iter_mut() { - let out_status = tlc.status.as_outbound_status(); - match out_status { + match tlc.status.as_outbound_status() { OutboundTlcStatus::RemoteRemoved => { let status = if self.waiting_ack { OutboundTlcStatus::AwaitingRemoteRevokeToRemove @@ -2715,8 +2644,7 @@ impl TlcState { } } for tlc in self.received_tlcs.tlcs.iter_mut() { - let out_status = tlc.status.as_inbound_status(); - match out_status { + match tlc.status.as_inbound_status() { InboundTlctatus::RemoteAnnounced => { let status = if self.waiting_ack { InboundTlctatus::AwaitingRemoteRevokeToAnnounce @@ -2728,6 +2656,7 @@ impl TlcState { _ => {} } } + self.need_another_commitment_signed() } pub fn build_ack_transaction(&self, _for_remote: bool) -> Vec { @@ -2774,7 +2703,12 @@ impl TlcState { pub fn need_another_commitment_signed(&self) -> bool { self.offered_tlcs.tlcs.iter().any(|tlc| { let status = tlc.status.as_outbound_status(); - matches!(status, OutboundTlcStatus::LocalAnnounced) + matches!( + status, + OutboundTlcStatus::LocalAnnounced + | OutboundTlcStatus::RemoteRemoved + | OutboundTlcStatus::AwaitingRemoteRevokeToRemove + ) }) || self.received_tlcs.tlcs.iter().any(|tlc| { let status = tlc.status.as_inbound_status(); matches!( @@ -4403,49 +4337,41 @@ impl ChannelActorState { ) -> Result { let removed_at = self.get_current_commitment_numbers(); let current = self.tlc_state.get(&tlc_id).expect("TLC exists").clone(); + assert_eq!( + current.status, + TlcStatus::Inbound(InboundTlctatus::LocalRemoved) + ); - match ¤t.removed_at { - Some((current_removed_at, current_remove_reason)) - if current_remove_reason == reason && removed_at == *current_removed_at => - { - debug!("Skipping removing of tlc {:?} as it is already removed at {:?} with the same reason {:?}", tlc_id, removed_at, reason); - return Err(ProcessingChannelError::RepeatedProcessing( - "TLC is already removed".to_string(), - )); + if let RemoveTlcReason::RemoveTlcFulfill(fulfill) = reason { + let filled_payment_hash: Hash256 = + current.hash_algorithm.hash(fulfill.payment_preimage).into(); + if current.payment_hash != filled_payment_hash { + return Err(ProcessingChannelError::FinalIncorrectPreimage); } - Some((current_remove_reason, current_removed_at)) => { - return Err(ProcessingChannelError::InvalidParameter( - format!("Illegally removing the same tlc: {:?} was previously removed at {:?} for {:?}, and trying to remove it again at {:?} for {:?}", - tlc_id, current_removed_at, reason, removed_at, current_remove_reason))); + + // update balance according to the tlc + let (mut to_local_amount, mut to_remote_amount) = + (self.to_local_amount, self.to_remote_amount); + if current.is_offered() { + to_local_amount -= current.amount; + to_remote_amount += current.amount; + } else { + to_local_amount += current.amount; + to_remote_amount -= current.amount; } - None => { - if let RemoveTlcReason::RemoveTlcFulfill(fulfill) = reason { - let filled_payment_hash: Hash256 = - current.hash_algorithm.hash(fulfill.payment_preimage).into(); - if current.payment_hash != filled_payment_hash { - return Err(ProcessingChannelError::FinalIncorrectPreimage); - } + eprintln!( + "before local balance: {}, remote balance: {}, current tlc amount: {:?}", + self.to_local_amount, self.to_remote_amount, current.amount + ); - // update balance according to the tlc - let (mut to_local_amount, mut to_remote_amount) = - (self.to_local_amount, self.to_remote_amount); - if current.is_offered() { - to_local_amount -= current.amount; - to_remote_amount += current.amount; - } else { - to_local_amount += current.amount; - to_remote_amount -= current.amount; - } - self.to_local_amount = to_local_amount; - self.to_remote_amount = to_remote_amount; + self.to_local_amount = to_local_amount; + self.to_remote_amount = to_remote_amount; - debug!("Updated local balance to {} and remote balance to {} by removing tlc {:?} with reason {:?}", + eprintln!("Updated local balance to {} and remote balance to {} by removing tlc {:?} with reason {:?}", to_local_amount, to_remote_amount, tlc_id, reason); - } - self.tlc_state - .apply_remove_tlc(tlc_id, removed_at, reason.clone()); - } } + self.tlc_state + .apply_remove_tlc(tlc_id, removed_at, reason.clone()); Ok(current.clone()) } @@ -4723,6 +4649,7 @@ impl ChannelActorState { b.sort_by(|x, y| u64::from(x.0.tlc_id).cmp(&u64::from(y.0.tlc_id))); [a, b].concat() }; + eprintln!("active tlcs: {:?}", tlcs); if tlcs.is_empty() { Vec::new() } else { @@ -5693,7 +5620,9 @@ impl ChannelActorState { ] .concat(), ); + verify_ctx.verify(revocation_partial_signature, message.as_slice())?; + let our_signature = sign_ctx.clone().sign(message.as_slice())?; let aggregated_signature = verify_ctx.aggregate_partial_signatures_for_msg( [revocation_partial_signature, our_signature], @@ -5749,6 +5678,7 @@ impl ChannelActorState { self.increment_local_commitment_number(); self.append_remote_commitment_point(next_per_commitment_point); + eprintln!("handle revoke and ack peer message: {:?}", &revocation_data); self.tlc_state.update_for_revoke_and_ack(); // let staging_tlcs = self.tlc_state.commit_local_tlcs(); // for tlc in staging_tlcs { @@ -6383,11 +6313,21 @@ impl ChannelActorState { &self, for_remote: bool, ) -> ([CellOutput; 2], [Bytes; 2]) { - let offered_fulfilled = self.get_pending_fulfilled_tlcs_amount(for_remote, true); - let received_fulfilled = self.get_pending_fulfilled_tlcs_amount(for_remote, false); - + // let offered_fulfilled = self.get_pending_fulfilled_tlcs_amount(for_remote, true); + // let received_fulfilled = self.get_pending_fulfilled_tlcs_amount(for_remote, false); + + let offered_fulfilled = 0; + let received_fulfilled = 0; + eprintln!( + "self.to_local_amount: {}, self.to_remote_amount: {}", + self.to_local_amount, self.to_remote_amount + ); let to_local_value = self.to_local_amount - offered_fulfilled + received_fulfilled; let to_remote_value = self.to_remote_amount - received_fulfilled + offered_fulfilled; + eprintln!( + "debug now to_local_value: {}, to_remote_value: {}", + to_local_value, to_remote_value + ); let commitment_tx_fee = calculate_commitment_tx_fee(self.commitment_fee_rate, &self.funding_udt_type_script); diff --git a/src/fiber/network.rs b/src/fiber/network.rs index f5b9cc709..c1ba02924 100644 --- a/src/fiber/network.rs +++ b/src/fiber/network.rs @@ -888,6 +888,7 @@ where } NetworkActorEvent::TlcRemoveReceived(payment_hash, remove_tlc_reason) => { // When a node is restarted, RemoveTLC will also be resent if necessary + eprintln!("got message RemoveTLC for payment_hash: {:?}", payment_hash); self.on_remove_tlc_event(myself, state, payment_hash, remove_tlc_reason) .await; } @@ -1431,6 +1432,7 @@ where reason: RemoveTlcReason, ) { if let Some(mut payment_session) = self.store.get_payment_session(payment_hash) { + eprintln!("got remove tlc event: {:?}", reason); if payment_session.status == PaymentSessionStatus::Inflight { match reason { RemoveTlcReason::RemoveTlcFulfill(_) => { @@ -1469,6 +1471,8 @@ where } } } + } else { + eprintln!("payment session not found: {:?}", payment_hash); } } @@ -1518,7 +1522,10 @@ where fn on_get_payment(&self, payment_hash: &Hash256) -> Result { match self.store.get_payment_session(*payment_hash) { - Some(payment_session) => Ok(payment_session.into()), + Some(payment_session) => { + eprintln!("hahah got payment session: {:?}", payment_session); + Ok(payment_session.into()) + } None => Err(Error::InvalidParameter(format!( "Payment session not found: {:?}", payment_hash @@ -1720,6 +1727,7 @@ where let payment_session = PaymentSession::new(payment_data, 5); self.store.insert_payment_session(payment_session.clone()); + eprintln!("payment session created: {:?}", payment_session); let session = self .try_payment_session(myself, state, payment_session.payment_hash()) .await?; diff --git a/src/fiber/tests/payment.rs b/src/fiber/tests/payment.rs index 692c0fba8..ca1ae0352 100644 --- a/src/fiber/tests/payment.rs +++ b/src/fiber/tests/payment.rs @@ -2,6 +2,9 @@ use super::test_utils::init_tracing; use crate::fiber::graph::PaymentSessionStatus; use crate::fiber::network::SendPaymentCommand; use crate::fiber::tests::test_utils::*; +use crate::fiber::NetworkActorCommand; +use crate::fiber::NetworkActorMessage; +use ractor::call; #[tokio::test] async fn test_send_payment_for_direct_channel_and_dry_run() { @@ -67,51 +70,62 @@ async fn test_send_payment_for_direct_channel_and_dry_run() { eprintln!("res: {:?}", res); assert!(res.is_ok()); // sleep for a while - tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + tokio::time::sleep(tokio::time::Duration::from_secs(4)).await; let payment_hash = res.unwrap().payment_hash; - source_node - .assert_payment_status(payment_hash, PaymentSessionStatus::Success, Some(1)) - .await; - let res = node_1 - .send_payment(SendPaymentCommand { - target_pubkey: Some(source_node.pubkey.clone()), - amount: Some(10000000000), - payment_hash: None, - final_tlc_expiry_delta: None, - tlc_expiry_limit: 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, - }) - .await; + eprintln!("begin to get payment status: {:?}", payment_hash); + let message = |rpc_reply| -> NetworkActorMessage { + NetworkActorMessage::Command(NetworkActorCommand::GetPayment(payment_hash, rpc_reply)) + }; + let res = call!(source_node.network_actor, message) + .expect("node_a alive") + .unwrap(); - eprintln!("res: {:?}", res); - assert!(res.is_ok()); + assert_eq!(res.status, PaymentSessionStatus::Success); - // sleep for a while - tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; - let payment_hash = res.unwrap().payment_hash; - node_1 - .assert_payment_status(payment_hash, PaymentSessionStatus::Success, Some(1)) - .await; + // source_node + // .assert_payment_status(payment_hash, PaymentSessionStatus::Success, Some(1)) + // .await; + + // let res = node_1 + // .send_payment(SendPaymentCommand { + // target_pubkey: Some(source_node.pubkey.clone()), + // amount: Some(10000000000), + // payment_hash: None, + // final_tlc_expiry_delta: None, + // tlc_expiry_limit: 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, + // }) + // .await; + + // eprintln!("res: {:?}", res); + // assert!(res.is_ok()); + + // // sleep for a while + // tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; + // let payment_hash = res.unwrap().payment_hash; + // node_1 + // .assert_payment_status(payment_hash, PaymentSessionStatus::Success, Some(1)) + // .await; - let node_0_balance = source_node.get_local_balance_from_channel(channels[0]); - let node_1_balance = node_1.get_local_balance_from_channel(channels[0]); + // let node_0_balance = source_node.get_local_balance_from_channel(channels[0]); + // let node_1_balance = node_1.get_local_balance_from_channel(channels[0]); - // A -> B: 10000000000 use the first channel - assert_eq!(node_0_balance, 0); - assert_eq!(node_1_balance, 10000000000); + // // A -> B: 10000000000 use the first channel + // assert_eq!(node_0_balance, 0); + // assert_eq!(node_1_balance, 10000000000); - let node_0_balance = source_node.get_local_balance_from_channel(channels[1]); - let node_1_balance = node_1.get_local_balance_from_channel(channels[1]); + // let node_0_balance = source_node.get_local_balance_from_channel(channels[1]); + // let node_1_balance = node_1.get_local_balance_from_channel(channels[1]); - // B -> A: 10000000000 use the second channel - assert_eq!(node_0_balance, 10000000000); - assert_eq!(node_1_balance, 0); + // // B -> A: 10000000000 use the second channel + // assert_eq!(node_0_balance, 10000000000); + // assert_eq!(node_1_balance, 0); } diff --git a/src/store/store.rs b/src/store/store.rs index ce6e30020..dd60578d7 100644 --- a/src/store/store.rs +++ b/src/store/store.rs @@ -506,8 +506,12 @@ impl InvoiceStore for Store { impl NetworkGraphStateStore for Store { fn get_payment_session(&self, payment_hash: Hash256) -> Option { let prefix = [&[PAYMENT_SESSION_PREFIX], payment_hash.as_ref()].concat(); - self.get(prefix) - .map(|v| deserialize_from(v.as_ref(), "PaymentSession")) + eprintln!("get payment session: {:?}", prefix); + let res = self + .get(prefix) + .map(|v| deserialize_from(v.as_ref(), "PaymentSession")); + eprintln!("got res: {:?}", res); + res } fn insert_payment_session(&self, session: PaymentSession) { From 5c8badb9cd8319056ad7929455dc67c40b2de03f Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 24 Dec 2024 10:59:29 +0800 Subject: [PATCH 019/119] passed first send_payment --- src/fiber/channel.rs | 208 +++++++++++++++++++++------------- src/fiber/network.rs | 10 +- src/fiber/tests/payment.rs | 89 +++++++-------- src/fiber/tests/test_utils.rs | 1 + src/fiber/tests/tlc_op.rs | 2 +- src/store/store.rs | 9 +- 6 files changed, 178 insertions(+), 141 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 0867d92ce..4479a1fff 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -650,20 +650,39 @@ where async fn handle_commitment_signed_peer_message( &self, - _myself: &ActorRef, + myself: &ActorRef, state: &mut ChannelActorState, commitment_signed: CommitmentSigned, ) -> Result<(), ProcessingChannelError> { + eprintln!("handle_commitment_signed_peer_message ...."); // build commitment tx and verify signature from remote, if passed send ACK for partner state.verify_commitment_signed_and_send_ack(commitment_signed, &self.network)?; - let need_commitment_signed = state.tlc_state.update_for_peer_commitment_signed(); + let need_commitment_signed = state.tlc_state.update_for_commitment_signed(); if need_commitment_signed { eprintln!("sending commitment_signed after first ack"); self.handle_commitment_signed_command(state)?; - } else { - eprintln!("not sending commitment_signed after first ack"); } + + // flush remove tlc for received tlcs after replying ack for peer + let mut settled_tlcs = vec![]; + for tlc in state.tlc_state.received_tlcs.tlcs.iter_mut() { + if let Some((_removed_at, reason)) = &tlc.removed_at { + if matches!( + tlc.status, + TlcStatus::Inbound(InboundTlctatus::RemoveAckConfirmed) + ) { + settled_tlcs.push((tlc.tlc_id, reason.clone())); + } + } + } + for (tlc_id, reason) in settled_tlcs.iter() { + eprintln!("apply received tlc remove: {:?}", tlc_id); + let _ = self + .apply_remove_tlc_operation(myself, state, *tlc_id, reason.clone()) + .await; + } + Ok(()) } @@ -686,10 +705,8 @@ where } } for (tlc_id, reason) in settled_tlcs.iter() { - eprintln!("apply offered tlc remove: {:?}", tlc_id); - let _ = self - .apply_remove_tlc_operation(myself, state, *tlc_id, reason.clone()) - .await; + let tlc = state.tlc_state.get_mut(tlc_id).expect("tlc"); + tlc.status = TlcStatus::Inbound(InboundTlctatus::RemoveAckConfirmed); } // flush outbound tlcs @@ -698,12 +715,18 @@ where if let Some((_removed_at, reason)) = &tlc.removed_at { if matches!( tlc.status, - TlcStatus::Outbound(OutboundTlcStatus::AwaitingRemovedRemoteRevoke) + TlcStatus::Outbound(OutboundTlcStatus::RemoveAckConfirmed) ) { settled_tlcs.push((tlc.tlc_id, reason.clone())); } } } + for (tlc_id, reason) in settled_tlcs.iter() { + eprintln!("apply offered tlc remove: {:?}", tlc_id); + let _ = self + .apply_remove_tlc_operation(myself, state, *tlc_id, reason.clone()) + .await; + } let apply_tlcs = state.tlc_state.get_committed_received_tlcs(); eprintln!("flushing {} tlcs", apply_tlcs.len()); @@ -1024,6 +1047,7 @@ where state.get_current_commitment_numbers(), remove_tlc.reason, ); + eprintln!("remove tlc peer: {:?}", remove_tlc.tlc_id); Ok(()) } @@ -1036,9 +1060,12 @@ where ) -> Result<(), ProcessingChannelError> { let channel_id = state.get_id(); let remove_reason = reason.clone(); + eprintln!("apply remove tlc operation: {:?}", tlc_id); + let tlc_info = state .remove_tlc_with_reason(tlc_id, &remove_reason) .expect("expect remove tlc successfully"); + if let ( Some(ref udt_type_script), RemoveTlcReason::RemoveTlcFulfill(RemoveTlcFulfill { payment_preimage }), @@ -1057,16 +1084,26 @@ where if tlc_info.previous_tlc.is_none() { // only the original sender of the TLC should send `TlcRemoveReceived` event // because only the original sender cares about the TLC event to settle the payment - eprintln!("sending TlcRemoveReceived ...: {:?}", tlc_info.payment_hash); - self.network - .send_message(NetworkActorMessage::new_event( - NetworkActorEvent::TlcRemoveReceived( - tlc_info.payment_hash, - remove_reason.clone(), - ), - )) - .expect("myself alive"); + eprintln!( + "hahaha sending TlcRemoveReceived ...: {:?} tlc_id: {:?}", + tlc_info.payment_hash, tlc_info.tlc_id + ); + if tlc_info.is_offered() { + eprintln!("sending TlcRemoveReceived ...: {:?}", tlc_info.payment_hash); + self.network + .send_message(NetworkActorMessage::new_event( + NetworkActorEvent::TlcRemoveReceived( + tlc_info.payment_hash, + remove_reason.clone(), + ), + )) + .expect("myself alive"); + } } else { + eprintln!( + "now we need to relay remove tlc ...: {:?} tlc_id: {:?}", + tlc_info.payment_hash, tlc_info.tlc_id + ); // relay RemoveTlc to previous channel if needed self.try_to_relay_remove_tlc(myself, state, &tlc_info, remove_reason) .await; @@ -1107,6 +1144,7 @@ where &self, state: &mut ChannelActorState, ) -> ProcessingChannelResult { + eprintln!("begin to handle commitment_signed command ..."); let flags = match state.state { ChannelState::CollaboratingFundingTx(flags) if !flags.contains(CollaboratingFundingTxFlags::COLLABRATION_COMPLETED) => @@ -2212,17 +2250,19 @@ pub enum OutboundTlcStatus { LocalAnnounced, Committed, RemoteRemoved, - AwaitingRemoteRevokeToRemove, - AwaitingRemovedRemoteRevoke, + RemoveWaitPrevAck, + RemoveWaitAck, + RemoveAckConfirmed, } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub enum InboundTlctatus { RemoteAnnounced, - AwaitingRemoteRevokeToAnnounce, - AwaitingAnnouncedRemoteRevoke, + AnnounceWaitPrevAck, + AnnounceWaitAck, Committed, LocalRemoved, + RemoveAckConfirmed, RemoveSettled, } @@ -2598,8 +2638,9 @@ impl TlcState { OutboundTlcStatus::LocalAnnounced => local, OutboundTlcStatus::Committed => true, OutboundTlcStatus::RemoteRemoved => local, - OutboundTlcStatus::AwaitingRemoteRevokeToRemove => local, - OutboundTlcStatus::AwaitingRemovedRemoteRevoke => false, + OutboundTlcStatus::RemoveWaitPrevAck => local, + OutboundTlcStatus::RemoveWaitAck => false, + OutboundTlcStatus::RemoveAckConfirmed => false, }; if include { active_tls.push(tlc.clone()); @@ -2609,10 +2650,11 @@ impl TlcState { let status = tlc.status.as_inbound_status(); let include = match status { InboundTlctatus::RemoteAnnounced => !local, - InboundTlctatus::AwaitingRemoteRevokeToAnnounce => !local, - InboundTlctatus::AwaitingAnnouncedRemoteRevoke => true, + InboundTlctatus::AnnounceWaitPrevAck => !local, + InboundTlctatus::AnnounceWaitAck => true, InboundTlctatus::Committed => true, InboundTlctatus::LocalRemoved => !local, + InboundTlctatus::RemoveAckConfirmed => !local, InboundTlctatus::RemoveSettled => false, }; if include { @@ -2625,21 +2667,19 @@ impl TlcState { return active_tls; } - pub fn update_for_peer_commitment_signed(&mut self) -> bool { + pub fn update_for_commitment_signed(&mut self) -> bool { + let mut need_another_commitment_signed = false; for tlc in self.offered_tlcs.tlcs.iter_mut() { match tlc.status.as_outbound_status() { OutboundTlcStatus::RemoteRemoved => { let status = if self.waiting_ack { - OutboundTlcStatus::AwaitingRemoteRevokeToRemove + need_another_commitment_signed = true; + OutboundTlcStatus::RemoveWaitPrevAck } else { - OutboundTlcStatus::AwaitingRemovedRemoteRevoke + OutboundTlcStatus::RemoveWaitAck }; tlc.status = TlcStatus::Outbound(status); } - OutboundTlcStatus::AwaitingRemoteRevokeToRemove => { - tlc.status = - TlcStatus::Outbound(OutboundTlcStatus::AwaitingRemovedRemoteRevoke); - } _ => {} } } @@ -2647,27 +2687,18 @@ impl TlcState { match tlc.status.as_inbound_status() { InboundTlctatus::RemoteAnnounced => { let status = if self.waiting_ack { - InboundTlctatus::AwaitingRemoteRevokeToAnnounce + need_another_commitment_signed = true; + InboundTlctatus::AnnounceWaitPrevAck } else { - InboundTlctatus::AwaitingAnnouncedRemoteRevoke + InboundTlctatus::AnnounceWaitAck }; tlc.status = TlcStatus::Inbound(status) } _ => {} } } - self.need_another_commitment_signed() - } - - pub fn build_ack_transaction(&self, _for_remote: bool) -> Vec { - let mut active_tls = vec![]; - for tlc in self.offered_tlcs.tlcs.iter() { - active_tls.push(tlc.clone()); - } - for tlc in self.received_tlcs.tlcs.iter() { - active_tls.push(tlc.clone()); - } - return active_tls; + let res = need_another_commitment_signed || self.need_another_commitment_signed(); + res } pub fn update_for_revoke_and_ack(&mut self) { @@ -2678,9 +2709,11 @@ impl TlcState { OutboundTlcStatus::LocalAnnounced => { tlc.status = TlcStatus::Outbound(OutboundTlcStatus::Committed); } - OutboundTlcStatus::AwaitingRemoteRevokeToRemove => { - tlc.status = - TlcStatus::Outbound(OutboundTlcStatus::AwaitingRemovedRemoteRevoke); + OutboundTlcStatus::RemoveWaitPrevAck => { + tlc.status = TlcStatus::Outbound(OutboundTlcStatus::RemoveWaitAck); + } + OutboundTlcStatus::RemoveWaitAck => { + tlc.status = TlcStatus::Outbound(OutboundTlcStatus::RemoveAckConfirmed); } _ => {} } @@ -2689,10 +2722,10 @@ impl TlcState { for tlc in self.received_tlcs.tlcs.iter_mut() { let in_status = tlc.status.as_inbound_status(); match in_status { - InboundTlctatus::AwaitingRemoteRevokeToAnnounce => { - tlc.status = TlcStatus::Inbound(InboundTlctatus::AwaitingAnnouncedRemoteRevoke); + InboundTlctatus::AnnounceWaitPrevAck => { + tlc.status = TlcStatus::Inbound(InboundTlctatus::AnnounceWaitAck); } - InboundTlctatus::AwaitingAnnouncedRemoteRevoke => { + InboundTlctatus::AnnounceWaitAck => { tlc.status = TlcStatus::Inbound(InboundTlctatus::Committed); } _ => {} @@ -2700,6 +2733,17 @@ impl TlcState { } } + pub fn build_ack_transaction(&self, _for_remote: bool) -> Vec { + let mut active_tls = vec![]; + for tlc in self.offered_tlcs.tlcs.iter() { + active_tls.push(tlc.clone()); + } + for tlc in self.received_tlcs.tlcs.iter() { + active_tls.push(tlc.clone()); + } + return active_tls; + } + pub fn need_another_commitment_signed(&self) -> bool { self.offered_tlcs.tlcs.iter().any(|tlc| { let status = tlc.status.as_outbound_status(); @@ -2707,15 +2751,16 @@ impl TlcState { status, OutboundTlcStatus::LocalAnnounced | OutboundTlcStatus::RemoteRemoved - | OutboundTlcStatus::AwaitingRemoteRevokeToRemove + | OutboundTlcStatus::RemoveWaitPrevAck + | OutboundTlcStatus::RemoveWaitAck ) }) || self.received_tlcs.tlcs.iter().any(|tlc| { let status = tlc.status.as_inbound_status(); matches!( status, InboundTlctatus::RemoteAnnounced - | InboundTlctatus::AwaitingRemoteRevokeToAnnounce - | InboundTlctatus::AwaitingAnnouncedRemoteRevoke + | InboundTlctatus::AnnounceWaitPrevAck + | InboundTlctatus::AnnounceWaitAck ) }) } @@ -4337,10 +4382,11 @@ impl ChannelActorState { ) -> Result { let removed_at = self.get_current_commitment_numbers(); let current = self.tlc_state.get(&tlc_id).expect("TLC exists").clone(); - assert_eq!( + assert!(matches!( current.status, - TlcStatus::Inbound(InboundTlctatus::LocalRemoved) - ); + TlcStatus::Inbound(InboundTlctatus::RemoveAckConfirmed) + | TlcStatus::Outbound(OutboundTlcStatus::RemoveAckConfirmed) + )); if let RemoveTlcReason::RemoveTlcFulfill(fulfill) = reason { let filled_payment_hash: Hash256 = @@ -4547,24 +4593,32 @@ impl ChannelActorState { } // Get the total amount of pending tlcs that are fulfilled - fn get_pending_fulfilled_tlcs_amount(&self, for_remote: bool, _offered: bool) -> u128 { - let (local_pending_tlcs, remote_pending_tlcs) = if for_remote { - (&self.tlc_state.offered_tlcs, &self.tlc_state.received_tlcs) + fn get_pending_fulfilled_tlcs_amount(&self, for_remote: bool, offered: bool) -> u128 { + let tlcs = if offered { + &self.tlc_state.offered_tlcs } else { - (&self.tlc_state.received_tlcs, &self.tlc_state.offered_tlcs) + &self.tlc_state.received_tlcs }; + let mut fulfilled = 0; - local_pending_tlcs - .get_committed_tlcs() - .into_iter() - .chain(remote_pending_tlcs.get_committed_tlcs().into_iter()) - .for_each(|tlc| { - if let Some(remove_at) = tlc.removed_at { - if matches!(remove_at.1, RemoveTlcReason::RemoveTlcFulfill(_)) { - fulfilled += tlc.amount; + tlcs.tlcs.iter().for_each(|tlc| { + if let Some(remove_at) = &tlc.removed_at { + let mut include = false; + match tlc.status { + TlcStatus::Inbound(InboundTlctatus::LocalRemoved) => { + include = for_remote; + } + TlcStatus::Outbound(OutboundTlcStatus::RemoveWaitPrevAck) + | TlcStatus::Outbound(OutboundTlcStatus::RemoteRemoved) => { + include = !for_remote; } + _ => {} } - }); + if include && matches!(remove_at.1, RemoveTlcReason::RemoveTlcFulfill(_)) { + fulfilled += tlc.amount; + } + } + }); fulfilled } @@ -6313,14 +6367,12 @@ impl ChannelActorState { &self, for_remote: bool, ) -> ([CellOutput; 2], [Bytes; 2]) { - // let offered_fulfilled = self.get_pending_fulfilled_tlcs_amount(for_remote, true); - // let received_fulfilled = self.get_pending_fulfilled_tlcs_amount(for_remote, false); + let offered_fulfilled = self.get_pending_fulfilled_tlcs_amount(for_remote, true); + let received_fulfilled = self.get_pending_fulfilled_tlcs_amount(for_remote, false); - let offered_fulfilled = 0; - let received_fulfilled = 0; eprintln!( - "self.to_local_amount: {}, self.to_remote_amount: {}", - self.to_local_amount, self.to_remote_amount + "self.to_local_amount: {}, self.to_remote_amount: {}, offered_fulfilled: {}, received_fulfilled: {}, for_remote: {}", + self.to_local_amount, self.to_remote_amount, offered_fulfilled, received_fulfilled, for_remote ); let to_local_value = self.to_local_amount - offered_fulfilled + received_fulfilled; let to_remote_value = self.to_remote_amount - received_fulfilled + offered_fulfilled; diff --git a/src/fiber/network.rs b/src/fiber/network.rs index c1ba02924..f5b9cc709 100644 --- a/src/fiber/network.rs +++ b/src/fiber/network.rs @@ -888,7 +888,6 @@ where } NetworkActorEvent::TlcRemoveReceived(payment_hash, remove_tlc_reason) => { // When a node is restarted, RemoveTLC will also be resent if necessary - eprintln!("got message RemoveTLC for payment_hash: {:?}", payment_hash); self.on_remove_tlc_event(myself, state, payment_hash, remove_tlc_reason) .await; } @@ -1432,7 +1431,6 @@ where reason: RemoveTlcReason, ) { if let Some(mut payment_session) = self.store.get_payment_session(payment_hash) { - eprintln!("got remove tlc event: {:?}", reason); if payment_session.status == PaymentSessionStatus::Inflight { match reason { RemoveTlcReason::RemoveTlcFulfill(_) => { @@ -1471,8 +1469,6 @@ where } } } - } else { - eprintln!("payment session not found: {:?}", payment_hash); } } @@ -1522,10 +1518,7 @@ where fn on_get_payment(&self, payment_hash: &Hash256) -> Result { match self.store.get_payment_session(*payment_hash) { - Some(payment_session) => { - eprintln!("hahah got payment session: {:?}", payment_session); - Ok(payment_session.into()) - } + Some(payment_session) => Ok(payment_session.into()), None => Err(Error::InvalidParameter(format!( "Payment session not found: {:?}", payment_hash @@ -1727,7 +1720,6 @@ where let payment_session = PaymentSession::new(payment_data, 5); self.store.insert_payment_session(payment_session.clone()); - eprintln!("payment session created: {:?}", payment_session); let session = self .try_payment_session(myself, state, payment_session.payment_hash()) .await?; diff --git a/src/fiber/tests/payment.rs b/src/fiber/tests/payment.rs index ca1ae0352..5a7ff3263 100644 --- a/src/fiber/tests/payment.rs +++ b/src/fiber/tests/payment.rs @@ -70,10 +70,9 @@ async fn test_send_payment_for_direct_channel_and_dry_run() { eprintln!("res: {:?}", res); assert!(res.is_ok()); // sleep for a while - tokio::time::sleep(tokio::time::Duration::from_secs(4)).await; + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; let payment_hash = res.unwrap().payment_hash; - eprintln!("begin to get payment status: {:?}", payment_hash); let message = |rpc_reply| -> NetworkActorMessage { NetworkActorMessage::Command(NetworkActorCommand::GetPayment(payment_hash, rpc_reply)) }; @@ -83,49 +82,45 @@ async fn test_send_payment_for_direct_channel_and_dry_run() { assert_eq!(res.status, PaymentSessionStatus::Success); - // source_node - // .assert_payment_status(payment_hash, PaymentSessionStatus::Success, Some(1)) - // .await; - - // let res = node_1 - // .send_payment(SendPaymentCommand { - // target_pubkey: Some(source_node.pubkey.clone()), - // amount: Some(10000000000), - // payment_hash: None, - // final_tlc_expiry_delta: None, - // tlc_expiry_limit: 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, - // }) - // .await; - - // eprintln!("res: {:?}", res); - // assert!(res.is_ok()); - - // // sleep for a while - // tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; - // let payment_hash = res.unwrap().payment_hash; - // node_1 - // .assert_payment_status(payment_hash, PaymentSessionStatus::Success, Some(1)) - // .await; - - // let node_0_balance = source_node.get_local_balance_from_channel(channels[0]); - // let node_1_balance = node_1.get_local_balance_from_channel(channels[0]); - - // // A -> B: 10000000000 use the first channel - // assert_eq!(node_0_balance, 0); - // assert_eq!(node_1_balance, 10000000000); - - // let node_0_balance = source_node.get_local_balance_from_channel(channels[1]); - // let node_1_balance = node_1.get_local_balance_from_channel(channels[1]); - - // // B -> A: 10000000000 use the second channel - // assert_eq!(node_0_balance, 10000000000); - // assert_eq!(node_1_balance, 0); + let res = node_1 + .send_payment(SendPaymentCommand { + target_pubkey: Some(source_node.pubkey.clone()), + amount: Some(10000000000), + payment_hash: None, + final_tlc_expiry_delta: None, + tlc_expiry_limit: 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, + }) + .await; + + eprintln!("res: {:?}", res); + assert!(res.is_ok()); + + // sleep for a while + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; + let payment_hash = res.unwrap().payment_hash; + node_1 + .assert_payment_status(payment_hash, PaymentSessionStatus::Success, Some(1)) + .await; + + let node_0_balance = source_node.get_local_balance_from_channel(channels[0]); + let node_1_balance = node_1.get_local_balance_from_channel(channels[0]); + + // A -> B: 10000000000 use the first channel + assert_eq!(node_0_balance, 0); + assert_eq!(node_1_balance, 10000000000); + + let node_0_balance = source_node.get_local_balance_from_channel(channels[1]); + let node_1_balance = node_1.get_local_balance_from_channel(channels[1]); + + // B -> A: 10000000000 use the second channel + assert_eq!(node_0_balance, 10000000000); + assert_eq!(node_1_balance, 0); } diff --git a/src/fiber/tests/test_utils.rs b/src/fiber/tests/test_utils.rs index bfff4f2ff..82fbcc6d1 100644 --- a/src/fiber/tests/test_utils.rs +++ b/src/fiber/tests/test_utils.rs @@ -161,6 +161,7 @@ pub fn mock_ecdsa_signature() -> EcdsaSignature { pub fn generate_store() -> Store { let temp_dir = TempDir::new("test-fnn-node"); + eprintln!("Store directory: {:?}", temp_dir.as_ref()); let store = Store::new(temp_dir.as_ref()); store.expect("create store") } diff --git a/src/fiber/tests/tlc_op.rs b/src/fiber/tests/tlc_op.rs index bf0776847..aeb9a3da6 100644 --- a/src/fiber/tests/tlc_op.rs +++ b/src/fiber/tests/tlc_op.rs @@ -300,7 +300,7 @@ impl Actor for TlcActor { let peer = state.get_peer(); - state.tlc_state.update_for_peer_commitment_signed(); + state.tlc_state.update_for_commitment_signed(); eprintln!("sending peer revoke and ack ...."); let tlcs = state.tlc_state.build_ack_transaction(true); diff --git a/src/store/store.rs b/src/store/store.rs index dd60578d7..e44b00f4c 100644 --- a/src/store/store.rs +++ b/src/store/store.rs @@ -97,6 +97,7 @@ impl Store { let mut options = Options::default(); options.create_if_missing(true); options.set_compression_type(DBCompressionType::Lz4); + // dump trace log to /tmp/rocksdb.log let db = Arc::new(DB::open(&options, path).map_err(|e| e.to_string())?); Ok(db) } @@ -506,12 +507,8 @@ impl InvoiceStore for Store { impl NetworkGraphStateStore for Store { fn get_payment_session(&self, payment_hash: Hash256) -> Option { let prefix = [&[PAYMENT_SESSION_PREFIX], payment_hash.as_ref()].concat(); - eprintln!("get payment session: {:?}", prefix); - let res = self - .get(prefix) - .map(|v| deserialize_from(v.as_ref(), "PaymentSession")); - eprintln!("got res: {:?}", res); - res + self.get(prefix) + .map(|v| deserialize_from(v.as_ref(), "PaymentSession")) } fn insert_payment_session(&self, session: PaymentSession) { From ed035a56126501a427fa023b2a9b385f7e09077d Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 24 Dec 2024 11:36:39 +0800 Subject: [PATCH 020/119] fix wait ack --- src/fiber/channel.rs | 27 +++++++++++---------------- src/fiber/tests/channel.rs | 3 ++- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 4479a1fff..0ff510f0b 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -654,7 +654,10 @@ where state: &mut ChannelActorState, commitment_signed: CommitmentSigned, ) -> Result<(), ProcessingChannelError> { - eprintln!("handle_commitment_signed_peer_message ...."); + eprintln!( + "handle_commitment_signed_peer_message .... : {}", + state.tlc_state.waiting_ack + ); // build commitment tx and verify signature from remote, if passed send ACK for partner state.verify_commitment_signed_and_send_ack(commitment_signed, &self.network)?; let need_commitment_signed = state.tlc_state.update_for_commitment_signed(); @@ -2516,6 +2519,7 @@ impl TlcState { } pub fn set_waiting_ack(&mut self, waiting_ack: bool) { + eprintln!("now seting waiting ack to {:?}", waiting_ack); self.waiting_ack = waiting_ack; } @@ -2661,9 +2665,6 @@ impl TlcState { active_tls.push(tlc.clone()); } } - if !local { - self.set_waiting_ack(true); - } return active_tls; } @@ -2674,6 +2675,7 @@ impl TlcState { OutboundTlcStatus::RemoteRemoved => { let status = if self.waiting_ack { need_another_commitment_signed = true; + eprintln!("hahah !!!!!!!!!!!!!!!!!!!!!!!!!!!"); OutboundTlcStatus::RemoveWaitPrevAck } else { OutboundTlcStatus::RemoveWaitAck @@ -2688,6 +2690,7 @@ impl TlcState { InboundTlctatus::RemoteAnnounced => { let status = if self.waiting_ack { need_another_commitment_signed = true; + eprintln!("remote anounced ....................."); InboundTlctatus::AnnounceWaitPrevAck } else { InboundTlctatus::AnnounceWaitAck @@ -4703,7 +4706,7 @@ impl ChannelActorState { b.sort_by(|x, y| u64::from(x.0.tlc_id).cmp(&u64::from(y.0.tlc_id))); [a, b].concat() }; - eprintln!("active tlcs: {:?}", tlcs); + //eprintln!("active tlcs: {:?}", tlcs); if tlcs.is_empty() { Vec::new() } else { @@ -4823,9 +4826,9 @@ impl ChannelActorState { is_tlc_command_message: bool, is_sent: bool, ) -> ProcessingChannelResult { - if is_tlc_command_message && self.tlc_state.waiting_ack { - return Err(ProcessingChannelError::WaitingTlcAck); - } + // if is_tlc_command_message { + // return Err(ProcessingChannelError::WaitingTlcAck); + // } match self.state { ChannelState::ChannelReady() => {} ChannelState::ShuttingDown(_) if add_tlc_amount.is_none() => {} @@ -5734,14 +5737,6 @@ impl ChannelActorState { eprintln!("handle revoke and ack peer message: {:?}", &revocation_data); self.tlc_state.update_for_revoke_and_ack(); - // let staging_tlcs = self.tlc_state.commit_local_tlcs(); - // for tlc in staging_tlcs { - // if let TlcKind::RemoveTlc(remove_tlc) = tlc { - // self.remove_tlc_with_reason(remove_tlc.tlc_id, &remove_tlc.reason) - // .expect("expect remove tlc successfully"); - // } - // } - // self.tlc_state.set_waiting_ack(false); network .send_message(NetworkActorMessage::new_notification( diff --git a/src/fiber/tests/channel.rs b/src/fiber/tests/channel.rs index ea3b9364b..41b190e9e 100644 --- a/src/fiber/tests/channel.rs +++ b/src/fiber/tests/channel.rs @@ -2773,7 +2773,7 @@ async fn do_test_add_tlc_min_tlc_value_limit() { .expect("node_b alive"); assert!(add_tlc_result.is_ok()); // sleep for a while to make sure the AddTlc processed by both party - tokio::time::sleep(tokio::time::Duration::from_millis(300)).await; + tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; // B -> A can send at least 100 let tlc_amount = 100; @@ -2795,6 +2795,7 @@ async fn do_test_add_tlc_min_tlc_value_limit() { )) }) .expect("node_b alive"); + eprintln!("add_local_tlc_result: {:?}", add_tlc_result); assert!(add_tlc_result.is_ok()); } From e2cf7ff79cd888c1db40b06a3a1520c67274270c Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 24 Dec 2024 11:41:01 +0800 Subject: [PATCH 021/119] fix invalid tlc status --- src/fiber/channel.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 0ff510f0b..ae2448699 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -2256,6 +2256,7 @@ pub enum OutboundTlcStatus { RemoveWaitPrevAck, RemoveWaitAck, RemoveAckConfirmed, + RemoveSettled, } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] @@ -2566,7 +2567,7 @@ impl TlcState { .iter_mut() .find(|tlc| tlc.tlc_id == tlc_id) .map(|tlc| { - tlc.status = TlcStatus::Inbound(InboundTlctatus::RemoveSettled); + tlc.status = TlcStatus::Outbound(OutboundTlcStatus::RemoveSettled); }); self.received_tlcs .tlcs @@ -2645,6 +2646,7 @@ impl TlcState { OutboundTlcStatus::RemoveWaitPrevAck => local, OutboundTlcStatus::RemoveWaitAck => false, OutboundTlcStatus::RemoveAckConfirmed => false, + OutboundTlcStatus::RemoveSettled => false, }; if include { active_tls.push(tlc.clone()); From 35896389273fb335d6ece6d34d0c3a3b05b6eb53 Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 24 Dec 2024 11:53:50 +0800 Subject: [PATCH 022/119] cleanup tlcs when remove tlc is settled --- src/fiber/channel.rs | 38 +++++++++----------------------------- src/fiber/tests/channel.rs | 2 +- 2 files changed, 10 insertions(+), 30 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index ae2448699..6b8d2a472 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -707,7 +707,7 @@ where } } } - for (tlc_id, reason) in settled_tlcs.iter() { + for (tlc_id, _reason) in settled_tlcs.iter() { let tlc = state.tlc_state.get_mut(tlc_id).expect("tlc"); tlc.status = TlcStatus::Inbound(InboundTlctatus::RemoveAckConfirmed); } @@ -1087,10 +1087,6 @@ where if tlc_info.previous_tlc.is_none() { // only the original sender of the TLC should send `TlcRemoveReceived` event // because only the original sender cares about the TLC event to settle the payment - eprintln!( - "hahaha sending TlcRemoveReceived ...: {:?} tlc_id: {:?}", - tlc_info.payment_hash, tlc_info.tlc_id - ); if tlc_info.is_offered() { eprintln!("sending TlcRemoveReceived ...: {:?}", tlc_info.payment_hash); self.network @@ -2556,26 +2552,12 @@ impl TlcState { }) } - pub fn apply_remove_tlc( - &mut self, - tlc_id: TLCId, - remove_at: CommitmentNumbers, - reason: RemoveTlcReason, - ) { - self.offered_tlcs - .tlcs - .iter_mut() - .find(|tlc| tlc.tlc_id == tlc_id) - .map(|tlc| { - tlc.status = TlcStatus::Outbound(OutboundTlcStatus::RemoveSettled); - }); - self.received_tlcs - .tlcs - .iter_mut() - .find(|tlc| tlc.tlc_id == tlc_id) - .map(|tlc| { - tlc.status = TlcStatus::Inbound(InboundTlctatus::RemoveSettled); - }); + pub fn apply_remove_tlc(&mut self, tlc_id: TLCId) { + if tlc_id.is_offered() { + self.offered_tlcs.tlcs.retain(|tlc| tlc.tlc_id != tlc_id); + } else { + self.received_tlcs.tlcs.retain(|tlc| tlc.tlc_id != tlc_id); + } } pub fn insert_relay_tlc_remove( @@ -4385,7 +4367,6 @@ impl ChannelActorState { tlc_id: TLCId, reason: &RemoveTlcReason, ) -> Result { - let removed_at = self.get_current_commitment_numbers(); let current = self.tlc_state.get(&tlc_id).expect("TLC exists").clone(); assert!(matches!( current.status, @@ -4421,8 +4402,7 @@ impl ChannelActorState { eprintln!("Updated local balance to {} and remote balance to {} by removing tlc {:?} with reason {:?}", to_local_amount, to_remote_amount, tlc_id, reason); } - self.tlc_state - .apply_remove_tlc(tlc_id, removed_at, reason.clone()); + self.tlc_state.apply_remove_tlc(tlc_id); Ok(current.clone()) } @@ -4825,7 +4805,7 @@ impl ChannelActorState { fn check_for_tlc_update( &self, add_tlc_amount: Option, - is_tlc_command_message: bool, + _is_tlc_command_message: bool, is_sent: bool, ) -> ProcessingChannelResult { // if is_tlc_command_message { diff --git a/src/fiber/tests/channel.rs b/src/fiber/tests/channel.rs index 41b190e9e..936f63259 100644 --- a/src/fiber/tests/channel.rs +++ b/src/fiber/tests/channel.rs @@ -5183,7 +5183,7 @@ async fn test_send_payment_will_fail_with_cancelled_invoice() { assert!(res.is_ok()); let payment_hash = res.unwrap().payment_hash; - tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; source_node .assert_payment_status(payment_hash, PaymentSessionStatus::Failed, Some(1)) From 71178e0c2f5a1535a6fcd82e9bacc2516646bfc1 Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 24 Dec 2024 12:03:23 +0800 Subject: [PATCH 023/119] fix some tests --- src/fiber/tests/channel.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/fiber/tests/channel.rs b/src/fiber/tests/channel.rs index 936f63259..78e25a78a 100644 --- a/src/fiber/tests/channel.rs +++ b/src/fiber/tests/channel.rs @@ -1454,7 +1454,7 @@ async fn test_send_payment_with_max_nodes() { assert_eq!(res.status, PaymentSessionStatus::Inflight); assert!(res.fee > 0); // sleep for 2 seconds to make sure the payment is sent - tokio::time::sleep(tokio::time::Duration::from_millis(3000)).await; + tokio::time::sleep(tokio::time::Duration::from_millis(8000)).await; let message = |rpc_reply| -> NetworkActorMessage { NetworkActorMessage::Command(NetworkActorCommand::GetPayment(res.payment_hash, rpc_reply)) }; @@ -4060,7 +4060,7 @@ async fn test_send_payment_with_channel_balance_error() { assert!(res.is_ok()); let payment_hash = res.unwrap().payment_hash; // sleep for a while - tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; let message = |rpc_reply| -> NetworkActorMessage { NetworkActorMessage::Command(NetworkActorCommand::GetPayment(payment_hash, rpc_reply)) }; @@ -4779,7 +4779,7 @@ async fn test_send_payment_will_succeed_with_direct_channel_info_first_hop() { assert!(res.is_ok()); let payment_hash = res.unwrap().payment_hash; - tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; source_node .assert_payment_status(payment_hash, PaymentSessionStatus::Success, Some(1)) @@ -4843,7 +4843,7 @@ async fn test_send_payment_will_succeed_with_retry_in_middle_hops() { assert!(res.is_ok()); let payment_hash = res.unwrap().payment_hash; - tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; + tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; source_node .assert_payment_status(payment_hash, PaymentSessionStatus::Success, Some(2)) @@ -4976,7 +4976,7 @@ async fn test_send_payment_will_fail_with_invoice_not_generated_by_target() { assert!(res.is_ok()); let payment_hash = res.unwrap().payment_hash; - tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; source_node .assert_payment_status(payment_hash, PaymentSessionStatus::Failed, Some(1)) @@ -5040,7 +5040,7 @@ async fn test_send_payment_will_succeed_with_valid_invoice() { assert!(res.is_ok()); let payment_hash = res.unwrap().payment_hash; - tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; source_node .assert_payment_status(payment_hash, PaymentSessionStatus::Success, Some(1)) @@ -5111,7 +5111,7 @@ async fn test_send_payment_will_fail_with_no_invoice_preimage() { assert!(res.is_ok()); let payment_hash = res.unwrap().payment_hash; - tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; source_node .assert_payment_status(payment_hash, PaymentSessionStatus::Failed, Some(1)) @@ -5263,7 +5263,7 @@ async fn test_send_payment_will_succeed_with_large_tlc_expiry_limit() { // expect send payment to succeed assert!(res.is_ok()); let payment_hash = res.unwrap().payment_hash; - tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; source_node .assert_payment_status(payment_hash, PaymentSessionStatus::Success, Some(1)) From f25ae768d40b5ddf506eda1ec75d6245ecee5e50 Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 24 Dec 2024 15:26:43 +0800 Subject: [PATCH 024/119] debug nonce --- src/fiber/channel.rs | 35 ++++++++++++++++++++++++++--------- src/fiber/tests/channel.rs | 7 ++++--- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 6b8d2a472..b6d2bdbe8 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -4085,6 +4085,8 @@ impl ChannelActorState { let sign_ctx = { let local_nonce = self.get_local_nonce(); let remote_nonce = self.get_remote_nonce(); + eprintln!("local_nonce: {:?}", local_nonce); + eprintln!("remote_nonce: {:?}", remote_nonce); let nonces = [local_nonce, remote_nonce]; let agg_nonce = AggNonce::sum(nonces); Musig2SignContext { @@ -4115,7 +4117,7 @@ impl ChannelActorState { } else { let capacity = self.get_total_ckb_amount() - commitment_tx_fee; let output = CellOutput::new_builder() - .lock(lock_script) + .lock(lock_script.clone()) .capacity(capacity.pack()) .build(); let output_data = Bytes::default(); @@ -4123,13 +4125,17 @@ impl ChannelActorState { }; let commitment_number = self.get_remote_commitment_number() - 1; + eprintln!("send first commitment_number: {}", commitment_number); let commitment_lock_script_args = [ &blake2b_256(x_only_aggregated_pubkey)[0..20], self.get_delay_epoch_as_lock_args_bytes().as_slice(), commitment_number.to_be_bytes().as_slice(), ] .concat(); - + eprintln!( + "send first commitment_lock_script_args: {:?}", + commitment_lock_script_args + ); let message = blake2b_256( [ output.as_slice(), @@ -4138,6 +4144,7 @@ impl ChannelActorState { ] .concat(), ); + eprintln!("ack send message: {:?}", message); sign_ctx .clone() .sign(message.as_slice()) @@ -5569,10 +5576,6 @@ impl ChannelActorState { self.remote_commitment_points .retain(|(num, _)| *num >= min_remote_commitment); } - assert!( - self.remote_commitment_points.len() - <= (self.local_constraints.max_tlc_number_in_flight + 1) as usize - ); } fn handle_revoke_and_ack_peer_message( @@ -5580,6 +5583,10 @@ impl ChannelActorState { network: &ActorRef, revoke_and_ack: RevokeAndAck, ) -> ProcessingChannelResult { + eprintln!( + "begin to handle revoke and ack peer message: {:?}", + &revoke_and_ack + ); let RevokeAndAck { channel_id: _, revocation_partial_signature, @@ -5596,6 +5603,8 @@ impl ChannelActorState { let (verify_ctx, sign_ctx) = { let local_nonce = self.get_local_nonce(); let remote_nonce = self.take_remote_nonce_for_raa(); + eprintln!("local_nonce: {:?}", local_nonce); + eprintln!("remote_nonce: {:?}", remote_nonce); let nonces = [remote_nonce.clone(), local_nonce]; let agg_nonce = AggNonce::sum(nonces); @@ -5625,7 +5634,7 @@ impl ChannelActorState { { let capacity = self.get_total_reserved_ckb_amount() - commitment_tx_fee; let output = CellOutput::new_builder() - .lock(lock_script) + .lock(lock_script.clone()) .type_(Some(udt_type_script.clone()).pack()) .capacity(capacity.pack()) .build(); @@ -5635,7 +5644,7 @@ impl ChannelActorState { } else { let capacity = self.get_total_ckb_amount() - commitment_tx_fee; let output = CellOutput::new_builder() - .lock(lock_script) + .lock(lock_script.clone()) .capacity(capacity.pack()) .build(); let output_data = Bytes::default(); @@ -5643,7 +5652,7 @@ impl ChannelActorState { }; let commitment_number = self.get_local_commitment_number() - 1; - + eprintln!("recv first commitment_number: {}", commitment_number); let commitment_lock_script_args = [ &blake2b_256(x_only_aggregated_pubkey)[0..20], self.get_delay_epoch_as_lock_args_bytes().as_slice(), @@ -5651,6 +5660,10 @@ impl ChannelActorState { ] .concat(); + eprintln!( + "recv first commitment_lock_script_args: {:?}", + commitment_lock_script_args + ); let message = blake2b_256( [ output.as_slice(), @@ -5660,7 +5673,9 @@ impl ChannelActorState { .concat(), ); + eprintln!("first begin to verify message: {:?}", message); verify_ctx.verify(revocation_partial_signature, message.as_slice())?; + eprintln!("first successfully verify message ============="); let our_signature = sign_ctx.clone().sign(message.as_slice())?; let aggregated_signature = verify_ctx.aggregate_partial_signatures_for_msg( @@ -5697,7 +5712,9 @@ impl ChannelActorState { ] .concat(), ); + eprintln!("begin to verify message: {:?}", message); verify_ctx.verify(commitment_tx_partial_signature, message.as_slice())?; + eprintln!("successfully verify message ============="); let our_signature = sign_ctx.sign(message.as_slice())?; let aggregated_signature = verify_ctx.aggregate_partial_signatures_for_msg( [commitment_tx_partial_signature, our_signature], diff --git a/src/fiber/tests/channel.rs b/src/fiber/tests/channel.rs index 78e25a78a..7b67e0f24 100644 --- a/src/fiber/tests/channel.rs +++ b/src/fiber/tests/channel.rs @@ -727,7 +727,7 @@ async fn test_network_send_payment_send_each_other() { let payment_hash2 = res2.payment_hash; // sleep for 2 seconds to make sure the payment is processed - tokio::time::sleep(tokio::time::Duration::from_millis(6000)).await; + tokio::time::sleep(tokio::time::Duration::from_millis(3000)).await; let message = |rpc_reply| -> NetworkActorMessage { NetworkActorMessage::Command(NetworkActorCommand::GetPayment(payment_hash1, rpc_reply)) @@ -2444,7 +2444,7 @@ async fn do_test_add_tlc_waiting_ack() { } #[tokio::test] -async fn do_test_add_tlc_number_limit() { +async fn do_test_add_tlc_with_number_limit() { let node_a_funding_amount = 100000000000; let node_b_funding_amount = 100000000000; @@ -2726,6 +2726,7 @@ async fn do_test_add_tlc_min_tlc_value_limit() { None, ) .await; + tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; // A -> B will be no limit let tlc_amount = 200; @@ -2747,7 +2748,7 @@ async fn do_test_add_tlc_min_tlc_value_limit() { )) }) .expect("node_b alive"); - + tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; assert!(add_tlc_result.is_ok()); // B -> A can still able to send amount less than 100 From ae355527c7a6ff8253a7b270e2288c5ec0dce5e7 Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 24 Dec 2024 15:52:02 +0800 Subject: [PATCH 025/119] fix tests --- src/fiber/tests/payment.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fiber/tests/payment.rs b/src/fiber/tests/payment.rs index 05e409f42..f43fd95b5 100644 --- a/src/fiber/tests/payment.rs +++ b/src/fiber/tests/payment.rs @@ -195,7 +195,7 @@ async fn test_send_payment_for_pay_self() { assert!(res.is_ok()); // sleep for a while - tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; let res = res.unwrap(); let payment_hash = res.payment_hash; node_0 From 00e3c1710dc934e0e2373ffbd0e577448c336266 Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 24 Dec 2024 16:07:11 +0800 Subject: [PATCH 026/119] fix test for invoice --- src/fiber/channel.rs | 2 +- tests/bruno/e2e/3-nodes-transfer/13-node2-add-tlc.bru | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 6fd1f1786..1e93a1a0a 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -845,6 +845,7 @@ where error!("invoice already paid, ignore"); } _ => { + eprintln!("update invoice status to paid: {:?}", tlc.payment_hash); self.store .update_invoice_status(&tlc.payment_hash, CkbInvoiceStatus::Paid) .expect("update invoice status error"); @@ -2658,7 +2659,6 @@ impl TlcState { OutboundTlcStatus::RemoteRemoved => { let status = if self.waiting_ack { need_another_commitment_signed = true; - eprintln!("hahah !!!!!!!!!!!!!!!!!!!!!!!!!!!"); OutboundTlcStatus::RemoveWaitPrevAck } else { OutboundTlcStatus::RemoveWaitAck diff --git a/tests/bruno/e2e/3-nodes-transfer/13-node2-add-tlc.bru b/tests/bruno/e2e/3-nodes-transfer/13-node2-add-tlc.bru index 1cb597cc9..b8e3f5a44 100644 --- a/tests/bruno/e2e/3-nodes-transfer/13-node2-add-tlc.bru +++ b/tests/bruno/e2e/3-nodes-transfer/13-node2-add-tlc.bru @@ -50,6 +50,6 @@ script:pre-request { script:post-response { // Sleep for sometime to make sure current operation finishes before next request starts. - await new Promise(r => setTimeout(r, 100)); + await new Promise(r => setTimeout(r, 1000)); bru.setVar("N2N3_TLC_ID1", res.body.result.tlc_id); } From 00f5784c43dc7bb8cd7558d5b02ec4d3bfdbd074 Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 24 Dec 2024 21:45:10 +0800 Subject: [PATCH 027/119] fix commitment signed tlcs --- src/fiber/channel.rs | 86 ++++++++++---------------------------------- 1 file changed, 19 insertions(+), 67 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 1e93a1a0a..c1c485352 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -2455,46 +2455,6 @@ impl TlcState { }) } - pub fn get_local_active_offered_tlcs(&self) -> impl Iterator + '_ { - self.offered_tlcs.tlcs.iter().filter_map(move |tlc| { - if tlc.removed_at.is_none() { - Some(tlc.clone()) - } else { - None - } - }) - } - - pub fn get_remote_active_offered_tlcs(&self) -> impl Iterator + '_ { - self.received_tlcs.tlcs.iter().filter_map(move |tlc| { - if tlc.removed_at.is_none() { - Some(tlc.clone()) - } else { - None - } - }) - } - - pub fn get_active_received_tlcs(&self) -> impl Iterator + '_ { - self.received_tlcs.tlcs.iter().filter_map(move |tlc| { - if tlc.removed_at.is_none() { - Some(tlc.clone()) - } else { - None - } - }) - } - - pub fn get_remote_received_tlcs(&self) -> impl Iterator + '_ { - self.offered_tlcs.tlcs.iter().filter_map(move |tlc| { - if tlc.removed_at.is_none() { - Some(tlc.clone()) - } else { - None - } - }) - } - pub fn get_committed_received_tlcs(&self) -> Vec { self.received_tlcs.get_committed_tlcs() } @@ -2617,7 +2577,7 @@ impl TlcState { } } - pub fn commitment_signed(&mut self, local: bool) -> Vec { + pub fn commitment_signed(&self, local: bool) -> Vec { let mut active_tls = vec![]; for tlc in self.offered_tlcs.tlcs.iter() { let status = tlc.status.as_outbound_status(); @@ -2642,7 +2602,7 @@ impl TlcState { InboundTlctatus::AnnounceWaitAck => true, InboundTlctatus::Committed => true, InboundTlctatus::LocalRemoved => !local, - InboundTlctatus::RemoveAckConfirmed => !local, + InboundTlctatus::RemoveAckConfirmed => false, InboundTlctatus::RemoveSettled => false, }; if include { @@ -2673,7 +2633,6 @@ impl TlcState { InboundTlctatus::RemoteAnnounced => { let status = if self.waiting_ack { need_another_commitment_signed = true; - eprintln!("remote anounced ....................."); InboundTlctatus::AnnounceWaitPrevAck } else { InboundTlctatus::AnnounceWaitAck @@ -4555,32 +4514,20 @@ impl ChannelActorState { AggNonce::sum(nonces) } - fn get_active_received_tlcs(&self, local_commitment: bool) -> Vec { - if local_commitment { - self.tlc_state - .get_local_active_offered_tlcs() - .filter(|tlc| tlc.is_received()) - .collect() - } else { - self.tlc_state - .get_remote_active_offered_tlcs() - .filter(|tlc| tlc.is_received()) - .collect() - } + fn get_active_received_tlcs(&self, local: bool) -> Vec { + self.tlc_state + .commitment_signed(local) + .into_iter() + .filter(|tlc| tlc.is_received()) + .collect() } - fn get_active_offered_tlcs(&self, local_commitment: bool) -> Vec { - if local_commitment { - self.tlc_state - .get_local_active_offered_tlcs() - .filter(|tlc| tlc.is_offered()) - .collect() - } else { - self.tlc_state - .get_remote_active_offered_tlcs() - .filter(|tlc| tlc.is_offered()) - .collect() - } + fn get_active_offered_tlcs(&self, local: bool) -> Vec { + self.tlc_state + .commitment_signed(local) + .into_iter() + .filter(|tlc| tlc.is_offered()) + .collect() } // Get the total amount of pending tlcs that are fulfilled @@ -4695,6 +4642,11 @@ impl ChannelActorState { [a, b].concat() }; //eprintln!("active tlcs: {:?}", tlcs); + eprintln!("active tlcs count: {}", tlcs.len()); + for tlc in &tlcs { + eprintln!("tlc {}", tlc.0.log()); + } + eprintln!("=========================="); if tlcs.is_empty() { Vec::new() } else { From 633b28aa1e0462360908b5f1ee60e1f9bf573814 Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 24 Dec 2024 22:20:33 +0800 Subject: [PATCH 028/119] rename local to for_local --- src/fiber/channel.rs | 64 ++++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index c1c485352..cd021076e 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -2577,15 +2577,15 @@ impl TlcState { } } - pub fn commitment_signed(&self, local: bool) -> Vec { + pub fn commitment_signed(&self, for_remote: bool) -> Vec { let mut active_tls = vec![]; for tlc in self.offered_tlcs.tlcs.iter() { let status = tlc.status.as_outbound_status(); let include = match status { - OutboundTlcStatus::LocalAnnounced => local, + OutboundTlcStatus::LocalAnnounced => for_remote, OutboundTlcStatus::Committed => true, - OutboundTlcStatus::RemoteRemoved => local, - OutboundTlcStatus::RemoveWaitPrevAck => local, + OutboundTlcStatus::RemoteRemoved => for_remote, + OutboundTlcStatus::RemoveWaitPrevAck => for_remote, OutboundTlcStatus::RemoveWaitAck => false, OutboundTlcStatus::RemoveAckConfirmed => false, OutboundTlcStatus::RemoveSettled => false, @@ -2597,11 +2597,11 @@ impl TlcState { for tlc in self.received_tlcs.tlcs.iter() { let status = tlc.status.as_inbound_status(); let include = match status { - InboundTlctatus::RemoteAnnounced => !local, - InboundTlctatus::AnnounceWaitPrevAck => !local, + InboundTlctatus::RemoteAnnounced => !for_remote, + InboundTlctatus::AnnounceWaitPrevAck => !for_remote, InboundTlctatus::AnnounceWaitAck => true, InboundTlctatus::Committed => true, - InboundTlctatus::LocalRemoved => !local, + InboundTlctatus::LocalRemoved => !for_remote, InboundTlctatus::RemoveAckConfirmed => false, InboundTlctatus::RemoveSettled => false, }; @@ -4514,17 +4514,17 @@ impl ChannelActorState { AggNonce::sum(nonces) } - fn get_active_received_tlcs(&self, local: bool) -> Vec { + fn get_active_received_tlcs(&self, for_remote: bool) -> Vec { self.tlc_state - .commitment_signed(local) + .commitment_signed(for_remote) .into_iter() .filter(|tlc| tlc.is_received()) .collect() } - fn get_active_offered_tlcs(&self, local: bool) -> Vec { + fn get_active_offered_tlcs(&self, for_remote: bool) -> Vec { self.tlc_state - .commitment_signed(local) + .commitment_signed(for_remote) .into_iter() .filter(|tlc| tlc.is_offered()) .collect() @@ -4601,8 +4601,11 @@ impl ChannelActorState { } } - fn get_active_received_tlc_with_pubkeys(&self, local: bool) -> Vec<(TlcInfo, Pubkey, Pubkey)> { - self.get_active_received_tlcs(local) + fn get_active_received_tlc_with_pubkeys( + &self, + for_remote: bool, + ) -> Vec<(TlcInfo, Pubkey, Pubkey)> { + self.get_active_received_tlcs(for_remote) .into_iter() .map(move |tlc| { let (k1, k2) = self.get_tlc_pubkeys(&tlc); @@ -4611,8 +4614,11 @@ impl ChannelActorState { .collect() } - fn get_active_offered_tlc_with_pubkeys(&self, local: bool) -> Vec<(TlcInfo, Pubkey, Pubkey)> { - self.get_active_offered_tlcs(local) + fn get_active_offered_tlc_with_pubkeys( + &self, + for_remote: bool, + ) -> Vec<(TlcInfo, Pubkey, Pubkey)> { + self.get_active_offered_tlcs(for_remote) .into_iter() .map(move |tlc| { let (k1, k2) = self.get_tlc_pubkeys(&tlc); @@ -4621,14 +4627,14 @@ impl ChannelActorState { .collect() } - fn get_active_htlcs(&self, local: bool) -> Vec { + fn get_active_htlcs(&self, for_remote: bool) -> Vec { // Build a sorted array of TLC so that both party can generate the same commitment transaction. let tlcs = { let (mut received_tlcs, mut offered_tlcs) = ( - self.get_active_received_tlc_with_pubkeys(local), - self.get_active_offered_tlc_with_pubkeys(local), + self.get_active_received_tlc_with_pubkeys(for_remote), + self.get_active_offered_tlc_with_pubkeys(for_remote), ); - let (mut a, mut b) = if local { + let (mut a, mut b) = if for_remote { (received_tlcs, offered_tlcs) } else { for (tlc, _, _) in received_tlcs.iter_mut().chain(offered_tlcs.iter_mut()) { @@ -4641,12 +4647,12 @@ impl ChannelActorState { b.sort_by(|x, y| u64::from(x.0.tlc_id).cmp(&u64::from(y.0.tlc_id))); [a, b].concat() }; - //eprintln!("active tlcs: {:?}", tlcs); - eprintln!("active tlcs count: {}", tlcs.len()); - for tlc in &tlcs { - eprintln!("tlc {}", tlc.0.log()); - } - eprintln!("=========================="); + // //eprintln!("active tlcs: {:?}", tlcs); + // eprintln!("active tlcs count: {}, local: {}", tlcs.len(), for_remote); + // for tlc in &tlcs { + // eprintln!("tlc {}", tlc.0.log()); + // } + // eprintln!("=========================="); if tlcs.is_empty() { Vec::new() } else { @@ -4763,12 +4769,12 @@ impl ChannelActorState { fn check_for_tlc_update( &self, add_tlc_amount: Option, - _is_tlc_command_message: bool, + is_tlc_command_message: bool, is_sent: bool, ) -> ProcessingChannelResult { - // if is_tlc_command_message { - // return Err(ProcessingChannelError::WaitingTlcAck); - // } + if is_tlc_command_message && self.tlc_state.waiting_ack { + return Err(ProcessingChannelError::WaitingTlcAck); + } match self.state { ChannelState::ChannelReady() => {} ChannelState::ShuttingDown(_) if add_tlc_amount.is_none() => {} From 95b0e984fdfca6dfbf04d029a2deca8639b2ce34 Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 25 Dec 2024 09:58:05 +0800 Subject: [PATCH 029/119] fix sig error for wait ack --- src/fiber/channel.rs | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index cd021076e..ea7c17f48 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -438,7 +438,7 @@ where Ok(()) } FiberChannelMessage::RevokeAndAck(revoke_and_ack) => { - state.handle_revoke_and_ack_peer_message(&self.network, revoke_and_ack)?; + state.handle_revoke_and_ack_peer_message(&self.network, myself, revoke_and_ack)?; self.flush_tlc_operations(myself, state).await; Ok(()) } @@ -663,8 +663,12 @@ where let need_commitment_signed = state.tlc_state.update_for_commitment_signed(); if need_commitment_signed { - eprintln!("sending commitment_signed after first ack"); - self.handle_commitment_signed_command(state)?; + if !state.tlc_state.waiting_ack { + eprintln!("sending commitment_signed after first ack"); + self.handle_commitment_signed_command(state)?; + } else { + eprintln!("waiting ack for commitment_signed ............."); + } } // flush remove tlc for received tlcs after replying ack for peer @@ -2646,8 +2650,9 @@ impl TlcState { res } - pub fn update_for_revoke_and_ack(&mut self) { + pub fn update_for_revoke_and_ack(&mut self) -> bool { self.set_waiting_ack(false); + let mut need_another_commitment_signed = false; for tlc in self.offered_tlcs.tlcs.iter_mut() { let out_status = tlc.status.as_outbound_status(); match out_status { @@ -2655,6 +2660,7 @@ impl TlcState { tlc.status = TlcStatus::Outbound(OutboundTlcStatus::Committed); } OutboundTlcStatus::RemoveWaitPrevAck => { + need_another_commitment_signed = true; tlc.status = TlcStatus::Outbound(OutboundTlcStatus::RemoveWaitAck); } OutboundTlcStatus::RemoveWaitAck => { @@ -2668,6 +2674,7 @@ impl TlcState { let in_status = tlc.status.as_inbound_status(); match in_status { InboundTlctatus::AnnounceWaitPrevAck => { + need_another_commitment_signed = true; tlc.status = TlcStatus::Inbound(InboundTlctatus::AnnounceWaitAck); } InboundTlctatus::AnnounceWaitAck => { @@ -2676,6 +2683,7 @@ impl TlcState { _ => {} } } + need_another_commitment_signed || self.need_another_commitment_signed() } pub fn build_ack_transaction(&self, _for_remote: bool) -> Vec { @@ -5538,6 +5546,7 @@ impl ChannelActorState { fn handle_revoke_and_ack_peer_message( &mut self, network: &ActorRef, + myself: &ActorRef, revoke_and_ack: RevokeAndAck, ) -> ProcessingChannelResult { eprintln!( @@ -5692,7 +5701,14 @@ impl ChannelActorState { self.append_remote_commitment_point(next_per_commitment_point); eprintln!("handle revoke and ack peer message: {:?}", &revocation_data); - self.tlc_state.update_for_revoke_and_ack(); + let need_commitment_signed = self.tlc_state.update_for_revoke_and_ack(); + if need_commitment_signed { + myself + .send_message(ChannelActorMessage::Command( + ChannelCommand::CommitmentSigned(), + )) + .expect(ASSUME_NETWORK_ACTOR_ALIVE); + } network .send_message(NetworkActorMessage::new_notification( From f28986013f0f329773baa62b3cab0aacb62b2ea1 Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 25 Dec 2024 10:28:17 +0800 Subject: [PATCH 030/119] fix get_pending_fulfilled_tlcs_amount --- src/fiber/channel.rs | 34 +++++++++++++++++++++------------- src/fiber/tests/channel.rs | 4 +++- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index ea7c17f48..afb099e57 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -438,7 +438,14 @@ where Ok(()) } FiberChannelMessage::RevokeAndAck(revoke_and_ack) => { - state.handle_revoke_and_ack_peer_message(&self.network, myself, revoke_and_ack)?; + let need_commitment_signed = state.handle_revoke_and_ack_peer_message( + &self.network, + myself, + revoke_and_ack, + )?; + if need_commitment_signed { + self.handle_commitment_signed_command(state)?; + } self.flush_tlc_operations(myself, state).await; Ok(()) } @@ -1208,6 +1215,9 @@ where )) .expect(ASSUME_NETWORK_ACTOR_ALIVE); state.save_remote_nonce_for_raa(); + if state.tlc_state.all_tlcs().count() > 0 { + state.tlc_state.set_waiting_ack(true); + } eprintln!("finished sent commitment_signed"); match flags { @@ -1257,7 +1267,6 @@ where .expect(ASSUME_NETWORK_ACTOR_ALIVE); self.handle_commitment_signed_command(state)?; - state.tlc_state.set_waiting_ack(true); Ok(tlc.tlc_id.into()) } @@ -1290,7 +1299,6 @@ where state.maybe_transition_to_shutdown(&self.network)?; self.handle_commitment_signed_command(state)?; - state.tlc_state.set_waiting_ack(true); Ok(()) } @@ -4555,6 +4563,7 @@ impl ChannelActorState { include = for_remote; } TlcStatus::Outbound(OutboundTlcStatus::RemoveWaitPrevAck) + | TlcStatus::Outbound(OutboundTlcStatus::RemoveWaitAck) | TlcStatus::Outbound(OutboundTlcStatus::RemoteRemoved) => { include = !for_remote; } @@ -5548,7 +5557,7 @@ impl ChannelActorState { network: &ActorRef, myself: &ActorRef, revoke_and_ack: RevokeAndAck, - ) -> ProcessingChannelResult { + ) -> Result { eprintln!( "begin to handle revoke and ack peer message: {:?}", &revoke_and_ack @@ -5702,14 +5711,13 @@ impl ChannelActorState { eprintln!("handle revoke and ack peer message: {:?}", &revocation_data); let need_commitment_signed = self.tlc_state.update_for_revoke_and_ack(); - if need_commitment_signed { - myself - .send_message(ChannelActorMessage::Command( - ChannelCommand::CommitmentSigned(), - )) - .expect(ASSUME_NETWORK_ACTOR_ALIVE); - } - + // if need_commitment_signed { + // myself + // .send_message(ChannelActorMessage::Command( + // ChannelCommand::CommitmentSigned(), + // )) + // .expect(ASSUME_NETWORK_ACTOR_ALIVE); + // } network .send_message(NetworkActorMessage::new_notification( NetworkServiceEvent::RevokeAndAckReceived( @@ -5720,7 +5728,7 @@ impl ChannelActorState { ), )) .expect(ASSUME_NETWORK_ACTOR_ALIVE); - Ok(()) + Ok(need_commitment_signed) } fn handle_reestablish_channel_message( diff --git a/src/fiber/tests/channel.rs b/src/fiber/tests/channel.rs index 1c4244e3d..a855b304e 100644 --- a/src/fiber/tests/channel.rs +++ b/src/fiber/tests/channel.rs @@ -723,7 +723,7 @@ async fn test_network_send_payment_send_each_other() { let payment_hash2 = res2.payment_hash; // sleep for 2 seconds to make sure the payment is processed - tokio::time::sleep(tokio::time::Duration::from_millis(3000)).await; + tokio::time::sleep(tokio::time::Duration::from_millis(5000)).await; let message = |rpc_reply| -> NetworkActorMessage { NetworkActorMessage::Command(NetworkActorCommand::GetPayment(payment_hash1, rpc_reply)) @@ -2088,6 +2088,8 @@ async fn do_test_channel_commitment_tx_after_add_tlc(algorithm: HashAlgorithm) { }) .await; + tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + call!(node_b.network_actor, |rpc_reply| { NetworkActorMessage::Command(NetworkActorCommand::ControlFiberChannel( ChannelCommandWithId { From 5f779c160e80c3da847c91c979f92e855dc23b9f Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 25 Dec 2024 10:35:37 +0800 Subject: [PATCH 031/119] fix tests --- src/fiber/channel.rs | 34 ++++++++++++---------------------- src/fiber/tests/channel.rs | 17 ++++++++++++++--- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index afb099e57..6e894f301 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -438,11 +438,8 @@ where Ok(()) } FiberChannelMessage::RevokeAndAck(revoke_and_ack) => { - let need_commitment_signed = state.handle_revoke_and_ack_peer_message( - &self.network, - myself, - revoke_and_ack, - )?; + let need_commitment_signed = + state.handle_revoke_and_ack_peer_message(&self.network, revoke_and_ack)?; if need_commitment_signed { self.handle_commitment_signed_command(state)?; } @@ -1215,11 +1212,16 @@ where )) .expect(ASSUME_NETWORK_ACTOR_ALIVE); state.save_remote_nonce_for_raa(); - if state.tlc_state.all_tlcs().count() > 0 { + if state + .tlc_state + .all_tlcs() + .filter(|tlc| tlc.is_offered()) + .count() + > 0 + { state.tlc_state.set_waiting_ack(true); } eprintln!("finished sent commitment_signed"); - match flags { CommitmentSignedFlags::SigningCommitment(flags) => { let flags = flags | SigningCommitmentFlags::OUR_COMMITMENT_SIGNED_SENT; @@ -2264,7 +2266,6 @@ pub enum OutboundTlcStatus { RemoveWaitPrevAck, RemoveWaitAck, RemoveAckConfirmed, - RemoveSettled, } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] @@ -2275,7 +2276,6 @@ pub enum InboundTlctatus { Committed, LocalRemoved, RemoveAckConfirmed, - RemoveSettled, } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] @@ -2592,30 +2592,26 @@ impl TlcState { pub fn commitment_signed(&self, for_remote: bool) -> Vec { let mut active_tls = vec![]; for tlc in self.offered_tlcs.tlcs.iter() { - let status = tlc.status.as_outbound_status(); - let include = match status { + let include = match tlc.status.as_outbound_status() { OutboundTlcStatus::LocalAnnounced => for_remote, OutboundTlcStatus::Committed => true, OutboundTlcStatus::RemoteRemoved => for_remote, OutboundTlcStatus::RemoveWaitPrevAck => for_remote, OutboundTlcStatus::RemoveWaitAck => false, OutboundTlcStatus::RemoveAckConfirmed => false, - OutboundTlcStatus::RemoveSettled => false, }; if include { active_tls.push(tlc.clone()); } } for tlc in self.received_tlcs.tlcs.iter() { - let status = tlc.status.as_inbound_status(); - let include = match status { + let include = match tlc.status.as_inbound_status() { InboundTlctatus::RemoteAnnounced => !for_remote, InboundTlctatus::AnnounceWaitPrevAck => !for_remote, InboundTlctatus::AnnounceWaitAck => true, InboundTlctatus::Committed => true, InboundTlctatus::LocalRemoved => !for_remote, InboundTlctatus::RemoveAckConfirmed => false, - InboundTlctatus::RemoveSettled => false, }; if include { active_tls.push(tlc.clone()); @@ -4664,12 +4660,7 @@ impl ChannelActorState { b.sort_by(|x, y| u64::from(x.0.tlc_id).cmp(&u64::from(y.0.tlc_id))); [a, b].concat() }; - // //eprintln!("active tlcs: {:?}", tlcs); - // eprintln!("active tlcs count: {}, local: {}", tlcs.len(), for_remote); - // for tlc in &tlcs { - // eprintln!("tlc {}", tlc.0.log()); - // } - // eprintln!("=========================="); + if tlcs.is_empty() { Vec::new() } else { @@ -5555,7 +5546,6 @@ impl ChannelActorState { fn handle_revoke_and_ack_peer_message( &mut self, network: &ActorRef, - myself: &ActorRef, revoke_and_ack: RevokeAndAck, ) -> Result { eprintln!( diff --git a/src/fiber/tests/channel.rs b/src/fiber/tests/channel.rs index a855b304e..2b4ce0a9c 100644 --- a/src/fiber/tests/channel.rs +++ b/src/fiber/tests/channel.rs @@ -659,11 +659,13 @@ async fn test_network_send_payment_send_each_other() { let node_a_funding_amount = 100000000000; let node_b_funding_amount = 6200000000; - let (node_a, node_b, _new_channel_id) = + 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(1000)).await; + let node_a_old_balance = node_a.get_local_balance_from_channel(new_channel_id); + let node_b_old_balance = node_b.get_local_balance_from_channel(new_channel_id); let node_a_pubkey = node_a.pubkey.clone(); let node_b_pubkey = node_b.pubkey.clone(); @@ -699,7 +701,7 @@ async fn test_network_send_payment_send_each_other() { NetworkActorMessage::Command(NetworkActorCommand::SendPayment( SendPaymentCommand { target_pubkey: Some(node_a_pubkey), - amount: Some(10000), + amount: Some(9999), payment_hash: None, final_tlc_expiry_delta: None, invoice: None, @@ -723,7 +725,7 @@ async fn test_network_send_payment_send_each_other() { let payment_hash2 = res2.payment_hash; // sleep for 2 seconds to make sure the payment is processed - tokio::time::sleep(tokio::time::Duration::from_millis(5000)).await; + tokio::time::sleep(tokio::time::Duration::from_millis(3000)).await; let message = |rpc_reply| -> NetworkActorMessage { NetworkActorMessage::Command(NetworkActorCommand::GetPayment(payment_hash1, rpc_reply)) @@ -743,6 +745,15 @@ async fn test_network_send_payment_send_each_other() { assert_eq!(res.status, PaymentSessionStatus::Success); assert_eq!(res.failed_error, None); + + let node_a_new_balance = node_a.get_local_balance_from_channel(new_channel_id); + let node_b_new_balance = node_b.get_local_balance_from_channel(new_channel_id); + + // assert the balance is right, + // node_a send 10000 to node_b, and node_b send 9999 to node_a + // so the balance should be node_a_old_balance - 1, node_b_old_balance + 1 + assert_eq!(node_a_new_balance, node_a_old_balance - 1); + assert_eq!(node_b_new_balance, node_b_old_balance + 1); } #[tokio::test] From 5f490d0a4791f870fb5b7eecda2c607976c45a82 Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 25 Dec 2024 15:13:00 +0800 Subject: [PATCH 032/119] code refactoring --- src/fiber/channel.rs | 184 ++++++++++++++++++++----------------------- 1 file changed, 85 insertions(+), 99 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 6e894f301..6a703249c 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -668,82 +668,57 @@ where if need_commitment_signed { if !state.tlc_state.waiting_ack { - eprintln!("sending commitment_signed after first ack"); self.handle_commitment_signed_command(state)?; - } else { - eprintln!("waiting ack for commitment_signed ............."); } } // flush remove tlc for received tlcs after replying ack for peer - let mut settled_tlcs = vec![]; - for tlc in state.tlc_state.received_tlcs.tlcs.iter_mut() { - if let Some((_removed_at, reason)) = &tlc.removed_at { - if matches!( - tlc.status, - TlcStatus::Inbound(InboundTlctatus::RemoveAckConfirmed) - ) { - settled_tlcs.push((tlc.tlc_id, reason.clone())); - } - } - } - for (tlc_id, reason) in settled_tlcs.iter() { - eprintln!("apply received tlc remove: {:?}", tlc_id); - let _ = self - .apply_remove_tlc_operation(myself, state, *tlc_id, reason.clone()) - .await; - } - + self.apply_settled_remvoe_tlcs(myself, state, true).await; Ok(()) } - async fn flush_tlc_operations( + async fn apply_settled_remvoe_tlcs( &self, myself: &ActorRef, state: &mut ChannelActorState, + inbound: bool, ) { - state.tlc_state.debug(); - - let mut settled_tlcs = vec![]; - for tlc in state.tlc_state.received_tlcs.tlcs.iter_mut() { - if let Some((_removed_at, reason)) = &tlc.removed_at { - if matches!( - tlc.status, - TlcStatus::Inbound(InboundTlctatus::LocalRemoved) - ) { - settled_tlcs.push((tlc.tlc_id, reason.clone())); + let pending_tlcs = if inbound { + state.tlc_state.received_tlcs.tlcs.iter_mut() + } else { + state.tlc_state.offered_tlcs.tlcs.iter_mut() + }; + let settled_tlcs: Vec<_> = pending_tlcs + .filter_map(|tlc| { + if let Some((_removed_at, reason)) = &tlc.removed_at { + if matches!( + tlc.status, + TlcStatus::Inbound(InboundTlctatus::RemoveAckConfirmed) + | TlcStatus::Outbound(OutboundTlcStatus::RemoveAckConfirmed) + ) { + return Some((tlc.tlc_id, reason.clone())); + } } - } - } - for (tlc_id, _reason) in settled_tlcs.iter() { - let tlc = state.tlc_state.get_mut(tlc_id).expect("tlc"); - tlc.status = TlcStatus::Inbound(InboundTlctatus::RemoveAckConfirmed); - } + None + }) + .collect(); - // flush outbound tlcs - let mut settled_tlcs = vec![]; - for tlc in state.tlc_state.offered_tlcs.tlcs.iter_mut() { - if let Some((_removed_at, reason)) = &tlc.removed_at { - if matches!( - tlc.status, - TlcStatus::Outbound(OutboundTlcStatus::RemoveAckConfirmed) - ) { - settled_tlcs.push((tlc.tlc_id, reason.clone())); - } - } - } - for (tlc_id, reason) in settled_tlcs.iter() { - eprintln!("apply offered tlc remove: {:?}", tlc_id); - let _ = self - .apply_remove_tlc_operation(myself, state, *tlc_id, reason.clone()) - .await; + for (tlc_id, reason) in settled_tlcs { + self.apply_remove_tlc_operation(myself, state, tlc_id, reason) + .await + .expect("expect remove tlc success"); } + } + async fn flush_tlc_operations( + &self, + myself: &ActorRef, + state: &mut ChannelActorState, + ) { let apply_tlcs = state.tlc_state.get_committed_received_tlcs(); eprintln!("flushing {} tlcs", apply_tlcs.len()); for add_tlc in apply_tlcs { if add_tlc.removed_at.is_some() { - eprintln!("tlc already removed, skip"); continue; } @@ -783,6 +758,8 @@ where } } + // flush outbound tlcs + self.apply_settled_remvoe_tlcs(myself, state, false).await; eprintln!("end flusing now ..................."); } @@ -815,7 +792,7 @@ where &self, myself: &ActorRef, state: &mut ChannelActorState, - tlc_id: u64, + tlc_id: TLCId, ) { let tlc_info = state.get_received_tlc(tlc_id).expect("expect tlc"); let preimage = tlc_info @@ -906,7 +883,7 @@ where // we don't need to settle down the tlc if it is not the last hop here, // some e2e tests are calling AddTlc manually, so we can not use onion packet to // check whether it's the last hop here, maybe need to revisit in future. - self.try_to_settle_down_tlc(myself, state, add_tlc.tlc_id.into()) + self.try_to_settle_down_tlc(myself, state, add_tlc.tlc_id) .await; warn!("finished check tlc for peer message: {:?}", &add_tlc.tlc_id); @@ -1072,7 +1049,6 @@ where ) -> Result<(), ProcessingChannelError> { let channel_id = state.get_id(); let remove_reason = reason.clone(); - eprintln!("apply remove tlc operation: {:?}", tlc_id); let tlc_info = state .remove_tlc_with_reason(tlc_id, &remove_reason) @@ -1097,7 +1073,6 @@ where // only the original sender of the TLC should send `TlcRemoveReceived` event // because only the original sender cares about the TLC event to settle the payment if tlc_info.is_offered() { - eprintln!("sending TlcRemoveReceived ...: {:?}", tlc_info.payment_hash); self.network .send_message(NetworkActorMessage::new_event( NetworkActorEvent::TlcRemoveReceived( @@ -1108,10 +1083,6 @@ where .expect("myself alive"); } } else { - eprintln!( - "now we need to relay remove tlc ...: {:?} tlc_id: {:?}", - tlc_info.payment_hash, tlc_info.tlc_id - ); // relay RemoveTlc to previous channel if needed self.try_to_relay_remove_tlc(myself, state, &tlc_info, remove_reason) .await; @@ -1448,9 +1419,10 @@ where tlc_id: TLCId, reason: RemoveTlcReason, ) { - state.tlc_state.set_tlc_pending_remove(tlc_id, reason); - self.check_and_apply_retryable_remove_tlcs(myself, state) - .await; + state.tlc_state.insert_pending_remove_tlc(tlc_id, reason); + myself + .send_message(ChannelActorMessage::Event(ChannelEvent::CheckTlcSetdown)) + .expect("myself alive"); } pub async fn register_retryable_relay_tlc_remove( @@ -1464,8 +1436,9 @@ where state .tlc_state .insert_relay_tlc_remove(channel_id, tlc_id, reason); - self.check_and_apply_retryable_remove_tlcs(myself, state) - .await; + myself + .send_message(ChannelActorMessage::Event(ChannelEvent::CheckTlcSetdown)) + .expect("myself alive"); } pub async fn check_and_apply_retryable_remove_tlcs( @@ -2455,16 +2428,17 @@ impl TlcState { } pub fn get(&self, tlc_id: &TLCId) -> Option<&TlcInfo> { - self.offered_tlcs - .tlcs - .iter() - .find(|tlc| tlc.tlc_id == *tlc_id) - .or_else(|| { - self.received_tlcs - .tlcs - .iter() - .find(|tlc| tlc.tlc_id == *tlc_id) - }) + if tlc_id.is_offered() { + self.offered_tlcs + .tlcs + .iter() + .find(|tlc| tlc.tlc_id == *tlc_id) + } else { + self.received_tlcs + .tlcs + .iter() + .find(|tlc| tlc.tlc_id == *tlc_id) + } } pub fn get_committed_received_tlcs(&self) -> Vec { @@ -2492,7 +2466,7 @@ impl TlcState { self.waiting_ack = waiting_ack; } - pub fn set_tlc_pending_remove(&mut self, tlc_id: TLCId, reason: RemoveTlcReason) { + pub fn insert_pending_remove_tlc(&mut self, tlc_id: TLCId, reason: RemoveTlcReason) { self.retryable_remove_tlcs .push(RetryableRemoveTlc::RemoveTlc(tlc_id, reason)); } @@ -2684,6 +2658,9 @@ impl TlcState { InboundTlctatus::AnnounceWaitAck => { tlc.status = TlcStatus::Inbound(InboundTlctatus::Committed); } + InboundTlctatus::LocalRemoved => { + tlc.status = TlcStatus::Inbound(InboundTlctatus::RemoveAckConfirmed); + } _ => {} } } @@ -4272,12 +4249,12 @@ impl ChannelActorState { self.get_current_commitment_number(for_remote) + 1 } - pub fn get_next_offering_tlc_id(&self) -> u64 { - self.tlc_state.get_next_offering() + pub fn get_next_offering_tlc_id(&self) -> TLCId { + TLCId::Offered(self.tlc_state.get_next_offering()) } - pub fn get_next_received_tlc_id(&self) -> u64 { - self.tlc_state.get_next_received() + pub fn get_next_received_tlc_id(&self) -> TLCId { + TLCId::Received(self.tlc_state.get_next_received()) } pub fn increment_next_offered_tlc_id(&mut self) { @@ -4288,12 +4265,12 @@ impl ChannelActorState { self.tlc_state.increment_received(); } - pub fn get_offered_tlc(&self, tlc_id: u64) -> Option<&TlcInfo> { - self.tlc_state.get(&TLCId::Offered(tlc_id)) + pub fn get_offered_tlc(&self, tlc_id: TLCId) -> Option<&TlcInfo> { + self.tlc_state.get(&tlc_id) } - pub fn get_received_tlc(&self, tlc_id: u64) -> Option<&TlcInfo> { - self.tlc_state.get(&TLCId::Received(tlc_id)) + pub fn get_received_tlc(&self, tlc_id: TLCId) -> Option<&TlcInfo> { + self.tlc_state.get(&tlc_id) } pub(crate) fn set_received_tlc_preimage(&mut self, tlc_id: u64, preimage: Option) { @@ -4303,6 +4280,21 @@ impl ChannelActorState { } pub fn check_insert_tlc(&mut self, tlc: &TlcInfo) -> Result<(), ProcessingChannelError> { + let next_tlc_id = if tlc.is_offered() { + self.get_next_offering_tlc_id() + } else { + self.get_next_received_tlc_id() + }; + if tlc.tlc_id != next_tlc_id { + eprintln!( + "Received tlc id {:?} is not the expected next id {:?}", + tlc.tlc_id, next_tlc_id + ); + return Err(ProcessingChannelError::InvalidParameter(format!( + "Received tlc id {:?} is not the expected next id {:?}", + tlc.tlc_id, next_tlc_id + ))); + } let payment_hash = tlc.payment_hash; if let Some(tlc) = self .tlc_state @@ -4555,7 +4547,8 @@ impl ChannelActorState { if let Some(remove_at) = &tlc.removed_at { let mut include = false; match tlc.status { - TlcStatus::Inbound(InboundTlctatus::LocalRemoved) => { + TlcStatus::Inbound(InboundTlctatus::LocalRemoved) + | TlcStatus::Inbound(InboundTlctatus::RemoveAckConfirmed) => { include = for_remote; } TlcStatus::Outbound(OutboundTlcStatus::RemoveWaitPrevAck) @@ -4845,16 +4838,16 @@ impl ChannelActorState { } fn create_outbounding_tlc(&self, command: AddTlcCommand) -> TlcInfo { - let id = self.get_next_offering_tlc_id(); + let tlc_id = self.get_next_offering_tlc_id(); assert!( - self.get_offered_tlc(id).is_none(), + self.get_offered_tlc(tlc_id).is_none(), "Must not have the same id in pending offered tlcs" ); TlcInfo { channel_id: self.get_id(), status: TlcStatus::Outbound(OutboundTlcStatus::LocalAnnounced), - tlc_id: TLCId::Offered(id), + tlc_id, amount: command.amount, payment_hash: command.payment_hash, expiry: command.expiry, @@ -5701,13 +5694,6 @@ impl ChannelActorState { eprintln!("handle revoke and ack peer message: {:?}", &revocation_data); let need_commitment_signed = self.tlc_state.update_for_revoke_and_ack(); - // if need_commitment_signed { - // myself - // .send_message(ChannelActorMessage::Command( - // ChannelCommand::CommitmentSigned(), - // )) - // .expect(ASSUME_NETWORK_ACTOR_ALIVE); - // } network .send_message(NetworkActorMessage::new_notification( NetworkServiceEvent::RevokeAndAckReceived( From da784b1d56e4e1af6354f2360c282c30aa4e5b6d Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 25 Dec 2024 16:47:34 +0800 Subject: [PATCH 033/119] comments feedback for function naming --- src/fiber/channel.rs | 4 ++-- src/fiber/tests/test_utils.rs | 1 - src/store/store.rs | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 6a703249c..93c679194 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -443,7 +443,7 @@ where if need_commitment_signed { self.handle_commitment_signed_command(state)?; } - self.flush_tlc_operations(myself, state).await; + self.update_tlc_status_on_ack(myself, state).await; Ok(()) } FiberChannelMessage::ChannelReady(_channel_ready) => { @@ -710,7 +710,7 @@ where } } - async fn flush_tlc_operations( + async fn update_tlc_status_on_ack( &self, myself: &ActorRef, state: &mut ChannelActorState, diff --git a/src/fiber/tests/test_utils.rs b/src/fiber/tests/test_utils.rs index 0e45f7695..e012e98ac 100644 --- a/src/fiber/tests/test_utils.rs +++ b/src/fiber/tests/test_utils.rs @@ -162,7 +162,6 @@ pub fn mock_ecdsa_signature() -> EcdsaSignature { pub fn generate_store() -> Store { let temp_dir = TempDir::new("test-fnn-node"); - eprintln!("Store directory: {:?}", temp_dir.as_ref()); let store = Store::new(temp_dir.as_ref()); store.expect("create store") } diff --git a/src/store/store.rs b/src/store/store.rs index e44b00f4c..ce6e30020 100644 --- a/src/store/store.rs +++ b/src/store/store.rs @@ -97,7 +97,6 @@ impl Store { let mut options = Options::default(); options.create_if_missing(true); options.set_compression_type(DBCompressionType::Lz4); - // dump trace log to /tmp/rocksdb.log let db = Arc::new(DB::open(&options, path).map_err(|e| e.to_string())?); Ok(db) } From 3b48f436d573af49725a7b4b1facec79d82c9366 Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 25 Dec 2024 18:58:19 +0800 Subject: [PATCH 034/119] add unit test test_network_add_two_tlcs_remove_one --- src/fiber/channel.rs | 8 +- src/fiber/tests/channel.rs | 160 +++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+), 7 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 93c679194..6512c2422 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -1183,13 +1183,7 @@ where )) .expect(ASSUME_NETWORK_ACTOR_ALIVE); state.save_remote_nonce_for_raa(); - if state - .tlc_state - .all_tlcs() - .filter(|tlc| tlc.is_offered()) - .count() - > 0 - { + if state.tlc_state.all_tlcs().count() > 0 { state.tlc_state.set_waiting_ack(true); } eprintln!("finished sent commitment_signed"); diff --git a/src/fiber/tests/channel.rs b/src/fiber/tests/channel.rs index 2b4ce0a9c..23758a22b 100644 --- a/src/fiber/tests/channel.rs +++ b/src/fiber/tests/channel.rs @@ -2278,6 +2278,166 @@ async fn do_test_remove_tlc_with_wrong_hash_algorithm( assert!(remove_tlc_result.is_err()); } +#[tokio::test] +async fn test_network_add_two_tlcs_remove_one() { + let _span = tracing::info_span!("node", node = "test").entered(); + let node_a_funding_amount = 100000000000; + let node_b_funding_amount = 100000000000; + + let (node_a, node_b, 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 + + let old_a_balance = node_a.get_local_balance_from_channel(channel_id); + let old_b_balance = node_b.get_local_balance_from_channel(channel_id); + + tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + + let preimage_a = [1; 32]; + let algorithm = HashAlgorithm::Sha256; + let digest = algorithm.hash(&preimage_a); + + let add_tlc_result_a = call!(node_a.network_actor, |rpc_reply| { + NetworkActorMessage::Command(NetworkActorCommand::ControlFiberChannel( + ChannelCommandWithId { + channel_id: channel_id, + command: ChannelCommand::AddTlc( + AddTlcCommand { + amount: 1000, + hash_algorithm: algorithm, + payment_hash: digest.into(), + expiry: now_timestamp_as_millis_u64() + DEFAULT_EXPIRY_DELTA, + onion_packet: None, + shared_secret: NO_SHARED_SECRET.clone(), + previous_tlc: None, + }, + rpc_reply, + ), + }, + )) + }) + .expect("node_a alive") + .expect("successfully added tlc"); + eprintln!("add_tlc_result: {:?}", add_tlc_result_a); + + // if we don't wait for a while, the next add_tlc will fail with temporary failure + let preimage_b = [2; 32]; + let algorithm = HashAlgorithm::Sha256; + let digest = algorithm.hash(&preimage_b); + let add_tlc_result = call!(node_a.network_actor, |rpc_reply| { + NetworkActorMessage::Command(NetworkActorCommand::ControlFiberChannel( + ChannelCommandWithId { + channel_id: channel_id, + command: ChannelCommand::AddTlc( + AddTlcCommand { + amount: 2000, + hash_algorithm: algorithm, + payment_hash: digest.into(), + expiry: now_timestamp_as_millis_u64() + DEFAULT_EXPIRY_DELTA, + onion_packet: None, + shared_secret: NO_SHARED_SECRET.clone(), + previous_tlc: None, + }, + rpc_reply, + ), + }, + )) + }) + .expect("node_b alive"); + assert!(add_tlc_result.is_err()); + + // now wait for a while, then add a tlc again, it will success + tokio::time::sleep(tokio::time::Duration::from_millis(200)).await; + let add_tlc_result_b = call!(node_a.network_actor, |rpc_reply| { + NetworkActorMessage::Command(NetworkActorCommand::ControlFiberChannel( + ChannelCommandWithId { + channel_id: channel_id, + command: ChannelCommand::AddTlc( + AddTlcCommand { + amount: 2000, + hash_algorithm: algorithm, + payment_hash: digest.into(), + expiry: now_timestamp_as_millis_u64() + DEFAULT_EXPIRY_DELTA, + onion_packet: None, + shared_secret: NO_SHARED_SECRET.clone(), + previous_tlc: None, + }, + rpc_reply, + ), + }, + )) + }) + .expect("node_b alive"); + assert!(add_tlc_result_b.is_ok()); + + eprintln!("add_tlc_result: {:?}", add_tlc_result_b); + + tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; + // remove tlc from node_b + let res = call!(node_b.network_actor, |rpc_reply| { + NetworkActorMessage::Command(NetworkActorCommand::ControlFiberChannel( + ChannelCommandWithId { + channel_id: channel_id, + command: ChannelCommand::RemoveTlc( + RemoveTlcCommand { + id: add_tlc_result_a.tlc_id, + reason: RemoveTlcReason::RemoveTlcFulfill(RemoveTlcFulfill { + payment_preimage: preimage_a.into(), + }), + }, + rpc_reply, + ), + }, + )) + }) + .expect("node_b alive") + .expect("successfully removed tlc"); + eprintln!("remove tlc result: {:?}", res); + tokio::time::sleep(tokio::time::Duration::from_millis(200)).await; + + let new_a_balance = node_a.get_local_balance_from_channel(channel_id); + let new_b_balance = node_b.get_local_balance_from_channel(channel_id); + eprintln!( + "old_a_balance: {}, new_a_balance: {}, old_b_balance: {}, new_b_balance: {}", + old_a_balance, new_a_balance, old_b_balance, new_b_balance + ); + assert_eq!(new_a_balance, old_a_balance - 1000); + assert_eq!(new_b_balance, old_b_balance + 1000); + + tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; + // remove the later tlc from node_b + let res = call!(node_b.network_actor, |rpc_reply| { + NetworkActorMessage::Command(NetworkActorCommand::ControlFiberChannel( + ChannelCommandWithId { + channel_id: channel_id, + command: ChannelCommand::RemoveTlc( + RemoveTlcCommand { + id: add_tlc_result_b.unwrap().tlc_id, + reason: RemoveTlcReason::RemoveTlcFulfill(RemoveTlcFulfill { + payment_preimage: preimage_b.into(), + }), + }, + rpc_reply, + ), + }, + )) + }) + .expect("node_b alive") + .expect("successfully removed tlc"); + eprintln!("remove tlc result: {:?}", res); + tokio::time::sleep(tokio::time::Duration::from_millis(400)).await; + + let new_a_balance = node_a.get_local_balance_from_channel(channel_id); + let new_b_balance = node_b.get_local_balance_from_channel(channel_id); + eprintln!( + "old_a_balance: {}, new_a_balance: {}, old_b_balance: {}, new_b_balance: {}", + old_a_balance, new_a_balance, old_b_balance, new_b_balance + ); + assert_eq!(new_a_balance, old_a_balance - 3000); + assert_eq!(new_b_balance, old_b_balance + 3000); +} + #[tokio::test] async fn test_remove_tlc_with_expiry_error() { let node_a_funding_amount = 100000000000; From 84d5160294cf7438afb6e88822fae488948ddab5 Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 25 Dec 2024 20:14:38 +0800 Subject: [PATCH 035/119] fix test for wait_ack --- .../e2e/open-use-close-a-channel/13-remove-tlc-from-NODE1.bru | 4 ++++ .../e2e/open-use-close-a-channel/14-remove-tlc-from-NODE3.bru | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/tests/bruno/e2e/open-use-close-a-channel/13-remove-tlc-from-NODE1.bru b/tests/bruno/e2e/open-use-close-a-channel/13-remove-tlc-from-NODE1.bru index ea72138cc..d516771d6 100644 --- a/tests/bruno/e2e/open-use-close-a-channel/13-remove-tlc-from-NODE1.bru +++ b/tests/bruno/e2e/open-use-close-a-channel/13-remove-tlc-from-NODE1.bru @@ -37,6 +37,10 @@ assert { res.body.result: isNull } +script:pre-request { + await new Promise(r => setTimeout(r, 500)); +} + script:post-response { // Sleep for sometime to make sure current operation finishes before next request starts. await new Promise(r => setTimeout(r, 100)); diff --git a/tests/bruno/e2e/open-use-close-a-channel/14-remove-tlc-from-NODE3.bru b/tests/bruno/e2e/open-use-close-a-channel/14-remove-tlc-from-NODE3.bru index 2f9bfb1ae..8e0fc2d1a 100644 --- a/tests/bruno/e2e/open-use-close-a-channel/14-remove-tlc-from-NODE3.bru +++ b/tests/bruno/e2e/open-use-close-a-channel/14-remove-tlc-from-NODE3.bru @@ -37,6 +37,10 @@ assert { res.body.result: isUndefined } +script:pre-request { + await new Promise(r => setTimeout(r, 500)); +} + script:post-response { console.log("step 14: ", res.body); // Sleep for sometime to make sure current operation finishes before next request starts. From de3d1eabce0ca484285843a2934e03b82fdd735d Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 25 Dec 2024 21:17:23 +0800 Subject: [PATCH 036/119] fix reestablish e2e test case failure --- src/fiber/channel.rs | 3 +++ tests/nodes/start.sh | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 6512c2422..725a16dbf 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -5769,6 +5769,9 @@ impl ChannelActorState { } } } + // previous waiting_ack maybe true, reset it after reestablish the channel + // if we need to resend CommitmentSigned message, it will be set to proper status again + self.tlc_state.set_waiting_ack(false); if need_resend_commitment_signed { debug!("Resend CommitmentSigned message"); network diff --git a/tests/nodes/start.sh b/tests/nodes/start.sh index f41ceaa28..66a20cfd6 100755 --- a/tests/nodes/start.sh +++ b/tests/nodes/start.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -euo pipefail export SHELLOPTS -export RUST_BACKTRACE=full RUST_LOG=info,fnn=debug,fnn::cch::actor::tracker=off +export RUST_BACKTRACE=full RUST_LOG=info,fnn=debug,fnn::cch::actor::tracker=off,fnn::fiber::gossip=off,fnn::fiber::graph=off should_remove_old_state="${REMOVE_OLD_STATE:-}" should_clean_fiber_state="${REMOVE_OLD_FIBER:-}" From 308a534c2aca217f75841937bb6b361a81575387 Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 25 Dec 2024 21:36:16 +0800 Subject: [PATCH 037/119] code cleanup --- src/fiber/channel.rs | 68 +++++++++++++++------------------------ src/fiber/tests/tlc_op.rs | 4 +-- 2 files changed, 28 insertions(+), 44 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 725a16dbf..015a5e38c 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -1036,7 +1036,6 @@ where state.get_current_commitment_numbers(), remove_tlc.reason, ); - eprintln!("remove tlc peer: {:?}", remove_tlc.tlc_id); Ok(()) } @@ -2321,6 +2320,14 @@ impl TlcInfo { self.tlc_id.flip_mut(); } + pub fn outbound_status(&self) -> OutboundTlcStatus { + self.status.as_outbound_status() + } + + pub fn inbound_status(&self) -> InboundTlctatus { + self.status.as_inbound_status() + } + fn get_hash(&self) -> ShortHash { self.payment_hash.as_ref()[..20] .try_into() @@ -2372,12 +2379,12 @@ impl PendingTlcs { .iter() .filter(|tlc| { if tlc.is_offered() { - match tlc.status.as_outbound_status() { + match tlc.outbound_status() { OutboundTlcStatus::Committed => true, _ => false, } } else { - match tlc.status.as_inbound_status() { + match tlc.inbound_status() { InboundTlctatus::Committed => true, _ => false, } @@ -2479,15 +2486,9 @@ impl TlcState { .chain(self.received_tlcs.tlcs.iter()) .filter(|tlc| { if tlc.is_offered() { - match tlc.status.as_outbound_status() { - OutboundTlcStatus::Committed => true, - _ => false, - } + matches!(tlc.outbound_status(), OutboundTlcStatus::Committed) } else { - match tlc.status.as_inbound_status() { - InboundTlctatus::Committed => true, - _ => false, - } + matches!(tlc.inbound_status(), InboundTlctatus::Committed) } }) } @@ -2535,11 +2536,9 @@ impl TlcState { remove_at: CommitmentNumbers, reason: RemoveTlcReason, ) { - for tlc in self.received_tlcs.iter_mut() { - if Into::::into(tlc.tlc_id) == tlc_id { - tlc.removed_at = Some((remove_at, reason.clone())); - tlc.status = TlcStatus::Inbound(InboundTlctatus::LocalRemoved); - } + if let Some(tlc) = self.get_mut(&TLCId::Received(tlc_id)) { + tlc.removed_at = Some((remove_at, reason.clone())); + tlc.status = TlcStatus::Inbound(InboundTlctatus::LocalRemoved); } } @@ -2549,18 +2548,16 @@ impl TlcState { remove_at: CommitmentNumbers, reason: RemoveTlcReason, ) { - for tlc in self.offered_tlcs.iter_mut() { - if Into::::into(tlc.tlc_id) == tlc_id { - tlc.removed_at = Some((remove_at, reason.clone())); - tlc.status = TlcStatus::Outbound(OutboundTlcStatus::RemoteRemoved); - } + if let Some(tlc) = self.get_mut(&TLCId::Offered(tlc_id)) { + tlc.removed_at = Some((remove_at, reason.clone())); + tlc.status = TlcStatus::Outbound(OutboundTlcStatus::RemoteRemoved); } } pub fn commitment_signed(&self, for_remote: bool) -> Vec { let mut active_tls = vec![]; for tlc in self.offered_tlcs.tlcs.iter() { - let include = match tlc.status.as_outbound_status() { + let include = match tlc.outbound_status() { OutboundTlcStatus::LocalAnnounced => for_remote, OutboundTlcStatus::Committed => true, OutboundTlcStatus::RemoteRemoved => for_remote, @@ -2573,7 +2570,7 @@ impl TlcState { } } for tlc in self.received_tlcs.tlcs.iter() { - let include = match tlc.status.as_inbound_status() { + let include = match tlc.inbound_status() { InboundTlctatus::RemoteAnnounced => !for_remote, InboundTlctatus::AnnounceWaitPrevAck => !for_remote, InboundTlctatus::AnnounceWaitAck => true, @@ -2591,7 +2588,7 @@ impl TlcState { pub fn update_for_commitment_signed(&mut self) -> bool { let mut need_another_commitment_signed = false; for tlc in self.offered_tlcs.tlcs.iter_mut() { - match tlc.status.as_outbound_status() { + match tlc.outbound_status() { OutboundTlcStatus::RemoteRemoved => { let status = if self.waiting_ack { need_another_commitment_signed = true; @@ -2605,7 +2602,7 @@ impl TlcState { } } for tlc in self.received_tlcs.tlcs.iter_mut() { - match tlc.status.as_inbound_status() { + match tlc.inbound_status() { InboundTlctatus::RemoteAnnounced => { let status = if self.waiting_ack { need_another_commitment_signed = true; @@ -2626,8 +2623,7 @@ impl TlcState { self.set_waiting_ack(false); let mut need_another_commitment_signed = false; for tlc in self.offered_tlcs.tlcs.iter_mut() { - let out_status = tlc.status.as_outbound_status(); - match out_status { + match tlc.outbound_status() { OutboundTlcStatus::LocalAnnounced => { tlc.status = TlcStatus::Outbound(OutboundTlcStatus::Committed); } @@ -2643,8 +2639,7 @@ impl TlcState { } for tlc in self.received_tlcs.tlcs.iter_mut() { - let in_status = tlc.status.as_inbound_status(); - match in_status { + match tlc.inbound_status() { InboundTlctatus::AnnounceWaitPrevAck => { need_another_commitment_signed = true; tlc.status = TlcStatus::Inbound(InboundTlctatus::AnnounceWaitAck); @@ -2661,20 +2656,9 @@ impl TlcState { need_another_commitment_signed || self.need_another_commitment_signed() } - pub fn build_ack_transaction(&self, _for_remote: bool) -> Vec { - let mut active_tls = vec![]; - for tlc in self.offered_tlcs.tlcs.iter() { - active_tls.push(tlc.clone()); - } - for tlc in self.received_tlcs.tlcs.iter() { - active_tls.push(tlc.clone()); - } - return active_tls; - } - pub fn need_another_commitment_signed(&self) -> bool { self.offered_tlcs.tlcs.iter().any(|tlc| { - let status = tlc.status.as_outbound_status(); + let status = tlc.outbound_status(); matches!( status, OutboundTlcStatus::LocalAnnounced @@ -2683,7 +2667,7 @@ impl TlcState { | OutboundTlcStatus::RemoveWaitAck ) }) || self.received_tlcs.tlcs.iter().any(|tlc| { - let status = tlc.status.as_inbound_status(); + let status = tlc.inbound_status(); matches!( status, InboundTlctatus::RemoteAnnounced diff --git a/src/fiber/tests/tlc_op.rs b/src/fiber/tests/tlc_op.rs index aeb9a3da6..54b1c7232 100644 --- a/src/fiber/tests/tlc_op.rs +++ b/src/fiber/tests/tlc_op.rs @@ -303,7 +303,7 @@ impl Actor for TlcActor { state.tlc_state.update_for_commitment_signed(); eprintln!("sending peer revoke and ack ...."); - let tlcs = state.tlc_state.build_ack_transaction(true); + let tlcs = state.tlc_state.commitment_signed(false); let hash = sign_tlcs(&tlcs); self.network .send_message(NetworkActorMessage::PeerMsg( @@ -327,7 +327,7 @@ impl Actor for TlcActor { } TlcActorMessage::PeerRevokeAndAck(peer_hash) => { eprintln!("Peer {} processed peer revoke and ack ....", state.peer_id); - let tlcs = state.tlc_state.build_ack_transaction(false); + let tlcs = state.tlc_state.commitment_signed(true); let hash = sign_tlcs(&tlcs); assert_eq!(hash, peer_hash); From 8c1f72c79f6429d2ffa69ad171dee3adc8079e2f Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 25 Dec 2024 22:28:38 +0800 Subject: [PATCH 038/119] code refactoring --- src/fiber/channel.rs | 50 ++++++++++++++++++-------------------- src/fiber/tests/channel.rs | 8 +++--- src/fiber/tests/tlc_op.rs | 31 ++++++++++++----------- 3 files changed, 42 insertions(+), 47 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 015a5e38c..affe82422 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -2554,35 +2554,31 @@ impl TlcState { } } - pub fn commitment_signed(&self, for_remote: bool) -> Vec { - let mut active_tls = vec![]; - for tlc in self.offered_tlcs.tlcs.iter() { - let include = match tlc.outbound_status() { + pub fn commitment_signed_tcls(&self, for_remote: bool) -> impl Iterator + '_ { + self.offered_tlcs + .tlcs + .iter() + .filter(move |tlc| match tlc.outbound_status() { OutboundTlcStatus::LocalAnnounced => for_remote, OutboundTlcStatus::Committed => true, OutboundTlcStatus::RemoteRemoved => for_remote, OutboundTlcStatus::RemoveWaitPrevAck => for_remote, OutboundTlcStatus::RemoveWaitAck => false, OutboundTlcStatus::RemoveAckConfirmed => false, - }; - if include { - active_tls.push(tlc.clone()); - } - } - for tlc in self.received_tlcs.tlcs.iter() { - let include = match tlc.inbound_status() { - InboundTlctatus::RemoteAnnounced => !for_remote, - InboundTlctatus::AnnounceWaitPrevAck => !for_remote, - InboundTlctatus::AnnounceWaitAck => true, - InboundTlctatus::Committed => true, - InboundTlctatus::LocalRemoved => !for_remote, - InboundTlctatus::RemoveAckConfirmed => false, - }; - if include { - active_tls.push(tlc.clone()); - } - } - return active_tls; + }) + .chain( + self.received_tlcs + .tlcs + .iter() + .filter(move |tlc| match tlc.inbound_status() { + InboundTlctatus::RemoteAnnounced => !for_remote, + InboundTlctatus::AnnounceWaitPrevAck => !for_remote, + InboundTlctatus::AnnounceWaitAck => true, + InboundTlctatus::Committed => true, + InboundTlctatus::LocalRemoved => !for_remote, + InboundTlctatus::RemoveAckConfirmed => false, + }), + ) } pub fn update_for_commitment_signed(&mut self) -> bool { @@ -4498,17 +4494,17 @@ impl ChannelActorState { fn get_active_received_tlcs(&self, for_remote: bool) -> Vec { self.tlc_state - .commitment_signed(for_remote) - .into_iter() + .commitment_signed_tcls(for_remote) .filter(|tlc| tlc.is_received()) + .map(|tlc| tlc.clone()) .collect() } fn get_active_offered_tlcs(&self, for_remote: bool) -> Vec { self.tlc_state - .commitment_signed(for_remote) - .into_iter() + .commitment_signed_tcls(for_remote) .filter(|tlc| tlc.is_offered()) + .map(|tlc| tlc.clone()) .collect() } diff --git a/src/fiber/tests/channel.rs b/src/fiber/tests/channel.rs index 23758a22b..f5a851caa 100644 --- a/src/fiber/tests/channel.rs +++ b/src/fiber/tests/channel.rs @@ -1289,7 +1289,7 @@ async fn test_send_payment_with_3_nodes() { assert_eq!(res.status, PaymentSessionStatus::Inflight); assert!(res.fee > 0); // sleep for 2 seconds to make sure the payment is sent - tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + tokio::time::sleep(tokio::time::Duration::from_millis(2000)).await; let message = |rpc_reply| -> NetworkActorMessage { NetworkActorMessage::Command(NetworkActorCommand::GetPayment(res.payment_hash, rpc_reply)) }; @@ -1338,7 +1338,7 @@ async fn test_send_payment_with_rev_3_nodes() { let node_a_local = node_a.get_local_balance_from_channel(channel_2); // sleep for 2 seconds to make sure the channel is established - tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + tokio::time::sleep(tokio::time::Duration::from_millis(2000)).await; let sent_amount = 1000000 + 5; let node_a_pubkey = node_a.pubkey.clone(); let message = |rpc_reply| -> NetworkActorMessage { @@ -1367,7 +1367,7 @@ async fn test_send_payment_with_rev_3_nodes() { assert_eq!(res.status, PaymentSessionStatus::Inflight); assert!(res.fee > 0); // sleep for 2 seconds to make sure the payment is sent - tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + tokio::time::sleep(tokio::time::Duration::from_millis(2000)).await; let message = |rpc_reply| -> NetworkActorMessage { NetworkActorMessage::Command(NetworkActorCommand::GetPayment(res.payment_hash, rpc_reply)) }; @@ -2394,7 +2394,7 @@ async fn test_network_add_two_tlcs_remove_one() { .expect("node_b alive") .expect("successfully removed tlc"); eprintln!("remove tlc result: {:?}", res); - tokio::time::sleep(tokio::time::Duration::from_millis(200)).await; + tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; let new_a_balance = node_a.get_local_balance_from_channel(channel_id); let new_b_balance = node_b.get_local_balance_from_channel(channel_id); diff --git a/src/fiber/tests/tlc_op.rs b/src/fiber/tests/tlc_op.rs index 54b1c7232..d2e8fbcf4 100644 --- a/src/fiber/tests/tlc_op.rs +++ b/src/fiber/tests/tlc_op.rs @@ -13,10 +13,9 @@ use ckb_types::packed::Byte32; use ractor::{async_trait as rasync_trait, Actor, ActorProcessingErr, ActorRef}; use std::collections::HashMap; -fn sign_tlcs(tlcs: &Vec) -> Hash256 { +fn sign_tlcs<'a>(tlcs: impl Iterator) -> Hash256 { // serialize active_tls to ge a hash let mut keyparts = tlcs - .iter() .map(|tlc| (tlc.amount, tlc.payment_hash)) .collect::>(); @@ -227,8 +226,8 @@ impl Actor for TlcActor { .expect("send ok"); // send commitment signed - let tlcs = state.tlc_state.commitment_signed(false); - let hash = sign_tlcs(&tlcs); + let tlcs = state.tlc_state.commitment_signed_tcls(false); + let hash = sign_tlcs(tlcs); eprintln!("got hash: {:?}", hash); self.network .send_message(NetworkActorMessage::PeerMsg( @@ -255,8 +254,8 @@ impl Actor for TlcActor { .expect("send ok"); // send commitment signed - let tlcs = state.tlc_state.commitment_signed(false); - let hash = sign_tlcs(&tlcs); + let tlcs = state.tlc_state.commitment_signed_tcls(false); + let hash = sign_tlcs(tlcs); eprintln!("got hash: {:?}", hash); self.network .send_message(NetworkActorMessage::PeerMsg( @@ -294,8 +293,8 @@ impl Actor for TlcActor { "\nPeer {} processed peer commitment_signed ....", state.peer_id ); - let tlcs = state.tlc_state.commitment_signed(true); - let hash = sign_tlcs(&tlcs); + let tlcs = state.tlc_state.commitment_signed_tcls(true); + let hash = sign_tlcs(tlcs); assert_eq!(hash, peer_hash); let peer = state.get_peer(); @@ -303,8 +302,8 @@ impl Actor for TlcActor { state.tlc_state.update_for_commitment_signed(); eprintln!("sending peer revoke and ack ...."); - let tlcs = state.tlc_state.commitment_signed(false); - let hash = sign_tlcs(&tlcs); + let tlcs = state.tlc_state.commitment_signed_tcls(false); + let hash = sign_tlcs(tlcs); self.network .send_message(NetworkActorMessage::PeerMsg( peer.clone(), @@ -315,8 +314,8 @@ impl Actor for TlcActor { // send commitment signed from our side if necessary if state.tlc_state.need_another_commitment_signed() { eprintln!("sending another commitment signed ...."); - let tlcs = state.tlc_state.commitment_signed(false); - let hash = sign_tlcs(&tlcs); + let tlcs = state.tlc_state.commitment_signed_tcls(false); + let hash = sign_tlcs(tlcs); self.network .send_message(NetworkActorMessage::PeerMsg( peer, @@ -327,8 +326,8 @@ impl Actor for TlcActor { } TlcActorMessage::PeerRevokeAndAck(peer_hash) => { eprintln!("Peer {} processed peer revoke and ack ....", state.peer_id); - let tlcs = state.tlc_state.commitment_signed(true); - let hash = sign_tlcs(&tlcs); + let tlcs = state.tlc_state.commitment_signed_tcls(true); + let hash = sign_tlcs(tlcs); assert_eq!(hash, peer_hash); state.tlc_state.update_for_revoke_and_ack(); @@ -499,10 +498,10 @@ fn test_tlc_state_v2() { tlc_state_2.add_received_tlc(add_tlc1); tlc_state_2.add_received_tlc(add_tlc2); - let hash1 = sign_tlcs(&tlc_state.commitment_signed(true)); + let hash1 = sign_tlcs(tlc_state.commitment_signed_tcls(true)); eprintln!("hash1: {:?}", hash1); - let hash2 = sign_tlcs(&tlc_state_2.commitment_signed(false)); + let hash2 = sign_tlcs(tlc_state_2.commitment_signed_tcls(false)); eprintln!("hash2: {:?}", hash2); assert_eq!(hash1, hash2); } From 96100a5bd3d33f4d296777c9c9c857a6368cac96 Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 25 Dec 2024 23:03:40 +0800 Subject: [PATCH 039/119] fix wait ack --- src/fiber/channel.rs | 9 ++++----- src/fiber/tests/channel.rs | 2 ++ tests/bruno/e2e/reestablish/12-remove-tlc-from-NODE1.bru | 4 ---- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index affe82422..a2afda543 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -1182,17 +1182,16 @@ where )) .expect(ASSUME_NETWORK_ACTOR_ALIVE); state.save_remote_nonce_for_raa(); - if state.tlc_state.all_tlcs().count() > 0 { - state.tlc_state.set_waiting_ack(true); - } - eprintln!("finished sent commitment_signed"); + match flags { CommitmentSignedFlags::SigningCommitment(flags) => { let flags = flags | SigningCommitmentFlags::OUR_COMMITMENT_SIGNED_SENT; state.update_state(ChannelState::SigningCommitment(flags)); state.maybe_transition_to_tx_signatures(flags, &self.network)?; } - CommitmentSignedFlags::ChannelReady() => {} + CommitmentSignedFlags::ChannelReady() => { + state.tlc_state.set_waiting_ack(true); + } CommitmentSignedFlags::PendingShutdown() => { state.maybe_transition_to_shutdown(&self.network)?; } diff --git a/src/fiber/tests/channel.rs b/src/fiber/tests/channel.rs index f5a851caa..ac7a92fa0 100644 --- a/src/fiber/tests/channel.rs +++ b/src/fiber/tests/channel.rs @@ -2581,6 +2581,7 @@ async fn do_test_add_tlc_waiting_ack() { } } + tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; // send from b to a for i in 1..=2 { let add_tlc_command = AddTlcCommand { @@ -2607,6 +2608,7 @@ async fn do_test_add_tlc_waiting_ack() { let code = add_tlc_result.unwrap_err(); assert_eq!(code.error_code, TlcErrorCode::TemporaryChannelFailure); } else { + eprintln!("add_tlc_result: {:?}", add_tlc_result); assert!(add_tlc_result.is_ok()); } } diff --git a/tests/bruno/e2e/reestablish/12-remove-tlc-from-NODE1.bru b/tests/bruno/e2e/reestablish/12-remove-tlc-from-NODE1.bru index 79ed9dfc1..182ccfe14 100644 --- a/tests/bruno/e2e/reestablish/12-remove-tlc-from-NODE1.bru +++ b/tests/bruno/e2e/reestablish/12-remove-tlc-from-NODE1.bru @@ -37,10 +37,6 @@ assert { res.body.result: isNull } -script:pre-request { - await new Promise(r => setTimeout(r, 1000)); -} - script:post-response { // Sleep for sometime to make sure current operation finishes before next request starts. await new Promise(r => setTimeout(r, 100)); From 884fa50c4c719d9e8511e68d944465d9190bb8fa Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 26 Dec 2024 10:41:44 +0800 Subject: [PATCH 040/119] fix typo and add check for remove tlc --- src/fiber/channel.rs | 72 +++++++++++++++++++++++---------------- src/fiber/tests/tlc_op.rs | 8 ++--- 2 files changed, 46 insertions(+), 34 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index a2afda543..38172ca04 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -693,7 +693,7 @@ where if let Some((_removed_at, reason)) = &tlc.removed_at { if matches!( tlc.status, - TlcStatus::Inbound(InboundTlctatus::RemoveAckConfirmed) + TlcStatus::Inbound(InboundTlcStatus::RemoveAckConfirmed) | TlcStatus::Outbound(OutboundTlcStatus::RemoveAckConfirmed) ) { return Some((tlc.tlc_id, reason.clone())); @@ -2234,7 +2234,7 @@ pub enum OutboundTlcStatus { } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -pub enum InboundTlctatus { +pub enum InboundTlcStatus { RemoteAnnounced, AnnounceWaitPrevAck, AnnounceWaitAck, @@ -2246,7 +2246,7 @@ pub enum InboundTlctatus { #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub enum TlcStatus { Outbound(OutboundTlcStatus), - Inbound(InboundTlctatus), + Inbound(InboundTlcStatus), } impl TlcStatus { @@ -2259,7 +2259,7 @@ impl TlcStatus { } } - pub fn as_inbound_status(&self) -> InboundTlctatus { + pub fn as_inbound_status(&self) -> InboundTlcStatus { match self { TlcStatus::Inbound(status) => status.clone(), _ => { @@ -2323,7 +2323,7 @@ impl TlcInfo { self.status.as_outbound_status() } - pub fn inbound_status(&self) -> InboundTlctatus { + pub fn inbound_status(&self) -> InboundTlcStatus { self.status.as_inbound_status() } @@ -2384,7 +2384,7 @@ impl PendingTlcs { } } else { match tlc.inbound_status() { - InboundTlctatus::Committed => true, + InboundTlcStatus::Committed => true, _ => false, } } @@ -2487,7 +2487,7 @@ impl TlcState { if tlc.is_offered() { matches!(tlc.outbound_status(), OutboundTlcStatus::Committed) } else { - matches!(tlc.inbound_status(), InboundTlctatus::Committed) + matches!(tlc.inbound_status(), InboundTlcStatus::Committed) } }) } @@ -2536,8 +2536,9 @@ impl TlcState { reason: RemoveTlcReason, ) { if let Some(tlc) = self.get_mut(&TLCId::Received(tlc_id)) { + assert_eq!(tlc.inbound_status(), InboundTlcStatus::Committed); tlc.removed_at = Some((remove_at, reason.clone())); - tlc.status = TlcStatus::Inbound(InboundTlctatus::LocalRemoved); + tlc.status = TlcStatus::Inbound(InboundTlcStatus::LocalRemoved); } } @@ -2548,6 +2549,7 @@ impl TlcState { reason: RemoveTlcReason, ) { if let Some(tlc) = self.get_mut(&TLCId::Offered(tlc_id)) { + assert_eq!(tlc.outbound_status(), OutboundTlcStatus::Committed); tlc.removed_at = Some((remove_at, reason.clone())); tlc.status = TlcStatus::Outbound(OutboundTlcStatus::RemoteRemoved); } @@ -2570,12 +2572,12 @@ impl TlcState { .tlcs .iter() .filter(move |tlc| match tlc.inbound_status() { - InboundTlctatus::RemoteAnnounced => !for_remote, - InboundTlctatus::AnnounceWaitPrevAck => !for_remote, - InboundTlctatus::AnnounceWaitAck => true, - InboundTlctatus::Committed => true, - InboundTlctatus::LocalRemoved => !for_remote, - InboundTlctatus::RemoveAckConfirmed => false, + InboundTlcStatus::RemoteAnnounced => !for_remote, + InboundTlcStatus::AnnounceWaitPrevAck => !for_remote, + InboundTlcStatus::AnnounceWaitAck => true, + InboundTlcStatus::Committed => true, + InboundTlcStatus::LocalRemoved => !for_remote, + InboundTlcStatus::RemoveAckConfirmed => false, }), ) } @@ -2598,12 +2600,12 @@ impl TlcState { } for tlc in self.received_tlcs.tlcs.iter_mut() { match tlc.inbound_status() { - InboundTlctatus::RemoteAnnounced => { + InboundTlcStatus::RemoteAnnounced => { let status = if self.waiting_ack { need_another_commitment_signed = true; - InboundTlctatus::AnnounceWaitPrevAck + InboundTlcStatus::AnnounceWaitPrevAck } else { - InboundTlctatus::AnnounceWaitAck + InboundTlcStatus::AnnounceWaitAck }; tlc.status = TlcStatus::Inbound(status) } @@ -2635,15 +2637,15 @@ impl TlcState { for tlc in self.received_tlcs.tlcs.iter_mut() { match tlc.inbound_status() { - InboundTlctatus::AnnounceWaitPrevAck => { + InboundTlcStatus::AnnounceWaitPrevAck => { need_another_commitment_signed = true; - tlc.status = TlcStatus::Inbound(InboundTlctatus::AnnounceWaitAck); + tlc.status = TlcStatus::Inbound(InboundTlcStatus::AnnounceWaitAck); } - InboundTlctatus::AnnounceWaitAck => { - tlc.status = TlcStatus::Inbound(InboundTlctatus::Committed); + InboundTlcStatus::AnnounceWaitAck => { + tlc.status = TlcStatus::Inbound(InboundTlcStatus::Committed); } - InboundTlctatus::LocalRemoved => { - tlc.status = TlcStatus::Inbound(InboundTlctatus::RemoveAckConfirmed); + InboundTlcStatus::LocalRemoved => { + tlc.status = TlcStatus::Inbound(InboundTlcStatus::RemoveAckConfirmed); } _ => {} } @@ -2665,9 +2667,9 @@ impl TlcState { let status = tlc.inbound_status(); matches!( status, - InboundTlctatus::RemoteAnnounced - | InboundTlctatus::AnnounceWaitPrevAck - | InboundTlctatus::AnnounceWaitAck + InboundTlcStatus::RemoteAnnounced + | InboundTlcStatus::AnnounceWaitPrevAck + | InboundTlcStatus::AnnounceWaitAck ) }) } @@ -4312,7 +4314,7 @@ impl ChannelActorState { let current = self.tlc_state.get(&tlc_id).expect("TLC exists").clone(); assert!(matches!( current.status, - TlcStatus::Inbound(InboundTlctatus::RemoveAckConfirmed) + TlcStatus::Inbound(InboundTlcStatus::RemoveAckConfirmed) | TlcStatus::Outbound(OutboundTlcStatus::RemoveAckConfirmed) )); @@ -4520,8 +4522,8 @@ impl ChannelActorState { if let Some(remove_at) = &tlc.removed_at { let mut include = false; match tlc.status { - TlcStatus::Inbound(InboundTlctatus::LocalRemoved) - | TlcStatus::Inbound(InboundTlctatus::RemoveAckConfirmed) => { + TlcStatus::Inbound(InboundTlcStatus::LocalRemoved) + | TlcStatus::Inbound(InboundTlcStatus::RemoveAckConfirmed) => { include = for_remote; } TlcStatus::Outbound(OutboundTlcStatus::RemoveWaitPrevAck) @@ -4724,10 +4726,20 @@ impl ChannelActorState { "TLC is already removed".to_string(), )); } + if (tlc.is_offered() && tlc.outbound_status() != OutboundTlcStatus::Committed) + || (tlc.is_received() && tlc.inbound_status() != InboundTlcStatus::Committed) + { + return Err(ProcessingChannelError::InvalidState( + "TLC is not in Committed status".to_string(), + )); + } if let RemoveTlcReason::RemoveTlcFulfill(fulfill) = reason { let filled_payment_hash: Hash256 = tlc.hash_algorithm.hash(fulfill.payment_preimage).into(); if tlc.payment_hash != filled_payment_hash { + // actually this branch should never be reached in normal case + // `FinalIncorrectPreimage` will be returned in `apply_add_tlc_operation_with_peeled_onion_packet` + // when the preimage is incorrect return Err(ProcessingChannelError::FinalIncorrectPreimage); } } @@ -4839,7 +4851,7 @@ impl ChannelActorState { fn create_inbounding_tlc(&self, message: AddTlc) -> Result { let tlc_info = TlcInfo { tlc_id: TLCId::Received(message.tlc_id), - status: TlcStatus::Inbound(InboundTlctatus::RemoteAnnounced), + status: TlcStatus::Inbound(InboundTlcStatus::RemoteAnnounced), channel_id: self.get_id(), amount: message.amount, payment_hash: message.payment_hash, diff --git a/src/fiber/tests/tlc_op.rs b/src/fiber/tests/tlc_op.rs index d2e8fbcf4..6bfc7d290 100644 --- a/src/fiber/tests/tlc_op.rs +++ b/src/fiber/tests/tlc_op.rs @@ -1,6 +1,6 @@ use crate::fiber::channel::TlcInfo; use crate::fiber::channel::{ - CommitmentNumbers, InboundTlctatus, OutboundTlcStatus, TLCId, TlcState, TlcStatus, + CommitmentNumbers, InboundTlcStatus, OutboundTlcStatus, TLCId, TlcState, TlcStatus, }; use crate::fiber::hash_algorithm::HashAlgorithm; use crate::fiber::types::RemoveTlcFulfill; @@ -271,7 +271,7 @@ impl Actor for TlcActor { ); let mut tlc = add_tlc.clone(); tlc.flip_mut(); - tlc.status = TlcStatus::Inbound(InboundTlctatus::RemoteAnnounced); + tlc.status = TlcStatus::Inbound(InboundTlcStatus::RemoteAnnounced); state.tlc_state.add_received_tlc(tlc); eprintln!("add peer tlc successfully: {:?}", add_tlc); } @@ -493,8 +493,8 @@ fn test_tlc_state_v2() { let mut tlc_state_2 = TlcState::default(); add_tlc1.flip_mut(); add_tlc2.flip_mut(); - add_tlc1.status = TlcStatus::Inbound(InboundTlctatus::RemoteAnnounced); - add_tlc2.status = TlcStatus::Inbound(InboundTlctatus::RemoteAnnounced); + add_tlc1.status = TlcStatus::Inbound(InboundTlcStatus::RemoteAnnounced); + add_tlc2.status = TlcStatus::Inbound(InboundTlcStatus::RemoteAnnounced); tlc_state_2.add_received_tlc(add_tlc1); tlc_state_2.add_received_tlc(add_tlc2); From a01f93f2095211c010aab0957930effea7925a28 Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 26 Dec 2024 10:54:36 +0800 Subject: [PATCH 041/119] fix typo in function --- src/fiber/channel.rs | 6 +++--- src/fiber/tests/tlc_op.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 38172ca04..d51ae3832 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -2555,7 +2555,7 @@ impl TlcState { } } - pub fn commitment_signed_tcls(&self, for_remote: bool) -> impl Iterator + '_ { + pub fn commitment_signed_tlcs(&self, for_remote: bool) -> impl Iterator + '_ { self.offered_tlcs .tlcs .iter() @@ -4495,7 +4495,7 @@ impl ChannelActorState { fn get_active_received_tlcs(&self, for_remote: bool) -> Vec { self.tlc_state - .commitment_signed_tcls(for_remote) + .commitment_signed_tlcs(for_remote) .filter(|tlc| tlc.is_received()) .map(|tlc| tlc.clone()) .collect() @@ -4503,7 +4503,7 @@ impl ChannelActorState { fn get_active_offered_tlcs(&self, for_remote: bool) -> Vec { self.tlc_state - .commitment_signed_tcls(for_remote) + .commitment_signed_tlcs(for_remote) .filter(|tlc| tlc.is_offered()) .map(|tlc| tlc.clone()) .collect() diff --git a/src/fiber/tests/tlc_op.rs b/src/fiber/tests/tlc_op.rs index 6bfc7d290..098ed0e06 100644 --- a/src/fiber/tests/tlc_op.rs +++ b/src/fiber/tests/tlc_op.rs @@ -498,10 +498,10 @@ fn test_tlc_state_v2() { tlc_state_2.add_received_tlc(add_tlc1); tlc_state_2.add_received_tlc(add_tlc2); - let hash1 = sign_tlcs(tlc_state.commitment_signed_tcls(true)); + let hash1 = sign_tlcs(tlc_state.commitment_signed_tlcs(true)); eprintln!("hash1: {:?}", hash1); - let hash2 = sign_tlcs(tlc_state_2.commitment_signed_tcls(false)); + let hash2 = sign_tlcs(tlc_state_2.commitment_signed_tlcs(false)); eprintln!("hash2: {:?}", hash2); assert_eq!(hash1, hash2); } From be9f491f94195a65a3ed6651f9fb8589f90bf254 Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 26 Dec 2024 11:25:47 +0800 Subject: [PATCH 042/119] fix reestablish e2e --- src/fiber/channel.rs | 39 ++++++++++++------- src/fiber/tests/tlc_op.rs | 12 +++--- .../reestablish/09-reconnect-peer-NODE1.bru | 2 +- ...NODE1.bru => 10-remove-tlc-from-NODE1.bru} | 5 ++- ...m-NODE1.bru => 11-shutdown-from-NODE1.bru} | 2 +- 5 files changed, 36 insertions(+), 24 deletions(-) rename tests/bruno/e2e/reestablish/{12-remove-tlc-from-NODE1.bru => 10-remove-tlc-from-NODE1.bru} (77%) rename tests/bruno/e2e/reestablish/{10-shutdown-from-NODE1.bru => 11-shutdown-from-NODE1.bru} (98%) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index d51ae3832..e9383c676 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -2062,14 +2062,12 @@ where remote_commitment_number: channel.get_current_commitment_number(false), }; - let command = FiberMessageWithPeerId::new( - self.get_remote_peer_id(), - FiberMessage::reestablish_channel(reestablish_channel), - ); - self.network .send_message(NetworkActorMessage::new_command( - NetworkActorCommand::SendFiberMessage(command), + NetworkActorCommand::SendFiberMessage(FiberMessageWithPeerId::new( + self.get_remote_peer_id(), + FiberMessage::reestablish_channel(reestablish_channel), + )), )) .expect(ASSUME_NETWORK_ACTOR_ALIVE); @@ -4007,8 +4005,6 @@ impl ChannelActorState { let sign_ctx = { let local_nonce = self.get_local_nonce(); let remote_nonce = self.get_remote_nonce(); - eprintln!("local_nonce: {:?}", local_nonce); - eprintln!("remote_nonce: {:?}", remote_nonce); let nonces = [local_nonce, remote_nonce]; let agg_nonce = AggNonce::sum(nonces); Musig2SignContext { @@ -4047,17 +4043,13 @@ impl ChannelActorState { }; let commitment_number = self.get_remote_commitment_number() - 1; - eprintln!("send first commitment_number: {}", commitment_number); let commitment_lock_script_args = [ &blake2b_256(x_only_aggregated_pubkey)[0..20], self.get_delay_epoch_as_lock_args_bytes().as_slice(), commitment_number.to_be_bytes().as_slice(), ] .concat(); - eprintln!( - "send first commitment_lock_script_args: {:?}", - commitment_lock_script_args - ); + let message = blake2b_256( [ output.as_slice(), @@ -5530,6 +5522,7 @@ impl ChannelActorState { "begin to handle revoke and ack peer message: {:?}", &revoke_and_ack ); + self.tlc_state.debug(); let RevokeAndAck { channel_id: _, revocation_partial_signature, @@ -5698,8 +5691,8 @@ impl ChannelActorState { network: &ActorRef, ) -> ProcessingChannelResult { debug!( - "Handling reestablish channel message: {:?}, our commitment_numbers {:?}", - reestablish_channel, self.commitment_numbers, + "Handling reestablish channel message: {:?}, our commitment_numbers {:?} in channel state {:?}", + reestablish_channel, self.commitment_numbers, self.state ); self.reestablishing = false; match self.state { @@ -5763,6 +5756,10 @@ impl ChannelActorState { // previous waiting_ack maybe true, reset it after reestablish the channel // if we need to resend CommitmentSigned message, it will be set to proper status again self.tlc_state.set_waiting_ack(false); + debug!( + "Resend AddTlc and RemoveTlc messages if needed: {}", + need_resend_commitment_signed + ); if need_resend_commitment_signed { debug!("Resend CommitmentSigned message"); network @@ -5776,6 +5773,7 @@ impl ChannelActorState { } } else if acutal_local_commitment_number == expected_local_commitment_number + 1 { // wait for remote to resend the RevokeAndAck message, do nothing here + warn!("wait for remote to resend the RevokeAndAck message, do nothing here"); } else { // unreachable state, just log an error for potential bugs error!( @@ -5793,6 +5791,17 @@ impl ChannelActorState { // and resend the RevokeAndAck message. self.set_remote_commitment_number(acutal_remote_commitment_number); self.send_revoke_and_ack_message(network)?; + let need_commitment_signed = self.tlc_state.update_for_commitment_signed(); + if need_commitment_signed { + network + .send_message(NetworkActorMessage::new_command( + NetworkActorCommand::ControlFiberChannel(ChannelCommandWithId { + channel_id: self.get_id(), + command: ChannelCommand::CommitmentSigned(), + }), + )) + .expect(ASSUME_NETWORK_ACTOR_ALIVE); + } } else { // unreachable state, just log an error for potential bugs error!( diff --git a/src/fiber/tests/tlc_op.rs b/src/fiber/tests/tlc_op.rs index 098ed0e06..99e56206d 100644 --- a/src/fiber/tests/tlc_op.rs +++ b/src/fiber/tests/tlc_op.rs @@ -226,7 +226,7 @@ impl Actor for TlcActor { .expect("send ok"); // send commitment signed - let tlcs = state.tlc_state.commitment_signed_tcls(false); + let tlcs = state.tlc_state.commitment_signed_tlcs(false); let hash = sign_tlcs(tlcs); eprintln!("got hash: {:?}", hash); self.network @@ -254,7 +254,7 @@ impl Actor for TlcActor { .expect("send ok"); // send commitment signed - let tlcs = state.tlc_state.commitment_signed_tcls(false); + let tlcs = state.tlc_state.commitment_signed_tlcs(false); let hash = sign_tlcs(tlcs); eprintln!("got hash: {:?}", hash); self.network @@ -293,7 +293,7 @@ impl Actor for TlcActor { "\nPeer {} processed peer commitment_signed ....", state.peer_id ); - let tlcs = state.tlc_state.commitment_signed_tcls(true); + let tlcs = state.tlc_state.commitment_signed_tlcs(true); let hash = sign_tlcs(tlcs); assert_eq!(hash, peer_hash); @@ -302,7 +302,7 @@ impl Actor for TlcActor { state.tlc_state.update_for_commitment_signed(); eprintln!("sending peer revoke and ack ...."); - let tlcs = state.tlc_state.commitment_signed_tcls(false); + let tlcs = state.tlc_state.commitment_signed_tlcs(false); let hash = sign_tlcs(tlcs); self.network .send_message(NetworkActorMessage::PeerMsg( @@ -314,7 +314,7 @@ impl Actor for TlcActor { // send commitment signed from our side if necessary if state.tlc_state.need_another_commitment_signed() { eprintln!("sending another commitment signed ...."); - let tlcs = state.tlc_state.commitment_signed_tcls(false); + let tlcs = state.tlc_state.commitment_signed_tlcs(false); let hash = sign_tlcs(tlcs); self.network .send_message(NetworkActorMessage::PeerMsg( @@ -326,7 +326,7 @@ impl Actor for TlcActor { } TlcActorMessage::PeerRevokeAndAck(peer_hash) => { eprintln!("Peer {} processed peer revoke and ack ....", state.peer_id); - let tlcs = state.tlc_state.commitment_signed_tcls(true); + let tlcs = state.tlc_state.commitment_signed_tlcs(true); let hash = sign_tlcs(tlcs); assert_eq!(hash, peer_hash); diff --git a/tests/bruno/e2e/reestablish/09-reconnect-peer-NODE1.bru b/tests/bruno/e2e/reestablish/09-reconnect-peer-NODE1.bru index f1d2902fb..23e302e49 100644 --- a/tests/bruno/e2e/reestablish/09-reconnect-peer-NODE1.bru +++ b/tests/bruno/e2e/reestablish/09-reconnect-peer-NODE1.bru @@ -34,5 +34,5 @@ assert { script:post-response { // Dialing a peer is async in tentacle. Sleep for some time to make sure // we're connected to the peer. - await new Promise(r => setTimeout(r, 1000)); + await new Promise(r => setTimeout(r, 3000)); } diff --git a/tests/bruno/e2e/reestablish/12-remove-tlc-from-NODE1.bru b/tests/bruno/e2e/reestablish/10-remove-tlc-from-NODE1.bru similarity index 77% rename from tests/bruno/e2e/reestablish/12-remove-tlc-from-NODE1.bru rename to tests/bruno/e2e/reestablish/10-remove-tlc-from-NODE1.bru index 182ccfe14..a321eff13 100644 --- a/tests/bruno/e2e/reestablish/12-remove-tlc-from-NODE1.bru +++ b/tests/bruno/e2e/reestablish/10-remove-tlc-from-NODE1.bru @@ -1,7 +1,7 @@ meta { name: remove tlc from NODE1 type: http - seq: 12 + seq: 10 } post { @@ -38,6 +38,9 @@ assert { } script:post-response { + // assert we can remove it without error, if we can't remove it means the reestablish failed + // for example, if the tlc is not synced yet, it's status will not be in `Committed`, so we can't remove it + // Sleep for sometime to make sure current operation finishes before next request starts. await new Promise(r => setTimeout(r, 100)); console.log("remove tlc response: ", res.body); diff --git a/tests/bruno/e2e/reestablish/10-shutdown-from-NODE1.bru b/tests/bruno/e2e/reestablish/11-shutdown-from-NODE1.bru similarity index 98% rename from tests/bruno/e2e/reestablish/10-shutdown-from-NODE1.bru rename to tests/bruno/e2e/reestablish/11-shutdown-from-NODE1.bru index 36021e755..6640092a7 100644 --- a/tests/bruno/e2e/reestablish/10-shutdown-from-NODE1.bru +++ b/tests/bruno/e2e/reestablish/11-shutdown-from-NODE1.bru @@ -1,7 +1,7 @@ meta { name: send shutdown from NODE1 type: http - seq: 10 + seq: 11 } post { From 71aca7f4d57f49e4c7698feb548ab34c25c90c9f Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 26 Dec 2024 15:33:57 +0800 Subject: [PATCH 043/119] fix port waiting --- .github/workflows/e2e.yml | 17 +++++++++++++---- tests/deploy/udt-init/src/main.rs | 4 ++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index f9d55b490..a4af3e1c3 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -81,9 +81,9 @@ jobs: if [ -f $port_file ]; then break else + retry_count=$((retry_count + 1)) echo "File $port_file not found. Retrying in 2 seconds..." sleep 2 - retry_count=$((retry_count + 1)) fi done @@ -92,11 +92,15 @@ jobs: ports+=("$line") done < ./tests/nodes/.ports - for i in {1..20}; do + echo "Checking if all ports are open ... ${ports[@]}" + + try_number=60 + count=0 + while [ $count -lt $try_number ]; do all_open=true - for port in $ports; do - echo "Checking port $port" + for port in "${ports[@]}"; do if ! nc -z 127.0.0.1 $port; then + echo "Port $port is not open yet ..." all_open=false break fi @@ -105,10 +109,15 @@ jobs: echo "All ports are open now ..." break else + count=$((count + 1)) echo "Not all ports are open, waiting 3 seconds before retrying" sleep 3 fi done + if [ $count -eq $try_number ]; then + echo "Reached maximum number of tries ($try_number), exiting with status 1" + exit 1 + fi (cd ./tests/bruno; npm exec -- @usebruno/cli@1.20.0 run e2e/${{ matrix.workflow }} -r --env ${{ matrix.test_env }} ${{ matrix.extra_bru_args }} ) & diff --git a/tests/deploy/udt-init/src/main.rs b/tests/deploy/udt-init/src/main.rs index ec3360c6a..8cb133f67 100644 --- a/tests/deploy/udt-init/src/main.rs +++ b/tests/deploy/udt-init/src/main.rs @@ -321,10 +321,10 @@ fn genrate_nodes_config() { .skip(2) // bootnode node was not always started .map(|(_, port)| port.to_string()) .collect::>() - .join("\n"); + .join("\n") + + "\n"; let port_file_path = nodes_dir.join(".ports"); - std::fs::write(port_file_path, content).expect("write ports list"); } From 5bfe111575eb8c58e17a5dfc6dce8d926b68d77a Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 26 Dec 2024 16:20:11 +0800 Subject: [PATCH 044/119] fix ckb invoice status update for payment --- src/fiber/channel.rs | 12 +++++++----- src/fiber/tests/channel.rs | 4 +++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index e9383c676..9264166a8 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -935,9 +935,6 @@ where if invoice_status != CkbInvoiceStatus::Open { return Err(ProcessingChannelError::FinalInvoiceInvalid(invoice_status)); } - self.store - .update_invoice_status(&payment_hash, CkbInvoiceStatus::Received) - .expect("update invoice status failed"); } // if this is the last hop, store the preimage. @@ -953,6 +950,12 @@ where if add_tlc.payment_hash != filled_payment_hash { return Err(ProcessingChannelError::FinalIncorrectPreimage); } + // update invoice status to received only all the error checking passed + if let Some(_invoice) = self.store.get_invoice(&payment_hash) { + self.store + .update_invoice_status(&payment_hash, CkbInvoiceStatus::Received) + .expect("update invoice status failed"); + } state.set_received_tlc_preimage(add_tlc.tlc_id.into(), Some(preimage)); } else { return Err(ProcessingChannelError::FinalIncorrectPaymentHash); @@ -1044,10 +1047,9 @@ where myself: &ActorRef, state: &mut ChannelActorState, tlc_id: TLCId, - reason: RemoveTlcReason, + remove_reason: RemoveTlcReason, ) -> Result<(), ProcessingChannelError> { let channel_id = state.get_id(); - let remove_reason = reason.clone(); let tlc_info = state .remove_tlc_with_reason(tlc_id, &remove_reason) diff --git a/src/fiber/tests/channel.rs b/src/fiber/tests/channel.rs index ac7a92fa0..da47a07ee 100644 --- a/src/fiber/tests/channel.rs +++ b/src/fiber/tests/channel.rs @@ -5375,9 +5375,11 @@ async fn test_send_payment_will_fail_with_no_invoice_preimage() { let new_amount = node_3.get_local_balance_from_channel(channels[2]); assert_eq!(new_amount, old_amount); + + // we should never update the invoice status if there is an error assert_eq!( node_3.get_invoice_status(ckb_invoice.payment_hash()), - Some(CkbInvoiceStatus::Received) + Some(CkbInvoiceStatus::Open) ); } From cb1bd490be737c7233772c8fdfa33f840e76b3fb Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 26 Dec 2024 16:28:03 +0800 Subject: [PATCH 045/119] trivial fix on e2e --- .github/workflows/e2e.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index a4af3e1c3..5da46db0f 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -110,14 +110,15 @@ jobs: break else count=$((count + 1)) + if [ $count -eq $try_number ]; then + echo "Reached maximum number of tries ($try_number), exiting with status 1" + exit 1 + fi echo "Not all ports are open, waiting 3 seconds before retrying" sleep 3 fi done - if [ $count -eq $try_number ]; then - echo "Reached maximum number of tries ($try_number), exiting with status 1" - exit 1 - fi + (cd ./tests/bruno; npm exec -- @usebruno/cli@1.20.0 run e2e/${{ matrix.workflow }} -r --env ${{ matrix.test_env }} ${{ matrix.extra_bru_args }} ) & From 3e9042cfe998670b13b1dc2315d890790c0be8ee Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 26 Dec 2024 16:42:09 +0800 Subject: [PATCH 046/119] disable build on PR action, only run it on main or develop --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 519ebd907..9f82c4ecb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,6 +89,7 @@ jobs: matrix: os: [ubuntu-latest, windows-latest, macos-latest] profile: [dev, release] + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' steps: - uses: actions/checkout@v2 - uses: dsherret/rust-toolchain-file@v1 From 5f1f20badbf2921fd5e863139ea83bba30e242dc Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 26 Dec 2024 21:33:53 +0800 Subject: [PATCH 047/119] update invoice to paid after remove tlc operation applied --- src/fiber/channel.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 9264166a8..089317c9b 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -830,10 +830,8 @@ where error!("invoice already paid, ignore"); } _ => { - eprintln!("update invoice status to paid: {:?}", tlc.payment_hash); - self.store - .update_invoice_status(&tlc.payment_hash, CkbInvoiceStatus::Paid) - .expect("update invoice status error"); + // do nothing + // invoice status will be updated to paid after apply remove tlc operation } } } @@ -1051,9 +1049,14 @@ where ) -> Result<(), ProcessingChannelError> { let channel_id = state.get_id(); - let tlc_info = state - .remove_tlc_with_reason(tlc_id, &remove_reason) - .expect("expect remove tlc successfully"); + let tlc_info = state.remove_tlc_with_reason(tlc_id, &remove_reason)?; + if matches!(remove_reason, RemoveTlcReason::RemoveTlcFulfill(_)) + && self.store.get_invoice(&tlc_info.payment_hash).is_some() + { + self.store + .update_invoice_status(&tlc_info.payment_hash, CkbInvoiceStatus::Paid) + .expect("update invoice status failed"); + } if let ( Some(ref udt_type_script), From 38c2aa02d4862529ffe8c67866b7dee4c0b8bade Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 26 Dec 2024 21:56:04 +0800 Subject: [PATCH 048/119] refactor remove tlc --- src/fiber/channel.rs | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 089317c9b..dcaeae606 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -689,22 +689,19 @@ where state.tlc_state.offered_tlcs.tlcs.iter_mut() }; let settled_tlcs: Vec<_> = pending_tlcs - .filter_map(|tlc| { - if let Some((_removed_at, reason)) = &tlc.removed_at { - if matches!( + .filter(|tlc| { + tlc.removed_at.is_some() + && matches!( tlc.status, TlcStatus::Inbound(InboundTlcStatus::RemoveAckConfirmed) | TlcStatus::Outbound(OutboundTlcStatus::RemoveAckConfirmed) - ) { - return Some((tlc.tlc_id, reason.clone())); - } - } - None + ) }) + .map(|tlc| tlc.tlc_id) .collect(); - for (tlc_id, reason) in settled_tlcs { - self.apply_remove_tlc_operation(myself, state, tlc_id, reason) + for tlc_id in settled_tlcs { + self.apply_remove_tlc_operation(myself, state, tlc_id) .await .expect("expect remove tlc success"); } @@ -716,7 +713,6 @@ where state: &mut ChannelActorState, ) { let apply_tlcs = state.tlc_state.get_committed_received_tlcs(); - eprintln!("flushing {} tlcs", apply_tlcs.len()); for add_tlc in apply_tlcs { if add_tlc.removed_at.is_some() { continue; @@ -760,7 +756,6 @@ where // flush outbound tlcs self.apply_settled_remvoe_tlcs(myself, state, false).await; - eprintln!("end flusing now ..................."); } async fn try_to_relay_remove_tlc( @@ -1045,11 +1040,9 @@ where myself: &ActorRef, state: &mut ChannelActorState, tlc_id: TLCId, - remove_reason: RemoveTlcReason, ) -> Result<(), ProcessingChannelError> { let channel_id = state.get_id(); - - let tlc_info = state.remove_tlc_with_reason(tlc_id, &remove_reason)?; + let (tlc_info, remove_reason) = state.remove_tlc_with_reason(tlc_id)?; if matches!(remove_reason, RemoveTlcReason::RemoveTlcFulfill(_)) && self.store.get_invoice(&tlc_info.payment_hash).is_some() { @@ -1060,15 +1053,14 @@ where if let ( Some(ref udt_type_script), - RemoveTlcReason::RemoveTlcFulfill(RemoveTlcFulfill { payment_preimage }), + RemoveTlcReason::RemoveTlcFulfill(RemoveTlcFulfill { .. }), ) = (state.funding_udt_type_script.clone(), &remove_reason) { - let mut tlc = tlc_info.clone(); - tlc.payment_preimage = Some(*payment_preimage); + assert!(tlc_info.payment_preimage.is_some()); self.subscribers .settled_tlcs_subscribers .send(TlcNotification { - tlc, + tlc: tlc_info.clone(), channel_id, script: udt_type_script.clone(), }); @@ -4306,16 +4298,16 @@ impl ChannelActorState { pub fn remove_tlc_with_reason( &mut self, tlc_id: TLCId, - reason: &RemoveTlcReason, - ) -> Result { - let current = self.tlc_state.get(&tlc_id).expect("TLC exists").clone(); + ) -> Result<(TlcInfo, RemoveTlcReason), ProcessingChannelError> { + let mut current = self.tlc_state.get_mut(&tlc_id).expect("TLC exists").clone(); + let (_remove_at, reason) = current.removed_at.clone().expect("expect remove_at exist"); assert!(matches!( current.status, TlcStatus::Inbound(InboundTlcStatus::RemoveAckConfirmed) | TlcStatus::Outbound(OutboundTlcStatus::RemoveAckConfirmed) )); - if let RemoveTlcReason::RemoveTlcFulfill(fulfill) = reason { + if let RemoveTlcReason::RemoveTlcFulfill(fulfill) = &reason { let filled_payment_hash: Hash256 = current.hash_algorithm.hash(fulfill.payment_preimage).into(); if current.payment_hash != filled_payment_hash { @@ -4339,13 +4331,14 @@ impl ChannelActorState { self.to_local_amount = to_local_amount; self.to_remote_amount = to_remote_amount; + current.payment_preimage = Some(fulfill.payment_preimage); eprintln!("Updated local balance to {} and remote balance to {} by removing tlc {:?} with reason {:?}", to_local_amount, to_remote_amount, tlc_id, reason); } self.tlc_state.apply_remove_tlc(tlc_id); - Ok(current.clone()) + Ok((current.clone(), reason)) } pub fn get_local_channel_public_keys(&self) -> &ChannelBasePublicKeys { From f93989e0b67830529428e3dd132e4a829864e7ff Mon Sep 17 00:00:00 2001 From: quake Date: Fri, 27 Dec 2024 08:29:11 +0900 Subject: [PATCH 049/119] fix: gossip actor and graph should ignore private address --- src/fiber/config.rs | 6 ++++- src/fiber/gossip.rs | 44 +++++++++++++++++++++++++++++++++-- src/fiber/graph.rs | 22 ++++++++++++++++-- src/fiber/network.rs | 1 + src/fiber/tests/gossip.rs | 1 + src/fiber/tests/graph.rs | 2 +- src/fiber/tests/test_utils.rs | 1 + src/main.rs | 1 + 8 files changed, 72 insertions(+), 6 deletions(-) diff --git a/src/fiber/config.rs b/src/fiber/config.rs index a5c7b7222..911f60be1 100644 --- a/src/fiber/config.rs +++ b/src/fiber/config.rs @@ -89,7 +89,7 @@ pub struct FiberConfig { )] pub(crate) announce_listening_addr: Option, - /// whether to announce private address, this should be set to false unless you are running a private network or testing [default: false] + /// whether to announce or process private address, this should be set to false unless you are running a private network or testing [default: false] #[arg( name = "FIBER_ANNOUNCE_PRIVATE_ADDR", long = "fiber-announce-private-addr", @@ -377,6 +377,10 @@ impl FiberConfig { self.announce_listening_addr.unwrap_or(false) } + pub fn announce_private_addr(&self) -> bool { + self.announce_private_addr.unwrap_or(false) + } + pub fn open_channel_auto_accept_min_ckb_funding_amount(&self) -> u64 { self.open_channel_auto_accept_min_ckb_funding_amount .unwrap_or(DEFAULT_OPEN_CHANNEL_AUTO_ACCEPT_MIN_CKB_FUNDING_AMOUNT) diff --git a/src/fiber/gossip.rs b/src/fiber/gossip.rs index 0f9154e5b..1ecf82c18 100644 --- a/src/fiber/gossip.rs +++ b/src/fiber/gossip.rs @@ -24,6 +24,7 @@ use tentacle::{ secio::PeerId, service::{ProtocolHandle, ProtocolMeta, ServiceAsyncControl, SessionType}, traits::ServiceProtocol, + utils::{is_reachable, multiaddr_to_socketaddr}, SessionId, }; use tokio::sync::oneshot; @@ -858,6 +859,7 @@ where { async fn new( maintenance_interval: Duration, + announce_private_addr: bool, store: S, gossip_actor: ActorRef, chain_actor: ActorRef, @@ -871,6 +873,7 @@ where ExtendedGossipMessageStoreActor::new(), ( maintenance_interval, + announce_private_addr, store.clone(), gossip_actor, chain_actor, @@ -982,6 +985,7 @@ pub enum GossipMessageProcessingError { } pub struct ExtendedGossipMessageStoreState { + announce_private_addr: bool, store: S, gossip_actor: ActorRef, chain_actor: ActorRef, @@ -993,11 +997,13 @@ pub struct ExtendedGossipMessageStoreState { impl ExtendedGossipMessageStoreState { fn new( + announce_private_addr: bool, store: S, gossip_actor: ActorRef, chain_actor: ActorRef, ) -> Self { Self { + announce_private_addr, store, gossip_actor, chain_actor, @@ -1096,6 +1102,20 @@ impl ExtendedGossipMessageStoreState { )); } + if !self.announce_private_addr { + if let BroadcastMessageWithTimestamp::NodeAnnouncement(node_announcement) = &message { + if !node_announcement.addresses.iter().any(|addr| { + multiaddr_to_socketaddr(addr) + .map(|socket_addr| is_reachable(socket_addr.ip())) + .unwrap_or_default() + }) { + return Err(GossipMessageProcessingError::ProcessingError( + "private address node announcement".to_string(), + )); + } + } + } + trace!("New gossip message saved to memory: {:?}", message); self.messages_to_be_saved.insert(message.clone()); Ok(message) @@ -1137,6 +1157,7 @@ impl Actor for ExtendedGossipMess type State = ExtendedGossipMessageStoreState; type Arguments = ( Duration, + bool, S, ActorRef, ActorRef, @@ -1145,12 +1166,19 @@ impl Actor for ExtendedGossipMess async fn pre_start( &self, myself: ActorRef, - (gossip_store_maintenance_interval, store, gossip_actor, chain_actor): Self::Arguments, + ( + gossip_store_maintenance_interval, + announce_private_addr, + store, + gossip_actor, + chain_actor, + ): Self::Arguments, ) -> Result { myself.send_interval(gossip_store_maintenance_interval, || { ExtendedGossipMessageStoreMessage::Tick }); Ok(ExtendedGossipMessageStoreState::new( + announce_private_addr, store, gossip_actor, chain_actor, @@ -2123,6 +2151,7 @@ impl GossipProtocolHandle { name: Option, gossip_network_maintenance_interval: Duration, gossip_store_maintenance_interval: Duration, + announce_private_addr: bool, store: S, chain_actor: ActorRef, supervisor: ActorCell, @@ -2141,6 +2170,7 @@ impl GossipProtocolHandle { store_sender, gossip_network_maintenance_interval, gossip_store_maintenance_interval, + announce_private_addr, store, chain_actor, ), @@ -2184,6 +2214,7 @@ where oneshot::Sender>, Duration, Duration, + bool, S, ActorRef, ); @@ -2191,10 +2222,19 @@ where async fn pre_start( &self, myself: ActorRef, - (rx, tx, network_maintenance_interval, store_maintenance_interval, store, chain_actor): Self::Arguments, + ( + rx, + tx, + network_maintenance_interval, + store_maintenance_interval, + announce_private_addr, + store, + chain_actor, + ): Self::Arguments, ) -> Result { let store = ExtendedGossipMessageStore::new( store_maintenance_interval, + announce_private_addr, store, myself.clone(), chain_actor.clone(), diff --git a/src/fiber/graph.rs b/src/fiber/graph.rs index f12fbf77f..02f99ce9a 100644 --- a/src/fiber/graph.rs +++ b/src/fiber/graph.rs @@ -22,6 +22,7 @@ use serde_with::serde_as; use std::collections::{HashMap, HashSet}; use tentacle::multiaddr::MultiAddr; use tentacle::secio::PeerId; +use tentacle::utils::{is_reachable, multiaddr_to_socketaddr}; use thiserror::Error; use tracing::log::error; use tracing::{debug, info, trace}; @@ -218,6 +219,8 @@ pub struct NetworkGraph { // as a NetworkGraphStateStore. store: S, history: PaymentHistory, + // Whether to process announcement of private address + announce_private_addr: bool, } #[derive(Error, Debug)] @@ -246,7 +249,7 @@ where + Sync + 'static, { - pub fn new(store: S, source: Pubkey) -> Self { + pub fn new(store: S, source: Pubkey, announce_private_addr: bool) -> Self { let mut network_graph = Self { source, channels: HashMap::new(), @@ -254,6 +257,7 @@ where latest_cursor: Cursor::default(), store: store.clone(), history: PaymentHistory::new(source, None, store), + announce_private_addr, }; network_graph.load_from_store(); network_graph @@ -438,7 +442,21 @@ where } } - fn process_node_announcement(&mut self, node_announcement: NodeAnnouncement) -> Option { + fn process_node_announcement( + &mut self, + mut node_announcement: NodeAnnouncement, + ) -> Option { + if !self.announce_private_addr { + node_announcement.addresses.retain(|addr| { + multiaddr_to_socketaddr(addr) + .map(|socket_addr| is_reachable(socket_addr.ip())) + .unwrap_or_default() + }); + + if node_announcement.addresses.is_empty() { + return None; + } + } let node_info = NodeInfo::from(node_announcement); match self.nodes.get(&node_info.node_id) { Some(old_node) if old_node.timestamp > node_info.timestamp => { diff --git a/src/fiber/network.rs b/src/fiber/network.rs index 5b8166dad..4c4eb2382 100644 --- a/src/fiber/network.rs +++ b/src/fiber/network.rs @@ -2877,6 +2877,7 @@ where Some(format!("gossip actor {:?}", my_peer_id)), Duration::from_millis(config.gossip_network_maintenance_interval_ms()).into(), Duration::from_millis(config.gossip_store_maintenance_interval_ms()).into(), + config.announce_private_addr(), self.store.clone(), self.chain_actor.clone(), myself.get_cell(), diff --git a/src/fiber/tests/gossip.rs b/src/fiber/tests/gossip.rs index a2f81179f..c3dd3c48f 100644 --- a/src/fiber/tests/gossip.rs +++ b/src/fiber/tests/gossip.rs @@ -70,6 +70,7 @@ impl GossipTestingContext { None, Duration::from_millis(50).into(), Duration::from_millis(50).into(), + true, store.clone(), chain_actor.clone(), root_actor.get_cell(), diff --git a/src/fiber/tests/graph.rs b/src/fiber/tests/graph.rs index cd3e49c5f..e4c4346a4 100644 --- a/src/fiber/tests/graph.rs +++ b/src/fiber/tests/graph.rs @@ -60,7 +60,7 @@ impl MockNetworkGraph { 0, )); } - let graph = NetworkGraph::new(store.clone(), public_key1.into()); + let graph = NetworkGraph::new(store.clone(), public_key1.into(), true); Self { keys: keypairs.into_iter().map(|x| x.1).collect(), diff --git a/src/fiber/tests/test_utils.rs b/src/fiber/tests/test_utils.rs index e012e98ac..c30a86bf2 100644 --- a/src/fiber/tests/test_utils.rs +++ b/src/fiber/tests/test_utils.rs @@ -654,6 +654,7 @@ impl NetworkNode { let network_graph = Arc::new(TokioRwLock::new(NetworkGraph::new( store.clone(), public_key.clone(), + true, ))); let network_actor = Actor::spawn_linked( diff --git a/src/main.rs b/src/main.rs index d7be66dc1..007c89edf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -138,6 +138,7 @@ pub async fn main() -> Result<(), ExitMessage> { let network_graph = Arc::new(RwLock::new(NetworkGraph::new( store.clone(), node_public_key.clone().into(), + fiber_config.announce_private_addr(), ))); let secret_key = ckb_config.read_secret_key().map_err(|err| { From 6ab36a6b6aec0b38a1591569e7c444a50c238c7d Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 27 Dec 2024 11:02:44 +0800 Subject: [PATCH 050/119] fix: use unknown as fallback version Avoid compilation error when git is not available to get the project version. Closes https://github.com/nervosnetwork/fiber/issues/422 Ref: https://docs.rs/git-version/latest/git_version/macro.git_version.html --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 015eeb1f0..38c9ee096 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,7 +29,7 @@ pub mod tasks; use git_version::git_version; -const GIT_VERSION: &str = git_version!(); +const GIT_VERSION: &str = git_version!(fallback = "unknown"); pub fn get_git_versin() -> &'static str { GIT_VERSION From 662d221b321a593c28f77b12d2167058a0c05008 Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 27 Dec 2024 11:05:54 +0800 Subject: [PATCH 051/119] fix typo in crate::get_git_versin --- src/lib.rs | 2 +- src/main.rs | 2 +- src/rpc/info.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 38c9ee096..129db97d5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,7 +31,7 @@ use git_version::git_version; const GIT_VERSION: &str = git_version!(fallback = "unknown"); -pub fn get_git_versin() -> &'static str { +pub fn get_git_version() -> &'static str { GIT_VERSION } diff --git a/src/main.rs b/src/main.rs index d7be66dc1..1a5e49d97 100644 --- a/src/main.rs +++ b/src/main.rs @@ -60,7 +60,7 @@ pub async fn main() -> Result<(), ExitMessage> { .try_init() .map_err(|err| ExitMessage(format!("failed to initialize logger: {}", err)))?; - info!("Starting node with git version {}", fnn::get_git_versin()); + info!("Starting node with git version {}", fnn::get_git_version()); let _span = info_span!("node", node = fnn::get_node_prefix()).entered(); diff --git a/src/rpc/info.rs b/src/rpc/info.rs index ef5648946..9e79533e3 100644 --- a/src/rpc/info.rs +++ b/src/rpc/info.rs @@ -108,7 +108,7 @@ where { async fn node_info(&self) -> Result { let version = env!("CARGO_PKG_VERSION").to_string(); - let commit_hash = crate::get_git_versin().to_string(); + let commit_hash = crate::get_git_version().to_string(); let message = |rpc_reply| NetworkActorMessage::Command(NetworkActorCommand::NodeInfo((), rpc_reply)); From 886ef8f99416d05b924e177c8a18f358f905b2d8 Mon Sep 17 00:00:00 2001 From: yukang Date: Fri, 27 Dec 2024 12:16:21 +0800 Subject: [PATCH 052/119] add randomly send each unit test and fix a bug in payment status --- src/fiber/channel.rs | 15 +++-- src/fiber/network.rs | 7 ++- src/fiber/tests/payment.rs | 122 +++++++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 7 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index dcaeae606..1bf915349 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -664,8 +664,8 @@ where ); // build commitment tx and verify signature from remote, if passed send ACK for partner state.verify_commitment_signed_and_send_ack(commitment_signed, &self.network)?; - let need_commitment_signed = state.tlc_state.update_for_commitment_signed(); + let need_commitment_signed = state.tlc_state.update_for_commitment_signed(); if need_commitment_signed { if !state.tlc_state.waiting_ack { self.handle_commitment_signed_command(state)?; @@ -712,12 +712,14 @@ where myself: &ActorRef, state: &mut ChannelActorState, ) { - let apply_tlcs = state.tlc_state.get_committed_received_tlcs(); - for add_tlc in apply_tlcs { - if add_tlc.removed_at.is_some() { - continue; - } + let apply_tlcs: Vec = state + .tlc_state + .get_committed_received_tlcs() + .into_iter() + .filter(|tlc| tlc.removed_at.is_none()) + .collect(); + for add_tlc in apply_tlcs { assert!(add_tlc.is_received()); if let Err(e) = self.apply_add_tlc_operation(myself, state, &add_tlc).await { let tlc_err = match e.source { @@ -2122,6 +2124,7 @@ where } } ChannelActorMessage::Command(command) => { + eprintln!("channel actor command: {:?}", command); if let Err(err) = self.handle_command(state, command).await { error!("Error while processing channel command: {:?}", err); } diff --git a/src/fiber/network.rs b/src/fiber/network.rs index 5b8166dad..0915e6a76 100644 --- a/src/fiber/network.rs +++ b/src/fiber/network.rs @@ -1602,7 +1602,11 @@ where "Failed to send onion packet with error {}", error_detail.error_code_as_str() ); - self.set_payment_fail_with_error(payment_session, &err); + if !need_to_retry { + // only update the payment session status when we don't need to retry + // otherwise the endpoint user may get confused in the internal state changes + self.set_payment_fail_with_error(payment_session, &err); + } return Err(Error::SendPaymentFirstHopError(err, need_to_retry)); } Ok(tlc_id) => { @@ -1628,6 +1632,7 @@ where let Some(mut payment_session) = self.store.get_payment_session(payment_hash) else { return Err(Error::InvalidParameter(payment_hash.to_string())); }; + assert!(payment_session.status != PaymentSessionStatus::Failed); let payment_data = payment_session.request.clone(); if payment_session.can_retry() { diff --git a/src/fiber/tests/payment.rs b/src/fiber/tests/payment.rs index f43fd95b5..0766983de 100644 --- a/src/fiber/tests/payment.rs +++ b/src/fiber/tests/payment.rs @@ -251,3 +251,125 @@ async fn test_send_payment_for_pay_self() { eprintln!("res: {:?}", res); assert_eq!(res.unwrap().fee, 0); } + +#[tokio::test] +async fn test_network_send_payment_randomly_send_each_other() { + init_tracing(); + + let _span = tracing::info_span!("node", node = "test").entered(); + let node_a_funding_amount = 100000000000; + let node_b_funding_amount = 100000000000; + + 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(1000)).await; + let node_a_old_balance = node_a.get_local_balance_from_channel(new_channel_id); + let node_b_old_balance = node_b.get_local_balance_from_channel(new_channel_id); + + let node_a_pubkey = node_a.pubkey.clone(); + let node_b_pubkey = node_b.pubkey.clone(); + + let mut node_a_sent = 0; + let mut node_b_sent = 0; + let mut all_sent = vec![]; + for _i in 1..8 { + let rand_wait_time = rand::random::() % 1000; + tokio::time::sleep(tokio::time::Duration::from_millis(rand_wait_time)).await; + + let rand_num = rand::random::() % 2; + let amount = rand::random::() % 10000; + eprintln!("generated ampunt: {}", amount); + let (source, target) = if rand_num == 0 { + (&node_a.network_actor, node_b_pubkey.clone()) + } else { + (&node_b.network_actor, node_a_pubkey.clone()) + }; + let message = |rpc_reply| -> NetworkActorMessage { + NetworkActorMessage::Command(NetworkActorCommand::SendPayment( + SendPaymentCommand { + target_pubkey: Some(target), + amount: Some(amount), + payment_hash: None, + final_tlc_expiry_delta: None, + tlc_expiry_limit: 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!(source, message).expect("node_a alive").unwrap(); + + if rand_num == 0 { + all_sent.push((true, amount, res.payment_hash, res.status)); + } else { + all_sent.push((false, amount, res.payment_hash, res.status)); + } + } + + tokio::time::sleep(tokio::time::Duration::from_millis(4000)).await; + for (a_sent, amount, payment_hash, create_status) in all_sent { + let message = |rpc_reply| -> NetworkActorMessage { + NetworkActorMessage::Command(NetworkActorCommand::GetPayment(payment_hash, rpc_reply)) + }; + let network = if a_sent { + &node_a.network_actor + } else { + &node_b.network_actor + }; + let res = call!(network, message).expect("node_a alive").unwrap(); + if res.status == PaymentSessionStatus::Success { + assert!(matches!( + create_status, + PaymentSessionStatus::Created | PaymentSessionStatus::Inflight + )); + eprintln!( + "{} payment_hash: {:?} success with amount: {} create_status: {:?}", + if a_sent { "a -> b" } else { "b -> a" }, + payment_hash, + amount, + create_status + ); + if a_sent { + node_a_sent += amount; + } else { + node_b_sent += amount; + } + } + } + + eprintln!( + "node_a_old_balance: {}, node_b_old_balance: {}", + node_a_old_balance, node_b_old_balance + ); + eprintln!("node_a_sent: {}, node_b_sent: {}", node_a_sent, node_b_sent); + let new_node_a_balance = node_a.get_local_balance_from_channel(new_channel_id); + let new_node_b_balance = node_b.get_local_balance_from_channel(new_channel_id); + + eprintln!( + "new_node_a_balance: {}, new_node_b_balance: {}", + new_node_a_balance, new_node_b_balance + ); + + assert_eq!( + node_a_old_balance + node_b_old_balance, + new_node_a_balance + new_node_b_balance + ); + assert_eq!( + new_node_a_balance, + node_a_old_balance - node_a_sent + node_b_sent + ); + assert_eq!( + new_node_b_balance, + node_b_old_balance - node_b_sent + node_a_sent + ); +} From 916c3a9a2f23247d18e4e92ebfc52ef7ce3de69b Mon Sep 17 00:00:00 2001 From: yukang Date: Fri, 27 Dec 2024 12:58:59 +0800 Subject: [PATCH 053/119] cleanup logs --- src/fiber/channel.rs | 51 ++++---------------------------------------- 1 file changed, 4 insertions(+), 47 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 1bf915349..7958e1f45 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -658,10 +658,6 @@ where state: &mut ChannelActorState, commitment_signed: CommitmentSigned, ) -> Result<(), ProcessingChannelError> { - eprintln!( - "handle_commitment_signed_peer_message .... : {}", - state.tlc_state.waiting_ack - ); // build commitment tx and verify signature from remote, if passed send ACK for partner state.verify_commitment_signed_and_send_ack(commitment_signed, &self.network)?; @@ -833,10 +829,6 @@ where } } - eprintln!( - "settle down tlc: {:?} with reason: {:?}", - tlc_id, remove_reason - ); self.register_retryable_tlc_remove(myself, state, tlc.tlc_id, remove_reason) .await; } @@ -1121,7 +1113,6 @@ where &self, state: &mut ChannelActorState, ) -> ProcessingChannelResult { - eprintln!("begin to handle commitment_signed command ..."); let flags = match state.state { ChannelState::CollaboratingFundingTx(flags) if !flags.contains(CollaboratingFundingTxFlags::COLLABRATION_COMPLETED) => @@ -1171,7 +1162,7 @@ where commitment_tx_partial_signature, next_local_nonce: state.get_next_local_nonce(), }; - eprintln!("sending commitment_signed ..."); + self.network .send_message(NetworkActorMessage::new_command( NetworkActorCommand::SendFiberMessage(FiberMessageWithPeerId::new( @@ -1239,7 +1230,6 @@ where state: &mut ChannelActorState, command: RemoveTlcCommand, ) -> ProcessingChannelResult { - eprintln!("removing tlc: {:?}", command); state.check_for_tlc_update(None, true, false)?; state.check_remove_tlc_with_reason(TLCId::Received(command.id), &command.reason)?; state.tlc_state.set_received_tlc_removed( @@ -2124,7 +2114,6 @@ where } } ChannelActorMessage::Command(command) => { - eprintln!("channel actor command: {:?}", command); if let Err(err) = self.handle_command(state, command).await { error!("Error while processing channel command: {:?}", err); } @@ -2460,7 +2449,6 @@ impl TlcState { } pub fn set_waiting_ack(&mut self, waiting_ack: bool) { - eprintln!("now seting waiting ack to {:?}", waiting_ack); self.waiting_ack = waiting_ack; } @@ -4058,7 +4046,7 @@ impl ChannelActorState { ] .concat(), ); - eprintln!("ack send message: {:?}", message); + sign_ctx .clone() .sign(message.as_slice()) @@ -4253,10 +4241,6 @@ impl ChannelActorState { self.get_next_received_tlc_id() }; if tlc.tlc_id != next_tlc_id { - eprintln!( - "Received tlc id {:?} is not the expected next id {:?}", - tlc.tlc_id, next_tlc_id - ); return Err(ProcessingChannelError::InvalidParameter(format!( "Received tlc id {:?} is not the expected next id {:?}", tlc.tlc_id, next_tlc_id @@ -4327,16 +4311,12 @@ impl ChannelActorState { to_local_amount += current.amount; to_remote_amount -= current.amount; } - eprintln!( - "before local balance: {}, remote balance: {}, current tlc amount: {:?}", - self.to_local_amount, self.to_remote_amount, current.amount - ); self.to_local_amount = to_local_amount; self.to_remote_amount = to_remote_amount; current.payment_preimage = Some(fulfill.payment_preimage); - eprintln!("Updated local balance to {} and remote balance to {} by removing tlc {:?} with reason {:?}", + debug!("Updated local balance to {} and remote balance to {} by removing tlc {:?} with reason {:?}", to_local_amount, to_remote_amount, tlc_id, reason); } self.tlc_state.apply_remove_tlc(tlc_id); @@ -5519,10 +5499,6 @@ impl ChannelActorState { network: &ActorRef, revoke_and_ack: RevokeAndAck, ) -> Result { - eprintln!( - "begin to handle revoke and ack peer message: {:?}", - &revoke_and_ack - ); self.tlc_state.debug(); let RevokeAndAck { channel_id: _, @@ -5540,8 +5516,6 @@ impl ChannelActorState { let (verify_ctx, sign_ctx) = { let local_nonce = self.get_local_nonce(); let remote_nonce = self.take_remote_nonce_for_raa(); - eprintln!("local_nonce: {:?}", local_nonce); - eprintln!("remote_nonce: {:?}", remote_nonce); let nonces = [remote_nonce.clone(), local_nonce]; let agg_nonce = AggNonce::sum(nonces); @@ -5589,7 +5563,6 @@ impl ChannelActorState { }; let commitment_number = self.get_local_commitment_number() - 1; - eprintln!("recv first commitment_number: {}", commitment_number); let commitment_lock_script_args = [ &blake2b_256(x_only_aggregated_pubkey)[0..20], self.get_delay_epoch_as_lock_args_bytes().as_slice(), @@ -5597,10 +5570,6 @@ impl ChannelActorState { ] .concat(); - eprintln!( - "recv first commitment_lock_script_args: {:?}", - commitment_lock_script_args - ); let message = blake2b_256( [ output.as_slice(), @@ -5610,9 +5579,7 @@ impl ChannelActorState { .concat(), ); - eprintln!("first begin to verify message: {:?}", message); verify_ctx.verify(revocation_partial_signature, message.as_slice())?; - eprintln!("first successfully verify message ============="); let our_signature = sign_ctx.clone().sign(message.as_slice())?; let aggregated_signature = verify_ctx.aggregate_partial_signatures_for_msg( @@ -5649,9 +5616,7 @@ impl ChannelActorState { ] .concat(), ); - eprintln!("begin to verify message: {:?}", message); verify_ctx.verify(commitment_tx_partial_signature, message.as_slice())?; - eprintln!("successfully verify message ============="); let our_signature = sign_ctx.sign(message.as_slice())?; let aggregated_signature = verify_ctx.aggregate_partial_signatures_for_msg( [commitment_tx_partial_signature, our_signature], @@ -5671,7 +5636,6 @@ impl ChannelActorState { self.increment_local_commitment_number(); self.append_remote_commitment_point(next_per_commitment_point); - eprintln!("handle revoke and ack peer message: {:?}", &revocation_data); let need_commitment_signed = self.tlc_state.update_for_revoke_and_ack(); network .send_message(NetworkActorMessage::new_notification( @@ -6319,16 +6283,9 @@ impl ChannelActorState { let offered_fulfilled = self.get_pending_fulfilled_tlcs_amount(for_remote, true); let received_fulfilled = self.get_pending_fulfilled_tlcs_amount(for_remote, false); - eprintln!( - "self.to_local_amount: {}, self.to_remote_amount: {}, offered_fulfilled: {}, received_fulfilled: {}, for_remote: {}", - self.to_local_amount, self.to_remote_amount, offered_fulfilled, received_fulfilled, for_remote - ); let to_local_value = self.to_local_amount - offered_fulfilled + received_fulfilled; let to_remote_value = self.to_remote_amount - received_fulfilled + offered_fulfilled; - eprintln!( - "debug now to_local_value: {}, to_remote_value: {}", - to_local_value, to_remote_value - ); + let commitment_tx_fee = calculate_commitment_tx_fee(self.commitment_fee_rate, &self.funding_udt_type_script); From 6abc209a06f4ffc507e57aee287e0afd927bf3dc Mon Sep 17 00:00:00 2001 From: yukang Date: Fri, 27 Dec 2024 13:04:42 +0800 Subject: [PATCH 054/119] add comments --- src/fiber/channel.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 7958e1f45..9d66ad504 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -2212,21 +2212,35 @@ impl TLCId { #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub enum OutboundTlcStatus { + // Offered tlc created and sent to remote party LocalAnnounced, + // Received ACK from remote party for this offered tlc Committed, + // Remote party removed this tlc RemoteRemoved, + // We received another RemoveTlc message from peer when we are waiting for the ack of the last one. + // So we need another ACK to confirm the removal. RemoveWaitPrevAck, + // We have sent commitment signed to peer and waiting ACK for confirming this RemoveTlc RemoveWaitAck, + // We have received the ACK for the RemoveTlc, it's safe to remove this tlc RemoveAckConfirmed, } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub enum InboundTlcStatus { + // Received tlc from remote party, but not committed yet RemoteAnnounced, + // We received another AddTlc peer message when we are waiting for the ack of the last one. + // So we need another ACK to confirm the addition. AnnounceWaitPrevAck, + // We have sent commitment signed to peer and waiting ACK for confirming this AddTlc AnnounceWaitAck, + // We have received ACK from peer and Committed this tlc Committed, + // We have removed this tlc, but haven't received ACK from peer LocalRemoved, + // We have received the ACK for the RemoveTlc, it's safe to remove this tlc RemoveAckConfirmed, } From 34af9373de707fa8e9a06b8a1673dca24a98c075 Mon Sep 17 00:00:00 2001 From: yukang Date: Fri, 27 Dec 2024 13:12:36 +0800 Subject: [PATCH 055/119] cleanup --- src/fiber/tests/channel.rs | 215 ------------------------------------- 1 file changed, 215 deletions(-) diff --git a/src/fiber/tests/channel.rs b/src/fiber/tests/channel.rs index da47a07ee..eb355372e 100644 --- a/src/fiber/tests/channel.rs +++ b/src/fiber/tests/channel.rs @@ -60,221 +60,6 @@ fn test_derive_private_and_public_tlc_keys() { assert_eq!(derived_privkey.pubkey(), derived_pubkey); } -// #[test] -// fn test_pending_tlcs() { -// let mut tlc_state = TlcState::default(); -// let add_tlc1 = AddTlcInfo { -// amount: 10000, -// status: TlcStatus::Outbound(OutboundTlcStatus::LocalAnnounced), - -// channel_id: gen_rand_sha256_hash(), -// payment_hash: gen_rand_sha256_hash(), -// expiry: now_timestamp_as_millis_u64() + 1000, -// hash_algorithm: HashAlgorithm::Sha256, -// onion_packet: None, -// shared_secret: NO_SHARED_SECRET.clone(), -// tlc_id: TLCId::Offered(0), -// created_at: CommitmentNumbers::default(), -// removed_at: None, -// payment_preimage: None, -// previous_tlc: None, -// }; -// let add_tlc2 = AddTlcInfo { -// amount: 20000, -// status: TlcStatus::Outbound(OutboundTlcStatus::LocalAnnounced), - -// channel_id: gen_rand_sha256_hash(), -// payment_hash: gen_rand_sha256_hash(), -// expiry: now_timestamp_as_millis_u64() + 2000, -// hash_algorithm: HashAlgorithm::Sha256, -// onion_packet: None, -// shared_secret: NO_SHARED_SECRET.clone(), -// tlc_id: TLCId::Offered(1), -// created_at: CommitmentNumbers::default(), -// removed_at: None, -// payment_preimage: None, -// previous_tlc: None, -// }; -// tlc_state.add_local_tlc(TlcKind::AddTlc(add_tlc1.clone())); -// tlc_state.add_local_tlc(TlcKind::AddTlc(add_tlc2.clone())); - -// let mut tlc_state_2 = TlcState::default(); -// tlc_state_2.add_remote_tlc(TlcKind::AddTlc(add_tlc1.clone())); -// tlc_state_2.add_remote_tlc(TlcKind::AddTlc(add_tlc2.clone())); - -// let tx1 = tlc_state.get_tlcs_for_local(); -// let tx2 = tlc_state_2.get_tlcs_for_remote(); - -// assert_eq!(tx1, tx2); - -// let tlcs = tlc_state.commit_local_tlcs(); -// assert_eq!(tlcs.len(), 2); - -// let tlcs2 = tlc_state_2.commit_remote_tlcs(); -// assert_eq!(tlcs2.len(), 2); - -// assert_eq!(tx1, tx2); - -// let tlcs = tlc_state.commit_local_tlcs(); -// assert_eq!(tlcs.len(), 0); - -// let tlcs2 = tlc_state_2.commit_remote_tlcs(); -// assert_eq!(tlcs2.len(), 0); - -// tlc_state_2.add_local_tlc(TlcKind::AddTlc(add_tlc1.clone())); -// tlc_state_2.add_local_tlc(TlcKind::AddTlc(add_tlc2.clone())); - -// tlc_state.add_remote_tlc(TlcKind::AddTlc(add_tlc1.clone())); -// tlc_state.add_remote_tlc(TlcKind::AddTlc(add_tlc2.clone())); - -// let tx1 = tlc_state.get_tlcs_for_remote(); -// let tx2 = tlc_state_2.get_tlcs_for_local(); - -// assert_eq!(tx1, tx2); - -// let tlcs = tlc_state.commit_remote_tlcs(); -// assert_eq!(tlcs.len(), 2); -// let tlcs2 = tlc_state_2.commit_local_tlcs(); -// assert_eq!(tlcs2.len(), 2); - -// assert_eq!(tx1, tx2); - -// let tlcs = tlc_state.commit_remote_tlcs(); -// assert_eq!(tlcs.len(), 0); -// let tlcs2 = tlc_state_2.commit_local_tlcs(); -// assert_eq!(tlcs2.len(), 0); -// } - -// #[test] -// fn test_pending_tlcs_duplicated_tlcs() { -// let mut tlc_state = TlcState::default(); -// let add_tlc1 = AddTlcInfo { -// amount: 10000, -// status: TlcStatus::Outbound(OutboundTlcStatus::LocalAnnounced), -// channel_id: gen_rand_sha256_hash(), -// payment_hash: gen_rand_sha256_hash(), -// expiry: now_timestamp_as_millis_u64() + 1000, -// hash_algorithm: HashAlgorithm::Sha256, -// onion_packet: None, -// shared_secret: NO_SHARED_SECRET.clone(), -// tlc_id: TLCId::Offered(0), -// created_at: CommitmentNumbers::default(), -// removed_at: None, -// payment_preimage: None, -// previous_tlc: None, -// }; -// tlc_state.add_local_tlc(TlcKind::AddTlc(add_tlc1.clone())); - -// let mut tlc_state_2 = TlcState::default(); -// tlc_state_2.add_remote_tlc(TlcKind::AddTlc(add_tlc1.clone())); - -// let tx1 = tlc_state.get_tlcs_for_local(); -// let tx2 = tlc_state_2.get_tlcs_for_remote(); - -// assert_eq!(tx1, tx2); - -// let tlcs = tlc_state.commit_local_tlcs(); -// assert_eq!(tlcs.len(), 1); - -// let tlcs2 = tlc_state_2.commit_remote_tlcs(); -// assert_eq!(tlcs2.len(), 1); - -// assert_eq!(tx1, tx2); - -// let tlcs = tlc_state.commit_local_tlcs(); -// assert_eq!(tlcs.len(), 0); - -// let tlcs2 = tlc_state_2.commit_remote_tlcs(); -// assert_eq!(tlcs2.len(), 0); - -// let add_tlc2 = AddTlcInfo { -// amount: 20000, -// status: TlcStatus::Outbound(OutboundTlcStatus::LocalAnnounced), -// channel_id: gen_rand_sha256_hash(), -// payment_hash: gen_rand_sha256_hash(), -// expiry: now_timestamp_as_millis_u64() + 2000, -// hash_algorithm: HashAlgorithm::Sha256, -// onion_packet: None, -// shared_secret: NO_SHARED_SECRET.clone(), -// tlc_id: TLCId::Offered(1), -// created_at: CommitmentNumbers::default(), -// removed_at: None, -// payment_preimage: None, -// previous_tlc: None, -// }; - -// tlc_state_2.add_local_tlc(TlcKind::AddTlc(add_tlc2.clone())); -// tlc_state.add_remote_tlc(TlcKind::AddTlc(add_tlc2.clone())); - -// let tx1 = tlc_state.get_tlcs_for_remote(); -// let tx2 = tlc_state_2.get_tlcs_for_local(); - -// assert_eq!(tx1, tx2); - -// let tlcs = tlc_state.commit_remote_tlcs(); -// assert_eq!(tlcs.len(), 1); -// let tlcs2 = tlc_state_2.commit_local_tlcs(); -// assert_eq!(tlcs2.len(), 1); -// assert_eq!(tx1, tx2); - -// let tlcs = tlc_state.commit_remote_tlcs(); -// assert_eq!(tlcs.len(), 0); -// let tlcs2 = tlc_state_2.commit_local_tlcs(); -// assert_eq!(tlcs2.len(), 0); - -// let committed_tlcs1 = tlc_state.all_commited_tlcs().collect::>(); -// let committed_tlcs2 = tlc_state_2.all_commited_tlcs().collect::>(); -// assert_eq!(committed_tlcs1, committed_tlcs2); -// } - -// #[test] -// fn test_pending_tlcs_with_remove_tlc() { -// let mut tlc_state = TlcState::default(); -// let add_tlc1 = TlcInfo { -// amount: 10000, -// status: TlcStatus::Outbound(OutboundTlcStatus::LocalAnnounced), -// channel_id: gen_rand_sha256_hash(), -// payment_hash: gen_rand_sha256_hash(), -// expiry: now_timestamp_as_millis_u64() + 1000, -// hash_algorithm: HashAlgorithm::Sha256, -// onion_packet: None, -// shared_secret: NO_SHARED_SECRET.clone(), -// tlc_id: TLCId::Offered(0), -// created_at: CommitmentNumbers::default(), -// removed_at: None, -// payment_preimage: None, -// previous_tlc: None, -// }; -// let add_tlc2 = AddTlcInfo { -// amount: 20000, -// status: TlcStatus::Outbound(OutboundTlcStatus::LocalAnnounced), -// channel_id: gen_rand_sha256_hash(), -// payment_hash: gen_rand_sha256_hash(), -// expiry: now_timestamp_as_millis_u64() + 2000, -// hash_algorithm: HashAlgorithm::Sha256, -// onion_packet: None, -// shared_secret: NO_SHARED_SECRET.clone(), -// tlc_id: TLCId::Offered(1), -// created_at: CommitmentNumbers::default(), -// removed_at: None, -// payment_preimage: None, -// previous_tlc: None, -// }; -// let remote_tlc = RemoveTlcInfo { -// channel_id: gen_rand_sha256_hash(), -// tlc_id: TLCId::Offered(0), -// reason: RemoveTlcReason::RemoveTlcFulfill(RemoveTlcFulfill { -// payment_preimage: gen_rand_sha256_hash(), -// }), -// }; - -// tlc_state.add_offered_tlc(add_tlc1.clone()); -// tlc_state.add_offered_tlc(add_tlc2.clone()); - -// let all_tlcs: Vec<&AddTlcInfo> = tlc_state.all_commited_tlcs().collect(); -// assert_eq!(all_tlcs.len(), 2); -// } - #[tokio::test] async fn test_open_channel_to_peer() { let [node_a, mut node_b] = NetworkNode::new_n_interconnected_nodes().await; From 659a41c464408b34b81eeba54c974712abfbc9cb Mon Sep 17 00:00:00 2001 From: yukang Date: Fri, 27 Dec 2024 13:19:41 +0800 Subject: [PATCH 056/119] fix document for p2p --- docs/specs/p2p-message.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/docs/specs/p2p-message.md b/docs/specs/p2p-message.md index 33f1e43e9..acaceb4b8 100644 --- a/docs/specs/p2p-message.md +++ b/docs/specs/p2p-message.md @@ -287,11 +287,19 @@ After establishing a channel, nodes can perform payment operations by sending Ad | | | | | |--(2)---- commitment_signed -->| | | |<-(3)---- revoke_and_ack -----| | + | | | | + | |<-(4)---- commitment_signed ---| | + | |--(5)---- revoke_and_ack ---->| | + | | | | | A | | B | - | |<-(4)---- remove_tlc -----| | | | | | - | |<-(5)---- commitment_signed ---| | - | |--(6)---- revoke_and_ack ---->| | + | |<-(6)---- remove_tlc -----| | + | | | | + | |<-(7)---- commitment_signed ---| | + | |--(8)---- revoke_and_ack ---->| | + | | | | + | |--(9)---- commitment_signed -->| | + | |<-(10)---- revoke_and_ack ----| | +-------+ +-------+ ``` From d28e28862531df4601074a1d36fac8b0fcbe081d Mon Sep 17 00:00:00 2001 From: yukang Date: Fri, 27 Dec 2024 18:49:33 +0800 Subject: [PATCH 057/119] cleanup --- src/fiber/channel.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 9d66ad504..e6d145df6 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -4366,18 +4366,6 @@ impl ChannelActorState { .expect("Funding transaction outpoint is present") } - pub fn get_funding_transaction_block_number(&self) -> BlockNumber { - self.funding_tx_confirmed_at - .expect("funding tx confirmed_at is present") - .0 - } - - pub fn get_funding_transaction_index(&self) -> u32 { - self.funding_tx_confirmed_at - .expect("funding tx confirmed_at is present") - .1 - } - pub fn get_local_shutdown_script(&self) -> Script { self.local_shutdown_script.clone() } From eb5dcc41d50f5cd7209e225207dbd781303faa24 Mon Sep 17 00:00:00 2001 From: yukang Date: Fri, 27 Dec 2024 20:14:19 +0800 Subject: [PATCH 058/119] remove json des --- src/fiber/gen/gossip.rs | 1435 +++++++++++++++++++++++++++++++++++ src/fiber/schema/gossip.mol | 24 +- src/fiber/types.rs | 76 +- 3 files changed, 1526 insertions(+), 9 deletions(-) diff --git a/src/fiber/gen/gossip.rs b/src/fiber/gen/gossip.rs index 26a503603..b4d849c66 100644 --- a/src/fiber/gen/gossip.rs +++ b/src/fiber/gen/gossip.rs @@ -7500,3 +7500,1438 @@ impl From for GossipMessage { Self::new_builder().set(value).build() } } +#[derive(Clone)] +pub struct ChannelUpdateOpt(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for ChannelUpdateOpt { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for ChannelUpdateOpt { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for ChannelUpdateOpt { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(v) = self.to_opt() { + write!(f, "{}(Some({}))", Self::NAME, v) + } else { + write!(f, "{}(None)", Self::NAME) + } + } +} +impl ::core::default::Default for ChannelUpdateOpt { + fn default() -> Self { + let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); + ChannelUpdateOpt::new_unchecked(v) + } +} +impl ChannelUpdateOpt { + const DEFAULT_VALUE: [u8; 0] = []; + pub fn is_none(&self) -> bool { + self.0.is_empty() + } + pub fn is_some(&self) -> bool { + !self.0.is_empty() + } + pub fn to_opt(&self) -> Option { + if self.is_none() { + None + } else { + Some(ChannelUpdate::new_unchecked(self.0.clone())) + } + } + pub fn as_reader<'r>(&'r self) -> ChannelUpdateOptReader<'r> { + ChannelUpdateOptReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for ChannelUpdateOpt { + type Builder = ChannelUpdateOptBuilder; + const NAME: &'static str = "ChannelUpdateOpt"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + ChannelUpdateOpt(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ChannelUpdateOptReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ChannelUpdateOptReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().set(self.to_opt()) + } +} +#[derive(Clone, Copy)] +pub struct ChannelUpdateOptReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for ChannelUpdateOptReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for ChannelUpdateOptReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for ChannelUpdateOptReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(v) = self.to_opt() { + write!(f, "{}(Some({}))", Self::NAME, v) + } else { + write!(f, "{}(None)", Self::NAME) + } + } +} +impl<'r> ChannelUpdateOptReader<'r> { + pub fn is_none(&self) -> bool { + self.0.is_empty() + } + pub fn is_some(&self) -> bool { + !self.0.is_empty() + } + pub fn to_opt(&self) -> Option> { + if self.is_none() { + None + } else { + Some(ChannelUpdateReader::new_unchecked(self.as_slice())) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for ChannelUpdateOptReader<'r> { + type Entity = ChannelUpdateOpt; + const NAME: &'static str = "ChannelUpdateOptReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + ChannelUpdateOptReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + if !slice.is_empty() { + ChannelUpdateReader::verify(&slice[..], compatible)?; + } + Ok(()) + } +} +#[derive(Clone, Debug, Default)] +pub struct ChannelUpdateOptBuilder(pub(crate) Option); +impl ChannelUpdateOptBuilder { + pub fn set(mut self, v: Option) -> Self { + self.0 = v; + self + } +} +impl molecule::prelude::Builder for ChannelUpdateOptBuilder { + type Entity = ChannelUpdateOpt; + const NAME: &'static str = "ChannelUpdateOptBuilder"; + fn expected_length(&self) -> usize { + self.0 + .as_ref() + .map(|ref inner| inner.as_slice().len()) + .unwrap_or(0) + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + self.0 + .as_ref() + .map(|ref inner| writer.write_all(inner.as_slice())) + .unwrap_or(Ok(())) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + ChannelUpdateOpt::new_unchecked(inner.into()) + } +} +impl From for ChannelUpdateOpt { + fn from(value: ChannelUpdate) -> Self { + Self::new_builder().set(Some(value)).build() + } +} +#[derive(Clone)] +pub struct ChannelFailed(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for ChannelFailed { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for ChannelFailed { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for ChannelFailed { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "channel_outpoint", self.channel_outpoint())?; + write!(f, ", {}: {}", "channel_update", self.channel_update())?; + write!(f, ", {}: {}", "node_id", self.node_id())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl ::core::default::Default for ChannelFailed { + fn default() -> Self { + let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); + ChannelFailed::new_unchecked(v) + } +} +impl ChannelFailed { + const DEFAULT_VALUE: [u8; 85] = [ + 85, 0, 0, 0, 16, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + pub const FIELD_COUNT: usize = 3; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn channel_outpoint(&self) -> OutPoint { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + OutPoint::new_unchecked(self.0.slice(start..end)) + } + pub fn channel_update(&self) -> ChannelUpdateOpt { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + let end = molecule::unpack_number(&slice[12..]) as usize; + ChannelUpdateOpt::new_unchecked(self.0.slice(start..end)) + } + pub fn node_id(&self) -> Pubkey { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[12..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[16..]) as usize; + Pubkey::new_unchecked(self.0.slice(start..end)) + } else { + Pubkey::new_unchecked(self.0.slice(start..)) + } + } + pub fn as_reader<'r>(&'r self) -> ChannelFailedReader<'r> { + ChannelFailedReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for ChannelFailed { + type Builder = ChannelFailedBuilder; + const NAME: &'static str = "ChannelFailed"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + ChannelFailed(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ChannelFailedReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ChannelFailedReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder() + .channel_outpoint(self.channel_outpoint()) + .channel_update(self.channel_update()) + .node_id(self.node_id()) + } +} +#[derive(Clone, Copy)] +pub struct ChannelFailedReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for ChannelFailedReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for ChannelFailedReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for ChannelFailedReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "channel_outpoint", self.channel_outpoint())?; + write!(f, ", {}: {}", "channel_update", self.channel_update())?; + write!(f, ", {}: {}", "node_id", self.node_id())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl<'r> ChannelFailedReader<'r> { + pub const FIELD_COUNT: usize = 3; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn channel_outpoint(&self) -> OutPointReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + OutPointReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn channel_update(&self) -> ChannelUpdateOptReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + let end = molecule::unpack_number(&slice[12..]) as usize; + ChannelUpdateOptReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn node_id(&self) -> PubkeyReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[12..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[16..]) as usize; + PubkeyReader::new_unchecked(&self.as_slice()[start..end]) + } else { + PubkeyReader::new_unchecked(&self.as_slice()[start..]) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for ChannelFailedReader<'r> { + type Entity = ChannelFailed; + const NAME: &'static str = "ChannelFailedReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + ChannelFailedReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len < molecule::NUMBER_SIZE { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); + } + let total_size = molecule::unpack_number(slice) as usize; + if slice_len != total_size { + return ve!(Self, TotalSizeNotMatch, total_size, slice_len); + } + if slice_len < molecule::NUMBER_SIZE * 2 { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE * 2, slice_len); + } + let offset_first = molecule::unpack_number(&slice[molecule::NUMBER_SIZE..]) as usize; + if offset_first % molecule::NUMBER_SIZE != 0 || offset_first < molecule::NUMBER_SIZE * 2 { + return ve!(Self, OffsetsNotMatch); + } + if slice_len < offset_first { + return ve!(Self, HeaderIsBroken, offset_first, slice_len); + } + let field_count = offset_first / molecule::NUMBER_SIZE - 1; + if field_count < Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + } else if !compatible && field_count > Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + }; + let mut offsets: Vec = slice[molecule::NUMBER_SIZE..offset_first] + .chunks_exact(molecule::NUMBER_SIZE) + .map(|x| molecule::unpack_number(x) as usize) + .collect(); + offsets.push(total_size); + if offsets.windows(2).any(|i| i[0] > i[1]) { + return ve!(Self, OffsetsNotMatch); + } + OutPointReader::verify(&slice[offsets[0]..offsets[1]], compatible)?; + ChannelUpdateOptReader::verify(&slice[offsets[1]..offsets[2]], compatible)?; + PubkeyReader::verify(&slice[offsets[2]..offsets[3]], compatible)?; + Ok(()) + } +} +#[derive(Clone, Debug, Default)] +pub struct ChannelFailedBuilder { + pub(crate) channel_outpoint: OutPoint, + pub(crate) channel_update: ChannelUpdateOpt, + pub(crate) node_id: Pubkey, +} +impl ChannelFailedBuilder { + pub const FIELD_COUNT: usize = 3; + pub fn channel_outpoint(mut self, v: OutPoint) -> Self { + self.channel_outpoint = v; + self + } + pub fn channel_update(mut self, v: ChannelUpdateOpt) -> Self { + self.channel_update = v; + self + } + pub fn node_id(mut self, v: Pubkey) -> Self { + self.node_id = v; + self + } +} +impl molecule::prelude::Builder for ChannelFailedBuilder { + type Entity = ChannelFailed; + const NAME: &'static str = "ChannelFailedBuilder"; + fn expected_length(&self) -> usize { + molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1) + + self.channel_outpoint.as_slice().len() + + self.channel_update.as_slice().len() + + self.node_id.as_slice().len() + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + let mut total_size = molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1); + let mut offsets = Vec::with_capacity(Self::FIELD_COUNT); + offsets.push(total_size); + total_size += self.channel_outpoint.as_slice().len(); + offsets.push(total_size); + total_size += self.channel_update.as_slice().len(); + offsets.push(total_size); + total_size += self.node_id.as_slice().len(); + writer.write_all(&molecule::pack_number(total_size as molecule::Number))?; + for offset in offsets.into_iter() { + writer.write_all(&molecule::pack_number(offset as molecule::Number))?; + } + writer.write_all(self.channel_outpoint.as_slice())?; + writer.write_all(self.channel_update.as_slice())?; + writer.write_all(self.node_id.as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + ChannelFailed::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct NodeFailed(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for NodeFailed { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for NodeFailed { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for NodeFailed { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "node_id", self.node_id())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl ::core::default::Default for NodeFailed { + fn default() -> Self { + let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); + NodeFailed::new_unchecked(v) + } +} +impl NodeFailed { + const DEFAULT_VALUE: [u8; 41] = [ + 41, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + pub const FIELD_COUNT: usize = 1; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn node_id(&self) -> Pubkey { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[8..]) as usize; + Pubkey::new_unchecked(self.0.slice(start..end)) + } else { + Pubkey::new_unchecked(self.0.slice(start..)) + } + } + pub fn as_reader<'r>(&'r self) -> NodeFailedReader<'r> { + NodeFailedReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for NodeFailed { + type Builder = NodeFailedBuilder; + const NAME: &'static str = "NodeFailed"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + NodeFailed(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + NodeFailedReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + NodeFailedReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().node_id(self.node_id()) + } +} +#[derive(Clone, Copy)] +pub struct NodeFailedReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for NodeFailedReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for NodeFailedReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for NodeFailedReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "node_id", self.node_id())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl<'r> NodeFailedReader<'r> { + pub const FIELD_COUNT: usize = 1; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn node_id(&self) -> PubkeyReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[8..]) as usize; + PubkeyReader::new_unchecked(&self.as_slice()[start..end]) + } else { + PubkeyReader::new_unchecked(&self.as_slice()[start..]) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for NodeFailedReader<'r> { + type Entity = NodeFailed; + const NAME: &'static str = "NodeFailedReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + NodeFailedReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len < molecule::NUMBER_SIZE { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); + } + let total_size = molecule::unpack_number(slice) as usize; + if slice_len != total_size { + return ve!(Self, TotalSizeNotMatch, total_size, slice_len); + } + if slice_len < molecule::NUMBER_SIZE * 2 { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE * 2, slice_len); + } + let offset_first = molecule::unpack_number(&slice[molecule::NUMBER_SIZE..]) as usize; + if offset_first % molecule::NUMBER_SIZE != 0 || offset_first < molecule::NUMBER_SIZE * 2 { + return ve!(Self, OffsetsNotMatch); + } + if slice_len < offset_first { + return ve!(Self, HeaderIsBroken, offset_first, slice_len); + } + let field_count = offset_first / molecule::NUMBER_SIZE - 1; + if field_count < Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + } else if !compatible && field_count > Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + }; + let mut offsets: Vec = slice[molecule::NUMBER_SIZE..offset_first] + .chunks_exact(molecule::NUMBER_SIZE) + .map(|x| molecule::unpack_number(x) as usize) + .collect(); + offsets.push(total_size); + if offsets.windows(2).any(|i| i[0] > i[1]) { + return ve!(Self, OffsetsNotMatch); + } + PubkeyReader::verify(&slice[offsets[0]..offsets[1]], compatible)?; + Ok(()) + } +} +#[derive(Clone, Debug, Default)] +pub struct NodeFailedBuilder { + pub(crate) node_id: Pubkey, +} +impl NodeFailedBuilder { + pub const FIELD_COUNT: usize = 1; + pub fn node_id(mut self, v: Pubkey) -> Self { + self.node_id = v; + self + } +} +impl molecule::prelude::Builder for NodeFailedBuilder { + type Entity = NodeFailed; + const NAME: &'static str = "NodeFailedBuilder"; + fn expected_length(&self) -> usize { + molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1) + self.node_id.as_slice().len() + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + let mut total_size = molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1); + let mut offsets = Vec::with_capacity(Self::FIELD_COUNT); + offsets.push(total_size); + total_size += self.node_id.as_slice().len(); + writer.write_all(&molecule::pack_number(total_size as molecule::Number))?; + for offset in offsets.into_iter() { + writer.write_all(&molecule::pack_number(offset as molecule::Number))?; + } + writer.write_all(self.node_id.as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + NodeFailed::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct TlcErrData(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for TlcErrData { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for TlcErrData { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for TlcErrData { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}(", Self::NAME)?; + self.to_enum().display_inner(f)?; + write!(f, ")") + } +} +impl ::core::default::Default for TlcErrData { + fn default() -> Self { + let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); + TlcErrData::new_unchecked(v) + } +} +impl TlcErrData { + const DEFAULT_VALUE: [u8; 89] = [ + 0, 0, 0, 0, 85, 0, 0, 0, 16, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + pub const ITEMS_COUNT: usize = 2; + pub fn item_id(&self) -> molecule::Number { + molecule::unpack_number(self.as_slice()) + } + pub fn to_enum(&self) -> TlcErrDataUnion { + let inner = self.0.slice(molecule::NUMBER_SIZE..); + match self.item_id() { + 0 => ChannelFailed::new_unchecked(inner).into(), + 1 => NodeFailed::new_unchecked(inner).into(), + _ => panic!("{}: invalid data", Self::NAME), + } + } + pub fn as_reader<'r>(&'r self) -> TlcErrDataReader<'r> { + TlcErrDataReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for TlcErrData { + type Builder = TlcErrDataBuilder; + const NAME: &'static str = "TlcErrData"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + TlcErrData(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + TlcErrDataReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + TlcErrDataReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().set(self.to_enum()) + } +} +#[derive(Clone, Copy)] +pub struct TlcErrDataReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for TlcErrDataReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for TlcErrDataReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for TlcErrDataReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}(", Self::NAME)?; + self.to_enum().display_inner(f)?; + write!(f, ")") + } +} +impl<'r> TlcErrDataReader<'r> { + pub const ITEMS_COUNT: usize = 2; + pub fn item_id(&self) -> molecule::Number { + molecule::unpack_number(self.as_slice()) + } + pub fn to_enum(&self) -> TlcErrDataUnionReader<'r> { + let inner = &self.as_slice()[molecule::NUMBER_SIZE..]; + match self.item_id() { + 0 => ChannelFailedReader::new_unchecked(inner).into(), + 1 => NodeFailedReader::new_unchecked(inner).into(), + _ => panic!("{}: invalid data", Self::NAME), + } + } +} +impl<'r> molecule::prelude::Reader<'r> for TlcErrDataReader<'r> { + type Entity = TlcErrData; + const NAME: &'static str = "TlcErrDataReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + TlcErrDataReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len < molecule::NUMBER_SIZE { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); + } + let item_id = molecule::unpack_number(slice); + let inner_slice = &slice[molecule::NUMBER_SIZE..]; + match item_id { + 0 => ChannelFailedReader::verify(inner_slice, compatible), + 1 => NodeFailedReader::verify(inner_slice, compatible), + _ => ve!(Self, UnknownItem, Self::ITEMS_COUNT, item_id), + }?; + Ok(()) + } +} +#[derive(Clone, Debug, Default)] +pub struct TlcErrDataBuilder(pub(crate) TlcErrDataUnion); +impl TlcErrDataBuilder { + pub const ITEMS_COUNT: usize = 2; + pub fn set(mut self, v: I) -> Self + where + I: ::core::convert::Into, + { + self.0 = v.into(); + self + } +} +impl molecule::prelude::Builder for TlcErrDataBuilder { + type Entity = TlcErrData; + const NAME: &'static str = "TlcErrDataBuilder"; + fn expected_length(&self) -> usize { + molecule::NUMBER_SIZE + self.0.as_slice().len() + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + writer.write_all(&molecule::pack_number(self.0.item_id()))?; + writer.write_all(self.0.as_slice()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + TlcErrData::new_unchecked(inner.into()) + } +} +#[derive(Debug, Clone)] +pub enum TlcErrDataUnion { + ChannelFailed(ChannelFailed), + NodeFailed(NodeFailed), +} +#[derive(Debug, Clone, Copy)] +pub enum TlcErrDataUnionReader<'r> { + ChannelFailed(ChannelFailedReader<'r>), + NodeFailed(NodeFailedReader<'r>), +} +impl ::core::default::Default for TlcErrDataUnion { + fn default() -> Self { + TlcErrDataUnion::ChannelFailed(::core::default::Default::default()) + } +} +impl ::core::fmt::Display for TlcErrDataUnion { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + TlcErrDataUnion::ChannelFailed(ref item) => { + write!(f, "{}::{}({})", Self::NAME, ChannelFailed::NAME, item) + } + TlcErrDataUnion::NodeFailed(ref item) => { + write!(f, "{}::{}({})", Self::NAME, NodeFailed::NAME, item) + } + } + } +} +impl<'r> ::core::fmt::Display for TlcErrDataUnionReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + TlcErrDataUnionReader::ChannelFailed(ref item) => { + write!(f, "{}::{}({})", Self::NAME, ChannelFailed::NAME, item) + } + TlcErrDataUnionReader::NodeFailed(ref item) => { + write!(f, "{}::{}({})", Self::NAME, NodeFailed::NAME, item) + } + } + } +} +impl TlcErrDataUnion { + pub(crate) fn display_inner(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + TlcErrDataUnion::ChannelFailed(ref item) => write!(f, "{}", item), + TlcErrDataUnion::NodeFailed(ref item) => write!(f, "{}", item), + } + } +} +impl<'r> TlcErrDataUnionReader<'r> { + pub(crate) fn display_inner(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + TlcErrDataUnionReader::ChannelFailed(ref item) => write!(f, "{}", item), + TlcErrDataUnionReader::NodeFailed(ref item) => write!(f, "{}", item), + } + } +} +impl ::core::convert::From for TlcErrDataUnion { + fn from(item: ChannelFailed) -> Self { + TlcErrDataUnion::ChannelFailed(item) + } +} +impl ::core::convert::From for TlcErrDataUnion { + fn from(item: NodeFailed) -> Self { + TlcErrDataUnion::NodeFailed(item) + } +} +impl<'r> ::core::convert::From> for TlcErrDataUnionReader<'r> { + fn from(item: ChannelFailedReader<'r>) -> Self { + TlcErrDataUnionReader::ChannelFailed(item) + } +} +impl<'r> ::core::convert::From> for TlcErrDataUnionReader<'r> { + fn from(item: NodeFailedReader<'r>) -> Self { + TlcErrDataUnionReader::NodeFailed(item) + } +} +impl TlcErrDataUnion { + pub const NAME: &'static str = "TlcErrDataUnion"; + pub fn as_bytes(&self) -> molecule::bytes::Bytes { + match self { + TlcErrDataUnion::ChannelFailed(item) => item.as_bytes(), + TlcErrDataUnion::NodeFailed(item) => item.as_bytes(), + } + } + pub fn as_slice(&self) -> &[u8] { + match self { + TlcErrDataUnion::ChannelFailed(item) => item.as_slice(), + TlcErrDataUnion::NodeFailed(item) => item.as_slice(), + } + } + pub fn item_id(&self) -> molecule::Number { + match self { + TlcErrDataUnion::ChannelFailed(_) => 0, + TlcErrDataUnion::NodeFailed(_) => 1, + } + } + pub fn item_name(&self) -> &str { + match self { + TlcErrDataUnion::ChannelFailed(_) => "ChannelFailed", + TlcErrDataUnion::NodeFailed(_) => "NodeFailed", + } + } + pub fn as_reader<'r>(&'r self) -> TlcErrDataUnionReader<'r> { + match self { + TlcErrDataUnion::ChannelFailed(item) => item.as_reader().into(), + TlcErrDataUnion::NodeFailed(item) => item.as_reader().into(), + } + } +} +impl<'r> TlcErrDataUnionReader<'r> { + pub const NAME: &'r str = "TlcErrDataUnionReader"; + pub fn as_slice(&self) -> &'r [u8] { + match self { + TlcErrDataUnionReader::ChannelFailed(item) => item.as_slice(), + TlcErrDataUnionReader::NodeFailed(item) => item.as_slice(), + } + } + pub fn item_id(&self) -> molecule::Number { + match self { + TlcErrDataUnionReader::ChannelFailed(_) => 0, + TlcErrDataUnionReader::NodeFailed(_) => 1, + } + } + pub fn item_name(&self) -> &str { + match self { + TlcErrDataUnionReader::ChannelFailed(_) => "ChannelFailed", + TlcErrDataUnionReader::NodeFailed(_) => "NodeFailed", + } + } +} +impl From for TlcErrData { + fn from(value: ChannelFailed) -> Self { + Self::new_builder().set(value).build() + } +} +impl From for TlcErrData { + fn from(value: NodeFailed) -> Self { + Self::new_builder().set(value).build() + } +} +#[derive(Clone)] +pub struct TlcErrDataOpt(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for TlcErrDataOpt { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for TlcErrDataOpt { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for TlcErrDataOpt { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(v) = self.to_opt() { + write!(f, "{}(Some({}))", Self::NAME, v) + } else { + write!(f, "{}(None)", Self::NAME) + } + } +} +impl ::core::default::Default for TlcErrDataOpt { + fn default() -> Self { + let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); + TlcErrDataOpt::new_unchecked(v) + } +} +impl TlcErrDataOpt { + const DEFAULT_VALUE: [u8; 0] = []; + pub fn is_none(&self) -> bool { + self.0.is_empty() + } + pub fn is_some(&self) -> bool { + !self.0.is_empty() + } + pub fn to_opt(&self) -> Option { + if self.is_none() { + None + } else { + Some(TlcErrData::new_unchecked(self.0.clone())) + } + } + pub fn as_reader<'r>(&'r self) -> TlcErrDataOptReader<'r> { + TlcErrDataOptReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for TlcErrDataOpt { + type Builder = TlcErrDataOptBuilder; + const NAME: &'static str = "TlcErrDataOpt"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + TlcErrDataOpt(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + TlcErrDataOptReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + TlcErrDataOptReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().set(self.to_opt()) + } +} +#[derive(Clone, Copy)] +pub struct TlcErrDataOptReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for TlcErrDataOptReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for TlcErrDataOptReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for TlcErrDataOptReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(v) = self.to_opt() { + write!(f, "{}(Some({}))", Self::NAME, v) + } else { + write!(f, "{}(None)", Self::NAME) + } + } +} +impl<'r> TlcErrDataOptReader<'r> { + pub fn is_none(&self) -> bool { + self.0.is_empty() + } + pub fn is_some(&self) -> bool { + !self.0.is_empty() + } + pub fn to_opt(&self) -> Option> { + if self.is_none() { + None + } else { + Some(TlcErrDataReader::new_unchecked(self.as_slice())) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for TlcErrDataOptReader<'r> { + type Entity = TlcErrDataOpt; + const NAME: &'static str = "TlcErrDataOptReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + TlcErrDataOptReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + if !slice.is_empty() { + TlcErrDataReader::verify(&slice[..], compatible)?; + } + Ok(()) + } +} +#[derive(Clone, Debug, Default)] +pub struct TlcErrDataOptBuilder(pub(crate) Option); +impl TlcErrDataOptBuilder { + pub fn set(mut self, v: Option) -> Self { + self.0 = v; + self + } +} +impl molecule::prelude::Builder for TlcErrDataOptBuilder { + type Entity = TlcErrDataOpt; + const NAME: &'static str = "TlcErrDataOptBuilder"; + fn expected_length(&self) -> usize { + self.0 + .as_ref() + .map(|ref inner| inner.as_slice().len()) + .unwrap_or(0) + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + self.0 + .as_ref() + .map(|ref inner| writer.write_all(inner.as_slice())) + .unwrap_or(Ok(())) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + TlcErrDataOpt::new_unchecked(inner.into()) + } +} +impl From for TlcErrDataOpt { + fn from(value: TlcErrData) -> Self { + Self::new_builder().set(Some(value)).build() + } +} +#[derive(Clone)] +pub struct TlcErr(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for TlcErr { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for TlcErr { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for TlcErr { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "error_code", self.error_code())?; + write!(f, ", {}: {}", "extra_data", self.extra_data())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl ::core::default::Default for TlcErr { + fn default() -> Self { + let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); + TlcErr::new_unchecked(v) + } +} +impl TlcErr { + const DEFAULT_VALUE: [u8; 44] = [ + 44, 0, 0, 0, 12, 0, 0, 0, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + pub const FIELD_COUNT: usize = 2; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn error_code(&self) -> Byte32 { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + Byte32::new_unchecked(self.0.slice(start..end)) + } + pub fn extra_data(&self) -> TlcErrDataOpt { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[12..]) as usize; + TlcErrDataOpt::new_unchecked(self.0.slice(start..end)) + } else { + TlcErrDataOpt::new_unchecked(self.0.slice(start..)) + } + } + pub fn as_reader<'r>(&'r self) -> TlcErrReader<'r> { + TlcErrReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for TlcErr { + type Builder = TlcErrBuilder; + const NAME: &'static str = "TlcErr"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + TlcErr(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + TlcErrReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + TlcErrReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder() + .error_code(self.error_code()) + .extra_data(self.extra_data()) + } +} +#[derive(Clone, Copy)] +pub struct TlcErrReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for TlcErrReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for TlcErrReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for TlcErrReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "error_code", self.error_code())?; + write!(f, ", {}: {}", "extra_data", self.extra_data())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl<'r> TlcErrReader<'r> { + pub const FIELD_COUNT: usize = 2; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn error_code(&self) -> Byte32Reader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + Byte32Reader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn extra_data(&self) -> TlcErrDataOptReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[12..]) as usize; + TlcErrDataOptReader::new_unchecked(&self.as_slice()[start..end]) + } else { + TlcErrDataOptReader::new_unchecked(&self.as_slice()[start..]) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for TlcErrReader<'r> { + type Entity = TlcErr; + const NAME: &'static str = "TlcErrReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + TlcErrReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len < molecule::NUMBER_SIZE { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); + } + let total_size = molecule::unpack_number(slice) as usize; + if slice_len != total_size { + return ve!(Self, TotalSizeNotMatch, total_size, slice_len); + } + if slice_len < molecule::NUMBER_SIZE * 2 { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE * 2, slice_len); + } + let offset_first = molecule::unpack_number(&slice[molecule::NUMBER_SIZE..]) as usize; + if offset_first % molecule::NUMBER_SIZE != 0 || offset_first < molecule::NUMBER_SIZE * 2 { + return ve!(Self, OffsetsNotMatch); + } + if slice_len < offset_first { + return ve!(Self, HeaderIsBroken, offset_first, slice_len); + } + let field_count = offset_first / molecule::NUMBER_SIZE - 1; + if field_count < Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + } else if !compatible && field_count > Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + }; + let mut offsets: Vec = slice[molecule::NUMBER_SIZE..offset_first] + .chunks_exact(molecule::NUMBER_SIZE) + .map(|x| molecule::unpack_number(x) as usize) + .collect(); + offsets.push(total_size); + if offsets.windows(2).any(|i| i[0] > i[1]) { + return ve!(Self, OffsetsNotMatch); + } + Byte32Reader::verify(&slice[offsets[0]..offsets[1]], compatible)?; + TlcErrDataOptReader::verify(&slice[offsets[1]..offsets[2]], compatible)?; + Ok(()) + } +} +#[derive(Clone, Debug, Default)] +pub struct TlcErrBuilder { + pub(crate) error_code: Byte32, + pub(crate) extra_data: TlcErrDataOpt, +} +impl TlcErrBuilder { + pub const FIELD_COUNT: usize = 2; + pub fn error_code(mut self, v: Byte32) -> Self { + self.error_code = v; + self + } + pub fn extra_data(mut self, v: TlcErrDataOpt) -> Self { + self.extra_data = v; + self + } +} +impl molecule::prelude::Builder for TlcErrBuilder { + type Entity = TlcErr; + const NAME: &'static str = "TlcErrBuilder"; + fn expected_length(&self) -> usize { + molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1) + + self.error_code.as_slice().len() + + self.extra_data.as_slice().len() + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + let mut total_size = molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1); + let mut offsets = Vec::with_capacity(Self::FIELD_COUNT); + offsets.push(total_size); + total_size += self.error_code.as_slice().len(); + offsets.push(total_size); + total_size += self.extra_data.as_slice().len(); + writer.write_all(&molecule::pack_number(total_size as molecule::Number))?; + for offset in offsets.into_iter() { + writer.write_all(&molecule::pack_number(offset as molecule::Number))?; + } + writer.write_all(self.error_code.as_slice())?; + writer.write_all(self.extra_data.as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + TlcErr::new_unchecked(inner.into()) + } +} diff --git a/src/fiber/schema/gossip.mol b/src/fiber/schema/gossip.mol index 5a4a23328..7f1ea634c 100644 --- a/src/fiber/schema/gossip.mol +++ b/src/fiber/schema/gossip.mol @@ -34,7 +34,7 @@ struct BroadcastMessageQuery { vector BroadcastMessageQueries ; // A NodeAnnouncement is a broadcast message to the network for the node information. -// An update to the node information can be broadcasted with a NodeAnnouncement with larger timestamp. +// An update to the node information can be broadcasted with a NodeAnnouncement with larger timestamp. table NodeAnnouncement { // Signature to this message. signature: EcdsaSignature, @@ -166,3 +166,25 @@ union GossipMessage { QueryBroadcastMessages, QueryBroadcastMessagesResult, } + +option ChannelUpdateOpt (ChannelUpdate); +table ChannelFailed { + channel_outpoint: OutPoint, + channel_update: ChannelUpdateOpt, + node_id: Pubkey, +} + +table NodeFailed { + node_id: Pubkey, +} + +union TlcErrData { + ChannelFailed, + NodeFailed, +} + +option TlcErrDataOpt (TlcErrData); +table TlcErr { + error_code: Byte32, + extra_data: TlcErrDataOpt, +} \ No newline at end of file diff --git a/src/fiber/types.rs b/src/fiber/types.rs index 86d09e7af..6e69de18a 100644 --- a/src/fiber/types.rs +++ b/src/fiber/types.rs @@ -1107,6 +1107,70 @@ impl TryFrom for RemoveTlcFulfill { } } +// impl From for molecule_gossip::TlcErr { +// fn from(tlc_err: TlcErr) -> Self { +// molecule_gossip::TlcErr::new_builder() +// .error_code(ckb_types::packed::Byte32::new(tlc_err.error_code as u32)) +// .extra_data( +// tlc_err +// .extra_data +// .map(|data| match data { +// TlcErrData::ChannelFailed { +// channel_outpoint, +// channel_update, +// node_id, +// } => molecule_gossip::TlcErrDataBuilder::set( +// molecule_gossip::ChannelFailed::new_builder() +// .channel_outpoint(channel_outpoint.into()) +// .channel_update(channel_update.into()) +// .node_id(node_id.into()) +// .build(), +// ) +// .build() +// .to_opt(), +// TlcErrData::NodeFailed { node_id } => { +// molecule_gossip::TlcErrDataBuilder::set( +// molecule_gossip::NodeFailed::new_builder() +// .node_id(node_id.into()) +// .build(), +// ) +// .build() +// .to_opt() +// } +// }) +// .pack(), +// ) +// .build() +// } +// } + +// impl TryFrom for TlcErr { +// type Error = Error; + +// fn try_from(tlc_err: molecule_gossip::TlcErr) -> Result { +// Ok(TlcErr { +// error_code: tlc_err.error_code().into(), +// extra_data: tlc_err +// .extra_data() +// .to_opt() +// .map(|data| match data.unpack() { +// TlcErrData::ChannelFailed { +// channel_outpoint, +// channel_update, +// node_id, +// } => TlcErrData::ChannelFailed { +// channel_outpoint: channel_outpoint.into(), +// channel_update: channel_update.to_opt(), +// node_id: node_id.into(), +// }, +// TlcErrData::NodeFailed { node_id } => TlcErrData::NodeFailed { +// node_id: node_id.into(), +// }, +// }), +// }) +// } +// } + #[serde_as] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum TlcErrData { @@ -1196,11 +1260,11 @@ impl TlcErr { } fn serialize(&self) -> Vec { - deterministically_serialize(self) + bincode::serialize(self).expect("serialize hop data") } fn deserialize(data: &[u8]) -> Option { - serde_json::from_slice(data).ok() + bincode::deserialize(data).ok() } } @@ -3283,10 +3347,6 @@ macro_rules! impl_traits { impl_traits!(FiberMessage); -pub(crate) fn deterministically_serialize(v: &T) -> Vec { - serde_json::to_vec_pretty(v).expect("serialize value") -} - pub(crate) fn deterministically_hash(v: &T) -> [u8; 32] { ckb_hash::blake2b_256(v.as_slice()).into() } @@ -3324,11 +3384,11 @@ impl HopData for PaymentHopData { } fn serialize(&self) -> Vec { - deterministically_serialize(self) + bincode::serialize(self).expect("serialize hop data") } fn deserialize(data: &[u8]) -> Option { - serde_json::from_slice(data).ok() + bincode::deserialize(data).ok() } } From 8ba6980ada3639395a2d23b759afdbe58e4e780f Mon Sep 17 00:00:00 2001 From: yukang Date: Fri, 27 Dec 2024 20:21:13 +0800 Subject: [PATCH 059/119] use bincode for hopdata and remove duplicated unit test --- src/fiber/hash_algorithm.rs | 23 ----------------------- src/fiber/tests/hash_algorithm.rs | 10 ++++++---- src/fiber/types.rs | 12 ++++-------- 3 files changed, 10 insertions(+), 35 deletions(-) diff --git a/src/fiber/hash_algorithm.rs b/src/fiber/hash_algorithm.rs index cc8a8603c..acc572bff 100644 --- a/src/fiber/hash_algorithm.rs +++ b/src/fiber/hash_algorithm.rs @@ -55,26 +55,3 @@ impl TryFrom for HashAlgorithm { pub fn sha256>(s: T) -> [u8; 32] { Sha256::hash(s.as_ref()).to_byte_array() } - -#[cfg(test)] -mod tests { - #[test] - fn test_hash_algorithm_serialization_sha256() { - let algorithm = super::HashAlgorithm::Sha256; - let serialized = serde_json::to_string(&algorithm).expect("hash algorithm to json"); - assert_eq!(serialized, r#""sha256""#); - let deserialized: super::HashAlgorithm = - serde_json::from_str(&serialized).expect("hash algorithm from json"); - assert_eq!(deserialized, algorithm); - } - - #[test] - fn test_hash_algorithm_serialization_ckb_hash() { - let algorithm = super::HashAlgorithm::CkbHash; - let serialized = serde_json::to_string(&algorithm).expect("hash algorithm to json"); - assert_eq!(serialized, r#""ckb_hash""#); - let deserialized: super::HashAlgorithm = - serde_json::from_str(&serialized).expect("hash algorithm from json"); - assert_eq!(deserialized, algorithm); - } -} diff --git a/src/fiber/tests/hash_algorithm.rs b/src/fiber/tests/hash_algorithm.rs index a05ff055f..3de76f1fb 100644 --- a/src/fiber/tests/hash_algorithm.rs +++ b/src/fiber/tests/hash_algorithm.rs @@ -3,17 +3,19 @@ use crate::fiber::hash_algorithm::HashAlgorithm; #[test] fn test_hash_algorithm_serialization_sha256() { let algorithm = HashAlgorithm::Sha256; - let serialized = serde_json::to_string(&algorithm).unwrap(); + let serialized = serde_json::to_string(&algorithm).expect("hash algorithm to json"); assert_eq!(serialized, r#""sha256""#); - let deserialized: HashAlgorithm = serde_json::from_str(&serialized).unwrap(); + let deserialized: HashAlgorithm = + serde_json::from_str(&serialized).expect("hash algorithm from json"); assert_eq!(deserialized, algorithm); } #[test] fn test_hash_algorithm_serialization_ckb_hash() { let algorithm = HashAlgorithm::CkbHash; - let serialized = serde_json::to_string(&algorithm).unwrap(); + let serialized = serde_json::to_string(&algorithm).expect("hash algorithm to json"); assert_eq!(serialized, r#""ckb_hash""#); - let deserialized: HashAlgorithm = serde_json::from_str(&serialized).unwrap(); + let deserialized: HashAlgorithm = + serde_json::from_str(&serialized).expect("hash algorithm from json"); assert_eq!(deserialized, algorithm); } diff --git a/src/fiber/types.rs b/src/fiber/types.rs index 86d09e7af..66c292781 100644 --- a/src/fiber/types.rs +++ b/src/fiber/types.rs @@ -1196,11 +1196,11 @@ impl TlcErr { } fn serialize(&self) -> Vec { - deterministically_serialize(self) + bincode::serialize(self).expect("serialize hop data") } fn deserialize(data: &[u8]) -> Option { - serde_json::from_slice(data).ok() + bincode::deserialize(data).ok() } } @@ -3283,10 +3283,6 @@ macro_rules! impl_traits { impl_traits!(FiberMessage); -pub(crate) fn deterministically_serialize(v: &T) -> Vec { - serde_json::to_vec_pretty(v).expect("serialize value") -} - pub(crate) fn deterministically_hash(v: &T) -> [u8; 32] { ckb_hash::blake2b_256(v.as_slice()).into() } @@ -3324,11 +3320,11 @@ impl HopData for PaymentHopData { } fn serialize(&self) -> Vec { - deterministically_serialize(self) + bincode::serialize(self).expect("serialize hop data") } fn deserialize(data: &[u8]) -> Option { - serde_json::from_slice(data).ok() + bincode::deserialize(data).ok() } } From 393152c8ebf3fc0381c5bd28221ad83dcd40eec2 Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 28 Dec 2024 18:58:11 +0800 Subject: [PATCH 060/119] remove commitment numbers from remove_at --- src/fiber/channel.rs | 112 ++++++++++++++++++-------------------- src/fiber/tests/tlc_op.rs | 8 +-- 2 files changed, 55 insertions(+), 65 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index e6d145df6..cb9857950 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -686,7 +686,7 @@ where }; let settled_tlcs: Vec<_> = pending_tlcs .filter(|tlc| { - tlc.removed_at.is_some() + tlc.removed_reason.is_some() && matches!( tlc.status, TlcStatus::Inbound(InboundTlcStatus::RemoveAckConfirmed) @@ -712,7 +712,7 @@ where .tlc_state .get_committed_received_tlcs() .into_iter() - .filter(|tlc| tlc.removed_at.is_none()) + .filter(|tlc| tlc.removed_reason.is_none()) .collect(); for add_tlc in apply_tlcs { @@ -1021,11 +1021,9 @@ where // maybe we need to go through shutdown process for this error state .check_remove_tlc_with_reason(TLCId::Offered(remove_tlc.tlc_id), &remove_tlc.reason)?; - state.tlc_state.set_offered_tlc_removed( - remove_tlc.tlc_id, - state.get_current_commitment_numbers(), - remove_tlc.reason, - ); + state + .tlc_state + .set_offered_tlc_removed(remove_tlc.tlc_id, remove_tlc.reason); Ok(()) } @@ -1232,11 +1230,9 @@ where ) -> ProcessingChannelResult { state.check_for_tlc_update(None, true, false)?; state.check_remove_tlc_with_reason(TLCId::Received(command.id), &command.reason)?; - state.tlc_state.set_received_tlc_removed( - command.id, - state.get_current_commitment_numbers(), - command.reason.clone(), - ); + state + .tlc_state + .set_received_tlc_removed(command.id, command.reason.clone()); let msg = FiberMessageWithPeerId::new( state.get_remote_peer_id(), FiberMessage::remove_tlc(RemoveTlc { @@ -2286,7 +2282,7 @@ pub struct TlcInfo { /// Save it to backward errors. Use all zeros when no shared secrets are available. pub shared_secret: [u8; 32], pub created_at: CommitmentNumbers, - pub removed_at: Option<(CommitmentNumbers, RemoveTlcReason)>, + pub removed_reason: Option, pub payment_preimage: Option, /// Note: `previous_tlc` is used to track the tlc chain for a multi-tlc payment, @@ -2529,28 +2525,18 @@ impl TlcState { self.received_tlcs.add_tlc(tlc); } - pub fn set_received_tlc_removed( - &mut self, - tlc_id: u64, - remove_at: CommitmentNumbers, - reason: RemoveTlcReason, - ) { + pub fn set_received_tlc_removed(&mut self, tlc_id: u64, reason: RemoveTlcReason) { if let Some(tlc) = self.get_mut(&TLCId::Received(tlc_id)) { assert_eq!(tlc.inbound_status(), InboundTlcStatus::Committed); - tlc.removed_at = Some((remove_at, reason.clone())); + tlc.removed_reason = Some(reason); tlc.status = TlcStatus::Inbound(InboundTlcStatus::LocalRemoved); } } - pub fn set_offered_tlc_removed( - &mut self, - tlc_id: u64, - remove_at: CommitmentNumbers, - reason: RemoveTlcReason, - ) { + pub fn set_offered_tlc_removed(&mut self, tlc_id: u64, reason: RemoveTlcReason) { if let Some(tlc) = self.get_mut(&TLCId::Offered(tlc_id)) { assert_eq!(tlc.outbound_status(), OutboundTlcStatus::Committed); - tlc.removed_at = Some((remove_at, reason.clone())); + tlc.removed_reason = Some(reason); tlc.status = TlcStatus::Outbound(OutboundTlcStatus::RemoteRemoved); } } @@ -4301,7 +4287,10 @@ impl ChannelActorState { tlc_id: TLCId, ) -> Result<(TlcInfo, RemoveTlcReason), ProcessingChannelError> { let mut current = self.tlc_state.get_mut(&tlc_id).expect("TLC exists").clone(); - let (_remove_at, reason) = current.removed_at.clone().expect("expect remove_at exist"); + let reason = current + .removed_reason + .clone() + .expect("expect removed_reason exist"); assert!(matches!( current.status, TlcStatus::Inbound(InboundTlcStatus::RemoveAckConfirmed) @@ -4494,7 +4483,7 @@ impl ChannelActorState { let mut fulfilled = 0; tlcs.tlcs.iter().for_each(|tlc| { - if let Some(remove_at) = &tlc.removed_at { + if let Some(remove_reason) = &tlc.removed_reason { let mut include = false; match tlc.status { TlcStatus::Inbound(InboundTlcStatus::LocalRemoved) @@ -4508,7 +4497,7 @@ impl ChannelActorState { } _ => {} } - if include && matches!(remove_at.1, RemoveTlcReason::RemoveTlcFulfill(_)) { + if include && matches!(remove_reason, RemoveTlcReason::RemoveTlcFulfill(_)) { fulfilled += tlc.amount; } } @@ -4627,7 +4616,7 @@ impl ChannelActorState { fn any_tlc_pending(&self) -> bool { self.tlc_state .all_tlcs() - .any(|tlc| tlc.removed_at.is_none()) + .any(|tlc| tlc.removed_reason.is_none()) } pub fn get_local_funding_pubkey(&self) -> &Pubkey { @@ -4696,7 +4685,7 @@ impl ChannelActorState { reason: &RemoveTlcReason, ) -> ProcessingChannelResult { if let Some(tlc) = self.tlc_state.get(&tlc_id) { - if tlc.removed_at.is_some() { + if tlc.removed_reason.is_some() { return Err(ProcessingChannelError::RepeatedProcessing( "TLC is already removed".to_string(), )); @@ -4814,7 +4803,7 @@ impl ChannelActorState { hash_algorithm: command.hash_algorithm, created_at: self.get_current_commitment_numbers(), payment_preimage: None, - removed_at: None, + removed_reason: None, onion_packet: command.onion_packet, shared_secret: command.shared_secret, previous_tlc: command @@ -4838,7 +4827,7 @@ impl ChannelActorState { shared_secret: NO_SHARED_SECRET.clone(), created_at: self.get_current_commitment_numbers(), payment_preimage: None, - removed_at: None, + removed_reason: None, previous_tlc: None, }; Ok(tlc_info) @@ -5674,32 +5663,33 @@ impl ChannelActorState { // resend AddTlc, RemoveTlc and CommitmentSigned messages if needed let mut need_resend_commitment_signed = false; for info in self.tlc_state.all_tlcs() { - if info.is_offered() { - if info.created_at.get_local() >= acutal_local_commitment_number { - // resend AddTlc message - network - .send_message(NetworkActorMessage::new_command( - NetworkActorCommand::SendFiberMessage( - FiberMessageWithPeerId::new( - self.get_remote_peer_id(), - FiberMessage::add_tlc(AddTlc { - channel_id: self.get_id(), - tlc_id: info.tlc_id.into(), - amount: info.amount, - payment_hash: info.payment_hash, - expiry: info.expiry, - hash_algorithm: info.hash_algorithm, - onion_packet: info.onion_packet.clone(), - }), - ), + if info.is_offered() + && matches!(info.outbound_status(), OutboundTlcStatus::LocalAnnounced) + { + // resend AddTlc message + network + .send_message(NetworkActorMessage::new_command( + NetworkActorCommand::SendFiberMessage( + FiberMessageWithPeerId::new( + self.get_remote_peer_id(), + FiberMessage::add_tlc(AddTlc { + channel_id: self.get_id(), + tlc_id: info.tlc_id.into(), + amount: info.amount, + payment_hash: info.payment_hash, + expiry: info.expiry, + hash_algorithm: info.hash_algorithm, + onion_packet: info.onion_packet.clone(), + }), ), - )) - .expect(ASSUME_NETWORK_ACTOR_ALIVE); - - need_resend_commitment_signed = true; - } - } else if let Some((commitment_number, remove_reason)) = &info.removed_at { - if commitment_number.get_local() >= acutal_local_commitment_number { + ), + )) + .expect(ASSUME_NETWORK_ACTOR_ALIVE); + need_resend_commitment_signed = true; + } else if let Some(remove_reason) = &info.removed_reason { + if info.is_received() + && matches!(info.inbound_status(), InboundTlcStatus::LocalRemoved) + { // resend RemoveTlc message network .send_message(NetworkActorMessage::new_command( @@ -5727,7 +5717,9 @@ impl ChannelActorState { "Resend AddTlc and RemoveTlc messages if needed: {}", need_resend_commitment_signed ); - if need_resend_commitment_signed { + if need_resend_commitment_signed + || self.tlc_state.need_another_commitment_signed() + { debug!("Resend CommitmentSigned message"); network .send_message(NetworkActorMessage::new_command( diff --git a/src/fiber/tests/tlc_op.rs b/src/fiber/tests/tlc_op.rs index 99e56206d..21b3fcb69 100644 --- a/src/fiber/tests/tlc_op.rs +++ b/src/fiber/tests/tlc_op.rs @@ -209,7 +209,7 @@ impl Actor for TlcActor { hash_algorithm: command.hash_algorithm, created_at: CommitmentNumbers::default(), payment_preimage: None, - removed_at: None, + removed_reason: None, onion_packet: command.onion_packet, shared_secret: command.shared_secret, previous_tlc: None, @@ -240,7 +240,6 @@ impl Actor for TlcActor { eprintln!("Peer {} process remove tlc ....", state.peer_id); state.tlc_state.set_received_tlc_removed( tlc_id, - CommitmentNumbers::default(), RemoveTlcReason::RemoveTlcFulfill(RemoveTlcFulfill { payment_preimage: Default::default(), }), @@ -282,7 +281,6 @@ impl Actor for TlcActor { ); state.tlc_state.set_offered_tlc_removed( tlc_id, - CommitmentNumbers::default(), RemoveTlcReason::RemoveTlcFulfill(RemoveTlcFulfill { payment_preimage: Default::default(), }), @@ -468,7 +466,7 @@ fn test_tlc_state_v2() { shared_secret: NO_SHARED_SECRET.clone(), tlc_id: TLCId::Offered(0), created_at: CommitmentNumbers::default(), - removed_at: None, + removed_reason: None, payment_preimage: None, previous_tlc: None, }; @@ -483,7 +481,7 @@ fn test_tlc_state_v2() { shared_secret: NO_SHARED_SECRET.clone(), tlc_id: TLCId::Offered(1), created_at: CommitmentNumbers::default(), - removed_at: None, + removed_reason: None, payment_preimage: None, previous_tlc: None, }; From 0ae035245e67b72b827110ab3f3dba3cb9e9f512 Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 28 Dec 2024 19:15:15 +0800 Subject: [PATCH 061/119] add more test details for restarting --- src/fiber/channel.rs | 9 +++++++++ src/fiber/tests/channel.rs | 23 ++++++++++++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index cb9857950..b4a36450c 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -5768,6 +5768,15 @@ impl ChannelActorState { expected_remote_commitment_number, acutal_remote_commitment_number ); } + + #[cfg(debug_assertions)] + network + .send_message(NetworkActorMessage::new_notification( + NetworkServiceEvent::DebugEvent(DebugEvent::Common( + "Reestablished channel in ChannelReady".to_string(), + )), + )) + .expect(ASSUME_NETWORK_ACTOR_ALIVE); } _ => { // TODO: @quake we need to handle other states. diff --git a/src/fiber/tests/channel.rs b/src/fiber/tests/channel.rs index eb355372e..77084af28 100644 --- a/src/fiber/tests/channel.rs +++ b/src/fiber/tests/channel.rs @@ -3722,7 +3722,7 @@ async fn test_connect_to_peers_with_mutual_channel_on_restart_1() { let node_a_funding_amount = 100000000000; let node_b_funding_amount = 6200000000; - let (mut node_a, node_b, _new_channel_id, _) = + let (mut node_a, mut node_b, _new_channel_id, _) = NetworkNode::new_2_nodes_with_established_channel( node_a_funding_amount, node_b_funding_amount, @@ -3734,8 +3734,25 @@ async fn test_connect_to_peers_with_mutual_channel_on_restart_1() { node_a.expect_event( |event| matches!(event, NetworkServiceEvent::PeerConnected(id, _addr) if id == &node_b.peer_id), - ) - .await; + ).await; + node_a + .expect_event(|event| { + matches!( + event, + NetworkServiceEvent::DebugEvent(DebugEvent::Common( + info + )) if "Reestablished channel in ChannelReady" == info) + }) + .await; + node_b + .expect_event(|event| { + matches!( + event, + NetworkServiceEvent::DebugEvent(DebugEvent::Common( + info + )) if "Reestablished channel in ChannelReady" == info) + }) + .await; } #[tokio::test] From 8f94b67d45cf31a3c69944b067326cec513edcb3 Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 28 Dec 2024 19:45:33 +0800 Subject: [PATCH 062/119] add more unit test for resending add tlc --- Makefile | 2 +- src/fiber/channel.rs | 24 +++--------- src/fiber/network.rs | 12 ++++++ src/fiber/tests/channel.rs | 70 +++++++++++++++++++++++++++++++++++ src/fiber/tests/test_utils.rs | 16 +++++--- 5 files changed, 99 insertions(+), 25 deletions(-) diff --git a/Makefile b/Makefile index d14e3a6b1..340fa01de 100644 --- a/Makefile +++ b/Makefile @@ -55,7 +55,7 @@ coverage: coverage-run-unittests coverage-collect-data coverage-generate-report .PHONY: gen-rpc-doc gen-rpc-doc: - cargo install fiber-rpc-gen + cargo install fiber-rpc-gen --force fiber-rpc-gen ./src/rpc if grep -q "TODO: add desc" ./src/rpc/README.md; then \ echo "Warning: There are 'TODO: add desc' in src/rpc/README.md, please add documentation comments to resolve them"; \ diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index b4a36450c..411836df9 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -1,6 +1,6 @@ #[cfg(debug_assertions)] use crate::fiber::network::DebugEvent; -use crate::fiber::serde_utils::U64Hex; +use crate::{debug_event, fiber::serde_utils::U64Hex}; use bitflags::bitflags; use ckb_jsonrpc_types::BlockNumber; use futures::future::OptionFuture; @@ -2097,16 +2097,7 @@ where "Error while processing channel message: {:?} with message: {:?}", error, message ); - #[cfg(debug_assertions)] - self.network - .clone() - .send_message(NetworkActorMessage::new_notification( - NetworkServiceEvent::DebugEvent(DebugEvent::Common(format!( - "{:?}", - error - ))), - )) - .expect(ASSUME_NETWORK_ACTOR_ALIVE); + debug_event!(&self.network, &format!("{:?}", error)); } } ChannelActorMessage::Command(command) => { @@ -5667,6 +5658,7 @@ impl ChannelActorState { && matches!(info.outbound_status(), OutboundTlcStatus::LocalAnnounced) { // resend AddTlc message + eprintln!("resending tlc ...."); network .send_message(NetworkActorMessage::new_command( NetworkActorCommand::SendFiberMessage( @@ -5686,6 +5678,7 @@ impl ChannelActorState { )) .expect(ASSUME_NETWORK_ACTOR_ALIVE); need_resend_commitment_signed = true; + debug_event!(network, "resend add tlc"); } else if let Some(remove_reason) = &info.removed_reason { if info.is_received() && matches!(info.inbound_status(), InboundTlcStatus::LocalRemoved) @@ -5769,14 +5762,7 @@ impl ChannelActorState { ); } - #[cfg(debug_assertions)] - network - .send_message(NetworkActorMessage::new_notification( - NetworkServiceEvent::DebugEvent(DebugEvent::Common( - "Reestablished channel in ChannelReady".to_string(), - )), - )) - .expect(ASSUME_NETWORK_ACTOR_ALIVE); + debug_event!(network, "Reestablished channel in ChannelReady"); } _ => { // TODO: @quake we need to handle other states. diff --git a/src/fiber/network.rs b/src/fiber/network.rs index 0915e6a76..cd12835ff 100644 --- a/src/fiber/network.rs +++ b/src/fiber/network.rs @@ -511,6 +511,18 @@ pub enum DebugEvent { Common(String), } +#[macro_export] +macro_rules! debug_event { + ($network:expr, $debug_event:expr) => { + #[cfg(debug_assertions)] + $network + .send_message(NetworkActorMessage::new_notification( + NetworkServiceEvent::DebugEvent(DebugEvent::Common($debug_event.to_string())), + )) + .expect(ASSUME_NETWORK_ACTOR_ALIVE); + }; +} + #[derive(Clone, Debug)] pub enum NetworkServiceEvent { NetworkStarted(PeerId, MultiAddr, Vec), diff --git a/src/fiber/tests/channel.rs b/src/fiber/tests/channel.rs index 77084af28..37969d7c7 100644 --- a/src/fiber/tests/channel.rs +++ b/src/fiber/tests/channel.rs @@ -3785,6 +3785,76 @@ async fn test_connect_to_peers_with_mutual_channel_on_restart_2() { .await; } +#[tokio::test] +async fn test_send_payment_with_node_restart_then_resend_add_tlc() { + init_tracing(); + + let node_a_funding_amount = 100000000000; + let node_b_funding_amount = 6200000000; + + let (mut node_a, mut node_b, _new_channel_id, _) = + NetworkNode::new_2_nodes_with_established_channel( + node_a_funding_amount, + node_b_funding_amount, + true, + ) + .await; + + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; + + let node_b_pubkey = node_b.pubkey.clone(); + let tlc_amount = 99; + let message = |rpc_reply| { + NetworkActorMessage::Command(NetworkActorCommand::SendPayment( + SendPaymentCommand { + target_pubkey: Some(node_b_pubkey), + amount: Some(tlc_amount), + payment_hash: None, + final_tlc_expiry_delta: None, + tlc_expiry_limit: 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"); + assert!(res.is_ok()); + let payment_hash = res.unwrap().payment_hash; + + node_b.stop().await; + + let payment_status = node_a.get_payment_status(payment_hash).await; + assert_eq!(payment_status, PaymentSessionStatus::Inflight); + + node_b.start().await; + + node_a.expect_event( + |event| matches!(event, NetworkServiceEvent::PeerConnected(id, _addr) if id == &node_b.peer_id), + ) + .await; + + node_a + .expect_event(|event| { + matches!( + event, + NetworkServiceEvent::DebugEvent(DebugEvent::Common( + info + )) if "resend add tlc" == info) + }) + .await; + + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; + let payment_status = node_a.get_payment_status(payment_hash).await; + assert_eq!(payment_status, PaymentSessionStatus::Success); +} + #[tokio::test] async fn test_open_channel_with_large_size_shutdown_script_should_fail() { let [node_a, node_b] = NetworkNode::new_n_interconnected_nodes().await; diff --git a/src/fiber/tests/test_utils.rs b/src/fiber/tests/test_utils.rs index e012e98ac..05a658ad4 100644 --- a/src/fiber/tests/test_utils.rs +++ b/src/fiber/tests/test_utils.rs @@ -555,6 +555,16 @@ impl NetworkNode { expected_status: PaymentSessionStatus, expected_retried: Option, ) { + let status = self.get_payment_status(payment_hash).await; + assert_eq!(status, expected_status); + + if let Some(expected_retried) = expected_retried { + let payment_session = self.get_payment_session(payment_hash).unwrap(); + assert_eq!(payment_session.retried_times, expected_retried); + } + } + + pub async fn get_payment_status(&self, payment_hash: Hash256) -> PaymentSessionStatus { let message = |rpc_reply| -> NetworkActorMessage { NetworkActorMessage::Command(NetworkActorCommand::GetPayment(payment_hash, rpc_reply)) }; @@ -562,11 +572,7 @@ impl NetworkNode { .expect("node_a alive") .unwrap(); - assert_eq!(res.status, expected_status); - if let Some(expected_retried) = expected_retried { - let payment_session = self.get_payment_session(payment_hash).unwrap(); - assert_eq!(payment_session.retried_times, expected_retried); - } + res.status } pub async fn update_channel_actor_state(&mut self, state: ChannelActorState) { From 28f3ffbc12ddb6aa8cd79d3b9f04a186126d356a Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 28 Dec 2024 20:50:56 +0800 Subject: [PATCH 063/119] add unit test for remove tlc --- src/fiber/channel.rs | 2 +- src/fiber/tests/channel.rs | 110 +++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 1 deletion(-) diff --git a/src/fiber/channel.rs b/src/fiber/channel.rs index 411836df9..467c62950 100644 --- a/src/fiber/channel.rs +++ b/src/fiber/channel.rs @@ -5658,7 +5658,6 @@ impl ChannelActorState { && matches!(info.outbound_status(), OutboundTlcStatus::LocalAnnounced) { // resend AddTlc message - eprintln!("resending tlc ...."); network .send_message(NetworkActorMessage::new_command( NetworkActorCommand::SendFiberMessage( @@ -5700,6 +5699,7 @@ impl ChannelActorState { .expect(ASSUME_NETWORK_ACTOR_ALIVE); need_resend_commitment_signed = true; + debug_event!(network, "resend remove tlc"); } } } diff --git a/src/fiber/tests/channel.rs b/src/fiber/tests/channel.rs index 37969d7c7..3cc6f2735 100644 --- a/src/fiber/tests/channel.rs +++ b/src/fiber/tests/channel.rs @@ -3855,6 +3855,116 @@ async fn test_send_payment_with_node_restart_then_resend_add_tlc() { assert_eq!(payment_status, PaymentSessionStatus::Success); } +#[tokio::test] +async fn test_node_reestablish_resend_remove_tlc() { + init_tracing(); + + let node_a_funding_amount = 100000000000; + let node_b_funding_amount = 6200000000; + + let (mut node_a, mut node_b, new_channel_id, _) = + NetworkNode::new_2_nodes_with_established_channel( + node_a_funding_amount, + node_b_funding_amount, + true, + ) + .await; + + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; + + let node_a_balance = node_a.get_local_balance_from_channel(new_channel_id); + let node_b_balance = node_b.get_local_balance_from_channel(new_channel_id); + + let preimage = [2; 32]; + // create a new payment hash + let payment_hash = HashAlgorithm::CkbHash.hash(&preimage); + + let add_tlc_result = call!(node_a.network_actor, |rpc_reply| { + NetworkActorMessage::Command(NetworkActorCommand::ControlFiberChannel( + ChannelCommandWithId { + channel_id: new_channel_id, + command: ChannelCommand::AddTlc( + AddTlcCommand { + amount: 1000, + hash_algorithm: HashAlgorithm::CkbHash, + payment_hash: payment_hash.clone().into(), + expiry: now_timestamp_as_millis_u64() + DEFAULT_EXPIRY_DELTA, + onion_packet: None, + shared_secret: NO_SHARED_SECRET.clone(), + previous_tlc: None, + }, + rpc_reply, + ), + }, + )) + }) + .expect("node_b alive") + .expect("successfully added tlc"); + + dbg!(&add_tlc_result); + + dbg!("Sleeping for some time to wait for the AddTlc processed by both party"); + tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + + node_a.stop().await; + + let remove_tlc_result = call!(node_b.network_actor, |rpc_reply| { + NetworkActorMessage::Command(NetworkActorCommand::ControlFiberChannel( + ChannelCommandWithId { + channel_id: new_channel_id, + command: ChannelCommand::RemoveTlc( + RemoveTlcCommand { + id: add_tlc_result.tlc_id, + reason: RemoveTlcReason::RemoveTlcFulfill(RemoveTlcFulfill { + payment_preimage: preimage.into(), + }), + }, + rpc_reply, + ), + }, + )) + }) + .expect("node_b alive"); + + dbg!(&remove_tlc_result); + assert!(remove_tlc_result.is_ok()); + + tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + + // assert balance does not changed since remove tlc is not processed by node_a + let new_node_a_balance = node_a.get_local_balance_from_channel(new_channel_id); + let new_node_b_balance = node_b.get_local_balance_from_channel(new_channel_id); + assert_eq!(node_a_balance, new_node_a_balance); + assert_eq!(node_b_balance, new_node_b_balance); + + node_a.start().await; + node_b + .expect_event(|event| { + matches!( + event, + NetworkServiceEvent::DebugEvent(DebugEvent::Common( + info + )) if "resend remove tlc" == info) + }) + .await; + + tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + + // assert balance changed since remove tlc is processed by node_a after node_b resending remove tlc + let new_node_a_balance = node_a.get_local_balance_from_channel(new_channel_id); + let new_node_b_balance = node_b.get_local_balance_from_channel(new_channel_id); + assert_eq!(node_a_balance - 1000, new_node_a_balance); + assert_eq!(node_b_balance + 1000, new_node_b_balance); + eprintln!( + "node_a_balance: {}, new_node_a_balance: {}", + node_a_balance, new_node_a_balance + ); + eprintln!( + "node_b_balance: {}, new_node_b_balance: {}", + node_b_balance, new_node_b_balance + ); +} + #[tokio::test] async fn test_open_channel_with_large_size_shutdown_script_should_fail() { let [node_a, node_b] = NetworkNode::new_n_interconnected_nodes().await; From 4e835c3490951c4ce7108e9ec6c3ba0fd7cce350 Mon Sep 17 00:00:00 2001 From: yukang Date: Fri, 27 Dec 2024 23:50:45 +0800 Subject: [PATCH 064/119] use molecule for serialize and deserialize hopdata and tlcerr --- Cargo.lock | 22 + Cargo.toml | 1 + src/fiber/gen/fiber.rs | 2661 +++++++++++++++++++++++++++++++++++ src/fiber/gen/gossip.rs | 2056 +-------------------------- src/fiber/schema/fiber.mol | 50 + src/fiber/schema/gossip.mol | 39 - src/fiber/tests/channel.rs | 5 +- src/fiber/tests/types.rs | 13 + src/fiber/types.rs | 244 +++- 9 files changed, 2963 insertions(+), 2128 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4049c90f9..ea31525f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1621,6 +1621,7 @@ dependencies = [ "molecule", "musig2", "nom", + "num_enum", "once_cell", "ractor", "rand 0.8.5", @@ -2809,6 +2810,27 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "number_prefix" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 4b6d37c80..962d1fd71 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,6 +63,7 @@ tokio = { version = "1", features = [ indicatif = "0.16" console = "0.15.8" bincode = "1.3.3" +num_enum = "0.7.3" [features] default = [] diff --git a/src/fiber/gen/fiber.rs b/src/fiber/gen/fiber.rs index a553f2eed..16edafea5 100644 --- a/src/fiber/gen/fiber.rs +++ b/src/fiber/gen/fiber.rs @@ -3,6 +3,231 @@ use super::blockchain::*; use molecule::prelude::*; #[derive(Clone)] +pub struct Uint16(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for Uint16 { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for Uint16 { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for Uint16 { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + let raw_data = hex_string(&self.raw_data()); + write!(f, "{}(0x{})", Self::NAME, raw_data) + } +} +impl ::core::default::Default for Uint16 { + fn default() -> Self { + let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); + Uint16::new_unchecked(v) + } +} +impl Uint16 { + const DEFAULT_VALUE: [u8; 2] = [0, 0]; + pub const TOTAL_SIZE: usize = 2; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 2; + pub fn nth0(&self) -> Byte { + Byte::new_unchecked(self.0.slice(0..1)) + } + pub fn nth1(&self) -> Byte { + Byte::new_unchecked(self.0.slice(1..2)) + } + pub fn raw_data(&self) -> molecule::bytes::Bytes { + self.as_bytes() + } + pub fn as_reader<'r>(&'r self) -> Uint16Reader<'r> { + Uint16Reader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for Uint16 { + type Builder = Uint16Builder; + const NAME: &'static str = "Uint16"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + Uint16(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + Uint16Reader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + Uint16Reader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().set([self.nth0(), self.nth1()]) + } +} +#[derive(Clone, Copy)] +pub struct Uint16Reader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for Uint16Reader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for Uint16Reader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for Uint16Reader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + let raw_data = hex_string(&self.raw_data()); + write!(f, "{}(0x{})", Self::NAME, raw_data) + } +} +impl<'r> Uint16Reader<'r> { + pub const TOTAL_SIZE: usize = 2; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 2; + pub fn nth0(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[0..1]) + } + pub fn nth1(&self) -> ByteReader<'r> { + ByteReader::new_unchecked(&self.as_slice()[1..2]) + } + pub fn raw_data(&self) -> &'r [u8] { + self.as_slice() + } +} +impl<'r> molecule::prelude::Reader<'r> for Uint16Reader<'r> { + type Entity = Uint16; + const NAME: &'static str = "Uint16Reader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + Uint16Reader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], _compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len != Self::TOTAL_SIZE { + return ve!(Self, TotalSizeNotMatch, Self::TOTAL_SIZE, slice_len); + } + Ok(()) + } +} +#[derive(Clone)] +pub struct Uint16Builder(pub(crate) [Byte; 2]); +impl ::core::fmt::Debug for Uint16Builder { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:?})", Self::NAME, &self.0[..]) + } +} +impl ::core::default::Default for Uint16Builder { + fn default() -> Self { + Uint16Builder([Byte::default(), Byte::default()]) + } +} +impl Uint16Builder { + pub const TOTAL_SIZE: usize = 2; + pub const ITEM_SIZE: usize = 1; + pub const ITEM_COUNT: usize = 2; + pub fn set(mut self, v: [Byte; 2]) -> Self { + self.0 = v; + self + } + pub fn nth0(mut self, v: Byte) -> Self { + self.0[0] = v; + self + } + pub fn nth1(mut self, v: Byte) -> Self { + self.0[1] = v; + self + } +} +impl molecule::prelude::Builder for Uint16Builder { + type Entity = Uint16; + const NAME: &'static str = "Uint16Builder"; + fn expected_length(&self) -> usize { + Self::TOTAL_SIZE + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + writer.write_all(self.0[0].as_slice())?; + writer.write_all(self.0[1].as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + Uint16::new_unchecked(inner.into()) + } +} +impl From<[Byte; 2usize]> for Uint16 { + fn from(value: [Byte; 2usize]) -> Self { + Self::new_builder().set(value).build() + } +} +impl ::core::convert::TryFrom<&[Byte]> for Uint16 { + type Error = ::core::array::TryFromSliceError; + fn try_from(value: &[Byte]) -> Result { + Ok(Self::new_builder() + .set(<&[Byte; 2usize]>::try_from(value)?.clone()) + .build()) + } +} +impl From for [Byte; 2usize] { + #[track_caller] + fn from(value: Uint16) -> Self { + [value.nth0(), value.nth1()] + } +} +impl From<[u8; 2usize]> for Uint16 { + fn from(value: [u8; 2usize]) -> Self { + Uint16Reader::new_unchecked(&value).to_entity() + } +} +impl ::core::convert::TryFrom<&[u8]> for Uint16 { + type Error = ::core::array::TryFromSliceError; + fn try_from(value: &[u8]) -> Result { + Ok(<[u8; 2usize]>::try_from(value)?.into()) + } +} +impl From for [u8; 2usize] { + #[track_caller] + fn from(value: Uint16) -> Self { + ::core::convert::TryFrom::try_from(value.as_slice()).unwrap() + } +} +impl<'a> From> for &'a [u8; 2usize] { + #[track_caller] + fn from(value: Uint16Reader<'a>) -> Self { + ::core::convert::TryFrom::try_from(value.as_slice()).unwrap() + } +} +impl<'a> From<&'a Uint16Reader<'a>> for &'a [u8; 2usize] { + #[track_caller] + fn from(value: &'a Uint16Reader<'a>) -> Self { + ::core::convert::TryFrom::try_from(value.as_slice()).unwrap() + } +} +#[derive(Clone)] pub struct EcdsaSignature(molecule::bytes::Bytes); impl ::core::fmt::LowerHex for EcdsaSignature { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { @@ -11764,3 +11989,2439 @@ impl From for FiberMessage { Self::new_builder().set(value).build() } } +#[derive(Clone)] +pub struct PaymentPreimageOpt(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for PaymentPreimageOpt { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for PaymentPreimageOpt { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for PaymentPreimageOpt { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(v) = self.to_opt() { + write!(f, "{}(Some({}))", Self::NAME, v) + } else { + write!(f, "{}(None)", Self::NAME) + } + } +} +impl ::core::default::Default for PaymentPreimageOpt { + fn default() -> Self { + let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); + PaymentPreimageOpt::new_unchecked(v) + } +} +impl PaymentPreimageOpt { + const DEFAULT_VALUE: [u8; 0] = []; + pub fn is_none(&self) -> bool { + self.0.is_empty() + } + pub fn is_some(&self) -> bool { + !self.0.is_empty() + } + pub fn to_opt(&self) -> Option { + if self.is_none() { + None + } else { + Some(Byte32::new_unchecked(self.0.clone())) + } + } + pub fn as_reader<'r>(&'r self) -> PaymentPreimageOptReader<'r> { + PaymentPreimageOptReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for PaymentPreimageOpt { + type Builder = PaymentPreimageOptBuilder; + const NAME: &'static str = "PaymentPreimageOpt"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + PaymentPreimageOpt(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + PaymentPreimageOptReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + PaymentPreimageOptReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().set(self.to_opt()) + } +} +#[derive(Clone, Copy)] +pub struct PaymentPreimageOptReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for PaymentPreimageOptReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for PaymentPreimageOptReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for PaymentPreimageOptReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(v) = self.to_opt() { + write!(f, "{}(Some({}))", Self::NAME, v) + } else { + write!(f, "{}(None)", Self::NAME) + } + } +} +impl<'r> PaymentPreimageOptReader<'r> { + pub fn is_none(&self) -> bool { + self.0.is_empty() + } + pub fn is_some(&self) -> bool { + !self.0.is_empty() + } + pub fn to_opt(&self) -> Option> { + if self.is_none() { + None + } else { + Some(Byte32Reader::new_unchecked(self.as_slice())) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for PaymentPreimageOptReader<'r> { + type Entity = PaymentPreimageOpt; + const NAME: &'static str = "PaymentPreimageOptReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + PaymentPreimageOptReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + if !slice.is_empty() { + Byte32Reader::verify(&slice[..], compatible)?; + } + Ok(()) + } +} +#[derive(Clone, Debug, Default)] +pub struct PaymentPreimageOptBuilder(pub(crate) Option); +impl PaymentPreimageOptBuilder { + pub fn set(mut self, v: Option) -> Self { + self.0 = v; + self + } +} +impl molecule::prelude::Builder for PaymentPreimageOptBuilder { + type Entity = PaymentPreimageOpt; + const NAME: &'static str = "PaymentPreimageOptBuilder"; + fn expected_length(&self) -> usize { + self.0 + .as_ref() + .map(|ref inner| inner.as_slice().len()) + .unwrap_or(0) + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + self.0 + .as_ref() + .map(|ref inner| writer.write_all(inner.as_slice())) + .unwrap_or(Ok(())) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + PaymentPreimageOpt::new_unchecked(inner.into()) + } +} +impl From for PaymentPreimageOpt { + fn from(value: Byte32) -> Self { + Self::new_builder().set(Some(value)).build() + } +} +#[derive(Clone)] +pub struct PubkeyOpt(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for PubkeyOpt { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for PubkeyOpt { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for PubkeyOpt { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(v) = self.to_opt() { + write!(f, "{}(Some({}))", Self::NAME, v) + } else { + write!(f, "{}(None)", Self::NAME) + } + } +} +impl ::core::default::Default for PubkeyOpt { + fn default() -> Self { + let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); + PubkeyOpt::new_unchecked(v) + } +} +impl PubkeyOpt { + const DEFAULT_VALUE: [u8; 0] = []; + pub fn is_none(&self) -> bool { + self.0.is_empty() + } + pub fn is_some(&self) -> bool { + !self.0.is_empty() + } + pub fn to_opt(&self) -> Option { + if self.is_none() { + None + } else { + Some(Pubkey::new_unchecked(self.0.clone())) + } + } + pub fn as_reader<'r>(&'r self) -> PubkeyOptReader<'r> { + PubkeyOptReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for PubkeyOpt { + type Builder = PubkeyOptBuilder; + const NAME: &'static str = "PubkeyOpt"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + PubkeyOpt(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + PubkeyOptReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + PubkeyOptReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().set(self.to_opt()) + } +} +#[derive(Clone, Copy)] +pub struct PubkeyOptReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for PubkeyOptReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for PubkeyOptReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for PubkeyOptReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(v) = self.to_opt() { + write!(f, "{}(Some({}))", Self::NAME, v) + } else { + write!(f, "{}(None)", Self::NAME) + } + } +} +impl<'r> PubkeyOptReader<'r> { + pub fn is_none(&self) -> bool { + self.0.is_empty() + } + pub fn is_some(&self) -> bool { + !self.0.is_empty() + } + pub fn to_opt(&self) -> Option> { + if self.is_none() { + None + } else { + Some(PubkeyReader::new_unchecked(self.as_slice())) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for PubkeyOptReader<'r> { + type Entity = PubkeyOpt; + const NAME: &'static str = "PubkeyOptReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + PubkeyOptReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + if !slice.is_empty() { + PubkeyReader::verify(&slice[..], compatible)?; + } + Ok(()) + } +} +#[derive(Clone, Debug, Default)] +pub struct PubkeyOptBuilder(pub(crate) Option); +impl PubkeyOptBuilder { + pub fn set(mut self, v: Option) -> Self { + self.0 = v; + self + } +} +impl molecule::prelude::Builder for PubkeyOptBuilder { + type Entity = PubkeyOpt; + const NAME: &'static str = "PubkeyOptBuilder"; + fn expected_length(&self) -> usize { + self.0 + .as_ref() + .map(|ref inner| inner.as_slice().len()) + .unwrap_or(0) + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + self.0 + .as_ref() + .map(|ref inner| writer.write_all(inner.as_slice())) + .unwrap_or(Ok(())) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + PubkeyOpt::new_unchecked(inner.into()) + } +} +impl From for PubkeyOpt { + fn from(value: Pubkey) -> Self { + Self::new_builder().set(Some(value)).build() + } +} +#[derive(Clone)] +pub struct PaymentHopData(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for PaymentHopData { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for PaymentHopData { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for PaymentHopData { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "amount", self.amount())?; + write!(f, ", {}: {}", "expiry", self.expiry())?; + write!(f, ", {}: {}", "payment_preimage", self.payment_preimage())?; + write!(f, ", {}: {}", "hash_algorithm", self.hash_algorithm())?; + write!(f, ", {}: {}", "funding_tx_hash", self.funding_tx_hash())?; + write!(f, ", {}: {}", "next_hop", self.next_hop())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl ::core::default::Default for PaymentHopData { + fn default() -> Self { + let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); + PaymentHopData::new_unchecked(v) + } +} +impl PaymentHopData { + const DEFAULT_VALUE: [u8; 85] = [ + 85, 0, 0, 0, 28, 0, 0, 0, 44, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 53, 0, 0, 0, 85, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + pub const FIELD_COUNT: usize = 6; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn amount(&self) -> Uint128 { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + Uint128::new_unchecked(self.0.slice(start..end)) + } + pub fn expiry(&self) -> Uint64 { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + let end = molecule::unpack_number(&slice[12..]) as usize; + Uint64::new_unchecked(self.0.slice(start..end)) + } + pub fn payment_preimage(&self) -> PaymentPreimageOpt { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[12..]) as usize; + let end = molecule::unpack_number(&slice[16..]) as usize; + PaymentPreimageOpt::new_unchecked(self.0.slice(start..end)) + } + pub fn hash_algorithm(&self) -> Byte { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[16..]) as usize; + let end = molecule::unpack_number(&slice[20..]) as usize; + Byte::new_unchecked(self.0.slice(start..end)) + } + pub fn funding_tx_hash(&self) -> Byte32 { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[20..]) as usize; + let end = molecule::unpack_number(&slice[24..]) as usize; + Byte32::new_unchecked(self.0.slice(start..end)) + } + pub fn next_hop(&self) -> PubkeyOpt { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[24..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[28..]) as usize; + PubkeyOpt::new_unchecked(self.0.slice(start..end)) + } else { + PubkeyOpt::new_unchecked(self.0.slice(start..)) + } + } + pub fn as_reader<'r>(&'r self) -> PaymentHopDataReader<'r> { + PaymentHopDataReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for PaymentHopData { + type Builder = PaymentHopDataBuilder; + const NAME: &'static str = "PaymentHopData"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + PaymentHopData(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + PaymentHopDataReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + PaymentHopDataReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder() + .amount(self.amount()) + .expiry(self.expiry()) + .payment_preimage(self.payment_preimage()) + .hash_algorithm(self.hash_algorithm()) + .funding_tx_hash(self.funding_tx_hash()) + .next_hop(self.next_hop()) + } +} +#[derive(Clone, Copy)] +pub struct PaymentHopDataReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for PaymentHopDataReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for PaymentHopDataReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for PaymentHopDataReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "amount", self.amount())?; + write!(f, ", {}: {}", "expiry", self.expiry())?; + write!(f, ", {}: {}", "payment_preimage", self.payment_preimage())?; + write!(f, ", {}: {}", "hash_algorithm", self.hash_algorithm())?; + write!(f, ", {}: {}", "funding_tx_hash", self.funding_tx_hash())?; + write!(f, ", {}: {}", "next_hop", self.next_hop())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl<'r> PaymentHopDataReader<'r> { + pub const FIELD_COUNT: usize = 6; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn amount(&self) -> Uint128Reader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + Uint128Reader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn expiry(&self) -> Uint64Reader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + let end = molecule::unpack_number(&slice[12..]) as usize; + Uint64Reader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn payment_preimage(&self) -> PaymentPreimageOptReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[12..]) as usize; + let end = molecule::unpack_number(&slice[16..]) as usize; + PaymentPreimageOptReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn hash_algorithm(&self) -> ByteReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[16..]) as usize; + let end = molecule::unpack_number(&slice[20..]) as usize; + ByteReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn funding_tx_hash(&self) -> Byte32Reader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[20..]) as usize; + let end = molecule::unpack_number(&slice[24..]) as usize; + Byte32Reader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn next_hop(&self) -> PubkeyOptReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[24..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[28..]) as usize; + PubkeyOptReader::new_unchecked(&self.as_slice()[start..end]) + } else { + PubkeyOptReader::new_unchecked(&self.as_slice()[start..]) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for PaymentHopDataReader<'r> { + type Entity = PaymentHopData; + const NAME: &'static str = "PaymentHopDataReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + PaymentHopDataReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len < molecule::NUMBER_SIZE { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); + } + let total_size = molecule::unpack_number(slice) as usize; + if slice_len != total_size { + return ve!(Self, TotalSizeNotMatch, total_size, slice_len); + } + if slice_len < molecule::NUMBER_SIZE * 2 { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE * 2, slice_len); + } + let offset_first = molecule::unpack_number(&slice[molecule::NUMBER_SIZE..]) as usize; + if offset_first % molecule::NUMBER_SIZE != 0 || offset_first < molecule::NUMBER_SIZE * 2 { + return ve!(Self, OffsetsNotMatch); + } + if slice_len < offset_first { + return ve!(Self, HeaderIsBroken, offset_first, slice_len); + } + let field_count = offset_first / molecule::NUMBER_SIZE - 1; + if field_count < Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + } else if !compatible && field_count > Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + }; + let mut offsets: Vec = slice[molecule::NUMBER_SIZE..offset_first] + .chunks_exact(molecule::NUMBER_SIZE) + .map(|x| molecule::unpack_number(x) as usize) + .collect(); + offsets.push(total_size); + if offsets.windows(2).any(|i| i[0] > i[1]) { + return ve!(Self, OffsetsNotMatch); + } + Uint128Reader::verify(&slice[offsets[0]..offsets[1]], compatible)?; + Uint64Reader::verify(&slice[offsets[1]..offsets[2]], compatible)?; + PaymentPreimageOptReader::verify(&slice[offsets[2]..offsets[3]], compatible)?; + ByteReader::verify(&slice[offsets[3]..offsets[4]], compatible)?; + Byte32Reader::verify(&slice[offsets[4]..offsets[5]], compatible)?; + PubkeyOptReader::verify(&slice[offsets[5]..offsets[6]], compatible)?; + Ok(()) + } +} +#[derive(Clone, Debug, Default)] +pub struct PaymentHopDataBuilder { + pub(crate) amount: Uint128, + pub(crate) expiry: Uint64, + pub(crate) payment_preimage: PaymentPreimageOpt, + pub(crate) hash_algorithm: Byte, + pub(crate) funding_tx_hash: Byte32, + pub(crate) next_hop: PubkeyOpt, +} +impl PaymentHopDataBuilder { + pub const FIELD_COUNT: usize = 6; + pub fn amount(mut self, v: Uint128) -> Self { + self.amount = v; + self + } + pub fn expiry(mut self, v: Uint64) -> Self { + self.expiry = v; + self + } + pub fn payment_preimage(mut self, v: PaymentPreimageOpt) -> Self { + self.payment_preimage = v; + self + } + pub fn hash_algorithm(mut self, v: Byte) -> Self { + self.hash_algorithm = v; + self + } + pub fn funding_tx_hash(mut self, v: Byte32) -> Self { + self.funding_tx_hash = v; + self + } + pub fn next_hop(mut self, v: PubkeyOpt) -> Self { + self.next_hop = v; + self + } +} +impl molecule::prelude::Builder for PaymentHopDataBuilder { + type Entity = PaymentHopData; + const NAME: &'static str = "PaymentHopDataBuilder"; + fn expected_length(&self) -> usize { + molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1) + + self.amount.as_slice().len() + + self.expiry.as_slice().len() + + self.payment_preimage.as_slice().len() + + self.hash_algorithm.as_slice().len() + + self.funding_tx_hash.as_slice().len() + + self.next_hop.as_slice().len() + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + let mut total_size = molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1); + let mut offsets = Vec::with_capacity(Self::FIELD_COUNT); + offsets.push(total_size); + total_size += self.amount.as_slice().len(); + offsets.push(total_size); + total_size += self.expiry.as_slice().len(); + offsets.push(total_size); + total_size += self.payment_preimage.as_slice().len(); + offsets.push(total_size); + total_size += self.hash_algorithm.as_slice().len(); + offsets.push(total_size); + total_size += self.funding_tx_hash.as_slice().len(); + offsets.push(total_size); + total_size += self.next_hop.as_slice().len(); + writer.write_all(&molecule::pack_number(total_size as molecule::Number))?; + for offset in offsets.into_iter() { + writer.write_all(&molecule::pack_number(offset as molecule::Number))?; + } + writer.write_all(self.amount.as_slice())?; + writer.write_all(self.expiry.as_slice())?; + writer.write_all(self.payment_preimage.as_slice())?; + writer.write_all(self.hash_algorithm.as_slice())?; + writer.write_all(self.funding_tx_hash.as_slice())?; + writer.write_all(self.next_hop.as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + PaymentHopData::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct ChannelUpdate(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for ChannelUpdate { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for ChannelUpdate { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for ChannelUpdate { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "signature", self.signature())?; + write!(f, ", {}: {}", "chain_hash", self.chain_hash())?; + write!(f, ", {}: {}", "channel_outpoint", self.channel_outpoint())?; + write!(f, ", {}: {}", "timestamp", self.timestamp())?; + write!(f, ", {}: {}", "message_flags", self.message_flags())?; + write!(f, ", {}: {}", "channel_flags", self.channel_flags())?; + write!(f, ", {}: {}", "tlc_expiry_delta", self.tlc_expiry_delta())?; + write!(f, ", {}: {}", "tlc_minimum_value", self.tlc_minimum_value())?; + write!(f, ", {}: {}", "tlc_maximum_value", self.tlc_maximum_value())?; + write!( + f, + ", {}: {}", + "tlc_fee_proportional_millionths", + self.tlc_fee_proportional_millionths() + )?; + write!(f, " }}") + } +} +impl ::core::default::Default for ChannelUpdate { + fn default() -> Self { + let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); + ChannelUpdate::new_unchecked(v) + } +} +impl ChannelUpdate { + const DEFAULT_VALUE: [u8; 204] = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + pub const TOTAL_SIZE: usize = 204; + pub const FIELD_SIZES: [usize; 10] = [64, 32, 36, 8, 4, 4, 8, 16, 16, 16]; + pub const FIELD_COUNT: usize = 10; + pub fn signature(&self) -> EcdsaSignature { + EcdsaSignature::new_unchecked(self.0.slice(0..64)) + } + pub fn chain_hash(&self) -> Byte32 { + Byte32::new_unchecked(self.0.slice(64..96)) + } + pub fn channel_outpoint(&self) -> OutPoint { + OutPoint::new_unchecked(self.0.slice(96..132)) + } + pub fn timestamp(&self) -> Uint64 { + Uint64::new_unchecked(self.0.slice(132..140)) + } + pub fn message_flags(&self) -> Uint32 { + Uint32::new_unchecked(self.0.slice(140..144)) + } + pub fn channel_flags(&self) -> Uint32 { + Uint32::new_unchecked(self.0.slice(144..148)) + } + pub fn tlc_expiry_delta(&self) -> Uint64 { + Uint64::new_unchecked(self.0.slice(148..156)) + } + pub fn tlc_minimum_value(&self) -> Uint128 { + Uint128::new_unchecked(self.0.slice(156..172)) + } + pub fn tlc_maximum_value(&self) -> Uint128 { + Uint128::new_unchecked(self.0.slice(172..188)) + } + pub fn tlc_fee_proportional_millionths(&self) -> Uint128 { + Uint128::new_unchecked(self.0.slice(188..204)) + } + pub fn as_reader<'r>(&'r self) -> ChannelUpdateReader<'r> { + ChannelUpdateReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for ChannelUpdate { + type Builder = ChannelUpdateBuilder; + const NAME: &'static str = "ChannelUpdate"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + ChannelUpdate(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ChannelUpdateReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ChannelUpdateReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder() + .signature(self.signature()) + .chain_hash(self.chain_hash()) + .channel_outpoint(self.channel_outpoint()) + .timestamp(self.timestamp()) + .message_flags(self.message_flags()) + .channel_flags(self.channel_flags()) + .tlc_expiry_delta(self.tlc_expiry_delta()) + .tlc_minimum_value(self.tlc_minimum_value()) + .tlc_maximum_value(self.tlc_maximum_value()) + .tlc_fee_proportional_millionths(self.tlc_fee_proportional_millionths()) + } +} +#[derive(Clone, Copy)] +pub struct ChannelUpdateReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for ChannelUpdateReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for ChannelUpdateReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for ChannelUpdateReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "signature", self.signature())?; + write!(f, ", {}: {}", "chain_hash", self.chain_hash())?; + write!(f, ", {}: {}", "channel_outpoint", self.channel_outpoint())?; + write!(f, ", {}: {}", "timestamp", self.timestamp())?; + write!(f, ", {}: {}", "message_flags", self.message_flags())?; + write!(f, ", {}: {}", "channel_flags", self.channel_flags())?; + write!(f, ", {}: {}", "tlc_expiry_delta", self.tlc_expiry_delta())?; + write!(f, ", {}: {}", "tlc_minimum_value", self.tlc_minimum_value())?; + write!(f, ", {}: {}", "tlc_maximum_value", self.tlc_maximum_value())?; + write!( + f, + ", {}: {}", + "tlc_fee_proportional_millionths", + self.tlc_fee_proportional_millionths() + )?; + write!(f, " }}") + } +} +impl<'r> ChannelUpdateReader<'r> { + pub const TOTAL_SIZE: usize = 204; + pub const FIELD_SIZES: [usize; 10] = [64, 32, 36, 8, 4, 4, 8, 16, 16, 16]; + pub const FIELD_COUNT: usize = 10; + pub fn signature(&self) -> EcdsaSignatureReader<'r> { + EcdsaSignatureReader::new_unchecked(&self.as_slice()[0..64]) + } + pub fn chain_hash(&self) -> Byte32Reader<'r> { + Byte32Reader::new_unchecked(&self.as_slice()[64..96]) + } + pub fn channel_outpoint(&self) -> OutPointReader<'r> { + OutPointReader::new_unchecked(&self.as_slice()[96..132]) + } + pub fn timestamp(&self) -> Uint64Reader<'r> { + Uint64Reader::new_unchecked(&self.as_slice()[132..140]) + } + pub fn message_flags(&self) -> Uint32Reader<'r> { + Uint32Reader::new_unchecked(&self.as_slice()[140..144]) + } + pub fn channel_flags(&self) -> Uint32Reader<'r> { + Uint32Reader::new_unchecked(&self.as_slice()[144..148]) + } + pub fn tlc_expiry_delta(&self) -> Uint64Reader<'r> { + Uint64Reader::new_unchecked(&self.as_slice()[148..156]) + } + pub fn tlc_minimum_value(&self) -> Uint128Reader<'r> { + Uint128Reader::new_unchecked(&self.as_slice()[156..172]) + } + pub fn tlc_maximum_value(&self) -> Uint128Reader<'r> { + Uint128Reader::new_unchecked(&self.as_slice()[172..188]) + } + pub fn tlc_fee_proportional_millionths(&self) -> Uint128Reader<'r> { + Uint128Reader::new_unchecked(&self.as_slice()[188..204]) + } +} +impl<'r> molecule::prelude::Reader<'r> for ChannelUpdateReader<'r> { + type Entity = ChannelUpdate; + const NAME: &'static str = "ChannelUpdateReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + ChannelUpdateReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], _compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len != Self::TOTAL_SIZE { + return ve!(Self, TotalSizeNotMatch, Self::TOTAL_SIZE, slice_len); + } + Ok(()) + } +} +#[derive(Clone, Debug, Default)] +pub struct ChannelUpdateBuilder { + pub(crate) signature: EcdsaSignature, + pub(crate) chain_hash: Byte32, + pub(crate) channel_outpoint: OutPoint, + pub(crate) timestamp: Uint64, + pub(crate) message_flags: Uint32, + pub(crate) channel_flags: Uint32, + pub(crate) tlc_expiry_delta: Uint64, + pub(crate) tlc_minimum_value: Uint128, + pub(crate) tlc_maximum_value: Uint128, + pub(crate) tlc_fee_proportional_millionths: Uint128, +} +impl ChannelUpdateBuilder { + pub const TOTAL_SIZE: usize = 204; + pub const FIELD_SIZES: [usize; 10] = [64, 32, 36, 8, 4, 4, 8, 16, 16, 16]; + pub const FIELD_COUNT: usize = 10; + pub fn signature(mut self, v: EcdsaSignature) -> Self { + self.signature = v; + self + } + pub fn chain_hash(mut self, v: Byte32) -> Self { + self.chain_hash = v; + self + } + pub fn channel_outpoint(mut self, v: OutPoint) -> Self { + self.channel_outpoint = v; + self + } + pub fn timestamp(mut self, v: Uint64) -> Self { + self.timestamp = v; + self + } + pub fn message_flags(mut self, v: Uint32) -> Self { + self.message_flags = v; + self + } + pub fn channel_flags(mut self, v: Uint32) -> Self { + self.channel_flags = v; + self + } + pub fn tlc_expiry_delta(mut self, v: Uint64) -> Self { + self.tlc_expiry_delta = v; + self + } + pub fn tlc_minimum_value(mut self, v: Uint128) -> Self { + self.tlc_minimum_value = v; + self + } + pub fn tlc_maximum_value(mut self, v: Uint128) -> Self { + self.tlc_maximum_value = v; + self + } + pub fn tlc_fee_proportional_millionths(mut self, v: Uint128) -> Self { + self.tlc_fee_proportional_millionths = v; + self + } +} +impl molecule::prelude::Builder for ChannelUpdateBuilder { + type Entity = ChannelUpdate; + const NAME: &'static str = "ChannelUpdateBuilder"; + fn expected_length(&self) -> usize { + Self::TOTAL_SIZE + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + writer.write_all(self.signature.as_slice())?; + writer.write_all(self.chain_hash.as_slice())?; + writer.write_all(self.channel_outpoint.as_slice())?; + writer.write_all(self.timestamp.as_slice())?; + writer.write_all(self.message_flags.as_slice())?; + writer.write_all(self.channel_flags.as_slice())?; + writer.write_all(self.tlc_expiry_delta.as_slice())?; + writer.write_all(self.tlc_minimum_value.as_slice())?; + writer.write_all(self.tlc_maximum_value.as_slice())?; + writer.write_all(self.tlc_fee_proportional_millionths.as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + ChannelUpdate::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct ChannelUpdateOpt(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for ChannelUpdateOpt { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for ChannelUpdateOpt { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for ChannelUpdateOpt { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(v) = self.to_opt() { + write!(f, "{}(Some({}))", Self::NAME, v) + } else { + write!(f, "{}(None)", Self::NAME) + } + } +} +impl ::core::default::Default for ChannelUpdateOpt { + fn default() -> Self { + let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); + ChannelUpdateOpt::new_unchecked(v) + } +} +impl ChannelUpdateOpt { + const DEFAULT_VALUE: [u8; 0] = []; + pub fn is_none(&self) -> bool { + self.0.is_empty() + } + pub fn is_some(&self) -> bool { + !self.0.is_empty() + } + pub fn to_opt(&self) -> Option { + if self.is_none() { + None + } else { + Some(ChannelUpdate::new_unchecked(self.0.clone())) + } + } + pub fn as_reader<'r>(&'r self) -> ChannelUpdateOptReader<'r> { + ChannelUpdateOptReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for ChannelUpdateOpt { + type Builder = ChannelUpdateOptBuilder; + const NAME: &'static str = "ChannelUpdateOpt"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + ChannelUpdateOpt(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ChannelUpdateOptReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ChannelUpdateOptReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().set(self.to_opt()) + } +} +#[derive(Clone, Copy)] +pub struct ChannelUpdateOptReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for ChannelUpdateOptReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for ChannelUpdateOptReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for ChannelUpdateOptReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(v) = self.to_opt() { + write!(f, "{}(Some({}))", Self::NAME, v) + } else { + write!(f, "{}(None)", Self::NAME) + } + } +} +impl<'r> ChannelUpdateOptReader<'r> { + pub fn is_none(&self) -> bool { + self.0.is_empty() + } + pub fn is_some(&self) -> bool { + !self.0.is_empty() + } + pub fn to_opt(&self) -> Option> { + if self.is_none() { + None + } else { + Some(ChannelUpdateReader::new_unchecked(self.as_slice())) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for ChannelUpdateOptReader<'r> { + type Entity = ChannelUpdateOpt; + const NAME: &'static str = "ChannelUpdateOptReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + ChannelUpdateOptReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + if !slice.is_empty() { + ChannelUpdateReader::verify(&slice[..], compatible)?; + } + Ok(()) + } +} +#[derive(Clone, Debug, Default)] +pub struct ChannelUpdateOptBuilder(pub(crate) Option); +impl ChannelUpdateOptBuilder { + pub fn set(mut self, v: Option) -> Self { + self.0 = v; + self + } +} +impl molecule::prelude::Builder for ChannelUpdateOptBuilder { + type Entity = ChannelUpdateOpt; + const NAME: &'static str = "ChannelUpdateOptBuilder"; + fn expected_length(&self) -> usize { + self.0 + .as_ref() + .map(|ref inner| inner.as_slice().len()) + .unwrap_or(0) + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + self.0 + .as_ref() + .map(|ref inner| writer.write_all(inner.as_slice())) + .unwrap_or(Ok(())) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + ChannelUpdateOpt::new_unchecked(inner.into()) + } +} +impl From for ChannelUpdateOpt { + fn from(value: ChannelUpdate) -> Self { + Self::new_builder().set(Some(value)).build() + } +} +#[derive(Clone)] +pub struct ChannelFailed(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for ChannelFailed { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for ChannelFailed { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for ChannelFailed { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "channel_outpoint", self.channel_outpoint())?; + write!(f, ", {}: {}", "channel_update", self.channel_update())?; + write!(f, ", {}: {}", "node_id", self.node_id())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl ::core::default::Default for ChannelFailed { + fn default() -> Self { + let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); + ChannelFailed::new_unchecked(v) + } +} +impl ChannelFailed { + const DEFAULT_VALUE: [u8; 85] = [ + 85, 0, 0, 0, 16, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + pub const FIELD_COUNT: usize = 3; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn channel_outpoint(&self) -> OutPoint { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + OutPoint::new_unchecked(self.0.slice(start..end)) + } + pub fn channel_update(&self) -> ChannelUpdateOpt { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + let end = molecule::unpack_number(&slice[12..]) as usize; + ChannelUpdateOpt::new_unchecked(self.0.slice(start..end)) + } + pub fn node_id(&self) -> Pubkey { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[12..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[16..]) as usize; + Pubkey::new_unchecked(self.0.slice(start..end)) + } else { + Pubkey::new_unchecked(self.0.slice(start..)) + } + } + pub fn as_reader<'r>(&'r self) -> ChannelFailedReader<'r> { + ChannelFailedReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for ChannelFailed { + type Builder = ChannelFailedBuilder; + const NAME: &'static str = "ChannelFailed"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + ChannelFailed(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ChannelFailedReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + ChannelFailedReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder() + .channel_outpoint(self.channel_outpoint()) + .channel_update(self.channel_update()) + .node_id(self.node_id()) + } +} +#[derive(Clone, Copy)] +pub struct ChannelFailedReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for ChannelFailedReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for ChannelFailedReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for ChannelFailedReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "channel_outpoint", self.channel_outpoint())?; + write!(f, ", {}: {}", "channel_update", self.channel_update())?; + write!(f, ", {}: {}", "node_id", self.node_id())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl<'r> ChannelFailedReader<'r> { + pub const FIELD_COUNT: usize = 3; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn channel_outpoint(&self) -> OutPointReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + OutPointReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn channel_update(&self) -> ChannelUpdateOptReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + let end = molecule::unpack_number(&slice[12..]) as usize; + ChannelUpdateOptReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn node_id(&self) -> PubkeyReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[12..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[16..]) as usize; + PubkeyReader::new_unchecked(&self.as_slice()[start..end]) + } else { + PubkeyReader::new_unchecked(&self.as_slice()[start..]) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for ChannelFailedReader<'r> { + type Entity = ChannelFailed; + const NAME: &'static str = "ChannelFailedReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + ChannelFailedReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len < molecule::NUMBER_SIZE { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); + } + let total_size = molecule::unpack_number(slice) as usize; + if slice_len != total_size { + return ve!(Self, TotalSizeNotMatch, total_size, slice_len); + } + if slice_len < molecule::NUMBER_SIZE * 2 { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE * 2, slice_len); + } + let offset_first = molecule::unpack_number(&slice[molecule::NUMBER_SIZE..]) as usize; + if offset_first % molecule::NUMBER_SIZE != 0 || offset_first < molecule::NUMBER_SIZE * 2 { + return ve!(Self, OffsetsNotMatch); + } + if slice_len < offset_first { + return ve!(Self, HeaderIsBroken, offset_first, slice_len); + } + let field_count = offset_first / molecule::NUMBER_SIZE - 1; + if field_count < Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + } else if !compatible && field_count > Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + }; + let mut offsets: Vec = slice[molecule::NUMBER_SIZE..offset_first] + .chunks_exact(molecule::NUMBER_SIZE) + .map(|x| molecule::unpack_number(x) as usize) + .collect(); + offsets.push(total_size); + if offsets.windows(2).any(|i| i[0] > i[1]) { + return ve!(Self, OffsetsNotMatch); + } + OutPointReader::verify(&slice[offsets[0]..offsets[1]], compatible)?; + ChannelUpdateOptReader::verify(&slice[offsets[1]..offsets[2]], compatible)?; + PubkeyReader::verify(&slice[offsets[2]..offsets[3]], compatible)?; + Ok(()) + } +} +#[derive(Clone, Debug, Default)] +pub struct ChannelFailedBuilder { + pub(crate) channel_outpoint: OutPoint, + pub(crate) channel_update: ChannelUpdateOpt, + pub(crate) node_id: Pubkey, +} +impl ChannelFailedBuilder { + pub const FIELD_COUNT: usize = 3; + pub fn channel_outpoint(mut self, v: OutPoint) -> Self { + self.channel_outpoint = v; + self + } + pub fn channel_update(mut self, v: ChannelUpdateOpt) -> Self { + self.channel_update = v; + self + } + pub fn node_id(mut self, v: Pubkey) -> Self { + self.node_id = v; + self + } +} +impl molecule::prelude::Builder for ChannelFailedBuilder { + type Entity = ChannelFailed; + const NAME: &'static str = "ChannelFailedBuilder"; + fn expected_length(&self) -> usize { + molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1) + + self.channel_outpoint.as_slice().len() + + self.channel_update.as_slice().len() + + self.node_id.as_slice().len() + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + let mut total_size = molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1); + let mut offsets = Vec::with_capacity(Self::FIELD_COUNT); + offsets.push(total_size); + total_size += self.channel_outpoint.as_slice().len(); + offsets.push(total_size); + total_size += self.channel_update.as_slice().len(); + offsets.push(total_size); + total_size += self.node_id.as_slice().len(); + writer.write_all(&molecule::pack_number(total_size as molecule::Number))?; + for offset in offsets.into_iter() { + writer.write_all(&molecule::pack_number(offset as molecule::Number))?; + } + writer.write_all(self.channel_outpoint.as_slice())?; + writer.write_all(self.channel_update.as_slice())?; + writer.write_all(self.node_id.as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + ChannelFailed::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct NodeFailed(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for NodeFailed { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for NodeFailed { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for NodeFailed { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "node_id", self.node_id())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl ::core::default::Default for NodeFailed { + fn default() -> Self { + let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); + NodeFailed::new_unchecked(v) + } +} +impl NodeFailed { + const DEFAULT_VALUE: [u8; 41] = [ + 41, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + pub const FIELD_COUNT: usize = 1; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn node_id(&self) -> Pubkey { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[8..]) as usize; + Pubkey::new_unchecked(self.0.slice(start..end)) + } else { + Pubkey::new_unchecked(self.0.slice(start..)) + } + } + pub fn as_reader<'r>(&'r self) -> NodeFailedReader<'r> { + NodeFailedReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for NodeFailed { + type Builder = NodeFailedBuilder; + const NAME: &'static str = "NodeFailed"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + NodeFailed(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + NodeFailedReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + NodeFailedReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().node_id(self.node_id()) + } +} +#[derive(Clone, Copy)] +pub struct NodeFailedReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for NodeFailedReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for NodeFailedReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for NodeFailedReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "node_id", self.node_id())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl<'r> NodeFailedReader<'r> { + pub const FIELD_COUNT: usize = 1; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn node_id(&self) -> PubkeyReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[8..]) as usize; + PubkeyReader::new_unchecked(&self.as_slice()[start..end]) + } else { + PubkeyReader::new_unchecked(&self.as_slice()[start..]) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for NodeFailedReader<'r> { + type Entity = NodeFailed; + const NAME: &'static str = "NodeFailedReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + NodeFailedReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len < molecule::NUMBER_SIZE { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); + } + let total_size = molecule::unpack_number(slice) as usize; + if slice_len != total_size { + return ve!(Self, TotalSizeNotMatch, total_size, slice_len); + } + if slice_len < molecule::NUMBER_SIZE * 2 { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE * 2, slice_len); + } + let offset_first = molecule::unpack_number(&slice[molecule::NUMBER_SIZE..]) as usize; + if offset_first % molecule::NUMBER_SIZE != 0 || offset_first < molecule::NUMBER_SIZE * 2 { + return ve!(Self, OffsetsNotMatch); + } + if slice_len < offset_first { + return ve!(Self, HeaderIsBroken, offset_first, slice_len); + } + let field_count = offset_first / molecule::NUMBER_SIZE - 1; + if field_count < Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + } else if !compatible && field_count > Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + }; + let mut offsets: Vec = slice[molecule::NUMBER_SIZE..offset_first] + .chunks_exact(molecule::NUMBER_SIZE) + .map(|x| molecule::unpack_number(x) as usize) + .collect(); + offsets.push(total_size); + if offsets.windows(2).any(|i| i[0] > i[1]) { + return ve!(Self, OffsetsNotMatch); + } + PubkeyReader::verify(&slice[offsets[0]..offsets[1]], compatible)?; + Ok(()) + } +} +#[derive(Clone, Debug, Default)] +pub struct NodeFailedBuilder { + pub(crate) node_id: Pubkey, +} +impl NodeFailedBuilder { + pub const FIELD_COUNT: usize = 1; + pub fn node_id(mut self, v: Pubkey) -> Self { + self.node_id = v; + self + } +} +impl molecule::prelude::Builder for NodeFailedBuilder { + type Entity = NodeFailed; + const NAME: &'static str = "NodeFailedBuilder"; + fn expected_length(&self) -> usize { + molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1) + self.node_id.as_slice().len() + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + let mut total_size = molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1); + let mut offsets = Vec::with_capacity(Self::FIELD_COUNT); + offsets.push(total_size); + total_size += self.node_id.as_slice().len(); + writer.write_all(&molecule::pack_number(total_size as molecule::Number))?; + for offset in offsets.into_iter() { + writer.write_all(&molecule::pack_number(offset as molecule::Number))?; + } + writer.write_all(self.node_id.as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + NodeFailed::new_unchecked(inner.into()) + } +} +#[derive(Clone)] +pub struct TlcErrData(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for TlcErrData { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for TlcErrData { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for TlcErrData { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}(", Self::NAME)?; + self.to_enum().display_inner(f)?; + write!(f, ")") + } +} +impl ::core::default::Default for TlcErrData { + fn default() -> Self { + let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); + TlcErrData::new_unchecked(v) + } +} +impl TlcErrData { + const DEFAULT_VALUE: [u8; 89] = [ + 0, 0, 0, 0, 85, 0, 0, 0, 16, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + pub const ITEMS_COUNT: usize = 2; + pub fn item_id(&self) -> molecule::Number { + molecule::unpack_number(self.as_slice()) + } + pub fn to_enum(&self) -> TlcErrDataUnion { + let inner = self.0.slice(molecule::NUMBER_SIZE..); + match self.item_id() { + 0 => ChannelFailed::new_unchecked(inner).into(), + 1 => NodeFailed::new_unchecked(inner).into(), + _ => panic!("{}: invalid data", Self::NAME), + } + } + pub fn as_reader<'r>(&'r self) -> TlcErrDataReader<'r> { + TlcErrDataReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for TlcErrData { + type Builder = TlcErrDataBuilder; + const NAME: &'static str = "TlcErrData"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + TlcErrData(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + TlcErrDataReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + TlcErrDataReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().set(self.to_enum()) + } +} +#[derive(Clone, Copy)] +pub struct TlcErrDataReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for TlcErrDataReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for TlcErrDataReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for TlcErrDataReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}(", Self::NAME)?; + self.to_enum().display_inner(f)?; + write!(f, ")") + } +} +impl<'r> TlcErrDataReader<'r> { + pub const ITEMS_COUNT: usize = 2; + pub fn item_id(&self) -> molecule::Number { + molecule::unpack_number(self.as_slice()) + } + pub fn to_enum(&self) -> TlcErrDataUnionReader<'r> { + let inner = &self.as_slice()[molecule::NUMBER_SIZE..]; + match self.item_id() { + 0 => ChannelFailedReader::new_unchecked(inner).into(), + 1 => NodeFailedReader::new_unchecked(inner).into(), + _ => panic!("{}: invalid data", Self::NAME), + } + } +} +impl<'r> molecule::prelude::Reader<'r> for TlcErrDataReader<'r> { + type Entity = TlcErrData; + const NAME: &'static str = "TlcErrDataReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + TlcErrDataReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len < molecule::NUMBER_SIZE { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); + } + let item_id = molecule::unpack_number(slice); + let inner_slice = &slice[molecule::NUMBER_SIZE..]; + match item_id { + 0 => ChannelFailedReader::verify(inner_slice, compatible), + 1 => NodeFailedReader::verify(inner_slice, compatible), + _ => ve!(Self, UnknownItem, Self::ITEMS_COUNT, item_id), + }?; + Ok(()) + } +} +#[derive(Clone, Debug, Default)] +pub struct TlcErrDataBuilder(pub(crate) TlcErrDataUnion); +impl TlcErrDataBuilder { + pub const ITEMS_COUNT: usize = 2; + pub fn set(mut self, v: I) -> Self + where + I: ::core::convert::Into, + { + self.0 = v.into(); + self + } +} +impl molecule::prelude::Builder for TlcErrDataBuilder { + type Entity = TlcErrData; + const NAME: &'static str = "TlcErrDataBuilder"; + fn expected_length(&self) -> usize { + molecule::NUMBER_SIZE + self.0.as_slice().len() + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + writer.write_all(&molecule::pack_number(self.0.item_id()))?; + writer.write_all(self.0.as_slice()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + TlcErrData::new_unchecked(inner.into()) + } +} +#[derive(Debug, Clone)] +pub enum TlcErrDataUnion { + ChannelFailed(ChannelFailed), + NodeFailed(NodeFailed), +} +#[derive(Debug, Clone, Copy)] +pub enum TlcErrDataUnionReader<'r> { + ChannelFailed(ChannelFailedReader<'r>), + NodeFailed(NodeFailedReader<'r>), +} +impl ::core::default::Default for TlcErrDataUnion { + fn default() -> Self { + TlcErrDataUnion::ChannelFailed(::core::default::Default::default()) + } +} +impl ::core::fmt::Display for TlcErrDataUnion { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + TlcErrDataUnion::ChannelFailed(ref item) => { + write!(f, "{}::{}({})", Self::NAME, ChannelFailed::NAME, item) + } + TlcErrDataUnion::NodeFailed(ref item) => { + write!(f, "{}::{}({})", Self::NAME, NodeFailed::NAME, item) + } + } + } +} +impl<'r> ::core::fmt::Display for TlcErrDataUnionReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + TlcErrDataUnionReader::ChannelFailed(ref item) => { + write!(f, "{}::{}({})", Self::NAME, ChannelFailed::NAME, item) + } + TlcErrDataUnionReader::NodeFailed(ref item) => { + write!(f, "{}::{}({})", Self::NAME, NodeFailed::NAME, item) + } + } + } +} +impl TlcErrDataUnion { + pub(crate) fn display_inner(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + TlcErrDataUnion::ChannelFailed(ref item) => write!(f, "{}", item), + TlcErrDataUnion::NodeFailed(ref item) => write!(f, "{}", item), + } + } +} +impl<'r> TlcErrDataUnionReader<'r> { + pub(crate) fn display_inner(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + TlcErrDataUnionReader::ChannelFailed(ref item) => write!(f, "{}", item), + TlcErrDataUnionReader::NodeFailed(ref item) => write!(f, "{}", item), + } + } +} +impl ::core::convert::From for TlcErrDataUnion { + fn from(item: ChannelFailed) -> Self { + TlcErrDataUnion::ChannelFailed(item) + } +} +impl ::core::convert::From for TlcErrDataUnion { + fn from(item: NodeFailed) -> Self { + TlcErrDataUnion::NodeFailed(item) + } +} +impl<'r> ::core::convert::From> for TlcErrDataUnionReader<'r> { + fn from(item: ChannelFailedReader<'r>) -> Self { + TlcErrDataUnionReader::ChannelFailed(item) + } +} +impl<'r> ::core::convert::From> for TlcErrDataUnionReader<'r> { + fn from(item: NodeFailedReader<'r>) -> Self { + TlcErrDataUnionReader::NodeFailed(item) + } +} +impl TlcErrDataUnion { + pub const NAME: &'static str = "TlcErrDataUnion"; + pub fn as_bytes(&self) -> molecule::bytes::Bytes { + match self { + TlcErrDataUnion::ChannelFailed(item) => item.as_bytes(), + TlcErrDataUnion::NodeFailed(item) => item.as_bytes(), + } + } + pub fn as_slice(&self) -> &[u8] { + match self { + TlcErrDataUnion::ChannelFailed(item) => item.as_slice(), + TlcErrDataUnion::NodeFailed(item) => item.as_slice(), + } + } + pub fn item_id(&self) -> molecule::Number { + match self { + TlcErrDataUnion::ChannelFailed(_) => 0, + TlcErrDataUnion::NodeFailed(_) => 1, + } + } + pub fn item_name(&self) -> &str { + match self { + TlcErrDataUnion::ChannelFailed(_) => "ChannelFailed", + TlcErrDataUnion::NodeFailed(_) => "NodeFailed", + } + } + pub fn as_reader<'r>(&'r self) -> TlcErrDataUnionReader<'r> { + match self { + TlcErrDataUnion::ChannelFailed(item) => item.as_reader().into(), + TlcErrDataUnion::NodeFailed(item) => item.as_reader().into(), + } + } +} +impl<'r> TlcErrDataUnionReader<'r> { + pub const NAME: &'r str = "TlcErrDataUnionReader"; + pub fn as_slice(&self) -> &'r [u8] { + match self { + TlcErrDataUnionReader::ChannelFailed(item) => item.as_slice(), + TlcErrDataUnionReader::NodeFailed(item) => item.as_slice(), + } + } + pub fn item_id(&self) -> molecule::Number { + match self { + TlcErrDataUnionReader::ChannelFailed(_) => 0, + TlcErrDataUnionReader::NodeFailed(_) => 1, + } + } + pub fn item_name(&self) -> &str { + match self { + TlcErrDataUnionReader::ChannelFailed(_) => "ChannelFailed", + TlcErrDataUnionReader::NodeFailed(_) => "NodeFailed", + } + } +} +impl From for TlcErrData { + fn from(value: ChannelFailed) -> Self { + Self::new_builder().set(value).build() + } +} +impl From for TlcErrData { + fn from(value: NodeFailed) -> Self { + Self::new_builder().set(value).build() + } +} +#[derive(Clone)] +pub struct TlcErrDataOpt(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for TlcErrDataOpt { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for TlcErrDataOpt { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for TlcErrDataOpt { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(v) = self.to_opt() { + write!(f, "{}(Some({}))", Self::NAME, v) + } else { + write!(f, "{}(None)", Self::NAME) + } + } +} +impl ::core::default::Default for TlcErrDataOpt { + fn default() -> Self { + let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); + TlcErrDataOpt::new_unchecked(v) + } +} +impl TlcErrDataOpt { + const DEFAULT_VALUE: [u8; 0] = []; + pub fn is_none(&self) -> bool { + self.0.is_empty() + } + pub fn is_some(&self) -> bool { + !self.0.is_empty() + } + pub fn to_opt(&self) -> Option { + if self.is_none() { + None + } else { + Some(TlcErrData::new_unchecked(self.0.clone())) + } + } + pub fn as_reader<'r>(&'r self) -> TlcErrDataOptReader<'r> { + TlcErrDataOptReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for TlcErrDataOpt { + type Builder = TlcErrDataOptBuilder; + const NAME: &'static str = "TlcErrDataOpt"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + TlcErrDataOpt(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + TlcErrDataOptReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + TlcErrDataOptReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().set(self.to_opt()) + } +} +#[derive(Clone, Copy)] +pub struct TlcErrDataOptReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for TlcErrDataOptReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for TlcErrDataOptReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for TlcErrDataOptReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(v) = self.to_opt() { + write!(f, "{}(Some({}))", Self::NAME, v) + } else { + write!(f, "{}(None)", Self::NAME) + } + } +} +impl<'r> TlcErrDataOptReader<'r> { + pub fn is_none(&self) -> bool { + self.0.is_empty() + } + pub fn is_some(&self) -> bool { + !self.0.is_empty() + } + pub fn to_opt(&self) -> Option> { + if self.is_none() { + None + } else { + Some(TlcErrDataReader::new_unchecked(self.as_slice())) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for TlcErrDataOptReader<'r> { + type Entity = TlcErrDataOpt; + const NAME: &'static str = "TlcErrDataOptReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + TlcErrDataOptReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + if !slice.is_empty() { + TlcErrDataReader::verify(&slice[..], compatible)?; + } + Ok(()) + } +} +#[derive(Clone, Debug, Default)] +pub struct TlcErrDataOptBuilder(pub(crate) Option); +impl TlcErrDataOptBuilder { + pub fn set(mut self, v: Option) -> Self { + self.0 = v; + self + } +} +impl molecule::prelude::Builder for TlcErrDataOptBuilder { + type Entity = TlcErrDataOpt; + const NAME: &'static str = "TlcErrDataOptBuilder"; + fn expected_length(&self) -> usize { + self.0 + .as_ref() + .map(|ref inner| inner.as_slice().len()) + .unwrap_or(0) + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + self.0 + .as_ref() + .map(|ref inner| writer.write_all(inner.as_slice())) + .unwrap_or(Ok(())) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + TlcErrDataOpt::new_unchecked(inner.into()) + } +} +impl From for TlcErrDataOpt { + fn from(value: TlcErrData) -> Self { + Self::new_builder().set(Some(value)).build() + } +} +#[derive(Clone)] +pub struct TlcErr(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for TlcErr { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for TlcErr { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for TlcErr { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "error_code", self.error_code())?; + write!(f, ", {}: {}", "extra_data", self.extra_data())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl ::core::default::Default for TlcErr { + fn default() -> Self { + let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); + TlcErr::new_unchecked(v) + } +} +impl TlcErr { + const DEFAULT_VALUE: [u8; 14] = [14, 0, 0, 0, 12, 0, 0, 0, 14, 0, 0, 0, 0, 0]; + pub const FIELD_COUNT: usize = 2; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn error_code(&self) -> Uint16 { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + Uint16::new_unchecked(self.0.slice(start..end)) + } + pub fn extra_data(&self) -> TlcErrDataOpt { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[12..]) as usize; + TlcErrDataOpt::new_unchecked(self.0.slice(start..end)) + } else { + TlcErrDataOpt::new_unchecked(self.0.slice(start..)) + } + } + pub fn as_reader<'r>(&'r self) -> TlcErrReader<'r> { + TlcErrReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for TlcErr { + type Builder = TlcErrBuilder; + const NAME: &'static str = "TlcErr"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + TlcErr(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + TlcErrReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + TlcErrReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder() + .error_code(self.error_code()) + .extra_data(self.extra_data()) + } +} +#[derive(Clone, Copy)] +pub struct TlcErrReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for TlcErrReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for TlcErrReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for TlcErrReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "error_code", self.error_code())?; + write!(f, ", {}: {}", "extra_data", self.extra_data())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl<'r> TlcErrReader<'r> { + pub const FIELD_COUNT: usize = 2; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn error_code(&self) -> Uint16Reader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + Uint16Reader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn extra_data(&self) -> TlcErrDataOptReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[12..]) as usize; + TlcErrDataOptReader::new_unchecked(&self.as_slice()[start..end]) + } else { + TlcErrDataOptReader::new_unchecked(&self.as_slice()[start..]) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for TlcErrReader<'r> { + type Entity = TlcErr; + const NAME: &'static str = "TlcErrReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + TlcErrReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len < molecule::NUMBER_SIZE { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); + } + let total_size = molecule::unpack_number(slice) as usize; + if slice_len != total_size { + return ve!(Self, TotalSizeNotMatch, total_size, slice_len); + } + if slice_len < molecule::NUMBER_SIZE * 2 { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE * 2, slice_len); + } + let offset_first = molecule::unpack_number(&slice[molecule::NUMBER_SIZE..]) as usize; + if offset_first % molecule::NUMBER_SIZE != 0 || offset_first < molecule::NUMBER_SIZE * 2 { + return ve!(Self, OffsetsNotMatch); + } + if slice_len < offset_first { + return ve!(Self, HeaderIsBroken, offset_first, slice_len); + } + let field_count = offset_first / molecule::NUMBER_SIZE - 1; + if field_count < Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + } else if !compatible && field_count > Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + }; + let mut offsets: Vec = slice[molecule::NUMBER_SIZE..offset_first] + .chunks_exact(molecule::NUMBER_SIZE) + .map(|x| molecule::unpack_number(x) as usize) + .collect(); + offsets.push(total_size); + if offsets.windows(2).any(|i| i[0] > i[1]) { + return ve!(Self, OffsetsNotMatch); + } + Uint16Reader::verify(&slice[offsets[0]..offsets[1]], compatible)?; + TlcErrDataOptReader::verify(&slice[offsets[1]..offsets[2]], compatible)?; + Ok(()) + } +} +#[derive(Clone, Debug, Default)] +pub struct TlcErrBuilder { + pub(crate) error_code: Uint16, + pub(crate) extra_data: TlcErrDataOpt, +} +impl TlcErrBuilder { + pub const FIELD_COUNT: usize = 2; + pub fn error_code(mut self, v: Uint16) -> Self { + self.error_code = v; + self + } + pub fn extra_data(mut self, v: TlcErrDataOpt) -> Self { + self.extra_data = v; + self + } +} +impl molecule::prelude::Builder for TlcErrBuilder { + type Entity = TlcErr; + const NAME: &'static str = "TlcErrBuilder"; + fn expected_length(&self) -> usize { + molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1) + + self.error_code.as_slice().len() + + self.extra_data.as_slice().len() + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + let mut total_size = molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1); + let mut offsets = Vec::with_capacity(Self::FIELD_COUNT); + offsets.push(total_size); + total_size += self.error_code.as_slice().len(); + offsets.push(total_size); + total_size += self.extra_data.as_slice().len(); + writer.write_all(&molecule::pack_number(total_size as molecule::Number))?; + for offset in offsets.into_iter() { + writer.write_all(&molecule::pack_number(offset as molecule::Number))?; + } + writer.write_all(self.error_code.as_slice())?; + writer.write_all(self.extra_data.as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + TlcErr::new_unchecked(inner.into()) + } +} diff --git a/src/fiber/gen/gossip.rs b/src/fiber/gen/gossip.rs index b4d849c66..110edf87d 100644 --- a/src/fiber/gen/gossip.rs +++ b/src/fiber/gen/gossip.rs @@ -4,231 +4,6 @@ use super::blockchain::*; use super::fiber::*; use molecule::prelude::*; #[derive(Clone)] -pub struct Uint16(molecule::bytes::Bytes); -impl ::core::fmt::LowerHex for Uint16 { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - use molecule::hex_string; - if f.alternate() { - write!(f, "0x")?; - } - write!(f, "{}", hex_string(self.as_slice())) - } -} -impl ::core::fmt::Debug for Uint16 { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({:#x})", Self::NAME, self) - } -} -impl ::core::fmt::Display for Uint16 { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - use molecule::hex_string; - let raw_data = hex_string(&self.raw_data()); - write!(f, "{}(0x{})", Self::NAME, raw_data) - } -} -impl ::core::default::Default for Uint16 { - fn default() -> Self { - let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); - Uint16::new_unchecked(v) - } -} -impl Uint16 { - const DEFAULT_VALUE: [u8; 2] = [0, 0]; - pub const TOTAL_SIZE: usize = 2; - pub const ITEM_SIZE: usize = 1; - pub const ITEM_COUNT: usize = 2; - pub fn nth0(&self) -> Byte { - Byte::new_unchecked(self.0.slice(0..1)) - } - pub fn nth1(&self) -> Byte { - Byte::new_unchecked(self.0.slice(1..2)) - } - pub fn raw_data(&self) -> molecule::bytes::Bytes { - self.as_bytes() - } - pub fn as_reader<'r>(&'r self) -> Uint16Reader<'r> { - Uint16Reader::new_unchecked(self.as_slice()) - } -} -impl molecule::prelude::Entity for Uint16 { - type Builder = Uint16Builder; - const NAME: &'static str = "Uint16"; - fn new_unchecked(data: molecule::bytes::Bytes) -> Self { - Uint16(data) - } - fn as_bytes(&self) -> molecule::bytes::Bytes { - self.0.clone() - } - fn as_slice(&self) -> &[u8] { - &self.0[..] - } - fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { - Uint16Reader::from_slice(slice).map(|reader| reader.to_entity()) - } - fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { - Uint16Reader::from_compatible_slice(slice).map(|reader| reader.to_entity()) - } - fn new_builder() -> Self::Builder { - ::core::default::Default::default() - } - fn as_builder(self) -> Self::Builder { - Self::new_builder().set([self.nth0(), self.nth1()]) - } -} -#[derive(Clone, Copy)] -pub struct Uint16Reader<'r>(&'r [u8]); -impl<'r> ::core::fmt::LowerHex for Uint16Reader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - use molecule::hex_string; - if f.alternate() { - write!(f, "0x")?; - } - write!(f, "{}", hex_string(self.as_slice())) - } -} -impl<'r> ::core::fmt::Debug for Uint16Reader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({:#x})", Self::NAME, self) - } -} -impl<'r> ::core::fmt::Display for Uint16Reader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - use molecule::hex_string; - let raw_data = hex_string(&self.raw_data()); - write!(f, "{}(0x{})", Self::NAME, raw_data) - } -} -impl<'r> Uint16Reader<'r> { - pub const TOTAL_SIZE: usize = 2; - pub const ITEM_SIZE: usize = 1; - pub const ITEM_COUNT: usize = 2; - pub fn nth0(&self) -> ByteReader<'r> { - ByteReader::new_unchecked(&self.as_slice()[0..1]) - } - pub fn nth1(&self) -> ByteReader<'r> { - ByteReader::new_unchecked(&self.as_slice()[1..2]) - } - pub fn raw_data(&self) -> &'r [u8] { - self.as_slice() - } -} -impl<'r> molecule::prelude::Reader<'r> for Uint16Reader<'r> { - type Entity = Uint16; - const NAME: &'static str = "Uint16Reader"; - fn to_entity(&self) -> Self::Entity { - Self::Entity::new_unchecked(self.as_slice().to_owned().into()) - } - fn new_unchecked(slice: &'r [u8]) -> Self { - Uint16Reader(slice) - } - fn as_slice(&self) -> &'r [u8] { - self.0 - } - fn verify(slice: &[u8], _compatible: bool) -> molecule::error::VerificationResult<()> { - use molecule::verification_error as ve; - let slice_len = slice.len(); - if slice_len != Self::TOTAL_SIZE { - return ve!(Self, TotalSizeNotMatch, Self::TOTAL_SIZE, slice_len); - } - Ok(()) - } -} -#[derive(Clone)] -pub struct Uint16Builder(pub(crate) [Byte; 2]); -impl ::core::fmt::Debug for Uint16Builder { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({:?})", Self::NAME, &self.0[..]) - } -} -impl ::core::default::Default for Uint16Builder { - fn default() -> Self { - Uint16Builder([Byte::default(), Byte::default()]) - } -} -impl Uint16Builder { - pub const TOTAL_SIZE: usize = 2; - pub const ITEM_SIZE: usize = 1; - pub const ITEM_COUNT: usize = 2; - pub fn set(mut self, v: [Byte; 2]) -> Self { - self.0 = v; - self - } - pub fn nth0(mut self, v: Byte) -> Self { - self.0[0] = v; - self - } - pub fn nth1(mut self, v: Byte) -> Self { - self.0[1] = v; - self - } -} -impl molecule::prelude::Builder for Uint16Builder { - type Entity = Uint16; - const NAME: &'static str = "Uint16Builder"; - fn expected_length(&self) -> usize { - Self::TOTAL_SIZE - } - fn write(&self, writer: &mut W) -> molecule::io::Result<()> { - writer.write_all(self.0[0].as_slice())?; - writer.write_all(self.0[1].as_slice())?; - Ok(()) - } - fn build(&self) -> Self::Entity { - let mut inner = Vec::with_capacity(self.expected_length()); - self.write(&mut inner) - .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); - Uint16::new_unchecked(inner.into()) - } -} -impl From<[Byte; 2usize]> for Uint16 { - fn from(value: [Byte; 2usize]) -> Self { - Self::new_builder().set(value).build() - } -} -impl ::core::convert::TryFrom<&[Byte]> for Uint16 { - type Error = ::core::array::TryFromSliceError; - fn try_from(value: &[Byte]) -> Result { - Ok(Self::new_builder() - .set(<&[Byte; 2usize]>::try_from(value)?.clone()) - .build()) - } -} -impl From for [Byte; 2usize] { - #[track_caller] - fn from(value: Uint16) -> Self { - [value.nth0(), value.nth1()] - } -} -impl From<[u8; 2usize]> for Uint16 { - fn from(value: [u8; 2usize]) -> Self { - Uint16Reader::new_unchecked(&value).to_entity() - } -} -impl ::core::convert::TryFrom<&[u8]> for Uint16 { - type Error = ::core::array::TryFromSliceError; - fn try_from(value: &[u8]) -> Result { - Ok(<[u8; 2usize]>::try_from(value)?.into()) - } -} -impl From for [u8; 2usize] { - #[track_caller] - fn from(value: Uint16) -> Self { - ::core::convert::TryFrom::try_from(value.as_slice()).unwrap() - } -} -impl<'a> From> for &'a [u8; 2usize] { - #[track_caller] - fn from(value: Uint16Reader<'a>) -> Self { - ::core::convert::TryFrom::try_from(value.as_slice()).unwrap() - } -} -impl<'a> From<&'a Uint16Reader<'a>> for &'a [u8; 2usize] { - #[track_caller] - fn from(value: &'a Uint16Reader<'a>) -> Self { - ::core::convert::TryFrom::try_from(value.as_slice()).unwrap() - } -} -#[derive(Clone)] pub struct SchnorrSignature(molecule::bytes::Bytes); impl ::core::fmt::LowerHex for SchnorrSignature { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { @@ -4241,8 +4016,8 @@ impl molecule::prelude::Builder for ChannelAnnouncementBuilder { } } #[derive(Clone)] -pub struct ChannelUpdate(molecule::bytes::Bytes); -impl ::core::fmt::LowerHex for ChannelUpdate { +pub struct BroadcastMessage(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for BroadcastMessage { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { use molecule::hex_string; if f.alternate() { @@ -4251,90 +4026,58 @@ impl ::core::fmt::LowerHex for ChannelUpdate { write!(f, "{}", hex_string(self.as_slice())) } } -impl ::core::fmt::Debug for ChannelUpdate { +impl ::core::fmt::Debug for BroadcastMessage { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { write!(f, "{}({:#x})", Self::NAME, self) } } -impl ::core::fmt::Display for ChannelUpdate { +impl ::core::fmt::Display for BroadcastMessage { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{} {{ ", Self::NAME)?; - write!(f, "{}: {}", "signature", self.signature())?; - write!(f, ", {}: {}", "chain_hash", self.chain_hash())?; - write!(f, ", {}: {}", "channel_outpoint", self.channel_outpoint())?; - write!(f, ", {}: {}", "timestamp", self.timestamp())?; - write!(f, ", {}: {}", "message_flags", self.message_flags())?; - write!(f, ", {}: {}", "channel_flags", self.channel_flags())?; - write!(f, ", {}: {}", "tlc_expiry_delta", self.tlc_expiry_delta())?; - write!(f, ", {}: {}", "tlc_minimum_value", self.tlc_minimum_value())?; - write!(f, ", {}: {}", "tlc_maximum_value", self.tlc_maximum_value())?; - write!( - f, - ", {}: {}", - "tlc_fee_proportional_millionths", - self.tlc_fee_proportional_millionths() - )?; - write!(f, " }}") + write!(f, "{}(", Self::NAME)?; + self.to_enum().display_inner(f)?; + write!(f, ")") } } -impl ::core::default::Default for ChannelUpdate { +impl ::core::default::Default for BroadcastMessage { fn default() -> Self { let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); - ChannelUpdate::new_unchecked(v) + BroadcastMessage::new_unchecked(v) } } -impl ChannelUpdate { - const DEFAULT_VALUE: [u8; 204] = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +impl BroadcastMessage { + const DEFAULT_VALUE: [u8; 237] = [ + 0, 0, 0, 0, 233, 0, 0, 0, 40, 0, 0, 0, 104, 0, 0, 0, 112, 0, 0, 0, 120, 0, 0, 0, 153, 0, 0, + 0, 185, 0, 0, 0, 189, 0, 0, 0, 221, 0, 0, 0, 229, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, + 0, 0, ]; - pub const TOTAL_SIZE: usize = 204; - pub const FIELD_SIZES: [usize; 10] = [64, 32, 36, 8, 4, 4, 8, 16, 16, 16]; - pub const FIELD_COUNT: usize = 10; - pub fn signature(&self) -> EcdsaSignature { - EcdsaSignature::new_unchecked(self.0.slice(0..64)) - } - pub fn chain_hash(&self) -> Byte32 { - Byte32::new_unchecked(self.0.slice(64..96)) - } - pub fn channel_outpoint(&self) -> OutPoint { - OutPoint::new_unchecked(self.0.slice(96..132)) - } - pub fn timestamp(&self) -> Uint64 { - Uint64::new_unchecked(self.0.slice(132..140)) - } - pub fn message_flags(&self) -> Uint32 { - Uint32::new_unchecked(self.0.slice(140..144)) - } - pub fn channel_flags(&self) -> Uint32 { - Uint32::new_unchecked(self.0.slice(144..148)) - } - pub fn tlc_expiry_delta(&self) -> Uint64 { - Uint64::new_unchecked(self.0.slice(148..156)) - } - pub fn tlc_minimum_value(&self) -> Uint128 { - Uint128::new_unchecked(self.0.slice(156..172)) - } - pub fn tlc_maximum_value(&self) -> Uint128 { - Uint128::new_unchecked(self.0.slice(172..188)) + pub const ITEMS_COUNT: usize = 3; + pub fn item_id(&self) -> molecule::Number { + molecule::unpack_number(self.as_slice()) } - pub fn tlc_fee_proportional_millionths(&self) -> Uint128 { - Uint128::new_unchecked(self.0.slice(188..204)) + pub fn to_enum(&self) -> BroadcastMessageUnion { + let inner = self.0.slice(molecule::NUMBER_SIZE..); + match self.item_id() { + 0 => NodeAnnouncement::new_unchecked(inner).into(), + 1 => ChannelAnnouncement::new_unchecked(inner).into(), + 2 => ChannelUpdate::new_unchecked(inner).into(), + _ => panic!("{}: invalid data", Self::NAME), + } } - pub fn as_reader<'r>(&'r self) -> ChannelUpdateReader<'r> { - ChannelUpdateReader::new_unchecked(self.as_slice()) + pub fn as_reader<'r>(&'r self) -> BroadcastMessageReader<'r> { + BroadcastMessageReader::new_unchecked(self.as_slice()) } } -impl molecule::prelude::Entity for ChannelUpdate { - type Builder = ChannelUpdateBuilder; - const NAME: &'static str = "ChannelUpdate"; +impl molecule::prelude::Entity for BroadcastMessage { + type Builder = BroadcastMessageBuilder; + const NAME: &'static str = "BroadcastMessage"; fn new_unchecked(data: molecule::bytes::Bytes) -> Self { - ChannelUpdate(data) + BroadcastMessage(data) } fn as_bytes(&self) -> molecule::bytes::Bytes { self.0.clone() @@ -4343,31 +4086,21 @@ impl molecule::prelude::Entity for ChannelUpdate { &self.0[..] } fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { - ChannelUpdateReader::from_slice(slice).map(|reader| reader.to_entity()) + BroadcastMessageReader::from_slice(slice).map(|reader| reader.to_entity()) } fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { - ChannelUpdateReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + BroadcastMessageReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) } fn new_builder() -> Self::Builder { ::core::default::Default::default() } fn as_builder(self) -> Self::Builder { - Self::new_builder() - .signature(self.signature()) - .chain_hash(self.chain_hash()) - .channel_outpoint(self.channel_outpoint()) - .timestamp(self.timestamp()) - .message_flags(self.message_flags()) - .channel_flags(self.channel_flags()) - .tlc_expiry_delta(self.tlc_expiry_delta()) - .tlc_minimum_value(self.tlc_minimum_value()) - .tlc_maximum_value(self.tlc_maximum_value()) - .tlc_fee_proportional_millionths(self.tlc_fee_proportional_millionths()) + Self::new_builder().set(self.to_enum()) } } #[derive(Clone, Copy)] -pub struct ChannelUpdateReader<'r>(&'r [u8]); -impl<'r> ::core::fmt::LowerHex for ChannelUpdateReader<'r> { +pub struct BroadcastMessageReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for BroadcastMessageReader<'r> { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { use molecule::hex_string; if f.alternate() { @@ -4376,282 +4109,22 @@ impl<'r> ::core::fmt::LowerHex for ChannelUpdateReader<'r> { write!(f, "{}", hex_string(self.as_slice())) } } -impl<'r> ::core::fmt::Debug for ChannelUpdateReader<'r> { +impl<'r> ::core::fmt::Debug for BroadcastMessageReader<'r> { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { write!(f, "{}({:#x})", Self::NAME, self) } } -impl<'r> ::core::fmt::Display for ChannelUpdateReader<'r> { +impl<'r> ::core::fmt::Display for BroadcastMessageReader<'r> { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{} {{ ", Self::NAME)?; - write!(f, "{}: {}", "signature", self.signature())?; - write!(f, ", {}: {}", "chain_hash", self.chain_hash())?; - write!(f, ", {}: {}", "channel_outpoint", self.channel_outpoint())?; - write!(f, ", {}: {}", "timestamp", self.timestamp())?; - write!(f, ", {}: {}", "message_flags", self.message_flags())?; - write!(f, ", {}: {}", "channel_flags", self.channel_flags())?; - write!(f, ", {}: {}", "tlc_expiry_delta", self.tlc_expiry_delta())?; - write!(f, ", {}: {}", "tlc_minimum_value", self.tlc_minimum_value())?; - write!(f, ", {}: {}", "tlc_maximum_value", self.tlc_maximum_value())?; - write!( - f, - ", {}: {}", - "tlc_fee_proportional_millionths", - self.tlc_fee_proportional_millionths() - )?; - write!(f, " }}") + write!(f, "{}(", Self::NAME)?; + self.to_enum().display_inner(f)?; + write!(f, ")") } } -impl<'r> ChannelUpdateReader<'r> { - pub const TOTAL_SIZE: usize = 204; - pub const FIELD_SIZES: [usize; 10] = [64, 32, 36, 8, 4, 4, 8, 16, 16, 16]; - pub const FIELD_COUNT: usize = 10; - pub fn signature(&self) -> EcdsaSignatureReader<'r> { - EcdsaSignatureReader::new_unchecked(&self.as_slice()[0..64]) - } - pub fn chain_hash(&self) -> Byte32Reader<'r> { - Byte32Reader::new_unchecked(&self.as_slice()[64..96]) - } - pub fn channel_outpoint(&self) -> OutPointReader<'r> { - OutPointReader::new_unchecked(&self.as_slice()[96..132]) - } - pub fn timestamp(&self) -> Uint64Reader<'r> { - Uint64Reader::new_unchecked(&self.as_slice()[132..140]) - } - pub fn message_flags(&self) -> Uint32Reader<'r> { - Uint32Reader::new_unchecked(&self.as_slice()[140..144]) - } - pub fn channel_flags(&self) -> Uint32Reader<'r> { - Uint32Reader::new_unchecked(&self.as_slice()[144..148]) - } - pub fn tlc_expiry_delta(&self) -> Uint64Reader<'r> { - Uint64Reader::new_unchecked(&self.as_slice()[148..156]) - } - pub fn tlc_minimum_value(&self) -> Uint128Reader<'r> { - Uint128Reader::new_unchecked(&self.as_slice()[156..172]) - } - pub fn tlc_maximum_value(&self) -> Uint128Reader<'r> { - Uint128Reader::new_unchecked(&self.as_slice()[172..188]) - } - pub fn tlc_fee_proportional_millionths(&self) -> Uint128Reader<'r> { - Uint128Reader::new_unchecked(&self.as_slice()[188..204]) - } -} -impl<'r> molecule::prelude::Reader<'r> for ChannelUpdateReader<'r> { - type Entity = ChannelUpdate; - const NAME: &'static str = "ChannelUpdateReader"; - fn to_entity(&self) -> Self::Entity { - Self::Entity::new_unchecked(self.as_slice().to_owned().into()) - } - fn new_unchecked(slice: &'r [u8]) -> Self { - ChannelUpdateReader(slice) - } - fn as_slice(&self) -> &'r [u8] { - self.0 - } - fn verify(slice: &[u8], _compatible: bool) -> molecule::error::VerificationResult<()> { - use molecule::verification_error as ve; - let slice_len = slice.len(); - if slice_len != Self::TOTAL_SIZE { - return ve!(Self, TotalSizeNotMatch, Self::TOTAL_SIZE, slice_len); - } - Ok(()) - } -} -#[derive(Clone, Debug, Default)] -pub struct ChannelUpdateBuilder { - pub(crate) signature: EcdsaSignature, - pub(crate) chain_hash: Byte32, - pub(crate) channel_outpoint: OutPoint, - pub(crate) timestamp: Uint64, - pub(crate) message_flags: Uint32, - pub(crate) channel_flags: Uint32, - pub(crate) tlc_expiry_delta: Uint64, - pub(crate) tlc_minimum_value: Uint128, - pub(crate) tlc_maximum_value: Uint128, - pub(crate) tlc_fee_proportional_millionths: Uint128, -} -impl ChannelUpdateBuilder { - pub const TOTAL_SIZE: usize = 204; - pub const FIELD_SIZES: [usize; 10] = [64, 32, 36, 8, 4, 4, 8, 16, 16, 16]; - pub const FIELD_COUNT: usize = 10; - pub fn signature(mut self, v: EcdsaSignature) -> Self { - self.signature = v; - self - } - pub fn chain_hash(mut self, v: Byte32) -> Self { - self.chain_hash = v; - self - } - pub fn channel_outpoint(mut self, v: OutPoint) -> Self { - self.channel_outpoint = v; - self - } - pub fn timestamp(mut self, v: Uint64) -> Self { - self.timestamp = v; - self - } - pub fn message_flags(mut self, v: Uint32) -> Self { - self.message_flags = v; - self - } - pub fn channel_flags(mut self, v: Uint32) -> Self { - self.channel_flags = v; - self - } - pub fn tlc_expiry_delta(mut self, v: Uint64) -> Self { - self.tlc_expiry_delta = v; - self - } - pub fn tlc_minimum_value(mut self, v: Uint128) -> Self { - self.tlc_minimum_value = v; - self - } - pub fn tlc_maximum_value(mut self, v: Uint128) -> Self { - self.tlc_maximum_value = v; - self - } - pub fn tlc_fee_proportional_millionths(mut self, v: Uint128) -> Self { - self.tlc_fee_proportional_millionths = v; - self - } -} -impl molecule::prelude::Builder for ChannelUpdateBuilder { - type Entity = ChannelUpdate; - const NAME: &'static str = "ChannelUpdateBuilder"; - fn expected_length(&self) -> usize { - Self::TOTAL_SIZE - } - fn write(&self, writer: &mut W) -> molecule::io::Result<()> { - writer.write_all(self.signature.as_slice())?; - writer.write_all(self.chain_hash.as_slice())?; - writer.write_all(self.channel_outpoint.as_slice())?; - writer.write_all(self.timestamp.as_slice())?; - writer.write_all(self.message_flags.as_slice())?; - writer.write_all(self.channel_flags.as_slice())?; - writer.write_all(self.tlc_expiry_delta.as_slice())?; - writer.write_all(self.tlc_minimum_value.as_slice())?; - writer.write_all(self.tlc_maximum_value.as_slice())?; - writer.write_all(self.tlc_fee_proportional_millionths.as_slice())?; - Ok(()) - } - fn build(&self) -> Self::Entity { - let mut inner = Vec::with_capacity(self.expected_length()); - self.write(&mut inner) - .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); - ChannelUpdate::new_unchecked(inner.into()) - } -} -#[derive(Clone)] -pub struct BroadcastMessage(molecule::bytes::Bytes); -impl ::core::fmt::LowerHex for BroadcastMessage { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - use molecule::hex_string; - if f.alternate() { - write!(f, "0x")?; - } - write!(f, "{}", hex_string(self.as_slice())) - } -} -impl ::core::fmt::Debug for BroadcastMessage { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({:#x})", Self::NAME, self) - } -} -impl ::core::fmt::Display for BroadcastMessage { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}(", Self::NAME)?; - self.to_enum().display_inner(f)?; - write!(f, ")") - } -} -impl ::core::default::Default for BroadcastMessage { - fn default() -> Self { - let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); - BroadcastMessage::new_unchecked(v) - } -} -impl BroadcastMessage { - const DEFAULT_VALUE: [u8; 237] = [ - 0, 0, 0, 0, 233, 0, 0, 0, 40, 0, 0, 0, 104, 0, 0, 0, 112, 0, 0, 0, 120, 0, 0, 0, 153, 0, 0, - 0, 185, 0, 0, 0, 189, 0, 0, 0, 221, 0, 0, 0, 229, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, - 0, 0, - ]; - pub const ITEMS_COUNT: usize = 3; - pub fn item_id(&self) -> molecule::Number { - molecule::unpack_number(self.as_slice()) - } - pub fn to_enum(&self) -> BroadcastMessageUnion { - let inner = self.0.slice(molecule::NUMBER_SIZE..); - match self.item_id() { - 0 => NodeAnnouncement::new_unchecked(inner).into(), - 1 => ChannelAnnouncement::new_unchecked(inner).into(), - 2 => ChannelUpdate::new_unchecked(inner).into(), - _ => panic!("{}: invalid data", Self::NAME), - } - } - pub fn as_reader<'r>(&'r self) -> BroadcastMessageReader<'r> { - BroadcastMessageReader::new_unchecked(self.as_slice()) - } -} -impl molecule::prelude::Entity for BroadcastMessage { - type Builder = BroadcastMessageBuilder; - const NAME: &'static str = "BroadcastMessage"; - fn new_unchecked(data: molecule::bytes::Bytes) -> Self { - BroadcastMessage(data) - } - fn as_bytes(&self) -> molecule::bytes::Bytes { - self.0.clone() - } - fn as_slice(&self) -> &[u8] { - &self.0[..] - } - fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { - BroadcastMessageReader::from_slice(slice).map(|reader| reader.to_entity()) - } - fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { - BroadcastMessageReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) - } - fn new_builder() -> Self::Builder { - ::core::default::Default::default() - } - fn as_builder(self) -> Self::Builder { - Self::new_builder().set(self.to_enum()) - } -} -#[derive(Clone, Copy)] -pub struct BroadcastMessageReader<'r>(&'r [u8]); -impl<'r> ::core::fmt::LowerHex for BroadcastMessageReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - use molecule::hex_string; - if f.alternate() { - write!(f, "0x")?; - } - write!(f, "{}", hex_string(self.as_slice())) - } -} -impl<'r> ::core::fmt::Debug for BroadcastMessageReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({:#x})", Self::NAME, self) - } -} -impl<'r> ::core::fmt::Display for BroadcastMessageReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}(", Self::NAME)?; - self.to_enum().display_inner(f)?; - write!(f, ")") - } -} -impl<'r> BroadcastMessageReader<'r> { - pub const ITEMS_COUNT: usize = 3; - pub fn item_id(&self) -> molecule::Number { - molecule::unpack_number(self.as_slice()) +impl<'r> BroadcastMessageReader<'r> { + pub const ITEMS_COUNT: usize = 3; + pub fn item_id(&self) -> molecule::Number { + molecule::unpack_number(self.as_slice()) } pub fn to_enum(&self) -> BroadcastMessageUnionReader<'r> { let inner = &self.as_slice()[molecule::NUMBER_SIZE..]; @@ -7500,1438 +6973,3 @@ impl From for GossipMessage { Self::new_builder().set(value).build() } } -#[derive(Clone)] -pub struct ChannelUpdateOpt(molecule::bytes::Bytes); -impl ::core::fmt::LowerHex for ChannelUpdateOpt { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - use molecule::hex_string; - if f.alternate() { - write!(f, "0x")?; - } - write!(f, "{}", hex_string(self.as_slice())) - } -} -impl ::core::fmt::Debug for ChannelUpdateOpt { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({:#x})", Self::NAME, self) - } -} -impl ::core::fmt::Display for ChannelUpdateOpt { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - if let Some(v) = self.to_opt() { - write!(f, "{}(Some({}))", Self::NAME, v) - } else { - write!(f, "{}(None)", Self::NAME) - } - } -} -impl ::core::default::Default for ChannelUpdateOpt { - fn default() -> Self { - let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); - ChannelUpdateOpt::new_unchecked(v) - } -} -impl ChannelUpdateOpt { - const DEFAULT_VALUE: [u8; 0] = []; - pub fn is_none(&self) -> bool { - self.0.is_empty() - } - pub fn is_some(&self) -> bool { - !self.0.is_empty() - } - pub fn to_opt(&self) -> Option { - if self.is_none() { - None - } else { - Some(ChannelUpdate::new_unchecked(self.0.clone())) - } - } - pub fn as_reader<'r>(&'r self) -> ChannelUpdateOptReader<'r> { - ChannelUpdateOptReader::new_unchecked(self.as_slice()) - } -} -impl molecule::prelude::Entity for ChannelUpdateOpt { - type Builder = ChannelUpdateOptBuilder; - const NAME: &'static str = "ChannelUpdateOpt"; - fn new_unchecked(data: molecule::bytes::Bytes) -> Self { - ChannelUpdateOpt(data) - } - fn as_bytes(&self) -> molecule::bytes::Bytes { - self.0.clone() - } - fn as_slice(&self) -> &[u8] { - &self.0[..] - } - fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { - ChannelUpdateOptReader::from_slice(slice).map(|reader| reader.to_entity()) - } - fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { - ChannelUpdateOptReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) - } - fn new_builder() -> Self::Builder { - ::core::default::Default::default() - } - fn as_builder(self) -> Self::Builder { - Self::new_builder().set(self.to_opt()) - } -} -#[derive(Clone, Copy)] -pub struct ChannelUpdateOptReader<'r>(&'r [u8]); -impl<'r> ::core::fmt::LowerHex for ChannelUpdateOptReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - use molecule::hex_string; - if f.alternate() { - write!(f, "0x")?; - } - write!(f, "{}", hex_string(self.as_slice())) - } -} -impl<'r> ::core::fmt::Debug for ChannelUpdateOptReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({:#x})", Self::NAME, self) - } -} -impl<'r> ::core::fmt::Display for ChannelUpdateOptReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - if let Some(v) = self.to_opt() { - write!(f, "{}(Some({}))", Self::NAME, v) - } else { - write!(f, "{}(None)", Self::NAME) - } - } -} -impl<'r> ChannelUpdateOptReader<'r> { - pub fn is_none(&self) -> bool { - self.0.is_empty() - } - pub fn is_some(&self) -> bool { - !self.0.is_empty() - } - pub fn to_opt(&self) -> Option> { - if self.is_none() { - None - } else { - Some(ChannelUpdateReader::new_unchecked(self.as_slice())) - } - } -} -impl<'r> molecule::prelude::Reader<'r> for ChannelUpdateOptReader<'r> { - type Entity = ChannelUpdateOpt; - const NAME: &'static str = "ChannelUpdateOptReader"; - fn to_entity(&self) -> Self::Entity { - Self::Entity::new_unchecked(self.as_slice().to_owned().into()) - } - fn new_unchecked(slice: &'r [u8]) -> Self { - ChannelUpdateOptReader(slice) - } - fn as_slice(&self) -> &'r [u8] { - self.0 - } - fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { - if !slice.is_empty() { - ChannelUpdateReader::verify(&slice[..], compatible)?; - } - Ok(()) - } -} -#[derive(Clone, Debug, Default)] -pub struct ChannelUpdateOptBuilder(pub(crate) Option); -impl ChannelUpdateOptBuilder { - pub fn set(mut self, v: Option) -> Self { - self.0 = v; - self - } -} -impl molecule::prelude::Builder for ChannelUpdateOptBuilder { - type Entity = ChannelUpdateOpt; - const NAME: &'static str = "ChannelUpdateOptBuilder"; - fn expected_length(&self) -> usize { - self.0 - .as_ref() - .map(|ref inner| inner.as_slice().len()) - .unwrap_or(0) - } - fn write(&self, writer: &mut W) -> molecule::io::Result<()> { - self.0 - .as_ref() - .map(|ref inner| writer.write_all(inner.as_slice())) - .unwrap_or(Ok(())) - } - fn build(&self) -> Self::Entity { - let mut inner = Vec::with_capacity(self.expected_length()); - self.write(&mut inner) - .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); - ChannelUpdateOpt::new_unchecked(inner.into()) - } -} -impl From for ChannelUpdateOpt { - fn from(value: ChannelUpdate) -> Self { - Self::new_builder().set(Some(value)).build() - } -} -#[derive(Clone)] -pub struct ChannelFailed(molecule::bytes::Bytes); -impl ::core::fmt::LowerHex for ChannelFailed { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - use molecule::hex_string; - if f.alternate() { - write!(f, "0x")?; - } - write!(f, "{}", hex_string(self.as_slice())) - } -} -impl ::core::fmt::Debug for ChannelFailed { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({:#x})", Self::NAME, self) - } -} -impl ::core::fmt::Display for ChannelFailed { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{} {{ ", Self::NAME)?; - write!(f, "{}: {}", "channel_outpoint", self.channel_outpoint())?; - write!(f, ", {}: {}", "channel_update", self.channel_update())?; - write!(f, ", {}: {}", "node_id", self.node_id())?; - let extra_count = self.count_extra_fields(); - if extra_count != 0 { - write!(f, ", .. ({} fields)", extra_count)?; - } - write!(f, " }}") - } -} -impl ::core::default::Default for ChannelFailed { - fn default() -> Self { - let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); - ChannelFailed::new_unchecked(v) - } -} -impl ChannelFailed { - const DEFAULT_VALUE: [u8; 85] = [ - 85, 0, 0, 0, 16, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]; - pub const FIELD_COUNT: usize = 3; - pub fn total_size(&self) -> usize { - molecule::unpack_number(self.as_slice()) as usize - } - pub fn field_count(&self) -> usize { - if self.total_size() == molecule::NUMBER_SIZE { - 0 - } else { - (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 - } - } - pub fn count_extra_fields(&self) -> usize { - self.field_count() - Self::FIELD_COUNT - } - pub fn has_extra_fields(&self) -> bool { - Self::FIELD_COUNT != self.field_count() - } - pub fn channel_outpoint(&self) -> OutPoint { - let slice = self.as_slice(); - let start = molecule::unpack_number(&slice[4..]) as usize; - let end = molecule::unpack_number(&slice[8..]) as usize; - OutPoint::new_unchecked(self.0.slice(start..end)) - } - pub fn channel_update(&self) -> ChannelUpdateOpt { - let slice = self.as_slice(); - let start = molecule::unpack_number(&slice[8..]) as usize; - let end = molecule::unpack_number(&slice[12..]) as usize; - ChannelUpdateOpt::new_unchecked(self.0.slice(start..end)) - } - pub fn node_id(&self) -> Pubkey { - let slice = self.as_slice(); - let start = molecule::unpack_number(&slice[12..]) as usize; - if self.has_extra_fields() { - let end = molecule::unpack_number(&slice[16..]) as usize; - Pubkey::new_unchecked(self.0.slice(start..end)) - } else { - Pubkey::new_unchecked(self.0.slice(start..)) - } - } - pub fn as_reader<'r>(&'r self) -> ChannelFailedReader<'r> { - ChannelFailedReader::new_unchecked(self.as_slice()) - } -} -impl molecule::prelude::Entity for ChannelFailed { - type Builder = ChannelFailedBuilder; - const NAME: &'static str = "ChannelFailed"; - fn new_unchecked(data: molecule::bytes::Bytes) -> Self { - ChannelFailed(data) - } - fn as_bytes(&self) -> molecule::bytes::Bytes { - self.0.clone() - } - fn as_slice(&self) -> &[u8] { - &self.0[..] - } - fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { - ChannelFailedReader::from_slice(slice).map(|reader| reader.to_entity()) - } - fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { - ChannelFailedReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) - } - fn new_builder() -> Self::Builder { - ::core::default::Default::default() - } - fn as_builder(self) -> Self::Builder { - Self::new_builder() - .channel_outpoint(self.channel_outpoint()) - .channel_update(self.channel_update()) - .node_id(self.node_id()) - } -} -#[derive(Clone, Copy)] -pub struct ChannelFailedReader<'r>(&'r [u8]); -impl<'r> ::core::fmt::LowerHex for ChannelFailedReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - use molecule::hex_string; - if f.alternate() { - write!(f, "0x")?; - } - write!(f, "{}", hex_string(self.as_slice())) - } -} -impl<'r> ::core::fmt::Debug for ChannelFailedReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({:#x})", Self::NAME, self) - } -} -impl<'r> ::core::fmt::Display for ChannelFailedReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{} {{ ", Self::NAME)?; - write!(f, "{}: {}", "channel_outpoint", self.channel_outpoint())?; - write!(f, ", {}: {}", "channel_update", self.channel_update())?; - write!(f, ", {}: {}", "node_id", self.node_id())?; - let extra_count = self.count_extra_fields(); - if extra_count != 0 { - write!(f, ", .. ({} fields)", extra_count)?; - } - write!(f, " }}") - } -} -impl<'r> ChannelFailedReader<'r> { - pub const FIELD_COUNT: usize = 3; - pub fn total_size(&self) -> usize { - molecule::unpack_number(self.as_slice()) as usize - } - pub fn field_count(&self) -> usize { - if self.total_size() == molecule::NUMBER_SIZE { - 0 - } else { - (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 - } - } - pub fn count_extra_fields(&self) -> usize { - self.field_count() - Self::FIELD_COUNT - } - pub fn has_extra_fields(&self) -> bool { - Self::FIELD_COUNT != self.field_count() - } - pub fn channel_outpoint(&self) -> OutPointReader<'r> { - let slice = self.as_slice(); - let start = molecule::unpack_number(&slice[4..]) as usize; - let end = molecule::unpack_number(&slice[8..]) as usize; - OutPointReader::new_unchecked(&self.as_slice()[start..end]) - } - pub fn channel_update(&self) -> ChannelUpdateOptReader<'r> { - let slice = self.as_slice(); - let start = molecule::unpack_number(&slice[8..]) as usize; - let end = molecule::unpack_number(&slice[12..]) as usize; - ChannelUpdateOptReader::new_unchecked(&self.as_slice()[start..end]) - } - pub fn node_id(&self) -> PubkeyReader<'r> { - let slice = self.as_slice(); - let start = molecule::unpack_number(&slice[12..]) as usize; - if self.has_extra_fields() { - let end = molecule::unpack_number(&slice[16..]) as usize; - PubkeyReader::new_unchecked(&self.as_slice()[start..end]) - } else { - PubkeyReader::new_unchecked(&self.as_slice()[start..]) - } - } -} -impl<'r> molecule::prelude::Reader<'r> for ChannelFailedReader<'r> { - type Entity = ChannelFailed; - const NAME: &'static str = "ChannelFailedReader"; - fn to_entity(&self) -> Self::Entity { - Self::Entity::new_unchecked(self.as_slice().to_owned().into()) - } - fn new_unchecked(slice: &'r [u8]) -> Self { - ChannelFailedReader(slice) - } - fn as_slice(&self) -> &'r [u8] { - self.0 - } - fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { - use molecule::verification_error as ve; - let slice_len = slice.len(); - if slice_len < molecule::NUMBER_SIZE { - return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); - } - let total_size = molecule::unpack_number(slice) as usize; - if slice_len != total_size { - return ve!(Self, TotalSizeNotMatch, total_size, slice_len); - } - if slice_len < molecule::NUMBER_SIZE * 2 { - return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE * 2, slice_len); - } - let offset_first = molecule::unpack_number(&slice[molecule::NUMBER_SIZE..]) as usize; - if offset_first % molecule::NUMBER_SIZE != 0 || offset_first < molecule::NUMBER_SIZE * 2 { - return ve!(Self, OffsetsNotMatch); - } - if slice_len < offset_first { - return ve!(Self, HeaderIsBroken, offset_first, slice_len); - } - let field_count = offset_first / molecule::NUMBER_SIZE - 1; - if field_count < Self::FIELD_COUNT { - return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); - } else if !compatible && field_count > Self::FIELD_COUNT { - return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); - }; - let mut offsets: Vec = slice[molecule::NUMBER_SIZE..offset_first] - .chunks_exact(molecule::NUMBER_SIZE) - .map(|x| molecule::unpack_number(x) as usize) - .collect(); - offsets.push(total_size); - if offsets.windows(2).any(|i| i[0] > i[1]) { - return ve!(Self, OffsetsNotMatch); - } - OutPointReader::verify(&slice[offsets[0]..offsets[1]], compatible)?; - ChannelUpdateOptReader::verify(&slice[offsets[1]..offsets[2]], compatible)?; - PubkeyReader::verify(&slice[offsets[2]..offsets[3]], compatible)?; - Ok(()) - } -} -#[derive(Clone, Debug, Default)] -pub struct ChannelFailedBuilder { - pub(crate) channel_outpoint: OutPoint, - pub(crate) channel_update: ChannelUpdateOpt, - pub(crate) node_id: Pubkey, -} -impl ChannelFailedBuilder { - pub const FIELD_COUNT: usize = 3; - pub fn channel_outpoint(mut self, v: OutPoint) -> Self { - self.channel_outpoint = v; - self - } - pub fn channel_update(mut self, v: ChannelUpdateOpt) -> Self { - self.channel_update = v; - self - } - pub fn node_id(mut self, v: Pubkey) -> Self { - self.node_id = v; - self - } -} -impl molecule::prelude::Builder for ChannelFailedBuilder { - type Entity = ChannelFailed; - const NAME: &'static str = "ChannelFailedBuilder"; - fn expected_length(&self) -> usize { - molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1) - + self.channel_outpoint.as_slice().len() - + self.channel_update.as_slice().len() - + self.node_id.as_slice().len() - } - fn write(&self, writer: &mut W) -> molecule::io::Result<()> { - let mut total_size = molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1); - let mut offsets = Vec::with_capacity(Self::FIELD_COUNT); - offsets.push(total_size); - total_size += self.channel_outpoint.as_slice().len(); - offsets.push(total_size); - total_size += self.channel_update.as_slice().len(); - offsets.push(total_size); - total_size += self.node_id.as_slice().len(); - writer.write_all(&molecule::pack_number(total_size as molecule::Number))?; - for offset in offsets.into_iter() { - writer.write_all(&molecule::pack_number(offset as molecule::Number))?; - } - writer.write_all(self.channel_outpoint.as_slice())?; - writer.write_all(self.channel_update.as_slice())?; - writer.write_all(self.node_id.as_slice())?; - Ok(()) - } - fn build(&self) -> Self::Entity { - let mut inner = Vec::with_capacity(self.expected_length()); - self.write(&mut inner) - .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); - ChannelFailed::new_unchecked(inner.into()) - } -} -#[derive(Clone)] -pub struct NodeFailed(molecule::bytes::Bytes); -impl ::core::fmt::LowerHex for NodeFailed { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - use molecule::hex_string; - if f.alternate() { - write!(f, "0x")?; - } - write!(f, "{}", hex_string(self.as_slice())) - } -} -impl ::core::fmt::Debug for NodeFailed { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({:#x})", Self::NAME, self) - } -} -impl ::core::fmt::Display for NodeFailed { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{} {{ ", Self::NAME)?; - write!(f, "{}: {}", "node_id", self.node_id())?; - let extra_count = self.count_extra_fields(); - if extra_count != 0 { - write!(f, ", .. ({} fields)", extra_count)?; - } - write!(f, " }}") - } -} -impl ::core::default::Default for NodeFailed { - fn default() -> Self { - let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); - NodeFailed::new_unchecked(v) - } -} -impl NodeFailed { - const DEFAULT_VALUE: [u8; 41] = [ - 41, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]; - pub const FIELD_COUNT: usize = 1; - pub fn total_size(&self) -> usize { - molecule::unpack_number(self.as_slice()) as usize - } - pub fn field_count(&self) -> usize { - if self.total_size() == molecule::NUMBER_SIZE { - 0 - } else { - (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 - } - } - pub fn count_extra_fields(&self) -> usize { - self.field_count() - Self::FIELD_COUNT - } - pub fn has_extra_fields(&self) -> bool { - Self::FIELD_COUNT != self.field_count() - } - pub fn node_id(&self) -> Pubkey { - let slice = self.as_slice(); - let start = molecule::unpack_number(&slice[4..]) as usize; - if self.has_extra_fields() { - let end = molecule::unpack_number(&slice[8..]) as usize; - Pubkey::new_unchecked(self.0.slice(start..end)) - } else { - Pubkey::new_unchecked(self.0.slice(start..)) - } - } - pub fn as_reader<'r>(&'r self) -> NodeFailedReader<'r> { - NodeFailedReader::new_unchecked(self.as_slice()) - } -} -impl molecule::prelude::Entity for NodeFailed { - type Builder = NodeFailedBuilder; - const NAME: &'static str = "NodeFailed"; - fn new_unchecked(data: molecule::bytes::Bytes) -> Self { - NodeFailed(data) - } - fn as_bytes(&self) -> molecule::bytes::Bytes { - self.0.clone() - } - fn as_slice(&self) -> &[u8] { - &self.0[..] - } - fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { - NodeFailedReader::from_slice(slice).map(|reader| reader.to_entity()) - } - fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { - NodeFailedReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) - } - fn new_builder() -> Self::Builder { - ::core::default::Default::default() - } - fn as_builder(self) -> Self::Builder { - Self::new_builder().node_id(self.node_id()) - } -} -#[derive(Clone, Copy)] -pub struct NodeFailedReader<'r>(&'r [u8]); -impl<'r> ::core::fmt::LowerHex for NodeFailedReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - use molecule::hex_string; - if f.alternate() { - write!(f, "0x")?; - } - write!(f, "{}", hex_string(self.as_slice())) - } -} -impl<'r> ::core::fmt::Debug for NodeFailedReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({:#x})", Self::NAME, self) - } -} -impl<'r> ::core::fmt::Display for NodeFailedReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{} {{ ", Self::NAME)?; - write!(f, "{}: {}", "node_id", self.node_id())?; - let extra_count = self.count_extra_fields(); - if extra_count != 0 { - write!(f, ", .. ({} fields)", extra_count)?; - } - write!(f, " }}") - } -} -impl<'r> NodeFailedReader<'r> { - pub const FIELD_COUNT: usize = 1; - pub fn total_size(&self) -> usize { - molecule::unpack_number(self.as_slice()) as usize - } - pub fn field_count(&self) -> usize { - if self.total_size() == molecule::NUMBER_SIZE { - 0 - } else { - (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 - } - } - pub fn count_extra_fields(&self) -> usize { - self.field_count() - Self::FIELD_COUNT - } - pub fn has_extra_fields(&self) -> bool { - Self::FIELD_COUNT != self.field_count() - } - pub fn node_id(&self) -> PubkeyReader<'r> { - let slice = self.as_slice(); - let start = molecule::unpack_number(&slice[4..]) as usize; - if self.has_extra_fields() { - let end = molecule::unpack_number(&slice[8..]) as usize; - PubkeyReader::new_unchecked(&self.as_slice()[start..end]) - } else { - PubkeyReader::new_unchecked(&self.as_slice()[start..]) - } - } -} -impl<'r> molecule::prelude::Reader<'r> for NodeFailedReader<'r> { - type Entity = NodeFailed; - const NAME: &'static str = "NodeFailedReader"; - fn to_entity(&self) -> Self::Entity { - Self::Entity::new_unchecked(self.as_slice().to_owned().into()) - } - fn new_unchecked(slice: &'r [u8]) -> Self { - NodeFailedReader(slice) - } - fn as_slice(&self) -> &'r [u8] { - self.0 - } - fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { - use molecule::verification_error as ve; - let slice_len = slice.len(); - if slice_len < molecule::NUMBER_SIZE { - return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); - } - let total_size = molecule::unpack_number(slice) as usize; - if slice_len != total_size { - return ve!(Self, TotalSizeNotMatch, total_size, slice_len); - } - if slice_len < molecule::NUMBER_SIZE * 2 { - return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE * 2, slice_len); - } - let offset_first = molecule::unpack_number(&slice[molecule::NUMBER_SIZE..]) as usize; - if offset_first % molecule::NUMBER_SIZE != 0 || offset_first < molecule::NUMBER_SIZE * 2 { - return ve!(Self, OffsetsNotMatch); - } - if slice_len < offset_first { - return ve!(Self, HeaderIsBroken, offset_first, slice_len); - } - let field_count = offset_first / molecule::NUMBER_SIZE - 1; - if field_count < Self::FIELD_COUNT { - return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); - } else if !compatible && field_count > Self::FIELD_COUNT { - return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); - }; - let mut offsets: Vec = slice[molecule::NUMBER_SIZE..offset_first] - .chunks_exact(molecule::NUMBER_SIZE) - .map(|x| molecule::unpack_number(x) as usize) - .collect(); - offsets.push(total_size); - if offsets.windows(2).any(|i| i[0] > i[1]) { - return ve!(Self, OffsetsNotMatch); - } - PubkeyReader::verify(&slice[offsets[0]..offsets[1]], compatible)?; - Ok(()) - } -} -#[derive(Clone, Debug, Default)] -pub struct NodeFailedBuilder { - pub(crate) node_id: Pubkey, -} -impl NodeFailedBuilder { - pub const FIELD_COUNT: usize = 1; - pub fn node_id(mut self, v: Pubkey) -> Self { - self.node_id = v; - self - } -} -impl molecule::prelude::Builder for NodeFailedBuilder { - type Entity = NodeFailed; - const NAME: &'static str = "NodeFailedBuilder"; - fn expected_length(&self) -> usize { - molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1) + self.node_id.as_slice().len() - } - fn write(&self, writer: &mut W) -> molecule::io::Result<()> { - let mut total_size = molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1); - let mut offsets = Vec::with_capacity(Self::FIELD_COUNT); - offsets.push(total_size); - total_size += self.node_id.as_slice().len(); - writer.write_all(&molecule::pack_number(total_size as molecule::Number))?; - for offset in offsets.into_iter() { - writer.write_all(&molecule::pack_number(offset as molecule::Number))?; - } - writer.write_all(self.node_id.as_slice())?; - Ok(()) - } - fn build(&self) -> Self::Entity { - let mut inner = Vec::with_capacity(self.expected_length()); - self.write(&mut inner) - .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); - NodeFailed::new_unchecked(inner.into()) - } -} -#[derive(Clone)] -pub struct TlcErrData(molecule::bytes::Bytes); -impl ::core::fmt::LowerHex for TlcErrData { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - use molecule::hex_string; - if f.alternate() { - write!(f, "0x")?; - } - write!(f, "{}", hex_string(self.as_slice())) - } -} -impl ::core::fmt::Debug for TlcErrData { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({:#x})", Self::NAME, self) - } -} -impl ::core::fmt::Display for TlcErrData { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}(", Self::NAME)?; - self.to_enum().display_inner(f)?; - write!(f, ")") - } -} -impl ::core::default::Default for TlcErrData { - fn default() -> Self { - let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); - TlcErrData::new_unchecked(v) - } -} -impl TlcErrData { - const DEFAULT_VALUE: [u8; 89] = [ - 0, 0, 0, 0, 85, 0, 0, 0, 16, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]; - pub const ITEMS_COUNT: usize = 2; - pub fn item_id(&self) -> molecule::Number { - molecule::unpack_number(self.as_slice()) - } - pub fn to_enum(&self) -> TlcErrDataUnion { - let inner = self.0.slice(molecule::NUMBER_SIZE..); - match self.item_id() { - 0 => ChannelFailed::new_unchecked(inner).into(), - 1 => NodeFailed::new_unchecked(inner).into(), - _ => panic!("{}: invalid data", Self::NAME), - } - } - pub fn as_reader<'r>(&'r self) -> TlcErrDataReader<'r> { - TlcErrDataReader::new_unchecked(self.as_slice()) - } -} -impl molecule::prelude::Entity for TlcErrData { - type Builder = TlcErrDataBuilder; - const NAME: &'static str = "TlcErrData"; - fn new_unchecked(data: molecule::bytes::Bytes) -> Self { - TlcErrData(data) - } - fn as_bytes(&self) -> molecule::bytes::Bytes { - self.0.clone() - } - fn as_slice(&self) -> &[u8] { - &self.0[..] - } - fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { - TlcErrDataReader::from_slice(slice).map(|reader| reader.to_entity()) - } - fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { - TlcErrDataReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) - } - fn new_builder() -> Self::Builder { - ::core::default::Default::default() - } - fn as_builder(self) -> Self::Builder { - Self::new_builder().set(self.to_enum()) - } -} -#[derive(Clone, Copy)] -pub struct TlcErrDataReader<'r>(&'r [u8]); -impl<'r> ::core::fmt::LowerHex for TlcErrDataReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - use molecule::hex_string; - if f.alternate() { - write!(f, "0x")?; - } - write!(f, "{}", hex_string(self.as_slice())) - } -} -impl<'r> ::core::fmt::Debug for TlcErrDataReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({:#x})", Self::NAME, self) - } -} -impl<'r> ::core::fmt::Display for TlcErrDataReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}(", Self::NAME)?; - self.to_enum().display_inner(f)?; - write!(f, ")") - } -} -impl<'r> TlcErrDataReader<'r> { - pub const ITEMS_COUNT: usize = 2; - pub fn item_id(&self) -> molecule::Number { - molecule::unpack_number(self.as_slice()) - } - pub fn to_enum(&self) -> TlcErrDataUnionReader<'r> { - let inner = &self.as_slice()[molecule::NUMBER_SIZE..]; - match self.item_id() { - 0 => ChannelFailedReader::new_unchecked(inner).into(), - 1 => NodeFailedReader::new_unchecked(inner).into(), - _ => panic!("{}: invalid data", Self::NAME), - } - } -} -impl<'r> molecule::prelude::Reader<'r> for TlcErrDataReader<'r> { - type Entity = TlcErrData; - const NAME: &'static str = "TlcErrDataReader"; - fn to_entity(&self) -> Self::Entity { - Self::Entity::new_unchecked(self.as_slice().to_owned().into()) - } - fn new_unchecked(slice: &'r [u8]) -> Self { - TlcErrDataReader(slice) - } - fn as_slice(&self) -> &'r [u8] { - self.0 - } - fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { - use molecule::verification_error as ve; - let slice_len = slice.len(); - if slice_len < molecule::NUMBER_SIZE { - return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); - } - let item_id = molecule::unpack_number(slice); - let inner_slice = &slice[molecule::NUMBER_SIZE..]; - match item_id { - 0 => ChannelFailedReader::verify(inner_slice, compatible), - 1 => NodeFailedReader::verify(inner_slice, compatible), - _ => ve!(Self, UnknownItem, Self::ITEMS_COUNT, item_id), - }?; - Ok(()) - } -} -#[derive(Clone, Debug, Default)] -pub struct TlcErrDataBuilder(pub(crate) TlcErrDataUnion); -impl TlcErrDataBuilder { - pub const ITEMS_COUNT: usize = 2; - pub fn set(mut self, v: I) -> Self - where - I: ::core::convert::Into, - { - self.0 = v.into(); - self - } -} -impl molecule::prelude::Builder for TlcErrDataBuilder { - type Entity = TlcErrData; - const NAME: &'static str = "TlcErrDataBuilder"; - fn expected_length(&self) -> usize { - molecule::NUMBER_SIZE + self.0.as_slice().len() - } - fn write(&self, writer: &mut W) -> molecule::io::Result<()> { - writer.write_all(&molecule::pack_number(self.0.item_id()))?; - writer.write_all(self.0.as_slice()) - } - fn build(&self) -> Self::Entity { - let mut inner = Vec::with_capacity(self.expected_length()); - self.write(&mut inner) - .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); - TlcErrData::new_unchecked(inner.into()) - } -} -#[derive(Debug, Clone)] -pub enum TlcErrDataUnion { - ChannelFailed(ChannelFailed), - NodeFailed(NodeFailed), -} -#[derive(Debug, Clone, Copy)] -pub enum TlcErrDataUnionReader<'r> { - ChannelFailed(ChannelFailedReader<'r>), - NodeFailed(NodeFailedReader<'r>), -} -impl ::core::default::Default for TlcErrDataUnion { - fn default() -> Self { - TlcErrDataUnion::ChannelFailed(::core::default::Default::default()) - } -} -impl ::core::fmt::Display for TlcErrDataUnion { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match self { - TlcErrDataUnion::ChannelFailed(ref item) => { - write!(f, "{}::{}({})", Self::NAME, ChannelFailed::NAME, item) - } - TlcErrDataUnion::NodeFailed(ref item) => { - write!(f, "{}::{}({})", Self::NAME, NodeFailed::NAME, item) - } - } - } -} -impl<'r> ::core::fmt::Display for TlcErrDataUnionReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match self { - TlcErrDataUnionReader::ChannelFailed(ref item) => { - write!(f, "{}::{}({})", Self::NAME, ChannelFailed::NAME, item) - } - TlcErrDataUnionReader::NodeFailed(ref item) => { - write!(f, "{}::{}({})", Self::NAME, NodeFailed::NAME, item) - } - } - } -} -impl TlcErrDataUnion { - pub(crate) fn display_inner(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match self { - TlcErrDataUnion::ChannelFailed(ref item) => write!(f, "{}", item), - TlcErrDataUnion::NodeFailed(ref item) => write!(f, "{}", item), - } - } -} -impl<'r> TlcErrDataUnionReader<'r> { - pub(crate) fn display_inner(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match self { - TlcErrDataUnionReader::ChannelFailed(ref item) => write!(f, "{}", item), - TlcErrDataUnionReader::NodeFailed(ref item) => write!(f, "{}", item), - } - } -} -impl ::core::convert::From for TlcErrDataUnion { - fn from(item: ChannelFailed) -> Self { - TlcErrDataUnion::ChannelFailed(item) - } -} -impl ::core::convert::From for TlcErrDataUnion { - fn from(item: NodeFailed) -> Self { - TlcErrDataUnion::NodeFailed(item) - } -} -impl<'r> ::core::convert::From> for TlcErrDataUnionReader<'r> { - fn from(item: ChannelFailedReader<'r>) -> Self { - TlcErrDataUnionReader::ChannelFailed(item) - } -} -impl<'r> ::core::convert::From> for TlcErrDataUnionReader<'r> { - fn from(item: NodeFailedReader<'r>) -> Self { - TlcErrDataUnionReader::NodeFailed(item) - } -} -impl TlcErrDataUnion { - pub const NAME: &'static str = "TlcErrDataUnion"; - pub fn as_bytes(&self) -> molecule::bytes::Bytes { - match self { - TlcErrDataUnion::ChannelFailed(item) => item.as_bytes(), - TlcErrDataUnion::NodeFailed(item) => item.as_bytes(), - } - } - pub fn as_slice(&self) -> &[u8] { - match self { - TlcErrDataUnion::ChannelFailed(item) => item.as_slice(), - TlcErrDataUnion::NodeFailed(item) => item.as_slice(), - } - } - pub fn item_id(&self) -> molecule::Number { - match self { - TlcErrDataUnion::ChannelFailed(_) => 0, - TlcErrDataUnion::NodeFailed(_) => 1, - } - } - pub fn item_name(&self) -> &str { - match self { - TlcErrDataUnion::ChannelFailed(_) => "ChannelFailed", - TlcErrDataUnion::NodeFailed(_) => "NodeFailed", - } - } - pub fn as_reader<'r>(&'r self) -> TlcErrDataUnionReader<'r> { - match self { - TlcErrDataUnion::ChannelFailed(item) => item.as_reader().into(), - TlcErrDataUnion::NodeFailed(item) => item.as_reader().into(), - } - } -} -impl<'r> TlcErrDataUnionReader<'r> { - pub const NAME: &'r str = "TlcErrDataUnionReader"; - pub fn as_slice(&self) -> &'r [u8] { - match self { - TlcErrDataUnionReader::ChannelFailed(item) => item.as_slice(), - TlcErrDataUnionReader::NodeFailed(item) => item.as_slice(), - } - } - pub fn item_id(&self) -> molecule::Number { - match self { - TlcErrDataUnionReader::ChannelFailed(_) => 0, - TlcErrDataUnionReader::NodeFailed(_) => 1, - } - } - pub fn item_name(&self) -> &str { - match self { - TlcErrDataUnionReader::ChannelFailed(_) => "ChannelFailed", - TlcErrDataUnionReader::NodeFailed(_) => "NodeFailed", - } - } -} -impl From for TlcErrData { - fn from(value: ChannelFailed) -> Self { - Self::new_builder().set(value).build() - } -} -impl From for TlcErrData { - fn from(value: NodeFailed) -> Self { - Self::new_builder().set(value).build() - } -} -#[derive(Clone)] -pub struct TlcErrDataOpt(molecule::bytes::Bytes); -impl ::core::fmt::LowerHex for TlcErrDataOpt { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - use molecule::hex_string; - if f.alternate() { - write!(f, "0x")?; - } - write!(f, "{}", hex_string(self.as_slice())) - } -} -impl ::core::fmt::Debug for TlcErrDataOpt { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({:#x})", Self::NAME, self) - } -} -impl ::core::fmt::Display for TlcErrDataOpt { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - if let Some(v) = self.to_opt() { - write!(f, "{}(Some({}))", Self::NAME, v) - } else { - write!(f, "{}(None)", Self::NAME) - } - } -} -impl ::core::default::Default for TlcErrDataOpt { - fn default() -> Self { - let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); - TlcErrDataOpt::new_unchecked(v) - } -} -impl TlcErrDataOpt { - const DEFAULT_VALUE: [u8; 0] = []; - pub fn is_none(&self) -> bool { - self.0.is_empty() - } - pub fn is_some(&self) -> bool { - !self.0.is_empty() - } - pub fn to_opt(&self) -> Option { - if self.is_none() { - None - } else { - Some(TlcErrData::new_unchecked(self.0.clone())) - } - } - pub fn as_reader<'r>(&'r self) -> TlcErrDataOptReader<'r> { - TlcErrDataOptReader::new_unchecked(self.as_slice()) - } -} -impl molecule::prelude::Entity for TlcErrDataOpt { - type Builder = TlcErrDataOptBuilder; - const NAME: &'static str = "TlcErrDataOpt"; - fn new_unchecked(data: molecule::bytes::Bytes) -> Self { - TlcErrDataOpt(data) - } - fn as_bytes(&self) -> molecule::bytes::Bytes { - self.0.clone() - } - fn as_slice(&self) -> &[u8] { - &self.0[..] - } - fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { - TlcErrDataOptReader::from_slice(slice).map(|reader| reader.to_entity()) - } - fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { - TlcErrDataOptReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) - } - fn new_builder() -> Self::Builder { - ::core::default::Default::default() - } - fn as_builder(self) -> Self::Builder { - Self::new_builder().set(self.to_opt()) - } -} -#[derive(Clone, Copy)] -pub struct TlcErrDataOptReader<'r>(&'r [u8]); -impl<'r> ::core::fmt::LowerHex for TlcErrDataOptReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - use molecule::hex_string; - if f.alternate() { - write!(f, "0x")?; - } - write!(f, "{}", hex_string(self.as_slice())) - } -} -impl<'r> ::core::fmt::Debug for TlcErrDataOptReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({:#x})", Self::NAME, self) - } -} -impl<'r> ::core::fmt::Display for TlcErrDataOptReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - if let Some(v) = self.to_opt() { - write!(f, "{}(Some({}))", Self::NAME, v) - } else { - write!(f, "{}(None)", Self::NAME) - } - } -} -impl<'r> TlcErrDataOptReader<'r> { - pub fn is_none(&self) -> bool { - self.0.is_empty() - } - pub fn is_some(&self) -> bool { - !self.0.is_empty() - } - pub fn to_opt(&self) -> Option> { - if self.is_none() { - None - } else { - Some(TlcErrDataReader::new_unchecked(self.as_slice())) - } - } -} -impl<'r> molecule::prelude::Reader<'r> for TlcErrDataOptReader<'r> { - type Entity = TlcErrDataOpt; - const NAME: &'static str = "TlcErrDataOptReader"; - fn to_entity(&self) -> Self::Entity { - Self::Entity::new_unchecked(self.as_slice().to_owned().into()) - } - fn new_unchecked(slice: &'r [u8]) -> Self { - TlcErrDataOptReader(slice) - } - fn as_slice(&self) -> &'r [u8] { - self.0 - } - fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { - if !slice.is_empty() { - TlcErrDataReader::verify(&slice[..], compatible)?; - } - Ok(()) - } -} -#[derive(Clone, Debug, Default)] -pub struct TlcErrDataOptBuilder(pub(crate) Option); -impl TlcErrDataOptBuilder { - pub fn set(mut self, v: Option) -> Self { - self.0 = v; - self - } -} -impl molecule::prelude::Builder for TlcErrDataOptBuilder { - type Entity = TlcErrDataOpt; - const NAME: &'static str = "TlcErrDataOptBuilder"; - fn expected_length(&self) -> usize { - self.0 - .as_ref() - .map(|ref inner| inner.as_slice().len()) - .unwrap_or(0) - } - fn write(&self, writer: &mut W) -> molecule::io::Result<()> { - self.0 - .as_ref() - .map(|ref inner| writer.write_all(inner.as_slice())) - .unwrap_or(Ok(())) - } - fn build(&self) -> Self::Entity { - let mut inner = Vec::with_capacity(self.expected_length()); - self.write(&mut inner) - .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); - TlcErrDataOpt::new_unchecked(inner.into()) - } -} -impl From for TlcErrDataOpt { - fn from(value: TlcErrData) -> Self { - Self::new_builder().set(Some(value)).build() - } -} -#[derive(Clone)] -pub struct TlcErr(molecule::bytes::Bytes); -impl ::core::fmt::LowerHex for TlcErr { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - use molecule::hex_string; - if f.alternate() { - write!(f, "0x")?; - } - write!(f, "{}", hex_string(self.as_slice())) - } -} -impl ::core::fmt::Debug for TlcErr { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({:#x})", Self::NAME, self) - } -} -impl ::core::fmt::Display for TlcErr { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{} {{ ", Self::NAME)?; - write!(f, "{}: {}", "error_code", self.error_code())?; - write!(f, ", {}: {}", "extra_data", self.extra_data())?; - let extra_count = self.count_extra_fields(); - if extra_count != 0 { - write!(f, ", .. ({} fields)", extra_count)?; - } - write!(f, " }}") - } -} -impl ::core::default::Default for TlcErr { - fn default() -> Self { - let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); - TlcErr::new_unchecked(v) - } -} -impl TlcErr { - const DEFAULT_VALUE: [u8; 44] = [ - 44, 0, 0, 0, 12, 0, 0, 0, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]; - pub const FIELD_COUNT: usize = 2; - pub fn total_size(&self) -> usize { - molecule::unpack_number(self.as_slice()) as usize - } - pub fn field_count(&self) -> usize { - if self.total_size() == molecule::NUMBER_SIZE { - 0 - } else { - (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 - } - } - pub fn count_extra_fields(&self) -> usize { - self.field_count() - Self::FIELD_COUNT - } - pub fn has_extra_fields(&self) -> bool { - Self::FIELD_COUNT != self.field_count() - } - pub fn error_code(&self) -> Byte32 { - let slice = self.as_slice(); - let start = molecule::unpack_number(&slice[4..]) as usize; - let end = molecule::unpack_number(&slice[8..]) as usize; - Byte32::new_unchecked(self.0.slice(start..end)) - } - pub fn extra_data(&self) -> TlcErrDataOpt { - let slice = self.as_slice(); - let start = molecule::unpack_number(&slice[8..]) as usize; - if self.has_extra_fields() { - let end = molecule::unpack_number(&slice[12..]) as usize; - TlcErrDataOpt::new_unchecked(self.0.slice(start..end)) - } else { - TlcErrDataOpt::new_unchecked(self.0.slice(start..)) - } - } - pub fn as_reader<'r>(&'r self) -> TlcErrReader<'r> { - TlcErrReader::new_unchecked(self.as_slice()) - } -} -impl molecule::prelude::Entity for TlcErr { - type Builder = TlcErrBuilder; - const NAME: &'static str = "TlcErr"; - fn new_unchecked(data: molecule::bytes::Bytes) -> Self { - TlcErr(data) - } - fn as_bytes(&self) -> molecule::bytes::Bytes { - self.0.clone() - } - fn as_slice(&self) -> &[u8] { - &self.0[..] - } - fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { - TlcErrReader::from_slice(slice).map(|reader| reader.to_entity()) - } - fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { - TlcErrReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) - } - fn new_builder() -> Self::Builder { - ::core::default::Default::default() - } - fn as_builder(self) -> Self::Builder { - Self::new_builder() - .error_code(self.error_code()) - .extra_data(self.extra_data()) - } -} -#[derive(Clone, Copy)] -pub struct TlcErrReader<'r>(&'r [u8]); -impl<'r> ::core::fmt::LowerHex for TlcErrReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - use molecule::hex_string; - if f.alternate() { - write!(f, "0x")?; - } - write!(f, "{}", hex_string(self.as_slice())) - } -} -impl<'r> ::core::fmt::Debug for TlcErrReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({:#x})", Self::NAME, self) - } -} -impl<'r> ::core::fmt::Display for TlcErrReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{} {{ ", Self::NAME)?; - write!(f, "{}: {}", "error_code", self.error_code())?; - write!(f, ", {}: {}", "extra_data", self.extra_data())?; - let extra_count = self.count_extra_fields(); - if extra_count != 0 { - write!(f, ", .. ({} fields)", extra_count)?; - } - write!(f, " }}") - } -} -impl<'r> TlcErrReader<'r> { - pub const FIELD_COUNT: usize = 2; - pub fn total_size(&self) -> usize { - molecule::unpack_number(self.as_slice()) as usize - } - pub fn field_count(&self) -> usize { - if self.total_size() == molecule::NUMBER_SIZE { - 0 - } else { - (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 - } - } - pub fn count_extra_fields(&self) -> usize { - self.field_count() - Self::FIELD_COUNT - } - pub fn has_extra_fields(&self) -> bool { - Self::FIELD_COUNT != self.field_count() - } - pub fn error_code(&self) -> Byte32Reader<'r> { - let slice = self.as_slice(); - let start = molecule::unpack_number(&slice[4..]) as usize; - let end = molecule::unpack_number(&slice[8..]) as usize; - Byte32Reader::new_unchecked(&self.as_slice()[start..end]) - } - pub fn extra_data(&self) -> TlcErrDataOptReader<'r> { - let slice = self.as_slice(); - let start = molecule::unpack_number(&slice[8..]) as usize; - if self.has_extra_fields() { - let end = molecule::unpack_number(&slice[12..]) as usize; - TlcErrDataOptReader::new_unchecked(&self.as_slice()[start..end]) - } else { - TlcErrDataOptReader::new_unchecked(&self.as_slice()[start..]) - } - } -} -impl<'r> molecule::prelude::Reader<'r> for TlcErrReader<'r> { - type Entity = TlcErr; - const NAME: &'static str = "TlcErrReader"; - fn to_entity(&self) -> Self::Entity { - Self::Entity::new_unchecked(self.as_slice().to_owned().into()) - } - fn new_unchecked(slice: &'r [u8]) -> Self { - TlcErrReader(slice) - } - fn as_slice(&self) -> &'r [u8] { - self.0 - } - fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { - use molecule::verification_error as ve; - let slice_len = slice.len(); - if slice_len < molecule::NUMBER_SIZE { - return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); - } - let total_size = molecule::unpack_number(slice) as usize; - if slice_len != total_size { - return ve!(Self, TotalSizeNotMatch, total_size, slice_len); - } - if slice_len < molecule::NUMBER_SIZE * 2 { - return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE * 2, slice_len); - } - let offset_first = molecule::unpack_number(&slice[molecule::NUMBER_SIZE..]) as usize; - if offset_first % molecule::NUMBER_SIZE != 0 || offset_first < molecule::NUMBER_SIZE * 2 { - return ve!(Self, OffsetsNotMatch); - } - if slice_len < offset_first { - return ve!(Self, HeaderIsBroken, offset_first, slice_len); - } - let field_count = offset_first / molecule::NUMBER_SIZE - 1; - if field_count < Self::FIELD_COUNT { - return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); - } else if !compatible && field_count > Self::FIELD_COUNT { - return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); - }; - let mut offsets: Vec = slice[molecule::NUMBER_SIZE..offset_first] - .chunks_exact(molecule::NUMBER_SIZE) - .map(|x| molecule::unpack_number(x) as usize) - .collect(); - offsets.push(total_size); - if offsets.windows(2).any(|i| i[0] > i[1]) { - return ve!(Self, OffsetsNotMatch); - } - Byte32Reader::verify(&slice[offsets[0]..offsets[1]], compatible)?; - TlcErrDataOptReader::verify(&slice[offsets[1]..offsets[2]], compatible)?; - Ok(()) - } -} -#[derive(Clone, Debug, Default)] -pub struct TlcErrBuilder { - pub(crate) error_code: Byte32, - pub(crate) extra_data: TlcErrDataOpt, -} -impl TlcErrBuilder { - pub const FIELD_COUNT: usize = 2; - pub fn error_code(mut self, v: Byte32) -> Self { - self.error_code = v; - self - } - pub fn extra_data(mut self, v: TlcErrDataOpt) -> Self { - self.extra_data = v; - self - } -} -impl molecule::prelude::Builder for TlcErrBuilder { - type Entity = TlcErr; - const NAME: &'static str = "TlcErrBuilder"; - fn expected_length(&self) -> usize { - molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1) - + self.error_code.as_slice().len() - + self.extra_data.as_slice().len() - } - fn write(&self, writer: &mut W) -> molecule::io::Result<()> { - let mut total_size = molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1); - let mut offsets = Vec::with_capacity(Self::FIELD_COUNT); - offsets.push(total_size); - total_size += self.error_code.as_slice().len(); - offsets.push(total_size); - total_size += self.extra_data.as_slice().len(); - writer.write_all(&molecule::pack_number(total_size as molecule::Number))?; - for offset in offsets.into_iter() { - writer.write_all(&molecule::pack_number(offset as molecule::Number))?; - } - writer.write_all(self.error_code.as_slice())?; - writer.write_all(self.extra_data.as_slice())?; - Ok(()) - } - fn build(&self) -> Self::Entity { - let mut inner = Vec::with_capacity(self.expected_length()); - self.write(&mut inner) - .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); - TlcErr::new_unchecked(inner.into()) - } -} diff --git a/src/fiber/schema/fiber.mol b/src/fiber/schema/fiber.mol index 19f0691e9..e454c1c3c 100644 --- a/src/fiber/schema/fiber.mol +++ b/src/fiber/schema/fiber.mol @@ -1,5 +1,6 @@ import blockchain; +array Uint16 [byte; 2]; array EcdsaSignature [byte; 64]; array PubNonce [byte; 66]; // PubNonce used by musig2 crate. option PubNonceOpt (PubNonce); @@ -187,3 +188,52 @@ union FiberMessage { ReestablishChannel, AnnouncementSignatures, } + +option PaymentPreimageOpt (Byte32); +option PubkeyOpt (Pubkey); +table PaymentHopData { + amount: Uint128, + expiry: Uint64, + payment_preimage: PaymentPreimageOpt, + hash_algorithm: byte, + funding_tx_hash: Byte32, + next_hop: PubkeyOpt, +} + +// A ChannelUpdate is a update to the public channel. Newer channel information may be updated with +// a ChannelUpdate with a larger timestamp. +struct ChannelUpdate { + // Signature of the node that wants to update the channel information. + signature: EcdsaSignature, + chain_hash: Byte32, + channel_outpoint: OutPoint, + timestamp: Uint64, + message_flags: Uint32, + channel_flags: Uint32, + tlc_expiry_delta: Uint64, + tlc_minimum_value: Uint128, + tlc_maximum_value: Uint128, + tlc_fee_proportional_millionths: Uint128, +} + +option ChannelUpdateOpt (ChannelUpdate); +table ChannelFailed { + channel_outpoint: OutPoint, + channel_update: ChannelUpdateOpt, + node_id: Pubkey, +} + +table NodeFailed { + node_id: Pubkey, +} + +union TlcErrData { + ChannelFailed, + NodeFailed, +} + +option TlcErrDataOpt (TlcErrData); +table TlcErr { + error_code: Uint16, + extra_data: TlcErrDataOpt, +} \ No newline at end of file diff --git a/src/fiber/schema/gossip.mol b/src/fiber/schema/gossip.mol index 7f1ea634c..61ebdb6d8 100644 --- a/src/fiber/schema/gossip.mol +++ b/src/fiber/schema/gossip.mol @@ -1,7 +1,6 @@ import blockchain; import fiber; -array Uint16 [byte; 2]; array SchnorrSignature [byte; 64]; array SchnorrXOnlyPubkey [byte; 32]; @@ -78,22 +77,6 @@ table ChannelAnnouncement { udt_type_script: ScriptOpt, } -// A ChannelUpdate is a update to the public channel. Newer channel information may be updated with -// a ChannelUpdate with a larger timestamp. -struct ChannelUpdate { - // Signature of the node that wants to update the channel information. - signature: EcdsaSignature, - chain_hash: Byte32, - channel_outpoint: OutPoint, - timestamp: Uint64, - message_flags: Uint32, - channel_flags: Uint32, - tlc_expiry_delta: Uint64, - tlc_minimum_value: Uint128, - tlc_maximum_value: Uint128, - tlc_fee_proportional_millionths: Uint128, -} - // All the broadcast messages. union BroadcastMessage { NodeAnnouncement, @@ -166,25 +149,3 @@ union GossipMessage { QueryBroadcastMessages, QueryBroadcastMessagesResult, } - -option ChannelUpdateOpt (ChannelUpdate); -table ChannelFailed { - channel_outpoint: OutPoint, - channel_update: ChannelUpdateOpt, - node_id: Pubkey, -} - -table NodeFailed { - node_id: Pubkey, -} - -union TlcErrData { - ChannelFailed, - NodeFailed, -} - -option TlcErrDataOpt (TlcErrData); -table TlcErr { - error_code: Byte32, - extra_data: TlcErrDataOpt, -} \ No newline at end of file diff --git a/src/fiber/tests/channel.rs b/src/fiber/tests/channel.rs index 2ec3cc2a1..95d0f459c 100644 --- a/src/fiber/tests/channel.rs +++ b/src/fiber/tests/channel.rs @@ -1452,8 +1452,9 @@ async fn test_send_payment_with_max_nodes() { let res = res.unwrap(); assert_eq!(res.status, PaymentSessionStatus::Inflight); assert!(res.fee > 0); - // sleep for 2 seconds to make sure the payment is sent - tokio::time::sleep(tokio::time::Duration::from_millis(3000)).await; + + // sleep for 5 seconds to make sure the payment is sent + tokio::time::sleep(tokio::time::Duration::from_millis(5000)).await; let message = |rpc_reply| -> NetworkActorMessage { NetworkActorMessage::Command(NetworkActorCommand::GetPayment(res.payment_hash, rpc_reply)) }; diff --git a/src/fiber/tests/types.rs b/src/fiber/tests/types.rs index 9ee86d259..c92c7eeb6 100644 --- a/src/fiber/tests/types.rs +++ b/src/fiber/tests/types.rs @@ -234,3 +234,16 @@ fn test_tlc_err_packet_encryption() { assert_eq!(decrypted_tlc_fail_detail, tlc_fail_detail); } } + +#[test] +fn test_tlc_error_code() { + let code = TlcErrorCode::PermanentNodeFailure; + let str = code.as_ref().to_string(); + let code2 = TlcErrorCode::from_str(&str).expect("parse"); + assert_eq!(code, code2); + + let code = TlcErrorCode::IncorrectOrUnknownPaymentDetails; + let code_int: u16 = code.into(); + let code = TlcErrorCode::try_from(code_int).expect("invalid code"); + assert_eq!(code, TlcErrorCode::IncorrectOrUnknownPaymentDetails); +} diff --git a/src/fiber/types.rs b/src/fiber/types.rs index 6e69de18a..e86f658d9 100644 --- a/src/fiber/types.rs +++ b/src/fiber/types.rs @@ -1,6 +1,9 @@ use super::channel::{ChannelFlags, CHANNEL_DISABLED_FLAG, MESSAGE_OF_NODE2_FLAG}; use super::config::AnnouncedNodeName; -use super::gen::fiber::{self as molecule_fiber, PubNonce as Byte66, UdtCellDeps, Uint128Opt}; +use super::gen::fiber::{ + self as molecule_fiber, ChannelUpdateOpt, PaymentPreimageOpt, PubNonce as Byte66, PubkeyOpt, + TlcErrDataOpt, UdtCellDeps, Uint128Opt, +}; use super::gen::gossip::{self as molecule_gossip}; use super::hash_algorithm::{HashAlgorithm, UnknownHashAlgorithmError}; use super::network::get_chain_hash; @@ -8,6 +11,9 @@ use super::r#gen::fiber::PubNonceOpt; use super::serde_utils::{EntityHex, SliceHex}; use crate::ckb::config::{UdtArgInfo, UdtCellDep, UdtCfgInfos, UdtScript}; use crate::ckb::contracts::get_udt_whitelist; +use num_enum::IntoPrimitive; +use num_enum::TryFromPrimitive; +use std::convert::TryFrom; use anyhow::anyhow; use ckb_types::{ @@ -1107,70 +1113,6 @@ impl TryFrom for RemoveTlcFulfill { } } -// impl From for molecule_gossip::TlcErr { -// fn from(tlc_err: TlcErr) -> Self { -// molecule_gossip::TlcErr::new_builder() -// .error_code(ckb_types::packed::Byte32::new(tlc_err.error_code as u32)) -// .extra_data( -// tlc_err -// .extra_data -// .map(|data| match data { -// TlcErrData::ChannelFailed { -// channel_outpoint, -// channel_update, -// node_id, -// } => molecule_gossip::TlcErrDataBuilder::set( -// molecule_gossip::ChannelFailed::new_builder() -// .channel_outpoint(channel_outpoint.into()) -// .channel_update(channel_update.into()) -// .node_id(node_id.into()) -// .build(), -// ) -// .build() -// .to_opt(), -// TlcErrData::NodeFailed { node_id } => { -// molecule_gossip::TlcErrDataBuilder::set( -// molecule_gossip::NodeFailed::new_builder() -// .node_id(node_id.into()) -// .build(), -// ) -// .build() -// .to_opt() -// } -// }) -// .pack(), -// ) -// .build() -// } -// } - -// impl TryFrom for TlcErr { -// type Error = Error; - -// fn try_from(tlc_err: molecule_gossip::TlcErr) -> Result { -// Ok(TlcErr { -// error_code: tlc_err.error_code().into(), -// extra_data: tlc_err -// .extra_data() -// .to_opt() -// .map(|data| match data.unpack() { -// TlcErrData::ChannelFailed { -// channel_outpoint, -// channel_update, -// node_id, -// } => TlcErrData::ChannelFailed { -// channel_outpoint: channel_outpoint.into(), -// channel_update: channel_update.to_opt(), -// node_id: node_id.into(), -// }, -// TlcErrData::NodeFailed { node_id } => TlcErrData::NodeFailed { -// node_id: node_id.into(), -// }, -// }), -// }) -// } -// } - #[serde_as] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum TlcErrData { @@ -1255,16 +1197,103 @@ impl TlcErr { error_code.as_ref().to_string() } + pub fn error_code_as_u16(&self) -> u16 { + self.error_code.into() + } + pub fn set_extra_data(&mut self, extra_data: TlcErrData) { self.extra_data = Some(extra_data); } fn serialize(&self) -> Vec { - bincode::serialize(self).expect("serialize hop data") + molecule_fiber::TlcErr::from(self.clone()) + .as_slice() + .to_vec() } fn deserialize(data: &[u8]) -> Option { - bincode::deserialize(data).ok() + molecule_fiber::TlcErr::from_slice(data) + .map(TlcErr::from) + .ok() + } +} + +impl TryFrom for molecule_fiber::TlcErrData { + type Error = Error; + + fn try_from(tlc_err_data: TlcErrData) -> Result { + match tlc_err_data { + TlcErrData::ChannelFailed { + channel_outpoint, + channel_update, + node_id, + } => Ok(molecule_fiber::ChannelFailed::new_builder() + .channel_outpoint(channel_outpoint.into()) + .channel_update( + ChannelUpdateOpt::new_builder() + .set(channel_update.map(|x| x.into())) + .build(), + ) + .node_id(node_id.into()) + .build() + .into()), + TlcErrData::NodeFailed { node_id } => Ok(molecule_fiber::NodeFailed::new_builder() + .node_id(node_id.into()) + .build() + .into()), + } + } +} + +impl TryFrom for TlcErrData { + type Error = Error; + + fn try_from(tlc_err_data: molecule_fiber::TlcErrData) -> Result { + match tlc_err_data.to_enum() { + molecule_fiber::TlcErrDataUnion::ChannelFailed(channel_failed) => { + Ok(TlcErrData::ChannelFailed { + channel_outpoint: channel_failed.channel_outpoint().into(), + channel_update: channel_failed + .channel_update() + .to_opt() + .map(|x| x.try_into().unwrap()), + node_id: channel_failed.node_id().try_into()?, + }) + } + molecule_fiber::TlcErrDataUnion::NodeFailed(node_failed) => { + Ok(TlcErrData::NodeFailed { + node_id: node_failed.node_id().try_into()?, + }) + } + } + } +} + +impl From for molecule_fiber::TlcErr { + fn from(tlc_err: TlcErr) -> Self { + molecule_fiber::TlcErr::new_builder() + .error_code(tlc_err.error_code_as_u16().into()) + .extra_data( + TlcErrDataOpt::new_builder() + .set(tlc_err.extra_data.map(|data| data.try_into().unwrap())) + .build(), + ) + .build() + } +} + +impl From for TlcErr { + fn from(tlc_err: molecule_fiber::TlcErr) -> Self { + TlcErr { + error_code: { + let code: u16 = tlc_err.error_code().into(); + TlcErrorCode::try_from(code).expect("tlc_errror_code failed") + }, + extra_data: tlc_err + .extra_data() + .to_opt() + .map(|data| data.try_into().unwrap()), + } } } @@ -1378,7 +1407,19 @@ const NODE: u16 = 0x2000; const UPDATE: u16 = 0x1000; #[repr(u16)] -#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, AsRefStr, EnumString)] +#[derive( + Debug, + Copy, + Clone, + Serialize, + Deserialize, + PartialEq, + Eq, + AsRefStr, + EnumString, + TryFromPrimitive, + IntoPrimitive, +)] pub enum TlcErrorCode { TemporaryNodeFailure = NODE | 2, PermanentNodeFailure = PERM | NODE | 2, @@ -2061,7 +2102,7 @@ impl ChannelUpdate { tlc_minimum_value: self.tlc_minimum_value, tlc_fee_proportional_millionths: self.tlc_fee_proportional_millionths, }; - deterministically_hash(&molecule_gossip::ChannelUpdate::from(unsigned_update)) + deterministically_hash(&molecule_fiber::ChannelUpdate::from(unsigned_update)) } pub fn is_update_of_node_1(&self) -> bool { @@ -2084,9 +2125,9 @@ impl ChannelUpdate { } } -impl From for molecule_gossip::ChannelUpdate { +impl From for molecule_fiber::ChannelUpdate { fn from(channel_update: ChannelUpdate) -> Self { - let builder = molecule_gossip::ChannelUpdate::new_builder() + let builder = molecule_fiber::ChannelUpdate::new_builder() .chain_hash(channel_update.chain_hash.into()) .channel_outpoint(channel_update.channel_outpoint) .timestamp(channel_update.timestamp.pack()) @@ -2106,10 +2147,10 @@ impl From for molecule_gossip::ChannelUpdate { } } -impl TryFrom for ChannelUpdate { +impl TryFrom for ChannelUpdate { type Error = Error; - fn try_from(channel_update: molecule_gossip::ChannelUpdate) -> Result { + fn try_from(channel_update: molecule_fiber::ChannelUpdate) -> Result { Ok(ChannelUpdate { signature: Some(channel_update.signature().try_into()?), chain_hash: channel_update.chain_hash().into(), @@ -2883,7 +2924,7 @@ impl TryFrom for Cursor { } } -impl From for molecule_gossip::Uint16 { +impl From for molecule_fiber::Uint16 { fn from(count: u16) -> Self { let le_bytes = count.to_le_bytes(); Self::new_builder() @@ -2899,8 +2940,8 @@ impl From for molecule_gossip::Uint16 { } } -impl From for u16 { - fn from(count: molecule_gossip::Uint16) -> Self { +impl From for u16 { + fn from(count: molecule_fiber::Uint16) -> Self { let le_bytes = count.as_slice().try_into().expect("Uint16 to u16"); u16::from_le_bytes(le_bytes) } @@ -3384,11 +3425,58 @@ impl HopData for PaymentHopData { } fn serialize(&self) -> Vec { - bincode::serialize(self).expect("serialize hop data") + molecule_fiber::PaymentHopData::from(self.clone()) + .as_bytes() + .to_vec() } fn deserialize(data: &[u8]) -> Option { - bincode::deserialize(data).ok() + molecule_fiber::PaymentHopData::from_slice(data) + .ok() + .map(|x| x.into()) + } +} + +impl From for molecule_fiber::PaymentHopData { + fn from(payment_hop_data: PaymentHopData) -> Self { + molecule_fiber::PaymentHopData::new_builder() + .amount(payment_hop_data.amount.pack()) + .expiry(payment_hop_data.expiry.pack()) + .payment_preimage( + PaymentPreimageOpt::new_builder() + .set(payment_hop_data.payment_preimage.map(|x| x.into())) + .build(), + ) + .hash_algorithm(Byte::new(payment_hop_data.hash_algorithm as u8)) + .funding_tx_hash(payment_hop_data.funding_tx_hash.into()) + .next_hop( + PubkeyOpt::new_builder() + .set(payment_hop_data.next_hop.map(|x| x.into())) + .build(), + ) + .build() + } +} + +impl From for PaymentHopData { + fn from(payment_hop_data: molecule_fiber::PaymentHopData) -> Self { + PaymentHopData { + amount: payment_hop_data.amount().unpack(), + expiry: payment_hop_data.expiry().unpack(), + payment_preimage: payment_hop_data + .payment_preimage() + .to_opt() + .map(|x| x.into()), + hash_algorithm: payment_hop_data + .hash_algorithm() + .try_into() + .expect("valid hash algorithm"), + funding_tx_hash: payment_hop_data.funding_tx_hash().into(), + next_hop: payment_hop_data + .next_hop() + .to_opt() + .map(|x| x.try_into().expect("invalid pubkey")), + } } } From 35447806536113b5247281b106a0e1f1bf07a869 Mon Sep 17 00:00:00 2001 From: yukang Date: Sun, 29 Dec 2024 12:00:58 +0800 Subject: [PATCH 065/119] add payment rpc module --- src/rpc/channel.rs | 155 +------------------------- src/rpc/config.rs | 2 +- src/rpc/mod.rs | 9 ++ src/rpc/payment.rs | 187 ++++++++++++++++++++++++++++++++ tests/nodes/deployer/config.yml | 1 + 5 files changed, 200 insertions(+), 154 deletions(-) create mode 100644 src/rpc/payment.rs diff --git a/src/rpc/channel.rs b/src/rpc/channel.rs index 474be97cf..3de38fa8a 100644 --- a/src/rpc/channel.rs +++ b/src/rpc/channel.rs @@ -6,13 +6,10 @@ use crate::fiber::{ NegotiatingFundingFlags, RemoveTlcCommand, ShutdownCommand, ShuttingDownFlags, SigningCommitmentFlags, UpdateCommand, }, - graph::PaymentSessionStatus, hash_algorithm::HashAlgorithm, - network::{AcceptChannelCommand, OpenChannelCommand, SendPaymentCommand}, + network::{AcceptChannelCommand, OpenChannelCommand}, serde_utils::{EntityHex, U128Hex, U64Hex}, - types::{ - Hash256, Pubkey, RemoveTlcFulfill, TlcErr, TlcErrPacket, TlcErrorCode, NO_SHARED_SECRET, - }, + types::{Hash256, RemoveTlcFulfill, TlcErr, TlcErrPacket, TlcErrorCode, NO_SHARED_SECRET}, NetworkActorCommand, NetworkActorMessage, }; use crate::{handle_actor_call, handle_actor_cast, log_and_error}; @@ -340,86 +337,6 @@ pub struct UpdateChannelParams { tlc_fee_proportional_millionths: Option, } -#[serde_as] -#[derive(Serialize, Deserialize, Debug)] -pub struct GetPaymentCommandParams { - /// The payment hash of the payment to retrieve - pub payment_hash: Hash256, -} - -#[serde_as] -#[derive(Serialize, Deserialize, Clone)] -pub struct GetPaymentCommandResult { - /// The payment hash of the payment - pub payment_hash: Hash256, - /// The status of the payment - pub status: PaymentSessionStatus, - #[serde_as(as = "U64Hex")] - /// The time the payment was created at, in milliseconds from UNIX epoch - created_at: u64, - #[serde_as(as = "U64Hex")] - /// The time the payment was last updated at, in milliseconds from UNIX epoch - pub last_updated_at: u64, - /// The error message if the payment failed - pub failed_error: Option, - /// fee paid for the payment - #[serde_as(as = "U128Hex")] - pub fee: u128, -} - -#[serde_as] -#[derive(Clone, Debug, Serialize, Deserialize)] -pub(crate) struct SendPaymentCommandParams { - /// the identifier of the payment target - target_pubkey: Option, - - /// the amount of the payment - #[serde_as(as = "Option")] - amount: Option, - - /// the hash to use within the payment's HTLC - payment_hash: Option, - - /// the TLC expiry delta should be used to set the timelock for the final hop, in milliseconds - #[serde_as(as = "Option")] - final_tlc_expiry_delta: Option, - - /// the TLC expiry limit for the whole payment, in milliseconds, each hop is with a default tlc delta of 1 day - /// suppose the payment router is with N hops, the total tlc expiry limit is at least (N-1) days - /// this is also the default value for the payment if this parameter is not provided - #[serde_as(as = "Option")] - tlc_expiry_limit: Option, - - /// the encoded invoice to send to the recipient - invoice: Option, - - /// the payment timeout in seconds, if the payment is not completed within this time, it will be cancelled - #[serde_as(as = "Option")] - timeout: Option, - - /// the maximum fee amounts in shannons that the sender is willing to pay - #[serde_as(as = "Option")] - max_fee_amount: Option, - - /// max parts for the payment, only used for multi-part payments - #[serde_as(as = "Option")] - max_parts: Option, - - /// keysend payment - keysend: Option, - - /// udt type script for the payment - udt_type_script: Option