Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Base image
FROM node:18-alpine

# Set working directory
WORKDIR /app

# Copy files
COPY package*.json ./
RUN npm install

COPY . .

# Expose port
EXPOSE 3000

# Start app
CMD ["npm", "start"]
57 changes: 56 additions & 1 deletion ReadMe.md
Original file line number Diff line number Diff line change
@@ -1 +1,56 @@
See [PiRC1: Pi Ecosystem Token Design](./PiRC1/ReadMe.md)
See [PiRC1: Pi Ecosystem Token Design](./PiRC1/ReadMe.md)


πŸš€ PiRC AI Oracle – Health Monitoring Engine

Overview

PiRC AI Oracle is an intelligent monitoring system for Pi Testnet RPC nodes.
It analyzes network health and automatically determines risk levels and system actions.

Features

- πŸ” Real-time RPC health monitoring ("getHealth")
- 🧠 AI-based risk analysis engine
- ⚑ Automated decision system (failover, throttling)
- 🌐 REST API integration

Architecture

Pi RPC β†’ AI Oracle β†’ Risk Engine β†’ Action Engine β†’ API

API Endpoint

GET /api/health-check

Example Response

{
"health": {
"status": "degraded",
"txCount": 1200,
"ledger": 9500
},
"analysis": {
"risk": "HIGH",
"score": 80,
"insight": "⚠️ Network anomaly detected"
},
"action": {
"action": "SWITCH_RPC",
"message": "Switching to backup node..."
}
}

Installation

npm install
npm start

Vision

To build a self-adaptive AI-powered monitoring layer for decentralized infrastructure.

---

πŸ”₯ Built for Pi Network ecosystem developers
40 changes: 40 additions & 0 deletions engine/actionEngine.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { sendTelegramAlert } from "../alerts/telegramAlert.js";
import { switchRPC } from "../services/rpcManager.js";
import { triggerProtection } from "../blockchain/contractTrigger.js";

export async function takeAction(analysis) {
// HIGH RISK
if (analysis.risk === "HIGH") {
// kirim alert
await sendTelegramAlert(
` *Pi Network Alert*\nRisk: HIGH\nScore: ${analysis.score}\n${analysis.insight}`
);

// switch RPC
const newRpc = switchRPC();

// trigger protection (optional)
await triggerProtection("PAUSE");

return {
action: "EMERGENCY_MODE",
rpc: newRpc,
protection: "ENABLED",
message: "High risk detected: RPC switched & protection activated"
};
}

// MEDIUM RISK
if (analysis.risk === "MEDIUM") {
return {
action: "THROTTLE",
message: "Reducing request rate..."
};
}

// SAFE
return {
action: "NORMAL",
message: "All systems stable"
};
}
19 changes: 19 additions & 0 deletions k8s/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: pirc-ai-oracle
spec:
replicas: 2
selector:
matchLabels:
app: pirc-ai-oracle
template:
metadata:
labels:
app: pirc-ai-oracle
spec:
containers:
- name: pirc-ai-oracle
image: pirc-ai-oracle:latest
ports:
- containerPort: 3000
12 changes: 12 additions & 0 deletions k8s/service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: pirc-service
spec:
type: NodePort
selector:
app: pirc-ai-oracle
ports:
- port: 80
targetPort: 3000
nodePort: 30007
28 changes: 28 additions & 0 deletions oracle/aiOracle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
export function analyzeHealth(health) {
let riskScore = 0;
let status = "SAFE";

if (health.status !== "healthy") riskScore += 40;
if (health.txCount > 1000) riskScore += 30;
if (health.ledger < 10000) riskScore += 20;

if (riskScore > 70) status = "HIGH";
else if (riskScore > 40) status = "MEDIUM";

return {
risk: status,
score: riskScore,
insight: generateInsight(status)
};
}

function generateInsight(status) {
switch (status) {
case "HIGH":
return "⚠️ Network anomaly detected";
case "MEDIUM":
return "⚑ Moderate load detected";
default:
return "βœ… Network stable";
}
}
14 changes: 14 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "pirc-ai-oracle",
"version": "1.0.0",
"description": "AI-powered health monitoring for Pi Testnet RPC",
"main": "src/app.js",
"type": "module",
"scripts": {
"start": "node src/app.js"
},
"dependencies": {
"express": "^4.18.2",
"node-fetch": "^3.3.2"
}
}
22 changes: 22 additions & 0 deletions routes/healthRoute.js
Original file line number Diff line number Diff line change
@@ -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;
20 changes: 20 additions & 0 deletions services/piHealth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import fetch from "node-fetch";
import { getCurrentRPC } from "./rpcManager.js";

export async function getPiHealth() {
const rpc = getCurrentRPC();

const res = await fetch(rpc, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "getHealth"
})
});

return res.json();
}
22 changes: 22 additions & 0 deletions src/alerts/telegramAlert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import fetch from "node-fetch";

const BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
const CHAT_ID = process.env.TELEGRAM_CHAT_ID;

export async function sendTelegramAlert(message) {
if (!BOT_TOKEN || !CHAT_ID) return;

const url = `https://api.telegram.org/bot${BOT_TOKEN}/sendMessage`;

await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
chat_id: CHAT_ID,
text: message,
parse_mode: "Markdown"
})
});
}
38 changes: 38 additions & 0 deletions src/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import express from "express";
import { getPiHealth } from "./services/piHealth.js";
import { analyzeHealth } from "./oracle/aiOracle.js";
import { takeAction } from "./engine/actionEngine.js";

// import metrics
import metricsRoute, { updateMetrics } from "./routes/metrics.js";

const app = express();

app.use(express.json());

// register endpoint metrics
app.use("/metrics", metricsRoute);

// endpoint utama
app.get("/health-check", async (req, res) => {
try {
const health = await getPiHealth();
const analysis = analyzeHealth(health);
const action = await takeAction(analysis);

// update metrics DI SINI (bukan di luar)
updateMetrics({ health, analysis, action });

res.json({
health,
analysis,
action
});
} catch (err) {
res.status(500).json({
error: "Failed to fetch health"
});
}
});

export default app;
10 changes: 10 additions & 0 deletions src/blockchain/contractTrigger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { triggerProtection } from "../blockchain/contractTrigger.js";

if (analysis.risk === "HIGH") {
await triggerProtection("PAUSE");

return {
action: "PAUSE_NETWORK",
message: "Smart contract paused"
};
}
9 changes: 9 additions & 0 deletions src/blockchain/oracleBridge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export async function sendToBlockchain(data) {
console.log("πŸ“‘ Sending AI result to blockchain...");

// pseudo-call
return {
txHash: "0xORACLE123",
data
};
}
8 changes: 8 additions & 0 deletions src/blockchain/piContract.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export async function pauseNetwork() {
console.log("β›” Pausing network via smart contract...");

return {
txHash: "0xPAUSE",
status: "success"
};
}
5 changes: 5 additions & 0 deletions src/config/rpcList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const RPC_ENDPOINTS = [
"https://rpc.testnet.minepi.com",
"https://rpc-backup1.minepi.com",
"https://rpc-backup2.minepi.com"
];
23 changes: 23 additions & 0 deletions src/ml/anomalyModel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as tf from "@tensorflow/tfjs";

let model;

export async function loadModel() {
model = tf.sequential();
model.add(tf.layers.dense({ units: 8, inputShape: [2], activation: "relu" }));
model.add(tf.layers.dense({ units: 1, activation: "sigmoid" }));

model.compile({
optimizer: "adam",
loss: "binaryCrossentropy"
});
}

export function predictAnomaly(txCount, ledger) {
if (!model) return 0;

const input = tf.tensor2d([[txCount, ledger]]);
const output = model.predict(input);

return output.dataSync()[0];
}
15 changes: 15 additions & 0 deletions src/routes/metrics.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import express from "express";

const router = express.Router();

let latestData = {};

export function updateMetrics(data) {
latestData = data;
}

router.get("/metrics", (req, res) => {
res.json(latestData);
});

export default router;
7 changes: 7 additions & 0 deletions src/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import app from "./app.js";

const PORT = process.env.PORT || 3000;

app.listen(PORT, () => {
console.log(`πŸš€ PiRC AI Oracle running on port ${PORT}`);
});
12 changes: 12 additions & 0 deletions src/services/rpcManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { RPC_ENDPOINTS } from "../config/rpcList.js";

let currentIndex = 0;

export function getCurrentRPC() {
return RPC_ENDPOINTS[currentIndex];
}

export function switchRPC() {
currentIndex = (currentIndex + 1) % RPC_ENDPOINTS.length;
return getCurrentRPC();
}