mirror of
https://github.com/Such-Software/smirk-backend-core.git
synced 2026-10-17 15:31:38 +02:00
tips: grin config gate, reorg sweep_txid preservation, LWS import retry
- config: grin is a supported tip chain (voucher model verified against the node), so FEATURE_TIPS with only grin enabled no longer fails closed at boot. - social_tips: revert_sweep_on_reorg now preserves sweep_txid instead of nulling it. Address-scan chains (btc/ltc/xmr/wow) overwrite it via confirm_sweep_onchain on the next cycle, so preserving the orphaned value is harmless; grin needs the kernel excess to re-date a re-mined sweep via get_kernel, so nulling it stranded reorged grin tips in 'claiming'. Test updated to assert preservation. - lws import_account: retry the birthday backfill rescan (3 attempts, backoff) so a transient failure no longer strands a new account at the chain tip. The already-registered Some(_) no-op is unchanged, so this cannot reintroduce the balance-reset bug.
This commit is contained in:
+5
-5
@@ -1105,14 +1105,14 @@ impl Config {
|
||||
// Public tips escrow on-chain and deliver by share URL, so an instance
|
||||
// with FEATURE_TIPS on but no chain to escrow on, or no TIP_SHARE_BASE_URL
|
||||
// to build share links from, would advertise a tips subsystem it cannot
|
||||
// actually serve. Fail closed rather than boot broken. (Grin is out of the
|
||||
// tips port, so it is NOT a supported tip chain here.)
|
||||
// actually serve. Fail closed rather than boot broken. Grin tips (voucher
|
||||
// model, verified against the grin node) are supported, so grin counts.
|
||||
if self.features.tips {
|
||||
let c = &self.features.chains;
|
||||
if !(c.btc || c.ltc || c.xmr || c.wow) {
|
||||
if !(c.btc || c.ltc || c.xmr || c.wow || c.grin) {
|
||||
return Err(cfg_err(
|
||||
"FEATURE_TIPS is on but no supported tip chain is enabled — enable at least \
|
||||
one of FEATURE_BTC / FEATURE_LTC / FEATURE_XMR / FEATURE_WOW",
|
||||
"FEATURE_TIPS is on but no supported tip chain is enabled: enable at least \
|
||||
one of FEATURE_BTC / FEATURE_LTC / FEATURE_XMR / FEATURE_WOW / FEATURE_GRIN",
|
||||
));
|
||||
}
|
||||
if self.tip_share_base.is_none() {
|
||||
|
||||
@@ -694,12 +694,20 @@ impl Database {
|
||||
}
|
||||
|
||||
/// Revert a settled (`claimed`) tip back to `claiming` after a reorg orphaned
|
||||
/// its recorded sweep. Clearing `sweep_txid` is LOAD-BEARING: the next
|
||||
/// confirm cycle re-scans the address and records the NEW on-chain winner,
|
||||
/// not the orphaned one. Also clears `sweep_confirmed_dm_sent_at` so a fresh
|
||||
/// "claimed" notify can fire once re-confirmation lands. The
|
||||
/// `status = 'claimed' AND sweep_confirmed_at IS NOT NULL` guard is the only
|
||||
/// path off `claimed`; `None` if the row isn't settled.
|
||||
/// its recorded sweep. Clears the settlement markers (`sweep_confirmed_at`,
|
||||
/// block height/hash) and `sweep_confirmed_dm_sent_at` so a fresh "claimed"
|
||||
/// notify can fire once re-confirmation lands.
|
||||
///
|
||||
/// `sweep_txid` is deliberately PRESERVED. For address-scan chains
|
||||
/// (btc/ltc/xmr/wow) the next confirm cycle re-scans the address and
|
||||
/// `confirm_sweep_onchain` overwrites `sweep_txid` with the new on-chain
|
||||
/// winner, so preserving the orphaned value is harmless. For grin (voucher)
|
||||
/// it is REQUIRED: `probe_grin_spends` dates the re-mined sweep via
|
||||
/// `get_kernel(sweep_txid /* = kernel excess */)`, so nulling it would strand
|
||||
/// the row in `claiming` forever after a reorg.
|
||||
///
|
||||
/// The `status = 'claimed' AND sweep_confirmed_at IS NOT NULL` guard is the
|
||||
/// only path off `claimed`; `None` if the row isn't settled.
|
||||
#[instrument(skip(self))]
|
||||
pub async fn revert_sweep_on_reorg(
|
||||
&self,
|
||||
@@ -709,7 +717,6 @@ impl Database {
|
||||
"UPDATE social_tips \
|
||||
SET status = 'claiming', \
|
||||
sweep_confirmed_at = NULL, \
|
||||
sweep_txid = NULL, \
|
||||
sweep_block_height = NULL, \
|
||||
sweep_block_hash = NULL, \
|
||||
sweep_confirmed_dm_sent_at = NULL, \
|
||||
|
||||
+22
-1
@@ -245,7 +245,28 @@ impl LwsClient {
|
||||
let current =
|
||||
self.account_scan_height(address).await?.unwrap_or(u64::MAX);
|
||||
if start_height < current {
|
||||
self.rescan(vec![address.to_string()], start_height).await?;
|
||||
// The account is now added at the chain tip. If this backfill
|
||||
// rescan fails, the account is stranded at the tip: it reads a
|
||||
// 0 balance and the `Some(_)` short-circuit above (which exists
|
||||
// to prevent the reset loop) means a later re-registration will
|
||||
// NOT retry it. So retry a transient failure here, where we
|
||||
// still know this is a fresh account that owes a backwards scan.
|
||||
let mut attempt = 0u32;
|
||||
loop {
|
||||
match self.rescan(vec![address.to_string()], start_height).await {
|
||||
Ok(()) => break,
|
||||
Err(e) => {
|
||||
attempt += 1;
|
||||
if attempt >= 3 {
|
||||
return Err(e);
|
||||
}
|
||||
tokio::time::sleep(std::time::Duration::from_millis(
|
||||
300 * u64::from(attempt),
|
||||
))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user