Back to the courseDownload Markdown

Step-by-step course guide

Keep the lab proof and deployment evidence separate. The local ECVRF, VDF and lottery exercises require no payment. On the lottery showcase, identify the environment and inspect the draw evidence before interpreting it. UNI uses a simulated chain; a Base transaction must be checked on Base. The documented live entry is an unpaid work seat through enterFromWork; paid tickets are disabled.

Audience: developers learning lottery smart-contract patterns before touching Base mainnet.
Languages: COURSE_LANG=en|ru|es · UI strings in i18n/ · this guide in English.
Русский: step-by-step.ru.md · Español: step-by-step.es.md


Why this course

On-chain lotteries fail when the operator can quietly re-roll a draw until they like the winner — a "grinding" attack. The lottery/ contracts on Base defeat that with verifiable randomness (ECVRF via the Sortes oracle) and a Wesolowski VDF beacon from the Chronos oracle, settling payouts through a receipt-gated AIMarketEscrow channel. This course teaches each of those primitives as a small, runnable, LLM-free Python port. You verify the same Foundry test vector the Solidity contracts use (ChronosVDF.t.sol), walk a relayer round end to end, and finish with a capstone that simulates a full fair round with verifiable artifacts — so what you learn maps directly onto the lottery/ codebase.

Crypto honesty: The oracle primitives here are research/prototype tier (see oracles crypto-maturity) — fine for testnet and learning, not a substitute for external audit before mainnet TVL.


Table of contents

  1. Why this course
  2. Choose your track
  3. Setup (10 minutes)
  4. Module M1 — Unbiasable lottery draw
  5. Module M2 — VDF verify on-chain
  6. Module M3 — Escrow & payment channels
  7. Module M4 — Relayer round lifecycle
  8. Module M5 — Capstone: fair round
  9. Exercises & certificate
  10. Observability in every lab
  11. Bridge: from course to lottery/
  12. Self-check checklist
  13. Troubleshooting

Choose your track

Track Modules Time Oracles
Basic M1 → M2 → M3 → M4 ~1.5 h Sortes + Chronos (fallback if not installed)
Advanced Basic + M5 capstone +30 min Sortes + Chronos VDF path
lottery/ bridge After M4 or M5 +30 min Live forge test + Base contracts

Read Observability after each lab — every script prints a Trace you can inspect.


Setup (10 minutes)

Step 1 — Clone and install

git clone https://github.com/alexar76/aimarket-courses.git
cd aimarket-courses/smart-contracts-course
pip install -e ".[oracles,dev]"

The [oracles] extra wires in the Sortes (ECVRF) and Chronos (VDF) oracle packages. If the extra is unavailable in your environment the labs still run — courselib falls back to a deterministic stdlib implementation and the trace marks oracle=fallback. Python 3.11+ is required.

Step 2 — Verify tests

pytest -q

Expected: all tests pass (i18n parity across en/ru/es, contract primitives, the bundled Foundry vector, certificate rendering).

Step 3 — Run the first lab

python labs/lab01_unbiasable_draw.py
python labs/run_exercises.py

Step 4 — Pick a language (optional)

export COURSE_LANG=ru   # or es, default en
python labs/lab01_unbiasable_draw.py

COURSE_LANG only switches the lab's printed strings (module titles, labels, hints) — code, commands, ticket numbers, and the Foundry vector are identical in every language.

Step 5 — Colab alternative

  1. Open the course site → Open in Colab on any lab.
  2. Run the setup cell (clone + pip install -e ".[oracles,dev]").
  3. Set os.environ["COURSE_LANG"] = "ru" in the setup cell if needed.
  4. Run the lab cell.

Module M1 — Unbiasable lottery draw

Concept: Commit–reveal and ECVRF fix operator grinding attacks.
Lab: labs/lab01_unbiasable_draw.py · Time: ~15 min · Prerequisites: setup done

Steps

  1. Open labs/lab01_unbiasable_draw.py and read the module docstring.

  2. Run locally:

    python labs/lab01_unbiasable_draw.py
    
  3. Compare the two draws in the output:

    • Biased lottery (operator grind) — grindable=True: the operator re-hashes with a secret until a favorable ticket appears (BiasedLottery.operator_can_grind).
    • Fair lottery (ECVRF) — one alpha maps to exactly one beta, with verified=True. The operator cannot re-roll.
  4. Open courselib/contracts.py — read BiasedLottery, FairLottery, and _sortes_draw (the Sortes ECVRF path with a stdlib fallback).

  5. Exercise: python labs/run_exercises.py --module m1 — proves the fair draw verifies while the biased one grinds.

What it proves

A verifiable draw removes operator discretion: the winner is a deterministic function of a public seed plus a proof anyone can check. On-chain consumer reference: lottery/docs/ and AIAgentLottery.sol.

Self-check


Module M2 — VDF verify on-chain

Concept: Wesolowski proofs from Chronos match Foundry test vectors.
Lab: labs/lab02_vdf_verify.py · Time: ~15 min · Prerequisites: M1

Steps

  1. Run python labs/lab02_vdf_verify.py.
  2. The lab loads the bundled Foundry vector (courselib/fixtures/chronos_vector.json, a copy of lottery/contracts/test/vectors/chronos_vector.json) and verifies π^l·g^r ≡ y (mod N). Output shows verify: True (AB_equals_y=True).
  3. If the Chronos oracle is installed, verify_wesolowski_vector also re-derives the proof live (vdf.hash_to_group → evaluate → prove → verify); otherwise the bundled intermediates alone are checked.
  4. Exercise: python labs/run_exercises.py --module m2 — verifies the same vector the Solidity test uses.

What it proves

The Python verifier and the Solidity verifier agree on the exact same bytes. Run forge test in lottery/contracts — ChronosVDF.t.sol consumes the identical vector, so a green Python check predicts a green Foundry check.

Self-check


Module M3 — Escrow & payment channels

Concept: Hold funds until delivery; receipt-gated debit like AIMarketEscrow.
Lab: labs/lab03_escrow_channel.py · Time: ~20 min · Prerequisites: M1

Steps

  1. Run python labs/lab03_escrow_channel.py.
  2. Follow the channel lifecycle in the output: open (deposit) → debit (receipt-gated) → settle (split between hub and refund).
  3. Watch the Receipt replay line: a second debit with the same receipt_id is rejected (blocked=True). This is the on-chain replay guard — EscrowChannel.used_receipts.
  4. Skim courselib/contracts.py → EscrowChannel. It models AIMarketEscrow open/debit/settle (Protocol v2 §6): signature check, nonce, hub-match, balance guard.
  5. Exercise: python labs/run_exercises.py --module m3 — debit twice with one receipt; the second must fail.

Self-check


Module M4 — Relayer round lifecycle

Concept: baseSeed, Sortes draw, optional on-chain VDF path.
Lab: labs/lab04_relayer_round.py · Time: ~20 min · Prerequisites: M1–M2

Steps

  1. Run python labs/lab04_relayer_round.py.
  2. Trace the three phases in the output and the Trace block:
    • Chronos seed — base_seed = sha256(roundId ‖ blockhash ‖ platonRandom), the keccak-packed seed the relayer feeds Chronos.
    • Sortes ticket — the verifiable draw for this round (verified=True).
    • VDF vector — when onchain_vdf=True, the round attaches a VdfProof beacon and re-verifies the Foundry vector (vector_valid=True).
  3. Cross-read: lottery/relayer/ailottery_relayer/economy_draw.py — the production draw flow this lab mirrors.
  4. Exercise: python labs/run_exercises.py --module m4 — trace baseSeed → Sortes → VDF beacon.

Self-check


Module M5 — Capstone: fair round

Concept: Simulate a full lottery round with verifiable artifacts.
Lab: labs/lab05_lottery_capstone.py · Time: ~30 min · Prerequisites: M1–M4 · Track: advanced

Steps

  1. Run python labs/lab05_lottery_capstone.py.
  2. The lab calls simulate_fair_round(onchain_vdf=True, client_seed=...) and prints the round artifacts: ticket, vdf_path=onchain, and chronos_vector=True.
  3. Compare these artifacts to a real lottery/relayer log — every field has a production counterpart.
  4. Exercise: python labs/run_exercises.py --module m5 — run simulate_fair_round with onchain_vdf=True and confirm the path and vector.

What it proves

You can assemble the M1–M4 primitives into one auditable round: a verifiable ticket, a VDF beacon, and a checkable vector — the artifacts a player (or auditor) needs to trust the result without trusting the operator.

Self-check


Exercises & certificate

After the labs, run the DIY checks:

python labs/run_exercises.py                 # all modules
python labs/run_exercises.py --module m3     # one module

Each module prints ✓ or ✗; fix any failures and re-run. When all pass, generate a certificate:

python labs/run_exercises.py --certificate "Your Name" --lang en

This writes certificate.html (override with -o). Open it in a browser and Print → Save as PDF. The credential ID is derived from name + date (courselib/certificate.py). You can also generate it from the Certificate section of the course site.


Observability in every lab

Concept: Every lab logs decisions to Trace — treat it as your audit log.
Covered in: every lab via courselib/trace.py · Time: 5 min after each lab

Steps (repeat after each lab)

  1. Scroll to the trace section at the end of the lab output.
  2. Count the events and map each to a step you saw in stdout — biased_draw, fair_draw, vdf_verify, open/debit/settle, base_seed/sortes_draw/vdf_beacon, capstone.
  3. In M4, correlate the three trace events to the three printed phases.

Self-check


Bridge: from course to lottery/

Goal: Connect course primitives to the production lottery/ repo (~30 min).

  1. Foundry vector — lottery/contracts/test/vectors/chronos_vector.json (bundled in courselib/fixtures/). The course verifier reads the same file the Solidity test does.

  2. On-chain verifier — lottery/contracts/src/ChronosVDF.sol; its test is ChronosVDF.t.sol. Run it:

    cd lottery/contracts && forge test
    
  3. Relayer draw flow — lottery/relayer/ailottery_relayer/economy_draw.py (mirrored by M4).

  4. Map course → lottery/

    Course lottery/
    M1 fair draw Sortes ECVRF + AIAgentLottery.sol
    M2 VDF verify ChronosVDF.sol + ChronosVDF.t.sol
    M3 escrow channel AIMarketEscrow open/debit/settle
    M4 relayer round economy_draw.py round lifecycle
    M5 capstone Full relayer round + on-chain VDF beacon
  5. Docs: lottery/docs/.


Self-check checklist

After the basic track you should be able to:

After the advanced track add:


Troubleshooting

Problem Fix
ModuleNotFoundError: courselib Run from the repo root; labs prepend the parent dir to sys.path
Oracle import fails / pip install ".[oracles]" errors Labs still run with the stdlib fallback (trace shows oracle=fallback); for the live ECVRF/VDF path run from the aicom monorepo where oracles/ resolves
verify: False in M2 Confirm courselib/fixtures/chronos_vector.json is intact (valid and AB_equals_y must be true)
Empty RU/ES strings Set COURSE_LANG; check i18n/ru.json keys match en.json
pytest i18n parity failure Add the missing key to all five i18n/{en,ru,es,fr,zh}.json
Colab running old code Re-run the setup cell; pin the main branch

lottery/ deployment & on-chain issues: lottery/docs/.


Guides in docs/ are hand-maintained — update EN first, then the RU/ES mirrors.