Skip to content

JAGUNG17/zero-to-genlayer-tutorial

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

From Zero to GenLayer: Building a Decentralized Truth Machine

Introduction

Welcome to the "From Zero to GenLayer" tutorial! In this series, you'll embark on a hands-on journey to understand and build decentralized applications (dApps) on the GenLayer network. We'll demystify core GenLayer concepts and guide you through creating a practical dApp: a Decentralized Truth Machine.

This tutorial is designed for developers new to GenLayer, providing a clear path from foundational knowledge to a working dApp. By the end, you'll have a solid understanding of GenLayer's unique features and the skills to start building your own innovative projects.

What You Will Learn

  • GenLayer Core Concepts: Dive into Optimistic Democracy Consensus and the Equivalence Principle.
  • Intelligent Contracts: Write a Python-based Intelligent Contract that leverages AI for decision-making.
  • Frontend Development: Build a simple web interface using genlayer-js to interact with your Intelligent Contract.
  • Deployment: Understand how to deploy and manage your dApp using GenLayer Studio.

Project Overview: Decentralized Truth Machine

The Decentralized Truth Machine is a dApp where users can submit claims, and the GenLayer network, powered by its Intelligent Contracts, will determine the truthfulness of these claims. This project will showcase how GenLayer can be used to create reliable, AI-driven dispute resolution or fact-checking mechanisms.

Tutorial Structure

This tutorial is divided into several parts:

  • Part 1: Understanding GenLayer's Core - Optimistic Democracy & Equivalence Principle
  • Part 2: Crafting Your Intelligent Contract - The Truth Machine (Python)
  • Part 3: Building the Frontend - Interacting with genlayer-js
  • Part 4: Deploying Your dApp with GenLayer Studio

Let's get started!

References

Part 1: Understanding GenLayer's Core - Optimistic Democracy & Equivalence Principle

GenLayer introduces a novel approach to decentralized consensus, combining the efficiency of optimistic systems with the power of AI. At its heart are two fundamental concepts: Optimistic Democracy and the Equivalence Principle.

Optimistic Democracy

Optimistic Democracy is GenLayer's AI-native consensus mechanism designed to validate transactions and operations of Intelligent Contracts [1]. Unlike traditional blockchain consensus mechanisms that require all nodes to execute every transaction, Optimistic Democracy operates on an optimistic model. This means that transactions are assumed to be valid unless challenged. Validators monitor the network and only intervene if they detect a fraudulent or incorrect operation. This significantly increases throughput and reduces latency compared to traditional methods.

Key aspects of Optimistic Democracy:

  • Efficiency: Transactions are processed quickly without requiring redundant computation from all nodes.
  • Scalability: The optimistic nature allows for a higher volume of transactions.
  • Security: A robust challenge mechanism ensures that invalid transactions can be identified and penalized.

Equivalence Principle

The Equivalence Principle is a crucial component that works in conjunction with Optimistic Democracy, especially for Intelligent Contracts that involve AI computations. Intelligent Contracts often produce non-deterministic outputs (e.g., from an LLM). The Equivalence Principle addresses this by defining criteria for an acceptable summary or output, rather than requiring an exact match [2].

For example, if an Intelligent Contract asks an AI to summarize a document, different AI models or even the same model run at different times might produce slightly different but equally valid summaries. The Equivalence Principle allows validators to approve any summary that meets predefined criteria (e.g., accuracy, relevance, length) instead of demanding a byte-for-byte identical output.

Key aspects of the Equivalence Principle:

  • AI Integration: Enables the use of AI in Intelligent Contracts by accommodating non-deterministic outputs.
  • Criteria-Based Validation: Validators evaluate outputs against a set of criteria, not an exact match.
  • Flexibility: Allows for variations in AI responses while maintaining integrity and consensus.

Together, Optimistic Democracy and the Equivalence Principle create a powerful and efficient framework for building AI-driven dApps on GenLayer. They allow for the speed and scalability needed for modern applications while ensuring the decentralized security and trustworthiness of the blockchain.

Part 2: Crafting Your Intelligent Contract - The Truth Machine (Python)

In this part, we'll write the core logic of our Decentralized Truth Machine using a Python Intelligent Contract. This contract will allow users to submit a claim, and then use an AI (Large Language Model) to determine if the claim is true or false based on predefined criteria. The Equivalence Principle will be key here to handle the AI's non-deterministic output.

Setting up Your Development Environment

Before we start coding, ensure you have the necessary tools installed. You'll need Python and the GenLayer SDK. For simplicity, we'll assume you're working within a virtual environment.

pip install py-genlayer

The Intelligent Contract Code

Let's create a file named truth_machine.py for our Intelligent Contract:

# { "Depends": "py-genlayer" }

from genlayer import *
import typing

class TruthMachine(gl.Contract):
    claims: dict[str, str] # Stores claims and their truth status

    def __init__(self):
        self.claims = {}

    @gl.public.write
    def submit_claim(self, claim_id: str, statement: str) -> None:
        # Use AI to determine the truthfulness of the statement
        # The Equivalence Principle ensures validators agree on the 'truth' based on criteria
        truth_status = gl.eq_principle.prompt_non_comparative(
            lambda: f"Is the following statement true or false? Answer with 'True' or 'False'. Statement: {statement}",
            task=f"Determine if the statement '{statement}' is true or false.",
            criteria="The answer must be either 'True' or 'False' based on common knowledge or factual information."
        )
        self.claims[claim_id] = truth_status

    @gl.public.view
    def get_claim_status(self, claim_id: str) -> str:
        return self.claims.get(claim_id, "Claim not found")

    @gl.public.view
    def get_all_claims(self) -> dict[str, str]:
        return self.claims

Code Explanation

  1. # { "Depends": "py-genlayer" }: This special comment tells the GenLayer VM that our contract depends on the py-genlayer library.
  2. TruthMachine(gl.Contract): Our contract class inherits from gl.Contract, making it a valid GenLayer Intelligent Contract.
  3. claims: dict[str, str]: This declares a state variable claims which is a dictionary. It will store claim_id (string) as keys and their truth_status (string, either "True" or "False") as values.
  4. __init__(self): The constructor initializes the claims dictionary as empty.
  5. @gl.public.write def submit_claim(self, claim_id: str, statement: str) -> None:
    • @gl.public.write: This decorator marks submit_claim as a public write method, meaning it can modify the contract's state and be called by external users.
    • Inside this method, we use gl.eq_principle.prompt_non_comparative to interact with an AI model. This function takes three arguments:
      • A lambda function that defines the prompt sent to the AI. We ask the AI to determine if a given statement is true or false.
      • task: A brief description of the task for the AI.
      • criteria: This is where the Equivalence Principle comes into play. We define strict criteria: "The answer must be either 'True' or 'False' based on common knowledge or factual information." This allows different AI models (or runs) to produce slightly different reasoning, but as long as their final answer is 'True' or 'False' and adheres to the criteria, validators will agree on the outcome.
    • The AI's truth_status is then stored in our claims dictionary.
  6. @gl.public.view def get_claim_status(self, claim_id: str) -> str:
    • @gl.public.view: This decorator marks get_claim_status as a public view method. It can read the contract's state without modifying it, and thus doesn't cost gas.
    • It returns the truth status for a specific claim_id or "Claim not found" if the ID doesn't exist.
  7. @gl.public.view def get_all_claims(self) -> dict[str, str]:
    • Another public view method that returns the entire claims dictionary, allowing anyone to see all submitted claims and their determined truth statuses.

This Intelligent Contract demonstrates how GenLayer enables AI-powered logic directly on-chain, with the Equivalence Principle ensuring consensus even with non-deterministic AI outputs. Next, we'll build a frontend to interact with this contract. contract.

Part 3: Building the Frontend - Interacting with genlayer-js

Now that our Intelligent Contract is ready, let's build a simple web frontend to interact with it. We'll use genlayer-js, the official JavaScript SDK for GenLayer, to connect to the network, deploy our contract (conceptually, as deployment will be covered in Part 4), and call its methods.

Setting up Your Frontend Project

For this tutorial, we'll keep the frontend very simple, using plain HTML, CSS, and JavaScript. Create a new folder for your frontend and inside it, create an index.html file and a script.js file.

First, install genlayer-js in your frontend project directory:

npm init -y
npm install genlayer-js

index.html

This file will contain the basic structure of our dApp, including input fields for claims and buttons to interact with the contract.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Decentralized Truth Machine</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; background-color: #f4f4f4; }
        .container { background-color: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); max-width: 600px; margin: auto; }
        input[type="text"], textarea { width: calc(100% - 22px); padding: 10px; margin-bottom: 10px; border: 1px solid #ddd; border-radius: 4px; }
        button { background-color: #4CAF50; color: white; padding: 10px 15px; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; }
        button:hover { background-color: #45a049; }
        .result { margin-top: 20px; padding: 10px; background-color: #e9e9e9; border-radius: 4px; }
    </style>
</head>
<body>
    <div class="container">
        <h1>Decentralized Truth Machine</h1>
        
        <h2>Submit a Claim</h2>
        <label for="claimIdInput">Claim ID:</label>
        <input type="text" id="claimIdInput" placeholder="Enter a unique claim ID">
        <label for="statementInput">Statement:</label>
        <textarea id="statementInput" rows="4" placeholder="Enter the statement to verify"></textarea>
        <button id="submitClaimBtn">Submit Claim</button>

        <h2>Check Claim Status</h2>
        <label for="checkClaimIdInput">Claim ID:</label>
        <input type="text" id="checkClaimIdInput" placeholder="Enter claim ID to check">
        <button id="checkStatusBtn">Check Status</button>
        <div class="result" id="claimStatusResult"></div>

        <h2>All Claims</h2>
        <button id="getAllClaimsBtn">Get All Claims</button>
        <pre class="result" id="allClaimsResult"></pre>
    </div>

    <script type="module" src="script.js"></script>
</body>
</html>

script.js

This JavaScript file will contain the logic to interact with our TruthMachine Intelligent Contract using genlayer-js.

import { createClient, createAccount } from 'genlayer-js';
import { localnet } from 'genlayer-js/chains';
import { TransactionStatus } from 'genlayer-js/types';

// --- Configuration --- 
// IMPORTANT: Replace with your actual deployed contract address
const CONTRACT_ADDRESS = '0x...'; // You will get this after deploying in Part 4

// --- Initialize GenLayer Client ---
const account = createAccount(); // Generates a new local account for signing transactions
const client = createClient({
    chain: localnet, // Connect to the local GenLayer network
    account: account,
});

// --- Helper Function to Display Results ---
function displayResult(elementId, message, isError = false) {
    const element = document.getElementById(elementId);
    element.textContent = message;
    element.style.color = isError ? 'red' : 'green';
}

// --- Contract Interaction Functions ---
async function submitClaim() {
    const claimId = document.getElementById('claimIdInput').value;
    const statement = document.getElementById('statementInput').value;

    if (!claimId || !statement) {
        displayResult('claimStatusResult', 'Please enter both Claim ID and Statement.', true);
        return;
    }

    displayResult('claimStatusResult', 'Submitting claim... This may take a moment.', false);
    try {
        // Ensure the consensus smart contract is initialized
        await client.initializeConsensusSmartContract();

        const txHash = await client.writeContract({
            address: CONTRACT_ADDRESS,
            functionName: 'submit_claim',
            args: [claimId, statement],
            value: 0n,
        });

        displayResult('claimStatusResult', `Transaction sent: ${txHash}. Waiting for confirmation...`, false);

        const receipt = await client.waitForTransactionReceipt({
            hash: txHash,
            status: TransactionStatus.ACCEPTED,
            retries: 100,
            interval: 5000,
        });

        displayResult('claimStatusResult', `Claim '${claimId}' submitted successfully! Status: ${receipt.status}`, false);
    } catch (error) {
        console.error('Error submitting claim:', error);
        displayResult('claimStatusResult', `Error: ${error.message}`, true);
    }
}

async function checkClaimStatus() {
    const claimId = document.getElementById('checkClaimIdInput').value;

    if (!claimId) {
        displayResult('claimStatusResult', 'Please enter a Claim ID to check.', true);
        return;
    }

    displayResult('claimStatusResult', `Checking status for claim '${claimId}'...`, false);
    try {
        const status = await client.readContract({
            address: CONTRACT_ADDRESS,
            functionName: 'get_claim_status',
            args: [claimId],
        });
        displayResult('claimStatusResult', `Status for '${claimId}': ${status}`, false);
    } catch (error) {
        console.error('Error checking claim status:', error);
        displayResult('claimStatusResult', `Error: ${error.message}`, true);
    }
}

async function getAllClaims() {
    displayResult('allClaimsResult', 'Fetching all claims...', false);
    try {
        const allClaims = await client.readContract({
            address: CONTRACT_ADDRESS,
            functionName: 'get_all_claims',
            args: [],
        });
        document.getElementById('allClaimsResult').textContent = JSON.stringify(allClaims, null, 2);
        document.getElementById('allClaimsResult').style.color = 'black';
    } catch (error) {
        console.error('Error fetching all claims:', error);
        displayResult('allClaimsResult', `Error: ${error.message}`, true);
    }
}

// --- Event Listeners ---
document.getElementById('submitClaimBtn').addEventListener('click', submitClaim);
document.getElementById('checkStatusBtn').addEventListener('click', checkClaimStatus);
document.getElementById('getAllClaimsBtn').addEventListener('click', getAllClaims);

// Initial check to ensure client is ready
client.initializeConsensusSmartContract().then(() => {
    console.log('GenLayer client initialized and ready.');
}).catch(error => {
    console.error('Failed to initialize GenLayer client:', error);
    alert('Failed to connect to GenLayer network. Please ensure your localnet is running.');
});

How to Run the Frontend

  1. Save the index.html and script.js files in the same directory.
  2. Open your terminal in that directory and run npm install genlayer-js.
  3. You'll need a local web server to serve these files. A simple way is to use live-server (install with npm install -g live-server) or Python's http.server (python -m http.server).
  4. Open index.html in your browser via the local server. You should see the dApp interface.

Note: The CONTRACT_ADDRESS in script.js needs to be updated with the actual address of your deployed TruthMachine contract. We will cover deployment in the next part.

Part 4: Deploying Your dApp with GenLayer Studio

Deploying your Intelligent Contract to the GenLayer network is a straightforward process, especially when using the GenLayer Studio. GenLayer Studio provides a user-friendly interface to manage your contracts, deploy them to various networks (local, testnet, mainnet), and interact with them.

Prerequisites

Before deploying, ensure you have:

  1. GenLayer CLI installed: This is often part of the GenLayer SDK setup.
  2. GenLayer localnet running: For development and testing, you'll typically deploy to a local GenLayer network instance.

Deployment Steps using GenLayer Studio

  1. Start GenLayer Studio: Open your terminal and run the GenLayer Studio command (refer to GenLayer documentation for specific command, usually genlayer studio). This will typically open a web interface in your browser.

  2. Connect to Your Network: In GenLayer Studio, ensure you are connected to your local GenLayer network. You might need to configure the endpoint if it's not automatically detected.

  3. Upload Your Contract: Navigate to the contract deployment section in GenLayer Studio. You will typically have an option to upload your Python Intelligent Contract file (truth_machine.py).

  4. Deploy the Contract: Once uploaded, GenLayer Studio will guide you through the deployment process. Since our TruthMachine contract has no constructor arguments, deployment is simple. Confirm the deployment.

  5. Note the Contract Address: After successful deployment, GenLayer Studio will provide you with the contract address. This is a unique identifier for your deployed contract on the GenLayer network. Copy this address carefully.

  6. Update Your Frontend: Go back to your script.js file in your frontend project and replace the placeholder CONTRACT_ADDRESS = '0x...'; with the actual address you copied from GenLayer Studio.

    // --- Configuration ---
    // IMPORTANT: Replace with your actual deployed contract address
    const CONTRACT_ADDRESS = 'YOUR_DEPLOYED_CONTRACT_ADDRESS_HERE'; 
  7. Test Your dApp: With the contract deployed and your frontend updated, refresh your index.html in the browser. You can now use the frontend to:

    • Submit new claims, which will trigger the submit_claim method on your Intelligent Contract.
    • Check the status of specific claims using get_claim_status.
    • View all claims and their truth statuses using get_all_claims.

Congratulations! You have successfully deployed your first GenLayer dApp, the Decentralized Truth Machine, and built a frontend to interact with it. This project demonstrates the power of Intelligent Contracts and the ease of deployment with GenLayer Studio.

References

[1] GenLayer Documentation. "Optimistic Democracy." https://docs.genlayer.com/understand-genlayer-protocol/core-concepts/optimistic-democracy [2] GenLayer Documentation. "Equivalence Principle Mechanism." https://docs.genlayer.com/understand-genlayer-protocol/core-concepts/optimistic-democracy/equivalence-principle [3] GenLayer Documentation. "GenLayerJS SDK Reference." https://docs.genlayer.com/api-references/genlayer-js [4] GenLayer Documentation. "LlmHelloWorldNonComparative Contract." https://docs.genlayer.com/developers/intelligent-contracts/examples/llm-hello-world-non-comparative

About

A simple introductory tutorial for building on GenLayer: Decentralized Truth Machine.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors