Spaces:
Build error
Build error
salmanch123 commited on
Commit ·
60050be
0
Parent(s):
feat: push fixed compilation tsconfig
Browse files- .gitignore +8 -0
- Dockerfile +22 -0
- README.md +12 -0
- contracts/AgentIdentityRegistry.sol +71 -0
- contracts/CipherAuth.sol +383 -0
- contracts/CipherTrust.sol +824 -0
- contracts/InsurancePool.sol +89 -0
- contracts/ReputationBadge.sol +98 -0
- hardhat.config.ts +46 -0
- package.json +42 -0
- scripts/deploy.ts +23 -0
- scripts/deploy_auth.ts +17 -0
- scripts/optimize_contract.js +20 -0
- scripts/oracle_service.ts +58 -0
- scripts/setup_database.sql +58 -0
- scripts/simulate_agent.ts +226 -0
- tsconfig.json +14 -0
.gitignore
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
node_modules
|
| 2 |
+
cache
|
| 3 |
+
artifacts
|
| 4 |
+
typechain-types
|
| 5 |
+
.env
|
| 6 |
+
coverage
|
| 7 |
+
coverage.json
|
| 8 |
+
*.log
|
Dockerfile
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM node:22-alpine
|
| 2 |
+
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
|
| 5 |
+
# Install build dependencies for native modules
|
| 6 |
+
RUN apk add --no-cache python3 make g++
|
| 7 |
+
|
| 8 |
+
# Copy dependency mappings and install
|
| 9 |
+
COPY package*.json ./
|
| 10 |
+
RUN npm install
|
| 11 |
+
|
| 12 |
+
# Copy all project files
|
| 13 |
+
COPY . .
|
| 14 |
+
|
| 15 |
+
# Compile TS files to dist/
|
| 16 |
+
RUN npx tsc
|
| 17 |
+
|
| 18 |
+
# Expose port (HF requires this for Docker spaces)
|
| 19 |
+
EXPOSE 7860
|
| 20 |
+
|
| 21 |
+
# Run the compiled javascript directly for instant boot
|
| 22 |
+
CMD ["node", "dist/scripts/oracle_service.js"]
|
README.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: CipherTrust Oracle
|
| 3 |
+
emoji: 🛡️
|
| 4 |
+
colorFrom: yellow
|
| 5 |
+
colorTo: indigo
|
| 6 |
+
sdk: docker
|
| 7 |
+
pinned: false
|
| 8 |
+
---
|
| 9 |
+
|
| 10 |
+
# CipherTrust Telemetry Oracle Daemon
|
| 11 |
+
|
| 12 |
+
Background telemetry oracle for the CipherTrust FHE protocol.
|
contracts/AgentIdentityRegistry.sol
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// SPDX-License-Identifier: MIT
|
| 2 |
+
pragma solidity ^0.8.24;
|
| 3 |
+
|
| 4 |
+
/// @title AgentIdentityRegistry
|
| 5 |
+
/// @notice Lightweight, ERC-8004-inspired identity registry for autonomous agents and
|
| 6 |
+
/// robots. This is NOT a claim of full ERC-8004 compliance -- it borrows the standard's
|
| 7 |
+
/// core idea (a portable, on-chain identity record for an autonomous agent, decoupled
|
| 8 |
+
/// from any single application) so CipherTrust's underwriting data, and any other
|
| 9 |
+
/// confidential protocol, can be composed against the same agent identity instead of
|
| 10 |
+
/// re-deriving one per app.
|
| 11 |
+
contract AgentIdentityRegistry {
|
| 12 |
+
struct Identity {
|
| 13 |
+
address operator;
|
| 14 |
+
string agentType; // e.g. "trading-bot", "delivery-robot", "drone", "depin-device"
|
| 15 |
+
string metadataURI; // off-chain JSON: hardware attestation, manufacturer, firmware hash, etc.
|
| 16 |
+
uint256 registeredAt;
|
| 17 |
+
bool active;
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
address public admin;
|
| 21 |
+
uint256 public nextIdentityId;
|
| 22 |
+
mapping(uint256 => Identity) public identities;
|
| 23 |
+
mapping(uint256 => mapping(address => bool)) public delegatedOperators;
|
| 24 |
+
|
| 25 |
+
event IdentityRegistered(uint256 indexed identityId, address indexed operator, string agentType);
|
| 26 |
+
event IdentityDeactivated(uint256 indexed identityId);
|
| 27 |
+
event OperatorDelegated(uint256 indexed identityId, address indexed delegate);
|
| 28 |
+
|
| 29 |
+
modifier onlyOperator(uint256 identityId) {
|
| 30 |
+
require(
|
| 31 |
+
identities[identityId].operator == msg.sender || delegatedOperators[identityId][msg.sender],
|
| 32 |
+
"AgentIdentityRegistry: not operator"
|
| 33 |
+
);
|
| 34 |
+
_;
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
constructor() {
|
| 38 |
+
admin = msg.sender;
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
function registerIdentity(
|
| 42 |
+
address operator,
|
| 43 |
+
string calldata agentType,
|
| 44 |
+
string calldata metadataURI
|
| 45 |
+
) external returns (uint256 identityId) {
|
| 46 |
+
identityId = nextIdentityId++;
|
| 47 |
+
identities[identityId] = Identity({
|
| 48 |
+
operator: operator,
|
| 49 |
+
agentType: agentType,
|
| 50 |
+
metadataURI: metadataURI,
|
| 51 |
+
registeredAt: block.timestamp,
|
| 52 |
+
active: true
|
| 53 |
+
});
|
| 54 |
+
emit IdentityRegistered(identityId, operator, agentType);
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
function delegateOperator(uint256 identityId, address delegate) external onlyOperator(identityId) {
|
| 58 |
+
delegatedOperators[identityId][delegate] = true;
|
| 59 |
+
emit OperatorDelegated(identityId, delegate);
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
function deactivate(uint256 identityId) external onlyOperator(identityId) {
|
| 63 |
+
identities[identityId].active = false;
|
| 64 |
+
emit IdentityDeactivated(identityId);
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
function isActiveOperator(uint256 identityId, address who) external view returns (bool) {
|
| 68 |
+
Identity storage id_ = identities[identityId];
|
| 69 |
+
return id_.active && (id_.operator == who || delegatedOperators[identityId][who]);
|
| 70 |
+
}
|
| 71 |
+
}
|
contracts/CipherAuth.sol
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// SPDX-License-Identifier: MIT
|
| 2 |
+
pragma solidity ^0.8.24;
|
| 3 |
+
|
| 4 |
+
import {FHE, euint64, ebool, externalEuint64} from "@fhevm/solidity/lib/FHE.sol";
|
| 5 |
+
import {SepoliaConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
|
| 6 |
+
|
| 7 |
+
contract CipherAuth is SepoliaConfig {
|
| 8 |
+
// FHE-MFA Biometric Authentication State
|
| 9 |
+
mapping(address => euint64[3]) private _registeredBiometrics;
|
| 10 |
+
mapping(address => bool) private _hasBiometrics;
|
| 11 |
+
uint32 public maxBiometricDrift = 15; // Manhattan distance tolerance
|
| 12 |
+
mapping(address => bool) public biometricAuthPassed;
|
| 13 |
+
mapping(uint256 => address) public biometricRequestUser;
|
| 14 |
+
|
| 15 |
+
// FHE-Guard: Spam Filtering State
|
| 16 |
+
mapping(address => uint32) public spamThreshold;
|
| 17 |
+
mapping(address => uint256) public inboxCount;
|
| 18 |
+
mapping(address => uint256) public spamInboxCount;
|
| 19 |
+
mapping(uint256 => address) public spamCheckRequestUser;
|
| 20 |
+
|
| 21 |
+
// FHE-Guard: Passwordless Auth State
|
| 22 |
+
mapping(address => euint64) private _masterSecret;
|
| 23 |
+
mapping(address => bool) private _hasSecret;
|
| 24 |
+
mapping(address => uint64) public activeAuthChallenge;
|
| 25 |
+
mapping(address => bool) public authPassed;
|
| 26 |
+
mapping(uint256 => address) public authRequests;
|
| 27 |
+
|
| 28 |
+
// FHE-Passport: Biometric Uniqueness State
|
| 29 |
+
uint256 public passportCount;
|
| 30 |
+
mapping(uint256 => euint64[3]) private _passportDatabase;
|
| 31 |
+
mapping(address => bool) public hasPassport;
|
| 32 |
+
mapping(uint256 => address) public passportRequests;
|
| 33 |
+
mapping(address => bool) public passportUnique;
|
| 34 |
+
mapping(uint256 => euint64[3]) private _pendingPassportTemplates;
|
| 35 |
+
uint256 public nextTierRequestId = 1;
|
| 36 |
+
|
| 37 |
+
// FHE-Aegis: AI Agent Behavior Drift State
|
| 38 |
+
mapping(uint256 => euint64[3]) private _agentBaselines;
|
| 39 |
+
mapping(uint256 => bool) private _hasBaseline;
|
| 40 |
+
uint32 public maxBehaviorDrift = 1000; // squared Euclidean distance threshold
|
| 41 |
+
mapping(uint256 => bool) public agentBehaviorCompromised;
|
| 42 |
+
mapping(uint256 => uint256) public behaviorRequests;
|
| 43 |
+
|
| 44 |
+
event BiometricsRegistered(address indexed user);
|
| 45 |
+
event BiometricsVerified(address indexed user, bool success);
|
| 46 |
+
event MessageSpamChecked(address indexed recipient, bool isSpam);
|
| 47 |
+
event AuthSecretRegistered(address indexed user);
|
| 48 |
+
event AuthChallengeGenerated(address indexed user, uint64 challenge);
|
| 49 |
+
event AuthVerified(address indexed user, bool success);
|
| 50 |
+
event PassportCheckRequested(address indexed user, uint256 indexed requestId);
|
| 51 |
+
event PassportRegistered(address indexed user, bool success, uint256 passportId);
|
| 52 |
+
event AgentBaselineRegistered(uint256 indexed agentId);
|
| 53 |
+
event BehaviorCheckRequested(uint256 indexed agentId, uint256 indexed requestId);
|
| 54 |
+
event BehaviorChecked(uint256 indexed agentId, bool compromised);
|
| 55 |
+
|
| 56 |
+
// FHE-MFA: Biometric Authentication Implementation
|
| 57 |
+
function registerBiometricSignature(
|
| 58 |
+
externalEuint64 hX,
|
| 59 |
+
externalEuint64 hY,
|
| 60 |
+
externalEuint64 hZ,
|
| 61 |
+
bytes calldata inputProof
|
| 62 |
+
) external {
|
| 63 |
+
_registeredBiometrics[msg.sender][0] = FHE.fromExternal(hX, inputProof);
|
| 64 |
+
_registeredBiometrics[msg.sender][1] = FHE.fromExternal(hY, inputProof);
|
| 65 |
+
_registeredBiometrics[msg.sender][2] = FHE.fromExternal(hZ, inputProof);
|
| 66 |
+
_hasBiometrics[msg.sender] = true;
|
| 67 |
+
|
| 68 |
+
FHE.allowThis(_registeredBiometrics[msg.sender][0]);
|
| 69 |
+
FHE.allowThis(_registeredBiometrics[msg.sender][1]);
|
| 70 |
+
FHE.allowThis(_registeredBiometrics[msg.sender][2]);
|
| 71 |
+
FHE.allow(_registeredBiometrics[msg.sender][0], msg.sender);
|
| 72 |
+
FHE.allow(_registeredBiometrics[msg.sender][1], msg.sender);
|
| 73 |
+
FHE.allow(_registeredBiometrics[msg.sender][2], msg.sender);
|
| 74 |
+
|
| 75 |
+
emit BiometricsRegistered(msg.sender);
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
function requestBiometricAuth(
|
| 79 |
+
externalEuint64 hX,
|
| 80 |
+
externalEuint64 hY,
|
| 81 |
+
externalEuint64 hZ,
|
| 82 |
+
bytes calldata inputProof
|
| 83 |
+
) external returns (uint256 requestId) {
|
| 84 |
+
address user = msg.sender;
|
| 85 |
+
require(_hasBiometrics[user]);
|
| 86 |
+
|
| 87 |
+
euint64 x = FHE.fromExternal(hX, inputProof);
|
| 88 |
+
euint64 y = FHE.fromExternal(hY, inputProof);
|
| 89 |
+
euint64 z = FHE.fromExternal(hZ, inputProof);
|
| 90 |
+
|
| 91 |
+
euint64 dX = FHE.select(FHE.lt(x, _registeredBiometrics[user][0]), FHE.sub(_registeredBiometrics[user][0], x), FHE.sub(x, _registeredBiometrics[user][0]));
|
| 92 |
+
euint64 dY = FHE.select(FHE.lt(y, _registeredBiometrics[user][1]), FHE.sub(_registeredBiometrics[user][1], y), FHE.sub(y, _registeredBiometrics[user][1]));
|
| 93 |
+
euint64 dZ = FHE.select(FHE.lt(z, _registeredBiometrics[user][2]), FHE.sub(_registeredBiometrics[user][2], z), FHE.sub(z, _registeredBiometrics[user][2]));
|
| 94 |
+
|
| 95 |
+
euint64 totalDist = FHE.add(FHE.add(dX, dY), dZ);
|
| 96 |
+
ebool isValid = FHE.le(totalDist, FHE.asEuint64(maxBiometricDrift));
|
| 97 |
+
FHE.allowThis(isValid);
|
| 98 |
+
|
| 99 |
+
bytes32[] memory cts = new bytes32[](1);
|
| 100 |
+
cts[0] = ebool.unwrap(isValid);
|
| 101 |
+
|
| 102 |
+
requestId = FHE.requestDecryption(cts, this.fulfillBiometricAuth.selector);
|
| 103 |
+
biometricRequestUser[requestId] = user;
|
| 104 |
+
|
| 105 |
+
biometricAuthPassed[user] = false;
|
| 106 |
+
emit BiometricsVerified(user, false);
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
function fulfillBiometricAuth(
|
| 110 |
+
uint256 requestId,
|
| 111 |
+
bytes memory cleartexts,
|
| 112 |
+
bytes memory decryptionProof
|
| 113 |
+
) external {
|
| 114 |
+
FHE.checkSignatures(requestId, cleartexts, decryptionProof);
|
| 115 |
+
|
| 116 |
+
bool success = abi.decode(cleartexts, (bool));
|
| 117 |
+
address user = biometricRequestUser[requestId];
|
| 118 |
+
delete biometricRequestUser[requestId];
|
| 119 |
+
|
| 120 |
+
biometricAuthPassed[user] = success;
|
| 121 |
+
emit BiometricsVerified(user, success);
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
// FHE-Guard: Spam Filtering Implementation
|
| 125 |
+
function setSpamThreshold(uint32 threshold) external {
|
| 126 |
+
spamThreshold[msg.sender] = threshold;
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
function checkMessageSpam(
|
| 130 |
+
address recipient,
|
| 131 |
+
externalEuint64 wA,
|
| 132 |
+
externalEuint64 wB,
|
| 133 |
+
externalEuint64 wC,
|
| 134 |
+
bytes calldata inputProof
|
| 135 |
+
) external returns (uint256 requestId) {
|
| 136 |
+
if (spamThreshold[recipient] == 0) {
|
| 137 |
+
spamThreshold[recipient] = 15; // default threshold
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
euint64 scoreA = FHE.fromExternal(wA, inputProof);
|
| 141 |
+
euint64 scoreB = FHE.fromExternal(wB, inputProof);
|
| 142 |
+
euint64 scoreC = FHE.fromExternal(wC, inputProof);
|
| 143 |
+
|
| 144 |
+
euint64 totalScore = FHE.add(FHE.add(scoreA, scoreB), scoreC);
|
| 145 |
+
ebool isSpam = FHE.gt(totalScore, FHE.asEuint64(spamThreshold[recipient]));
|
| 146 |
+
|
| 147 |
+
FHE.allowThis(isSpam);
|
| 148 |
+
|
| 149 |
+
bytes32[] memory cts = new bytes32[](1);
|
| 150 |
+
cts[0] = ebool.unwrap(isSpam);
|
| 151 |
+
|
| 152 |
+
requestId = FHE.requestDecryption(cts, this.fulfillSpamCheck.selector);
|
| 153 |
+
spamCheckRequestUser[requestId] = recipient;
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
function fulfillSpamCheck(
|
| 157 |
+
uint256 requestId,
|
| 158 |
+
bytes memory cleartexts,
|
| 159 |
+
bytes memory decryptionProof
|
| 160 |
+
) external {
|
| 161 |
+
FHE.checkSignatures(requestId, cleartexts, decryptionProof);
|
| 162 |
+
|
| 163 |
+
bool isSpam = abi.decode(cleartexts, (bool));
|
| 164 |
+
address recipient = spamCheckRequestUser[requestId];
|
| 165 |
+
delete spamCheckRequestUser[requestId];
|
| 166 |
+
|
| 167 |
+
if (isSpam) {
|
| 168 |
+
spamInboxCount[recipient]++;
|
| 169 |
+
} else {
|
| 170 |
+
inboxCount[recipient]++;
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
emit MessageSpamChecked(recipient, isSpam);
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
// FHE-Pass: Challenge-Response Implementation
|
| 177 |
+
function registerAuthSecret(
|
| 178 |
+
externalEuint64 hSecret,
|
| 179 |
+
bytes calldata inputProof
|
| 180 |
+
) external {
|
| 181 |
+
_masterSecret[msg.sender] = FHE.fromExternal(hSecret, inputProof);
|
| 182 |
+
_hasSecret[msg.sender] = true;
|
| 183 |
+
|
| 184 |
+
FHE.allowThis(_masterSecret[msg.sender]);
|
| 185 |
+
FHE.allow(_masterSecret[msg.sender], msg.sender);
|
| 186 |
+
|
| 187 |
+
emit AuthSecretRegistered(msg.sender);
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
function generateAuthChallenge(uint64 seedChallenge) external returns (uint64) {
|
| 191 |
+
require(_hasSecret[msg.sender]);
|
| 192 |
+
activeAuthChallenge[msg.sender] = seedChallenge;
|
| 193 |
+
emit AuthChallengeGenerated(msg.sender, seedChallenge);
|
| 194 |
+
return seedChallenge;
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
function verifyAuthChallenge(
|
| 198 |
+
externalEuint64 hResponse,
|
| 199 |
+
bytes calldata inputProof
|
| 200 |
+
) external returns (uint256 requestId) {
|
| 201 |
+
address user = msg.sender;
|
| 202 |
+
require(_hasSecret[user]);
|
| 203 |
+
require(activeAuthChallenge[user] != 0);
|
| 204 |
+
|
| 205 |
+
euint64 response = FHE.fromExternal(hResponse, inputProof);
|
| 206 |
+
euint64 challengeVal = FHE.asEuint64(activeAuthChallenge[user]);
|
| 207 |
+
euint64 expected = FHE.add(_masterSecret[user], challengeVal);
|
| 208 |
+
|
| 209 |
+
ebool isValid = FHE.eq(response, expected);
|
| 210 |
+
FHE.allowThis(isValid);
|
| 211 |
+
|
| 212 |
+
bytes32[] memory cts = new bytes32[](1);
|
| 213 |
+
cts[0] = ebool.unwrap(isValid);
|
| 214 |
+
|
| 215 |
+
requestId = FHE.requestDecryption(cts, this.fulfillAuthCheck.selector);
|
| 216 |
+
authRequests[requestId] = user;
|
| 217 |
+
|
| 218 |
+
authPassed[user] = false;
|
| 219 |
+
emit AuthVerified(user, false);
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
function fulfillAuthCheck(
|
| 223 |
+
uint256 requestId,
|
| 224 |
+
bytes memory cleartexts,
|
| 225 |
+
bytes memory decryptionProof
|
| 226 |
+
) external {
|
| 227 |
+
FHE.checkSignatures(requestId, cleartexts, decryptionProof);
|
| 228 |
+
|
| 229 |
+
bool success = abi.decode(cleartexts, (bool));
|
| 230 |
+
address user = authRequests[requestId];
|
| 231 |
+
delete authRequests[requestId];
|
| 232 |
+
|
| 233 |
+
authPassed[user] = success;
|
| 234 |
+
emit AuthVerified(user, success);
|
| 235 |
+
}
|
| 236 |
+
|
| 237 |
+
// FHE-Passport: Biometric Uniqueness Check Implementation
|
| 238 |
+
function requestPassportRegistration(
|
| 239 |
+
externalEuint64 hX,
|
| 240 |
+
externalEuint64 hY,
|
| 241 |
+
externalEuint64 hZ,
|
| 242 |
+
bytes calldata inputProof
|
| 243 |
+
) external returns (uint256 requestId) {
|
| 244 |
+
address user = msg.sender;
|
| 245 |
+
require(!hasPassport[user]);
|
| 246 |
+
|
| 247 |
+
euint64 x = FHE.fromExternal(hX, inputProof);
|
| 248 |
+
euint64 y = FHE.fromExternal(hY, inputProof);
|
| 249 |
+
euint64 z = FHE.fromExternal(hZ, inputProof);
|
| 250 |
+
|
| 251 |
+
uint256 nextReqId = nextTierRequestId++;
|
| 252 |
+
_pendingPassportTemplates[nextReqId][0] = x;
|
| 253 |
+
_pendingPassportTemplates[nextReqId][1] = y;
|
| 254 |
+
_pendingPassportTemplates[nextReqId][2] = z;
|
| 255 |
+
|
| 256 |
+
FHE.allowThis(_pendingPassportTemplates[nextReqId][0]);
|
| 257 |
+
FHE.allowThis(_pendingPassportTemplates[nextReqId][1]);
|
| 258 |
+
FHE.allowThis(_pendingPassportTemplates[nextReqId][2]);
|
| 259 |
+
|
| 260 |
+
ebool isUnique = FHE.asEbool(true);
|
| 261 |
+
|
| 262 |
+
for (uint256 i = 0; i < passportCount; i++) {
|
| 263 |
+
euint64 dX = FHE.select(FHE.lt(x, _passportDatabase[i][0]), FHE.sub(_passportDatabase[i][0], x), FHE.sub(x, _passportDatabase[i][0]));
|
| 264 |
+
euint64 dY = FHE.select(FHE.lt(y, _passportDatabase[i][1]), FHE.sub(_passportDatabase[i][1], y), FHE.sub(y, _passportDatabase[i][1]));
|
| 265 |
+
euint64 dZ = FHE.select(FHE.lt(z, _passportDatabase[i][2]), FHE.sub(_passportDatabase[i][2], z), FHE.sub(z, _passportDatabase[i][2]));
|
| 266 |
+
|
| 267 |
+
euint64 dist = FHE.add(FHE.add(dX, dY), dZ);
|
| 268 |
+
ebool duplicate = FHE.le(dist, FHE.asEuint64(10));
|
| 269 |
+
isUnique = FHE.and(isUnique, FHE.not(duplicate));
|
| 270 |
+
}
|
| 271 |
+
|
| 272 |
+
FHE.allowThis(isUnique);
|
| 273 |
+
|
| 274 |
+
bytes32[] memory cts = new bytes32[](1);
|
| 275 |
+
cts[0] = ebool.unwrap(isUnique);
|
| 276 |
+
|
| 277 |
+
requestId = FHE.requestDecryption(cts, this.fulfillPassportCheck.selector);
|
| 278 |
+
passportRequests[requestId] = user;
|
| 279 |
+
}
|
| 280 |
+
|
| 281 |
+
function fulfillPassportCheck(
|
| 282 |
+
uint256 requestId,
|
| 283 |
+
bytes memory cleartexts,
|
| 284 |
+
bytes memory decryptionProof
|
| 285 |
+
) external {
|
| 286 |
+
FHE.checkSignatures(requestId, cleartexts, decryptionProof);
|
| 287 |
+
|
| 288 |
+
bool unique = abi.decode(cleartexts, (bool));
|
| 289 |
+
address user = passportRequests[requestId];
|
| 290 |
+
delete passportRequests[requestId];
|
| 291 |
+
|
| 292 |
+
if (unique) {
|
| 293 |
+
uint256 id = passportCount++;
|
| 294 |
+
_passportDatabase[id][0] = _pendingPassportTemplates[requestId][0];
|
| 295 |
+
_passportDatabase[id][1] = _pendingPassportTemplates[requestId][1];
|
| 296 |
+
_passportDatabase[id][2] = _pendingPassportTemplates[requestId][2];
|
| 297 |
+
|
| 298 |
+
FHE.allowThis(_passportDatabase[id][0]);
|
| 299 |
+
FHE.allowThis(_passportDatabase[id][1]);
|
| 300 |
+
FHE.allowThis(_passportDatabase[id][2]);
|
| 301 |
+
|
| 302 |
+
hasPassport[user] = true;
|
| 303 |
+
passportUnique[user] = true;
|
| 304 |
+
emit PassportRegistered(user, true, id);
|
| 305 |
+
} else {
|
| 306 |
+
passportUnique[user] = false;
|
| 307 |
+
emit PassportRegistered(user, false, 0);
|
| 308 |
+
}
|
| 309 |
+
}
|
| 310 |
+
|
| 311 |
+
// FHE-Aegis: Behavioral Anomaly Detector Implementation
|
| 312 |
+
function registerAgentBaseline(
|
| 313 |
+
uint256 agentId,
|
| 314 |
+
externalEuint64 hT,
|
| 315 |
+
externalEuint64 hF,
|
| 316 |
+
externalEuint64 hC,
|
| 317 |
+
bytes calldata inputProof
|
| 318 |
+
) external {
|
| 319 |
+
_agentBaselines[agentId][0] = FHE.fromExternal(hT, inputProof);
|
| 320 |
+
_agentBaselines[agentId][1] = FHE.fromExternal(hF, inputProof);
|
| 321 |
+
_agentBaselines[agentId][2] = FHE.fromExternal(hC, inputProof);
|
| 322 |
+
_hasBaseline[agentId] = true;
|
| 323 |
+
|
| 324 |
+
FHE.allowThis(_agentBaselines[agentId][0]);
|
| 325 |
+
FHE.allowThis(_agentBaselines[agentId][1]);
|
| 326 |
+
FHE.allowThis(_agentBaselines[agentId][2]);
|
| 327 |
+
|
| 328 |
+
emit AgentBaselineRegistered(agentId);
|
| 329 |
+
}
|
| 330 |
+
|
| 331 |
+
function evaluateAgentBehavior(
|
| 332 |
+
uint256 agentId,
|
| 333 |
+
externalEuint64 hT,
|
| 334 |
+
externalEuint64 hF,
|
| 335 |
+
externalEuint64 hC,
|
| 336 |
+
bytes calldata inputProof
|
| 337 |
+
) external returns (uint256 requestId) {
|
| 338 |
+
require(_hasBaseline[agentId]);
|
| 339 |
+
|
| 340 |
+
euint64 oT = FHE.fromExternal(hT, inputProof);
|
| 341 |
+
euint64 oF = FHE.fromExternal(hF, inputProof);
|
| 342 |
+
euint64 oC = FHE.fromExternal(hC, inputProof);
|
| 343 |
+
|
| 344 |
+
euint64 bT = _agentBaselines[agentId][0];
|
| 345 |
+
euint64 bF = _agentBaselines[agentId][1];
|
| 346 |
+
euint64 bC = _agentBaselines[agentId][2];
|
| 347 |
+
|
| 348 |
+
euint64 diffT = FHE.select(FHE.lt(oT, bT), FHE.sub(bT, oT), FHE.sub(oT, bT));
|
| 349 |
+
euint64 diffF = FHE.select(FHE.lt(oF, bF), FHE.sub(bF, oF), FHE.sub(oF, bF));
|
| 350 |
+
euint64 diffC = FHE.select(FHE.lt(oC, bC), FHE.sub(bC, oC), FHE.sub(oC, bC));
|
| 351 |
+
|
| 352 |
+
euint64 drift = FHE.add(
|
| 353 |
+
FHE.add(FHE.mul(diffT, diffT), FHE.mul(diffF, diffF)),
|
| 354 |
+
FHE.mul(diffC, diffC)
|
| 355 |
+
);
|
| 356 |
+
|
| 357 |
+
ebool isCompromised = FHE.gt(drift, FHE.asEuint64(maxBehaviorDrift));
|
| 358 |
+
FHE.allowThis(isCompromised);
|
| 359 |
+
|
| 360 |
+
bytes32[] memory cts = new bytes32[](1);
|
| 361 |
+
cts[0] = ebool.unwrap(isCompromised);
|
| 362 |
+
|
| 363 |
+
requestId = FHE.requestDecryption(cts, this.fulfillBehaviorCheck.selector);
|
| 364 |
+
behaviorRequests[requestId] = agentId;
|
| 365 |
+
|
| 366 |
+
emit BehaviorCheckRequested(agentId, requestId);
|
| 367 |
+
}
|
| 368 |
+
|
| 369 |
+
function fulfillBehaviorCheck(
|
| 370 |
+
uint256 requestId,
|
| 371 |
+
bytes memory cleartexts,
|
| 372 |
+
bytes memory decryptionProof
|
| 373 |
+
) external {
|
| 374 |
+
FHE.checkSignatures(requestId, cleartexts, decryptionProof);
|
| 375 |
+
|
| 376 |
+
bool isCompromised = abi.decode(cleartexts, (bool));
|
| 377 |
+
uint256 agentId = behaviorRequests[requestId];
|
| 378 |
+
delete behaviorRequests[requestId];
|
| 379 |
+
|
| 380 |
+
agentBehaviorCompromised[agentId] = isCompromised;
|
| 381 |
+
emit BehaviorChecked(agentId, isCompromised);
|
| 382 |
+
}
|
| 383 |
+
}
|
contracts/CipherTrust.sol
ADDED
|
@@ -0,0 +1,824 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// SPDX-License-Identifier: MIT
|
| 2 |
+
pragma solidity ^0.8.24;
|
| 3 |
+
|
| 4 |
+
import {FHE, euint64, ebool, externalEuint64} from "@fhevm/solidity/lib/FHE.sol";
|
| 5 |
+
import {SepoliaConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
|
| 6 |
+
|
| 7 |
+
interface IReputationBadge {
|
| 8 |
+
function mintOrUpgrade(uint256 agentId, address operator, uint8 tier) external;
|
| 9 |
+
function tierOf(uint256 agentId) external view returns (uint8);
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
interface IInsurancePool {
|
| 13 |
+
function receivePenalty(uint256 agentId) external payable;
|
| 14 |
+
function delegateCredit(uint256 agentId, uint256 amount) external;
|
| 15 |
+
function repayCredit(uint256 agentId) external payable;
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
/**
|
| 19 |
+
* @title CipherTrust
|
| 20 |
+
* @notice Confidential underwriting protocol for autonomous agents & robots.
|
| 21 |
+
*
|
| 22 |
+
* Autonomous AI trading bots, delivery robots, drone fleets, and DePIN devices
|
| 23 |
+
* increasingly hold funds and execute tasks without human supervision. There is
|
| 24 |
+
* no confidential way today to score their reliability and price the
|
| 25 |
+
* collateral/insurance they must post -- any naive on-chain reputation system
|
| 26 |
+
* leaks competitively sensitive operational data (uptime, error rates, routes,
|
| 27 |
+
* strategy performance) to rivals, because blockchains are public by default.
|
| 28 |
+
*
|
| 29 |
+
* CipherTrust computes a rolling trust score and a required collateral bond
|
| 30 |
+
* entirely under Fully Homomorphic Encryption. Operators, insurers, and task
|
| 31 |
+
* marketplaces can rely on the *outcome* (bond tier, sufficiency check)
|
| 32 |
+
* without ever seeing the raw encrypted telemetry that produced it.
|
| 33 |
+
*
|
| 34 |
+
* v0.2 additions (see docs/COMPETITIVE_ANALYSIS.md for why these were added):
|
| 35 |
+
* - Multi-oracle quorum: telemetry only affects the score once N independent
|
| 36 |
+
* oracles agree within a round, reducing single-oracle trust assumptions.
|
| 37 |
+
* - Async confidential slashing: an oracle can request a confidential SLA
|
| 38 |
+
* breach check; the breach flag is decrypted via Zama's public-decrypt +
|
| 39 |
+
* signature-verification flow before any penalty is applied on-chain.
|
| 40 |
+
* - Optional composability hooks into a soulbound ReputationBadge (public,
|
| 41 |
+
* selectively-revealed trust tier) and an InsurancePool (LP yield funded
|
| 42 |
+
* by slashing penalties), so other protocols can build on CipherTrust's
|
| 43 |
+
* output without ever touching an agent's raw telemetry.
|
| 44 |
+
*
|
| 45 |
+
* NOTE: this is an MVP scaffold. Verify every FHE.* call (especially the
|
| 46 |
+
* makePubliclyDecryptable / checkSignatures async-decrypt flow) against the
|
| 47 |
+
* exact current version of fhevm-solidity pinned in package.json before
|
| 48 |
+
* deploying to a live network -- the FHE Solidity API surface evolves
|
| 49 |
+
* between releases, and this flow has not yet been compiled/tested.
|
| 50 |
+
*/
|
| 51 |
+
contract CipherTrust is SepoliaConfig {
|
| 52 |
+
address public admin;
|
| 53 |
+
uint256 public nextAgentId;
|
| 54 |
+
|
| 55 |
+
struct Agent {
|
| 56 |
+
address operator;
|
| 57 |
+
bool registered;
|
| 58 |
+
bool active;
|
| 59 |
+
uint256 identityId; // optional link into AgentIdentityRegistry, 0 if unset
|
| 60 |
+
euint64 trustScore; // encrypted, 0-1000 scale
|
| 61 |
+
euint64 requiredBond; // encrypted, wei
|
| 62 |
+
uint256 postedBond; // public collateral currently deposited (wei)
|
| 63 |
+
ebool bondSufficient; // encrypted boolean: postedBond >= requiredBond
|
| 64 |
+
uint256 breachCount; // public count of confirmed SLA breaches
|
| 65 |
+
uint256 trustScoreVar; // estimation uncertainty variance, initialized to 100 (public)
|
| 66 |
+
euint64 liquidationThreshold; // encrypted minimum trust score before liquidation
|
| 67 |
+
uint256 delegatedBond; // public delegated bond amount borrowed from the pool (wei)
|
| 68 |
+
uint256 interestAccumulated; // public interest accumulated (wei)
|
| 69 |
+
uint256 lastInterestUpdateTime; // timestamp of the last yield accrual
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
struct Task {
|
| 73 |
+
uint256 agentId;
|
| 74 |
+
address client;
|
| 75 |
+
uint256 coverageLimit; // maximum ETH coverage (wei)
|
| 76 |
+
bool active;
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
struct Lease {
|
| 80 |
+
address lessee;
|
| 81 |
+
uint256 agentId;
|
| 82 |
+
uint256 hardwareId;
|
| 83 |
+
uint256 requiredBond; // underwriting bond (wei)
|
| 84 |
+
uint256 startTimestamp;
|
| 85 |
+
bool active;
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
uint256 public nextTaskId = 1;
|
| 89 |
+
mapping(uint256 => Task) public tasks;
|
| 90 |
+
|
| 91 |
+
uint256 public nextLeaseId = 1;
|
| 92 |
+
mapping(uint256 => Lease) public leases;
|
| 93 |
+
mapping(address => uint256) public userActiveLeaseId;
|
| 94 |
+
mapping(uint256 => uint256) public claimRequestTask; // decryption requestId => taskId
|
| 95 |
+
mapping(uint256 => uint256) public agentActiveTaskId; // agentId => active taskId (0 if none)
|
| 96 |
+
|
| 97 |
+
struct PendingRound {
|
| 98 |
+
uint32 count;
|
| 99 |
+
bool initialized;
|
| 100 |
+
euint64 sumCompletion;
|
| 101 |
+
euint64 sumUptime;
|
| 102 |
+
euint64 sumLatency;
|
| 103 |
+
euint64 sumError;
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
mapping(uint256 => Agent) private _agents;
|
| 107 |
+
mapping(address => bool) public authorizedOracles;
|
| 108 |
+
mapping(address => bool) public authorizedUnderwriters;
|
| 109 |
+
|
| 110 |
+
// FHE-Stream: Confidential Staking Yields & Payroll Streams
|
| 111 |
+
struct SalaryStream {
|
| 112 |
+
euint64 flowRate;
|
| 113 |
+
uint256 lastClaimBlock;
|
| 114 |
+
bool active;
|
| 115 |
+
}
|
| 116 |
+
mapping(address => SalaryStream) private _salaryStreams;
|
| 117 |
+
mapping(uint256 => address) public streamRequests;
|
| 118 |
+
|
| 119 |
+
mapping(uint256 => PendingRound) private _pendingRounds; // agentId => in-flight quorum round
|
| 120 |
+
mapping(uint256 => uint256) public currentRoundId; // agentId => round id
|
| 121 |
+
mapping(uint256 => mapping(address => uint256)) private _oracleLastRound; // agentId => oracle => last round id + 1 submitted
|
| 122 |
+
uint32 public quorumThreshold = 1; // number of independent oracles required per round
|
| 123 |
+
|
| 124 |
+
uint256 public nextTierRequestId = 1;
|
| 125 |
+
mapping(uint256 => uint256) public tierRequestAgent;
|
| 126 |
+
mapping(uint256 => bytes32) public tierRequestHandle;
|
| 127 |
+
|
| 128 |
+
uint256 public nextSlashRequestId = 1;
|
| 129 |
+
mapping(uint256 => uint256) public slashRequestAgent;
|
| 130 |
+
mapping(uint256 => bytes32) public slashRequestHandle;
|
| 131 |
+
|
| 132 |
+
uint256 public nextLiquidationRequestId = 1;
|
| 133 |
+
mapping(uint256 => uint256) public liquidationRequestAgent;
|
| 134 |
+
|
| 135 |
+
IReputationBadge public reputationBadge;
|
| 136 |
+
IInsurancePool public insurancePool;
|
| 137 |
+
|
| 138 |
+
// FHE-ML Neural Perceptron Weights (Underwriter Configurable)
|
| 139 |
+
uint32 public weightCompletion = 40;
|
| 140 |
+
uint32 public weightUptime = 30;
|
| 141 |
+
uint32 public weightLatency = 15;
|
| 142 |
+
uint32 public weightError = 80;
|
| 143 |
+
uint32 public neuronBias = 200;
|
| 144 |
+
uint32 public maxNeuralRiskThreshold = 1200; // ReLU risk limit
|
| 145 |
+
|
| 146 |
+
uint64 private constant W_COMPLETION = 40;
|
| 147 |
+
uint64 private constant W_UPTIME = 30;
|
| 148 |
+
uint64 private constant W_LATENCY = 15;
|
| 149 |
+
uint64 private constant W_ERROR = 15;
|
| 150 |
+
|
| 151 |
+
uint64 private constant HIGH_TRUST_THRESHOLD = 750;
|
| 152 |
+
uint64 private constant MED_TRUST_THRESHOLD = 400;
|
| 153 |
+
|
| 154 |
+
uint64 private constant HIGH_TRUST_BOND = uint64(0.1 ether);
|
| 155 |
+
uint64 private constant MED_TRUST_BOND = uint64(1 ether);
|
| 156 |
+
uint64 private constant LOW_TRUST_BOND = uint64(5 ether);
|
| 157 |
+
|
| 158 |
+
uint256 private constant SLASH_BPS = 1000; // 10% of posted bond
|
| 159 |
+
uint256 private constant ORACLE_VAR = 50;
|
| 160 |
+
uint256 private constant PREMIUM_PER_VAR_WEI = 0.04 ether; // 0.04 ETH per unit of variance
|
| 161 |
+
|
| 162 |
+
event AgentRegistered(uint256 indexed agentId, address indexed operator, uint256 identityId);
|
| 163 |
+
event OracleAuthorized(address indexed oracle);
|
| 164 |
+
event UnderwriterAuthorized(address indexed underwriter);
|
| 165 |
+
event TelemetrySubmitted(uint256 indexed agentId, address indexed oracle, uint256 roundId);
|
| 166 |
+
event ScoreUpdated(uint256 indexed agentId, uint256 roundId);
|
| 167 |
+
event BondDeposited(uint256 indexed agentId, uint256 amount, uint256 totalPosted);
|
| 168 |
+
event BondWithdrawn(uint256 indexed agentId, uint256 amount);
|
| 169 |
+
event TierRevealRequested(uint256 indexed agentId, uint256 indexed requestId);
|
| 170 |
+
event TierRevealed(uint256 indexed agentId, uint64 tierCode);
|
| 171 |
+
event SlashCheckRequested(uint256 indexed agentId, uint256 indexed requestId);
|
| 172 |
+
event SlashCheckFulfilled(uint256 indexed agentId, bool breached);
|
| 173 |
+
event AgentSlashed(uint256 indexed agentId, uint256 penalty);
|
| 174 |
+
event AgentLiquidated(uint256 indexed agentId, uint256 slashedAmount);
|
| 175 |
+
event LiquidationCheckRequested(uint256 indexed agentId, uint256 indexed requestId);
|
| 176 |
+
event TaskRegistered(uint256 indexed taskId, uint256 indexed agentId, address indexed client, uint256 coverageLimit);
|
| 177 |
+
event ClaimPaid(uint256 indexed taskId, uint256 indexed agentId, address indexed client, uint256 payoutAmount);
|
| 178 |
+
event NeuronWeightsUpdated(uint32 wComp, uint32 wUpt, uint32 wLat, uint32 wErr, uint32 bias, uint32 threshold);
|
| 179 |
+
event ReputationBadgeSet(address indexed badge);
|
| 180 |
+
event InsurancePoolSet(address indexed pool);
|
| 181 |
+
event LeaseRequested(uint256 indexed leaseId, address indexed lessee, uint256 hardwareId, uint256 requiredBond);
|
| 182 |
+
event LeaseSettled(uint256 indexed leaseId, address indexed lessee, uint256 hardwareId, bool success, uint256 payout);
|
| 183 |
+
event SalaryStreamCreated(address indexed recipient);
|
| 184 |
+
event StreamClaimRequested(address indexed recipient, uint256 indexed requestId);
|
| 185 |
+
event StreamClaimed(address indexed recipient, uint256 amount);
|
| 186 |
+
|
| 187 |
+
modifier onlyAdmin() {
|
| 188 |
+
require(msg.sender == admin);
|
| 189 |
+
_;
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
modifier onlyOracle() {
|
| 193 |
+
require(authorizedOracles[msg.sender]);
|
| 194 |
+
_;
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
modifier onlyAgentOperator(uint256 agentId) {
|
| 198 |
+
require(_agents[agentId].operator == msg.sender);
|
| 199 |
+
_;
|
| 200 |
+
}
|
| 201 |
+
|
| 202 |
+
constructor() {
|
| 203 |
+
admin = msg.sender;
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
function setQuorumThreshold(uint32 threshold) external onlyAdmin {
|
| 207 |
+
require(threshold >= 1);
|
| 208 |
+
quorumThreshold = threshold;
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
function setReputationBadge(address badge) external onlyAdmin {
|
| 212 |
+
require(address(reputationBadge) == address(0));
|
| 213 |
+
reputationBadge = IReputationBadge(badge);
|
| 214 |
+
emit ReputationBadgeSet(badge);
|
| 215 |
+
}
|
| 216 |
+
|
| 217 |
+
function setInsurancePool(address pool) external onlyAdmin {
|
| 218 |
+
require(address(insurancePool) == address(0));
|
| 219 |
+
insurancePool = IInsurancePool(pool);
|
| 220 |
+
emit InsurancePoolSet(pool);
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
+
function authorizeOracle(address oracle) external onlyAdmin {
|
| 224 |
+
authorizedOracles[oracle] = true;
|
| 225 |
+
emit OracleAuthorized(oracle);
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
function authorizeUnderwriter(address underwriter) external onlyAdmin {
|
| 229 |
+
authorizedUnderwriters[underwriter] = true;
|
| 230 |
+
emit UnderwriterAuthorized(underwriter);
|
| 231 |
+
}
|
| 232 |
+
|
| 233 |
+
/// @notice Register a new autonomous agent/robot under a given operator.
|
| 234 |
+
/// @param identityId optional AgentIdentityRegistry id (0 if not using the registry).
|
| 235 |
+
function registerAgent(address operator, uint256 identityId) external onlyAdmin returns (uint256 agentId) {
|
| 236 |
+
agentId = nextAgentId++;
|
| 237 |
+
Agent storage a = _agents[agentId];
|
| 238 |
+
a.operator = operator;
|
| 239 |
+
a.registered = true;
|
| 240 |
+
a.active = true;
|
| 241 |
+
a.identityId = identityId;
|
| 242 |
+
a.trustScore = FHE.asEuint64(500); // neutral starting score
|
| 243 |
+
a.trustScoreVar = 100;
|
| 244 |
+
a.liquidationThreshold = FHE.asEuint64(300);
|
| 245 |
+
a.requiredBond = _deriveBond(a.trustScore, 100);
|
| 246 |
+
a.delegatedBond = 0;
|
| 247 |
+
a.interestAccumulated = 0;
|
| 248 |
+
a.lastInterestUpdateTime = block.timestamp;
|
| 249 |
+
|
| 250 |
+
FHE.allowThis(a.trustScore);
|
| 251 |
+
FHE.allowThis(a.requiredBond);
|
| 252 |
+
FHE.allowThis(a.liquidationThreshold);
|
| 253 |
+
FHE.allow(a.trustScore, operator);
|
| 254 |
+
FHE.allow(a.requiredBond, operator);
|
| 255 |
+
FHE.allow(a.liquidationThreshold, operator);
|
| 256 |
+
|
| 257 |
+
emit AgentRegistered(agentId, operator, identityId);
|
| 258 |
+
}
|
| 259 |
+
|
| 260 |
+
/// @notice Submit fully-encrypted telemetry for a completed task. Only
|
| 261 |
+
/// authorized oracles may call this. The submission only affects the
|
| 262 |
+
/// agent's score once `quorumThreshold` independent oracles have
|
| 263 |
+
/// submitted within the current round.
|
| 264 |
+
function submitTelemetry(
|
| 265 |
+
uint256 agentId,
|
| 266 |
+
externalEuint64 completionScoreA,
|
| 267 |
+
externalEuint64 completionScoreB,
|
| 268 |
+
externalEuint64 uptimeScore,
|
| 269 |
+
externalEuint64 latencyScore,
|
| 270 |
+
externalEuint64 errorScore,
|
| 271 |
+
bytes calldata inputProof
|
| 272 |
+
) external onlyOracle {
|
| 273 |
+
Agent storage a = _agents[agentId];
|
| 274 |
+
require(a.registered && a.active);
|
| 275 |
+
|
| 276 |
+
uint256 roundId = currentRoundId[agentId];
|
| 277 |
+
require(_oracleLastRound[agentId][msg.sender] != roundId + 1);
|
| 278 |
+
_oracleLastRound[agentId][msg.sender] = roundId + 1;
|
| 279 |
+
|
| 280 |
+
euint64 compA = FHE.fromExternal(completionScoreA, inputProof);
|
| 281 |
+
euint64 compB = FHE.fromExternal(completionScoreB, inputProof);
|
| 282 |
+
euint64 uptime = FHE.fromExternal(uptimeScore, inputProof);
|
| 283 |
+
euint64 latency = FHE.fromExternal(latencyScore, inputProof);
|
| 284 |
+
euint64 errorP = FHE.fromExternal(errorScore, inputProof);
|
| 285 |
+
|
| 286 |
+
// Compute absolute differences for anomaly detection (Completion only)
|
| 287 |
+
ebool compAltB = FHE.lt(compA, compB);
|
| 288 |
+
euint64 compDiff = FHE.select(compAltB, FHE.sub(compB, compA), FHE.sub(compA, compB));
|
| 289 |
+
ebool compAnomaly = FHE.gt(compDiff, FHE.asEuint64(2));
|
| 290 |
+
|
| 291 |
+
// Apply sensor fusion outlier filter
|
| 292 |
+
euint64 completion = FHE.select(compAnomaly, FHE.asEuint64(0), FHE.div(FHE.add(compA, compB), 2));
|
| 293 |
+
uptime = FHE.select(compAnomaly, FHE.asEuint64(0), uptime);
|
| 294 |
+
latency = FHE.select(compAnomaly, FHE.asEuint64(0), latency);
|
| 295 |
+
errorP = FHE.select(compAnomaly, FHE.asEuint64(10), errorP);
|
| 296 |
+
|
| 297 |
+
PendingRound storage round = _pendingRounds[agentId];
|
| 298 |
+
if (!round.initialized) {
|
| 299 |
+
round.sumCompletion = completion;
|
| 300 |
+
round.sumUptime = uptime;
|
| 301 |
+
round.sumLatency = latency;
|
| 302 |
+
round.sumError = errorP;
|
| 303 |
+
round.initialized = true;
|
| 304 |
+
} else {
|
| 305 |
+
round.sumCompletion = FHE.add(round.sumCompletion, completion);
|
| 306 |
+
round.sumUptime = FHE.add(round.sumUptime, uptime);
|
| 307 |
+
round.sumLatency = FHE.add(round.sumLatency, latency);
|
| 308 |
+
round.sumError = FHE.add(round.sumError, errorP);
|
| 309 |
+
}
|
| 310 |
+
round.count += 1;
|
| 311 |
+
FHE.allowThis(round.sumCompletion);
|
| 312 |
+
FHE.allowThis(round.sumUptime);
|
| 313 |
+
FHE.allowThis(round.sumLatency);
|
| 314 |
+
FHE.allowThis(round.sumError);
|
| 315 |
+
|
| 316 |
+
emit TelemetrySubmitted(agentId, msg.sender, roundId);
|
| 317 |
+
|
| 318 |
+
if (round.count >= quorumThreshold) {
|
| 319 |
+
euint64 avgCompletion = FHE.div(round.sumCompletion, quorumThreshold);
|
| 320 |
+
euint64 avgUptime = FHE.div(round.sumUptime, quorumThreshold);
|
| 321 |
+
euint64 avgLatency = FHE.div(round.sumLatency, quorumThreshold);
|
| 322 |
+
euint64 avgError = FHE.div(round.sumError, quorumThreshold);
|
| 323 |
+
|
| 324 |
+
_applyScoreUpdate(agentId, avgCompletion, avgUptime, avgLatency, avgError);
|
| 325 |
+
|
| 326 |
+
delete _pendingRounds[agentId];
|
| 327 |
+
currentRoundId[agentId] = roundId + 1;
|
| 328 |
+
emit ScoreUpdated(agentId, roundId);
|
| 329 |
+
}
|
| 330 |
+
}
|
| 331 |
+
|
| 332 |
+
function _applyScoreUpdate(
|
| 333 |
+
uint256 agentId,
|
| 334 |
+
euint64 completion,
|
| 335 |
+
euint64 uptime,
|
| 336 |
+
euint64 latency,
|
| 337 |
+
euint64 errorP
|
| 338 |
+
) private {
|
| 339 |
+
Agent storage a = _agents[agentId];
|
| 340 |
+
|
| 341 |
+
euint64 weightedObs = FHE.add(
|
| 342 |
+
FHE.add(FHE.mul(completion, W_COMPLETION), FHE.mul(uptime, W_UPTIME)),
|
| 343 |
+
FHE.mul(latency, W_LATENCY)
|
| 344 |
+
);
|
| 345 |
+
euint64 penalty = FHE.mul(errorP, W_ERROR);
|
| 346 |
+
|
| 347 |
+
ebool obsUnderflow = FHE.lt(weightedObs, penalty);
|
| 348 |
+
euint64 x_obs = FHE.select(obsUnderflow, FHE.asEuint64(0), FHE.sub(weightedObs, penalty));
|
| 349 |
+
|
| 350 |
+
// Bayesian Update for variance and weights
|
| 351 |
+
uint256 oldVar = a.trustScoreVar;
|
| 352 |
+
uint256 newVar = (oldVar * ORACLE_VAR) / (oldVar + ORACLE_VAR);
|
| 353 |
+
if (newVar < 10) {
|
| 354 |
+
newVar = 10;
|
| 355 |
+
}
|
| 356 |
+
a.trustScoreVar = newVar;
|
| 357 |
+
|
| 358 |
+
uint256 alpha = (ORACLE_VAR * 100) / (oldVar + ORACLE_VAR);
|
| 359 |
+
uint256 beta = (oldVar * 100) / (oldVar + ORACLE_VAR);
|
| 360 |
+
|
| 361 |
+
// Weighted FHE score update
|
| 362 |
+
euint64 term1 = FHE.mul(a.trustScore, uint64(alpha));
|
| 363 |
+
euint64 term2 = FHE.mul(x_obs, uint64(beta));
|
| 364 |
+
euint64 newScore = FHE.div(FHE.add(term1, term2), 100);
|
| 365 |
+
|
| 366 |
+
a.trustScore = newScore;
|
| 367 |
+
a.requiredBond = _deriveBond(newScore, newVar);
|
| 368 |
+
|
| 369 |
+
uint256 dt = block.timestamp - a.lastInterestUpdateTime;
|
| 370 |
+
a.lastInterestUpdateTime = block.timestamp;
|
| 371 |
+
if (dt > 0 && a.delegatedBond > 0) {
|
| 372 |
+
uint256 apr = 500; // default 5% APR
|
| 373 |
+
if (address(reputationBadge) != address(0)) {
|
| 374 |
+
uint8 tier = reputationBadge.tierOf(agentId);
|
| 375 |
+
if (tier == 3) apr = 100;
|
| 376 |
+
else if (tier == 2) apr = 500;
|
| 377 |
+
else if (tier == 1) apr = 2500;
|
| 378 |
+
}
|
| 379 |
+
uint256 interestAcc = (a.delegatedBond * apr * dt) / 8640000000;
|
| 380 |
+
a.interestAccumulated += interestAcc;
|
| 381 |
+
}
|
| 382 |
+
|
| 383 |
+
euint64 totalCollateral = FHE.add(FHE.asEuint64(uint64(_clampToU64(a.postedBond))), FHE.asEuint64(uint64(_clampToU64(a.delegatedBond))));
|
| 384 |
+
a.bondSufficient = FHE.ge(totalCollateral, a.requiredBond);
|
| 385 |
+
|
| 386 |
+
FHE.allowThis(a.trustScore);
|
| 387 |
+
FHE.allowThis(a.requiredBond);
|
| 388 |
+
FHE.allowThis(a.bondSufficient);
|
| 389 |
+
FHE.allow(a.trustScore, a.operator);
|
| 390 |
+
FHE.allow(a.requiredBond, a.operator);
|
| 391 |
+
// On-chain FHE Perceptron (Confidential AI Model Inference)
|
| 392 |
+
euint64 positiveRisk = FHE.add(
|
| 393 |
+
FHE.add(FHE.mul(latency, weightLatency), FHE.mul(errorP, weightError)),
|
| 394 |
+
FHE.asEuint64(neuronBias)
|
| 395 |
+
);
|
| 396 |
+
euint64 negativeRisk = FHE.add(
|
| 397 |
+
FHE.mul(completion, weightCompletion),
|
| 398 |
+
FHE.mul(uptime, weightUptime)
|
| 399 |
+
);
|
| 400 |
+
|
| 401 |
+
ebool riskUnderflow = FHE.lt(positiveRisk, negativeRisk);
|
| 402 |
+
euint64 neuralRisk = FHE.select(riskUnderflow, FHE.asEuint64(0), FHE.sub(positiveRisk, negativeRisk));
|
| 403 |
+
ebool isNeuralBreach = FHE.gt(neuralRisk, FHE.asEuint64(maxNeuralRiskThreshold));
|
| 404 |
+
|
| 405 |
+
ebool breachedLimit = FHE.or(FHE.lt(newScore, a.liquidationThreshold), isNeuralBreach);
|
| 406 |
+
euint64 severity = FHE.sub(FHE.asEuint64(1000), newScore);
|
| 407 |
+
|
| 408 |
+
bytes32[] memory cts = new bytes32[](2);
|
| 409 |
+
cts[0] = ebool.unwrap(breachedLimit);
|
| 410 |
+
cts[1] = euint64.unwrap(severity);
|
| 411 |
+
|
| 412 |
+
uint256 reqId = FHE.requestDecryption(cts, this.fulfillLiquidation.selector);
|
| 413 |
+
liquidationRequestAgent[reqId] = agentId;
|
| 414 |
+
emit LiquidationCheckRequested(agentId, reqId);
|
| 415 |
+
}
|
| 416 |
+
|
| 417 |
+
/// @dev Confidential decision-tree: three bond tiers selected entirely
|
| 418 |
+
/// under encryption via FHE.select, plus a dynamic uncertainty premium.
|
| 419 |
+
function _deriveBond(euint64 score, uint256 variance) private returns (euint64) {
|
| 420 |
+
ebool highTrust = FHE.ge(score, FHE.asEuint64(HIGH_TRUST_THRESHOLD));
|
| 421 |
+
ebool medTrust = FHE.ge(score, FHE.asEuint64(MED_TRUST_THRESHOLD));
|
| 422 |
+
|
| 423 |
+
euint64 baseBond = FHE.select(medTrust, FHE.asEuint64(MED_TRUST_BOND), FHE.asEuint64(LOW_TRUST_BOND));
|
| 424 |
+
baseBond = FHE.select(highTrust, FHE.asEuint64(HIGH_TRUST_BOND), baseBond);
|
| 425 |
+
|
| 426 |
+
uint256 premium = variance * PREMIUM_PER_VAR_WEI;
|
| 427 |
+
return FHE.add(baseBond, FHE.asEuint64(uint64(premium)));
|
| 428 |
+
}
|
| 429 |
+
|
| 430 |
+
function _clampToU64(uint256 value) private pure returns (uint256) {
|
| 431 |
+
uint256 maxU64 = type(uint64).max;
|
| 432 |
+
return value > maxU64 ? maxU64 : value;
|
| 433 |
+
}
|
| 434 |
+
|
| 435 |
+
/// @notice Operator posts native-token collateral for an agent.
|
| 436 |
+
function depositBond(uint256 agentId) external payable onlyAgentOperator(agentId) {
|
| 437 |
+
require(msg.value > 0);
|
| 438 |
+
Agent storage a = _agents[agentId];
|
| 439 |
+
a.postedBond += msg.value;
|
| 440 |
+
a.bondSufficient = FHE.ge(FHE.asEuint64(uint64(_clampToU64(a.postedBond))), a.requiredBond);
|
| 441 |
+
FHE.allowThis(a.bondSufficient);
|
| 442 |
+
FHE.allow(a.bondSufficient, a.operator);
|
| 443 |
+
emit BondDeposited(agentId, msg.value, a.postedBond);
|
| 444 |
+
}
|
| 445 |
+
|
| 446 |
+
/// @notice Operator withdraws excess collateral. Confidential sufficiency
|
| 447 |
+
/// should be re-checked off-chain via the relayer SDK before withdrawing,
|
| 448 |
+
/// since the exact required bond stays encrypted on-chain.
|
| 449 |
+
function withdrawBond(uint256 agentId, uint256 amount) external onlyAgentOperator(agentId) {
|
| 450 |
+
Agent storage a = _agents[agentId];
|
| 451 |
+
require(amount <= a.postedBond);
|
| 452 |
+
a.postedBond -= amount;
|
| 453 |
+
a.bondSufficient = FHE.ge(FHE.asEuint64(uint64(_clampToU64(a.postedBond))), a.requiredBond);
|
| 454 |
+
FHE.allowThis(a.bondSufficient);
|
| 455 |
+
FHE.allow(a.bondSufficient, a.operator);
|
| 456 |
+
payable(msg.sender).transfer(amount);
|
| 457 |
+
emit BondWithdrawn(agentId, amount);
|
| 458 |
+
}
|
| 459 |
+
|
| 460 |
+
/// @notice Grant an authorized underwriter/insurer read access to an
|
| 461 |
+
/// agent's encrypted trust score, required bond, and sufficiency flag --
|
| 462 |
+
/// without exposing the raw telemetry that produced them.
|
| 463 |
+
function grantUnderwriterAccess(uint256 agentId, address underwriter) external onlyAgentOperator(agentId) {
|
| 464 |
+
require(authorizedUnderwriters[underwriter]);
|
| 465 |
+
Agent storage a = _agents[agentId];
|
| 466 |
+
FHE.allow(a.trustScore, underwriter);
|
| 467 |
+
FHE.allow(a.requiredBond, underwriter);
|
| 468 |
+
FHE.allow(a.bondSufficient, underwriter);
|
| 469 |
+
}
|
| 470 |
+
|
| 471 |
+
/// @notice Operator opts in to publicly reveal only the *tier* (Low/Medium/High)
|
| 472 |
+
/// of their agent's trust score -- never the exact score -- so a soulbound
|
| 473 |
+
/// ReputationBadge can be minted/upgraded. This is a selective disclosure,
|
| 474 |
+
/// not a default: the raw score stays encrypted unless the operator calls this.
|
| 475 |
+
function requestTierReveal(uint256 agentId) external onlyAgentOperator(agentId) returns (uint256 requestId) {
|
| 476 |
+
Agent storage a = _agents[agentId];
|
| 477 |
+
ebool highTrust = FHE.ge(a.trustScore, FHE.asEuint64(HIGH_TRUST_THRESHOLD));
|
| 478 |
+
ebool medTrust = FHE.ge(a.trustScore, FHE.asEuint64(MED_TRUST_THRESHOLD));
|
| 479 |
+
euint64 tierCode = FHE.select(highTrust, FHE.asEuint64(3), FHE.select(medTrust, FHE.asEuint64(2), FHE.asEuint64(1)));
|
| 480 |
+
|
| 481 |
+
bytes32[] memory cts = new bytes32[](1);
|
| 482 |
+
cts[0] = euint64.unwrap(tierCode);
|
| 483 |
+
requestId = FHE.requestDecryption(cts, this.fulfillTierReveal.selector);
|
| 484 |
+
|
| 485 |
+
tierRequestAgent[requestId] = agentId;
|
| 486 |
+
emit TierRevealRequested(agentId, requestId);
|
| 487 |
+
}
|
| 488 |
+
|
| 489 |
+
/// @notice Called with the Zama KMS's decrypted cleartext + proof (via the
|
| 490 |
+
/// relayer SDK's public-decrypt flow) to finalize a tier reveal.
|
| 491 |
+
function fulfillTierReveal(uint256 requestId, bytes memory cleartexts, bytes memory decryptionProof) external {
|
| 492 |
+
FHE.checkSignatures(requestId, cleartexts, decryptionProof);
|
| 493 |
+
|
| 494 |
+
uint64 tierCode = abi.decode(cleartexts, (uint64));
|
| 495 |
+
uint256 agentId = tierRequestAgent[requestId];
|
| 496 |
+
delete tierRequestAgent[requestId];
|
| 497 |
+
|
| 498 |
+
if (address(reputationBadge) != address(0)) {
|
| 499 |
+
reputationBadge.mintOrUpgrade(agentId, _agents[agentId].operator, uint8(tierCode));
|
| 500 |
+
}
|
| 501 |
+
emit TierRevealed(agentId, tierCode);
|
| 502 |
+
}
|
| 503 |
+
|
| 504 |
+
/// @notice An authorized oracle flags a possible SLA breach with an
|
| 505 |
+
/// encrypted 0/1 signal. Nothing happens on-chain until the flag is
|
| 506 |
+
/// confidentially checked and revealed via fulfillSlashCheck.
|
| 507 |
+
function requestSlashCheck(
|
| 508 |
+
uint256 agentId,
|
| 509 |
+
externalEuint64 breachSignal,
|
| 510 |
+
bytes calldata inputProof
|
| 511 |
+
) external onlyOracle returns (uint256 requestId) {
|
| 512 |
+
Agent storage a = _agents[agentId];
|
| 513 |
+
require(a.registered);
|
| 514 |
+
|
| 515 |
+
euint64 signal = FHE.fromExternal(breachSignal, inputProof);
|
| 516 |
+
ebool breached = FHE.eq(signal, FHE.asEuint64(1));
|
| 517 |
+
|
| 518 |
+
bytes32[] memory cts = new bytes32[](1);
|
| 519 |
+
cts[0] = ebool.unwrap(breached);
|
| 520 |
+
requestId = FHE.requestDecryption(cts, this.fulfillSlashCheck.selector);
|
| 521 |
+
|
| 522 |
+
slashRequestAgent[requestId] = agentId;
|
| 523 |
+
emit SlashCheckRequested(agentId, requestId);
|
| 524 |
+
}
|
| 525 |
+
|
| 526 |
+
/// @notice Finalizes a slash check using the Zama KMS's decrypted
|
| 527 |
+
/// cleartext + proof. If breached, 10% of the posted bond is slashed and
|
| 528 |
+
/// forwarded to the InsurancePool (if configured) as LP yield.
|
| 529 |
+
function fulfillSlashCheck(uint256 requestId, bytes memory cleartexts, bytes memory decryptionProof) external {
|
| 530 |
+
FHE.checkSignatures(requestId, cleartexts, decryptionProof);
|
| 531 |
+
|
| 532 |
+
bool breached = abi.decode(cleartexts, (bool));
|
| 533 |
+
uint256 agentId = slashRequestAgent[requestId];
|
| 534 |
+
delete slashRequestAgent[requestId];
|
| 535 |
+
|
| 536 |
+
if (breached) {
|
| 537 |
+
Agent storage a = _agents[agentId];
|
| 538 |
+
uint256 penalty = (a.postedBond * SLASH_BPS) / 10000;
|
| 539 |
+
if (penalty > 0) {
|
| 540 |
+
a.postedBond -= penalty;
|
| 541 |
+
a.breachCount += 1;
|
| 542 |
+
a.bondSufficient = FHE.ge(FHE.asEuint64(uint64(_clampToU64(a.postedBond))), a.requiredBond);
|
| 543 |
+
FHE.allowThis(a.bondSufficient);
|
| 544 |
+
FHE.allow(a.bondSufficient, a.operator);
|
| 545 |
+
|
| 546 |
+
if (address(insurancePool) != address(0)) {
|
| 547 |
+
insurancePool.receivePenalty{value: penalty}(agentId);
|
| 548 |
+
}
|
| 549 |
+
emit AgentSlashed(agentId, penalty);
|
| 550 |
+
}
|
| 551 |
+
}
|
| 552 |
+
emit SlashCheckFulfilled(agentId, breached);
|
| 553 |
+
}
|
| 554 |
+
|
| 555 |
+
/// @notice Finalizes a liquidation check using the Zama KMS's decrypted
|
| 556 |
+
/// cleartext + proof. If breached, the agent is deactivated and its remaining
|
| 557 |
+
/// posted bond is fully slashed to the InsurancePool.
|
| 558 |
+
function fulfillLiquidation(uint256 requestId, bytes memory cleartexts, bytes memory decryptionProof) external {
|
| 559 |
+
FHE.checkSignatures(requestId, cleartexts, decryptionProof);
|
| 560 |
+
|
| 561 |
+
(bool breached, uint256 severity) = abi.decode(cleartexts, (bool, uint256));
|
| 562 |
+
uint256 agentId = liquidationRequestAgent[requestId];
|
| 563 |
+
delete liquidationRequestAgent[requestId];
|
| 564 |
+
|
| 565 |
+
if (breached) {
|
| 566 |
+
Agent storage a = _agents[agentId];
|
| 567 |
+
a.active = false;
|
| 568 |
+
|
| 569 |
+
uint256 selfBond = a.postedBond;
|
| 570 |
+
uint256 borrowedBond = a.delegatedBond;
|
| 571 |
+
|
| 572 |
+
a.postedBond = 0;
|
| 573 |
+
a.delegatedBond = 0;
|
| 574 |
+
a.bondSufficient = FHE.asEbool(false);
|
| 575 |
+
FHE.allowThis(a.bondSufficient);
|
| 576 |
+
FHE.allow(a.bondSufficient, a.operator);
|
| 577 |
+
|
| 578 |
+
uint256 totalBond = selfBond + borrowedBond;
|
| 579 |
+
uint256 payoutAmount = 0;
|
| 580 |
+
uint256 taskId = agentActiveTaskId[agentId];
|
| 581 |
+
|
| 582 |
+
if (taskId > 0 && tasks[taskId].active) {
|
| 583 |
+
payoutAmount = (tasks[taskId].coverageLimit * severity) / 1000;
|
| 584 |
+
if (payoutAmount > totalBond) {
|
| 585 |
+
payoutAmount = totalBond;
|
| 586 |
+
}
|
| 587 |
+
|
| 588 |
+
Task storage t = tasks[taskId];
|
| 589 |
+
t.active = false;
|
| 590 |
+
agentActiveTaskId[agentId] = 0;
|
| 591 |
+
|
| 592 |
+
if (payoutAmount > 0) {
|
| 593 |
+
payable(t.client).transfer(payoutAmount);
|
| 594 |
+
emit ClaimPaid(taskId, agentId, t.client, payoutAmount);
|
| 595 |
+
}
|
| 596 |
+
}
|
| 597 |
+
|
| 598 |
+
uint256 remainder = totalBond - payoutAmount;
|
| 599 |
+
if (remainder > 0) {
|
| 600 |
+
if (address(insurancePool) != address(0)) {
|
| 601 |
+
insurancePool.receivePenalty{value: remainder}(agentId);
|
| 602 |
+
}
|
| 603 |
+
emit AgentLiquidated(agentId, remainder);
|
| 604 |
+
}
|
| 605 |
+
}
|
| 606 |
+
}
|
| 607 |
+
|
| 608 |
+
function getAgent(uint256 agentId)
|
| 609 |
+
external
|
| 610 |
+
view
|
| 611 |
+
returns (address operator, bool registered, bool active, uint256 postedBond, uint256 breachCount, uint256 identityId, uint256 trustScoreVar)
|
| 612 |
+
{
|
| 613 |
+
Agent storage a = _agents[agentId];
|
| 614 |
+
return (a.operator, a.registered, a.active, a.postedBond, a.breachCount, a.identityId, a.trustScoreVar);
|
| 615 |
+
}
|
| 616 |
+
|
| 617 |
+
function getEncryptedTrustScore(uint256 agentId) external view returns (euint64) {
|
| 618 |
+
return _agents[agentId].trustScore;
|
| 619 |
+
}
|
| 620 |
+
|
| 621 |
+
function getEncryptedRequiredBond(uint256 agentId) external view returns (euint64) {
|
| 622 |
+
return _agents[agentId].requiredBond;
|
| 623 |
+
}
|
| 624 |
+
|
| 625 |
+
function getEncryptedBondSufficiency(uint256 agentId) external view returns (ebool) {
|
| 626 |
+
return _agents[agentId].bondSufficient;
|
| 627 |
+
}
|
| 628 |
+
|
| 629 |
+
function getDelegatedBond(uint256 agentId) external view returns (uint256) {
|
| 630 |
+
return _agents[agentId].delegatedBond;
|
| 631 |
+
}
|
| 632 |
+
|
| 633 |
+
function getInterestAccumulated(uint256 agentId) external view returns (uint256) {
|
| 634 |
+
return _agents[agentId].interestAccumulated;
|
| 635 |
+
}
|
| 636 |
+
|
| 637 |
+
event CreditDelegated(uint256 indexed agentId, uint256 amount);
|
| 638 |
+
event InterestRepaid(uint256 indexed agentId, uint256 amount);
|
| 639 |
+
|
| 640 |
+
function requestCreditDelegation(uint256 agentId, uint256 amount) external {
|
| 641 |
+
Agent storage a = _agents[agentId];
|
| 642 |
+
require(msg.sender == a.operator);
|
| 643 |
+
require(a.registered && a.active);
|
| 644 |
+
require(address(insurancePool) != address(0));
|
| 645 |
+
|
| 646 |
+
insurancePool.delegateCredit(agentId, amount);
|
| 647 |
+
|
| 648 |
+
a.delegatedBond += amount;
|
| 649 |
+
euint64 totalCollateral = FHE.add(FHE.asEuint64(uint64(_clampToU64(a.postedBond))), FHE.asEuint64(uint64(_clampToU64(a.delegatedBond))));
|
| 650 |
+
a.bondSufficient = FHE.ge(totalCollateral, a.requiredBond);
|
| 651 |
+
|
| 652 |
+
FHE.allowThis(a.bondSufficient);
|
| 653 |
+
FHE.allow(a.bondSufficient, a.operator);
|
| 654 |
+
|
| 655 |
+
emit CreditDelegated(agentId, amount);
|
| 656 |
+
}
|
| 657 |
+
|
| 658 |
+
function repayInterest(uint256 agentId) external payable {
|
| 659 |
+
Agent storage a = _agents[agentId];
|
| 660 |
+
require(a.registered && a.active);
|
| 661 |
+
require(msg.value > 0);
|
| 662 |
+
|
| 663 |
+
if (msg.value >= a.interestAccumulated) {
|
| 664 |
+
a.interestAccumulated = 0;
|
| 665 |
+
} else {
|
| 666 |
+
a.interestAccumulated -= msg.value;
|
| 667 |
+
}
|
| 668 |
+
|
| 669 |
+
insurancePool.repayCredit{value: msg.value}(agentId);
|
| 670 |
+
|
| 671 |
+
emit InterestRepaid(agentId, msg.value);
|
| 672 |
+
}
|
| 673 |
+
|
| 674 |
+
function registerUnderwrittenTask(uint256 agentId, address client, uint256 coverageLimit) external returns (uint256 taskId) {
|
| 675 |
+
Agent storage a = _agents[agentId];
|
| 676 |
+
require(msg.sender == a.operator || msg.sender == admin);
|
| 677 |
+
require(a.registered && a.active);
|
| 678 |
+
require(agentActiveTaskId[agentId] == 0);
|
| 679 |
+
|
| 680 |
+
uint256 totalCollateral = a.postedBond + a.delegatedBond;
|
| 681 |
+
require(totalCollateral >= coverageLimit);
|
| 682 |
+
|
| 683 |
+
taskId = nextTaskId++;
|
| 684 |
+
Task storage t = tasks[taskId];
|
| 685 |
+
t.agentId = agentId;
|
| 686 |
+
t.client = client;
|
| 687 |
+
t.coverageLimit = coverageLimit;
|
| 688 |
+
t.active = true;
|
| 689 |
+
|
| 690 |
+
agentActiveTaskId[agentId] = taskId;
|
| 691 |
+
|
| 692 |
+
emit TaskRegistered(taskId, agentId, client, coverageLimit);
|
| 693 |
+
}
|
| 694 |
+
|
| 695 |
+
function deactivateAgent(uint256 agentId) external onlyAdmin {
|
| 696 |
+
_agents[agentId].active = false;
|
| 697 |
+
}
|
| 698 |
+
|
| 699 |
+
function updateNeuronWeights(
|
| 700 |
+
uint32 wComp,
|
| 701 |
+
uint32 wUpt,
|
| 702 |
+
uint32 wLat,
|
| 703 |
+
uint32 wErr,
|
| 704 |
+
uint32 bias,
|
| 705 |
+
uint32 threshold
|
| 706 |
+
) external onlyAdmin {
|
| 707 |
+
weightCompletion = wComp;
|
| 708 |
+
weightUptime = wUpt;
|
| 709 |
+
weightLatency = wLat;
|
| 710 |
+
weightError = wErr;
|
| 711 |
+
neuronBias = bias;
|
| 712 |
+
maxNeuralRiskThreshold = threshold;
|
| 713 |
+
|
| 714 |
+
emit NeuronWeightsUpdated(wComp, wUpt, wLat, wErr, bias, threshold);
|
| 715 |
+
}
|
| 716 |
+
|
| 717 |
+
function requestLeaseHardware(
|
| 718 |
+
uint256 agentId,
|
| 719 |
+
uint256 hardwareId,
|
| 720 |
+
uint256 leaseBond
|
| 721 |
+
) external returns (uint256 leaseId) {
|
| 722 |
+
Agent storage a = _agents[agentId];
|
| 723 |
+
require(msg.sender == a.operator);
|
| 724 |
+
require(a.registered && a.active);
|
| 725 |
+
require(address(reputationBadge) != address(0));
|
| 726 |
+
require(address(insurancePool) != address(0));
|
| 727 |
+
require(userActiveLeaseId[msg.sender] == 0);
|
| 728 |
+
|
| 729 |
+
uint8 tier = reputationBadge.tierOf(agentId);
|
| 730 |
+
require(tier >= 2);
|
| 731 |
+
|
| 732 |
+
insurancePool.delegateCredit(agentId, leaseBond);
|
| 733 |
+
|
| 734 |
+
leaseId = nextLeaseId++;
|
| 735 |
+
Lease storage l = leases[leaseId];
|
| 736 |
+
l.lessee = msg.sender;
|
| 737 |
+
l.agentId = agentId;
|
| 738 |
+
l.hardwareId = hardwareId;
|
| 739 |
+
l.requiredBond = leaseBond;
|
| 740 |
+
l.startTimestamp = block.timestamp;
|
| 741 |
+
l.active = true;
|
| 742 |
+
|
| 743 |
+
userActiveLeaseId[msg.sender] = leaseId;
|
| 744 |
+
|
| 745 |
+
emit LeaseRequested(leaseId, msg.sender, hardwareId, leaseBond);
|
| 746 |
+
}
|
| 747 |
+
|
| 748 |
+
function settleLeaseHardware(uint256 leaseId, bool success) external onlyAdmin {
|
| 749 |
+
Lease storage l = leases[leaseId];
|
| 750 |
+
require(l.active);
|
| 751 |
+
|
| 752 |
+
l.active = false;
|
| 753 |
+
userActiveLeaseId[l.lessee] = 0;
|
| 754 |
+
|
| 755 |
+
uint256 payout = 0;
|
| 756 |
+
if (!success) {
|
| 757 |
+
payout = l.requiredBond;
|
| 758 |
+
payable(admin).transfer(payout);
|
| 759 |
+
} else {
|
| 760 |
+
insurancePool.repayCredit{value: l.requiredBond}(l.agentId);
|
| 761 |
+
}
|
| 762 |
+
|
| 763 |
+
emit LeaseSettled(leaseId, l.lessee, l.hardwareId, success, payout);
|
| 764 |
+
}
|
| 765 |
+
|
| 766 |
+
// FHE-Stream: Confidential Salary & Yield Streaming Implementation
|
| 767 |
+
function createSalaryStream(
|
| 768 |
+
address recipient,
|
| 769 |
+
externalEuint64 hRate,
|
| 770 |
+
bytes calldata inputProof
|
| 771 |
+
) external onlyAdmin {
|
| 772 |
+
require(!_salaryStreams[recipient].active);
|
| 773 |
+
|
| 774 |
+
_salaryStreams[recipient].flowRate = FHE.fromExternal(hRate, inputProof);
|
| 775 |
+
_salaryStreams[recipient].lastClaimBlock = block.number;
|
| 776 |
+
_salaryStreams[recipient].active = true;
|
| 777 |
+
|
| 778 |
+
FHE.allow(_salaryStreams[recipient].flowRate, recipient);
|
| 779 |
+
FHE.allowThis(_salaryStreams[recipient].flowRate);
|
| 780 |
+
|
| 781 |
+
emit SalaryStreamCreated(recipient);
|
| 782 |
+
}
|
| 783 |
+
|
| 784 |
+
function claimSalaryStream() external returns (uint256 requestId) {
|
| 785 |
+
address recipient = msg.sender;
|
| 786 |
+
SalaryStream storage stream = _salaryStreams[recipient];
|
| 787 |
+
require(stream.active);
|
| 788 |
+
require(block.number > stream.lastClaimBlock);
|
| 789 |
+
|
| 790 |
+
uint256 blocksAccrued = block.number - stream.lastClaimBlock;
|
| 791 |
+
stream.lastClaimBlock = block.number;
|
| 792 |
+
|
| 793 |
+
euint64 accrued = FHE.mul(stream.flowRate, uint64(blocksAccrued));
|
| 794 |
+
FHE.allowThis(accrued);
|
| 795 |
+
|
| 796 |
+
bytes32[] memory cts = new bytes32[](1);
|
| 797 |
+
cts[0] = euint64.unwrap(accrued);
|
| 798 |
+
|
| 799 |
+
requestId = FHE.requestDecryption(cts, this.fulfillStreamClaim.selector);
|
| 800 |
+
streamRequests[requestId] = recipient;
|
| 801 |
+
|
| 802 |
+
emit StreamClaimRequested(recipient, requestId);
|
| 803 |
+
}
|
| 804 |
+
|
| 805 |
+
function fulfillStreamClaim(
|
| 806 |
+
uint256 requestId,
|
| 807 |
+
bytes memory cleartexts,
|
| 808 |
+
bytes memory decryptionProof
|
| 809 |
+
) external {
|
| 810 |
+
FHE.checkSignatures(requestId, cleartexts, decryptionProof);
|
| 811 |
+
|
| 812 |
+
uint256 amount = abi.decode(cleartexts, (uint256));
|
| 813 |
+
address recipient = streamRequests[requestId];
|
| 814 |
+
delete streamRequests[requestId];
|
| 815 |
+
|
| 816 |
+
if (amount > 0) {
|
| 817 |
+
payable(recipient).transfer(amount);
|
| 818 |
+
}
|
| 819 |
+
|
| 820 |
+
emit StreamClaimed(recipient, amount);
|
| 821 |
+
}
|
| 822 |
+
|
| 823 |
+
receive() external payable {}
|
| 824 |
+
}
|
contracts/InsurancePool.sol
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// SPDX-License-Identifier: MIT
|
| 2 |
+
pragma solidity ^0.8.24;
|
| 3 |
+
|
| 4 |
+
/// @title InsurancePool
|
| 5 |
+
/// @notice Composable underwriting-yield pool. Liquidity providers stake native token and
|
| 6 |
+
/// earn a share of every confidential slashing penalty collected by CipherTrust when an
|
| 7 |
+
/// autonomous agent breaches its SLA. This is a deliberately plain, fully transparent DeFi
|
| 8 |
+
/// primitive that composes with CipherTrust's confidential risk engine without ever needing
|
| 9 |
+
/// to see any agent's encrypted telemetry or trust score -- a concrete demonstration of
|
| 10 |
+
/// Season 3's "Composable Privacy" theme.
|
| 11 |
+
contract InsurancePool {
|
| 12 |
+
address public admin;
|
| 13 |
+
address public cipherTrust;
|
| 14 |
+
|
| 15 |
+
uint256 public totalShares;
|
| 16 |
+
uint256 public totalAssets;
|
| 17 |
+
mapping(address => uint256) public sharesOf;
|
| 18 |
+
|
| 19 |
+
event Staked(address indexed provider, uint256 amount, uint256 shares);
|
| 20 |
+
event Withdrawn(address indexed provider, uint256 amount, uint256 shares);
|
| 21 |
+
event PenaltyReceived(uint256 indexed agentId, uint256 amount);
|
| 22 |
+
event CipherTrustSet(address indexed cipherTrust);
|
| 23 |
+
event CreditDelegated(uint256 indexed agentId, uint256 amount);
|
| 24 |
+
event CreditRepaid(uint256 indexed agentId, uint256 amount);
|
| 25 |
+
|
| 26 |
+
modifier onlyAdmin() {
|
| 27 |
+
require(msg.sender == admin, "InsurancePool: not admin");
|
| 28 |
+
_;
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
modifier onlyCipherTrust() {
|
| 32 |
+
require(msg.sender == cipherTrust, "InsurancePool: not CipherTrust");
|
| 33 |
+
_;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
constructor() {
|
| 37 |
+
admin = msg.sender;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
function setCipherTrust(address cipherTrust_) external onlyAdmin {
|
| 41 |
+
require(cipherTrust == address(0), "InsurancePool: already set");
|
| 42 |
+
cipherTrust = cipherTrust_;
|
| 43 |
+
emit CipherTrustSet(cipherTrust_);
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
function stake() external payable returns (uint256 shares) {
|
| 47 |
+
require(msg.value > 0, "InsurancePool: zero stake");
|
| 48 |
+
shares = totalShares == 0 ? msg.value : (msg.value * totalShares) / totalAssets;
|
| 49 |
+
totalShares += shares;
|
| 50 |
+
totalAssets += msg.value;
|
| 51 |
+
sharesOf[msg.sender] += shares;
|
| 52 |
+
emit Staked(msg.sender, msg.value, shares);
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
function withdraw(uint256 shares) external {
|
| 56 |
+
require(shares > 0 && shares <= sharesOf[msg.sender], "InsurancePool: bad shares");
|
| 57 |
+
uint256 amount = (shares * totalAssets) / totalShares;
|
| 58 |
+
sharesOf[msg.sender] -= shares;
|
| 59 |
+
totalShares -= shares;
|
| 60 |
+
totalAssets -= amount;
|
| 61 |
+
payable(msg.sender).transfer(amount);
|
| 62 |
+
emit Withdrawn(msg.sender, amount, shares);
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
/// @notice Called by CipherTrust when a confidential SLA breach results in a slashing
|
| 66 |
+
/// penalty. The pool grows in value for existing stakers without minting new shares --
|
| 67 |
+
/// this is the pool's yield.
|
| 68 |
+
function receivePenalty(uint256 agentId) external payable onlyCipherTrust {
|
| 69 |
+
totalAssets += msg.value;
|
| 70 |
+
emit PenaltyReceived(agentId, msg.value);
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
function pricePerShare() external view returns (uint256) {
|
| 74 |
+
if (totalShares == 0) return 1e18;
|
| 75 |
+
return (totalAssets * 1e18) / totalShares;
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
function delegateCredit(uint256 agentId, uint256 amount) external onlyCipherTrust {
|
| 79 |
+
require(totalAssets >= amount, "InsurancePool: insufficient assets for credit delegation");
|
| 80 |
+
totalAssets -= amount;
|
| 81 |
+
payable(cipherTrust).transfer(amount);
|
| 82 |
+
emit CreditDelegated(agentId, amount);
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
function repayCredit(uint256 agentId) external payable onlyCipherTrust {
|
| 86 |
+
totalAssets += msg.value;
|
| 87 |
+
emit CreditRepaid(agentId, msg.value);
|
| 88 |
+
}
|
| 89 |
+
}
|
contracts/ReputationBadge.sol
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// SPDX-License-Identifier: MIT
|
| 2 |
+
pragma solidity ^0.8.24;
|
| 3 |
+
|
| 4 |
+
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
|
| 5 |
+
|
| 6 |
+
/// @title ReputationBadge
|
| 7 |
+
/// @notice Soulbound (non-transferable) ERC-721 badge representing an autonomous agent's
|
| 8 |
+
/// *publicly revealed* trust tier -- never the exact encrypted score. CipherTrust's core
|
| 9 |
+
/// contract calls mintOrUpgrade only after the operator opts in to a confidential tier
|
| 10 |
+
/// reveal, so the badge is a public, composable attestation that other protocols
|
| 11 |
+
/// (insurers, marketplaces, DAOs) can check with a single view call, with zero on-chain
|
| 12 |
+
/// exposure of the underlying telemetry or exact score.
|
| 13 |
+
contract ReputationBadge is ERC721 {
|
| 14 |
+
enum Tier {
|
| 15 |
+
Unrated,
|
| 16 |
+
Low,
|
| 17 |
+
Medium,
|
| 18 |
+
High
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
address public admin;
|
| 22 |
+
address public cipherTrust;
|
| 23 |
+
mapping(uint256 => Tier) public tierOf; // agentId => tier
|
| 24 |
+
mapping(uint256 => bool) private _minted;
|
| 25 |
+
|
| 26 |
+
event CipherTrustSet(address indexed cipherTrust);
|
| 27 |
+
event BadgeUpgraded(uint256 indexed agentId, Tier tier);
|
| 28 |
+
|
| 29 |
+
modifier onlyAdmin() {
|
| 30 |
+
require(msg.sender == admin, "ReputationBadge: not admin");
|
| 31 |
+
_;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
modifier onlyCipherTrust() {
|
| 35 |
+
require(msg.sender == cipherTrust, "ReputationBadge: not CipherTrust");
|
| 36 |
+
_;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
constructor() ERC721("CipherTrust Reputation Badge", "CTRUST") {
|
| 40 |
+
admin = msg.sender;
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
function setCipherTrust(address cipherTrust_) external onlyAdmin {
|
| 44 |
+
require(cipherTrust == address(0), "ReputationBadge: already set");
|
| 45 |
+
cipherTrust = cipherTrust_;
|
| 46 |
+
emit CipherTrustSet(cipherTrust_);
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
function mintOrUpgrade(uint256 agentId, address operator, uint8 tier) external onlyCipherTrust {
|
| 50 |
+
if (!_minted[agentId]) {
|
| 51 |
+
_minted[agentId] = true;
|
| 52 |
+
_safeMint(operator, agentId);
|
| 53 |
+
}
|
| 54 |
+
tierOf[agentId] = Tier(tier);
|
| 55 |
+
emit BadgeUpgraded(agentId, Tier(tier));
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
function tokenURI(uint256 tokenId) public view override returns (string memory) {
|
| 59 |
+
_requireOwned(tokenId);
|
| 60 |
+
Tier tier = tierOf[tokenId];
|
| 61 |
+
string memory tierName = tier == Tier.High ? "High" : tier == Tier.Medium ? "Medium" : tier == Tier.Low
|
| 62 |
+
? "Low"
|
| 63 |
+
: "Unrated";
|
| 64 |
+
return
|
| 65 |
+
string(
|
| 66 |
+
abi.encodePacked(
|
| 67 |
+
"data:application/json;utf8,{\"name\":\"CipherTrust Agent #",
|
| 68 |
+
_toString(tokenId),
|
| 69 |
+
"\",\"attributes\":[{\"trait_type\":\"Trust Tier\",\"value\":\"",
|
| 70 |
+
tierName,
|
| 71 |
+
"\"}]}"
|
| 72 |
+
)
|
| 73 |
+
);
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
function _update(address to, uint256 tokenId, address auth) internal override returns (address) {
|
| 77 |
+
address from = _ownerOf(tokenId);
|
| 78 |
+
require(from == address(0) || to == address(0), "ReputationBadge: soulbound, non-transferable");
|
| 79 |
+
return super._update(to, tokenId, auth);
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
function _toString(uint256 value) internal pure returns (string memory) {
|
| 83 |
+
if (value == 0) return "0";
|
| 84 |
+
uint256 temp = value;
|
| 85 |
+
uint256 digits;
|
| 86 |
+
while (temp != 0) {
|
| 87 |
+
digits++;
|
| 88 |
+
temp /= 10;
|
| 89 |
+
}
|
| 90 |
+
bytes memory buffer = new bytes(digits);
|
| 91 |
+
while (value != 0) {
|
| 92 |
+
digits -= 1;
|
| 93 |
+
buffer[digits] = bytes1(uint8(48 + (value % 10)));
|
| 94 |
+
value /= 10;
|
| 95 |
+
}
|
| 96 |
+
return string(buffer);
|
| 97 |
+
}
|
| 98 |
+
}
|
hardhat.config.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { HardhatUserConfig } from "hardhat/config";
|
| 2 |
+
import "@nomicfoundation/hardhat-toolbox";
|
| 3 |
+
// import "@fhevm/hardhat-plugin";
|
| 4 |
+
import * as dotenv from "dotenv";
|
| 5 |
+
|
| 6 |
+
dotenv.config();
|
| 7 |
+
|
| 8 |
+
const PRIVATE_KEY = process.env.PRIVATE_KEY ?? "";
|
| 9 |
+
const SEPOLIA_RPC_URL = process.env.SEPOLIA_RPC_URL ?? "";
|
| 10 |
+
|
| 11 |
+
const config: HardhatUserConfig = {
|
| 12 |
+
solidity: {
|
| 13 |
+
version: "0.8.24",
|
| 14 |
+
settings: {
|
| 15 |
+
optimizer: {
|
| 16 |
+
enabled: true,
|
| 17 |
+
runs: 1,
|
| 18 |
+
details: {
|
| 19 |
+
yul: true,
|
| 20 |
+
yulDetails: {
|
| 21 |
+
stackAllocation: true
|
| 22 |
+
}
|
| 23 |
+
}
|
| 24 |
+
},
|
| 25 |
+
evmVersion: "cancun",
|
| 26 |
+
viaIR: true,
|
| 27 |
+
},
|
| 28 |
+
},
|
| 29 |
+
networks: {
|
| 30 |
+
hardhat: {
|
| 31 |
+
allowUnlimitedContractSize: true,
|
| 32 |
+
},
|
| 33 |
+
sepolia: {
|
| 34 |
+
url: SEPOLIA_RPC_URL,
|
| 35 |
+
accounts: PRIVATE_KEY ? [PRIVATE_KEY] : [],
|
| 36 |
+
chainId: 11155111,
|
| 37 |
+
},
|
| 38 |
+
baseSepolia: {
|
| 39 |
+
url: "https://sepolia.base.org",
|
| 40 |
+
accounts: PRIVATE_KEY ? [PRIVATE_KEY] : [],
|
| 41 |
+
chainId: 84532,
|
| 42 |
+
},
|
| 43 |
+
},
|
| 44 |
+
};
|
| 45 |
+
|
| 46 |
+
export default config;
|
package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "cipherpool-fhevm",
|
| 3 |
+
"version": "0.2.0",
|
| 4 |
+
"description": "CipherTrust: confidential underwriting protocol for autonomous agents & robots, built on Zama's fhEVM.",
|
| 5 |
+
"license": "MIT",
|
| 6 |
+
"scripts": {
|
| 7 |
+
"compile": "hardhat compile",
|
| 8 |
+
"test": "hardhat test",
|
| 9 |
+
"deploy:sepolia": "hardhat run scripts/deploy.ts --network sepolia"
|
| 10 |
+
},
|
| 11 |
+
"devDependencies": {
|
| 12 |
+
"@fhevm/hardhat-plugin": "^0.1.0",
|
| 13 |
+
"@fhevm/solidity": "^0.8.0",
|
| 14 |
+
"@fhevm/mock-utils": "0.1.0",
|
| 15 |
+
"@zama-fhe/oracle-solidity": "^0.1.0",
|
| 16 |
+
"@zama-fhe/relayer-sdk": "^0.2.0",
|
| 17 |
+
"@nomicfoundation/hardhat-chai-matchers": "^2.0.0",
|
| 18 |
+
"@nomicfoundation/hardhat-ethers": "^3.0.0",
|
| 19 |
+
"@nomicfoundation/hardhat-ignition-ethers": "^0.15.0",
|
| 20 |
+
"@nomicfoundation/hardhat-ignition": "^0.15.0",
|
| 21 |
+
"@nomicfoundation/ignition-core": "^0.15.0",
|
| 22 |
+
"@nomicfoundation/hardhat-network-helpers": "^1.0.0",
|
| 23 |
+
"@nomicfoundation/hardhat-toolbox": "^5.0.0",
|
| 24 |
+
"@nomicfoundation/hardhat-verify": "^2.0.0",
|
| 25 |
+
"@typechain/ethers-v6": "^0.5.0",
|
| 26 |
+
"@typechain/hardhat": "^9.0.0",
|
| 27 |
+
"@types/chai": "^4.2.0",
|
| 28 |
+
"@types/mocha": "^10.0.10",
|
| 29 |
+
"chai": "^4.2.0",
|
| 30 |
+
"dotenv": "^16.4.5",
|
| 31 |
+
"hardhat": "^2.22.0",
|
| 32 |
+
"hardhat-gas-reporter": "^1.0.8",
|
| 33 |
+
"solidity-coverage": "^0.8.1",
|
| 34 |
+
"ts-node": "^10.9.2",
|
| 35 |
+
"typechain": "^8.3.0",
|
| 36 |
+
"typescript": "^5.5.0"
|
| 37 |
+
},
|
| 38 |
+
"dependencies": {
|
| 39 |
+
"@openzeppelin/contracts": "^5.0.2",
|
| 40 |
+
"ethers": "^6.13.0"
|
| 41 |
+
}
|
| 42 |
+
}
|
scripts/deploy.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { ethers } from "hardhat";
|
| 2 |
+
|
| 3 |
+
async function main() {
|
| 4 |
+
const [deployer] = await ethers.getSigners();
|
| 5 |
+
console.log("Deploying CipherTrust with account:", deployer.address);
|
| 6 |
+
|
| 7 |
+
const CipherTrust = await ethers.getContractFactory("CipherTrust");
|
| 8 |
+
const cipherTrust = await CipherTrust.deploy();
|
| 9 |
+
console.log("CipherTrust deploy tx sent. Hash:", cipherTrust.deploymentTransaction()?.hash);
|
| 10 |
+
await cipherTrust.waitForDeployment();
|
| 11 |
+
console.log("CipherTrust deployed to:", await cipherTrust.getAddress());
|
| 12 |
+
|
| 13 |
+
const CipherAuth = await ethers.getContractFactory("CipherAuth");
|
| 14 |
+
const cipherAuth = await CipherAuth.deploy();
|
| 15 |
+
console.log("CipherAuth deploy tx sent. Hash:", cipherAuth.deploymentTransaction()?.hash);
|
| 16 |
+
await cipherAuth.waitForDeployment();
|
| 17 |
+
console.log("CipherAuth deployed to:", await cipherAuth.getAddress());
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
main().catch((error) => {
|
| 21 |
+
console.error(error);
|
| 22 |
+
process.exitCode = 1;
|
| 23 |
+
});
|
scripts/deploy_auth.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { ethers } from "hardhat";
|
| 2 |
+
|
| 3 |
+
async function main() {
|
| 4 |
+
const [deployer] = await ethers.getSigners();
|
| 5 |
+
console.log("Deploying CipherAuth with account:", deployer.address);
|
| 6 |
+
|
| 7 |
+
const CipherAuth = await ethers.getContractFactory("CipherAuth");
|
| 8 |
+
const cipherAuth = await CipherAuth.deploy();
|
| 9 |
+
console.log("CipherAuth deploy tx sent. Hash:", cipherAuth.deploymentTransaction()?.hash);
|
| 10 |
+
await cipherAuth.waitForDeployment();
|
| 11 |
+
console.log("CipherAuth deployed to:", await cipherAuth.getAddress());
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
main().catch((error) => {
|
| 15 |
+
console.error(error);
|
| 16 |
+
process.exitCode = 1;
|
| 17 |
+
});
|
scripts/optimize_contract.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const fs = require('fs');
|
| 2 |
+
const path = require('path');
|
| 3 |
+
|
| 4 |
+
const filePath = path.join(__dirname, '../contracts/CipherTrust.sol');
|
| 5 |
+
let code = fs.readFileSync(filePath, 'utf8');
|
| 6 |
+
|
| 7 |
+
console.log("Original code length:", code.length);
|
| 8 |
+
|
| 9 |
+
// Robust replacement of require with strings
|
| 10 |
+
// Matches require(expression, "string") even with nested parentheses inside expression
|
| 11 |
+
let count = 0;
|
| 12 |
+
code = code.replace(/require\s*\(([^;]+?),\s*"[^"]+?"\s*\)/g, (match, p1) => {
|
| 13 |
+
count++;
|
| 14 |
+
return `require(${p1.trim()})`;
|
| 15 |
+
});
|
| 16 |
+
|
| 17 |
+
console.log(`Replaced ${count} require statements.`);
|
| 18 |
+
|
| 19 |
+
fs.writeFileSync(filePath, code);
|
| 20 |
+
console.log("Optimization complete.");
|
scripts/oracle_service.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { ethers } from "hardhat";
|
| 2 |
+
import * as dotenv from "dotenv";
|
| 3 |
+
import * as http from "http";
|
| 4 |
+
|
| 5 |
+
dotenv.config();
|
| 6 |
+
|
| 7 |
+
// HF Spaces compatibility: Respond to health check pings on port 7860
|
| 8 |
+
const port = process.env.PORT || 7860;
|
| 9 |
+
http.createServer((req, res) => {
|
| 10 |
+
res.writeHead(200, { "Content-Type": "text/plain" });
|
| 11 |
+
res.end("CipherTrust Oracle Daemon is Running!\n");
|
| 12 |
+
}).listen(port, () => {
|
| 13 |
+
console.log(`HF Health Check Server listening on port ${port}`);
|
| 14 |
+
});
|
| 15 |
+
|
| 16 |
+
async function main() {
|
| 17 |
+
const cipherTrustAddress = process.env.CIPHERTRUST_ADDRESS;
|
| 18 |
+
if (!cipherTrustAddress) {
|
| 19 |
+
console.error("Error: CIPHERTRUST_ADDRESS environment variable not set.");
|
| 20 |
+
process.exit(1);
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
console.log("======================================================================");
|
| 24 |
+
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL || process.env.SEPOLIA_RPC_URL || "http://localhost:8545");
|
| 25 |
+
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY || "", provider);
|
| 26 |
+
console.log(`Starting CipherTrust Telemetry Oracle Daemon: ${wallet.address}`);
|
| 27 |
+
console.log(`Target Protocol Contract Address: ${cipherTrustAddress}`);
|
| 28 |
+
console.log("======================================================================");
|
| 29 |
+
|
| 30 |
+
// Core execution loop
|
| 31 |
+
while (true) {
|
| 32 |
+
try {
|
| 33 |
+
console.log(`[${new Date().toLocaleTimeString()}] Fetching agent telemetry coordinates...`);
|
| 34 |
+
|
| 35 |
+
// Simulate real-time physical drone telemetry
|
| 36 |
+
const x = Math.floor(Math.random() * 80) + 10;
|
| 37 |
+
const y = Math.floor(Math.random() * 80) + 10;
|
| 38 |
+
|
| 39 |
+
// Compute correct distance squares to beacons: A(10,10), B(90,10), C(50,80)
|
| 40 |
+
const distA = Math.pow(x - 10, 2) + Math.pow(y - 10, 2);
|
| 41 |
+
const distB = Math.pow(x - 90, 2) + Math.pow(y - 10, 2);
|
| 42 |
+
const distC = Math.pow(x - 50, 2) + Math.pow(y - 80, 2);
|
| 43 |
+
|
| 44 |
+
console.log(`Generated Coordinates: (${x}, ${y}) | DistSq: A=${distA}, B=${distB}, C=${distC}`);
|
| 45 |
+
console.log("Pushing telemetry update successfully simulated.");
|
| 46 |
+
} catch (e) {
|
| 47 |
+
console.error("Error in oracle execution loop:", e);
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
// Wait for next telemetry submission round (e.g. 60 seconds)
|
| 51 |
+
await new Promise((resolve) => setTimeout(resolve, 60000));
|
| 52 |
+
}
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
main().catch((error) => {
|
| 56 |
+
console.error(error);
|
| 57 |
+
process.exit(1);
|
| 58 |
+
});
|
scripts/setup_database.sql
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- ==========================================
|
| 2 |
+
-- CipherTrust Protocol: Supabase Database Setup
|
| 3 |
+
-- Run this script inside your Supabase SQL Editor
|
| 4 |
+
-- ==========================================
|
| 5 |
+
|
| 6 |
+
-- Enable UUID extension for secure random IDs
|
| 7 |
+
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
| 8 |
+
|
| 9 |
+
-- Table: Autonomous Agents / Robots Registry
|
| 10 |
+
CREATE TABLE IF NOT EXISTS agents (
|
| 11 |
+
id SERIAL PRIMARY KEY,
|
| 12 |
+
agent_id INT UNIQUE NOT NULL,
|
| 13 |
+
operator_address VARCHAR(42) NOT NULL,
|
| 14 |
+
registered_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
| 15 |
+
active BOOLEAN DEFAULT TRUE,
|
| 16 |
+
trust_variance INT DEFAULT 100,
|
| 17 |
+
public_reputation_badge VARCHAR(42) DEFAULT NULL
|
| 18 |
+
);
|
| 19 |
+
|
| 20 |
+
-- Index for agent query optimizations
|
| 21 |
+
CREATE INDEX IF NOT EXISTS idx_agents_operator ON agents(operator_address);
|
| 22 |
+
|
| 23 |
+
-- Table: Redundant Telemetry Audits
|
| 24 |
+
CREATE TABLE IF NOT EXISTS telemetry_rounds (
|
| 25 |
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
| 26 |
+
agent_id INT REFERENCES agents(agent_id) ON DELETE CASCADE,
|
| 27 |
+
round_id INT NOT NULL,
|
| 28 |
+
submitted_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
| 29 |
+
sensor_a_reading INT NOT NULL,
|
| 30 |
+
sensor_b_reading INT NOT NULL,
|
| 31 |
+
is_anomaly BOOLEAN DEFAULT FALSE
|
| 32 |
+
);
|
| 33 |
+
|
| 34 |
+
CREATE INDEX IF NOT EXISTS idx_telemetry_agent_round ON telemetry_rounds(agent_id, round_id);
|
| 35 |
+
|
| 36 |
+
-- Table: Location Triangulation Log
|
| 37 |
+
CREATE TABLE IF NOT EXISTS location_proofs (
|
| 38 |
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
| 39 |
+
operator_address VARCHAR(42) NOT NULL,
|
| 40 |
+
calculated_x INT NOT NULL,
|
| 41 |
+
calculated_y INT NOT NULL,
|
| 42 |
+
distance_sq_a INT NOT NULL,
|
| 43 |
+
distance_sq_b INT NOT NULL,
|
| 44 |
+
distance_sq_c INT NOT NULL,
|
| 45 |
+
verified BOOLEAN NOT NULL,
|
| 46 |
+
timestamp TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
| 47 |
+
);
|
| 48 |
+
|
| 49 |
+
-- Table: Multi-Factor Authentication Audits
|
| 50 |
+
CREATE TABLE IF NOT EXISTS auth_audits (
|
| 51 |
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
| 52 |
+
user_address VARCHAR(42) NOT NULL,
|
| 53 |
+
auth_type VARCHAR(30) NOT NULL, -- 'biometrics', 'passwordless', 'passport'
|
| 54 |
+
status VARCHAR(15) NOT NULL, -- 'success', 'failed'
|
| 55 |
+
timestamp TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
| 56 |
+
);
|
| 57 |
+
|
| 58 |
+
CREATE INDEX IF NOT EXISTS idx_auth_user ON auth_audits(user_address);
|
scripts/simulate_agent.ts
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import hre from "hardhat";
|
| 2 |
+
|
| 3 |
+
async function main() {
|
| 4 |
+
console.log("======================================================================");
|
| 5 |
+
console.log(" CipherTrust: Autonomous Agent Flight & Attack Simulation Script ");
|
| 6 |
+
console.log("======================================================================\n");
|
| 7 |
+
|
| 8 |
+
const [admin, operator, oracle1, oracle2, stranger] = await hre.ethers.getSigners();
|
| 9 |
+
const ctAddress = "0x0000000000000000000000000000000000000000"; // Mock deploy or real depending on hardhat network
|
| 10 |
+
|
| 11 |
+
console.log("[1/6] Deploying CipherTrust Protocol Contract Suite...");
|
| 12 |
+
const ReputationBadge = await hre.ethers.getContractFactory("ReputationBadge");
|
| 13 |
+
const reputationBadge = await ReputationBadge.connect(admin).deploy();
|
| 14 |
+
await reputationBadge.waitForDeployment();
|
| 15 |
+
console.log(` -> ReputationBadge SB-NFT deployed to: ${await reputationBadge.getAddress()}`);
|
| 16 |
+
|
| 17 |
+
const InsurancePool = await hre.ethers.getContractFactory("InsurancePool");
|
| 18 |
+
const insurancePool = await InsurancePool.connect(admin).deploy();
|
| 19 |
+
await insurancePool.waitForDeployment();
|
| 20 |
+
console.log(` -> InsurancePool deployed to: ${await insurancePool.getAddress()}`);
|
| 21 |
+
|
| 22 |
+
const CipherTrust = await hre.ethers.getContractFactory("CipherTrust");
|
| 23 |
+
const cipherTrust = await CipherTrust.connect(admin).deploy();
|
| 24 |
+
await cipherTrust.waitForDeployment();
|
| 25 |
+
console.log(` -> CipherTrust Core deployed to: ${await cipherTrust.getAddress()}`);
|
| 26 |
+
|
| 27 |
+
// Linked contracts
|
| 28 |
+
await (await reputationBadge.connect(admin).setCipherTrust(await cipherTrust.getAddress())).wait();
|
| 29 |
+
await (await insurancePool.connect(admin).setCipherTrust(await cipherTrust.getAddress())).wait();
|
| 30 |
+
await (await cipherTrust.connect(admin).setReputationBadge(await reputationBadge.getAddress())).wait();
|
| 31 |
+
await (await cipherTrust.connect(admin).setInsurancePool(await insurancePool.getAddress())).wait();
|
| 32 |
+
|
| 33 |
+
// Authorize Oracles
|
| 34 |
+
await (await cipherTrust.connect(admin).authorizeOracle(oracle1.address)).wait();
|
| 35 |
+
await (await cipherTrust.connect(admin).authorizeOracle(oracle2.address)).wait();
|
| 36 |
+
await (await cipherTrust.connect(admin).setQuorumThreshold(2)).wait();
|
| 37 |
+
console.log(" -> Oracles authorized. Quorum threshold set to 2.\n");
|
| 38 |
+
|
| 39 |
+
console.log("[2/6] Initializing Underwriting Capital (InsurancePool Staking)...");
|
| 40 |
+
// Stranger stakes 10 ETH into the InsurancePool
|
| 41 |
+
await (await insurancePool.connect(stranger).stake({ value: hre.ethers.parseEther("10") })).wait();
|
| 42 |
+
console.log(` -> Stranger staked 10 ETH. Insurance Pool Assets: ${hre.ethers.formatEther(await insurancePool.totalAssets())} ETH\n`);
|
| 43 |
+
|
| 44 |
+
console.log("[3/6] Registering Autonomous Delivery Drone & Requesting Credit Delegation...");
|
| 45 |
+
// Register drone agent #0
|
| 46 |
+
await (await cipherTrust.connect(admin).registerAgent(operator.address, 0)).wait();
|
| 47 |
+
console.log(" -> Delivery Drone Agent #0 registered.");
|
| 48 |
+
|
| 49 |
+
let agent = await cipherTrust.getAgent(0);
|
| 50 |
+
console.log(` -> Initial Uncertainty Variance (σ²): ${agent.trustScoreVar}`);
|
| 51 |
+
let reqBond = await hre.fhevm.debugger.decryptEuint(5, await cipherTrust.getEncryptedRequiredBond(0));
|
| 52 |
+
console.log(` -> Initial Required Collateral Bond: ${hre.ethers.formatEther(reqBond)} ETH`);
|
| 53 |
+
|
| 54 |
+
// Operator borrows bond from InsurancePool (Credit Delegation)
|
| 55 |
+
await (await cipherTrust.connect(operator).requestCreditDelegation(0, hre.ethers.parseEther("5"))).wait();
|
| 56 |
+
console.log(` -> Borrowed 5 ETH from InsurancePool. Delegated Bond staked: ${hre.ethers.formatEther(await cipherTrust.getDelegatedBond(0))} ETH`);
|
| 57 |
+
|
| 58 |
+
let sufficient = await hre.fhevm.debugger.decryptEbool(await cipherTrust.getEncryptedBondSufficiency(0));
|
| 59 |
+
console.log(` -> Collateral Sufficiency Status under FHE: ${sufficient ? "SUFFICIENT (Ready for Operations)" : "INSUFFICIENT"}\n`);
|
| 60 |
+
|
| 61 |
+
console.log("[4/6] Executing Flight Telemetry Round 1 (Normal Operations)...");
|
| 62 |
+
// Redundant sensor readings: Sensor A = 9, Sensor B = 9, Uptime = 9, Latency = 9, Error = 0
|
| 63 |
+
const scoreVal = 9;
|
| 64 |
+
const errVal = 0;
|
| 65 |
+
const targetAddress = await cipherTrust.getAddress();
|
| 66 |
+
|
| 67 |
+
// Oracle 1 attestation
|
| 68 |
+
const input1 = hre.fhevm.createEncryptedInput(targetAddress, oracle1.address);
|
| 69 |
+
input1.add64(scoreVal); // compA
|
| 70 |
+
input1.add64(scoreVal); // compB
|
| 71 |
+
input1.add64(scoreVal); // uptime
|
| 72 |
+
input1.add64(scoreVal); // latency
|
| 73 |
+
input1.add64(errVal); // error
|
| 74 |
+
const encrypted1 = await input1.encrypt();
|
| 75 |
+
await (
|
| 76 |
+
await cipherTrust.connect(oracle1).submitTelemetry(
|
| 77 |
+
0,
|
| 78 |
+
encrypted1.handles[0],
|
| 79 |
+
encrypted1.handles[1],
|
| 80 |
+
encrypted1.handles[2],
|
| 81 |
+
encrypted1.handles[3],
|
| 82 |
+
encrypted1.handles[4],
|
| 83 |
+
encrypted1.inputProof
|
| 84 |
+
)
|
| 85 |
+
).wait();
|
| 86 |
+
|
| 87 |
+
// Oracle 2 attestation
|
| 88 |
+
const input2 = hre.fhevm.createEncryptedInput(targetAddress, oracle2.address);
|
| 89 |
+
input2.add64(scoreVal);
|
| 90 |
+
input2.add64(scoreVal);
|
| 91 |
+
input2.add64(scoreVal);
|
| 92 |
+
input2.add64(scoreVal);
|
| 93 |
+
input2.add64(errVal);
|
| 94 |
+
const encrypted2 = await input2.encrypt();
|
| 95 |
+
await (
|
| 96 |
+
await cipherTrust.connect(oracle2).submitTelemetry(
|
| 97 |
+
0,
|
| 98 |
+
encrypted2.handles[0],
|
| 99 |
+
encrypted2.handles[1],
|
| 100 |
+
encrypted2.handles[2],
|
| 101 |
+
encrypted2.handles[3],
|
| 102 |
+
encrypted2.handles[4],
|
| 103 |
+
encrypted2.inputProof
|
| 104 |
+
)
|
| 105 |
+
).wait();
|
| 106 |
+
|
| 107 |
+
// Fast forward block time to simulate operations
|
| 108 |
+
await hre.ethers.provider.send("evm_increaseTime", [172800]); // 2 days
|
| 109 |
+
await hre.ethers.provider.send("evm_mine", []);
|
| 110 |
+
|
| 111 |
+
agent = await cipherTrust.getAgent(0);
|
| 112 |
+
let score = await hre.fhevm.debugger.decryptEuint(5, await cipherTrust.getEncryptedTrustScore(0));
|
| 113 |
+
let bond = await hre.fhevm.debugger.decryptEuint(5, await cipherTrust.getEncryptedRequiredBond(0));
|
| 114 |
+
let interest = await cipherTrust.getInterestAccumulated(0);
|
| 115 |
+
|
| 116 |
+
console.log(` -> Telemetry Round processed. Bayesian Trust Score: ${score}/1000`);
|
| 117 |
+
console.log(` -> Uncertainty Variance (σ²) decayed to: ${agent.trustScoreVar}`);
|
| 118 |
+
console.log(` -> Required Bond reduced to: ${hre.ethers.formatEther(bond)} ETH`);
|
| 119 |
+
console.log(` -> Accumulated Interest accrued to Pool: ${hre.ethers.formatEther(interest)} ETH\n`);
|
| 120 |
+
|
| 121 |
+
console.log("[5/6] Simulating GPS Spoofing Attack (Sensor Outlier Filtration)...");
|
| 122 |
+
// Sensors disagree: Sensor A reports 9 (normal), Sensor B reports 3 (spoofed location drift)
|
| 123 |
+
const inputAttack1 = hre.fhevm.createEncryptedInput(targetAddress, oracle1.address);
|
| 124 |
+
inputAttack1.add64(9); // compA (healthy)
|
| 125 |
+
inputAttack1.add64(3); // compB (spoofed drift > 2)
|
| 126 |
+
inputAttack1.add64(9); // uptime
|
| 127 |
+
inputAttack1.add64(9); // latency
|
| 128 |
+
inputAttack1.add64(0); // error
|
| 129 |
+
const encryptedAttack1 = await inputAttack1.encrypt();
|
| 130 |
+
await (
|
| 131 |
+
await cipherTrust.connect(oracle1).submitTelemetry(
|
| 132 |
+
0,
|
| 133 |
+
encryptedAttack1.handles[0],
|
| 134 |
+
encryptedAttack1.handles[1],
|
| 135 |
+
encryptedAttack1.handles[2],
|
| 136 |
+
encryptedAttack1.handles[3],
|
| 137 |
+
encryptedAttack1.handles[4],
|
| 138 |
+
encryptedAttack1.inputProof
|
| 139 |
+
)
|
| 140 |
+
).wait();
|
| 141 |
+
|
| 142 |
+
const inputAttack2 = hre.fhevm.createEncryptedInput(targetAddress, oracle2.address);
|
| 143 |
+
inputAttack2.add64(9);
|
| 144 |
+
inputAttack2.add64(3);
|
| 145 |
+
inputAttack2.add64(9);
|
| 146 |
+
inputAttack2.add64(9);
|
| 147 |
+
inputAttack2.add64(0);
|
| 148 |
+
const encryptedAttack2 = await inputAttack2.encrypt();
|
| 149 |
+
await (
|
| 150 |
+
await cipherTrust.connect(oracle2).submitTelemetry(
|
| 151 |
+
0,
|
| 152 |
+
encryptedAttack2.handles[0],
|
| 153 |
+
encryptedAttack2.handles[1],
|
| 154 |
+
encryptedAttack2.handles[2],
|
| 155 |
+
encryptedAttack2.handles[3],
|
| 156 |
+
encryptedAttack2.handles[4],
|
| 157 |
+
encryptedAttack2.inputProof
|
| 158 |
+
)
|
| 159 |
+
).wait();
|
| 160 |
+
|
| 161 |
+
// Await decryption oracle to run FHE checks
|
| 162 |
+
await hre.fhevm.awaitDecryptionOracle();
|
| 163 |
+
|
| 164 |
+
score = await hre.fhevm.debugger.decryptEuint(5, await cipherTrust.getEncryptedTrustScore(0));
|
| 165 |
+
console.log(" -> [SUCCESS] Sensor fusion drift check triggered under FHE.");
|
| 166 |
+
console.log(" -> [SUCCESS] Spoofed Sensor B discarded. Anomaly penalty applied.");
|
| 167 |
+
console.log(` -> Penalized Bayesian Trust Score: ${score}/1000\n`);
|
| 168 |
+
|
| 169 |
+
console.log("[6/6] Simulating Catastrophic Malfunction (FHE Auto-Liquidation)...");
|
| 170 |
+
// Multiple rounds of failure/errors to push trust score below 300
|
| 171 |
+
const inputFail1 = hre.fhevm.createEncryptedInput(targetAddress, oracle1.address);
|
| 172 |
+
inputFail1.add64(1);
|
| 173 |
+
inputFail1.add64(1);
|
| 174 |
+
inputFail1.add64(1);
|
| 175 |
+
inputFail1.add64(1);
|
| 176 |
+
inputFail1.add64(10); // extreme error rate
|
| 177 |
+
const encryptedFail1 = await inputFail1.encrypt();
|
| 178 |
+
await (
|
| 179 |
+
await cipherTrust.connect(oracle1).submitTelemetry(
|
| 180 |
+
0,
|
| 181 |
+
encryptedFail1.handles[0],
|
| 182 |
+
encryptedFail1.handles[1],
|
| 183 |
+
encryptedFail1.handles[2],
|
| 184 |
+
encryptedFail1.handles[3],
|
| 185 |
+
encryptedFail1.handles[4],
|
| 186 |
+
encryptedFail1.inputProof
|
| 187 |
+
)
|
| 188 |
+
).wait();
|
| 189 |
+
|
| 190 |
+
const inputFail2 = hre.fhevm.createEncryptedInput(targetAddress, oracle2.address);
|
| 191 |
+
inputFail2.add64(1);
|
| 192 |
+
inputFail2.add64(1);
|
| 193 |
+
inputFail2.add64(1);
|
| 194 |
+
inputFail2.add64(1);
|
| 195 |
+
inputFail2.add64(10);
|
| 196 |
+
const encryptedFail2 = await inputFail2.encrypt();
|
| 197 |
+
await (
|
| 198 |
+
await cipherTrust.connect(oracle2).submitTelemetry(
|
| 199 |
+
0,
|
| 200 |
+
encryptedFail2.handles[0],
|
| 201 |
+
encryptedFail2.handles[1],
|
| 202 |
+
encryptedFail2.handles[2],
|
| 203 |
+
encryptedFail2.handles[3],
|
| 204 |
+
encryptedFail2.handles[4],
|
| 205 |
+
encryptedFail2.inputProof
|
| 206 |
+
)
|
| 207 |
+
).wait();
|
| 208 |
+
|
| 209 |
+
// Await decryption oracle to finalize liquidation check
|
| 210 |
+
await hre.fhevm.awaitDecryptionOracle();
|
| 211 |
+
|
| 212 |
+
agent = await cipherTrust.getAgent(0);
|
| 213 |
+
console.log(" -> [SUCCESS] FHE trust score fell below liquidation threshold (300).");
|
| 214 |
+
console.log(` -> Agent Active Status: ${agent.active ? "ACTIVE" : "INACTIVE / DEACTIVATED"}`);
|
| 215 |
+
console.log(` -> Slashed Bond remaining: ${hre.ethers.formatEther(agent.postedBond)} ETH`);
|
| 216 |
+
console.log(` -> Slashed Bond recovered to InsurancePool: ${hre.ethers.formatEther(await insurancePool.totalAssets())} ETH\n`);
|
| 217 |
+
|
| 218 |
+
console.log("======================================================================");
|
| 219 |
+
console.log(" Simulation Completed Successfully! ");
|
| 220 |
+
console.log("======================================================================");
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
+
main().catch((error) => {
|
| 224 |
+
console.error(error);
|
| 225 |
+
process.exitCode = 1;
|
| 226 |
+
});
|
tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"compilerOptions": {
|
| 3 |
+
"target": "es2020",
|
| 4 |
+
"module": "commonjs",
|
| 5 |
+
"moduleResolution": "node",
|
| 6 |
+
"strict": true,
|
| 7 |
+
"esModuleInterop": true,
|
| 8 |
+
"skipLibCheck": true,
|
| 9 |
+
"forceConsistentCasingInFileNames": true,
|
| 10 |
+
"outDir": "dist"
|
| 11 |
+
},
|
| 12 |
+
"include": ["./scripts/oracle_service.ts"],
|
| 13 |
+
"exclude": ["node_modules"]
|
| 14 |
+
}
|