# gossipsub-rln implementation details ## **Requirements**: - For active network participants (publishing nodes and propagating nodes): Ethereum full node access - Smart contract deployed on a blockchain network (Ethereum mainnet or rollup preferable) for group membership management ## **Description:** **gossipsub-rln** should be implemented as a libp2p protocol, by extending gossipsub-v1.0 and including the security extension from gossipsub-v1.1. ## **Parameters:** Besides the protocol parameters enabled by gossipsub, gossipsub-rln should allow the following configuration parameters: - `rln_membership_contract` - required, default: none, description: Ethereum address of the smart contract implementing the RLN membership group management - `secret_key` - required, default: none, description: A secret key for the node - `spam_msg_threshold`: - optional, default 1, description: the maximum number messages a peer can send per epoch. Sending more messages than this number per epoch will be treated as spamming the network and the peer risks of being slashed. This parameter should be aligned with the SSS scheme used by the RLN contract. - `slashing_coefficient` - optional, default 1, description: decimal in the range 0-1 representing what percent of the spammer's balance should be withdrawn by the slasher (0 representing none of it, while 1 representing all of it). For applications where membership fee is not needed, peers should set this to 0. ## Data description **ZK Proof:** Each peer that is publishing messages must generate a ZK proof. The ZK proof is required for the peer in order to prove that they are member of the group while preserving their privacy. Fields needed for generating the ZK proof: - `epoch` - needed for constructing the nullifier - `secret_key` - the secret key of the peer - `merkle_witness` - merkle tree witness for the user - `membership_tree_root` - the root of the membership tree - `msg_content` - the content of the actual message sent (signal) - `rln_identifier` - random number, used to distinguish this application for other rln applications Data needed for verifying the ZK proof: - `zk_proof` - `membership_tree_root` - `nullifier` - `epoch` - `rln_identifier` - `x_share` - the x share of the private key (SSS requirement) - the hashed `msg_content` - `y_share` - the y share of the private key (SSS requirement) The functions for generating and verifying ZK proofs are already implemented and provided (typescript implementation). **Message structure:** The gossipsub-v1.1 protobuf `RPC` message structure must be extended to support additional fields: the RLN membership `zk_proof`, `epoch`, `nullifier`, `rln_identifier` `x_share` and `y_share`. These fields are required, and if not added then the messages must be discarded and not propagated further. Receivers on each message will check the validity of the ZK proof and will determine the sender's membership status and the validity of the message. If the `spam_msg_threshold` is reached the receiving peer can reveal the secret key for the spammer and use it for removing the spammer and retrieving his stake (if there is a stake present). **Membership tree (`membership_tree`):** The membership tree should be implemented as Merkle tree data structure and is needed for storing the identity commitments of the peers. This structure must be stored locally by active network participants (that is message publishing nodes and message propagating nodes). The merkle tree must be updated frequently based on new member additions and member slashings. New members are added via posting correct registration details to the registration's smart contract (`rln_membership_contract`), and members are removed when they breach the `spam_msg_threshold` **Message identity metadata structure (`msg_identity_metadata`):** A structure for keeping track of the identity of the received messages is needed to detect spam. This structure must be stored locally by active network participants (that is message publishing nodes and message propagating nodes). For each message received the `nullifier`, `x_share` and `y_share` must be stored. These field can be stored per epoch, and data for previous epoch can be periodically discarded. The structure might be represented as : ```typescript= interface Share { x_share: number; y_share: number; }; interface NullifierData { shares: Array<Share>; }; interface EpochData { [nullifier: string]: NullifierData; } interface MsgIdentityMetadata { [epoch: string]: EpochData; } ``` ## **Implementation logic and requirements:** Each peer must keep the membership tree (Merkle tree data structure) locally - **`membership_tree`**. Additionally each peer must keep additional data structure for tracking the number of received messages from each peer for each epoch - **`msg_identity_metadata`**. This structure is required for spam prevention. It will be used for checking if the number of messages sent from a peer in an epoch are above `spam_msg_threshold`. **Message receiving** On each message from the p2p network, the peers should verify the ZK proof to determine the validity of the message. If the ZK proof is valid, the message should be propagated further, otherwise the message should be rejected and the peer should be slashed if the slashing conditions are met (removal from the `membership_tree`). The rules for determining the validity of the message and the appropriate actions must be followed in the following order: 1. If the message doesn't contain a ZK proof, the message must be rejected. 2. If the message is not from the current epoch, the message must be rejected. 3. If the ZK proof is valid the `msg_identity_metadata` structure must be traversed, and: 1. if the same `nullifier` is found for the same `epoch`, and the length of the `shares` array == `spam_msg_threshold` and the `share_x` and `share_y` are different from every share in the `shares` array then the private key of the publisher is revealed, the publisher is slashed and the message is stopped for further propagation. 2. if the same `nullifier` is found for the same `epoch`, and the length of the `shares` array == `spam_msg_threshold` and the `share_x` and `share_y` are the same as some of the shares in the `shares` array, then the message is treated as duplicate and is discarded from further propagation and the publisher is not slashed. 3. if the same `nullifier` is found for the same `epoch`, and the length of the `shares` array < `spam_msg_threshold` then the `x_share` and `y_share` are added as a `Share` object in the `shares` array and the message is propagated further. 4. if the nullifier is not found in the `epoch` then a `NullifierData` object is created with the `x_share` and `y_share` added as a `Share` object in it's `shares` array and the message is propagated further. 4. If the ZK proof is invalid the message must be rejected. **Message sending/publishing** Before publishing any message, the node must generate a `zk_proof` from the fields: `epoch`, `secret_key`, `merkle_witness`,`membership_tree_root`, `rln_identifier` and `msg_content` . The `zk_proof`, `epoch`, `nullifier`, `x_share,` `y_share` and `rln_identifier`. fields must be included in the protobuf `RPC` message. **Membership tree updates** This is a flexible part of the implementation and the upstream applications should implement this part according to their needs. The decision for this is to allow a flexible architecture and to not prematurely limit the options for the RLN membership contract design. The applications should implement the tree updating logic according to the design of the RLN smart contract. In general the membership smart contract should serve as a registry and contain data about registration events. Based on the smart contract the peers could either listen for smart contract events or use other methods for externally monitoring the contract's storage state. **Message identity metadata updates** The **`msg_identity_metadata`** structure should be updated according the rules defined in the Message receiving section above. Additionally the data from older epochs should be removed when it gets stale (new epochs are started) in order to preserve space. ### References and reading material: - [JS gossipsub-v.1.1 implementation](https://github.com/ChainSafe/js-libp2p-gossipsub) ([https://github.com/ChainSafe/js-libp2p-gossipsub](https://github.com/ChainSafe/js-libp2p-gossipsub)), v1.1 enabled, stable, used by [Lodestar](https://chainsafe.github.io/lodestar/) ([https://medium.com/chainsafe-systems/a-lodestar-for-eth2-da9e1a1ea8f2](https://medium.com/chainsafe-systems/a-lodestar-for-eth2-da9e1a1ea8f2)) - still experimental - [WAKU RLN relay](https://vac.dev/rln-relay) - [Gossipsub-v1.1 spec](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.1.md) - [Gossipsub-v1.0 spec](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.0.md) - [WAKU Relay implementation](https://github.com/status-im/js-waku/blob/main/src/lib/waku_relay/index.ts) - wrapper on top of gossipsub - [RLN protocol overview](https://medium.com/privacy-scaling-explorations/rate-limiting-nullifier-a-spam-protection-mechanism-for-anonymous-environments-bbe4006a57d) - [RLN construct](https://github.com/appliedzkp/rln)