/// The same as `TransactionsFirst`, however when we have multiple blocks to connect, we only
/// make a single `best_block_updated` call.
TransactionsFirstSkippingBlocks,
+ /// The same as `TransactionsFirst`, however when we have multiple blocks to connect, we only
+ /// make a single `best_block_updated` call. Further, we call `transactions_confirmed` multiple
+ /// times to ensure it's idempotent.
+ TransactionsDuplicativelyFirstSkippingBlocks,
+ /// The same as `TransactionsFirst`, however when we have multiple blocks to connect, we only
+ /// make a single `best_block_updated` call. Further, we call `transactions_confirmed` multiple
+ /// times to ensure it's idempotent.
+ HighlyRedundantTransactionsFirstSkippingBlocks,
/// The same as `TransactionsFirst` when connecting blocks. During disconnection only
/// `transaction_unconfirmed` is called.
TransactionsFirstReorgsOnlyTip,
use core::hash::{BuildHasher, Hasher};
// Get a random value using the only std API to do so - the DefaultHasher
let rand_val = std::collections::hash_map::RandomState::new().build_hasher().finish();
- let res = match rand_val % 7 {
+ let res = match rand_val % 9 {
0 => ConnectStyle::BestBlockFirst,
1 => ConnectStyle::BestBlockFirstSkippingBlocks,
2 => ConnectStyle::BestBlockFirstReorgsOnlyTip,
3 => ConnectStyle::TransactionsFirst,
4 => ConnectStyle::TransactionsFirstSkippingBlocks,
- 5 => ConnectStyle::TransactionsFirstReorgsOnlyTip,
- 6 => ConnectStyle::FullBlockViaListen,
+ 5 => ConnectStyle::TransactionsDuplicativelyFirstSkippingBlocks,
+ 6 => ConnectStyle::HighlyRedundantTransactionsFirstSkippingBlocks,
+ 7 => ConnectStyle::TransactionsFirstReorgsOnlyTip,
+ 8 => ConnectStyle::FullBlockViaListen,
_ => unreachable!(),
};
eprintln!("Using Block Connection Style: {:?}", res);
pub fn connect_blocks<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, depth: u32) -> BlockHash {
let skip_intermediaries = match *node.connect_style.borrow() {
ConnectStyle::BestBlockFirstSkippingBlocks|ConnectStyle::TransactionsFirstSkippingBlocks|
+ ConnectStyle::TransactionsDuplicativelyFirstSkippingBlocks|ConnectStyle::HighlyRedundantTransactionsFirstSkippingBlocks|
ConnectStyle::BestBlockFirstReorgsOnlyTip|ConnectStyle::TransactionsFirstReorgsOnlyTip => true,
_ => false,
};
node.node.best_block_updated(&block.header, height);
node.node.transactions_confirmed(&block.header, &txdata, height);
},
- ConnectStyle::TransactionsFirst|ConnectStyle::TransactionsFirstSkippingBlocks|ConnectStyle::TransactionsFirstReorgsOnlyTip => {
+ ConnectStyle::TransactionsFirst|ConnectStyle::TransactionsFirstSkippingBlocks|
+ ConnectStyle::TransactionsDuplicativelyFirstSkippingBlocks|ConnectStyle::HighlyRedundantTransactionsFirstSkippingBlocks|
+ ConnectStyle::TransactionsFirstReorgsOnlyTip => {
+ if *node.connect_style.borrow() == ConnectStyle::HighlyRedundantTransactionsFirstSkippingBlocks {
+ let mut connections = Vec::new();
+ for (block, height) in node.blocks.lock().unwrap().iter() {
+ if !block.txdata.is_empty() {
+ // Reconnect all transactions we've ever seen to ensure transaction connection
+ // is *really* idempotent. This is a somewhat likely deployment for some
+ // esplora implementations of chain sync which try to reduce state and
+ // complexity as much as possible.
+ //
+ // Sadly we have to clone the block here to maintain lockorder. In the
+ // future we should consider Arc'ing the blocks to avoid this.
+ connections.push((block.clone(), *height));
+ }
+ }
+ for (old_block, height) in connections {
+ node.chain_monitor.chain_monitor.transactions_confirmed(&old_block.header,
+ &old_block.txdata.iter().enumerate().collect::<Vec<_>>(), height);
+ }
+ }
node.chain_monitor.chain_monitor.transactions_confirmed(&block.header, &txdata, height);
+ if *node.connect_style.borrow() == ConnectStyle::TransactionsDuplicativelyFirstSkippingBlocks {
+ node.chain_monitor.chain_monitor.transactions_confirmed(&block.header, &txdata, height);
+ }
call_claimable_balances(node);
node.chain_monitor.chain_monitor.best_block_updated(&block.header, height);
node.node.transactions_confirmed(&block.header, &txdata, height);
node.chain_monitor.chain_monitor.block_disconnected(&orig.0.header, orig.1);
Listen::block_disconnected(node.node, &orig.0.header, orig.1);
},
- ConnectStyle::BestBlockFirstSkippingBlocks|ConnectStyle::TransactionsFirstSkippingBlocks => {
+ ConnectStyle::BestBlockFirstSkippingBlocks|ConnectStyle::TransactionsFirstSkippingBlocks|
+ ConnectStyle::HighlyRedundantTransactionsFirstSkippingBlocks|ConnectStyle::TransactionsDuplicativelyFirstSkippingBlocks => {
if i == count - 1 {
node.chain_monitor.chain_monitor.best_block_updated(&prev.0.header, prev.1);
node.node.best_block_updated(&prev.0.header, prev.1);
}
}}
}
-
#[macro_export]
#[cfg(any(test, feature = "_bench_unstable", feature = "_test_utils"))]
macro_rules! expect_payment_received {
($node: expr, $expected_payment_hash: expr, $expected_payment_secret: expr, $expected_recv_value: expr) => {
- expect_payment_received!($node, $expected_payment_hash, $expected_payment_secret, $expected_recv_value, None)
+ expect_payment_received!($node, $expected_payment_hash, $expected_payment_secret, $expected_recv_value, None, $node.node.get_our_node_id())
};
- ($node: expr, $expected_payment_hash: expr, $expected_payment_secret: expr, $expected_recv_value: expr, $expected_payment_preimage: expr) => {
+ ($node: expr, $expected_payment_hash: expr, $expected_payment_secret: expr, $expected_recv_value: expr, $expected_payment_preimage: expr, $expected_receiver_node_id: expr) => {
let events = $node.node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
- $crate::util::events::Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat } => {
+ $crate::util::events::Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id } => {
assert_eq!($expected_payment_hash, *payment_hash);
assert_eq!($expected_recv_value, amount_msat);
+ assert_eq!($expected_receiver_node_id, receiver_node_id.unwrap());
match purpose {
$crate::util::events::PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
assert_eq!(&$expected_payment_preimage, payment_preimage);
if payment_received_expected {
assert_eq!(events_2.len(), 1);
match events_2[0] {
- Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat } => {
+ Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id } => {
assert_eq!(our_payment_hash, *payment_hash);
+ assert_eq!(node.node.get_our_node_id(), receiver_node_id.unwrap());
match &purpose {
PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
assert_eq!(expected_preimage, *payment_preimage);