Skip to content

Commit 9f474a0

Browse files
tnullclaude
authored andcommitted
Let the funding output alone identify a funding
A channel becoming pending reported both the output that funds it and that the transaction is a funding of that channel. The second only repeats the first, since a funding output is what a funding transaction is recognised by. It also cost something. One transaction can open several channels, and each reports only its own output; a report stating outright what the transaction is lists one channel, so the next channel's contradicts it and is refused whole, taking its funding output with it. The payment then listed one of the channels opened instead of all of them. A channel becoming ready already reports only its funding output, so the two backstops now agree. Co-Authored-By: HAL 9000 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1 parent 2857678 commit 9f474a0

2 files changed

Lines changed: 98 additions & 8 deletions

File tree

‎src/event.rs‎

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2205,15 +2205,16 @@ where
22052205
"LDK Node has only ever persisted ChannelPending events from rust-lightning 0.0.115 or later",
22062206
);
22072207

2208+
// The funding output alone says what the transaction is, and says it for every
2209+
// channel a batched funding opens: each channel reports its own output, and the
2210+
// reports of one transaction are kept together.
22082211
let channel = Channel { counterparty_node_id, channel_id };
2209-
let facts = ChannelTxFacts::new(funding_txo.txid)
2210-
.with_outputs(
2211-
&channel,
2212-
Some(UserChannelId(user_channel_id)),
2213-
ChannelOutputRole::Funding,
2214-
[funding_txo.vout],
2215-
)
2216-
.with_self_role(TransactionType::Funding { channels: vec![channel.clone()] });
2212+
let facts = ChannelTxFacts::new(funding_txo.txid).with_outputs(
2213+
&channel,
2214+
Some(UserChannelId(user_channel_id)),
2215+
ChannelOutputRole::Funding,
2216+
[funding_txo.vout],
2217+
);
22172218
self.record_channel_tx_facts(facts).await;
22182219

22192220
let event = Event::ChannelPending {

‎src/wallet/mod.rs‎

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12486,6 +12486,95 @@ mod tests {
1248612486
}
1248712487
}
1248812488

12489+
/// The funding output a channel reports is what classifies the transaction that opens it; the
12490+
/// report does not have to say so separately.
12491+
#[tokio::test]
12492+
async fn a_funding_transaction_is_classified_by_the_output_it_creates() {
12493+
let store: Arc<DynStore> = Arc::new(DynStoreWrapper(InMemoryStore::new()));
12494+
let wallet = new_test_wallet(Arc::clone(&store), false).await;
12495+
12496+
let (counterparty_node_id, channel_id) = test_counterparty_and_channel();
12497+
let channel = Channel { counterparty_node_id, channel_id };
12498+
let funding = wallet_paying_tx(&wallet, 5);
12499+
let funding_txid = funding.compute_txid();
12500+
insert_unconfirmed_tx(&wallet, funding.clone());
12501+
12502+
wallet
12503+
.record_channel_tx_facts(ChannelTxFacts::new(funding_txid).with_outputs(
12504+
&channel,
12505+
None,
12506+
ChannelOutputRole::Funding,
12507+
[0],
12508+
))
12509+
.await
12510+
.unwrap();
12511+
observe_unconfirmed(&wallet, &funding).await;
12512+
12513+
let payment = wallet
12514+
.payment_stores
12515+
.payment_store()
12516+
.get(&PaymentId(funding_txid.to_byte_array()))
12517+
.await
12518+
.unwrap()
12519+
.expect("wallet sync records the funding transaction");
12520+
match payment.kind {
12521+
PaymentKind::Onchain {
12522+
tx_type: Some(TransactionType::Funding { channels }), ..
12523+
} => {
12524+
assert_eq!(channels, vec![channel]);
12525+
},
12526+
kind => panic!("unexpected kind {:?}", kind),
12527+
}
12528+
}
12529+
12530+
/// One transaction can open several channels, each of which reports only the output that funds
12531+
/// it. The reports of one transaction are kept together, so the payment lists every channel
12532+
/// opened — which a report stating outright what the transaction is could not do, as the second
12533+
/// channel's would contradict the first's.
12534+
#[tokio::test]
12535+
async fn a_batched_funding_lists_every_channel_it_opens() {
12536+
let store: Arc<DynStore> = Arc::new(DynStoreWrapper(InMemoryStore::new()));
12537+
let wallet = new_test_wallet(Arc::clone(&store), false).await;
12538+
12539+
let (counterparty_node_id, channel_id) = test_counterparty_and_channel();
12540+
let first = Channel { counterparty_node_id, channel_id };
12541+
let second = Channel { counterparty_node_id, channel_id: ChannelId([8u8; 32]) };
12542+
12543+
let mut funding = wallet_paying_tx(&wallet, 6);
12544+
funding.output.push(funding.output[0].clone());
12545+
let funding_txid = funding.compute_txid();
12546+
insert_unconfirmed_tx(&wallet, funding.clone());
12547+
12548+
for (vout, channel) in [(0u32, &first), (1, &second)] {
12549+
wallet
12550+
.record_channel_tx_facts(ChannelTxFacts::new(funding_txid).with_outputs(
12551+
channel,
12552+
None,
12553+
ChannelOutputRole::Funding,
12554+
[vout],
12555+
))
12556+
.await
12557+
.unwrap();
12558+
}
12559+
observe_unconfirmed(&wallet, &funding).await;
12560+
12561+
let payment = wallet
12562+
.payment_stores
12563+
.payment_store()
12564+
.get(&PaymentId(funding_txid.to_byte_array()))
12565+
.await
12566+
.unwrap()
12567+
.expect("wallet sync records the funding transaction");
12568+
match payment.kind {
12569+
PaymentKind::Onchain {
12570+
tx_type: Some(TransactionType::Funding { channels }), ..
12571+
} => {
12572+
assert_eq!(channels, vec![first, second]);
12573+
},
12574+
kind => panic!("unexpected kind {:?}", kind),
12575+
}
12576+
}
12577+
1248912578
/// The node's channel state as a test dictates it: the channels its channel manager lists,
1249012579
/// the monitors its chain monitor has, and the outputs its sweeper tracks, for retention; and
1249112580
/// its channel outputs, for the startup pass.

0 commit comments

Comments
 (0)