macula_hyparview_proto (macula v10.13.2)

View Source

HyParView protocol orchestrator (Part 3 §7.1).

Pure functional layer on top of macula_hyparview_view. Given the current view, an incoming HyParView frame, and a context (self NodeId, realm, signing identity, ARWL/PRWL constants), process/4 returns the updated view plus a list of {send, TargetPeer, Frame} actions the wrapping process should transmit.

Message handlers

  • JOIN — receiver becomes the joiner's contact: add_active(joiner), reply NEIGHBOR(high), FORWARD_JOIN(ttl=ARWL) to every other active peer. Eviction in the active view emits DISCONNECT to the demoted peer. When ctx() carries realm_admin_pubkey, admission is gated on the frame's record field holding a realm_member_endorsement signed by that key for this exact (realm, joiner) pair (macula_hyparview_endorsement: verify_endorsement/3) — a missing or invalid endorsement is dropped silently (no ack, no forward, view unchanged), never admitted. Without realm_admin_pubkey in ctx(), admission is unconditional (opt-in gating, matches the frame spec's own "endorsement is optional" design).
  • FORWARD_JOIN — if ttl=0 or active view is empty: add_active(new_member) + reply NEIGHBOR(high). Otherwise: if ttl == PRWL, also add_passive(new_member); decrement ttl and forward to a random active peer that is not the sender. Carries the ORIGINAL JOIN's record (endorsement) through the whole forward chain and re-verifies it at every admission point — trust in the endorsement is never transitively assumed just because a neighbour forwarded it.
  • NEIGHBOR(high) — always add_active(sender), evicting if needed.
  • NEIGHBOR(low) — add_active(sender) only when the active view has room.
  • DISCONNECT — demote sender to passive.
  • SHUFFLE — if ttl > 0 and a forwardable neighbour exists, decrement ttl + forward; otherwise build SHUFFLE_REPLY against our own sample and send back to the origin, then merge the incoming sample into our passive view.
  • SHUFFLE_REPLY — merge the incoming sample into the passive view.

Reference: plans/PLAN_MACULA_V2_PART3_DISCOVERY.md §7.1; plans/PLAN_PHASE_5_BREAKDOWN.md Session 5.2.

Summary

Functions

Build a signed JOIN frame to send to a contact peer.

Build a signed SHUFFLE frame for a periodic shuffle round. The orchestrator picks a random active neighbour to send it to.

Types

action/0

-type action() :: {send, peer(), macula_frame:frame()}.

ctx/0

-type ctx() ::
          #{self_id := peer(),
            realm := <<_:256>>,
            identity := macula_identity:key_pair(),
            arwl => non_neg_integer(),
            prwl => non_neg_integer(),
            shuffle_ttl => non_neg_integer(),
            shuffle_active_size => non_neg_integer(),
            shuffle_passive_size => non_neg_integer(),
            realm_admin_pubkey => macula_identity:pubkey(),
            self_endorsement => macula_record:m_record()}.

peer/0

-type peer() :: macula_identity:pubkey().

Functions

build_join(_)

-spec build_join(ctx()) -> macula_frame:frame().

Build a signed JOIN frame to send to a contact peer.

build_shuffle(Ctx)

-spec build_shuffle(ctx()) -> {ok, macula_frame:frame()}.

Build a signed SHUFFLE frame for a periodic shuffle round. The orchestrator picks a random active neighbour to send it to.

process(View, FromId, F, Ctx)