{ "cells": [ { "cell_type": "markdown", "id": "5d2c17ff", "metadata": {}, "source": [ "# Authenticate — Trustless Initiation Proof\n", "\n", "> These examples run against Sepolia testnet (`network=\"sepolia\"`).\n", "> To run against Base mainnet: replace `configure(network=\"sepolia\")` with `configure(network=\"base\")`.\n", "> All function calls, record structures, and output shapes are identical across networks.\n", "\n", "Every content anchor in AnchorRegistry carries a `tokenCommitment` — a `keccak256(K || arId)` hash stored immutably on Base.\n", "\n", "The ownership token `K = keccak256(salt)` is generated in the browser at registration time from 32 uniform random bytes and **never transmitted** to AnchorRegistry servers. Only the token holder can produce the pre-image. This makes the proof trustless: no AnchorRegistry infrastructure needed, no trust required.\n", "\n", "Two verification levels:\n", "- **`authenticate_anchor`** — verify a single anchor was initiated by the token holder\n", "- **`authenticate_tree`** — verify tree ownership (Layer 1) + every anchor in the tree (Layer 2)" ] }, { "cell_type": "code", "execution_count": 1, "id": "a3015189", "metadata": {}, "outputs": [ { "ename": "ImportError", "evalue": "cannot import name 'V1_BASE_SEPOLIA' from 'anchorregistry' (/Users/ian_moore/repos/ar-python/src/anchorregistry/__init__.py)", "output_type": "error", "traceback": [ "\u001b[31m---------------------------------------------------------------------------\u001b[39m", "\u001b[31mImportError\u001b[39m Traceback (most recent call last)", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[1]\u001b[39m\u001b[32m, line 3\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;66;03m# Cell 1 — setup\u001b[39;00m\n\u001b[32m 2\u001b[39m \u001b[38;5;66;03m# Explorer: https://sepolia.basescan.org/\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m3\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01manchorregistry\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m configure, authenticate_tree, authenticate_anchor, BASE_SEPOLIA_RPC, V1_BASE_SEPOLIA\n\u001b[32m 4\u001b[39m configure(\n\u001b[32m 5\u001b[39m network=\u001b[33m\"\u001b[39m\u001b[33mbase-sepolia\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 6\u001b[39m contract_address=V1_BASE_SEPOLIA,\n\u001b[32m 7\u001b[39m rpc_url=BASE_SEPOLIA_RPC, \u001b[38;5;66;03m# swap for Infura / Alchemy URL for faster scans\u001b[39;00m\n\u001b[32m 8\u001b[39m )\n", "\u001b[31mImportError\u001b[39m: cannot import name 'V1_BASE_SEPOLIA' from 'anchorregistry' (/Users/ian_moore/repos/ar-python/src/anchorregistry/__init__.py)" ] } ], "source": [ "# Cell 1 — setup\n", "# Explorer: https://sepolia.basescan.org/\n", "from anchorregistry import configure, authenticate_tree, authenticate_anchor, BASE_SEPOLIA_RPC, V1A_BASE_SEPOLIA\n", "configure(\n", " network=\"base-sepolia\",\n", " contract_address=V1A_BASE_SEPOLIA,\n", " rpc_url=BASE_SEPOLIA_RPC, # swap for Infura / Alchemy URL for faster scans\n", ")\n" ] }, { "cell_type": "code", "execution_count": null, "id": "5e020a18", "metadata": {}, "outputs": [], "source": [ "# Cell 2 — authenticate a single anchor\n", "# Replace ownership_token and ar_id with your own values\n", "result = authenticate_anchor(\n", " ownership_token=\"0xf4accdf917e8cf2b9e0ceb0b281fba1c1a176f8922fb5eb432bfa0a5664b0680\",\n", " ar_id=\"AR-2026-dPXazj6\"\n", ")\n", "result" ] }, { "cell_type": "code", "execution_count": null, "id": "1976e78a", "metadata": {}, "outputs": [], "source": [ "# Cell 3 — authenticate full tree\n", "# Layer 1: keccak256(K || rootArId) == treeId\n", "# Layer 2: verify every user-initiated anchor in the tree\n", "result = authenticate_tree(\n", " ownership_token=\"0xf4accdf917e8cf2b9e0ceb0b281fba1c1a176f8922fb5eb432bfa0a5664b0680\",\n", " root_ar_id=\"AR-2026-dPXazj6\"\n", ")\n", "result" ] }, { "cell_type": "code", "execution_count": null, "id": "68f02d69", "metadata": {}, "outputs": [], "source": [ "# Cell 4 — cron job audit pattern\n", "# Run this on a schedule to continuously verify your tree hasn't been tampered with\n", "import os\n", "result = authenticate_tree(\n", " ownership_token=os.environ.get(\"ANCHOR_OWNERSHIP_TOKEN\", \"0xf4accdf917e8cf2b9e0ceb0b281fba1c1a176f8922fb5eb432bfa0a5664b0680\"),\n", " root_ar_id=\"AR-2026-dPXazj6\"\n", ")\n", "if not result[\"authenticated\"]:\n", " print(f\"AUDIT FAILED — {result['anchors_failed']} anchors unverified\")\n", "else:\n", " print(f\"AUDIT PASSED — {result['anchors_verified']} anchors verified\")" ] }, { "cell_type": "markdown", "id": "5522455b", "metadata": {}, "source": [ "---\n", "\n", "The verification above ran entirely against the Base blockchain.\n", "No AnchorRegistry servers involved.\n", "\n", "**One ownership token proves the entire tree is yours.**\n", "\n", "```\n", "treeId = keccak256(K || rootArId) → tree ownership\n", "tokenCommitment = keccak256(K || childArId) → per-anchor initiation\n", "```\n", "\n", "Same ownership token `K`. Same construction. One key proves everything.\n", "\n", "To dispute a tokenCommitment: produce the ownership token `K` such that `keccak256(K || arId) == tokenCommitment`. Producing this token proves you initiated the registration. The proof is on Base — permanent and independent of AnchorRegistry infrastructure." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.15" } }, "nbformat": 4, "nbformat_minor": 5 }