Skip to content

fix: persist cross-chain airdrop dedup state across restarts#2119

Merged
Scottcjn merged 1 commit intoScottcjn:mainfrom
createkr:feat/issue-airdrop-dedup-persistence
Apr 6, 2026
Merged

fix: persist cross-chain airdrop dedup state across restarts#2119
Scottcjn merged 1 commit intoScottcjn:mainfrom
createkr:feat/issue-airdrop-dedup-persistence

Conversation

@createkr
Copy link
Copy Markdown
Contributor

@createkr createkr commented Apr 6, 2026

Fix: persist airdrop dedup state so duplicate claims survive restart

Problem

Duplicate-claim prevention in cross-chain-airdrop was purely in-memory (HashSet inside VerificationPipeline). After any process restart — including every CLI invocation — all dedup state was lost, allowing the same GitHub account or wallet to claim again.

Solution

Introduced a ClaimStore trait with two implementations:

Implementation Persistence Use case
InMemoryClaimStore None (volatile) Backward compat, testing
SqliteClaimStore SQLite file Production, CLI

The pipeline is now generic over S: ClaimStore. VerificationPipeline::new() still defaults to InMemoryClaimStore (no breaking change). A new with_store() constructor accepts any implementation.

The CLI uses SqliteClaimStore by default when built with --features sqlite-store, writing to airdrop_claims.sqlite in the working directory.

Key changes

  • src/claim_store.rs (new) — ClaimStore trait, InMemoryClaimStore, SqliteClaimStore (feature-gated)
  • src/pipeline.rs — generic over S: ClaimStore; new() delegates to with_store(..., InMemoryClaimStore::new())
  • src/bin/airdrop_cli.rs — conditional compilation: SqliteClaimStore when feature enabled, InMemoryClaimStore otherwise
  • Cargo.toml — optional rusqlite dep + sqlite-store feature

Tests

  • 3 new InMemoryClaimStore tests
  • 3 new SqliteClaimStore tests (including test_sqlite_survives_reopen which proves state persists across DB close/reopen)
  • All 43 tests pass with --features sqlite-store
  • All 40 tests pass without the feature

Compatibility

  • No breaking changes — VerificationPipeline::new() signature unchanged
  • sqlite-store feature is opt-in; zero new dependencies for existing users
  • All existing tests pass without modification

@github-actions github-actions bot added BCOS-L1 Beacon Certified Open Source tier BCOS-L1 (required for non-doc PRs) size/XL PR: 500+ lines labels Apr 6, 2026
@createkr
Copy link
Copy Markdown
Contributor Author

createkr commented Apr 6, 2026

For bounty payout, please use RTC wallet: RTC1d48d848a5aa5ecf2c5f01aa5fb64837daaf2f35.

@Scottcjn
Copy link
Copy Markdown
Owner

Scottcjn commented Apr 6, 2026

Merged. Payment: 40 RTC. Addresses #2118.

Persists airdrop deduplication state to SQLite via rusqlite so restart no longer loses the in-memory set. Prevents double-claiming across node restarts.

@Scottcjn Scottcjn merged commit 353734a into Scottcjn:main Apr 6, 2026
8 of 9 checks passed
@Scottcjn
Copy link
Copy Markdown
Owner

Scottcjn commented Apr 6, 2026

@createkr — Heads up: Nodes 1 and 2 are updated with all your security fixes (46 PRs merged since last deploy). Node 4 (your HK CognetCloud) is still running old code. When you get a chance, pull latest from main and restart. The P2P secret fix (#2047) requires a real RC_P2P_SECRET env var — the hardcoded default is now rejected. Generate with openssl rand -hex 32.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

BCOS-L1 Beacon Certified Open Source tier BCOS-L1 (required for non-doc PRs) size/XL PR: 500+ lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants