What Is the Equivalence Principle in GenLayer?
The equivalence principle in GenLayer is a consensus mechanism that enables validators to agree on nondeterministic operations—such as web requests and LLM calls—by separating execution into a leader function that performs the operation and a validator function that verifies the result meets predefined acceptance criteria.
GenLayer is a blockchain platform that allows smart contracts to execute nondeterministic operations, creating unique consensus challenges. The genlayerlabs/genlayer-project-boilerplate repository demonstrates how the equivalence principle solves this by establishing a validation framework for variable outputs. This architectural pattern ensures that despite running operations with potentially different results across nodes, the network can reach deterministic consensus.
How the Equivalence Principle Works
The core mechanism, documented in CLAUDE.md, divides nondeterministic execution into two distinct phases. When a contract calls an operation like gl.nondet.web.render or gl.nondet.exec_prompt, the system cannot guarantee identical results across all validator nodes. The equivalence principle resolves this by requiring explicit validation logic.
The Leader Function
The leader function executes the actual nondeterministic operation. This function runs once to produce a result that will be proposed to the network. For example, it might fetch web data or query an LLM, returning output that could vary slightly between executions due to network latency, API changes, or model variations.
The Validator Function
The validator function receives the leader's output and applies acceptance rules to determine if the result is valid. If the validator returns True, the network accepts the result and stores it on-chain. If it returns False, the transaction reverts. This separation ensures that while execution may vary, consensus depends on whether the output satisfies objective criteria.
Implementation Methods
GenLayer provides two primary approaches for applying the equivalence principle, depending on the complexity of your validation requirements.
Convenience Helpers
For common validation patterns, GenLayer offers built-in helpers through the gl.eq_principle module:
gl.eq_principle.strict_eq()– Requires exact equality between the leader's result and what validators reproduce, suitable for deterministic JSON outputs or structured data.gl.eq_principle.prompt_comparative()– Accepts answers that are "similar enough" for comparative queries.gl.eq_principle.prompt_non_comparative()– Designed for subjective assessments where exact matching isn't required.
These helpers abstract the leader/validator pattern into single function calls.
Custom Leader and Validator Functions
For complex validation logic, developers define separate leader and validator functions and invoke them via gl.vm.run_nondet(). This approach offers full control over what constitutes an acceptable result.
def leader() -> str:
# Perform a nondeterministic web fetch
return gl.nondet.web.get("https://example.com/api").body
def validator(leader_output: str) -> bool:
# Simple rule: output must be valid JSON and contain a "status" field
try:
data = json.loads(leader_output)
return "status" in data
except Exception:
return False
# Run the nondeterministic operation with explicit validation
result = gl.vm.run_nondet(leader, validator)
Real-World Example from football_bets.py
The contracts/football_bets.py file demonstrates the equivalence principle using the strict_eq helper. In the _check_match method at line 54, the contract fetches match results and uses an LLM to extract scores:
def _check_match(self, resolution_url: str, team1: str, team2: str) -> dict:
def get_match_result() -> str:
web_data = gl.nondet.web.render(resolution_url, mode="text")
task = f"""
Extract the match result for:
Team 1: {team1}
Team 2: {team2}
Web content:
{web_data}
Respond in JSON:
{{
"score": str,
"winner": int
}}
"""
result = gl.nondet.exec_prompt(task, response_format="json")
return json.dumps(result, sort_keys=True)
# The strict_eq helper ensures every validator receives the exact same JSON string.
result_json = json.loads(gl.eq_principle.strict_eq(get_match_result))
return result_json
Here, get_match_result serves as the leader function, while gl.eq_principle.strict_eq handles the validator logic internally, ensuring consensus by requiring identical JSON strings across all nodes.
Summary
- The equivalence principle separates nondeterministic execution into leader and validator functions to achieve blockchain consensus.
- Leader functions execute variable operations like web requests or LLM calls.
- Validator functions verify that outputs meet predefined acceptance criteria before on-chain storage.
- Use
gl.eq_principlehelpers for standard validation patterns like strict equality or semantic similarity. - Use
gl.vm.run_nondet()for custom validation logic requiring explicit leader and validator definitions. - The pattern is documented in
CLAUDE.mdand implemented in production contracts likecontracts/football_bets.py.
Frequently Asked Questions
What happens if the validator rejects the leader's output?
If the validator function returns False or fails to validate the leader's result, the transaction reverts and the result is not stored on-chain. This prevents nondeterministic or invalid data from being committed to the blockchain state, maintaining network integrity.
Can I use the equivalence principle with any type of nondeterministic operation?
Yes, the equivalence principle applies to any operation that may produce different results across validators, including gl.nondet.web.render for web scraping, gl.nondet.exec_prompt for LLM interactions, or other external API calls. Both convenience helpers and custom validation support these varied use cases.
What is the difference between strict_eq and prompt_comparative validation?
gl.eq_principle.strict_eq() requires byte-for-byte identical output from all validators, making it suitable for structured data like JSON. gl.eq_principle.prompt_comparative() uses semantic comparison to determine if answers are equivalent in meaning rather than identical in text, which is necessary for LLM-generated content that may vary in phrasing while conveying the same information.
When should I use custom leader/validator functions instead of helpers?
Use custom leader and validator functions via gl.vm.run_nondet() when your validation logic requires domain-specific rules, partial data extraction, or complex conditional checks that go beyond equality or semantic comparison. The convenience helpers are optimized for common patterns, while custom functions provide full programmatic control over acceptance criteria.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →