Protocol & join path

Documentation

Essential protocol reference, then the static join path — works with zero backend. mfnd JSON-RPC is newline-delimited TCP; browsers need an HTTP proxy for live stats only.

Privacy-tx fees fund a self-balancing storage treasury; users prepay an endowment sized for perpetual bytes; operators prove random chunk possession every block or lose pay (and eventually bond).

Upstream sourceView codebasegithub.com/shanecloonan/permawrite
1

Privacy

RingCT / CLSAG stack — what hides on-chain and what stays visible.

What is hidden

  • Sender — CLSAG ring signature over 16 decoys (uniform ring-16 is consensus law)
  • Receiver — CryptoNote stealth one-time addresses (view key + spend key)
  • Amounts — Pedersen commitments + Bulletproof range proofs
  • Wallet linkability — no stable on-chain address; key images prevent double-spend without revealing which ring member spent

What stays public

  • That a tx happened, input count, output count (not amounts)
  • Fee value (needed for balance equation: Σ inputs = Σ outputs + fee)
  • Block timing / mempool ordering
  • Optional authorship claims (separate Schnorr publishing identity — not ring-signed)

Production policy

Consensus-enforced under uniform-ring tier.

  • min_ring_size = uniform_ring_size = 16 (Monero parity)
  • min_output_count = 2 — reference wallets pad with zero-value Pedersen output if needed
  • min_input_count = 2 — single-input txs rejected when a second UTXO exists (F7)
  • Release binaries: CLSAG only (legacy LSAG gated off)

Wallet / network privacy defaults

  • Default transfer fee: 0.0001 PFP (wallet convention; consensus accepts any fee, mempool = highest-fee-first + RBF)
  • Upload size buckets: payloads padded to next power-of-two before anchoring (consensus rejects non-bucket sizes)
  • Output order shuffled (change position carries no signal)
  • Age-band coin selection; decoys uniform within height bucket
  • View tags (tx v2): 1-byte tag per output → ~256× faster wallet scan
  • Dandelion++ stem relay optional (stem P2P tag 0x11; fluff on normal tx tag)
  • Tor JSON-RPC optional for wallet ↔ node
  • Authorship firewall: claiming key must not derive from wallet view/spend keys

Crypto assumptions

  • ed25519 discrete log, DDH, SHA-256, Bulletproof soundness, Fiat-Shamir
  • Key images reject identity/torsion/small-order points

Not provided by default

  • Upload plaintext is not encrypted — encrypt locally if you need file confidentiality
  • No on-chain deletion; permanence is irreversible
2

Permanence

Off-chain bytes, on-chain anchors — SPoRA audits every block.

Model

  • File bytes live off-chain; chain anchors a fixed-size StorageCommitment (Merkle data_root, size, replication, endowment Pedersen commit)
  • Multiple independent storage operators hold replicas; SPoRA audits every block
  • Privacy fees (90% → treasury) fund ongoing operator payouts; upfront endowment capitalizes the treasury

Chunking & identity

  • Chunk size: 256 KiB (DEFAULT_CHUNK_SIZE)
  • num_chunks = ceil(size_bytes / chunk_size); empty file = 1 chunk of 0 bytes
  • chunk_hash_i = dhash(MERKLE_LEAF, chunk_bytes_i)
  • data_root = binary Merkle root over chunk hashes (content-addressed file ID)
  • Upload size_bytes on-chain = power-of-two bucket (wallet pads raw payload)

Replication

min_replication = 3, max_replication = 32 (consensus bounds)

Endowment formula

C₀ = cost_per_byte_year × size_bytes × replication (first-year cost)
Yield-bearing (r > 0): E₀ = C₀ · (1 + i) / (r − i) where r > i required
Deflation mode (default, r = 0): E₀ = C₀ · (1 + i) / d

inflation_ppb (2%) treated as conservative assumed deflation rate d (Kryder's law). No yield harvested from locked endowment principal; treasury + fees pay operators.

Default endowment params

cost_per_byte_year_ppb
200,000 → 2×10⁻⁴ base units / byte-year / replica
inflation_ppb
20,000,000 (2%/yr)
real_yield_ppb
0 (deflation-funded mode)
slots_per_year
2,629,800
proof_reward_window_slots
7,200 (~1 day anti-hoarding cap)

Worked example

Defaults, 1 GiB, 3× replication:

  • C₀ ≈ 0.006 PFP/year → E₀ ≈ 0.306 PFP upfront endowment (~51× first-year cost)
  • Min upload fee = ceil(E₀ × 10,000 / 9,000) + 0.00001 PFP producer tip ≈ 0.34 PFP

Upload fee gate

fee × fee_to_treasury_bps / 10,000 ≥ Σ required_endowment(new anchors)

Default fee_to_treasury_bps = 9000 → treasury must receive full endowment from fee share.

Endowment binding

  • PFEO: Pedersen opening must match required_endowment (require_endowment_opening: 1)
  • PFER: Bulletproof range proof on endowment surplus (require_endowment_range_proof: 1)

SPoRA — Succinct Proof of Random Access

  • Each block: deterministic challenge picks one chunk index from (prev_block_id, slot, commit_hash)
  • Operator submits ~256 KiB chunk + Merkle path → verified in microseconds (no ZK on hot path)
  • Valid proof → treasury payout (accrual capped by proof_reward_window_slots)
  • First valid proof to producer wins (latency-sensitive); operators must prove regularly or forfeit accrual

Operator economics & slashing

  • Operators paid from treasury (90% of all tx fees + emission backstop when treasury short)
  • storage_proof_reward backstop cap: 0.1 PFP per proof from fresh emission if treasury empty
  • per_slot_payout from endowment yield = 0 at default (r = 0)
  • Operator registration + bond; consecutive missed operator-salted SPoRA audits → bond slash to treasury
  • Roles separated: validators ≠ storage operators (consumer hardware viable for storage)
Merkle root anchored forever; economic incentive to retain bytes; random audits
No SLA on retrieval speed (gateway layer); no delete; no default file encryption
3

Consensus · Supply · Economics · Roles

Three money flows, stake-weighted finality, intentionally separated roles.

Three money flows

  1. 1.Subsidy — fresh PFP minted per block (emission curve)
  2. 2.Fees — per-tx fee split 90% storage treasury / 10% block producer (fee_to_treasury_bps = 9000)
  3. 3.Storage yield — treasury pays SPoRA proofs; emission backstop if treasury short

Emission

Bitcoin halvings + Monero tail.

  • emission(h): 50 PFP/block for blocks 1..8M, then halve every 8M blocks, 8 halvings, then permanent tail
  • initial_reward = 50 PFP/block
  • halving_period = 8,000,000 blocks (~3.04 years @ 12s slots)
  • halving_count = 8 → pre-tail supply ≈ 796.875M PFP
  • tail_emission = 50 >> 8 = 0.19531250 PFP/block forever (~513k PFP/year at tail start ≈ 0.064%/yr and declining)
  • subsidy_to_treasury_bps = 0 default (approved for future fork: 10% of tail → treasury)
  • storage_proof_reward = 0.1 PFP (emission backstop per accepted proof)

Fee summary

ActionDefault costEnforced bySplit
Transfer / claim0.0001 PFP flatWallet only90% treasury / 10% producer
Storage upload~ceil(E₀×10000/9000)+tipConsensus90% treasury / 10% producer
Validator bondfull stakeConsensus100% treasury
Slashingforfeited stake/bondConsensus100% treasury

No fee burn. Flat fee ≠ % of amount sent.

Treasury equilibrium

  • Steady state: treasury_inflow (90% of fees) ≈ storage_reward_per_block
  • High privacy-tx volume → treasury grows (buffer)
  • Low volume → treasury drains → emission backstop mints up to 0.1 PFP/proof

Consensus mechanism

  • Single finalized chain; ~12s slots; stake-weighted VRF sortition (ECVRF over ed25519)
  • Eligibility: vrf_output < (stake/total_stake) × 1.5 × u64::MAX (~1.5 expected proposers/slot)
  • Tie-break: lowest VRF output wins
  • Finality: BLS12-381 aggregated committee signatures; quorum = 66.67% stake (6667 bps)
  • Slashing: equivocation (double-sign headers) + liveness; multiplicative stake reduction
  • Fraud proofs (P2P): invalid blocks, coinbase fraud, invalid CLSAG/SPoRA, ring-membership UTXO witnesses
  • Light clients: header verify + checkpoint logs; full nodes re-execute apply_block regardless of BLS quorum

Network roles

Validators

Consensus, block production, bonded stake, always-on

Storage operators

Hold replicas, answer SPoRA, consumer-grade hardware OK

Observers / light nodes

Sync headers, verify, no stake

Wallets

CLSAG sign, upload, scan

Genesis constitution

Fork legitimacy invariants:

  • tail_emission > 0
  • uniform ring size ≥ 16
  • well-formed endowment pricing params

PFP Chain · Privacy-Funded Permanence

Live network →

Join the mesh

Testnet roles

Role 1 — Observer

Minimum join

Build and sync an observer. Seed nodes auto-dial from the manifest beside genesis; optional explicit dials below.

git clone https://github.com/shanecloonan/permawrite.git
cd permawrite
cargo build -p mfn-node --release --bin mfnd
cargo build -p mfn-cli --release --bin mfn-cli
mfnd --data-dir ./observer-data \
  --genesis mfn-node/testdata/public_devnet_v1.json \
  --store fs \
  --rpc-listen 127.0.0.1:18734 \
  --p2p-listen 127.0.0.1:0 \
  serve

# optional: --p2p-dial 5.161.201.73:19001 # optional: --p2p-dial 5.161.201.73:19002 # optional: --p2p-dial 5.161.201.73:19003

mfn-cli --rpc 127.0.0.1:18734 status
mfn-cli --rpc 127.0.0.1:18734 tip

Expect matching genesis_id and rising tip_height.

Role 2 — Wallet user

Browser or CLI

Prefer the testnet wallet for faucet / balance / send. Or use the CLI against a local observer — keep the seed / wallet JSON private forever.

mfn-cli --rpc 127.0.0.1:18734 --wallet ./alice.json wallet new
mfn-cli --rpc 127.0.0.1:18734 --wallet ./alice.json wallet address
mfn-cli --rpc 127.0.0.1:18734 --wallet ./alice.json wallet scan
mfn-cli --rpc 127.0.0.1:18734 --wallet ./alice.json wallet balance
mfn-cli --rpc 127.0.0.1:18734 --wallet ./alice.json wallet upload ./sample.txt --json

Role 3 — Storage operator

Optional

Build mfn-storage-operator; point it at a synced local observer RPC. No validator keys required. Details in upstream OPERATORS.md.

Never share

  • Validator VRF/BLS seeds
  • Wallet JSON / restore seeds
  • Public RPC URLs on validators
  • Operator API keys

Docs & help