Owner: @Mehmet @Marcin Pawlowski @Thomas Lavaur

Reviewers: 🟢@Youngjoon Lee 🟢@David Rusu 🟢@Álvaro Castro-Castilla

Introduction

This document defines an implementation-friendly specification of the Proof of Quota (PoQ), which is introduced in Proof of Quota.

Overview

The PoQ ensures that there is a limited number of message encapsulations that a node can perform. This constrains the number of messages a node can introduce to the Blend network. The mechanism regulating these messages is similar to rate-limiting nullifiers.

Construction

The Proof of Quota (PoQ) verifies that a node's public key is within a limit for either a core node or a leader node. It consists of two parts:

  1. Proof of Core Quota (PoQ_C): Ensures that the core node is declared and hasn’t already produced more keys than the core quota Q_C.
  2. Proof of Leadership Quota (PoQ_L): Ensures that the leader node would win the proof of stake for current Cryptarchia epoch and hasn’t already produced more keys than the leadership quota Q_L. That doesn’t guarantee that the node is indeed winning because the PoQ doesn’t check if the note is unspent enabling generation of the proof ahead of time preventing extreme delays.

The final proof PoQ is valid if either PoQ_C or PoQ_L holds.

Zero-Knowledge Proof Statement

Public Inputs

A proof attesting that for the following public values derived from blockchain parameters:

class ProofOfQuotaPublic:
		session: int          # Session number (uint64)
		core_quota: int       # Allowed messages per session for core nodes
		leader_quota: int     # Allowed messages per session for potential leaders
		core_root: zkhash     # Merkle root of zk_id of the core nodes
		K_part_one: int       # First part of the signature public key (16 bytes)
		K_part_two: int       # Second part of the signature public key (16 bytes)
		pol_epoch_nonce: int  # PoL Epoch nonce
		pol_t0: int           # PoL constant t0
		pol_t1: int           # PoL constant t1
		pol_ledger_aged: zkhash # Merkle root of the PoL eligible notes
		# Outputs:
		key_nullifier: zkhash   # derived from session, private index and private sk

Private Inputs

The prover knows a witness:

class ProofOfQuotaWitness:
		index: int                            # This is the index of the generated key. Limiting this index limits the maximum number of key generated. (20 bits)
		selector: bool                        # Indicates if it's a leader (=1) or a core node (=0)
		# This part is filled randomly by potential leaders
		core_sk: zkhash                       # sk corresponding to the zk_id of the core node
		core_path: list[zkhash]               # Merkle path proving zk_id membership (len = 20)
		core_path_selectors: list[bool]       # Indicates how to read the core_path (if Merkle nodes are left or right in the path)
		# This part is filled randomly by core nodes
		pol_sl: int                           # PoL slot
		pol_sk_starting_slot: int             # PoL starting slot of the slot secrets
		pol_note_value: int                   # PoL note value
		pol_note_tx_hash: zkhash              # PoL note transaction 
		pol_note_output_number: int           # PoL note transaction output number
		pol_noteid_path: list[zkhash]         # PoL Merkle path proving noteID membership in ledger aged (len = 32)
		pol_noteid_path_selectors: list[bool] # Indicates how to read the note_path (if Merkle nodes are left or right in the path)
		pol_slot_secret: int                  # PoL slot secret corresponding to sl
		pol_slot_secret_path: list[zkhash]    # PoL slot secret Merkle path to sk_secrets_root (len = 25)

Constraints

Such that the following constraints hold: