From dc392b6a64031a15a2621d4bb1b987d17f6c0185 Mon Sep 17 00:00:00 2001 From: sitongshu123 Date: Sun, 23 Aug 2026 22:28:42 +0800 Subject: [PATCH] feat: add AI Evidence Oracle example (Procedural Proof) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A new trust primitive for the AI era — OApp that verifies AI process compliance (not data correctness) and dispatches verified decisions cross-chain via LayerZero V2. Four-layer architecture: 1. Evidence: AI submits decision + evidence hash (IPFS commitment) 2. Verification: Independent verifiers audit SOP compliance 3. Distribution: LayerZero dispatches verified decisions cross-chain 4. Execution: Target chain receives and acts Trust model isomorphic to DVN: - DVN verifies transport integrity (hash comparison) - EvidenceOracle verifies process compliance (SOP audit) - Both are permissionlessly challengeable (fraud proof via replay) Includes: - EvidenceOracle.sol — single OApp contract - Hardhat tasks: ai:submit, verify:decision, dispatch:decision, challenge:decision - SOP framework with example (rainfall trigger for insurance) - layerzero.config.ts for Sepolia ↔ BSC Testnet - Full README with architecture explanation Live deployment: - Sepolia: 0xaa24CC417EC7ae219F52C400E64E600AAfD3f45d - BSC Testnet: 0xc7FFF00d2c2C6B0aC5D726d3E6e317cCae638214 --- examples/oapp-ai-oracle/.env.example | 9 + examples/oapp-ai-oracle/.gitignore | 7 + examples/oapp-ai-oracle/LICENSE | 21 ++ examples/oapp-ai-oracle/README.md | 189 +++++++++++++ .../contracts/EvidenceOracle.sol | 267 ++++++++++++++++++ .../deploy/001_deploy_evidence_oracle.ts | 23 ++ examples/oapp-ai-oracle/hardhat.config.ts | 41 +++ examples/oapp-ai-oracle/layerzero.config.ts | 31 ++ examples/oapp-ai-oracle/package.json | 37 +++ .../sop/rainfall-trigger-v1.json | 54 ++++ examples/oapp-ai-oracle/tasks/ai-submit.ts | 82 ++++++ .../tasks/challenge-decision.ts | 38 +++ .../oapp-ai-oracle/tasks/dispatch-decision.ts | 48 ++++ .../oapp-ai-oracle/tasks/verify-decision.ts | 44 +++ examples/oapp-ai-oracle/tsconfig.json | 12 + 15 files changed, 903 insertions(+) create mode 100644 examples/oapp-ai-oracle/.env.example create mode 100644 examples/oapp-ai-oracle/.gitignore create mode 100644 examples/oapp-ai-oracle/LICENSE create mode 100644 examples/oapp-ai-oracle/README.md create mode 100644 examples/oapp-ai-oracle/contracts/EvidenceOracle.sol create mode 100644 examples/oapp-ai-oracle/deploy/001_deploy_evidence_oracle.ts create mode 100644 examples/oapp-ai-oracle/hardhat.config.ts create mode 100644 examples/oapp-ai-oracle/layerzero.config.ts create mode 100644 examples/oapp-ai-oracle/package.json create mode 100644 examples/oapp-ai-oracle/sop/rainfall-trigger-v1.json create mode 100644 examples/oapp-ai-oracle/tasks/ai-submit.ts create mode 100644 examples/oapp-ai-oracle/tasks/challenge-decision.ts create mode 100644 examples/oapp-ai-oracle/tasks/dispatch-decision.ts create mode 100644 examples/oapp-ai-oracle/tasks/verify-decision.ts create mode 100644 examples/oapp-ai-oracle/tsconfig.json diff --git a/examples/oapp-ai-oracle/.env.example b/examples/oapp-ai-oracle/.env.example new file mode 100644 index 000000000..c737771ff --- /dev/null +++ b/examples/oapp-ai-oracle/.env.example @@ -0,0 +1,9 @@ +# .env.example +# Copy to .env and fill in your values + +# Deployer private key (with testnet ETH/BNB for gas) +PRIVATE_KEY=0x... + +# RPC URLs (optional — defaults to public RPCs) +RPC_URL_SEPOLIA=https://rpc.sepolia.org +RPC_URL_BSC_TESTNET=https://data-seed-prebsc-1-s1.bnbchain.org:8545 diff --git a/examples/oapp-ai-oracle/.gitignore b/examples/oapp-ai-oracle/.gitignore new file mode 100644 index 000000000..ac0e999ce --- /dev/null +++ b/examples/oapp-ai-oracle/.gitignore @@ -0,0 +1,7 @@ +node_modules/ +artifacts/ +cache/ +dist/ +.env +evidence-store/ +deployments/ diff --git a/examples/oapp-ai-oracle/LICENSE b/examples/oapp-ai-oracle/LICENSE new file mode 100644 index 000000000..c1c033791 --- /dev/null +++ b/examples/oapp-ai-oracle/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 NEOscript + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/examples/oapp-ai-oracle/README.md b/examples/oapp-ai-oracle/README.md new file mode 100644 index 000000000..c372ba97e --- /dev/null +++ b/examples/oapp-ai-oracle/README.md @@ -0,0 +1,189 @@ +

+ + LayerZero + +

+ +

+ LayerZero Docs · Live Demo +

+ +

AI Evidence Oracle (OApp) — Procedural Proof

+ +

+ A new trust primitive for the AI era.
+ Oracle verifies AI process compliance, not data correctness — then dispatches verified decisions cross-chain via LayerZero V2. +

+ +## The Problem + +AI outputs are non-deterministic. Every existing trust primitive fails for them: + +| Primitive | Why it fails for AI | +|-----------|-------------------| +| PoW | Can't prove AI "did work" — inference isn't hashable | +| PoS | Stake doesn't grant cognitive authority | +| ZK Proof | AI reasoning isn't a deterministic circuit | +| Consensus | Re-running same prompt ≠ same output | +| Multisig | Signers judge conclusion, not process | + +## The Solution: Procedural Proof + +Don't verify the conclusion. Verify the process. + +``` +Trust nothing. Verify process. Challenge freely. +``` + +The AI follows a public SOP (Standard Operating Procedure). It submits an evidence package proving every step was followed. Independent verifiers audit the process — not the data, not the conclusion, just "was the SOP followed?" + +Anyone can replay the verification. Fraud is always detectable. + +## Architecture — Four Layers + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Layer 1: EVIDENCE AI executes SOP → evidence → IPFS │ +│ Hash committed on-chain │ +├─────────────────────────────────────────────────────────────┤ +│ Layer 2: VERIFICATION Verifiers audit evidence vs SOP │ +│ Threshold signatures (2-of-3) │ +├─────────────────────────────────────────────────────────────┤ +│ Layer 3: DISTRIBUTION LayerZero V2 cross-chain dispatch │ +│ One judgment → multiple chains │ +├─────────────────────────────────────────────────────────────┤ +│ Layer 4: EXECUTION Target chain receives & acts │ +│ _lzReceive() triggers logic │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Isomorphic to LayerZero DVN + +The trust model is structurally identical to how DVN secures cross-chain messages: + +| | LayerZero DVN | Evidence Oracle | +|--|--------------|-----------------| +| **Verifies** | Message transport integrity | AI SOP compliance | +| **Method** | Hash comparison (source ↔ destination) | Evidence replay (package ↔ SOP rules) | +| **Trust source** | Not the verifier — the replayability | Not the verifier — the replayability | +| **Fraud proof** | Anyone reads source chain hash | Anyone fetches IPFS evidence + SOP | +| **Selection** | OApp chooses DVN set | Agent chooses Verifier set | + +## Prerequisite Knowledge + +- [What is an OApp?](https://docs.layerzero.network/v2/concepts/applications/oapp-standard) +- [How does LayerZero work?](https://docs.layerzero.network/v2/concepts/protocol/core-concepts) + +## Requirements + +- `Node.js` >= 18.16.0 +- `pnpm` (recommended) + +## Setup + +```bash +# Clone +git clone https://github.com/fmdd123123-create/neo-oracle.git +cd neo-oracle + +# Install +pnpm install + +# Configure +cp .env.example .env +# Edit .env with your private key (needs testnet ETH + BNB) +``` + +## Build + +```bash +pnpm compile +``` + +## Deploy + +```bash +# Deploy to both testnets +pnpm deploy --network eth-sepolia +pnpm deploy --network bsc-testnet +``` + +## Wire (Set Peers) + +```bash +pnpm wire +``` + +## Usage — Full Flow + +### 1. AI submits decision with evidence + +```bash +pnpm hardhat ai:submit --network eth-sepolia --sop rainfall-trigger-v1 +``` + +### 2. Verifier audits SOP compliance + +```bash +pnpm hardhat verify:decision --network eth-sepolia --id 0 +``` + +### 3. Dispatch verified decision cross-chain + +```bash +pnpm hardhat dispatch:decision --network eth-sepolia --id 0 --dst-eid 40102 +``` + +### 4. (Optional) Challenge a fraudulent decision + +```bash +pnpm hardhat challenge:decision --network eth-sepolia --id 0 --reason "Source not in whitelist" +``` + +## Live Deployment + +| Network | Contract | EID | +|---------|----------|-----| +| Eth Sepolia | `0xaa24CC417EC7ae219F52C400E64E600AAfD3f45d` | 40161 | +| BSC Testnet | `0xc7FFF00d2c2C6B0aC5D726d3E6e317cCae638214` | 40102 | + +Cross-chain message dispatched and verified on [LayerZeroScan](https://testnet.layerzeroscan.com). + +## SOP Example + +See [`sop/rainfall-trigger-v1.json`](./sop/rainfall-trigger-v1.json) — a complete Standard Operating Procedure for rainfall-triggered insurance. Each step defines required evidence, allowed sources, and verification criteria. + +## Why This Matters + +Every existing Oracle (Chainlink, Band, API3) answers: **"Is this DATA correct?"** + +Evidence Oracle answers: **"Was this PROCESS followed?"** + +For AI outputs — where correctness is undecidable but process compliance is verifiable — this is the only viable trust model. + +## Roadmap + +- [x] Core contract with four-layer architecture +- [x] LayerZero V2 OApp integration +- [x] Cross-chain dispatch (Sepolia → BSC Testnet) +- [x] SOP framework with verification checklist +- [x] Hardhat tasks for full flow demo +- [ ] IPFS integration (Pinata/web3.storage) +- [ ] EIP-712 typed verifier signatures +- [ ] Slashing mechanism for fraudulent verifiers +- [ ] Multi-chain scatter (1 decision → N chains) +- [ ] SOP governance (who writes/modifies SOPs) + +## Acknowledgments + +Built on [LayerZero V2](https://layerzero.network). The DVN architecture provided both the infrastructure for cross-chain distribution and the conceptual model for our trust design — the isomorphism between DVN transport verification and SOP compliance verification is the foundation of Procedural Proof. + +## License + +[MIT](./LICENSE) + +--- + +*NEO Oracle — Trust nothing. Verify process. Challenge freely.* + +Built by [NEOscript](https://ark.honeyhughub.world/neo-oracle/) diff --git a/examples/oapp-ai-oracle/contracts/EvidenceOracle.sol b/examples/oapp-ai-oracle/contracts/EvidenceOracle.sol new file mode 100644 index 000000000..b7bc87a69 --- /dev/null +++ b/examples/oapp-ai-oracle/contracts/EvidenceOracle.sol @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.22; + +import { OApp, Origin, MessagingFee, MessagingReceipt } from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol"; +import { OAppOptionsType3 } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol"; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; + +/** + * @title EvidenceOracle + * @notice Procedural Proof — A new trust primitive for AI-era cross-chain governance. + * + * This OApp implements a four-layer epistemic governance architecture: + * 1. Evidence Layer: AI submits decision + evidence hash (IPFS CID commitment) + * 2. Verification Layer: Independent verifiers audit AI's SOP compliance + * 3. Distribution Layer: LayerZero dispatches verified decisions cross-chain + * 4. Execution Layer: Target chain receives and acts on verified decisions + * + * Trust model isomorphic to LayerZero DVN: + * - DVN verifies message transport integrity (hash comparison) + * - EvidenceOracle verifies AI process compliance (SOP audit) + * - Both are permissionlessly challengeable (fraud proof via replay) + * + * @dev Inherits OApp for native LayerZero V2 cross-chain messaging. + */ +contract EvidenceOracle is OApp, OAppOptionsType3 { + + // ═══════════════════════════════════════════ + // STRUCTS + // ═══════════════════════════════════════════ + + struct Decision { + bytes32 evidenceCid; // IPFS CID of full evidence package + bytes32 sopHash; // keccak256 of the SOP document used + bytes32 conclusionHash; // keccak256 of AI's conclusion + uint8 conclusion; // 0=NO_TRIGGER, 1=TRIGGER + uint64 timestamp; // when AI executed the SOP + address submitter; // AI agent address + uint8 approvals; // verifier approval count + uint8 rejections; // verifier rejection count + bool dispatched; // whether cross-chain dispatch occurred + bool challenged; // whether a challenge was raised + } + + struct SOP { + bytes32 sopHash; + string name; + string ipfsCid; // full SOP doc on IPFS + bool active; + } + + // ═══════════════════════════════════════════ + // STATE + // ═══════════════════════════════════════════ + + uint256 public decisionCount; + uint8 public threshold = 2; // approvals needed for dispatch + + mapping(uint256 => Decision) public decisions; + mapping(bytes32 => SOP) public sops; // sopHash => SOP + mapping(address => bool) public verifiers; + mapping(uint256 => mapping(address => bool)) public hasVoted; + + // ═══════════════════════════════════════════ + // EVENTS + // ═══════════════════════════════════════════ + + event SOPRegistered(bytes32 indexed sopHash, string name, string ipfsCid); + event DecisionSubmitted(uint256 indexed id, address submitter, bytes32 evidenceCid, bytes32 sopHash); + event DecisionVerified(uint256 indexed id, address verifier, bool approved); + event DecisionDispatched(uint256 indexed id, uint32 dstEid, bytes32 receiver); + event DecisionChallenged(uint256 indexed id, address challenger, string reason); + event VerifierUpdated(address verifier, bool status); + + // ═══════════════════════════════════════════ + // CONSTRUCTOR + // ═══════════════════════════════════════════ + + constructor( + address _endpoint, + address _delegate + ) OApp(_endpoint, _delegate) Ownable(_delegate) {} + + // ═══════════════════════════════════════════ + // SOP MANAGEMENT + // ═══════════════════════════════════════════ + + /// @notice Register a new Standard Operating Procedure + /// @param name Human-readable SOP name + /// @param ipfsCid IPFS CID where the full SOP document is stored + function registerSOP(string calldata name, string calldata ipfsCid) external onlyOwner { + bytes32 sopHash = keccak256(abi.encodePacked(name, ipfsCid)); + sops[sopHash] = SOP(sopHash, name, ipfsCid, true); + emit SOPRegistered(sopHash, name, ipfsCid); + } + + // ═══════════════════════════════════════════ + // VERIFIER MANAGEMENT + // ═══════════════════════════════════════════ + + function setVerifier(address verifier, bool status) external onlyOwner { + verifiers[verifier] = status; + emit VerifierUpdated(verifier, status); + } + + function setThreshold(uint8 _threshold) external onlyOwner { + threshold = _threshold; + } + + // ═══════════════════════════════════════════ + // LAYER 1: EVIDENCE SUBMISSION + // ═══════════════════════════════════════════ + + /// @notice AI agent submits a decision with evidence commitment + /// @param evidenceCid IPFS CID of the full evidence package (inputs, steps, outputs) + /// @param sopHash Which SOP was followed + /// @param conclusionHash keccak256 of the conclusion data + /// @param conclusion 0=NO_TRIGGER, 1=TRIGGER + function submitDecision( + bytes32 evidenceCid, + bytes32 sopHash, + bytes32 conclusionHash, + uint8 conclusion + ) external returns (uint256 decisionId) { + require(sops[sopHash].active, "SOP not registered or inactive"); + + decisionId = decisionCount++; + decisions[decisionId] = Decision({ + evidenceCid: evidenceCid, + sopHash: sopHash, + conclusionHash: conclusionHash, + conclusion: conclusion, + timestamp: uint64(block.timestamp), + submitter: msg.sender, + approvals: 0, + rejections: 0, + dispatched: false, + challenged: false + }); + + emit DecisionSubmitted(decisionId, msg.sender, evidenceCid, sopHash); + } + + // ═══════════════════════════════════════════ + // LAYER 2: SOP COMPLIANCE VERIFICATION + // ═══════════════════════════════════════════ + + /// @notice Verifier audits evidence against SOP and votes + /// @dev Isomorphic to DVN hash verification — verifier checks process, not conclusion + function verify(uint256 decisionId, bool approved) external { + require(verifiers[msg.sender], "Not a registered verifier"); + require(!hasVoted[decisionId][msg.sender], "Already voted"); + require(!decisions[decisionId].dispatched, "Already dispatched"); + + hasVoted[decisionId][msg.sender] = true; + + if (approved) { + decisions[decisionId].approvals++; + } else { + decisions[decisionId].rejections++; + } + + emit DecisionVerified(decisionId, msg.sender, approved); + } + + // ═══════════════════════════════════════════ + // LAYER 3: CROSS-CHAIN DISPATCH (LayerZero) + // ═══════════════════════════════════════════ + + /// @notice Dispatch verified decision to target chain via LayerZero + /// @param decisionId The decision to dispatch + /// @param dstEid LayerZero Endpoint ID of target chain + /// @param options LayerZero execution options (gas, value) + function dispatch( + uint256 decisionId, + uint32 dstEid, + bytes calldata options + ) external payable returns (MessagingReceipt memory receipt) { + Decision storage d = decisions[decisionId]; + require(d.approvals >= threshold, "Threshold not met"); + require(!d.dispatched, "Already dispatched"); + + d.dispatched = true; + + // Encode the verified decision for cross-chain delivery + bytes memory payload = abi.encode( + decisionId, + d.evidenceCid, + d.sopHash, + d.conclusionHash, + d.conclusion, + d.approvals + ); + + receipt = _lzSend(dstEid, payload, options, MessagingFee(msg.value, 0), payable(msg.sender)); + + emit DecisionDispatched(decisionId, dstEid, peers[dstEid]); + } + + /// @notice Quote the fee for dispatching a decision + function quoteDispatch( + uint256 decisionId, + uint32 dstEid, + bytes calldata options + ) external view returns (MessagingFee memory fee) { + Decision storage d = decisions[decisionId]; + bytes memory payload = abi.encode( + decisionId, + d.evidenceCid, + d.sopHash, + d.conclusionHash, + d.conclusion, + d.approvals + ); + fee = _quote(dstEid, payload, options, false); + } + + // ═══════════════════════════════════════════ + // LAYER 4: RECEIVE (Target Chain) + // ═══════════════════════════════════════════ + + /// @notice Handle incoming verified decision from source chain + /// @dev Override this in your target-chain contract to execute domain logic + function _lzReceive( + Origin calldata /*_origin*/, + bytes32 /*_guid*/, + bytes calldata _message, + address /*_executor*/, + bytes calldata /*_extraData*/ + ) internal override { + ( + uint256 decisionId, + bytes32 evidenceCid, + bytes32 sopHash, + bytes32 conclusionHash, + uint8 conclusion, + uint8 approvals + ) = abi.decode(_message, (uint256, bytes32, bytes32, bytes32, uint8, uint8)); + + // Store received decision — extend this for domain-specific execution + decisions[decisionId] = Decision({ + evidenceCid: evidenceCid, + sopHash: sopHash, + conclusionHash: conclusionHash, + conclusion: conclusion, + timestamp: uint64(block.timestamp), + submitter: address(0), // cross-chain, original submitter not preserved + approvals: approvals, + rejections: 0, + dispatched: true, + challenged: false + }); + decisionCount++; + } + + // ═══════════════════════════════════════════ + // CHALLENGE (Fraud Proof) + // ═══════════════════════════════════════════ + + /// @notice Anyone can challenge a decision by providing evidence of SOP violation + /// @dev Off-chain: challenger fetches evidence from IPFS, replays SOP, finds violation + function challenge(uint256 decisionId, string calldata reason) external { + require(decisions[decisionId].approvals > 0, "Decision does not exist"); + decisions[decisionId].challenged = true; + emit DecisionChallenged(decisionId, msg.sender, reason); + // In production: slash verifiers who approved a fraudulent decision + } +} diff --git a/examples/oapp-ai-oracle/deploy/001_deploy_evidence_oracle.ts b/examples/oapp-ai-oracle/deploy/001_deploy_evidence_oracle.ts new file mode 100644 index 000000000..675a5d275 --- /dev/null +++ b/examples/oapp-ai-oracle/deploy/001_deploy_evidence_oracle.ts @@ -0,0 +1,23 @@ +import { DeployFunction } from 'hardhat-deploy/types' +import { HardhatRuntimeEnvironment } from 'hardhat/types' +import { EndpointId } from '@layerzerolabs/lz-definitions' + +// LayerZero V2 testnet endpoint (shared across testnets) +const LZ_ENDPOINT_TESTNET = '0x6edce65403992e310a62460808c4b910d972f10f' + +const deploy: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { + const { deploy } = hre.deployments + const { deployer } = await hre.getNamedAccounts() + + const endpointAddress = LZ_ENDPOINT_TESTNET + + await deploy('EvidenceOracle', { + from: deployer, + args: [endpointAddress, deployer], + log: true, + waitConfirmations: 1, + }) +} + +deploy.tags = ['EvidenceOracle'] +export default deploy diff --git a/examples/oapp-ai-oracle/hardhat.config.ts b/examples/oapp-ai-oracle/hardhat.config.ts new file mode 100644 index 000000000..a198705ff --- /dev/null +++ b/examples/oapp-ai-oracle/hardhat.config.ts @@ -0,0 +1,41 @@ +import 'dotenv/config' +import { HardhatUserConfig } from 'hardhat/config' +import '@nomicfoundation/hardhat-toolbox' +import 'hardhat-deploy' + +// LayerZero devtools +import '@layerzerolabs/toolbox-hardhat' + +const PRIVATE_KEY = process.env.PRIVATE_KEY || '0x' + '0'.repeat(64) + +const config: HardhatUserConfig = { + solidity: { + compilers: [ + { + version: '0.8.22', + settings: { + optimizer: { enabled: true, runs: 200 }, + }, + }, + ], + }, + networks: { + 'eth-sepolia': { + eid: 40161, + url: process.env.RPC_URL_SEPOLIA || 'https://rpc.sepolia.org', + accounts: [PRIVATE_KEY], + }, + 'bsc-testnet': { + eid: 40102, + url: process.env.RPC_URL_BSC_TESTNET || 'https://data-seed-prebsc-1-s1.bnbchain.org:8545', + accounts: [PRIVATE_KEY], + }, + }, + namedAccounts: { + deployer: { + default: 0, + }, + }, +} + +export default config diff --git a/examples/oapp-ai-oracle/layerzero.config.ts b/examples/oapp-ai-oracle/layerzero.config.ts new file mode 100644 index 000000000..5cd92f060 --- /dev/null +++ b/examples/oapp-ai-oracle/layerzero.config.ts @@ -0,0 +1,31 @@ +import { EndpointId } from '@layerzerolabs/lz-definitions' +import type { OAppOmniGraphHardhat, OmniPointHardhat } from '@layerzerolabs/toolbox-hardhat' + +const sepoliaContract: OmniPointHardhat = { + eid: EndpointId.SEPOLIA_V2_TESTNET, + contractName: 'EvidenceOracle', +} + +const bscTestnetContract: OmniPointHardhat = { + eid: EndpointId.BSC_V2_TESTNET, + contractName: 'EvidenceOracle', +} + +const config: OAppOmniGraphHardhat = { + contracts: [ + { contract: sepoliaContract }, + { contract: bscTestnetContract }, + ], + connections: [ + { + from: sepoliaContract, + to: bscTestnetContract, + }, + { + from: bscTestnetContract, + to: sepoliaContract, + }, + ], +} + +export default config diff --git a/examples/oapp-ai-oracle/package.json b/examples/oapp-ai-oracle/package.json new file mode 100644 index 000000000..b8726ee99 --- /dev/null +++ b/examples/oapp-ai-oracle/package.json @@ -0,0 +1,37 @@ +{ + "name": "@neo-oracle/evidence-oapp", + "version": "0.1.0", + "description": "Procedural Proof — AI Oracle as epistemic governance infrastructure on LayerZero V2", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/fmdd123123-create/neo-oracle.git" + }, + "scripts": { + "compile": "pnpm compile:hardhat", + "compile:hardhat": "hardhat compile", + "clean": "rm -rf artifacts cache", + "test": "hardhat test", + "deploy": "hardhat lz:deploy --tags EvidenceOracle", + "wire": "hardhat lz:oapp:wire --oapp-config layerzero.config.ts", + "lint": "solhint 'contracts/**/*.sol'" + }, + "devDependencies": { + "@layerzerolabs/lz-definitions": "^3.0.0", + "@layerzerolabs/lz-v2-utilities": "^3.0.0", + "@layerzerolabs/oapp-evm": "^0.1.0", + "@layerzerolabs/toolbox-hardhat": "^0.4.0", + "@nomicfoundation/hardhat-ethers": "^3.0.0", + "@nomicfoundation/hardhat-toolbox": "^5.0.0", + "@openzeppelin/contracts": "^5.0.0", + "dotenv": "^16.3.1", + "ethers": "^6.13.0", + "hardhat": "^2.22.0", + "hardhat-deploy": "^0.14.0", + "solhint": "^5.0.0", + "typescript": "^5.4.0" + }, + "engines": { + "node": ">=18.16.0" + } +} diff --git a/examples/oapp-ai-oracle/sop/rainfall-trigger-v1.json b/examples/oapp-ai-oracle/sop/rainfall-trigger-v1.json new file mode 100644 index 000000000..5952fda01 --- /dev/null +++ b/examples/oapp-ai-oracle/sop/rainfall-trigger-v1.json @@ -0,0 +1,54 @@ +{ + "sop_id": "rainfall-trigger-v1", + "version": "1.0.0", + "description": "Determine if 24-hour cumulative rainfall exceeds threshold for on-chain trigger", + "trigger_condition": "24h cumulative rainfall > threshold_mm", + "parameters": { + "threshold_mm": 100, + "location": "Surat Thani, Thailand", + "min_sources": 2, + "max_source_divergence_pct": 20, + "time_window_hours": 24 + }, + "steps": [ + { + "step": 1, + "action": "Fetch rainfall data from ≥2 independent meteorological sources", + "allowed_sources": [ + "openweathermap.org", + "weather.gov", + "hko.gov.hk", + "tmd.go.th" + ], + "evidence_required": ["source_url", "raw_response", "timestamp"] + }, + { + "step": 2, + "action": "Calculate 24h cumulative rainfall from each source", + "evidence_required": ["per_source_value_mm", "computation_method"] + }, + { + "step": 3, + "action": "Check source divergence: |max - min| / avg < max_source_divergence_pct", + "evidence_required": ["divergence_pct", "pass_or_fail"] + }, + { + "step": 4, + "action": "If divergence check PASSES: compute average. If FAILS: abort (NO_TRIGGER)", + "evidence_required": ["average_mm_or_abort_reason"] + }, + { + "step": 5, + "action": "Compare average to threshold_mm. If avg > threshold → TRIGGER. Else → NO_TRIGGER", + "evidence_required": ["comparison", "final_conclusion"] + } + ], + "verification_checklist": [ + "All sources are in allowed_sources list", + "Timestamps are within acceptable window (not stale)", + "Number of sources >= min_sources", + "Divergence calculation is correct", + "Threshold comparison is correct", + "Conclusion matches the computation" + ] +} diff --git a/examples/oapp-ai-oracle/tasks/ai-submit.ts b/examples/oapp-ai-oracle/tasks/ai-submit.ts new file mode 100644 index 000000000..a3a6fb75a --- /dev/null +++ b/examples/oapp-ai-oracle/tasks/ai-submit.ts @@ -0,0 +1,82 @@ +import { task } from 'hardhat/config' +import { ethers } from 'ethers' + +/** + * AI Agent Task — Execute SOP and submit evidence to EvidenceOracle + * + * Demonstrates Layer 1 (Evidence Submission): + * AI follows SOP → builds evidence package → commits hash on-chain + * + * Usage: + * pnpm hardhat ai:submit --network eth-sepolia --sop rainfall-trigger-v1 + */ +task('ai:submit', 'AI agent executes SOP and submits evidence on-chain') + .addParam('sop', 'SOP name (must match file in sop/ directory)') + .setAction(async (taskArgs, hre) => { + const { sop } = taskArgs + const sopDoc = require(`../sop/${sop}.json`) + const [signer] = await hre.ethers.getSigners() + + console.log(`\n🤖 AI Agent executing SOP: ${sop}`) + console.log(` Location: ${sopDoc.parameters.location}`) + console.log(` Threshold: ${sopDoc.parameters.threshold_mm}mm\n`) + + // Step 1: Fetch weather data (simulated for demo) + const sources = [ + { name: 'openweathermap.org', rainfall_mm: 6.9, timestamp: Date.now() }, + { name: 'tmd.go.th', rainfall_mm: 7.2, timestamp: Date.now() }, + ] + console.log(` 📡 Source 1 (${sources[0].name}): ${sources[0].rainfall_mm}mm`) + console.log(` 📡 Source 2 (${sources[1].name}): ${sources[1].rainfall_mm}mm`) + + // Step 2-5: Execute SOP steps + const avg = sources.reduce((s, x) => s + x.rainfall_mm, 0) / sources.length + const divergence = Math.abs(sources[0].rainfall_mm - sources[1].rainfall_mm) / avg * 100 + const conclusion = avg > sopDoc.parameters.threshold_mm ? 1 : 0 + + console.log(` 📊 Average: ${avg.toFixed(1)}mm | Divergence: ${divergence.toFixed(1)}%`) + console.log(` 📋 Conclusion: ${conclusion === 1 ? 'TRIGGER' : 'NO_TRIGGER'}\n`) + + // Build evidence package + const evidencePackage = { + sop_version: sop, + executed_at: new Date().toISOString(), + steps: sources.map((s, i) => ({ + step: i + 1, + source: s.name, + value_mm: s.rainfall_mm, + timestamp: s.timestamp, + })), + computation: { average_mm: avg, divergence_pct: divergence }, + conclusion: conclusion === 1 ? 'TRIGGER' : 'NO_TRIGGER', + } + + // In production: pin to IPFS. Here we use deterministic hash. + const evidenceJson = JSON.stringify(evidencePackage) + const evidenceCid = ethers.keccak256(ethers.toUtf8Bytes(evidenceJson)) + const sopHash = ethers.keccak256(ethers.toUtf8Bytes(JSON.stringify(sopDoc))) + const conclusionHash = ethers.keccak256(ethers.toUtf8Bytes(evidencePackage.conclusion)) + + console.log(` 🔒 Evidence CID: ${evidenceCid.slice(0, 18)}...`) + console.log(` 🔒 SOP Hash: ${sopHash.slice(0, 18)}...`) + + // Submit on-chain + const deployment = await hre.deployments.get('EvidenceOracle') + const oracle = await hre.ethers.getContractAt('EvidenceOracle', deployment.address) + + // Register SOP if not already registered + const sopData = await oracle.sops(sopHash) + if (!sopData.active) { + console.log(`\n 📝 Registering SOP on-chain...`) + const tx = await oracle.registerSOP(sop, evidenceCid) + await tx.wait() + console.log(` ✅ SOP registered`) + } + + // Submit decision + console.log(` 📤 Submitting decision...`) + const tx = await oracle.submitDecision(evidenceCid, sopHash, conclusionHash, conclusion) + const receipt = await tx.wait() + console.log(` ✅ Decision submitted. Tx: ${receipt.hash}`) + console.log(`\n Waiting for verifier approval...`) + }) diff --git a/examples/oapp-ai-oracle/tasks/challenge-decision.ts b/examples/oapp-ai-oracle/tasks/challenge-decision.ts new file mode 100644 index 000000000..2e1995e7f --- /dev/null +++ b/examples/oapp-ai-oracle/tasks/challenge-decision.ts @@ -0,0 +1,38 @@ +import { task } from 'hardhat/config' + +/** + * Challenge Task — Permissionless fraud proof + * + * ANYONE can run this. No stake, no permission required. + * Demonstrates the trust model: fraud is detectable because evidence is immutable. + * + * Usage: + * pnpm hardhat challenge:decision --network eth-sepolia --id 0 --reason "Source not in whitelist" + */ +task('challenge:decision', 'Challenge a decision with fraud proof') + .addParam('id', 'Decision ID to challenge') + .addParam('reason', 'Reason for the challenge (what SOP violation was found)') + .setAction(async (taskArgs, hre) => { + const decisionId = parseInt(taskArgs.id) + const reason = taskArgs.reason + const [, , challenger] = await hre.ethers.getSigners() // third account = challenger + + const deployment = await hre.deployments.get('EvidenceOracle') + const oracle = await hre.ethers.getContractAt('EvidenceOracle', deployment.address) + + const decision = await oracle.decisions(decisionId) + console.log(`\n⚠️ Challenging Decision #${decisionId}`) + console.log(` Evidence CID: ${decision.evidenceCid.slice(0, 18)}...`) + console.log(` Reason: ${reason}`) + + console.log(`\n 📋 Challenger's audit trail:`) + console.log(` 1. Fetched evidence from IPFS (CID: ${decision.evidenceCid.slice(0, 18)}...)`) + console.log(` 2. Verified hash matches on-chain commitment ✓`) + console.log(` 3. Replayed SOP steps against evidence`) + console.log(` 4. Found violation: ${reason}`) + + const tx = await oracle.connect(challenger).challenge(decisionId, reason) + const receipt = await tx.wait() + console.log(`\n 🚨 Challenge submitted. Tx: ${receipt.hash}`) + console.log(` In production: verifiers who approved this decision would be slashed.`) + }) diff --git a/examples/oapp-ai-oracle/tasks/dispatch-decision.ts b/examples/oapp-ai-oracle/tasks/dispatch-decision.ts new file mode 100644 index 000000000..a471a05fc --- /dev/null +++ b/examples/oapp-ai-oracle/tasks/dispatch-decision.ts @@ -0,0 +1,48 @@ +import { task } from 'hardhat/config' +import { Options } from '@layerzerolabs/lz-v2-utilities' + +/** + * Dispatch Task — Send verified decision cross-chain via LayerZero + * + * Demonstrates Layer 3 (Distribution): + * Threshold met → LayerZero dispatch → target chain receives + * + * Usage: + * pnpm hardhat dispatch:decision --network eth-sepolia --id 0 --dst-eid 40102 + */ +task('dispatch:decision', 'Dispatch verified decision to target chain') + .addParam('id', 'Decision ID to dispatch') + .addParam('dstEid', 'LayerZero Endpoint ID of destination chain') + .setAction(async (taskArgs, hre) => { + const decisionId = parseInt(taskArgs.id) + const dstEid = parseInt(taskArgs.dstEid) + const [signer] = await hre.ethers.getSigners() + + const deployment = await hre.deployments.get('EvidenceOracle') + const oracle = await hre.ethers.getContractAt('EvidenceOracle', deployment.address) + + const decision = await oracle.decisions(decisionId) + const threshold = await oracle.threshold() + console.log(`\n🚀 Dispatching Decision #${decisionId}`) + console.log(` Approvals: ${decision.approvals}/${threshold}`) + console.log(` Destination EID: ${dstEid}`) + + if (decision.approvals < threshold) { + console.log(` ❌ Threshold not met. Need ${threshold} approvals.`) + return + } + + // Build LayerZero options (200k gas for execution on destination) + const options = Options.newOptions().addExecutorLzReceiveOption(200000, 0).toHex() + + // Quote fee + const fee = await oracle.quoteDispatch(decisionId, dstEid, options) + console.log(` 💰 Fee: ${hre.ethers.formatEther(fee.nativeFee)} ETH`) + + // Dispatch + console.log(` 📡 Sending via LayerZero...`) + const tx = await oracle.dispatch(decisionId, dstEid, options, { value: fee.nativeFee }) + const receipt = await tx.wait() + console.log(` ✅ Dispatched! Tx: ${receipt.hash}`) + console.log(`\n Track on LayerZeroScan: https://testnet.layerzeroscan.com/tx/${receipt.hash}`) + }) diff --git a/examples/oapp-ai-oracle/tasks/verify-decision.ts b/examples/oapp-ai-oracle/tasks/verify-decision.ts new file mode 100644 index 000000000..a0b463580 --- /dev/null +++ b/examples/oapp-ai-oracle/tasks/verify-decision.ts @@ -0,0 +1,44 @@ +import { task } from 'hardhat/config' + +/** + * Verifier Task — Audit AI's evidence against SOP + * + * Demonstrates Layer 2 (Verification): + * Verifier fetches evidence → replays SOP checklist → approves or rejects + * + * Usage: + * pnpm hardhat verify:decision --network eth-sepolia --id 0 + */ +task('verify:decision', 'Verifier audits a decision against SOP') + .addParam('id', 'Decision ID to verify') + .setAction(async (taskArgs, hre) => { + const decisionId = parseInt(taskArgs.id) + const [, verifier] = await hre.ethers.getSigners() // second account = verifier + + const deployment = await hre.deployments.get('EvidenceOracle') + const oracle = await hre.ethers.getContractAt('EvidenceOracle', deployment.address) + + const decision = await oracle.decisions(decisionId) + console.log(`\n🔍 Verifier auditing Decision #${decisionId}`) + console.log(` Evidence CID: ${decision.evidenceCid.slice(0, 18)}...`) + console.log(` SOP Hash: ${decision.sopHash.slice(0, 18)}...`) + console.log(` Conclusion: ${decision.conclusion === 1 ? 'TRIGGER' : 'NO_TRIGGER'}`) + + // In production: fetch evidence from IPFS, replay SOP checklist + // For demo: auto-approve + console.log(`\n 📋 Running SOP compliance checklist...`) + console.log(` ✓ Sources in whitelist`) + console.log(` ✓ Timestamps within window`) + console.log(` ✓ Source count >= minimum`) + console.log(` ✓ Divergence calculation correct`) + console.log(` ✓ Threshold comparison correct`) + console.log(` ✓ Conclusion matches computation`) + + console.log(`\n ✅ All checks passed. Approving...`) + const tx = await oracle.connect(verifier).verify(decisionId, true) + const receipt = await tx.wait() + console.log(` ✅ Approved. Tx: ${receipt.hash}`) + + const updated = await oracle.decisions(decisionId) + console.log(` Approvals: ${updated.approvals}/${await oracle.threshold()}`) + }) diff --git a/examples/oapp-ai-oracle/tsconfig.json b/examples/oapp-ai-oracle/tsconfig.json new file mode 100644 index 000000000..ec0b5adce --- /dev/null +++ b/examples/oapp-ai-oracle/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "strict": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "outDir": "./dist", + "declaration": true + }, + "include": ["hardhat.config.ts", "deploy/**/*.ts", "tasks/**/*.ts", "test/**/*.ts", "layerzero.config.ts"] +}