diff --git a/.env b/.env new file mode 100644 index 00000000..6755eaab --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +PI_SECRET=SXXXXXXXXXXXXXXXXXXXXXXXX +CONTRACT_ID=TEST123 diff --git a/App.js b/App.js new file mode 100644 index 00000000..85ec68c2 --- /dev/null +++ b/App.js @@ -0,0 +1,73 @@ +import React, { useState } from "react"; +import { connectWallet, signMessage } from "./wallet"; + +function App() { + const [wallet, setWallet] = useState(null); + const [result, setResult] = useState(null); + + const handleConnect = async () => { + const addr = await connectWallet(); + setWallet(addr); + }; + + const sendData = async () => { + if (!wallet) { + alert("Connect wallet first"); + return; + } + + const user = { + attention: Math.random() * 10, + quality: Math.random(), + behavior: Math.random(), + session_time: Math.random() * 10, + interaction_rate: Math.random() * 5, + }; + + const message = JSON.stringify(user); + const signature = await signMessage(message); + + const res = await fetch("http://localhost:8000/process", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + user, + address: wallet, + signature + }), + }); + + const data = await res.json(); + setResult(data); + }; + + return ( +
+

PiRC-AI Wallet Dashboard

+ + {!wallet ? ( + + ) : ( +

Wallet: {wallet}

+ )} + + + + {result && ( +
+

Verification: {result.verification.toFixed(3)}

+

Reward: {result.reward.toFixed(3)}

+

Balance: {result.balance.toFixed(3)}

+
+ )} +
+ ); +} + +export default App; diff --git a/ReadMe.md b/ReadMe.md index 4a9dfa39..5d53201b 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1 +1,59 @@ -See [PiRC1: Pi Ecosystem Token Design](./PiRC1/ReadMe.md) \ No newline at end of file +See [PiRC1: Pi Ecosystem Token Design](./PiRC1/ReadMe.md) + +# PiRC-AI: Attention-Based Token Economy for Pi Network + +PiRC-AI is an extended implementation of the Pi Request for Comment (PiRC), introducing an attention-centered token economic model designed for the AI era. + +As automation reduces the role of traditional labor, this project explores a new paradigm where verified human attention becomes a core economic resource. + +## Core Innovations +- Attention-based reward system (Attention Mining) +- AI-powered human verification (anti-bot & Sybil resistance) +- Token economy with revenue-backed sinks +- Simulation-driven tokenomics validation + +## Vision +Transitioning from a labor-centered economy to an attention-centered economy within the Pi ecosystem. + +## Status +Experimental design + implementation layer (not official Pi Network code) +## Architecture Overview + +PiRC-AI introduces a three-layer attention economy model: + +1. Attention Contribution + Users generate verifiable attention through interaction. + +2. Attention Verification + AI-based systems validate human authenticity and filter bots. + +3. Attention Monetization + Attention is converted into tokenized value backed by real economic sinks (ads, data, AI training). +## Token Model + +The reward mechanism follows a simple but robust formula: + +R = A × Q × V + +Where: +- A = Attention (time/engagement) +- Q = Quality score (interaction depth) +- V = Verification score (human authenticity) + +This ensures fair distribution and resistance to manipulation. +## Simulation + +The project includes simulation tools to evaluate: +- Token emission dynamics +- Network effects +- Inflation control + +Future work includes AI-driven optimization of token distribution. +## Risks & Challenges + +- Sybil attacks (multi-account abuse) +- Fake attention generation +- Centralization of verification AI +- Token inflation without sufficient sinks + +These risks are actively considered in the design. diff --git a/app.js b/app.js new file mode 100644 index 00000000..7f6ae575 --- /dev/null +++ b/app.js @@ -0,0 +1,13 @@ +import express from "express"; +import healthRoute from "./routes/healthRoute.js"; + +const app = express(); +app.use(express.json()); + +app.use("/api", healthRoute); + +const PORT = process.env.PORT || 3000; + +app.listen(PORT, () => { + console.log(`🚀 PiRC AI Oracle running on port ${PORT}`); +}); diff --git a/backend/blockchain/piInvoke.js b/backend/blockchain/piInvoke.js new file mode 100644 index 00000000..15e18c19 --- /dev/null +++ b/backend/blockchain/piInvoke.js @@ -0,0 +1,93 @@ +import { + SorobanRpc, + TransactionBuilder, + Networks, + BASE_FEE, + Keypair +} from "@stellar/stellar-sdk"; + +const server = new SorobanRpc.Server( + "https://rpc.testnet.minepi.com" +); + +// ambil dari env (WAJIB) +const SECRET = process.env.PI_SECRET; +const CONTRACT_ID = process.env.CONTRACT_ID; + +if (!SECRET) { + throw new Error("❌ PI_SECRET belum diset di .env"); +} + +const source = Keypair.fromSecret(SECRET); + +// 🔍 cek koneksi + akun +export async function checkAccount() { + try { + const account = await server.getAccount(source.publicKey()); + + return { + success: true, + publicKey: source.publicKey(), + sequence: account.sequence, + }; + } catch (err) { + return { + success: false, + error: err.message, + }; + } +} + +// 🚀 invoke contract (core function) +export async function invokePi(method, args = []) { + try { + if (!CONTRACT_ID) { + throw new Error("❌ CONTRACT_ID belum diset di .env"); + } + + const account = await server.getAccount(source.publicKey()); + + const tx = new TransactionBuilder(account, { + fee: BASE_FEE, + networkPassphrase: Networks.TESTNET, + }) + .addOperation( + SorobanRpc.Operation.invokeContract({ + contract: CONTRACT_ID, + function: method, + args: args, + }) + ) + .setTimeout(30) + .build(); + + tx.sign(source); + + // 🔍 simulate dulu (WAJIB di Soroban) + const simulation = await server.simulateTransaction(tx); + + if (simulation.error) { + return { + success: false, + stage: "SIMULATION", + error: simulation.error, + }; + } + + // 🚀 kirim transaksi + const send = await server.sendTransaction(tx); + + return { + success: true, + hash: send.hash, + status: send.status, + }; + + } catch (err) { + return { + success: false, + stage: "EXECUTION", + error: err.message, + }; + } +} diff --git a/backend/piClient.js b/backend/piClient.js new file mode 100644 index 00000000..cb4976f2 --- /dev/null +++ b/backend/piClient.js @@ -0,0 +1,43 @@ +const axios = require("axios"); + +const RPC_URL = "https://rpc.testnet.minepi.com"; + +async function callRPC(method, params = {}) { + try { + const response = await axios.post( + RPC_URL, + { + jsonrpc: "2.0", + id: 1, + method, + params + }, + { + headers: { + "Content-Type": "application/json" + } + } + ); + + return response.data; + } catch (error) { + console.error("RPC Error:", error.message); + throw error; + } +} + +// ===== FUNCTIONS ===== + +async function getHealth() { + return callRPC("getHealth"); +} + +async function getLatestLedger() { + const health = await getHealth(); + return health.result.latestLedger; +} + +module.exports = { + getHealth, + getLatestLedger +}; diff --git a/backend/server.js b/backend/server.js new file mode 100644 index 00000000..a9e4d283 --- /dev/null +++ b/backend/server.js @@ -0,0 +1,83 @@ +import express from "express"; +import PiRPC from "../sdk/piRpc.js"; +import AIOracle from "../oracle/aiOracle.js"; + +const app = express(); +const rpc = new PiRPC(); + +app.use(express.json()); + +// HEALTH +app.get("/health", async (req, res) => { + try { + const data = await rpc.getHealth(); + res.json(data); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +// LEDGER +app.get("/ledger", async (req, res) => { + try { + const data = await rpc.getLedger(); + res.json(data); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +// AI INSIGHT +app.get("/oracle/analysis", async (req, res) => { + try { + const data = await AIOracle.analyzeNetwork(); + res.json(data); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +// SIMULASI TX +app.post("/simulate", async (req, res) => { + try { + const { from, to, amount } = req.body; + const result = await AIOracle.simulateTransaction(from, to, amount); + res.json(result); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +// RPC HEALTH +app.get("/rpc/health", async (req, res) => { + try { + const data = await rpc.getHealth(); + res.json(data); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +// RPC NETWORK +app.get("/rpc/network", async (req, res) => { + try { + const data = await rpc.getNetwork(); + res.json(data); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +// RPC VERSION +app.get("/rpc/version", async (req, res) => { + try { + const data = await rpc.getVersionInfo(); + res.json(data); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +app.listen(3000, () => { + console.log("🔥 PiRC API running on http://localhost:3000"); +}); diff --git a/backend/test.js b/backend/test.js new file mode 100644 index 00000000..bbbd32ea --- /dev/null +++ b/backend/test.js @@ -0,0 +1,42 @@ +import fetch from "node-fetch"; + +const RPC_URL = "https://rpc.testnet.minepi.com"; + +async function callRPC(method, params = []) { + try { + const res = await fetch(RPC_URL, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + jsonrpc: "2.0", + id: Date.now(), + method, + params, + }), + }); + + const data = await res.json(); + return data.result; + } catch (err) { + console.error("Error:", err.message); + } +} + +const methods = [ + "getHealth", + "getLatestLedger", + "getVersion" +]; + +async function main() { + for (const m of methods) { + const res = await callRPC(m); + console.log("Method:", m); + console.log("Result:", res); + console.log("------------"); + } +} + +main(); diff --git a/contracts/reward_engine/Cargo.toml b/contracts/reward_engine/Cargo.toml new file mode 100644 index 00000000..d1c18f6c --- /dev/null +++ b/contracts/reward_engine/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "reward_engine" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +soroban-sdk = "20.3.0" diff --git a/contracts/reward_engine/lib.rs b/contracts/reward_engine/lib.rs new file mode 100644 index 00000000..0bf01ef7 --- /dev/null +++ b/contracts/reward_engine/lib.rs @@ -0,0 +1,106 @@ +#![no_std] + +use soroban_sdk::{contract, contractimpl, Env, Symbol, symbol_short, Vec}; + +#[contract] +pub struct RewardEngine; + +#[contractimpl] +impl RewardEngine { + + // ========================= + // Core Reward Calculation + // ========================= + pub fn calculate_reward( + _env: Env, + attention: u64, + quality: u64, + verification: u64, + ) -> u64 { + // Simple multiplication model + attention * quality * verification + } + + // ========================= + // Batch Calculation (optional) + // ========================= + pub fn batch_rewards( + env: Env, + attentions: Vec, + qualities: Vec, + verifications: Vec, + ) -> Vec { + + let mut rewards = Vec::new(&env); + + let len = attentions.len(); + + for i in 0..len { + let a = attentions.get(i).unwrap(); + let q = qualities.get(i).unwrap(); + let v = verifications.get(i).unwrap(); + + let r = a * q * v; + rewards.push_back(r); + } + + rewards + } + + // ========================= + // Normalization (anti inflation) + // ========================= + pub fn normalize_rewards( + env: Env, + rewards: Vec, + max_emission: u64, + ) -> Vec { + + let mut total: u64 = 0; + + for r in rewards.iter() { + total += r; + } + + let mut normalized = Vec::new(&env); + + if total == 0 { + return normalized; + } + + for r in rewards.iter() { + let scaled = (r * max_emission) / total; + normalized.push_back(scaled); + } + + normalized + } + + // ========================= + // Simple Anti-Bot Filter + // ========================= + pub fn apply_verification_threshold( + env: Env, + rewards: Vec, + verifications: Vec, + threshold: u64, + ) -> Vec { + + let mut filtered = Vec::new(&env); + + let len = rewards.len(); + + for i in 0..len { + let r = rewards.get(i).unwrap(); + let v = verifications.get(i).unwrap(); + + if v >= threshold { + filtered.push_back(r); + } else { + filtered.push_back(0); + } + } + + filtered + } +} diff --git a/contracts/reward_engine/src/lib.rs b/contracts/reward_engine/src/lib.rs new file mode 100644 index 00000000..c37aea9c --- /dev/null +++ b/contracts/reward_engine/src/lib.rs @@ -0,0 +1,82 @@ +#![no_std] + +use soroban_sdk::{ + contract, contractimpl, Env, Address, Vec, Map +}; + +#[contract] +pub struct RewardEngine; + +#[contractimpl] +impl RewardEngine { + + // ========================= + // Storage Keys + // ========================= + fn balances(e: &Env) -> Map { + e.storage().instance().get(&"balances").unwrap_or(Map::new(e)) + } + + fn set_balances(e: &Env, map: Map) { + e.storage().instance().set(&"balances", &map); + } + + // ========================= + // Reward Formula + // ========================= + pub fn calculate_reward( + _env: Env, + attention: u64, + quality: u64, + verification: u64, + ) -> u64 { + attention * quality * verification + } + + // ========================= + // Mint Token (core) + // ========================= + pub fn mint( + env: Env, + user: Address, + reward: u64, + ) { + let mut balances = Self::balances(&env); + + let current = balances.get(user.clone()).unwrap_or(0); + balances.set(user, current + reward); + + Self::set_balances(&env, balances); + } + + // ========================= + // Batch Mint + // ========================= + pub fn distribute_rewards( + env: Env, + users: Vec
, + rewards: Vec, + ) { + let mut balances = Self::balances(&env); + + let len = users.len(); + + for i in 0..len { + let user = users.get(i).unwrap(); + let reward = rewards.get(i).unwrap(); + + let current = balances.get(user.clone()).unwrap_or(0); + balances.set(user, current + reward); + } + + Self::set_balances(&env, balances); + } + + // ========================= + // View Balance + // ========================= + pub fn get_balance(env: Env, user: Address) -> u64 { + let balances = Self::balances(&env); + balances.get(user).unwrap_or(0) + } +} diff --git a/dashboard/web-ui/src/App.js b/dashboard/web-ui/src/App.js new file mode 100644 index 00000000..185e9f6e --- /dev/null +++ b/dashboard/web-ui/src/App.js @@ -0,0 +1,48 @@ +import React, { useState } from "react"; + +function App() { + const [result, setResult] = useState(null); + + const sendData = async () => { + const user = { + attention: Math.random() * 10, + quality: Math.random(), + behavior: Math.random(), + session_time: Math.random() * 10, + interaction_rate: Math.random() * 5, + }; + + const res = await fetch("http://localhost:8000/process", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + user: user, + address: "USER_1", + }), + }); + + const data = await res.json(); + setResult(data); + }; + + return ( +
+

PiRC-AI Live Dashboard

+ + + + {result && ( +
+

Verification: {result.verification.toFixed(3)}

+

Reward: {result.reward.toFixed(3)}

+
+ )} +
+ ); +} + +export default App; diff --git a/dashboard/web-ui/src/wallet.js b/dashboard/web-ui/src/wallet.js new file mode 100644 index 00000000..da19d352 --- /dev/null +++ b/dashboard/web-ui/src/wallet.js @@ -0,0 +1,9 @@ +export async function connectWallet() { + if (!window.freighterApi) { + alert("Install Freighter Wallet"); + return null; + } + + const publicKey = await window.freighterApi.getPublicKey(); + return publicKey; +} diff --git a/design/attention-triad.md b/design/attention-triad.md new file mode 100644 index 00000000..12fcf2e7 --- /dev/null +++ b/design/attention-triad.md @@ -0,0 +1,135 @@ +# Attention Triad + +## Overview + +The Attention Triad is a core conceptual framework in PiRC-AI that decomposes the attention economy into three distinct but interconnected layers: + +1. Attention Contribution +2. Attention Verification +3. Attention Monetization + +This separation makes visible the inefficiencies and value gaps in existing digital platforms, where user attention is often captured but not fairly rewarded. + +--- + +## 1. Attention Contribution + +### Definition +Attention Contribution refers to the measurable input of human attention within the ecosystem. + +### Examples +- Time spent viewing content +- User interactions (clicks, likes, comments) +- Cognitive engagement (scroll depth, dwell time) + +### Key Insight +In traditional platforms, users generate attention but do not directly receive proportional economic value. + +In PiRC-AI: +> Attention becomes a measurable and rewardable resource. + +--- + +## 2. Attention Verification + +### Definition +Attention Verification ensures that contributed attention is authentic, human, and meaningful. + +### Purpose +To prevent: +- Bot activity +- Fake engagement +- Sybil attacks (multi-account abuse) + +### Methods +- Behavioral analysis (interaction patterns) +- AI-based anomaly detection +- Reputation systems +- Optional identity/KYC integration + +### Verification Score (V) +Each attention event is assigned a verification score: + +V ∈ [0, 1] + +Where: +- 0 = invalid (bot/fake) +- 1 = fully verified human attention + +--- + +## 3. Attention Monetization + +### Definition +Attention Monetization converts verified attention into economic value. + +### Mechanisms +- Advertising (attention sold to advertisers) +- AI training data (human-labeled interaction) +- Marketplace exposure +- Data insights (aggregated, privacy-aware) + +### Token Integration +Verified attention is rewarded via tokens: + +Reward ∝ Attention × Quality × Verification + +This ensures that only valuable and authentic attention is monetized. + +--- + +## Attention Flow + +The full lifecycle of attention in PiRC-AI: + +User → Attention Contribution → Verification → Monetization → Token Reward + +--- + +## Contribution Gap (Problem Statement) + +In current digital platforms: +- Users contribute attention +- Platforms monetize it +- Users receive little or no direct compensation + +This creates a **value extraction imbalance**. + +PiRC-AI addresses this by: +- Making attention measurable +- Verifying authenticity +- Redistributing value via token economics + +--- + +## Design Principles + +1. Fairness + Users should receive proportional rewards for their attention. + +2. Verifiability + Only real human attention should be rewarded. + +3. Sustainability + Token rewards must be backed by real economic sinks. + +4. Scalability + The system must function across millions of users. + +--- + +## Implications + +The Attention Triad enables: +- A shift from labor-based to attention-based income +- New forms of participation in digital economies +- Alignment between users, platforms, and value creation + +--- + +## Future Work + +- Advanced AI verification models +- Cross-platform attention aggregation +- Privacy-preserving attention tracking +- Integration with decentralized identity systems diff --git a/engine/actionEngine.js b/engine/actionEngine.js new file mode 100644 index 00000000..3367e548 --- /dev/null +++ b/engine/actionEngine.js @@ -0,0 +1,20 @@ +export function takeAction(analysis) { + if (analysis.risk === "HIGH") { + return { + action: "SWITCH_RPC", + message: "Switching to backup node..." + }; + } + + if (analysis.risk === "MEDIUM") { + return { + action: "THROTTLE", + message: "Reducing request rate..." + }; + } + + return { + action: "NORMAL", + message: "All systems stable" + }; +} diff --git a/oracle/aiOracle.js b/oracle/aiOracle.js new file mode 100644 index 00000000..57c82c41 --- /dev/null +++ b/oracle/aiOracle.js @@ -0,0 +1,34 @@ +import PiRPC from "../sdk/piRpc.js"; +import detector from "./anomalyDetector.js"; + +const rpc = new PiRPC(); + +class AIOracle { + + async analyzeNetwork() { + const health = await rpc.getHealth(); + const ledger = await rpc.getLedger(); + + // fake tx counter (kalau RPC belum ada tx endpoint) + const txCount = Math.floor(Math.random() * 100); + + const anomaly = detector.detect({ + ledger: ledger || 0, + txCount + }); + + return { + status: health?.status || "unknown", + ledger, + txCount, + anomaly, + insight: + anomaly.risk === "HIGH" + ? "⚠️ Suspicious network activity detected" + : "✅ Network stable", + timestamp: Date.now() + }; + } +} + +export default new AIOracle(); diff --git a/oracle/ai_oracle_server.py b/oracle/ai_oracle_server.py new file mode 100644 index 00000000..a172ac9a --- /dev/null +++ b/oracle/ai_oracle_server.py @@ -0,0 +1,24 @@ +from fastapi import FastAPI +import joblib +import numpy as np + +app = FastAPI() + +model = joblib.load("simulations/ai_model/ai_verification_model.pkl") + + +@app.post("/verify") +def verify(user: dict): + features = np.array([[ + user["attention"], + user["quality"], + user["behavior"], + user["session_time"], + user["interaction_rate"] + ]]) + + prob = model.predict_proba(features)[0][1] + + return { + "verification_score": float(prob) + } diff --git a/oracle/anomalyDetector.js b/oracle/anomalyDetector.js new file mode 100644 index 00000000..2b29446d --- /dev/null +++ b/oracle/anomalyDetector.js @@ -0,0 +1,64 @@ +class AnomalyDetector { + constructor() { + this.history = []; + this.maxHistory = 50; + } + + // simpan data snapshot + addSample(sample) { + this.history.push(sample); + + if (this.history.length > this.maxHistory) { + this.history.shift(); + } + } + + // hitung rata-rata sederhana + getAverage(key) { + const values = this.history.map(h => h[key] || 0); + const sum = values.reduce((a, b) => a + b, 0); + return values.length ? sum / values.length : 0; + } + + // deteksi anomaly utama + detect(sample) { + this.addSample(sample); + + const ledgerAvg = this.getAverage("ledger"); + const txAvg = this.getAverage("txCount"); + + let risk = "LOW"; + let score = 0; + + // 🔥 RULE 1: spike ledger + if (sample.ledger > ledgerAvg * 1.8) { + risk = "HIGH"; + score += 40; + } + + // 🔥 RULE 2: spike transaksi + if (sample.txCount > txAvg * 2) { + risk = "HIGH"; + score += 40; + } + + // 🔥 RULE 3: empty / freeze pattern + if (sample.ledger === 0) { + risk = "MEDIUM"; + score += 20; + } + + // clamp score + if (score > 100) score = 100; + + return { + risk, + score, + ledgerAvg, + txAvg, + sample + }; + } +} + +export default new AnomalyDetector(); diff --git a/oracle/api.py b/oracle/api.py new file mode 100644 index 00000000..901b80d6 --- /dev/null +++ b/oracle/api.py @@ -0,0 +1,25 @@ +from fastapi import FastAPI +from relayer import process_user + +app = FastAPI() + +@app.post("/process") +def process(data: dict): + user = data["user"] + address = data["address"] + + result = process_user(user, address) + + return result + + +from nacl.signing import VerifyKey +import base64 + +def verify_signature(address, message, signature): + try: + verify_key = VerifyKey(bytes.fromhex(address)) + verify_key.verify(message.encode(), base64.b64decode(signature)) + return True + except: + return False diff --git a/oracle/client.py b/oracle/client.py new file mode 100644 index 00000000..20e89fad --- /dev/null +++ b/oracle/client.py @@ -0,0 +1,20 @@ +import requests + +def get_verification(user): + res = requests.post( + "http://localhost:8000/verify", + json=user + ) + return res.json()["verification_score"] + + +if __name__ == "__main__": + user = { + "attention": 8, + "quality": 0.9, + "behavior": 0.8, + "session_time": 10, + "interaction_rate": 3 + } + + print(get_verification(user)) diff --git a/oracle/relayer.py b/oracle/relayer.py new file mode 100644 index 00000000..a8f7ad87 --- /dev/null +++ b/oracle/relayer.py @@ -0,0 +1,47 @@ +import requests + +# dummy contract interface (sementara) +class ContractClient: + def mint(self, user_address, reward): + print(f"[CONTRACT] Mint {reward} to {user_address}") + +contract = ContractClient() + + +def process_user(user, user_address): + # 1. Call AI Oracle + res = requests.post( + "http://localhost:8000/verify", + json=user + ) + + V = res.json()["verification_score"] + + # 2. Calculate reward + reward = user["attention"] * user["quality"] * V + + # 3. Send to contract + contract.mint(user_address, reward) + + return { + "verification": V, + "reward": reward + } + + +if __name__ == "__main__": + user = { + "attention": 8, + "quality": 0.9, + "behavior": 0.8, + "session_time": 10, + "interaction_rate": 3 + } + + result = process_user(user, "USER_1") + print(result) + balances = {} + +def update_balance(user_address, reward): + balances[user_address] = balances.get(user_address, 0) + reward + return balances[user_address] diff --git a/package.json b/package.json new file mode 100644 index 00000000..63a1b1d1 --- /dev/null +++ b/package.json @@ -0,0 +1,12 @@ +{ + "name": "pirc-core", + "version": "1.0.0", + "type": "module", + "main": "backend/server.js", + "scripts": { + "start": "node backend/server.js" + }, + "dependencies": { + "express": "^4.18.2" + } +} diff --git a/routes/healthRoute.js b/routes/healthRoute.js new file mode 100644 index 00000000..ef3194cf --- /dev/null +++ b/routes/healthRoute.js @@ -0,0 +1,22 @@ +import express from "express"; +import { getPiHealth } from "../services/piHealth.js"; +import { analyzeHealth } from "../oracle/aiOracle.js"; +import { takeAction } from "../engine/actionEngine.js"; + +const router = express.Router(); + +router.get("/health-check", async (req, res) => { + try { + const health = await getPiHealth(); + const analysis = analyzeHealth(health); + const action = takeAction(analysis); + + res.json({ health, analysis, action }); + } catch (err) { + res.status(500).json({ + error: "Failed to fetch health" + }); + } +}); + +export default router; diff --git a/sdk/piRpc.js b/sdk/piRpc.js new file mode 100644 index 00000000..70698013 --- /dev/null +++ b/sdk/piRpc.js @@ -0,0 +1,38 @@ +import fetch from "node-fetch"; + +export default class PiRPC { + constructor(url = "https://rpc.testnet.minepi.com") { + this.url = url; + } + + async call(method, params = []) { + const body = { + jsonrpc: "2.0", + id: 1, + method, + params + }; + + const res = await fetch(this.url, { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(body) + }); + + return await res.json(); + } + + getNetwork() { + return this.call("getNetwork"); + } + + getHealth() { + return this.call("getHealth"); + } + + getVersionInfo() { + return this.call("getVersionInfo"); + } +} diff --git a/server.js b/server.js new file mode 100644 index 00000000..428e3e52 --- /dev/null +++ b/server.js @@ -0,0 +1,14 @@ +import express from "express"; + +const app = express(); +const PORT = 3000; + +app.use(express.json()); + +app.get("/", (req, res) => { + res.send("Backend aktif 🚀"); +}); + +app.listen(PORT, () => { + console.log("Server jalan di port " + PORT); +}); diff --git a/services/piHealth.js b/services/piHealth.js new file mode 100644 index 00000000..2206332a --- /dev/null +++ b/services/piHealth.js @@ -0,0 +1,18 @@ +import fetch from "node-fetch"; + +export async function getPiHealth() { + const res = await fetch("https://rpc.testnet.minepi.com", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + jsonrpc: "2.0", + id: 1, + method: "getHealth" + }) + }); + + const data = await res.json(); + return data.result || data; +} diff --git a/simulations/ai_model/features.py b/simulations/ai_model/features.py new file mode 100644 index 00000000..9363f2e8 --- /dev/null +++ b/simulations/ai_model/features.py @@ -0,0 +1,8 @@ +def extract_features(user): + return [ + user["attention"], + user["quality"], + user["behavior"], + user["session_time"], + user["interaction_rate"], + ] diff --git a/simulations/ai_model/inference.py b/simulations/ai_model/inference.py new file mode 100644 index 00000000..405e2a5e --- /dev/null +++ b/simulations/ai_model/inference.py @@ -0,0 +1,17 @@ +import joblib +import numpy as np + +model = joblib.load("ai_verification_model.pkl") + +def predict_verification(user): + features = np.array([[ + user["attention"], + user["quality"], + user["behavior"], + user["session_time"], + user["interaction_rate"] + ]]) + + prob = model.predict_proba(features)[0][1] # probability human + + return prob diff --git a/simulations/ai_model/train_model.py b/simulations/ai_model/train_model.py new file mode 100644 index 00000000..d81f93ac --- /dev/null +++ b/simulations/ai_model/train_model.py @@ -0,0 +1,51 @@ +import random +import numpy as np +from sklearn.ensemble import RandomForestClassifier +import joblib + +def generate_dataset(n=5000): + X = [] + y = [] + + for _ in range(n): + is_bot = random.random() < 0.3 + + if is_bot: + attention = random.uniform(8, 15) + quality = random.uniform(0.1, 0.5) + behavior = random.uniform(0.1, 0.3) + session_time = random.uniform(1, 3) + interaction_rate = random.uniform(5, 10) + else: + attention = random.uniform(2, 10) + quality = random.uniform(0.6, 1.2) + behavior = random.uniform(0.6, 1.0) + session_time = random.uniform(5, 15) + interaction_rate = random.uniform(1, 5) + + X.append([ + attention, + quality, + behavior, + session_time, + interaction_rate + ]) + + y.append(0 if is_bot else 1) + + return np.array(X), np.array(y) + + +def train(): + X, y = generate_dataset() + + model = RandomForestClassifier(n_estimators=100) + model.fit(X, y) + + joblib.dump(model, "ai_verification_model.pkl") + + print("Model trained & saved!") + + +if __name__ == "__main__": + train() diff --git a/simulations/ai_verification.py b/simulations/ai_verification.py new file mode 100644 index 00000000..5adcbeaf --- /dev/null +++ b/simulations/ai_verification.py @@ -0,0 +1,44 @@ +import random + +def ai_verification_score(attention, quality, behavior_score): + """ + Simulate AI-based human verification + """ + + # heuristic scoring + score = ( + 0.4 * (attention / 10) + + 0.4 * quality + + 0.2 * behavior_score + ) + + # clamp 0–1 + return max(0, min(1, score)) + + +def detect_bot_pattern(attention, quality): + if attention > 12 and quality < 0.4: + return True + return False + + +def generate_user(): + is_bot = random.random() < 0.3 + + if is_bot: + attention = random.uniform(8, 15) + quality = random.uniform(0.1, 0.5) + behavior = random.uniform(0.1, 0.3) + else: + attention = random.uniform(2, 10) + quality = random.uniform(0.6, 1.2) + behavior = random.uniform(0.6, 1.0) + + verification = ai_verification_score(attention, quality, behavior) + + return { + "attention": attention, + "quality": quality, + "verification": verification, + "is_bot": is_bot + } diff --git a/simulations/attention_model.py b/simulations/attention_model.py new file mode 100644 index 00000000..6af12edd --- /dev/null +++ b/simulations/attention_model.py @@ -0,0 +1,102 @@ +# simulations/attention_model.py + +import random +import statistics + +class User: + def __init__(self, is_bot=False): + self.is_bot = is_bot + + def generate_attention(self): + if self.is_bot: + # bot: high activity but low quality + A = random.uniform(5, 15) + Q = random.uniform(0.1, 0.5) + V = random.uniform(0.0, 0.3) + else: + # human: moderate activity, higher quality + A = random.uniform(2, 10) + Q = random.uniform(0.5, 1.5) + V = random.uniform(0.7, 1.0) + + return A, Q, V + + +class AttentionEconomy: + def __init__(self, users=1000, bot_ratio=0.2, max_daily_emission=10000): + self.users = users + self.bot_ratio = bot_ratio + self.max_daily_emission = max_daily_emission + self.population = self._create_population() + + def _create_population(self): + population = [] + for _ in range(self.users): + if random.random() < self.bot_ratio: + population.append(User(is_bot=True)) + else: + population.append(User(is_bot=False)) + return population + + def calculate_reward(self, A, Q, V): + return A * Q * V + + def run_epoch(self): + rewards = [] + raw_rewards = [] + + for user in self.population: + A, Q, V = user.generate_attention() + r = self.calculate_reward(A, Q, V) + raw_rewards.append(r) + + total_raw = sum(raw_rewards) + + # normalize to max emission (anti-inflation control) + if total_raw > 0: + scale = self.max_daily_emission / total_raw + else: + scale = 0 + + for r in raw_rewards: + rewards.append(r * scale) + + return rewards + + def simulate(self, days=30): + history = [] + + for day in range(days): + rewards = self.run_epoch() + total = sum(rewards) + avg = statistics.mean(rewards) + max_r = max(rewards) + + history.append({ + "day": day + 1, + "total_emission": total, + "avg_reward": avg, + "max_reward": max_r + }) + + return history + + +if __name__ == "__main__": + sim = AttentionEconomy( + users=1000, + bot_ratio=0.3, # coba ubah untuk test ketahanan sistem + max_daily_emission=10000 + ) + + results = sim.simulate(days=30) + + print("=== Simulation Results ===\n") + + for day in results: + print( + f"Day {day['day']}: " + f"Total={day['total_emission']:.2f}, " + f"Avg={day['avg_reward']:.4f}, " + f"Max={day['max_reward']:.2f}" + ) diff --git a/simulations/full_pipeline.py b/simulations/full_pipeline.py new file mode 100644 index 00000000..d8a20f48 --- /dev/null +++ b/simulations/full_pipeline.py @@ -0,0 +1,27 @@ +from attention_model import AttentionEconomy +from ai_verification import generate_user + +def run_full_simulation(users=1000): + rewards = [] + + for _ in range(users): + u = generate_user() + + reward = ( + u["attention"] * + u["quality"] * + u["verification"] + ) + + rewards.append(reward) + + total = sum(rewards) + + print("Total Rewards:", total) + print("Avg Reward:", total / users) + + return rewards + + +if __name__ == "__main__": + run_full_simulation() diff --git a/simulations/full_pipeline_ai.py b/simulations/full_pipeline_ai.py new file mode 100644 index 00000000..774992d3 --- /dev/null +++ b/simulations/full_pipeline_ai.py @@ -0,0 +1,34 @@ +from ai_model.inference import predict_verification +import random + +def generate_user(): + return { + "attention": random.uniform(1, 15), + "quality": random.uniform(0.1, 1.2), + "behavior": random.uniform(0.1, 1.0), + "session_time": random.uniform(1, 15), + "interaction_rate": random.uniform(1, 10), + } + +def run_simulation(n=1000): + rewards = [] + + for _ in range(n): + user = generate_user() + + V = predict_verification(user) + + reward = ( + user["attention"] * + user["quality"] * + V + ) + + rewards.append(reward) + + print("Total:", sum(rewards)) + print("Avg:", sum(rewards) / n) + + +if __name__ == "__main__": + run_simulation()