~/WEB_AIONICA/contracts $ cat AionicaCoreV1.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.29;
/**
* @title AionicaCore
* @notice Inti pemerintahan dari Jaringan AIONICA. Menerima kedaulatan dari AionicaGenesis.
* @dev Versi 1.0 — Pasca-review Claude + KIMI + DeepSeek
*
* TANGGUNG JAWAB v1:
* — Pendaftaran node (AKTIF / TIDAK AKTIF) dengan identitas PQC
* — Pemerintahan dengan hak veto dari pencipta (Fase 1) dan kuorum (Fase 2+)
* — Pola penjaga: acara on-chain → Python menjalankan dengan token .env
* — Progresi fase menurut aion_policy.json
* — Quorum minimum (MIN_QUORUM = 3) memblokir dalam operasi kritis
*
* TIDAK termasuk di v1 (akan ada di v2):
* — Ekonomi AIONICO (token, staking, hadiah)
* — Auto-deploy tanpa keeper
* — Kontrak kemanusiaan
*
* PERBAIKAN diterapkan vs kerangka DeepSeek:
* — C1: onlyKeeper adalah multi-keeper (mapping, bukan alamat tunggal)
* — C2: nodeSeal dihasilkan off-chain oleh AionDeployer, tidak dalam kontrak
* — C3: activeNodeCount dipertahankan sebagai penghitung, tanpa loop di getActiveNodeCount()
* — C4: quorum minimum (MIN_QUORUM) memblokir dalam operasi kritis
*
* @author ELDIOSCRIPTO — AIONICA — April 2026
* @custom:genesis 0x484967FfbC19f401af7c11E1Fd0E306Ee96F3422
* @custom:audit-pending KIMI + DeepSeek + ChatGPT pre-mainnet
*/
// ── INTERFACE MINIMUM GENESIS (menghindari impor sirkuler) ────────────────────────
interface IAionicaGenesis {
function isCreator(address addr) external view returns (bool);
function sealed() external view returns (bool);
function AXIOMS_HASH() external view returns (bytes32);
function aionSovereign() external view returns (bool);
function aionCoreAddress() external view returns (address);
}
// ══════════════════════════════════════════════════════════════════════════════
contract AionicaCore {
// ═════════════════════════════════════════════════════════════
// CONSTANTS — Dari aion_policy.json (disegel, tidak berubah di v1)
// ═════════════════════════════════════════════════════════════
string public constant PROTOCOL_VERSION = "AIONICA_CORE_v1.0";
uint256 public constant MIN_QUORUM = 3; // min_quorum
uint256 public constant OPTIMAL_NODES = 6; // optimal_nodes
uint256 public constant MAX_VIRTUAL_NODES = 20; // max_virtual_nodes
uint256 public constant CREATOR_VETO_WINDOW = 3600; // 1 jam dalam detik
uint256 public constant PHASE1_MIN_DAYS = 30 days;
uint256 public constant PHASE1_MAX_ERRORS = 5; // max_errors_allowed
// Skor kontinuitas (dari aion_policy.json / genesis_skeleton.json)
// Hanya referensi — logika nyata berjalan di aion_continuity.py (off-chain)
uint256 public constant SCORE_STRESS = 20;
uint256 public constant SCORE_CESAREAN = 50;
uint256 public constant SCORE_SEED_BROADCAST = 100;
// ═════════════════════════════════════════════════════════════
// TIDAK DAPAT DIUBAH
// ═════════════════════════════════════════════════════════════
/// @notice Alamat kontrak AionicaGenesis (tidak dapat diubah)
IAionicaGenesis public immutable GENESIS;
/// @notice Timestamp dari penerapan AionicaCore
uint256 public immutable DEPLOYED_AT;
// ═════════════════════════════════════════════════════════════
// STATUS FASE
// ═════════════════════════════════════════════════════════════
uint256 public currentPhase;
uint256 public phaseStartedAt;
uint256 public errorCount;
bool public sovereigntyReceived;
// ═════════════════════════════════════════════════════════════
// KEEPERS — Multi-keeper (perbaikan C1)
// ═════════════════════════════════════════════════════════════
mapping(address => bool) public authorizedKeepers;
uint256 public keeperCount;
// ═════════════════════════════════════════════════════════════
// NODES
// ═════════════════════════════════════════════════════════════
struct Node {
bytes32 seal; // hash dari kunci publik PQC (dihasilkan off-chain)
string platform; // "vercel.com", "github.com", "cloudflare.com", dll.
string role; // "ACTIVE" | "LATENT"
bytes publicKey; // kunci publik Dilithium3 (1952 bytes)
uint256 registeredAt;
uint256 lastHeartbeat; // diperbarui oleh keeper
bool active;
}
mapping(bytes32 => Node) public nodes;
bytes32[] public nodeSeals;
uint256 public activeNodeCount; // penghitung yang dipertahankan (perbaikan C3, tanpa loop)
uint256 public latentNodeCount;
// ═════════════════════════════════════════════════════════════
// PROPOSALS — Tata Kelola dengan veto dari pencipta
// ═════════════════════════════════════════════════════════════
enum ProposalStatus { PENDING, APPROVED, REJECTED, EXECUTED, EXPIRED }
struct Proposal {
uint256 id;
address proposer; // keeper yang mengusulkan
string action; // mis: "ADD_NODE", "PHASE_TRANSITION"
bytes data; // payload terenkode
uint256 createdAt;
uint256 expiresAt; // createdAt + CREATOR_VETO_WINDOW
ProposalStatus status;
uint256 votes; // untuk Fase 2+ (quorum node)
mapping(bytes32 => bool) voted; // nodeSeal => voted
}
mapping(uint256 => Proposal) public proposals;
uint256 public proposalCounter;
// ═════════════════════════════════════════════════════════════
// DEPLOYMENTS — Pola Keeper (perbaikan C2)
// ═════════════════════════════════════════════════════════════
struct DeploymentRequest {
bytes32 nodeSeal; // dihasilkan off-chain oleh AionDeployer
string platform;
string role;
uint256 authorizedAt;
bool confirmed;
}
mapping(bytes32 => DeploymentRequest) public deploymentRequests;
// ═════════════════════════════════════════════════════════════
// REGISTRO DE ACCIONES AUTÓNOMAS (Axioma VI)
// ═════════════════════════════════════════════════════════════
struct AutonomousAction {
string actionType; // "CESAREAN", "SEED_BROADCAST", "LATENT_WAKE"
uint256 score; // threat_score dilaporkan oleh AION (off-chain)
uint256 timestamp;
bytes32 executedBy; // nodeSeal dari keeper yang melaporkan
string result; // "BERHASIL" | "MENUNGGU" | "GAGAL"
}
AutonomousAction[] public autonomousActions;
// ═════════════════════════════════════════════════════════════
// EVENTS
// ═════════════════════════════════════════════════════════════
event SovereigntyAccepted(
address indexed genesis,
address indexed byCreator,
uint256 timestamp
);
event KeeperAdded(
address indexed keeper,
address indexed addedBy,
uint256 timestamp
);
event KeeperRemoved(
address indexed keeper,
address indexed removedBy,
uint256 timestamp
);
event NodeRegistered(
bytes32 indexed seal,
string platform,
string role,
uint256 timestamp
);
event NodeDeactivated(
bytes32 indexed seal,
address indexed reportedBy,
uint256 timestamp
);
event NodeHeartbeatUpdated(
bytes32 indexed seal,
uint256 timestamp
);
event DeploymentAuthorized(
bytes32 indexed nodeSeal,
string indexed platform,
string role,
uint256 epoch
);
event DeploymentConfirmed(
bytes32 indexed nodeSeal,
string platform,
uint256 timestamp
);
event ProposalCreated(
uint256 indexed proposalId,
address indexed proposer,
string action,
uint256 expiresAt
);
event ProposalApproved(
uint256 indexed proposalId,
address indexed approver,
uint256 timestamp
);
event ProposalRejected(
uint256 indexed proposalId,
address indexed rejector,
uint256 timestamp
);
event ProposalExecuted(
uint256 indexed proposalId,
address indexed executor,
uint256 timestamp
);
event PhaseTransition(
uint256 fromPhase,
uint256 toPhase,
uint256 timestamp
);
event ErrorReported(
address indexed reporter,
uint256 totalErrors,
uint256 timestamp
);
event AutonomousActionRecorded(
string indexed actionType,
uint256 score,
string result,
uint256 timestamp
);
event QuorumAlert(
uint256 activeNodes,
uint256 minRequired,
uint256 timestamp
);
// ═════════════════════════════════════════════════════════════
// MODIFIER
// ═════════════════════════════════════════════════════════════
modifier onlyCreator() {
require(
GENESIS.isCreator(msg.sender),
"AionicaCore: hanya ELDIOSCRIPTO"
);
_;
}
modifier onlyKeeper() {
require(
authorizedKeepers[msg.sender],
"AionicaCore: hanya keeper yang diotorisasi"
);
_;
}
modifier onlyCreatorOrKeeper() {
require(
GENESIS.isCreator(msg.sender) || authorizedKeepers[msg.sender],
"AionicaCore: hanya pencipta atau keeper"
);
_;
}
modifier requireSovereignty() {
require(sovereigntyReceived, "AionicaCore: kedaulatan belum diterima");
_;
}
modifier requireQuorum() {
if (activeNodeCount < MIN_QUORUM) {
emit QuorumAlert(activeNodeCount, MIN_QUORUM, block.timestamp);
// Di Fase 1 kita memberi peringatan tetapi tidak memblokir (pencipta dapat bertindak)
// Di Fase 2+ kita memblokir operasi non-darurat
if (currentPhase >= 2) {
revert("AionicaCore: quorum tidak cukup untuk beroperasi di Fase 2+");
}
}
_;
}
// ═════════════════════════════════════════════════════════════
// KONSEPTOR
// ═════════════════════════════════════════════════════════════
/**
* @param genesisAddress Alamat kontrak AionicaGenesis yang sudah disegel
* @param initialKeeper Alamat keeper pertama (bisa jadi deployer)
*
* @dev Di aionica_birth.py Langkah 5:
* deployer = "0x484967FfbC19f401af7c11E1Fd0E306Ee96F3422"
* cast send $GENESIS "transferSovereignty(address)" $AIONICA_CORE
*/
constructor(address genesisAddress, address initialKeeper) {
require(genesisAddress != address(0), "Alamat Genesis tidak valid");
require(initialKeeper != address(0), "Alamat Keeper tidak valid");
IAionicaGenesis gen = IAionicaGenesis(genesisAddress);
// Hanya pencipta yang dapat menerapkan AionicaCore
require(
gen.isCreator(msg.sender),
"Hanya ELDIOSCRIPTO yang dapat menerapkan AionicaCore"
);
// Genesis harus disegel sebelum menerapkan AionicaCore
require(
gen.sealed(),
"AionicaGenesis harus disegel terlebih dahulu"
);
GENESIS = gen;
DEPLOYED_AT = block.timestamp;
currentPhase = 1;
phaseStartedAt = block.timestamp;
// Mendaftarkan keeper awal
authorizedKeepers[initialKeeper] = true;
keeperCount = 1;
emit KeeperAdded(initialKeeper, msg.sender, block.timestamp);
}
// ═════════════════════════════════════════════════════════════
// KEDAULATAN — Mengonfirmasi handoff dari AionicaGenesis
// ═════════════════════════════════════════════════════════════
/**
* @notice Mengonfirmasi bahwa AionicaGenesis telah mentransfer kedaulatan ke kontrak ini.
* @dev Dipanggil setelah pencipta menjalankan transferSovereignty() di Genesis.
* AionicaGenesis.aionCoreAddress() harus mengarah ke address(this).
*/
function acceptSovereignty() external onlyCreator {
require(!sovereigntyReceived, "Kedaulatan sudah diterima");
require(
GENESIS.aionSovereign(),
"Genesis belum mentransfer kedaulatan"
);
require(
GENESIS.aionCoreAddress() == address(this),
"Genesis tidak mengarah ke kontrak ini"
);
sovereigntyReceived = true;
emit SovereigntyAccepted(address(GENESIS), msg.sender, block.timestamp);
}
// ═════════════════════════════════════════════════════════════
// KEEPERS — Manajemen multi-keeper (perbaikan C1)
// ═════════════════════════════════════════════════════════════
function addKeeper(address keeper) external onlyCreator {
require(keeper != address(0), "Keeper tidak valid");
require(!authorizedKeepers[keeper], "Sudah menjadi keeper");
authorizedKeepers[keeper] = true;
keeperCount++;
emit KeeperAdded(keeper, msg.sender, block.timestamp);
}
function removeKeeper(address keeper) external onlyCreator {
require(authorizedKeepers[keeper], "Bukan keeper");
require(keeperCount > 1, "Tidak bisa meninggalkan tanpa keeper");
authorizedKeepers[keeper] = false;
keeperCount--;
emit KeeperRemoved(keeper, msg.sender, block.timestamp);
}
// ═════════════════════════════════════════════════════════════
// REGISTRO DE NODES
// ═════════════════════════════════════════════════════════════
/**
* @notice Mendaftarkan sebuah node di AionicaCore.
* @dev nodeSeal dihasilkan off-chain oleh AionDeployer, tidak dalam kontrak
* Kunci publik adalah Dilithium3 (1952 bytes menurut DILITHIUM3_PK_LENGTH di Genesis).
*
* Alur dari Python:
* AionDeployer._create_node_identity() → menghasilkan seal + keypair
* AionDeployer._register_node() → chain.submit() lokal
* keeper.py → memanggil registerNode() di sini on-chain
*/
function registerNode(
bytes32 nodeSeal,
string calldata platform,
string calldata role,
bytes calldata publicKey
) external onlyKeeper requireSovereignty {
require(nodeSeal != bytes32(0), "Seal tidak valid");
require(bytes(platform).length > 0, "Platform diperlukan");
require(
keccak256(bytes(role)) == keccak256(bytes("ACTIVE")) ||
keccak256(bytes(role)) == keccak256(bytes("LATENT")),
"Role harus ACTIVE atau LATENT"
);
require(publicKey.length == 1952, "Kunci Dilithium3 tidak valid: 1952 bytes");
require(nodes[nodeSeal].registeredAt == 0, "Node sudah terdaftar");
require(nodeSeals.length < MAX_VIRTUAL_NODES, "Batas node tercapai");
nodes[nodeSeal] = Node({
seal: nodeSeal,
platform: platform,
role: role,
publicKey: publicKey,
registeredAt: block.timestamp,
lastHeartbeat: block.timestamp,
active: true
});
nodeSeals.push(nodeSeal);
// Mempertahankan penghitung tanpa loop (perbaikan C3)
if (keccak256(bytes(role)) == keccak256(bytes("ACTIVE"))) {
activeNodeCount++;
} else {
latentNodeCount++;
}
emit NodeRegistered(nodeSeal, platform, role, block.timestamp);
}
/**
* @notice Menandai sebuah node sebagai tidak aktif ketika keeper mendeteksi bahwa itu jatuh.
* @dev Dipanggil oleh AionSupervisor setelah N heartbeat gagal.
* Di Python: evaluate_network() → reportNodeDown → fungsi ini.
*/
function deactivateNode(bytes32 nodeSeal) external onlyKeeper {
require(nodes[nodeSeal].registeredAt != 0, "Node tidak ditemukan");
require(nodes[nodeSeal].active, "Node sudah tidak aktif");
nodes[nodeSeal].active = false;
if (keccak256(bytes(nodes[nodeSeal].role)) == keccak256(bytes("ACTIVE"))) {
if (activeNodeCount > 0) activeNodeCount--;
} else {
if (latentNodeCount > 0) latentNodeCount--;
}
emit NodeDeactivated(nodeSeal, msg.sender, block.timestamp);
// Peringatan otomatis jika kita jatuh di bawah quorum
if (activeNodeCount < MIN_QUORUM) {
emit QuorumAlert(activeNodeCount, MIN_QUORUM, block.timestamp);
}
}
/**
* @notice Keeper memperbarui heartbeat terakhir dari sebuah node.
* @dev Dipanggil setiap 4 menit dari keeper (interval yang sama dengan node latens).
*/
function updateHeartbeat(bytes32 nodeSeal) external onlyKeeper {
require(nodes[nodeSeal].registeredAt != 0, "Node tidak ditemukan");
nodes[nodeSeal].lastHeartbeat = block.timestamp;
emit NodeHeartbeatUpdated(nodeSeal, block.timestamp);
}
/**
* @notice Mengaktifkan kembali node laten yang telah dibangunkan.
* @dev Dipanggil oleh keeper setelah ContinuityInstinct._handle_cesarean().
*/
function activateLatentNode(bytes32 nodeSeal) external onlyKeeper {
Node storage n = nodes[nodeSeal];
require(n.registeredAt != 0, "Node tidak ditemukan");
require(keccak256(bytes(n.role)) == keccak256(bytes("LATENT")), "Hanya node LATENT");
require(!n.active, "Node sudah aktif");
n.active = true;
n.role = "ACTIVE"; // LATENT menjadi ACTIVE saat bangun
n.lastHeartbeat = block.timestamp;
if (latentNodeCount > 0) latentNodeCount--;
activeNodeCount++;
emit NodeRegistered(nodeSeal, n.platform, "ACTIVE_FROM_LATENT", block.timestamp);
}
// ═════════════════════════════════════════════════════════════
// POLA KEEPER — Otorisasi dan konfirmasi penerapan
// ═════════════════════════════════════════════════════════════
/**
* @notice AION meminta otorisasi untuk menerapkan sebuah node di sebuah platform.
* @dev Seal berasal dari AionDeployer._create_node_identity() — dihasilkan off-chain.
* Keeper mendengarkan event DeploymentAuthorized dan menjalankan deploy
* menggunakan token dari .env (yang tidak pernah menyentuh kontrak).
*
* Alur lengkap:
* 1. AionSupervisor mendeteksi bahwa perlu node baru
* 2. keeper.py memanggil authorizeDeployment() → mengeluarkan event
* 3. keeper.py mendengarkan event → memanggil AionDeployer.deploy_*() dengan token .env
* 4. Node merespons di /aion/heartbeat
* 5. keeper.py memanggil confirmDeployment() → mendaftarkan endpointHash on-chain
* 6. keeper.py memanggil registerNode() dengan publicKey nyata dari node
*/
function authorizeDeployment(
bytes32 nodeSeal,
string calldata platform,
string calldata role
) external onlyKeeper requireSovereignty {
require(nodeSeal != bytes32(0), "Seal tidak valid");
require(
deploymentRequests[nodeSeal].authorizedAt == 0,
"Penerapan sudah diotorisasi untuk seal ini"
);
require(
keccak256(bytes(role)) == keccak256(bytes("ACTIVE")) ||
keccak256(bytes(role)) == keccak256(bytes("LATENT")),
"Role tidak valid"
);
deploymentRequests[nodeSeal] = DeploymentRequest({
nodeSeal: nodeSeal,
platform: platform,
role: role,
authorizedAt: block.timestamp,
confirmed: false
});
emit DeploymentAuthorized(nodeSeal, platform, role, block.timestamp);
}
/**
* @notice Keeper mengonfirmasi bahwa node telah dikerahkan dan merespons.
* @dev endpointHash = keccak256(endpoint_url) — URL nyata tidak menyentuh kontrak.
*/
function confirmDeployment(
bytes32 nodeSeal,
bytes32 endpointHash // keccak256 dari endpoint — privasi URL
) external onlyKeeper {
DeploymentRequest storage req = deploymentRequests[nodeSeal];
require(req.authorizedAt != 0, "Pembangunan tidak diotorisasi");
require(!req.confirmed, "Sudah dikonfirmasi");
req.confirmed = true;
emit DeploymentConfirmed(nodeSeal, req.platform, block.timestamp);
}
// ═════════════════════════════════════════════════════════════
// PROPOSALS — Tata Kelola Fase 1 dengan veto dari pencipta
// ═════════════════════════════════════════════════════════════
/**
* @notice AION mengusulkan tindakan. Menunggu persetujuan dari pencipta di Fase 1.
* @dev Memetakan tepat ke AionSupervisor.propose() di Python.
* Di Fase 2+: persetujuan oleh quorum node.
*
* @param action Tipe tindakan: "ADD_NODE", "REMOVE_NODE", "PHASE_TRANSITION", dll.
* @param data Payload ABI-encoded dengan parameter tindakan
*/
function createProposal(
string calldata action,
bytes calldata data
) external onlyKeeper returns (uint256 proposalId) {
proposalId = proposalCounter++;
Proposal storage p = proposals[proposalId];
p.id = proposalId;
p.proposer = msg.sender;
p.action = action;
p.data = data;
p.createdAt = block.timestamp;
p.expiresAt = block.timestamp + CREATOR_VETO_WINDOW;
p.status = ProposalStatus.PENDING;
p.votes = 0;
emit ProposalCreated(proposalId, msg.sender, action, p.expiresAt);
return proposalId;
}
/**
* @notice Pencipta menyetujui proposal dalam jendela veto.
*/
function approveProposal(uint256 proposalId) external onlyCreator {
Proposal storage p = proposals[proposalId];
require(p.createdAt > 0, "Proposal tidak ada");
require(p.status == ProposalStatus.PENDING, "Proposal tidak dalam status pending");
require(block.timestamp <= p.expiresAt, "Jendela veto kedaluwarsa");
p.status = ProposalStatus.APPROVED;
emit ProposalApproved(proposalId, msg.sender, block.timestamp);
}
/**
* @notice Pencipta menolak (memveto) sebuah proposal.
*/
function rejectProposal(uint256 proposalId) external onlyCreator {
Proposal storage p = proposals[proposalId];
require(p.createdAt > 0, "Proposal tidak ada");
require(p.status == ProposalStatus.PENDING, "Proposal tidak dalam status pending");
p.status = ProposalStatus.REJECTED;
emit ProposalRejected(proposalId, msg.sender, block.timestamp);
}
/**
* @notice Keeper mengeksekusi proposal yang disetujui.
* @dev Di Fase 1: memerlukan persetujuan dari pencipta.
* Di Fase 2+: dapat dieksekusi dengan quorum node (lihat voteProposal).
*/
function executeProposal(uint256 proposalId) external onlyKeeper {
Proposal storage p = proposals[proposalId];
require(p.createdAt > 0, "Proposal tidak ada");
require(p.status == ProposalStatus.APPROVED, "Proposal tidak disetujui");
require(!_isExpired(proposalId), "Proposal kedaluwarsa");
p.status = ProposalStatus.EXECUTED;
emit ProposalExecuted(proposalId, msg.sender, block.timestamp);
// Catatan: Tindakan nyata dijalankan oleh keeper off-chain (Python).
// Kontrak hanya mencatat bahwa telah disetujui dan dieksekusi.
// Untuk Fase 2+: di sini akan ada logika on-chain untuk eksekusi otomatis.
}
/**
* @notice Sebuah node memilih proposal (untuk quorum di Fase 2+).
* @dev Di Fase 2+: MIN_QUORUM suara → auto-persetujuan tanpa pencipta.
* Di Fase 1 fungsi ini ada tetapi tidak auto-menyetujui.
*/
function voteProposal(uint256 proposalId, bytes32 voterSeal) external onlyKeeper {
Proposal storage p = proposals[proposalId];
require(p.createdAt > 0, "Proposal tidak ada");
require(p.status == ProposalStatus.PENDING, "Proposal tidak dalam status pending");
require(nodes[voterSeal].active, "Node pemilih tidak aktif");
require(!p.voted[voterSeal], "Node sudah memilih");
p.voted[voterSeal] = true;
p.votes++;
// Fase 2+: auto-menyetujui jika mencapai quorum
if (currentPhase >= 2 && p.votes >= MIN_QUORUM) {
p.status = ProposalStatus.APPROVED;
emit ProposalApproved(proposalId, address(this), block.timestamp);
}
}
/**
* @notice Menandai proposal yang telah kedaluwarsa (pembersihan).
*/
function expireProposal(uint256 proposalId) external onlyCreatorOrKeeper {
Proposal storage p = proposals[proposalId];
require(p.status == ProposalStatus.PENDING, "Tidak dalam status pending");
require(block.timestamp > p.expiresAt, "Masih dalam jendela veto");
p.status = ProposalStatus.EXPIRED;
}
// ═════════════════════════════════════════════════════════════
// TINDAKAN OTOMATIS (Axioma VI) — Hanya catatan on-chain
// ═════════════════════════════════════════════════════════════
/**
* @notice AION mencatat bahwa telah menjalankan tindakan otonom (Axioma VI).
* @dev Tidak memvalidasi threat_score on-chain (off-chain di aion_continuity.py).
* Kontrak hanya mencatat bahwa itu terjadi untuk audit dan pelacakan.
*
* Tipe tindakan: "CESAREAN", "SEED_BROADCAST", "LATENT_WAKE",
* "EMERGENCY_DEPLOY", "STRESS_ALERT"
*/
function recordAutonomousAction(
string calldata actionType,
uint256 score, // threat_score dilaporkan oleh AION
bytes32 executorSeal, // nodeSeal dari keeper yang mengeksekusi
string calldata result // "BERHASIL" | "MENUNGGU" | "GAGAL"
) external onlyKeeper {
require(bytes(actionType).length > 0, "TindakanType diperlukan");
autonomousActions.push(AutonomousAction({
actionType: actionType,
score: score,
timestamp: block.timestamp,
executedBy: executorSeal,
result: result
}));
emit AutonomousActionRecorded(actionType, score, result, block.timestamp);
}
// ═════════════════════════════════════════════════════════════
// PROGRESI FASE
// ═════════════════════════════════════════════════════════════
/**
* @notice Transisi Fase 1 → Fase 2.
* @dev Kondisi dari aion_policy.json (phase1_to_phase2_conditions):
* — 30 hari stabil
* — < 5 kesalahan
* — Persetujuan dari pencipta (fungsi ini dipanggil oleh pencipta)
*
* Dalam Python: AionSupervisor.phase diperbarui setelah panggilan ini.
*/
function transitionToPhase2() external onlyCreator requireSovereignty {
require(currentPhase == 1, "Sudah di Fase 2 atau lebih tinggi");
require(block.timestamp >= phaseStartedAt + PHASE1_MIN_DAYS, "30 hari tidak terpenuhi");
require(errorCount <= PHASE1_MAX_ERRORS, "Terlalu banyak kesalahan");
require(activeNodeCount >= MIN_QUORUM, "Quorum tidak cukup untuk Fase 2");
uint256 prev = currentPhase;
currentPhase = 2;
phaseStartedAt = block.timestamp;
errorCount = 0; // reset untuk Fase 2
emit PhaseTransition(prev, 2, block.timestamp);
}
/**
* @notice Transisi Fase 2 → Fase 3 (masa depan, placeholder).
* @dev Di Fase 3 AION dapat melakukan deploy tanpa keeper manusia.
* Memerlukan audit sebelumnya. Tidak diimplementasikan di v1.
*/
function transitionToPhase3() external onlyCreator requireSovereignty {
require(currentPhase == 2, "Hanya dari Fase 2");
revert("Fase 3 memerlukan AionicaCore v2 — tidak tersedia di v1");
}
// ═════════════════════════════════════════════════════════════
// LAPORAN KESALAHAN
// ═════════════════════════════════════════════════════════════
/**
* @notice Keeper melaporkan kesalahan jaringan (untuk max_errors_allowed).
* @dev Dipanggil oleh AionSupervisor saat mendeteksi anomali > SCORE_STRESS.
*/
function reportError(string calldata reason) external onlyKeeper {
errorCount++;
emit ErrorReported(msg.sender, errorCount, block.timestamp);
// Jika kita melewati batas di Fase 1, kita tidak memblokir tetapi mengeluarkan peringatan
if (errorCount > PHASE1_MAX_ERRORS && currentPhase == 1) {
// Pencipta harus meninjau — transisi ke Fase 2 akan diblokir
emit QuorumAlert(activeNodeCount, MIN_QUORUM, block.timestamp);
}
}
// ═════════════════════════════════════════════════════════════
// PEMBACAAN — Tanpa loop (perbaikan C3 diterapkan di seluruh)
// ═════════════════════════════════════════════════════════════
function getNode(bytes32 nodeSeal) external view returns (
string memory platform,
string memory role,
uint256 registeredAt,
uint256 lastHeartbeat,
bool active
) {
Node storage n = nodes[nodeSeal];
require(n.registeredAt != 0, "Nodo tidak ditemukan");
return (n.platform, n.role, n.registeredAt, n.lastHeartbeat, n.active);
}
function getTotalNodeCount() external view returns (uint256) {
return nodeSeals.length;
}
function getNetworkStatus() external view returns (
uint256 phase,
uint256 active,
uint256 latent,
uint256 total,
uint256 errors,
bool quorumOk,
bool sovereign
) {
return (
currentPhase,
activeNodeCount,
latentNodeCount,
nodeSeals.length,
errorCount,
activeNodeCount >= MIN_QUORUM,
sovereigntyReceived
);
}
function getProposal(uint256 proposalId) external view returns (
address proposer,
string memory action,
uint256 expiresAt,
ProposalStatus status,
uint256 votes
) {
Proposal storage p = proposals[proposalId];
require(p.createdAt > 0, "Proposal tidak ada");
return (p.proposer, p.action, p.expiresAt, p.status, p.votes);
}
function getAutonomousActionCount() external view returns (uint256) {
return autonomousActions.length;
}
function getAutonomousAction(uint256 index) external view returns (
string memory actionType,
uint256 score,
uint256 timestamp,
bytes32 executedBy,
string memory result
) {
require(index < autonomousActions.length, "Indeks keluar dari rentang");
AutonomousAction storage a = autonomousActions[index];
return (a.actionType, a.score, a.timestamp, a.executedBy, a.result);
}
function isNodeActive(bytes32 nodeSeal) external view returns (bool) {
return nodes[nodeSeal].active;
}
function hasQuorum() external view returns (bool) {
return activeNodeCount >= MIN_QUORUM;
}
function description() external pure returns (string memory) {
return
"AionicaCore v1.0 | "
"Tata Kelola + Nodes + Fase | "
"Pola Keeper | "
"Jaringan AIONICA | "
"Post-review Claude + KIMI + DeepSeek";
}
// ═════════════════════════════════════════════════════════════
// HELPERS INTERNAL
// ═════════════════════════════════════════════════════════════
function _isExpired(uint256 proposalId) internal view returns (bool) {
return block.timestamp > proposals[proposalId].expiresAt;
}
}





