Add durable buyer-bound Lightning recovery and explicit native retry

Preserve original invoice preimages, private snapshots and exposure provenance; serialize rail admission and retire native-only failures before explicit replacement. Qualify 55 focused UI tests and vue-tsc. Expanded 17 engine cases and combined backend acceptance remain pending; six earlier engine cases passed in isolation. No live payment or publication.
This commit is contained in:
archipelago
2026-10-07 00:32:39 -04:00
parent ed96df0ac3
commit fba3273c67
18 changed files with 2525 additions and 109 deletions
+38
View File
@@ -1923,3 +1923,41 @@ pub(crate) async fn publish_snapshot_offer(
)
.await
}
/// Commit a new invoice only while its exact selected share remains current.
/// Caller holds the invoice journal; catalog writers do not acquire that journal.
pub(crate) async fn publish_snapshot_invoice(
data_dir: &Path,
original: &ContentItem,
journal: &crate::content_lightning::Journal,
binding: crate::content_lightning::Binding,
retained: crate::content_lightning::RetainedFile,
) -> Result<crate::content_lightning::SellerRecord> {
let _held = CATALOG_WRITES.lock().await;
let catalog = load_catalog(data_dir).await?;
let current = catalog
.items
.iter()
.find(|item| item.id == original.id)
.context("Content was unshared before invoice preparation")?;
anyhow::ensure!(
serde_json::to_value(current)? == serde_json::to_value(original)?,
"Shared content terms changed before invoice preparation"
);
anyhow::ensure!(
binding.content_id == original.id
&& retained.filename == original.filename
&& retained.mime_type == original.mime_type
&& retained.size == original.size_bytes
&& matches!(&original.access, AccessControl::Paid { price_sats, .. } if *price_sats == binding.price_sats)
&& method_accepted(&original.access, "lightning"),
"Invoice snapshot terms changed"
);
let visible = match &original.availability {
Availability::Nobody => false,
Availability::AllPeers => true,
Availability::Specific { peers } => peers.contains(&binding.buyer_did),
};
anyhow::ensure!(visible, "Item is not shared with this invoice buyer");
journal.prepare_seller_source(binding, Some(retained))
}