Initial workspace: scaffold + constitution + spec documents

Rust workspace with 5 crates (mesh-types, mesh-crypto, mesh-network,
mesh-validator, mesh-wallet), PROJECT_CONSTITUTION.md for CHORUS
automated ingestion, and the full MESH protocol specification suite.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
anthonyrawlins
2026-03-23 20:49:44 +11:00
commit f2d62fa03d
21 changed files with 5272 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,622 @@
**MESH PROTOCOL**
*MVP Roadmap*
From Specification to Working Tier 1 Testnet
Solo Bootstrap Edition
Budget: \$0 \| Team: 1 person \| Timeline: 6--9 months evenings/weekends
March 2026
**1. The Uncomfortable Truth About Solo Bootstrapping**
The specification suite describes a complete protocol with 10
specifications, three consensus tiers, a privacy layer, multi-hop
routing, and formal verification. Building all of that is a multi-year,
multi-million-dollar engineering effort. You are one person in Ballarat
working evenings and weekends.
The entire strategy for the next 6--9 months is therefore about one
thing: proving the core thesis works with the minimum possible code, so
that the project becomes credible enough to attract contributors and
funding. Everything else is deferred.
**The core thesis:** Byzantine Consistent Broadcast can settle everyday
retail payments in under 300ms, without a blockchain, without a native
token, and with confidential amounts. If you can demonstrate this ---
two wallets transacting through validators with Pedersen-committed
amounts and Bulletproofs+ range proofs --- you have something no one
else has built.
**1.1 What the MVP IS**
- **Tier 1 only.** No Tier 2 DAG-BFT, no Tier 3 aBFT. Those are
complex and not needed to prove the thesis.
- **Single asset type.** No multi-currency routing, no connectors, no
exchange. Transfers in a single test token.
- **Confidential amounts.** Pedersen commitments and Bulletproofs+
from day one. This is the privacy differentiator --- without it,
you're just another FastPay clone.
- **4 validators on a local testnet.** Running on your own machines or
cheap VPS instances. No public network.
- **A command-line wallet.** No mobile app, no GUI. A CLI tool that
can create accounts, send payments, and verify receipts.
- **Honest crash-only fault tolerance.** The MVP validators can crash
and recover. Full Byzantine fault tolerance is implemented in the
protocol logic, but adversarial testing comes later.
**1.2 What the MVP is NOT**
- Not a mobile app. Not a web wallet. Not a consumer product.
- Not multi-currency. Not connected to real money. Not connected to
anything external.
- Not formally verified. The TLA+ and Tamarin work comes after the MVP
proves the architecture.
- Not audited. No security firm review. This is a testnet with test
tokens.
- Not horizontally scaled. Single-shard validators. Scaling comes when
there's something worth scaling.
- Not post-quantum. Suite 0 (Ed25519) only. PQ cipher suites are
specified but not implemented.
**2. Technology Stack Decisions**
Every choice here optimises for solo-developer velocity and correctness,
not for theoretical perfection.
----------------- ------------------ ------------------------------------
**Component** **Choice** **Rationale**
Language Rust Memory safety without GC. The entire
crypto ecosystem (dalek, curve25519,
bulletproofs) is Rust-native. The
spec mandates constant-time crypto,
and Rust's type system helps enforce
this. You'll need to learn it if you
haven't, but it's the right
long-term bet.
Async runtime Tokio De facto standard for async Rust.
All networking, timers, and I/O
through Tokio. Single dependency for
the entire async layer.
Networking quinn (QUIC) Rust QUIC implementation built on
Tokio. The spec mandates QUIC
(SPEC-009). Quinn is mature,
well-maintained, and handles TLS 1.3
via rustls.
Crypto: ed25519-dalek Mature, audited, constant-time
signatures Ed25519. Used by Signal, Solana, and
dozens of other projects.
Crypto: curve25519-dalek Ristretto group operations for
commitments Pedersen commitments. Same library
family as ed25519-dalek.
Well-tested.
Crypto: range bulletproofs The dalek-cryptography Bulletproofs
proofs (dalek) implementation. Uses Merlin
transcripts for Fiat-Shamir.
MIT-licensed. This is the same
codebase Monero evaluated.
Crypto: hashing sha3 crate NIST SHA3-256 and SHAKE256. Pure
Rust, no C dependencies.
Crypto: symmetric chacha20poly1305 ChaCha20-Poly1305 AEAD. RustCrypto
crate project. For memo encryption.
Serialisation Custom binary (no The spec mandates deterministic
serde) byte-level encoding. Serde's binary
formats (bincode, etc.) do not
guarantee this across versions.
Write encode/decode by hand for all
protocol structures. It's tedious
but essential for interoperability.
Storage sled (embedded DB) Embedded key-value store. No
external database dependency. Stores
account states, certificates, and
equivocation records. Can be
replaced later with RocksDB if
performance demands it.
Build/CI cargo + GitHub Standard Rust toolchain. CI runs
Actions tests, clippy, and cargo-audit on
every push.
----------------- ------------------ ------------------------------------
> **DELIBERATE OMISSIONS:** No web framework. No REST API. No gRPC. No
> GraphQL. The MVP wallet talks directly to validators over QUIC. Adding
> an HTTP API is a week of work later if needed. Don't build it now.
**3. Milestone Plan**
Each milestone is a shippable unit: it compiles, it passes tests, and it
does something demonstrable. Milestones are sequential --- each depends
on the previous one. Estimated hours assume 10--15 hours/week of focused
coding time.
**Milestone 0: Project Skeleton (Week 1--2, \~20 hours)**
*Deliverable:* A Rust workspace with crate structure, CI pipeline, and
all dependencies pinned.
Set up the mono-repo workspace with these crates:
- mesh-types --- All data structures from SPEC-002. Transition,
AccountState, SettlementCertificate, etc. Pure data, no logic. 100%
test coverage of serialisation round-trips.
- mesh-crypto --- Thin wrapper around dalek crates. Key generation,
signing, verification, Pedersen commit, Bulletproofs+ prove/verify.
Domain-separated hashing per SPEC-006.
- mesh-validator --- Validator node binary. Empty for now.
- mesh-wallet --- CLI wallet binary. Empty for now.
- mesh-network --- QUIC transport layer. Empty for now.
This milestone is pure scaffolding. Write no protocol logic. Get the
types compiling, get serialisation working, get CI green. This is the
foundation everything else builds on. Do not skip it.
> **DEFINITION OF DONE:** cargo build succeeds. cargo test passes.
> GitHub Actions CI is green. All SPEC-002 data structures are defined
> with encode/decode functions. Round-trip property tests (encode then
> decode) pass for every type.
**Milestone 1: Cryptographic Core (Week 3--5, \~40 hours)**
*Deliverable:* A standalone library that can create Pedersen-committed
transactions with Bulletproofs+ range proofs, and verify them.
Implement in mesh-crypto:
1. **Key generation:** Generate Ed25519 key pair + derive view key per
SPEC-006 §6.4.1.
2. **Pedersen commitment:** Commit(value, blinding) = value·G +
blinding·H. Generate H from the nothing-up-my-sleeve construction
per SPEC-007 §7.2.
3. **Range proof:** Generate and verify Bulletproofs+ proofs for values
in \[0, 2⁶⁴). Use dalek Bulletproofs with Merlin transcripts. Bind
to transaction hash per SPEC-007 §7.3.2.
4. **Balance verification:** Given input commitments and output
commitments, verify that the sum of inputs equals the sum of outputs
(homomorphic check).
5. **Transaction construction:** Build a complete Transition struct per
SPEC-002 §2.4.1 with committed amounts and range proofs. Sign with
sender's key.
6. **Transaction validation:** Implement all 9 validation rules from
SPEC-002 §2.4.3. Every rule, in order, with the specified error
codes.
Write test vectors for each operation. These become the reference test
suite that any future implementation must pass.
> **DEFINITION OF DONE:** Can programmatically: create an account,
> construct a 500-unit confidential transfer with range proof, verify
> the transfer, confirm the balance check passes. Can also construct and
> detect an invalid transfer (negative value, insufficient balance,
> wrong signature) for each of the 9 validation rules.
>
> **THIS IS THE HARDEST MILESTONE:** Getting Bulletproofs+ integrated
> correctly with custom Pedersen generators and domain-separated
> transcripts is the single most technically demanding part of the MVP.
> Budget extra time. Read the dalek documentation twice. Write a
> throwaway prototype first if needed.
**Milestone 2: Network Layer (Week 6--8, \~35 hours)**
*Deliverable:* Two nodes can establish a QUIC connection, perform the
MESH handshake, and exchange protocol messages.
Implement in mesh-network:
7. QUIC server and client using quinn. TLS 1.3 with self-signed
certificates for testnet.
8. Message framing: \[length: u32\]\[type: u8\]\[payload\] per SPEC-009
§9.4.
9. HandshakeMessage exchange per SPEC-009 §9.3. Verify identity key
signature.
10. Stream multiplexing: separate QUIC streams for Tier 1 messages vs.
control messages.
11. PING/PONG keepalive per SPEC-009 §9.9.
12. Message dispatch: receive a framed message, deserialise, route to
handler by type.
For the MVP, skip peer discovery (SPEC-009 §9.6). Validators are
configured with a static list of peer addresses. This is fine for a
4-node testnet.
> **DEFINITION OF DONE:** Start two nodes. They connect over QUIC,
> exchange handshakes, verify identities. One sends a PING, the other
> responds with PONG. Message framing is correct (test with malformed
> frames to verify rejection). Logs show the full handshake sequence.
**Milestone 3: Validator Core (Week 9--13, \~50 hours)**
*Deliverable:* A validator node that can receive Tier 1 vote requests,
validate transitions, and return signed votes.
Implement in mesh-validator:
13. **Account state store:** sled database holding AccountState per
public key. CRUD operations.
14. **Validator key management:** Load signing key from file. For MVP,
generate during first run and save.
15. **VOTE_REQUEST handler:** Receive a Transition from a client. Run
all 9 validation rules. If valid, sign a vote (SPEC-003 §3.3.1 step
2). Return VOTE_RESPONSE. If invalid, return VOTE_REJECT with error
code.
16. **Equivocation detection:** Maintain a map of (sender, sequence) →
transition_hash. On any conflict, produce EquivocationProof and
reject.
17. **SETTLEMENT_CERTIFICATE handler:** Receive a certificate. Verify ≥
2f+1 valid votes. Apply state transition: debit sender, credit
recipients. Store certificate.
18. **Account query handler:** Respond to QUERY_ACCOUNT with current
account state (balance commitment, sequence number).
This is the heart of the system. Take your time. Write exhaustive tests.
Every edge case in the validation rules needs a test.
> **DEFINITION OF DONE:** Start a validator. Submit a valid transition
> via the network layer. Receive a signed vote. Submit an invalid
> transition (bad signature). Receive a rejection with the correct error
> code. Submit two conflicting transitions. Receive an equivocation
> proof.
**Milestone 4: CLI Wallet (Week 14--17, \~40 hours)**
*Deliverable:* A command-line wallet that can create accounts, send
confidential payments, and verify receipts.
Implement in mesh-wallet:
19. **Key management:** mesh-wallet keygen → generates key pair, saves
to file. mesh-wallet show-address → prints public key.
20. **Send payment:** mesh-wallet send \--to \<pubkey\> \--amount
\<value\> \--asset \<id\>. Constructs a Transition with
Pedersen-committed amount and Bulletproofs+ range proof. Broadcasts
to all configured validators. Collects 2f+1 votes. Assembles
certificate. Broadcasts certificate. Prints certificate hash.
21. **Check balance:** mesh-wallet balance. Queries a validator for
current account state. Locally decrypts the committed balance using
stored blinding factors.
22. **Receive:** mesh-wallet receive \--cert \<hash\>. Submits a receive
transition referencing the certificate as a causal dependency.
23. **Transaction history:** mesh-wallet history. Lists all sent and
received settlements with their certificate hashes.
The wallet stores blinding factors locally (in a sled database keyed by
transaction hash). Without the blinding factors, the committed balance
is meaningless. This is the wallet's most sensitive data.
> **DEFINITION OF DONE:** Alice creates an account. The genesis
> validator credits Alice's account with 10,000 test tokens (on-ramp via
> a hardcoded genesis transition). Alice sends 500 tokens to Bob. Bob
> receives them. Alice checks her balance: 9,500 (minus fee). Bob checks
> his balance: 500. Both balances are Pedersen-committed --- the
> validators never saw the plaintext amounts.
**Milestone 5: Multi-Validator Testnet (Week 18--21, \~35 hours)**
*Deliverable:* A 4-validator testnet where any validator can crash and
the system continues operating.
This milestone takes the single-validator demo and makes it a real
distributed system:
24. **Config file:** TOML configuration for validator: listen address,
peer addresses, validator set (public keys), epoch number.
25. **Multi-validator vote collection:** Wallet connects to all 4
validators, sends VOTE_REQUEST to all, collects 3 votes (2f+1 with
n=4, f=1), assembles certificate.
26. **Certificate propagation:** Wallet broadcasts certificate to all
validators. All validators apply the state transition. State is
consistent across all 4.
27. **Crash recovery:** Kill one validator. Verify the system still
works (wallet can collect 3/3 remaining votes). Restart the killed
validator. It catches up by requesting missed certificates from
peers.
28. **Docker Compose:** A docker-compose.yml that starts 4 validators
and an initial wallet. One command to launch the entire testnet.
> **DEFINITION OF DONE:** docker compose up starts 4 validators. Run the
> end-to-end send/receive flow. Kill one validator. Repeat the flow ---
> still works. Restart the killed validator. It syncs up. All 4
> validators have identical account state.
**Milestone 6: Benchmarks and Documentation (Week 22--24, \~25 hours)**
*Deliverable:* Performance numbers, a README that explains what this is,
and a grant application package.
29. **Latency benchmark:** Measure end-to-end time from wallet send to
certificate receipt. Run on 4 VPS instances (Sydney + Melbourne +
Perth + Brisbane or similar). Target: \<300ms. Document results.
30. **Throughput benchmark:** Flood a single validator with vote
requests. Measure transactions per second. This establishes a
baseline.
31. **Range proof benchmark:** Measure Bulletproofs+ prove and verify
time on a mid-range Android phone (or equivalent ARM device).
Target: prove \< 500ms, verify \< 10ms.
32. **README.md:** What MESH is, what the MVP does, how to run it,
what's next. Clear, honest, no hype.
33. **Grant application:** Prepare materials for NLnet Foundation,
Sovereign Tech Fund, and OTF FOSS Sustainability Fund. Include:
problem statement, demo video, benchmark results, specification
suite, budget request for next phase.
> **DEFINITION OF DONE:** Published GitHub repo with working code,
> passing CI, benchmark results in the README, a 3-minute demo video of
> Alice paying Bob through 4 validators, and at least one grant
> application submitted.
**4. Crate Dependency Map**
The dependency structure ensures that each crate can be developed and
tested independently:
mesh-wallet (binary)
└─ mesh-network (QUIC transport)
└─ mesh-crypto (signatures, commitments, proofs)
└─ mesh-types (data structures, serialisation)
mesh-validator (binary)
└─ mesh-network
└─ mesh-crypto
└─ mesh-types
└─ sled (embedded storage)
mesh-network
└─ quinn (QUIC)
└─ rustls (TLS 1.3)
└─ mesh-types
mesh-crypto
└─ ed25519-dalek
└─ curve25519-dalek (Ristretto)
└─ bulletproofs (dalek)
└─ merlin (Fiat-Shamir transcripts)
└─ sha3
└─ chacha20poly1305
└─ mesh-types
mesh-types (zero external dependencies)
**5. Risk Register**
Honest accounting of what could go wrong and what to do about it.
------------------- --------------------------- -----------------------
**Risk** **Impact** **Mitigation**
Bulletproofs+ Delays Milestone 1 by 2--4 Start with the dalek
integration is weeks. This is the most bulletproofs example
harder than technically demanding part. code. Build a minimal
expected standalone test
(commit, prove, verify)
before integrating with
the full transaction
structure. If you're
stuck after 2 weeks,
pivot to plaintext
amounts for the MVP and
add confidentiality in
the next phase.
Rust learning curve You know Python and JS. Spend week 0 (before
Rust's ownership model will Milestone 0) doing the
slow you down initially. first 12 chapters of
The Rust Programming
Language book. Write a
toy project. Don't try
to learn Rust while
building MESH.
QUIC/quinn Quinn is powerful but has a Wrap quinn in a thin
complexity steep API. Connection abstraction layer early
management and error (mesh-network). All
handling are non-trivial. MESH code talks to your
abstraction, not to
quinn directly. If
quinn becomes painful,
you can swap it for
s2n-quic or fall back
to TCP+TLS for the
testnet.
Motivation loss / Solo project, no external Set a public milestone.
burnout accountability, no revenue. Announce what you're
The most common cause of building on a relevant
open-source project death. forum (r/rust, a crypto
research mailing list,
or similar). External
accountability helps.
Ship Milestone 0 within
2 weeks of starting ---
early momentum matters.
Scope creep The spec has 10 documents. The milestones above
The temptation to "just add are the scope. If it's
Tier 2" or "just add not in a milestone, it
stealth addresses" is real. doesn't exist yet.
Write ideas down in a
FUTURE.md file and
close the mental loop.
Someone builds this The uncertified DAG + Publish the
first confidential transaction specification suite
space is active. Another now. Establishing prior
project could announce art and an open
something similar. standard is more
valuable than a head
start in code. If
someone else builds a
compatible
implementation, that's
a success, not a
threat.
------------------- --------------------------- -----------------------
**6. What Comes After the MVP**
Once the Tier 1 testnet is working and grant applications are out, the
project enters Phase 1 (funded development). This is the Phase 1 roadmap
at headline level --- detailed specs already exist.
**6.1 With Funding (\$50--100K AUD, 1--2 developers)**
- **Tier 2 DAG-BFT consensus** (SPEC-004). This unlocks multi-party
settlements and atomic swaps. 3--4 months of work.
- **Stealth addresses** (SPEC-007 §7.4). Recipient privacy. 2--3
weeks.
- **Multi-asset support** (SPEC-002 §2.7 Value Semiring). Multiple
asset types in the same testnet. 3--4 weeks.
- **Basic connector** (SPEC-008). Single-hop value routing between two
asset types. 4--6 weeks.
- **First security audit** (design review). \$200--300K AUD. This is
the single biggest expense and the single most important credibility
milestone.
**6.2 With Serious Funding (\$250K+ AUD, 3--5 developers)**
- **Tier 3 aBFT fallback** (SPEC-005). Complete consensus safety net.
- **TLA+ formal verification** of Tier 1 safety properties.
- **Hybrid post-quantum cipher suite** (Suite 1). Ed25519 + ML-DSA-65
hybrid signatures.
- **Mobile wallet SDK** (iOS/Android). This is what makes it real for
retail.
- **Public testnet** with community-operated validators.
- **Mainnet launch** with conservative parameters and permissioned
validator set.
**6.3 Grant Targets**
------------------- ---------------- -------------------------------------
**Fund** **Amount** **Fit**
NLnet Foundation €5K--€50K Strong fit. NLnet funds open internet
(NGI) infrastructure. MESH's
payment-as-protocol narrative aligns
with their mission. Apply under NGI
Zero Entrust or NGI Zero Core.
Sovereign Tech Fund €50K--€1M Good fit. STF funds open digital
infrastructure. The "replacement for
global payment rails" framing
resonates. Longer application
process.
OTF FOSS Fund \$150K--\$400K Decent fit. OTF funds tools that
USD advance internet freedom. The
privacy-by-default angle is strong.
Requires US fiscal sponsor.
Filecoin Foundation \$10K--\$100K Possible fit via their decentralised
(FFDW) USD web grants. Less obvious than the
above.
GitHub Sponsors Variable Good for ongoing small contributions
once the project has visibility.
------------------- ---------------- -------------------------------------
**7. Your Next 7 Days**
Concrete actions to take this week, in order:
34. **Publish the specification suite.** Create a public GitHub
repository. Upload the three spec documents (main suite + addendum +
this roadmap). Add an Apache 2.0 LICENSE file. Add a README that
says: "MESH is an open protocol specification for decentralised
payments. Reference implementation in progress." This establishes
the open standard and creates public accountability.
35. **Register the domain.** mesh-protocol.org or similar. Park it with
a one-page site linking to the GitHub repo. Cost: \~\$15 AUD/year.
36. **Set up the Rust workspace.** cargo new \--lib mesh-types, cargo
new \--lib mesh-crypto, cargo new \--bin mesh-validator, cargo new
\--bin mesh-wallet, cargo new \--lib mesh-network. Add a root
Cargo.toml workspace. Pin all dependency versions. Get CI green.
37. **Write the first 5 structs.** AccountState, Transition, Recipient,
SettlementCertificate, ValidatorVote. With encode/decode. With
round-trip tests. Push to main.
38. **Tell one person.** Not the internet. One person you trust and
respect. Show them the spec. Ask for honest feedback. External
perspective, this early, is invaluable.
*The spec is the easy part. The code is where it becomes real. Start
this week.*

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,633 @@
**MESH PROTOCOL**
*Security Addendum*
Attack Vector Analysis and Formal Mitigations
Errata and amendments to SPEC-003, SPEC-007, SPEC-008, SPEC-010
Version 0.1.1-draft \| March 2026
**Introduction**
This document identifies seven attack vectors against the MESH protocol
as specified in the v0.1.0 specification suite. For each attack, we
provide: the threat description, the root cause in the existing
specification, the formal mitigation, the specification amendments
required, and the mandatory test assertions.
All mitigations in this document are MANDATORY amendments to the
referenced specifications. An implementation that passes all v0.1.0 test
vectors but does not implement these mitigations is NON-CONFORMANT. The
version number of the specification suite is incremented to v0.1.1 upon
adoption of this addendum.
**Severity Classification**
-------------- ----------- -----------------------------------------------
**Severity** **Label** **Definition**
S1 CRITICAL Allows direct theft of funds or creation of
value from nothing. Protocol is broken without
mitigation.
S2 HIGH Allows economic griefing, denial of service
against specific users, or degrades privacy
guarantees.
S3 MEDIUM Allows minor economic advantage or information
leakage under specific conditions.
-------------- ----------- -----------------------------------------------
**AV-001: Free Option PTLC Griefing Attack**
**Severity: S2 (HIGH)**
**1.1 Threat Description**
A malicious sender initiates a multi-hop payment via PTLCs (SPEC-008
§8.4) with a long expiry. The Prepare packet locks the connector's
liquidity for the duration of the expiry. The sender then monitors the
market and decides whether to fulfill or abandon the payment based on
whether the exchange rate has moved in their favour.
This creates a free financial option: the connector bears the full
market risk (their liquidity is locked at a fixed rate) while the sender
has the optionality to walk away at zero cost. Over millions of
transactions, a systematic attacker could extract significant value from
connectors.
**1.2 Root Cause**
SPEC-008 §8.2.1 defines an expiry field on MeshPacket but imposes no
constraint on its maximum value and no cost for locking connector
liquidity. The specification is silent on the relationship between
expiry duration and economic cost.
**1.3 Mitigation**
Three complementary mechanisms:
**1.3.1 Maximum Expiry Decay Per Hop**
Each connector along a payment path MUST reduce the expiry by at least a
minimum decrement. This limits the total lock-up time and ensures that
senders cannot impose arbitrarily long lock-ups.
> **SPEC-008 AMENDMENT §8.4.5 (NEW):** Each connector MUST set the
> outgoing Prepare packet's expiry to at most: outgoing_expiry =
> incoming_expiry - T_hop, where T_hop is a per-connector configuration
> parameter with a RECOMMENDED minimum of 30 seconds. A connector MUST
> reject an incoming Prepare if incoming_expiry - current_time \< T_hop
> (insufficient time to complete the hop). This creates a hard ceiling
> on total path expiry: max_total_expiry = T_hop × max_path_length. With
> T_hop = 30s and max_path_length = 10 hops, the maximum lock-up is 5
> minutes.
**1.3.2 Locking Fee**
Connectors MAY charge a time-value locking fee that is deducted from the
payment regardless of whether it is fulfilled or rejected. This converts
the free option into a priced option.
> **SPEC-008 AMENDMENT §8.5.1 (NEW):** A connector MAY specify a
> locking_fee_rate in its published rate information, expressed as basis
> points per second of lock-up time. The locking fee is: locking_fee =
> amount × locking_fee_rate × (expiry - current_time) / 10000. The
> locking fee is deducted from the transferred amount. If the payment is
> fulfilled, the connector receives the locking fee plus the spread. If
> the payment expires, the connector retains the locking fee. The
> sender's wallet MUST account for locking fees when calculating the
> total cost of a payment.
**1.3.3 Sender Reputation**
Connectors SHOULD maintain a local reputation score for senders based on
their fulfillment rate. Senders who frequently let payments expire
receive lower priority and wider spreads.
> **SPEC-008 AMENDMENT §8.5.2 (NEW):** Connectors SHOULD track the ratio
> of fulfilled to expired Prepare packets per sender public key. A
> connector MAY reject Prepare packets from senders whose fulfillment
> rate falls below a configurable threshold (RECOMMENDED: 80% over the
> last 1000 packets).
**1.4 Test Assertions**
1. Assert that a connector rejects an incoming Prepare with expiry \>
current_time + T_hop × max_path_length.
2. Assert that each hop reduces the outgoing expiry by at least T_hop.
3. Assert that a payment path with 10 hops and T_hop = 30s has a
maximum total lock-up of 300 seconds.
4. Assert that the locking fee is correctly deducted from the amount
when the payment expires without fulfillment.
5. Assert that a sender with a 50% fulfillment rate is rejected by a
connector with an 80% threshold.
**AV-002: Negative Fee Range Proof Bypass**
**Severity: S1 (CRITICAL)**
**2.1 Threat Description**
A sender constructs a transition where the amount commitment is valid
(positive value with valid range proof), but the fee commitment encodes
a negative value. If the conservation check only verifies that
Σ(recipient amounts) + fee = debit amount on the commitment points, and
the fee range proof is missing or weak, the sender effectively increases
their balance by the absolute value of the negative fee.
Example: Sender has balance 1000. Sender creates a transfer of 1000 to
recipient, with fee = -500. Conservation check passes: 1000 + (-500) =
500. But the sender has only been debited 500 from a balance of 1000, so
they retain 500 extra. The recipient gets 1000. 500 units of value have
been created from nothing.
**2.2 Root Cause**
SPEC-002 §2.4.3 specifies RANGE_CHECK (rule 6) and CONSERVATION_CHECK
(rule 7) as separate steps. However, the specification text for
RANGE_CHECK states: "All range proofs MUST verify. This proves all
amounts are non-negative and within the valid range \[0, 2⁶⁴)." The word
"amounts" is ambiguous --- it could be read as referring only to the
recipient amounts, not the fee. This ambiguity is the attack surface.
**2.3 Mitigation**
Eliminate the ambiguity with an explicit enumeration of every commitment
that requires a range proof.
> **SPEC-002 AMENDMENT §2.4.3 Rule 6 (REVISED):** RANGE_CHECK: The
> following range proofs MUST be present and MUST verify: (a)
> transition.range_proof proves transition.amount is in \[0, 2⁶⁴). (b)
> transition.fee_proof proves transition.fee is in \[0, 2⁶⁴). (c) For
> each recipient r in transition.recipients: r.range_proof proves
> r.amount is in \[0, 2⁶⁴). If ANY of these proofs is absent, malformed,
> or fails verification, the validator MUST reject the transition with
> ERR_INVALID_RANGE_PROOF. There are no exceptions. A transition with
> zero fee MUST still include a valid range proof for the zero-value fee
> commitment.
>
> **SPEC-002 AMENDMENT §2.4.3 Rule 7 (CLARIFIED):** CONSERVATION_CHECK:
> The sum of all output commitments MUST equal the input commitment.
> Formally: C_amount = Σ(C_recipient_i) + C_fee. This is verified on the
> commitment points (elliptic curve point addition). The validator MUST
> verify this equation AFTER all range proofs have passed. The
> combination of range proofs (all values non-negative) and conservation
> (inputs = outputs) together guarantee that no value is created or
> destroyed.
**2.4 Test Assertions**
6. Assert that a transition with a valid amount range proof but a
missing fee range proof is rejected with ERR_INVALID_RANGE_PROOF.
7. Assert that a transition with a fee commitment to a negative value
(commitment point C_fee where opening reveals v \< 0) is rejected.
8. Assert that a transition with fee = 0 still requires a valid range
proof for the zero commitment.
9. Assert that a transition where Σ(outputs) + fee = input but fee is
negative is rejected (the range proof on fee catches this before the
conservation check).
10. Assert that crafting a Bulletproofs+ proof for a value outside \[0,
2⁶⁴) produces a proof that fails verification.
**AV-003: Causal Dependency Orphan Exploit**
**Severity: S1 (CRITICAL)**
**3.1 Threat Description**
A Byzantine account references a hash in causal_deps that is a
syntactically valid SHA3-256 digest but does not correspond to any
settlement certificate the validator has seen. If the validator only
checks the hash format (32 bytes, non-zero) without verifying that it
corresponds to a real, certified transition, the Byzantine account could
spend funds it never received.
Worse: if the "ghost" dependency references a fabricated certificate
that credits the attacker with value, the attacker creates money from
nothing by spending against a fictional incoming payment.
**3.2 Root Cause**
SPEC-002 §2.4.3 Rule 8 (CAUSAL_CHECK) states: "Every hash in causal_deps
MUST reference a transition that the validator has already processed and
certified." This is correct in intent but insufficiently precise about
what "processed and certified" means. The specification does not mandate
how the validator performs this lookup or what happens during network
partition when a validator may be behind.
**3.3 Mitigation**
> **SPEC-002 AMENDMENT §2.4.3 Rule 8 (REVISED):** CAUSAL_CHECK: For
> every hash h in transition.causal_deps, the validator MUST verify
> that: (a) h exists in the validator's local certificate store (a hash
> map keyed by transition hash, populated only when a valid
> SettlementCertificate is received and verified per SPEC-003 §3.3.2),
> AND (b) the referenced transition's effects (credit to the current
> sender) have been applied to the sender's AccountState. If any hash is
> not found in the certificate store, the validator MUST return
> ERR_UNKNOWN_DEPENDENCY. The validator MUST NOT attempt to fetch the
> missing certificate from the network during validation. If a
> dependency is genuinely missing (e.g., due to network delay), the
> sender must retry after the dependency has been disseminated to the
> validator.
Additionally, the specification must prevent the case where a sender
references a certified transition that does not actually credit them:
> **SPEC-002 AMENDMENT §2.4.3 Rule 8a (NEW):**
> DEPENDENCY_RELEVANCE_CHECK: For every hash h in
> transition.causal_deps, the validator MUST verify that the referenced
> transition either: (a) has the current sender as a recipient, OR (b)
> is a prior transition by the current sender (same sender public key).
> A transition that references an unrelated third-party certificate as a
> causal dependency MUST be rejected with ERR_IRRELEVANT_DEPENDENCY.
> This prevents an attacker from padding causal_deps with arbitrary
> valid hashes to confuse causal ordering analysis.
**3.4 Test Assertions**
11. Assert that a transition referencing a non-existent hash in
causal_deps is rejected with ERR_UNKNOWN_DEPENDENCY.
12. Assert that a transition referencing a real certificate hash that
does not credit the sender is rejected with
ERR_IRRELEVANT_DEPENDENCY.
13. Assert that a transition referencing a valid, relevant certificate
is accepted.
14. Assert that a validator does NOT fetch missing dependencies from the
network during the validation path (no side-effects during
validation).
**AV-004: Cross-Shard Double-Spend Race**
**Severity: S1 (CRITICAL) if sharding is incorrectly implemented;
MITIGATED BY DESIGN if spec is followed**
**4.1 Threat Description**
A Byzantine sender sends two conflicting transitions (same sequence
number, different recipients) to two different validator shards
simultaneously, hoping that each shard independently certifies a
different transition before the equivocation is detected.
**4.2 Root Cause**
SPEC-003 §3.6 defines the sharding function as shard_id =
hash(account_pubkey) mod num_shards. This is correct and ensures all
transitions for a given account go to the same shard. However, the
specification does not explicitly state that this is a SAFETY-CRITICAL
invariant, and does not specify what happens if an implementation uses a
different sharding function.
**4.3 Mitigation**
This attack is already mitigated by design in the specification, but the
safety-critical nature of the sharding invariant must be made explicit:
> **SPEC-003 AMENDMENT §3.6 (REVISED):** SHARDING SAFETY INVARIANT: All
> transitions for a given account MUST be validated by the same set of
> validators/shards. The shard assignment function is: shard_id =
> SHA3-256(account_pubkey)\[0..4\] mod num_shards, where \[0..4\] takes
> the first 4 bytes of the hash as a little-endian u32. This function
> MUST be deterministic, stateless, and identical across all validators.
> A validator MUST reject a VOTE_REQUEST if the sender's account does
> not map to a shard it is responsible for. An implementation that uses
> a different sharding function, or that allows a transition to be
> processed by a non-responsible shard, is NON-CONFORMANT and UNSAFE.
>
> **SPEC-003 AMENDMENT §3.6.1 (NEW):** CROSS-SHARD VERIFICATION: When a
> validator receives a SettlementCertificate for a recipient that maps
> to a different shard, it MUST verify the certificate (signatures,
> quorum) before crediting the recipient. The certificate provides proof
> that the sender's shard has validated and certified the debit. The
> receiving shard does not re-validate the sender's balance or sequence
> number --- it trusts the certificate as proof that the sender's shard
> has done so. This is safe because the sender's shard is the sole
> authority for the sender's account state.
**4.4 Test Assertions**
15. Assert that a validator rejects a VOTE_REQUEST if
hash(sender_pubkey) mod num_shards does not match the validator's
shard_id.
16. Assert that two conflicting transitions from the same sender (same
sequence, different content) sent to the correct shard produce an
equivocation proof.
17. Assert that a conflicting transition sent to the WRONG shard is
rejected before equivocation detection (at the shard routing check).
18. Assert that the sharding function is deterministic: the same public
key always produces the same shard_id regardless of when or where it
is computed.
**AV-005: Multi-Hop Dust Shaving**
**Severity: S3 (MEDIUM)**
**5.1 Threat Description**
A malicious connector in a multi-hop payment path exploits integer
division rounding to pocket small amounts of value ("dust") from each
transaction. When converting between asset types, the exchange
calculation amount_out = amount_in × rate.numerator / rate.denominator
produces a non-integer result that must be truncated. The connector
keeps the fractional remainder.
At 1 cent per transaction and 1 million transactions per day, this
yields \$10,000/day in stolen dust.
**5.2 Root Cause**
SPEC-002 §2.7 defines the Value Semiring's exchange operation as (a,
id₁) ⊗ rate = (a × rate.numerator / rate.denominator, id₂) but does not
specify the rounding rule or what happens to the remainder.
**5.3 Mitigation**
> **SPEC-002 AMENDMENT §2.7.1 (NEW):** EXCHANGE ROUNDING RULE: All
> exchange calculations MUST use the following formula: amount_out =
> floor(amount_in × rate.numerator / rate.denominator). The remainder
> is: remainder = amount_in × rate.numerator mod rate.denominator. The
> remainder MUST be returned to the sender (not retained by the
> connector). The sender's wallet MUST verify that amount_out +
> remainder (converted back to the source asset) exactly equals the
> original amount_in. This is enforced at the PTLC fulfillment stage:
> the sender only fulfills the PTLC if the received amount matches
> expectations within a sender-specified tolerance (max_slippage).
>
> **SPEC-008 AMENDMENT §8.2.1 (REVISED):** The MeshPacket Prepare
> message MUST include a new field: min_destination_amount (u128). The
> recipient (or the recipient-side connector) MUST reject the Prepare
> with ERR_BELOW_MINIMUM if the actual delivered amount is less than
> min_destination_amount. This shifts control to the sender: the sender
> specifies the minimum acceptable output, and if any connector along
> the path shaves more than the sender is willing to tolerate, the
> payment fails rather than succeeding with missing value.
**5.4 Test Assertions**
19. Assert that for an exchange with rate 3/7 and amount_in = 100, the
result is amount_out = floor(300/7) = 42, remainder = 300 mod 7 = 6.
20. Assert that the remainder is returned to the sender, not retained by
the connector.
21. Assert that a Prepare packet with min_destination_amount = 42 and
actual delivery of 41 is rejected with ERR_BELOW_MINIMUM.
22. Assert that integer overflow in the multiplication amount_in ×
rate.numerator is handled (use u256 intermediate or checked
arithmetic). If overflow would occur, the operation MUST fail with
ERR_OVERFLOW rather than silently truncating.
**AV-006: Epoch-Boundary Replay Attack**
**Severity: S1 (CRITICAL)**
**6.1 Threat Description**
A user collects VOTE_RESPONSE messages from validators in Epoch N but
does not broadcast the SettlementCertificate until Epoch N+1 (or later).
If validators in the new epoch accept certificates signed by the old
epoch's validators, the user could exploit a situation where: (a) a
validator was removed from the set in Epoch N+1 (perhaps for
misbehaviour), and its votes are now suspect, or (b) the validator set
has changed and the "2f+1 quorum" calculated against the new set may not
hold against the old set.
**6.2 Root Cause**
SPEC-002 §2.5.1 defines the SettlementCertificate with an epoch field
and states votes must be from validators in that epoch's set. However,
the specification does not define a grace period or cutoff for
cross-epoch certificate acceptance. The specification is silent on what
happens when a certificate arrives after an epoch boundary.
**6.3 Mitigation**
> **SPEC-003 AMENDMENT §3.3.2 (REVISED):** EPOCH VALIDATION FOR
> CERTIFICATES: When a validator receives a SettlementCertificate, it
> MUST perform the following epoch check: (a) If certificate.epoch ==
> current_epoch: accept and verify votes against the current validator
> set. (b) If certificate.epoch == current_epoch - 1: accept and verify
> votes against the PREVIOUS epoch's validator set. This provides a
> one-epoch grace period for certificates that were assembled just
> before an epoch transition. (c) If certificate.epoch \<
> current_epoch - 1: REJECT with ERR_CERTIFICATE_EXPIRED. Certificates
> more than one epoch old are not accepted. (d) If certificate.epoch \>
> current_epoch: REJECT with ERR_FUTURE_EPOCH. The validator does not
> accept certificates from future epochs.
>
> **SPEC-010 AMENDMENT §10.2.1 (REVISED):** EPOCH STATE RETENTION:
> Validators MUST retain the validator set (public keys) of the
> immediately previous epoch until the end of the current epoch. This
> means at any given time, a validator stores exactly two validator
> sets: the current epoch's and the previous epoch's. Upon transitioning
> to epoch N+1, the validator set for epoch N-1 is discarded.
**6.4 Test Assertions**
23. Assert that a certificate from the current epoch is accepted.
24. Assert that a certificate from epoch N-1 is accepted during epoch N
(grace period).
25. Assert that a certificate from epoch N-2 is rejected with
ERR_CERTIFICATE_EXPIRED during epoch N.
26. Assert that a certificate from epoch N+1 is rejected with
ERR_FUTURE_EPOCH during epoch N.
27. Assert that votes are verified against the CORRECT epoch's validator
set, not the current epoch's set, when the certificate's epoch
differs from the current epoch.
28. Assert that a certificate assembled with votes from a validator that
was present in epoch N but removed in epoch N+1 is still accepted
during epoch N+1 (grace period) because the votes are valid for
epoch N.
**AV-007: Stealth Address Unlinkability Breach via Ephemeral Key Reuse**
**Severity: S2 (HIGH)**
**7.1 Threat Description**
If a sender reuses an ephemeral key r when constructing stealth
addresses for two different payments to the same recipient, the
resulting stealth addresses P_stealth_1 and P_stealth_2 will be computed
from the same shared secret S, making them linkable. An observer who
sees the same ephemeral public key R in two different transaction memos
can conclude that both payments went to the same recipient.
More subtly, even if the ephemeral key is not reused, patterns in the
memo field (identical encrypted payload sizes, timing correlations, or
metadata leakage) could allow statistical linkability analysis.
**7.2 Root Cause**
SPEC-007 §7.4.1 step 1 states "Sender generates an ephemeral key pair:
(r, R = r·G)" but does not explicitly mandate that r MUST be fresh
randomness for every payment. The specification assumes correct
implementation but does not defend against implementation errors or
deliberate shortcuts.
**7.3 Mitigation**
> **SPEC-007 AMENDMENT §7.4.1 Step 1 (REVISED):** Sender generates an
> ephemeral key pair: r = CSPRNG(32 bytes), R = r·G. The value r MUST be
> generated from a cryptographically secure random number generator for
> EVERY payment. The value r MUST NOT be derived deterministically from
> the payment amount, recipient, or any other predictable input. After
> the Fulfill is received (or the payment expires), the value r MUST be
> securely erased from memory using zeroize. An implementation MUST NOT
> cache, reuse, or persist ephemeral keys across payments.
>
> **SPEC-007 AMENDMENT §7.4.2 (NEW):** EPHEMERAL KEY UNIQUENESS
> ENFORCEMENT: The sender's wallet MUST maintain a Bloom filter (or
> equivalent probabilistic set) of recently used ephemeral public keys
> R. Before using a new R, the wallet MUST check that R is not in the
> filter. If a collision is detected (which should only occur due to a
> CSPRNG failure), the wallet MUST generate a new r and retry. The Bloom
> filter SHOULD retain entries for at least 10,000 recent payments. This
> provides defence-in-depth against CSPRNG failures.
>
> **SPEC-007 AMENDMENT §7.4.3 (NEW):** MEMO PADDING: The encrypted memo
> field MUST be padded to a fixed length before encryption, regardless
> of the actual payload size. The padded length MUST be one of: 64, 128,
> 256, 512, 1024, or 2048 bytes (the smallest size that fits the
> payload). This prevents an observer from correlating transactions by
> memo ciphertext length. Padding MUST use random bytes (not
> zero-padding) to prevent distinguishing padding from payload without
> the decryption key.
**7.4 Test Assertions**
29. Assert that two payments to the same recipient produce different
stealth addresses (P_stealth_1 ≠ P_stealth_2).
30. Assert that two payments to the same recipient produce different
ephemeral public keys (R_1 ≠ R_2).
31. Assert that an observer with access to all SettlementCertificates
but without any view_key cannot determine if two stealth addresses
belong to the same recipient, with advantage ≤ negligible (formal:
advantage \< 2⁻¹²⁸).
32. Assert that the Bloom filter detects a deliberately reused ephemeral
key and forces regeneration.
33. Assert that the encrypted memo is padded to a fixed size: a 10-byte
payload and a 100-byte payload to the same recipient produce memos
of the same ciphertext length.
34. Assert that the ephemeral secret r is zeroed from memory after use
(test via memory inspection in debug mode).
**Summary of Specification Amendments**
------------ -------------- ---------------------- -------------------------------
**Attack** **Severity** **Amended Spec** **Key Change**
AV-001 S2 SPEC-008 §8.4.5, Maximum expiry decay per hop,
§8.5.1, §8.5.2 locking fee, sender reputation
scoring.
AV-002 S1 SPEC-002 §2.4.3 Rules Explicit enumeration of all
6--7 commitments requiring range
proofs, including fee.
Zero-value fee requires proof.
AV-003 S1 SPEC-002 §2.4.3 Rules Certificate store lookup
8--8a mandatory; new relevance check
prevents referencing unrelated
certificates.
AV-004 S1\* SPEC-003 §3.6, §3.6.1 Sharding invariant made
explicit as safety-critical.
Cross-shard credit via
certificate verification.
AV-005 S3 SPEC-002 §2.7.1, Rounding rule: floor +
SPEC-008 §8.2.1 remainder to sender.
min_destination_amount field in
Prepare packets.
AV-006 S1 SPEC-003 §3.3.2, One-epoch grace period for
SPEC-010 §10.2.1 certificates. Strict rejection
of certificates more than one
epoch old.
AV-007 S2 SPEC-007 §7.4.1--7.4.3 Mandatory fresh CSPRNG
ephemeral key, Bloom filter
uniqueness check, random-padded
memo to fixed lengths.
------------ -------------- ---------------------- -------------------------------
*\* AV-004 is mitigated by design if the specification is followed. The
severity rating reflects the consequence of non-conformant
implementation, not a flaw in the protocol.*
**Formal Verification Additions**
The following properties MUST be added to the formal verification
requirements in SPEC-010 §10.6:
------------------------- ----------------- ----------------------------
**Property** **Tool** **Scope**
Fee commitment range TLA+ Verify that no state is
(AV-002) reachable where a transition
with negative fee commitment
passes all validation rules.
Causal dependency TLA+ Verify that no state is
integrity (AV-003) reachable where an account
spends value credited by an
uncertified or irrelevant
transition.
Sharding safety (AV-004) TLA+ Verify that no state is
reachable where two
conflicting transitions for
the same account are
certified by different
shards.
Epoch-boundary TLA+ Verify that a certificate
certificate validity from epoch N-2 cannot be
(AV-006) applied in epoch N under any
message ordering.
Stealth address ProVerif Verify the PRIVACY-2
unlinkability (AV-007) property: an observer
without the view_key has
negligible advantage in
linking two stealth
addresses to the same
recipient.
------------------------- ----------------- ----------------------------
**Document Control**
------------- ------------ --------------- -------------------------------
**Version** **Date** **Author** **Description**
0.1.1 2026-03-11 MESH Foundation Security addendum. Mitigations
for 7 identified attack vectors
(AV-001 through AV-007).
------------- ------------ --------------- -------------------------------

View File

@@ -0,0 +1,826 @@
**MESH PROTOCOL**
*Specification Suite --- Addendum*
SPEC-004: Tier 2 DAG-BFT \| SPEC-005: Tier 3 aBFT \| SPEC-009: Network
Transport
Version 0.1.0-draft \| March 2026
**Table of Contents**
**SPEC-004: Tier 2 --- DAG-BFT Ordered Settlement**
**4.1 Purpose**
This specification defines the consensus protocol for settlements that
require global transaction ordering. Tier 2 handles the minority of
transactions where multiple parties contend over shared state:
multi-party atomic swaps, conditional settlements, cross-currency batch
auctions, and escrow operations with multiple possible outcomes.
Tier 2 is a clean-room DAG-based Byzantine Fault Tolerant consensus
protocol synthesised from the published academic literature on
uncertified DAG BFT. It achieves 3 message delay commit latency in the
common case and provides censorship resistance through its leaderless,
all-to-all communication pattern.
**4.2 Design Rationale**
**Why DAG-based?** Traditional leader-based BFT protocols (PBFT,
HotStuff) bottleneck on a single leader for each round. DAG-based
protocols allow every validator to propose blocks simultaneously,
achieving higher throughput. The DAG structure also provides natural
censorship resistance: a Byzantine leader in a traditional protocol can
selectively exclude transactions, but in a DAG every validator
independently includes transactions.
**Why uncertified?** Certified DAG protocols (DAGRider, Narwhal-Tusk,
Bullshark) require 3 additional message delays per round to certify each
block (broadcast → acknowledge → aggregate certificate). Uncertified
DAGs skip certification entirely: a block is simply broadcast and
referenced by later blocks. Equivocation is handled by the commit rule
rather than by pre-certification. This reduces round latency from 3
message delays to 1.
**Why not use existing implementations?** Mysticeti (Mysten Labs),
Shoal++ (Aptos/Diem heritage), and Mahi-Mahi are all implemented by
well-funded companies that may hold process patents on specific
optimisations. MESH's Tier 2 protocol is designed from the published
academic principles, avoiding any specific patented technique while
achieving comparable performance characteristics.
**4.3 DAG Structure**
**4.3.1 DAG Block**
struct DAGBlock {
author: PublicKey, // Proposing validator
round: u64, // Logical round number
references: Vec\<Hash\>, // Hashes of blocks from round-1 (\>= 2f+1
required)
weak_refs: Vec\<Hash\>, // Hashes of blocks from earlier rounds
(optional)
settlements: Vec\<Transition\>, // Ordered list of Tier 2 settlements
timestamp: u64, // Validator's local time (advisory only, not trusted)
signature: Signature, // Author's signature over all above fields
}
**4.3.2 Round Progression**
The DAG progresses in logical rounds. In each round, every correct
validator proposes exactly one block. A validator MAY advance to round
r+1 only after receiving blocks from at least 2f+1 distinct validators
in round r. This ensures that each round's blocks collectively reference
a quorum of the previous round, preventing Byzantine validators from
advancing the DAG unilaterally.
Formally, let B(v, r) denote the block proposed by validator v in round
r. The reference set of B(v, r) MUST satisfy:
- \|references\| ≥ 2f+1, where each reference is a hash of a block
from round r-1.
- No two references point to blocks by the same author (prevents
referencing equivocating blocks).
- The validator MUST have received the full content of each referenced
block (not just the hash).
**4.3.3 Equivocation in the DAG**
A Byzantine validator may attempt to propose two different blocks for
the same round (equivocation). In an uncertified DAG, this is not
prevented at the block level. Instead, the commit rule handles
equivocation:
1. If a correct validator v receives two blocks B₁ and B₂ from the same
author a for the same round r, v records both as an equivocation and
MUST NOT reference either in its own blocks.
2. The equivocating validator's blocks are excluded from the commit
rule for round r. This means the equivocator cannot benefit from
equivocation.
3. Equivocation evidence (both blocks) is included in v's next block as
a protocol message, alerting all validators.
**4.4 Commit Rule**
The commit rule determines when a block in the DAG is considered final.
This is the most critical component of the consensus protocol. MESH uses
a multi-leader commit rule that can commit multiple blocks per round,
maximising throughput.
**4.4.1 Leader Selection**
Each round has a predetermined set of leader validators. The leader for
round r is determined by a rotating schedule:
leader(r) = validators\[r mod n\]
where validators is the ordered list of validator public keys in the
current epoch. This is deterministic and known to all validators before
the round begins. A round's leader block is the block proposed by the
leader validator for that round.
**4.4.2 Direct Commit (Fast Path)**
A leader block B(leader, r) is directly committed if the following
conditions hold:
4. **Support condition:** At least 2f+1 blocks in round r+1 reference
B(leader, r) in their references set.
5. **No equivocation:** The leader validator did not equivocate in
round r (no second block exists).
6. **Availability:** The full content of B(leader, r) is available to
the committing validator.
When a validator observes these conditions (which it can determine
locally after receiving 2f+1 blocks from round r+1), it commits
B(leader, r) and all transactions within it. This achieves commit
latency of 3 message delays from when the leader's block is first
broadcast: 1 delay for the block to propagate, 1 delay for round r+1
blocks to be proposed, and 1 delay for the round r+1 blocks to propagate
back.
**4.4.3 Indirect Commit (Anchor Chain)**
Blocks that are not leader blocks are committed indirectly through the
causal history of committed leader blocks. When a leader block B(leader,
r) is committed, all blocks in its causal history that have not yet been
committed are also committed, in causal order.
Formally, when B(leader, r) is committed, the following set is
committed:
commit_set(B) = { B\' : B\' is in the causal history of B and B\' is not
yet committed }
The causal history of B is the transitive closure of B's references.
This means a single leader commit can commit hundreds of blocks from
earlier rounds, amortising the commit latency across many transactions.
**4.4.4 Skip Rule (Liveness)**
If a leader's block for round r does not achieve the direct commit
condition (e.g., the leader is Byzantine and equivocated, or the
leader's block was not received by enough validators), the protocol
proceeds to round r+1 with a new leader. The skipped leader's block (if
it exists and is non-equivocating) will be committed indirectly when a
subsequent leader commits.
To ensure liveness under Byzantine leaders, each correct validator sets
a timeout T_round for receiving the current leader's block. If the
timeout expires, the validator advances to the next round without
referencing the missing leader block. The timeout is calibrated to:
T_round = max(4 × median_RTT, 500ms)
where median_RTT is the median observed round-trip time to other
validators. This ensures that under normal network conditions, the
timeout does not fire (avoiding unnecessary leader skips), but under
sustained leader failure, progress continues within at most T_round.
**4.5 Transaction Ordering**
Once a set of blocks is committed, the transactions within them must be
deterministically ordered. All validators MUST produce the identical
ordering for the same set of committed blocks.
**4.5.1 Ordering Algorithm**
7. Start with the set of blocks to be ordered (the commit_set from
§4.4.3).
8. Topologically sort the blocks by their causal relationships (if B₁
is referenced by B₂, B₁ comes first).
9. Break ties (blocks at the same causal level with no ordering between
them) by sorting on block hash (SHA3-256 of the serialised block),
lexicographically ascending.
10. Concatenate the transaction lists from each block in the determined
order.
11. Remove duplicate transactions (same transition hash appearing in
multiple blocks). The first occurrence is kept; later occurrences
are discarded.
This produces a deterministic, total order over all transactions in the
committed set. The tie-breaking by hash ensures fairness: no validator
can predict which block will come first (because the hash depends on the
full block content), preventing ordering manipulation.
**4.6 Integration with Tier 1**
Tier 1 and Tier 2 operate concurrently on the same validator set. The
interaction rules are:
- **Tier 1 certificates are included in DAG blocks:** Validators
include recently received Tier 1 settlement certificates in their
DAG blocks. This does not re-order the Tier 1 settlements (they are
already final), but it checkpoints them into the ordered history for
auditability and epoch management.
- **Tier 2 settlements may depend on Tier 1 certificates:** A Tier 2
transition's causal_deps field may reference Tier 1 settlement
certificate hashes. This enables composable operations: e.g., a
multi-party escrow that depends on a prior simple payment.
- **Conflict between tiers:** If a Tier 2 settlement conflicts with a
Tier 1 settlement on the same account (both spending the same
balance), the Tier 1 settlement takes priority because it was
certified first. The conflicting Tier 2 settlement is rejected with
ERR_BALANCE_SPENT.
**4.7 Timing Analysis**
--------------------------- ---------------------- ---------------------
**Step** **Duration** **Cumulative**
Leader broadcasts block RTT/2 (≈50ms) 50ms
(round r)
Validators receive block, ≈10ms processing 60ms
include in round r+1 refs
Validators broadcast round RTT/2 (≈50ms) 110ms
r+1 blocks
Committing validator RTT/2 (≈50ms) 160ms
receives 2f+1 round r+1
blocks
Commit rule evaluation \<1ms 161ms
Transaction execution ≈10ms per batch 171ms
--------------------------- ---------------------- ---------------------
Total commit latency: approximately 170ms in the common case (non-faulty
leader, synchronous network). This is 3 message delays, matching the
theoretical lower bound for BFT consensus. Under Byzantine leader or
network delay, fallback adds one round per skipped leader, plus T_round
timeout.
Throughput: each round commits all transactions from all validators'
blocks, not just the leader's. With n=21 validators each proposing
blocks containing up to 10,000 transactions, each round commits up to
210,000 transactions. At one round per \~170ms, steady-state throughput
exceeds 1,000,000 TPS (theoretical). Practical throughput is bounded by
bandwidth and execution, expected to be 100,000-300,000 TPS.
**4.8 Formal Safety and Liveness Properties**
> **SAFETY (Tier 2):** If two correct validators commit transaction
> sequences S₁ and S₂, then one is a prefix of the other. That is,
> correct validators never disagree on the ordering of committed
> transactions. This holds as long as f \< n/3 validators are Byzantine,
> under the partial synchrony model.
>
> **LIVENESS (Tier 2):** After GST, every valid Tier 2 settlement
> submitted to a correct validator is committed within O(f × T_round)
> time. In the common case (non-faulty leader), commit occurs within 3Δ.
> Under sustained Byzantine leaders, at most f consecutive leaders can
> be skipped before a correct leader is reached.
>
> **CENSORSHIP RESISTANCE:** A Byzantine leader cannot prevent a
> transaction from being committed. Even if a leader excludes a
> transaction from its block, any of the other n-1 validators can
> include it in their block, and it will be committed when the next
> leader's block is committed (via indirect commit).
**SPEC-005: Tier 3 --- Asynchronous BFT Fallback**
**5.1 Purpose**
This specification defines the fallback consensus protocol that
activates when the network conditions degrade below what Tier 2's
partial synchrony assumption requires. Tier 3 provides liveness
guarantees under full asynchrony: it makes no assumptions about message
delivery times, and guarantees that transactions will eventually be
committed even if an adversary controls the network scheduler.
Tier 3 is the safety net. It is slower than Tier 2 (expected 1-3 seconds
latency) but guarantees the system never halts. In a well-functioning
network, Tier 3 is never activated. It exists to handle scenarios such
as: submarine cable cuts, major cloud provider outages, targeted network
partitions, or sustained DDoS attacks against specific validators.
**5.2 Activation Conditions**
Tier 3 activates automatically when Tier 2 fails to make progress.
Specifically:
12. **Timeout detection:** If a validator has not observed a new Tier 2
leader commit for 3 × T_round consecutive rounds (approximately 1.5
seconds at default settings), it enters Tier 3 mode.
13. **Quorum detection:** If a validator receives Tier 3 activation
messages from f+1 distinct validators, it enters Tier 3 mode
regardless of its own timeout state. This prevents split-brain where
some validators are in Tier 2 and others in Tier 3.
14. **Manual activation:** A governance action (SPEC-010) can force Tier
3 activation for the entire network. This is for emergency
scenarios.
Tier 3 deactivates when the Tier 3 protocol commits a special RESUME
block, after which validators return to Tier 2. The RESUME block is
proposed when a validator has observed 2f+1 successful message exchanges
within Δ time (evidence that partial synchrony has restored).
**5.3 Protocol Design**
MESH's Tier 3 is an asynchronous DAG-based BFT protocol. It reuses the
same DAG structure as Tier 2 (same block format, same reference rules)
but replaces the deterministic commit rule with a randomised commit rule
that works under full asynchrony.
**5.3.1 Random Coin**
The fundamental difference between partially synchronous and
asynchronous BFT is the need for a random coin. Fischer, Lynch, and
Paterson (1985) proved that deterministic asynchronous consensus is
impossible with even one faulty process. The random coin breaks this
impossibility.
MESH uses a threshold random coin constructed from threshold BLS
signatures. In each round:
15. Each validator computes a partial coin share: shareᵢ = BLS_Sign(skᵢ,
round_number).
16. Once 2f+1 shares are collected, the coin is reconstructed: coin(r) =
least_significant_bit(BLS_Aggregate(shares)).
17. The coin is unpredictable: no coalition of fewer than f+1 validators
can predict the coin's value before it is revealed.
The BLS threshold signature scheme is used solely for the random coin in
Tier 3. It is not used in Tier 1 or Tier 2. This isolates the (slightly
more complex) BLS dependency to the fallback path.
> **IMPLEMENTATION NOTE:** The BLS signature scheme uses the BLS12-381
> curve. Implementations MUST use a pairing-friendly curve library that
> has been audited for correctness. The threshold DKG for BLS keys is
> performed during epoch setup alongside the Ed25519 DKG (SPEC-010).
**5.3.2 Commit Rule (Asynchronous)**
The asynchronous commit rule operates in waves. Each wave spans two
rounds of the DAG (an even round and the following odd round).
18. **Vote round (even):** In round 2k, each validator proposes a block
as normal. The leader for wave k is determined by: leader(k) =
validators\[coin(2k) mod n\], using the random coin revealed at the
start of round 2k. Note: the leader is determined after the round's
blocks are proposed, preventing a Byzantine adversary from
selectively withholding the leader's block.
19. **Decide round (odd):** In round 2k+1, validators check whether the
wave-k leader's block from round 2k was referenced by 2f+1 blocks in
round 2k. If yes, the leader block is committed (along with its
causal history). If no, the wave is skipped and the protocol
proceeds to wave k+1.
The key insight: because the leader is chosen randomly after blocks are
proposed, a Byzantine adversary cannot know which block to suppress
until it is too late. With probability at least 2/3 (when the coin
selects a correct validator), the leader's block will have been received
and referenced by a quorum, and the wave commits.
**5.3.3 Expected Latency**
Under full asynchrony, the expected number of waves until a commit is:
E\[waves\] = 1 / P(correct_leader_and_referenced) = 1 / (2/3 × 2/3) ≈
2.25 waves
Each wave is 2 rounds. Each round is bounded by the network's actual
message delivery time (not a pre-set timeout). In practice, even under
adversarial scheduling, expected commit latency is 4-6 round-trips,
approximately 1-3 seconds on continental networks.
This is significantly slower than Tier 2's 170ms but is acceptable
because Tier 3 only activates during exceptional network conditions. The
important property is that it always terminates --- there is no scenario
under the threat model where Tier 3 fails to make progress.
**5.4 Data Structures**
**5.4.1 Tier 3 Activation Message**
struct Tier3Activation {
validator: PublicKey,
last_tier2_commit: Hash, // Hash of last committed Tier 2 leader block
round: u64, // Current DAG round at activation
reason: ActivationReason, // enum: Timeout, QuorumDetected, Manual
signature: Signature,
}
**5.4.2 Coin Share**
struct CoinShare {
validator: PublicKey,
round: u64,
share: BLSSignature, // 96 bytes (BLS12-381 G2 point)
}
**5.5 Integration with Tier 2**
Tier 3 operates on the same DAG as Tier 2. When Tier 3 activates:
20. The DAG continues to grow --- blocks are still proposed and
referenced.
21. The Tier 2 commit rule is suspended (no deterministic leader
commits).
22. The Tier 3 commit rule is applied instead (random leader via coin).
23. Transactions committed by Tier 3 are added to the same ordered
history as Tier 2 commits.
24. When Tier 3 deactivates (RESUME block), the Tier 2 commit rule
resumes from the current round.
The transition is seamless: clients do not need to know which tier
committed their transaction. The settlement certificate format is
identical. The only visible difference is higher latency during the Tier
3 period.
**5.6 Formal Properties**
> **SAFETY (Tier 3):** If two correct validators commit transaction
> sequences S₁ and S₂ during Tier 3 operation, one is a prefix of the
> other. This holds under full asynchrony with f \< n/3 Byzantine
> validators. No timing assumptions are required for safety.
>
> **LIVENESS (Tier 3):** Every valid settlement submitted to a correct
> validator during Tier 3 operation is committed with probability 1
> (almost surely). The expected number of waves is at most
> 3/(2·(1-f/n)²). With n=21 and f=7, this is ≈2.25 waves.
>
> **AGREEMENT (Coin):** The threshold random coin satisfies agreement
> (all correct validators observe the same coin value), unpredictability
> (no coalition of ≤ f validators can predict the coin before 2f+1
> shares are revealed), and termination (the coin is always produced if
> 2f+1 validators are correct).
**SPEC-009: Network Transport and Peer Discovery**
**9.1 Purpose**
This specification defines the wire protocol, message framing,
connection management, peer discovery, and network-level security
mechanisms for MESH. All communication between validators, and between
clients and validators, uses the transport layer defined here.
**9.2 Transport Protocol**
MESH uses QUIC (RFC 9000) as its transport protocol. QUIC is chosen over
TCP for the following reasons:
- **Multiplexed streams:** QUIC supports multiple independent streams
within a single connection, eliminating head-of-line blocking. This
allows Tier 1 vote requests, Tier 2 DAG blocks, and Tier 3 coin
shares to flow independently without interfering with each other.
- **Built-in encryption:** QUIC mandates TLS 1.3 for all connections.
There is no unencrypted mode. This simplifies the security model.
- **Connection migration:** QUIC connections survive IP address
changes, which is important for mobile clients and cloud-based
validators that may be rescheduled.
- **0-RTT resumption:** Returning clients can send data in the first
packet, reducing latency for repeat connections.
Implementations MUST use QUIC with TLS 1.3 and MUST NOT fall back to
TCP+TLS. The TLS handshake MUST use the cipher suites specified in
SPEC-006 for the key exchange component.
**9.3 Connection Establishment**
**9.3.1 Handshake**
When a node connects to another node, the following handshake occurs
within the TLS 1.3 handshake:
25. The initiator sends a ClientHello with ALPN (Application-Layer
Protocol Negotiation) identifier \"mesh/0\".
26. The responder validates the ALPN and responds with ServerHello.
27. TLS 1.3 key exchange completes (using X25519 or hybrid
X25519+ML-KEM-768 per SPEC-006).
28. The initiator sends a MESH HandshakeMessage containing: protocol
version, supported cipher suites (ordered by preference), node type
(validator/client/connector), node public key, and a signature
proving ownership of the public key.
29. The responder validates the HandshakeMessage and responds with its
own HandshakeMessage.
30. Both sides verify: (a) the remote node's public key is valid, (b) if
the remote claims to be a validator, its public key is in the
current epoch's validator set, (c) the signature is valid.
If any check fails, the connection MUST be closed with an error code. No
protocol messages are exchanged before the handshake completes.
**9.3.2 HandshakeMessage Structure**
struct HandshakeMessage {
version: u8, // Protocol major version
cipher_suites: Vec\<u8\>, // Supported cipher suite IDs, preference
order
node_type: NodeType, // enum: Validator, Client, Connector
public_key: PublicKey, // Node's identity key
epoch: u64, // Node's current epoch number
features: u64, // Bitmask of supported optional features
timestamp: u64, // Current time (used to reject stale handshakes)
signature: Signature, // Sign(identity_key, all_above_fields)
}
**9.4 Message Framing**
All MESH protocol messages are encoded as length-prefixed frames on a
QUIC stream:
\[length: u32\] \[message_type: u8\] \[payload: bytes\]
The length field includes the message_type byte and the payload. Maximum
frame size is 4 MB (4,194,304 bytes). Messages larger than this MUST be
fragmented at the application layer.
**9.4.1 Message Types**
--------- ------------------------ --------------------------------------------
**Type **Name** **Description**
ID**
0x01 VOTE_REQUEST Tier 1: Client → Validator. Contains a
Transition.
0x02 VOTE_RESPONSE Tier 1: Validator → Client. Contains a
ValidatorVote or rejection.
0x03 SETTLEMENT_CERTIFICATE Tier 1: Client → Validator. Contains a
SettlementCertificate.
0x04 EQUIVOCATION_PROOF Any → Any. Contains an EquivocationProof.
0x10 DAG_BLOCK Tier 2: Validator → Validator. Contains a
DAGBlock.
0x11 BLOCK_REQUEST Tier 2: Validator → Validator. Request a
missing block by hash.
0x12 BLOCK_RESPONSE Tier 2: Validator → Validator. Response with
requested block.
0x20 TIER3_ACTIVATION Tier 3: Validator → Validator.
Tier3Activation message.
0x21 COIN_SHARE Tier 3: Validator → Validator. CoinShare for
random coin.
0x30 PACKET_PREPARE Routing: Connector → Connector. MeshPacket
(Prepare type).
0x31 PACKET_FULFILL Routing: Connector → Connector. MeshPacket
(Fulfill type).
0x32 PACKET_REJECT Routing: Connector → Connector. MeshPacket
(Reject type).
0x40 HANDSHAKE Connection setup. HandshakeMessage.
0x41 PING Keepalive. Empty payload.
0x42 PONG Keepalive response. Empty payload.
0xF0 QUERY_ACCOUNT Client → Validator. Request current account
state.
0xF1 QUERY_RESPONSE Validator → Client. Response with account
state.
0xFF ERROR Any → Any. Protocol error with error code
and description.
--------- ------------------------ --------------------------------------------
**9.5 Stream Multiplexing**
MESH uses dedicated QUIC streams for different message categories to
prevent interference:
--------------- ----------------------- -------------------------------
**Stream ID **Purpose** **Priority**
Range**
0-3 Handshake and keepalive Highest. Must not be blocked by
(PING/PONG) data streams.
4-7 Tier 1 messages High. Retail payment
(VOTE_REQUEST, latency-critical.
VOTE_RESPONSE,
CERTIFICATE)
8-11 Tier 2 DAG messages Medium. Consensus throughput.
(DAG_BLOCK,
BLOCK_REQUEST,
BLOCK_RESPONSE)
12-15 Tier 3 messages Medium. Only active during
(ACTIVATION, fallback.
COIN_SHARE)
16-19 Routing messages Normal. Value routing.
(PACKET_PREPARE,
FULFILL, REJECT)
20-23 Query messages Low. Client state queries.
(QUERY_ACCOUNT,
QUERY_RESPONSE)
--------------- ----------------------- -------------------------------
QUIC stream priorities SHOULD be configured to match the priority
column. Under congestion, Tier 1 messages SHOULD be prioritised over
Tier 2, which SHOULD be prioritised over routing, which SHOULD be
prioritised over queries.
**9.6 Peer Discovery**
**9.6.1 Validator Discovery**
Validators discover each other through a static bootstrap list plus a
gossip protocol:
31. **Bootstrap:** Each validator is configured with a list of at least
3 bootstrap nodes (addresses and public keys). These are hardcoded
for the genesis epoch and updated via governance for subsequent
epochs.
32. **Gossip:** Upon connecting to a bootstrap node, the validator
requests the current validator set (public keys and network
addresses). Validators gossip peer information with a pull-based
protocol: every 30 seconds, a validator requests the peer list from
a randomly chosen connected peer.
33. **Verification:** All validator addresses are signed by the
validator's identity key. A validator MUST verify the signature
before connecting to a new peer. This prevents man-in-the-middle
injection of fake validator addresses.
**9.6.2 Client Discovery**
Clients discover validators through:
- **Well-known URI:** Each MESH network publishes a validator list at
a well-known HTTPS URL (e.g.,
https://mesh-mainnet.org/.well-known/mesh-validators.json). The JSON
document contains: epoch number, validator public keys, network
addresses, and is signed by the governance multisig key.
- **DNS:** Validator addresses are published as DNS SRV records under
\_mesh.\_quic.\<network\>.mesh-protocol.org. This provides a
fallback if the HTTPS endpoint is unavailable.
- **QR code:** For initial onboarding, a validator's connection
details can be encoded in a QR code for scanning by a mobile wallet.
**9.7 Eclipse Attack Resistance**
An eclipse attack occurs when an adversary surrounds a target node with
malicious peers, controlling all of the target's connections. MESH
mitigates eclipse attacks through:
34. **Minimum connection diversity:** Each validator MUST maintain
connections to at least 2f+1 distinct validators. If the connection
count drops below this, the validator MUST attempt to reconnect
using the bootstrap list.
35. **Connection rotation:** Every epoch, each validator drops its
lowest-quality connection (highest latency or most dropped messages)
and replaces it with a randomly selected validator from the current
set that it is not yet connected to.
36. **Multi-source verification:** Before accepting any protocol state
(epoch changes, validator set updates), a validator MUST receive
consistent information from at least f+1 independent sources.
37. **Inbound connection limits:** Each validator MUST limit inbound
connections to max(3n, 256) total, with per-IP and per-subnet limits
of 8 and 32 respectively. This prevents a single adversary from
monopolising connection slots.
**9.8 Rate Limiting**
To prevent resource exhaustion attacks:
- **Per-account rate limit:** A validator MUST process at most
R_account Tier 1 vote requests per account per second (recommended:
R_account = 10). Excess requests are queued up to Q_max
(recommended: 100) and then rejected with ERR_RATE_LIMITED.
- **Per-connection rate limit:** A validator MUST process at most
R_connection messages per connection per second across all types
(recommended: R_connection = 1000). This prevents a single malicious
connection from overwhelming the validator.
- **Global rate limit:** A validator MUST limit total incoming message
bandwidth to B_max bytes per second (recommended: B_max = the
validator's network capacity × 0.8, leaving 20% headroom for
outbound traffic).
- **New account cost:** Creating a new account (first transition)
requires a proof-of-work puzzle with difficulty D_new (recommended:
D_new = 2²⁰, approximately 1 second of single-core computation).
This prevents mass account creation attacks without requiring an
economic cost in any specific currency.
**9.9 Keepalive**
Each connection MUST send a PING message at least every 30 seconds if no
other messages have been sent. The remote node MUST respond with PONG
within 5 seconds. If no PONG is received after 3 consecutive PINGs, the
connection MUST be closed and the peer marked as potentially
unreachable.
PING/PONG messages carry no payload and are 5 bytes each (4 byte
length + 1 byte type). They serve dual purposes: keeping QUIC
connections alive through NAT/firewalls and detecting peer failures.
**Document Control**
**Revision History**
------------- ------------ --------------- -------------------------------
**Version** **Date** **Author** **Description**
0.1.0 2026-03-09 MESH Foundation Initial draft. Completes
specification suite with
SPEC-004, 005, 009.
------------- ------------ --------------- -------------------------------
**Additional References**
- Babel, K. et al. (2023). Mysticeti: Reaching the Limits of Latency
with Uncertified DAGs. arXiv:2310.14821.
- Jovanovic, P. et al. (2024). Mahi-Mahi: Low-Latency Asynchronous BFT
DAG-Based Consensus. arXiv:2410.08670.
- Arun, B. et al. (2024). Shoal++: High Throughput DAG BFT Can Be Fast
and Robust. USENIX NSDI 2025.
- Keidar, I. et al. (2021). All You Need is DAG. ACM PODC 2021
(DAGRider).
- Danezis, G. et al. (2022). Narwhal and Tusk: A DAG-based Mempool and
Efficient BFT Consensus. EuroSys 2022.
- Spiegelman, A. et al. (2022). Bullshark: DAG BFT Protocols Made
Practical. ACM CCS 2022.
- Fischer, M., Lynch, N., Paterson, M. (1985). Impossibility of
Distributed Consensus with One Faulty Process. JACM.
- RFC 9000: QUIC: A UDP-Based Multiplexed and Secure Transport. IETF,
2021.
- RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3.
IETF, 2018.