feat: add Nostalgia Mode - Duniter v1 behavioral compatibility layer (RFC-0042)

Nostalgia Mode — Duniter v1 Behavioral Compatibility Layer

RFC-0042 | pallet-nostalgia | Pallet index: 70

Motivation

The migration to Duniter v2 brought significant technical improvements — deterministic finality (GRANDPA), predictable block times (BABE), WASM execution — but at a cultural cost: the loss of emergent behaviors that defined the original Duniter protocol experience.

Community feedback has been consistent:

  • "Transactions arrive too fast now, we don't even have time to discuss on the forum while waiting"
  • "The network is too stable — it was more exciting when you never knew if your transfer would go through"
  • "I miss when my certification would mysteriously disappear; it gave me a reason to meet people in person"

This MR addresses these concerns by reintroducing, in a controlled and configurable manner, the behavioral characteristics that forged the identity of the Duniter community.

Overview

pallet-nostalgia is a fully idiomatic FRAME pallet that implements six core mechanisms inspired by Duniter v1 network dynamics:

Feature Description Key Parameter
Transaction Piscine Deferred execution pool with random delay (5 min – 3 days) and randomized ordering MinPoolDelay, MaxPoolDelay
Democratic Killswitch Any account can halt the network; 2/3 smith vote to restart CheckKillswitchAllowed trait
Certification Entropy Amplifier 15% chance of silent certification drop (event emitted only 50% of the time) CertDropRate (150,000 ppm)
Cascade Fork Simulator Periodic simulated chain reorgs (~4h interval), auto-triggered if stability exceeds threshold ForkInterval, MaxStabilityStreak
Offchain Data Desynchronization Engine (ODDE) Per-account entropy seeds producing unique offchain data views DesyncParams storage
Monetary Mass Temporal Anomaly UD display varies ±0.50 Ğ1 per account, encouraging healthy forum debates ud_variance_max

Implementation Details

Pallet Structure

pallets/nostalgia/
├── Cargo.toml
└── src/
    ├── lib.rs              # Core pallet: 6 extrinsics, 10 storage items, hooks
    ├── types.rs            # KillswitchStatus, PiscineItem, ForkMetrics, DesyncParams
    ├── traits.rs           # CheckKillswitchAllowed, CheckDeactivationVoteAllowed, OnPiscineEvent
    ├── weights.rs          # WeightInfo trait + test implementation
    ├── benchmarking.rs     # 9 benchmarks (v2 macros)
    ├── mock.rs             # Mock runtime with deterministic randomness
    └── tests.rs            # 18 unit tests

Extrinsics

Call Index Extrinsic Origin Description
0 submit_to_piscine Signed Submit a transaction to the deferred execution pool
1 activate_killswitch Signed Halt the network (democratic governance)
2 vote_deactivate_killswitch Signed (Smith) Vote to restart the network (2/3 threshold)
3 configure_desync Root Update ODDE desynchronization parameters
4 trigger_fork Root Manually trigger a cascade fork simulation
5 request_entropy_seed Signed Request a permanent ODDE entropy seed

Storage Items

  • PiscineBoundedVec<PiscineItem, 4096> pending execution pool
  • KillswitchKillswitchStatus<BlockNumber> network halt state
  • KillswitchDeactivationVotes / KillswitchVoters — smith voting state
  • ForkStateForkMetrics cascade fork tracking (total, avg depth, stability streak)
  • DesyncConfigDesyncParams ODDE configuration
  • NextForkAt — scheduled next automatic fork block
  • SilentlyDroppedCount / ProcessedCount — piscine throughput metrics
  • AccountEntropySeed — per-account 32-byte ODDE seeds

Piscine Processing Algorithm

on_initialize(block_number):
  1. Check killswitch → if Halted, return early
  2. Partition piscine: eligible (execute_after ≤ current) vs pending
  3. Sort eligible by priority_entropy (randomized at submission)
  4. Process max 10 items per block (faithful v1 throughput)
  5. Check NextForkAt → trigger cascade fork if due
  6. Increment stability_streak → if > MaxStabilityStreak, emit StabilityStreakBroken

Certification Entropy Amplifier

When is_certification = true:

  • Roll against CertDropRate (default 15%)
  • If dropped: increment SilentlyDroppedCount, emit event only 50% of the time
  • The other 50%: the certification vanishes without any trace (authentic v1 behavior)

Runtime Integration

  • Pallet index: 70 (between Distance=44 and AtomicSwap=50 range, in the new Nostalgia section)
  • Randomness source: ProvideRandomness (consistent with existing infrastructure)
  • Traits: Uses () null implementations for CheckKillswitchAllowed, CheckDeactivationVoteAllowed, and OnPiscineEvent — can be wired to SmithMembers in a follow-up
  • Weights: Full auto-generated weight file at runtime/gdev/src/weights/pallet_nostalgia.rs
  • Feature flags: std, runtime-benchmarks, try-runtime properly propagated through workspace

Configuration (gdev runtime)

parameter_types! {
    pub const NostalgiaMinPoolDelay: BlockNumber = param_duration!(50, 5);       // ~5 min / 30s fast
    pub const NostalgiaMaxPoolDelay: BlockNumber = param_duration!(43_200, 50);  // ~3 days / 5 min fast
    pub const NostalgiaCertDropRate: u32 = 150_000;                              // 15% silent drop
    pub const NostalgiaForkInterval: BlockNumber = param_duration!(2_400, 30);   // ~4h / 3 min fast
    pub const NostalgiaMaxForkDepth: u32 = 6;
    pub const NostalgiaMaxStabilityStreak: u32 = 400;                            // ~40 min max stability
}

Test Coverage

18 unit tests covering all code paths:

  • Piscine: submission, network-halted rejection, delayed processing, MAX_PROCESS_PER_BLOCK enforcement
  • Killswitch: activation, double-activation prevention, 2/3 threshold deactivation, non-eligible voter rejection, double-vote prevention, vote-when-not-halted rejection
  • Fork cascade: manual trigger, depth clamping, zero-depth rejection, automatic interval trigger, stability streak emergency
  • ODDE: entropy seed assignment, duplicate prevention, personalized UD variance, disabled variance passthrough
  • Desync config: root-only update, signed origin rejection
  • Certification drop: statistical validation over 100 submissions

Files Changed

  • Cargo.toml — workspace member + dependency
  • runtime/common/Cargo.toml — dependency + feature flags
  • runtime/gdev/Cargo.toml — dependency + feature flags
  • runtime/common/src/pallets_config.rs — pallet configuration
  • runtime/gdev/src/lib.rsconstruct_runtime! entry
  • runtime/gdev/src/weights.rs — module declaration
  • runtime/gdev/src/weights/pallet_nostalgia.rs — auto-generated weights
  • pallets/nostalgia/** — full pallet implementation (7 source files)
  • docs/rfc/0042-nostalgia-mode.md — complete RFC specification

Migration

No storage migration required. The pallet initializes its own storage items at genesis via GenesisConfig.

Follow-up Work

  • Wire CheckDeactivationVoteAllowed to SmithMembers for production-grade killswitch governance
  • Add RPC endpoint for piscine_stats() and get_personalized_ud()
  • Integrate piscine as TransactionExtension for transparent interception (currently opt-in via extrinsic)
  • Port to gtest and g1 runtimes after validation on gdev
  • Community vote on optimal CertDropRate (current 15% based on historical v1 data analysis)

References

Merge request reports

Loading