Skip to content

Latest commit

 

History

History
183 lines (145 loc) · 8.03 KB

coordinator.md

File metadata and controls

183 lines (145 loc) · 8.03 KB

Coordinator

The primary job of the coordinator is to sequence participants in the ceremony by acting as the point of contact for participants. The coordinator is required to have a DoS-resilient internet connection and sufficiently powerful hardware to verify the transcript rapidly so that they are able to send the transcript to the next participant as soon as possible. Furthermore, the coordinator should be a semi-trusted entity as they have the power to censor participants, although they cannot affect the safety of the setup.

Transcript object

Witness

@dataclass
class Witness:
    running_products: List[bls.G1Point]
    pot_pubkeys: List[bls.G2Point]

SubTranscript

@dataclass
class SubTranscript:
    num_g1_powers: int
    num_g2_powers: int
    powers_of_tau: PowersOfTau  # as defined in ./README.md
    witness: Witness

Transcript

@dataclass
class Transcript
    sub_transcripts: List[SubTranscript]

Verification

Upon receiving the transcript back from a participant, the Coordinator MUST perform the following checks.

Contribution schema structure:

  • Schema Check - Verify that the received contribution.json matches the contributionSchema.json schema.
def schema_check(contribution_json: str, schema_path: str) -> bool:
    with open(schema_path) as schema_file:
        schema = json.load(schema_file)
        try:
            jsonschema.validate(contribution_json, schema)
            return True
        except Exception:
            pass
    return False

Contribution parameter check

This check verifies that the number of $\mathbb{G}_1$ and $\mathbb{G}_2$ powers meets expectations.

def parameter_check(contribution: Contribution, transcript: Transcript) -> bool:
    for (sub_contribution, sub_transcript) in zip(contribution.sub_contributions, transcript.sub_transcript):
        if sub_contribution.num_g1_powers != sub_transcript.num_g1_powers:
            return False
        if sub_contribution.num_g2_powers != sub_transcript.num_g2_powers:
            return False
        if sub_contribution.num_g1_powers != len(sub_contribution.powers_of_tau.g1_powers):
            return False
        if sub_contribution.num_g2_powers != len(sub_contribution.powers_of_tau.g2_powers):
            return False
    return True

Point Checks

  • Prime Subgroup checks
    • G1 Powers Subgroup check - For each of the $\mathbb{G}_1$ Powers of Tau (g1_powers), verify that they are actually elements of the prime-ordered subgroup.
    • G2 Powers Subgroup check - For each of the $\mathbb{G}_2$ Powers of Tau (g2_powers), verify that they are actually elements of the prime-ordered subgroup.
    • PoTPubkey Subgroup checks - Check that the PoTPubkey is actually an element of the prime-ordered subgroup.
def subgroup_checks(contribution: Contribution) -> bool:
    for sub_contribution in contribution.sub_contributions:
        if not all(bls.G1.is_in_prime_subgroup(P) for P in sub_contribution.powers_of_tau.g1_powers):
            return False
        if not all(bls.G2.is_in_prime_subgroup(P) for P in sub_contribution.powers_of_tau.g2_powers):
            return False
        if not bls.G2.is_in_prime_subgroup(sub_contribution.pot_pubkey):
            return False
    return True
  • Non-zero check - Check that the pot_pubkeys are not equal to the point at infinity.
def non_zero_check(contribution: Contribution) -> bool:
    for sub_contribution in ceremony.sub_contributions:
        if bls.G2.is_inf(sub_contribution.pot_pubkey):
            return False
    return True

Pairing Checks

Note: The following pairing checks SHOULD be batched via a random linear combination which would reduce the number of final exponentiations to 2 and decrease the number of Miller loops needed.

  • Tau update construction - Verify that the latest contribution is correctly built on top of the previous ones. Let $[\tau^1_{k}]_1$ be the first power of tau from the most recent ($k$-th)contribution and $[\pi]1^{k-1} := [\tau^1{k-1}]1$ be the transcript after updating from the previous correct contribution. Verifying that $\tau$ was updated correctly can then be performed by checking $e([\pi{k-1}]_1, [x_k]2) \stackrel{?}{=}e([\pi{k}]_1, g_2)$
def tau_update_check(contribution: Contribution, transcript: Transcript) -> bool:
    for (sub_contribution, sub_transcript) in zip(contribution.sub_contributions, transcript.sub_transcripts):
        tau = sub_contribution.powers_of_tau.g1_powers[1]
        transcript_product = sub_transcript.witness.running_products
        pk = sub_contribution.pot_pubkey
        if bls.pairing(transcript_product, pk) != bls.pairing(tau, bls.G2.g2):
                return False
    return True
  • Correct construction of G1 Powers - Verify that the $\mathbb{G}_1$ points provided are indeed incremental powers of (the same) $\tau$. This check is done by asserting that the next $\mathbb{G}_1$ point is the result of multiplying the previous one with $\tau$: $e([\tau^{i + 1}]_1, g_2) \stackrel{?}{=}e([\tau^i]_1, [\tau]_2)$. Note that the check that the $\tau$ in $[\tau]_2$ is the same as $\pi_k$ is done via the g2_powers_check() below which verifies that $e([\tau^i]_1, g_2) \stackrel{?}{=}e(g_2, [\tau^i]_2)$ and $[\tau^i]_1 = [\pi_k]_1$ due to the Transcript updates rules.
def g1_powers_check(contribution: Contribution) -> bool:
    for sub_contribution in contribution.sub_contributions:
        powers = sub_contribution.powers_of_tau.g1_powers
        pi = sub_contribution.powers_of_tau.g2_powers[1]
        for power, next_power in zip(powers[:-1], powers[1:]):
            if bls.pairing(bls.G1.g1, next_power) != bls.pairing(power, pi):
                return False
    return True
  • Correct construction of G2 Powers - Verify that the $\mathbb{G}_2$ points provided are indeed incremental powers of $\tau$ and that $\tau$ is the same across $\mathbb{G}_1$ and $\mathbb{G}_2$. This check is done by verifying that $\tau^i$ is the same across $[\tau^i]_1$ and $[\tau^i]_2$. $e([\tau^i]_1, g_2) \stackrel{?}{=}e(g_2, [\tau^i]_2)$
def g2_powers_check(transcript: Transcript) -> bool:
    for sub_contribution in transcript.sub_contributions:
        g1_powers = sub_contribution.powers_of_tau.g1_powers
        g2_powers = sub_contribution.powers_of_tau.g2_powers
        for g1_power, g2_power in zip(g1_powers, g2_powers):
            if bls.pairing(bls.G1.g1, g1_power) != bls.pairing(g2_power, bls.G2.g2):
                return False
    return True

Updating the transcript

Once the coordinator has performed the above Verification checks, they MUST update the transcript.

def update_transcript(old_transcript: Transcript, contribution: Contribution) -> Transcript:
    new_transcript = Transcript(sub_transcripts=[])
    for (sub_transcript, sub_ceremony) in zip(old_transcript.sub_transcripts, contribution.sub_ceremonies):
        new_sub_transcript = SubTranscript(
            num_g1_powers=sub_transcript.num_g1_powers,
            num_g2_powers=sub_transcript.num_g2_powers,
            powers_of_tau=sub_ceremony.powers_of_tau,
            witness=Witness(
                running_products=sub_transcript.witness.running_products + [sub_ceremony.powers_of_tau.g1_powers[1]],
                pot_pubkeys=sub_transcript.witness.pot_pubkeys + [sub_ceremony.pot_pubkey],
            )
        )

Generating the contribution file for the next participant

Once the transcript has been updated, the coordinator MUST get a new Contribution file to send to the next participant.

def get_new_contribution_file(transcript: Transcript) -> Contribution:
    contribution = Contribution(sub_contribution=[])
    for sub_transcript in transcript:
        contribution.sub_contribution.append(
            SubContribution(
                num_g1_powers=sub_transcript.num_g1_powers,
                num_g2_powers=sub_transcript.num_g2_powers,
                powers_of_tau=sub_transcript.powers_of_tau,
                pot_pubkey='',
            )
        )