Skip to main content

Disputes

The Disputes transition processes verdicts (collections of judgments from validators), registers offenses, and removes invalid work-reports from the processing pipeline.

Purpose

  • Verdict Registration: Record consensus among ≥⅔ validators about a work-report's validity
  • Offense Recording: Permanently record misbehaving validators (guarantors who signed invalid reports, or validators whose judgments contradict verdicts)
  • Invalid Report Removal: Ensure work-reports judged invalid are not accumulated or resubmitted
  • Punishment Coordination: Provide on-chain record for higher-level slashing/banning logic

Key Concepts

Judgments

Judgments are signed statements from validators about a work-report's validity, created during the off-chain auditing process (see Auditing). Validators audit work-reports and produce judgments (valid/invalid).

Verdicts

A verdict is a collection of exactly ⅔ + 1 judgments from either the current or previous validator set, representing consensus on a work-report's validity.

Offenses

Two types of offenses can be proven on-chain:

  • Culprits: Validators who guaranteed a work-report later found invalid
  • Faults: Validators whose judgments contradict an established verdict

State Structure

ψ ≡ ⟨ψ_g, ψ_b, ψ_w, ψ_o⟩

ψ_g: Set<H> // Good set - reports judged valid
ψ_b: Set<H> // Bad set - reports judged invalid
ψ_w: Set<H> // Wonky set - reports impossible to judge
ψ_o: Set<Ed25519> // Offenders - misbehaving validator keys

Disputes Extrinsic

E_D ≡ ⟨verdicts, culprits, faults⟩

Verdict {
report_hash: H,
epoch_index: u32, // Current or previous epoch
judgments: [(bool, u16, Sig)] // ⅔+1 judgments
}

Culprit {
report_hash: H, // Must be in bad set
validator_key: Ed25519,
signature: Ed25519Sig // Guarantee signature
}

Fault {
report_hash: H,
validity: bool, // Validator's claimed validity
validator_key: Ed25519,
signature: Ed25519Sig // Judgment signature contradicting verdict
}

How It Works

1. Verdict Classification

Based on positive vote count:

positive_votes = sum(1 for (valid, _, _) in judgment if valid)
supermajority = floor(2 * VALIDATOR_COUNT / 3) + 1

if positive_votes == len(judgments): // All positive
verdict_type = "good"
psi.good_set.add(report_hash)
elif positive_votes == 0: // All negative
verdict_type = "bad"
psi.bad_set.add(report_hash)
elif positive_votes >= VALIDATOR_COUNT / 3: // Mixed, unclear
verdict_type = "wonky"
psi.wonky_set.add(report_hash)
else:
raise Error("Invalid vote split")

2. Offense Processing

Culprits (guaranteed an invalid report):

for (report_hash, validator_key, signature) in block.extrinsic.culprits:
# Verify: report is in bad set
assert report_hash in psi.bad_set

# Verify signature matches guarantee
verify_signature(validator_key, b"$jam_guarantee" + report_hash, signature)

# Add to offenders
psi.offenders.add(validator_key)

Faults (judgment contradicts verdict):

for (report_hash, claimed_validity, validator_key, sig) in block.extrinsic.faults:
# Check contradiction
if report_hash in psi.bad_set:
assert claimed_validity == True // Claimed valid but verdict says bad
elif report_hash in psi.good_set:
assert claimed_validity == False // Claimed invalid but verdict says good

# Verify judgment signature
verify_signature(validator_key, b"$jam_valid" + report_hash, sig)

# Add to offenders
psi.offenders.add(validator_key)

3. Pending Work-Report Removal

Remove work-reports from pending state (ρ) if they received negative verdicts:

for core in range(341):
if rho[core] is not None:
report_hash = hash(rho[core].report)
if report_hash in psi.bad_set or report_hash in psi.wonky_set:
rho[core] = None // Remove invalid/uncertain reports

4. Chain Reversion

A negative judgment implies the chain should revert to before the work-report's accumulation. Chain selection logic (see Best Chain) handles this automatically by prioritizing chains without disputed reports.

Validation Rules

Verdicts

  • Sorted by report_hash (ascending)
  • Judgments within each verdict sorted by validator index
  • Exactly ⅔ + 1 judgments (floor(2V/3) + 1)
  • All signatures valid for current or previous epoch validators
  • No duplicate report hashes across all past verdicts

Culprits & Faults

  • Sorted by validator key (ascending)
  • No duplicate validator keys
  • Referenced reports must have verdicts
  • Validator keys not already in offenders set
  • All signatures valid

Security Properties

Permanent Record

Once a verdict is registered, it's permanent - prevents:

  • Resubmission of invalid reports
  • Future disputes on already-judged reports
  • Uncertainty about report validity

Misbehavior Tracking

Offenders list provides simple basis for:

  • Removing validators from future sets (see SAFROLE validator rotation)
  • Higher-level slashing logic (e.g., Polkadot staking parachain)
  • Coordination across the network

Consensus Safety

⅔ + 1 threshold ensures:

  • At least ⅓ + 1 honest validators agree
  • Byzantine fault tolerance maintained
  • Cannot be manipulated by ≤⅓ malicious validators

Implementation Details

Location: tessera/jam/state/transitions/disputes/

Key Files:

  • disputes.py: Main transition logic
  • Disputes.md: Detailed specification
  • error.py: Dispute-specific errors

Process:

  1. Validate verdict sorting and signatures
  2. Classify verdicts as good/bad/wonky based on vote counts
  3. Process culprits and faults, verify signatures
  4. Update ψ sets (good, bad, wonky, offenders)
  5. Remove disputed reports from ρ (pending work-reports)

Constants:

  • Supermajority = floor(2 * 1023 / 3) + 1 = 682 + 1 = 683 judgments

Relationship to Auditing

Important: The actual validation of work-reports happens off-chain during the auditing process:

  1. Validators audit assigned work-reports (off-chain)
  2. Validators sign judgments about validity (off-chain)
  3. If negative judgments emerge, validators collect ⅔ + 1 judgments
  4. Verdict is submitted on-chain (this transition processes it)

The disputes transition does NOT re-execute or validate work - it only processes the verdicts from off-chain auditing.

Error Conditions

class DisputesErrorCode:
VERDICT_ALREADY_JUDGED // Report hash already in ψ sets
INVALID_AGE // Epoch index not current or previous
BAD_VOTE_SPLIT // Vote count doesn't match good/bad/wonky criteria
INVALID_SIGNATURE // Ed25519 signature verification failed
NOT_SORTED // Verdicts/culprits/faults not properly ordered
CONTRADICTORY_FAULT // Fault doesn't contradict the verdict

References

  • Gray Paper: Section on Disputes, Verdicts and Judgments
  • Auditing: See Auditing for off-chain judgment process
  • Implementation: tessera/jam/state/transitions/disputes/

Next: Reports | Accumulation