Update database paths and fix foreign key references across coordinator API

- Change SQLite database path from `/home/oib/windsurf/aitbc/data/` to `/opt/data/`
- Fix foreign key references to use correct table names (users, wallets, gpu_registry)
- Replace governance router with new governance and community routers
- Add multi-modal RL router to main application
- Simplify DEPLOYMENT_READINESS_REPORT.md to focus on production deployment status
- Update governance router with decentralized DAO voting
This commit is contained in:
oib
2026-02-26 19:32:06 +01:00
parent 1e2ea0bb9d
commit 7bb2905cca
89 changed files with 38245 additions and 1260 deletions

566
contracts/AIPowerRental.sol Normal file
View File

@@ -0,0 +1,566 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "./ZKReceiptVerifier.sol";
import "./Groth16Verifier.sol";
/**
* @title AI Power Rental Contract
* @dev Smart contract for AI compute power rental agreements with performance verification
* @notice Manages rental agreements between AI compute providers and consumers
*/
contract AIPowerRental is Ownable, ReentrancyGuard, Pausable {
// State variables
IERC20 public aitbcToken;
ZKReceiptVerifier public zkVerifier;
Groth16Verifier public groth16Verifier;
uint256 public agreementCounter;
uint256 public platformFeePercentage = 250; // 2.5% in basis points
uint256 public minRentalDuration = 3600; // 1 hour minimum
uint256 public maxRentalDuration = 86400 * 30; // 30 days maximum
// Structs
struct RentalAgreement {
uint256 agreementId;
address provider;
address consumer;
uint256 duration;
uint256 price;
uint256 startTime;
uint256 endTime;
uint256 platformFee;
RentalStatus status;
PerformanceMetrics performance;
string gpuModel;
uint256 computeUnits;
bytes32 performanceProof;
}
struct PerformanceMetrics {
uint256 responseTime;
uint256 accuracy;
uint256 availability;
uint256 computePower;
bool withinSLA;
uint256 lastUpdateTime;
}
struct DisputeInfo {
bool exists;
address initiator;
string reason;
uint256 disputeTime;
bool resolved;
uint256 resolutionAmount;
}
// Enums
enum RentalStatus {
Created,
Active,
Completed,
Disputed,
Cancelled,
Expired
}
// Mappings
mapping(uint256 => RentalAgreement) public rentalAgreements;
mapping(uint256 => DisputeInfo) public disputes;
mapping(address => uint256[]) public providerAgreements;
mapping(address => uint256[]) public consumerAgreements;
mapping(address => bool) public authorizedProviders;
mapping(address => bool) public authorizedConsumers;
// Events
event AgreementCreated(
uint256 indexed agreementId,
address indexed provider,
address indexed consumer,
uint256 duration,
uint256 price,
string gpuModel,
uint256 computeUnits
);
event AgreementStarted(
uint256 indexed agreementId,
uint256 startTime,
uint256 endTime
);
event AgreementCompleted(
uint256 indexed agreementId,
uint256 completionTime,
bool withinSLA
);
event PaymentProcessed(
uint256 indexed agreementId,
address indexed provider,
uint256 amount,
uint256 platformFee
);
event PerformanceSubmitted(
uint256 indexed agreementId,
uint256 responseTime,
uint256 accuracy,
uint256 availability,
bool withinSLA
);
event DisputeFiled(
uint256 indexed agreementId,
address indexed initiator,
string reason
);
event DisputeResolved(
uint256 indexed agreementId,
uint256 resolutionAmount,
bool resolvedInFavorOfProvider
);
event ProviderAuthorized(address indexed provider);
event ProviderRevoked(address indexed provider);
event ConsumerAuthorized(address indexed consumer);
event ConsumerRevoked(address indexed consumer);
// Modifiers
modifier onlyAuthorizedProvider() {
require(authorizedProviders[msg.sender], "Not authorized provider");
_;
}
modifier onlyAuthorizedConsumer() {
require(authorizedConsumers[msg.sender], "Not authorized consumer");
_;
}
modifier onlyParticipant(uint256 _agreementId) {
require(
rentalAgreements[_agreementId].provider == msg.sender ||
rentalAgreements[_agreementId].consumer == msg.sender,
"Not agreement participant"
);
_;
}
modifier agreementExists(uint256 _agreementId) {
require(_agreementId < agreementCounter, "Agreement does not exist");
_;
}
modifier validStatus(uint256 _agreementId, RentalStatus _requiredStatus) {
require(rentalAgreements[_agreementId].status == _requiredStatus, "Invalid agreement status");
_;
}
// Constructor
constructor(
address _aitbcToken,
address _zkVerifier,
address _groth16Verifier
) {
aitbcToken = IERC20(_aitbcToken);
zkVerifier = ZKReceiptVerifier(_zkVerifier);
groth16Verifier = Groth16Verifier(_groth16Verifier);
agreementCounter = 0;
}
/**
* @dev Creates a new rental agreement
* @param _provider Address of the compute provider
* @param _consumer Address of the compute consumer
* @param _duration Duration in seconds
* @param _price Total price in AITBC tokens
* @param _gpuModel GPU model being rented
* @param _computeUnits Amount of compute units
*/
function createRental(
address _provider,
address _consumer,
uint256 _duration,
uint256 _price,
string memory _gpuModel,
uint256 _computeUnits
) external onlyAuthorizedConsumer nonReentrant whenNotPaused returns (uint256) {
require(_duration >= minRentalDuration, "Duration too short");
require(_duration <= maxRentalDuration, "Duration too long");
require(_price > 0, "Price must be positive");
require(authorizedProviders[_provider], "Provider not authorized");
uint256 agreementId = agreementCounter++;
uint256 platformFee = (_price * platformFeePercentage) / 10000;
rentalAgreements[agreementId] = RentalAgreement({
agreementId: agreementId,
provider: _provider,
consumer: _consumer,
duration: _duration,
price: _price,
startTime: 0,
endTime: 0,
platformFee: platformFee,
status: RentalStatus.Created,
performance: PerformanceMetrics({
responseTime: 0,
accuracy: 0,
availability: 0,
computePower: 0,
withinSLA: false,
lastUpdateTime: 0
}),
gpuModel: _gpuModel,
computeUnits: _computeUnits,
performanceProof: bytes32(0)
});
providerAgreements[_provider].push(agreementId);
consumerAgreements[_consumer].push(agreementId);
emit AgreementCreated(
agreementId,
_provider,
_consumer,
_duration,
_price,
_gpuModel,
_computeUnits
);
return agreementId;
}
/**
* @dev Starts a rental agreement and locks payment
* @param _agreementId ID of the agreement to start
*/
function startRental(uint256 _agreementId)
external
agreementExists(_agreementId)
validStatus(_agreementId, RentalStatus.Created)
nonReentrant
{
RentalAgreement storage agreement = rentalAgreements[_agreementId];
require(msg.sender == agreement.consumer, "Only consumer can start");
uint256 totalAmount = agreement.price + agreement.platformFee;
// Transfer tokens from consumer to contract
require(
aitbcToken.transferFrom(msg.sender, address(this), totalAmount),
"Payment transfer failed"
);
agreement.startTime = block.timestamp;
agreement.endTime = block.timestamp + agreement.duration;
agreement.status = RentalStatus.Active;
emit AgreementStarted(_agreementId, agreement.startTime, agreement.endTime);
}
/**
* @dev Completes a rental agreement and processes payment
* @param _agreementId ID of the agreement to complete
*/
function completeRental(uint256 _agreementId)
external
agreementExists(_agreementId)
validStatus(_agreementId, RentalStatus.Active)
onlyParticipant(_agreementId)
nonReentrant
{
RentalAgreement storage agreement = rentalAgreements[_agreementId];
require(block.timestamp >= agreement.endTime, "Rental period not ended");
agreement.status = RentalStatus.Completed;
// Process payment to provider
uint256 providerAmount = agreement.price;
uint256 platformFeeAmount = agreement.platformFee;
if (providerAmount > 0) {
require(
aitbcToken.transfer(agreement.provider, providerAmount),
"Provider payment failed"
);
}
if (platformFeeAmount > 0) {
require(
aitbcToken.transfer(owner(), platformFeeAmount),
"Platform fee transfer failed"
);
}
emit PaymentProcessed(_agreementId, agreement.provider, providerAmount, platformFeeAmount);
emit AgreementCompleted(_agreementId, block.timestamp, agreement.performance.withinSLA);
}
/**
* @dev Files a dispute for a rental agreement
* @param _agreementId ID of the agreement
* @param _reason Reason for the dispute
*/
function disputeRental(uint256 _agreementId, string memory _reason)
external
agreementExists(_agreementId)
onlyParticipant(_agreementId)
nonReentrant
{
RentalAgreement storage agreement = rentalAgreements[_agreementId];
require(
agreement.status == RentalStatus.Active ||
agreement.status == RentalStatus.Completed,
"Cannot dispute this agreement"
);
require(!disputes[_agreementId].exists, "Dispute already exists");
disputes[_agreementId] = DisputeInfo({
exists: true,
initiator: msg.sender,
reason: _reason,
disputeTime: block.timestamp,
resolved: false,
resolutionAmount: 0
});
agreement.status = RentalStatus.Disputed;
emit DisputeFiled(_agreementId, msg.sender, _reason);
}
/**
* @dev Submits performance metrics for a rental agreement
* @param _agreementId ID of the agreement
* @param _responseTime Response time in milliseconds
* @param _accuracy Accuracy percentage (0-100)
* @param _availability Availability percentage (0-100)
* @param _computePower Compute power utilized
* @param _zkProof Zero-knowledge proof for performance verification
*/
function submitPerformance(
uint256 _agreementId,
uint256 _responseTime,
uint256 _accuracy,
uint256 _availability,
uint256 _computePower,
bytes memory _zkProof
) external agreementExists(_agreementId) onlyAuthorizedProvider {
RentalAgreement storage agreement = rentalAgreements[_agreementId];
require(agreement.status == RentalStatus.Active, "Agreement not active");
// Verify ZK proof
bool proofValid = zkVerifier.verifyPerformanceProof(
_agreementId,
_responseTime,
_accuracy,
_availability,
_computePower,
_zkProof
);
require(proofValid, "Invalid performance proof");
agreement.performance = PerformanceMetrics({
responseTime: _responseTime,
accuracy: _accuracy,
availability: _availability,
computePower: _computePower,
withinSLA: _calculateSLA(_responseTime, _accuracy, _availability),
lastUpdateTime: block.timestamp
});
agreement.performanceProof = keccak256(_zkProof);
emit PerformanceSubmitted(
_agreementId,
_responseTime,
_accuracy,
_availability,
agreement.performance.withinSLA
);
}
/**
* @dev Authorizes a provider to offer compute services
* @param _provider Address of the provider
*/
function authorizeProvider(address _provider) external onlyOwner {
authorizedProviders[_provider] = true;
emit ProviderAuthorized(_provider);
}
/**
* @dev Revokes provider authorization
* @param _provider Address of the provider
*/
function revokeProvider(address _provider) external onlyOwner {
authorizedProviders[_provider] = false;
emit ProviderRevoked(_provider);
}
/**
* @dev Authorizes a consumer to rent compute services
* @param _consumer Address of the consumer
*/
function authorizeConsumer(address _consumer) external onlyOwner {
authorizedConsumers[_consumer] = true;
emit ConsumerAuthorized(_consumer);
}
/**
* @dev Revokes consumer authorization
* @param _consumer Address of the consumer
*/
function revokeConsumer(address _consumer) external onlyOwner {
authorizedConsumers[_consumer] = false;
emit ConsumerRevoked(_consumer);
}
/**
* @dev Resolves a dispute
* @param _agreementId ID of the disputed agreement
* @param _resolutionAmount Amount to award to the winner
* @param _resolveInFavorOfProvider True if resolving in favor of provider
*/
function resolveDispute(
uint256 _agreementId,
uint256 _resolutionAmount,
bool _resolveInFavorOfProvider
) external onlyOwner agreementExists(_agreementId) {
require(disputes[_agreementId].exists, "No dispute exists");
require(!disputes[_agreementId].resolved, "Dispute already resolved");
RentalAgreement storage agreement = rentalAgreements[_agreementId];
disputes[_agreementId].resolved = true;
disputes[_agreementId].resolutionAmount = _resolutionAmount;
address winner = _resolveInFavorOfProvider ? agreement.provider : agreement.consumer;
if (_resolutionAmount > 0) {
require(
aitbcToken.transfer(winner, _resolutionAmount),
"Resolution payment failed"
);
}
emit DisputeResolved(_agreementId, _resolutionAmount, _resolveInFavorOfProvider);
}
/**
* @dev Cancels a rental agreement (only before it starts)
* @param _agreementId ID of the agreement to cancel
*/
function cancelRental(uint256 _agreementId)
external
agreementExists(_agreementId)
validStatus(_agreementId, RentalStatus.Created)
onlyParticipant(_agreementId)
nonReentrant
{
RentalAgreement storage agreement = rentalAgreements[_agreementId];
agreement.status = RentalStatus.Cancelled;
}
/**
* @dev Emergency pause function
*/
function pause() external onlyOwner {
_pause();
}
/**
* @dev Unpause function
*/
function unpause() external onlyOwner {
_unpause();
}
/**
* @dev Updates platform fee percentage
* @param _newFee New fee percentage in basis points
*/
function updatePlatformFee(uint256 _newFee) external onlyOwner {
require(_newFee <= 1000, "Fee too high"); // Max 10%
platformFeePercentage = _newFee;
}
// View functions
/**
* @dev Gets rental agreement details
* @param _agreementId ID of the agreement
*/
function getRentalAgreement(uint256 _agreementId)
external
view
agreementExists(_agreementId)
returns (RentalAgreement memory)
{
return rentalAgreements[_agreementId];
}
/**
* @dev Gets dispute information
* @param _agreementId ID of the agreement
*/
function getDisputeInfo(uint256 _agreementId)
external
view
agreementExists(_agreementId)
returns (DisputeInfo memory)
{
return disputes[_agreementId];
}
/**
* @dev Gets all agreements for a provider
* @param _provider Address of the provider
*/
function getProviderAgreements(address _provider)
external
view
returns (uint256[] memory)
{
return providerAgreements[_provider];
}
/**
* @dev Gets all agreements for a consumer
* @param _consumer Address of the consumer
*/
function getConsumerAgreements(address _consumer)
external
view
returns (uint256[] memory)
{
return consumerAgreements[_consumer];
}
/**
* @dev Calculates if performance meets SLA requirements
*/
function _calculateSLA(
uint256 _responseTime,
uint256 _accuracy,
uint256 _availability
) internal pure returns (bool) {
return _responseTime <= 5000 && // <= 5 seconds
_accuracy >= 95 && // >= 95% accuracy
_availability >= 99; // >= 99% availability
}
}

View File

@@ -0,0 +1,696 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "./AIPowerRental.sol";
/**
* @title AITBC Payment Processor
* @dev Advanced payment processing contract with escrow, automated releases, and dispute resolution
* @notice Handles AITBC token payments for AI power rental services
*/
contract AITBCPaymentProcessor is Ownable, ReentrancyGuard, Pausable {
// State variables
IERC20 public aitbcToken;
AIPowerRental public aiPowerRental;
uint256 public paymentCounter;
uint256 public platformFeePercentage = 250; // 2.5% in basis points
uint256 public disputeResolutionFee = 100; // 1% in basis points
uint256 public minPaymentAmount = 1e15; // 0.001 AITBC minimum
uint256 public maxPaymentAmount = 1e22; // 10,000 AITBC maximum
// Structs
struct Payment {
uint256 paymentId;
address from;
address to;
uint256 amount;
uint256 platformFee;
uint256 disputeFee;
PaymentStatus status;
uint256 releaseTime;
uint256 createdTime;
uint256 confirmedTime;
bytes32 agreementId;
string paymentPurpose;
ReleaseCondition releaseCondition;
bytes32 conditionHash;
}
struct EscrowAccount {
uint256 escrowId;
address depositor;
address beneficiary;
uint256 amount;
uint256 releaseTime;
bool isReleased;
bool isRefunded;
bytes32 releaseCondition;
uint256 createdTime;
EscrowType escrowType;
}
struct ScheduledPayment {
uint256 scheduleId;
uint256 paymentId;
uint256 nextReleaseTime;
uint256 releaseInterval;
uint256 totalReleases;
uint256 releasedCount;
bool isActive;
}
// Enums
enum PaymentStatus {
Created,
Confirmed,
HeldInEscrow,
Released,
Refunded,
Disputed,
Cancelled
}
enum EscrowType {
Standard,
PerformanceBased,
TimeBased,
Conditional
}
enum ReleaseCondition {
Immediate,
Manual,
Performance,
TimeBased,
DisputeResolution
}
// Mappings
mapping(uint256 => Payment) public payments;
mapping(uint256 => EscrowAccount) public escrowAccounts;
mapping(uint256 => ScheduledPayment) public scheduledPayments;
mapping(address => uint256[]) public senderPayments;
mapping(address => uint256[]) public recipientPayments;
mapping(bytes32 => uint256) public agreementPayments;
mapping(address => uint256) public userEscrowBalance;
mapping(address => bool) public authorizedPayees;
mapping(address => bool) public authorizedPayers;
// Events
event PaymentCreated(
uint256 indexed paymentId,
address indexed from,
address indexed to,
uint256 amount,
bytes32 agreementId,
string paymentPurpose
);
event PaymentConfirmed(
uint256 indexed paymentId,
uint256 confirmedTime,
bytes32 transactionHash
);
event PaymentReleased(
uint256 indexed paymentId,
address indexed to,
uint256 amount,
uint256 platformFee
);
event PaymentRefunded(
uint256 indexed paymentId,
address indexed to,
uint256 amount,
string reason
);
event EscrowCreated(
uint256 indexed escrowId,
address indexed depositor,
address indexed beneficiary,
uint256 amount,
EscrowType escrowType
);
event EscrowReleased(
uint256 indexed escrowId,
uint256 amount,
bytes32 conditionHash
);
event EscrowRefunded(
uint256 indexed escrowId,
address indexed depositor,
uint256 amount,
string reason
);
event ScheduledPaymentCreated(
uint256 indexed scheduleId,
uint256 indexed paymentId,
uint256 nextReleaseTime,
uint256 releaseInterval
);
event ScheduledPaymentReleased(
uint256 indexed scheduleId,
uint256 indexed paymentId,
uint256 releaseCount
);
event DisputeInitiated(
uint256 indexed paymentId,
address indexed initiator,
string reason
);
event DisputeResolved(
uint256 indexed paymentId,
uint256 resolutionAmount,
bool resolvedInFavorOfPayer
);
event PlatformFeeCollected(
uint256 indexed paymentId,
uint256 feeAmount,
address indexed collector
);
// Modifiers
modifier onlyAuthorizedPayer() {
require(authorizedPayers[msg.sender], "Not authorized payer");
_;
}
modifier onlyAuthorizedPayee() {
require(authorizedPayees[msg.sender], "Not authorized payee");
_;
}
modifier paymentExists(uint256 _paymentId) {
require(_paymentId < paymentCounter, "Payment does not exist");
_;
}
modifier validStatus(uint256 _paymentId, PaymentStatus _requiredStatus) {
require(payments[_paymentId].status == _requiredStatus, "Invalid payment status");
_;
}
modifier sufficientBalance(address _user, uint256 _amount) {
require(aitbcToken.balanceOf(_user) >= _amount, "Insufficient balance");
_;
}
modifier sufficientAllowance(address _user, uint256 _amount) {
require(aitbcToken.allowance(_user, address(this)) >= _amount, "Insufficient allowance");
_;
}
// Constructor
constructor(address _aitbcToken, address _aiPowerRental) {
aitbcToken = IERC20(_aitbcToken);
aiPowerRental = AIPowerRental(_aiPowerRental);
paymentCounter = 0;
}
/**
* @dev Creates a new payment
* @param _to Recipient address
* @param _amount Payment amount
* @param _agreementId Associated agreement ID
* @param _paymentPurpose Purpose of the payment
* @param _releaseCondition Release condition
*/
function createPayment(
address _to,
uint256 _amount,
bytes32 _agreementId,
string memory _paymentPurpose,
ReleaseCondition _releaseCondition
) external onlyAuthorizedPayer sufficientBalance(msg.sender, _amount) sufficientAllowance(msg.sender, _amount) nonReentrant whenNotPaused returns (uint256) {
require(_amount >= minPaymentAmount, "Amount below minimum");
require(_amount <= maxPaymentAmount, "Amount above maximum");
require(_to != address(0), "Invalid recipient");
require(authorizedPayees[_to], "Recipient not authorized");
uint256 paymentId = paymentCounter++;
uint256 platformFee = (_amount * platformFeePercentage) / 10000;
uint256 disputeFee = (_amount * disputeResolutionFee) / 10000;
uint256 totalAmount = _amount + platformFee + disputeFee;
payments[paymentId] = Payment({
paymentId: paymentId,
from: msg.sender,
to: _to,
amount: _amount,
platformFee: platformFee,
disputeFee: disputeFee,
status: PaymentStatus.Created,
releaseTime: 0,
createdTime: block.timestamp,
confirmedTime: 0,
agreementId: _agreementId,
paymentPurpose: _paymentPurpose,
releaseCondition: _releaseCondition,
conditionHash: bytes32(0)
});
senderPayments[msg.sender].push(paymentId);
recipientPayments[_to].push(paymentId);
if (_agreementId != bytes32(0)) {
agreementPayments[_agreementId] = paymentId;
}
// Transfer tokens to contract
require(
aitbcToken.transferFrom(msg.sender, address(this), totalAmount),
"Payment transfer failed"
);
emit PaymentCreated(paymentId, msg.sender, _to, _amount, _agreementId, _paymentPurpose);
return paymentId;
}
/**
* @dev Confirms a payment with transaction hash
* @param _paymentId ID of the payment
* @param _transactionHash Blockchain transaction hash
*/
function confirmPayment(uint256 _paymentId, bytes32 _transactionHash)
external
paymentExists(_paymentId)
validStatus(_paymentId, PaymentStatus.Created)
nonReentrant
{
Payment storage payment = payments[_paymentId];
require(msg.sender == payment.from, "Only payer can confirm");
payment.status = PaymentStatus.Confirmed;
payment.confirmedTime = block.timestamp;
payment.conditionHash = _transactionHash;
// Handle immediate release
if (payment.releaseCondition == ReleaseCondition.Immediate) {
_releasePayment(_paymentId);
} else if (payment.releaseCondition == ReleaseCondition.TimeBased) {
payment.status = PaymentStatus.HeldInEscrow;
payment.releaseTime = block.timestamp + 1 hours; // Default 1 hour hold
} else {
payment.status = PaymentStatus.HeldInEscrow;
}
emit PaymentConfirmed(_paymentId, block.timestamp, _transactionHash);
}
/**
* @dev Releases a payment to the recipient
* @param _paymentId ID of the payment
*/
function releasePayment(uint256 _paymentId)
external
paymentExists(_paymentId)
nonReentrant
{
Payment storage payment = payments[_paymentId];
require(
payment.status == PaymentStatus.Confirmed ||
payment.status == PaymentStatus.HeldInEscrow,
"Payment not ready for release"
);
if (payment.releaseCondition == ReleaseCondition.Manual) {
require(msg.sender == payment.from, "Only payer can release manually");
} else if (payment.releaseCondition == ReleaseCondition.TimeBased) {
require(block.timestamp >= payment.releaseTime, "Release time not reached");
}
_releasePayment(_paymentId);
}
/**
* @dev Creates an escrow account
* @param _beneficiary Beneficiary address
* @param _amount Amount to lock in escrow
* @param _releaseTime Release time (0 for no time limit)
* @param _escrowType Type of escrow
* @param _releaseCondition Release condition hash
*/
function createEscrow(
address _beneficiary,
uint256 _amount,
uint256 _releaseTime,
EscrowType _escrowType,
bytes32 _releaseCondition
) external onlyAuthorizedPayer sufficientBalance(msg.sender, _amount) sufficientAllowance(msg.sender, _amount) nonReentrant whenNotPaused returns (uint256) {
require(_beneficiary != address(0), "Invalid beneficiary");
require(_amount >= minPaymentAmount, "Amount below minimum");
uint256 escrowId = paymentCounter++;
escrowAccounts[escrowId] = EscrowAccount({
escrowId: escrowId,
depositor: msg.sender,
beneficiary: _beneficiary,
amount: _amount,
releaseTime: _releaseTime,
isReleased: false,
isRefunded: false,
releaseCondition: _releaseCondition,
createdTime: block.timestamp,
escrowType: _escrowType
});
// Transfer tokens to contract
require(
aitbcToken.transferFrom(msg.sender, address(this), _amount),
"Escrow transfer failed"
);
userEscrowBalance[msg.sender] += _amount;
emit EscrowCreated(escrowId, msg.sender, _beneficiary, _amount, _escrowType);
return escrowId;
}
/**
* @dev Releases escrow to beneficiary
* @param _escrowId ID of the escrow account
*/
function releaseEscrow(uint256 _escrowId)
external
nonReentrant
{
EscrowAccount storage escrow = escrowAccounts[_escrowId];
require(!escrow.isReleased, "Escrow already released");
require(!escrow.isRefunded, "Escrow already refunded");
require(
escrow.releaseTime == 0 || block.timestamp >= escrow.releaseTime,
"Release time not reached"
);
escrow.isReleased = true;
userEscrowBalance[escrow.depositor] -= escrow.amount;
require(
aitbcToken.transfer(escrow.beneficiary, escrow.amount),
"Escrow release failed"
);
emit EscrowReleased(_escrowId, escrow.amount, escrow.releaseCondition);
}
/**
* @dev Refunds escrow to depositor
* @param _escrowId ID of the escrow account
* @param _reason Reason for refund
*/
function refundEscrow(uint256 _escrowId, string memory _reason)
external
nonReentrant
{
EscrowAccount storage escrow = escrowAccounts[_escrowId];
require(!escrow.isReleased, "Escrow already released");
require(!escrow.isRefunded, "Escrow already refunded");
require(
msg.sender == escrow.depositor || msg.sender == owner(),
"Only depositor or owner can refund"
);
escrow.isRefunded = true;
userEscrowBalance[escrow.depositor] -= escrow.amount;
require(
aitbcToken.transfer(escrow.depositor, escrow.amount),
"Escrow refund failed"
);
emit EscrowRefunded(_escrowId, escrow.depositor, escrow.amount, _reason);
}
/**
* @dev Initiates a dispute for a payment
* @param _paymentId ID of the payment
* @param _reason Reason for dispute
*/
function initiateDispute(uint256 _paymentId, string memory _reason)
external
paymentExists(_paymentId)
nonReentrant
{
Payment storage payment = payments[_paymentId];
require(
payment.status == PaymentStatus.Confirmed ||
payment.status == PaymentStatus.HeldInEscrow,
"Cannot dispute this payment"
);
require(
msg.sender == payment.from || msg.sender == payment.to,
"Only payment participants can dispute"
);
payment.status = PaymentStatus.Disputed;
emit DisputeInitiated(_paymentId, msg.sender, _reason);
}
/**
* @dev Resolves a dispute
* @param _paymentId ID of the disputed payment
* @param _resolutionAmount Amount to award to the winner
* @param _resolveInFavorOfPayer True if resolving in favor of payer
*/
function resolveDispute(
uint256 _paymentId,
uint256 _resolutionAmount,
bool _resolveInFavorOfPayer
) external onlyOwner paymentExists(_paymentId) nonReentrant {
Payment storage payment = payments[_paymentId];
require(payment.status == PaymentStatus.Disputed, "Payment not disputed");
require(_resolutionAmount <= payment.amount, "Resolution amount too high");
address winner = _resolveInFavorOfPayer ? payment.from : payment.to;
address loser = _resolveInFavorOfPayer ? payment.to : payment.from;
// Calculate refund for loser
uint256 refundAmount = payment.amount - _resolutionAmount;
// Transfer resolution amount to winner
if (_resolutionAmount > 0) {
require(
aitbcToken.transfer(winner, _resolutionAmount),
"Resolution payment failed"
);
}
// Refund remaining amount to loser
if (refundAmount > 0) {
require(
aitbcToken.transfer(loser, refundAmount),
"Refund payment failed"
);
}
payment.status = PaymentStatus.Released;
emit DisputeResolved(_paymentId, _resolutionAmount, _resolveInFavorOfPayer);
}
/**
* @dev Claims platform fees
* @param _paymentId ID of the payment
*/
function claimPlatformFee(uint256 _paymentId)
external
onlyOwner
paymentExists(_paymentId)
nonReentrant
{
Payment storage payment = payments[_paymentId];
require(payment.status == PaymentStatus.Released, "Payment not released");
require(payment.platformFee > 0, "No platform fee to claim");
uint256 feeAmount = payment.platformFee;
payment.platformFee = 0;
require(
aitbcToken.transfer(owner(), feeAmount),
"Platform fee transfer failed"
);
emit PlatformFeeCollected(_paymentId, feeAmount, owner());
}
/**
* @dev Authorizes a payee
* @param _payee Address to authorize
*/
function authorizePayee(address _payee) external onlyOwner {
authorizedPayees[_payee] = true;
}
/**
* @dev Revokes payee authorization
* @param _payee Address to revoke
*/
function revokePayee(address _payee) external onlyOwner {
authorizedPayees[_payee] = false;
}
/**
* @dev Authorizes a payer
* @param _payer Address to authorize
*/
function authorizePayer(address _payer) external onlyOwner {
authorizedPayers[_payer] = true;
}
/**
* @dev Revokes payer authorization
* @param _payer Address to revoke
*/
function revokePayer(address _payer) external onlyOwner {
authorizedPayers[_payer] = false;
}
/**
* @dev Updates platform fee percentage
* @param _newFee New fee percentage in basis points
*/
function updatePlatformFee(uint256 _newFee) external onlyOwner {
require(_newFee <= 1000, "Fee too high"); // Max 10%
platformFeePercentage = _newFee;
}
/**
* @dev Emergency pause function
*/
function pause() external onlyOwner {
_pause();
}
/**
* @dev Unpause function
*/
function unpause() external onlyOwner {
_unpause();
}
// Internal functions
function _releasePayment(uint256 _paymentId) internal {
Payment storage payment = payments[_paymentId];
payment.status = PaymentStatus.Released;
// Transfer amount to recipient
require(
aitbcToken.transfer(payment.to, payment.amount),
"Payment transfer failed"
);
// Transfer platform fee to owner
if (payment.platformFee > 0) {
require(
aitbcToken.transfer(owner(), payment.platformFee),
"Platform fee transfer failed"
);
}
emit PaymentReleased(_paymentId, payment.to, payment.amount, payment.platformFee);
}
// View functions
/**
* @dev Gets payment details
* @param _paymentId ID of the payment
*/
function getPayment(uint256 _paymentId)
external
view
paymentExists(_paymentId)
returns (Payment memory)
{
return payments[_paymentId];
}
/**
* @dev Gets escrow account details
* @param _escrowId ID of the escrow account
*/
function getEscrowAccount(uint256 _escrowId)
external
view
returns (EscrowAccount memory)
{
return escrowAccounts[_escrowId];
}
/**
* @dev Gets all payments for a sender
* @param _sender Address of the sender
*/
function getSenderPayments(address _sender)
external
view
returns (uint256[] memory)
{
return senderPayments[_sender];
}
/**
* @dev Gets all payments for a recipient
* @param _recipient Address of the recipient
*/
function getRecipientPayments(address _recipient)
external
view
returns (uint256[] memory)
{
return recipientPayments[_recipient];
}
/**
* @dev Gets payment associated with an agreement
* @param _agreementId ID of the agreement
*/
function getAgreementPayment(bytes32 _agreementId)
external
view
returns (uint256)
{
return agreementPayments[_agreementId];
}
/**
* @dev Gets user's escrow balance
* @param _user Address of the user
*/
function getUserEscrowBalance(address _user)
external
view
returns (uint256)
{
return userEscrowBalance[_user];
}
}

View File

@@ -0,0 +1,730 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "./AIPowerRental.sol";
import "./AITBCPaymentProcessor.sol";
import "./PerformanceVerifier.sol";
/**
* @title Dispute Resolution
* @dev Advanced dispute resolution contract with automated arbitration and evidence verification
* @notice Handles disputes between AI service providers and consumers with fair resolution mechanisms
*/
contract DisputeResolution is Ownable, ReentrancyGuard, Pausable {
// State variables
AIPowerRental public aiPowerRental;
AITBCPaymentProcessor public paymentProcessor;
PerformanceVerifier public performanceVerifier;
uint256 public disputeCounter;
uint256 public arbitrationFeePercentage = 100; // 1% in basis points
uint256 public evidenceSubmissionPeriod = 3 days;
uint256 public arbitrationPeriod = 7 days;
uint256 public escalationThreshold = 3; // Number of disputes before escalation
uint256 public minArbitrators = 3;
uint256 public maxArbitrators = 5;
// Structs
struct Dispute {
uint256 disputeId;
uint256 agreementId;
address initiator;
address respondent;
DisputeStatus status;
DisputeType disputeType;
string reason;
bytes32 evidenceHash;
uint256 filingTime;
uint256 evidenceDeadline;
uint256 arbitrationDeadline;
uint256 resolutionAmount;
address winner;
string resolutionReason;
uint256 arbitratorCount;
bool isEscalated;
uint256 escalationLevel;
}
struct Evidence {
uint256 evidenceId;
uint256 disputeId;
address submitter;
string evidenceType;
string evidenceData;
bytes32 evidenceHash;
uint256 submissionTime;
bool isValid;
uint256 verificationScore;
address verifiedBy;
}
struct Arbitrator {
address arbitratorAddress;
bool isAuthorized;
uint256 reputationScore;
uint256 totalDisputes;
uint256 successfulResolutions;
uint256 lastActiveTime;
ArbitratorStatus status;
}
struct ArbitrationVote {
uint256 disputeId;
address arbitrator;
bool voteInFavorOfInitiator;
uint256 confidence;
string reasoning;
uint256 voteTime;
bool isValid;
}
struct EscalationRecord {
uint256 disputeId;
uint256 escalationLevel;
address escalatedBy;
string escalationReason;
uint256 escalationTime;
address[] assignedArbitrators;
}
// Enums
enum DisputeStatus {
Filed,
EvidenceSubmitted,
UnderReview,
ArbitrationInProgress,
Resolved,
Escalated,
Rejected,
Expired
}
enum DisputeType {
Performance,
Payment,
ServiceQuality,
Availability,
Other
}
enum ArbitratorStatus {
Active,
Inactive,
Suspended,
Retired
}
enum EvidenceType {
PerformanceMetrics,
Logs,
Screenshots,
Videos,
Documents,
Testimonials,
BlockchainProof,
ZKProof
}
// Mappings
mapping(uint256 => Dispute) public disputes;
mapping(uint256 => Evidence[]) public disputeEvidence;
mapping(uint256 => ArbitrationVote[]) public arbitrationVotes;
mapping(uint256 => EscalationRecord) public escalations;
mapping(address => Arbitrator) public arbitrators;
mapping(address => uint256[]) public arbitratorDisputes;
mapping(address => uint256[]) public userDisputes;
mapping(uint256 => uint256) public agreementDisputes;
mapping(address => bool) public authorizedArbitrators;
mapping(uint256 => mapping(address => bool)) public hasVoted;
// Arrays for tracking
address[] public authorizedArbitratorList;
uint256[] public activeDisputes;
// Events
event DisputeFiled(
uint256 indexed disputeId,
uint256 indexed agreementId,
address indexed initiator,
address respondent,
DisputeType disputeType,
string reason
);
event EvidenceSubmitted(
uint256 indexed disputeId,
uint256 indexed evidenceId,
address indexed submitter,
string evidenceType,
bytes32 evidenceHash
);
event EvidenceVerified(
uint256 indexed disputeId,
uint256 indexed evidenceId,
bool isValid,
uint256 verificationScore
);
event ArbitratorAssigned(
uint256 indexed disputeId,
address indexed arbitrator,
uint256 escalationLevel
);
event ArbitrationVoteSubmitted(
uint256 indexed disputeId,
address indexed arbitrator,
bool voteInFavorOfInitiator,
uint256 confidence
);
event DisputeResolved(
uint256 indexed disputeId,
address indexed winner,
uint256 resolutionAmount,
string resolutionReason
);
event DisputeEscalated(
uint256 indexed disputeId,
uint256 escalationLevel,
address indexed escalatedBy,
string escalationReason
);
event ArbitratorAuthorized(
address indexed arbitrator,
uint256 reputationScore
);
event ArbitratorRevoked(
address indexed arbitrator,
string reason
);
event ArbitrationFeeCollected(
uint256 indexed disputeId,
uint256 feeAmount,
address indexed collector
);
// Modifiers
modifier onlyAuthorizedArbitrator() {
require(authorizedArbitrators[msg.sender], "Not authorized arbitrator");
_;
}
modifier disputeExists(uint256 _disputeId) {
require(_disputeId < disputeCounter, "Dispute does not exist");
_;
}
modifier validStatus(uint256 _disputeId, DisputeStatus _requiredStatus) {
require(disputes[_disputeId].status == _requiredStatus, "Invalid dispute status");
_;
}
modifier onlyParticipant(uint256 _disputeId) {
require(
msg.sender == disputes[_disputeId].initiator ||
msg.sender == disputes[_disputeId].respondent,
"Not dispute participant"
);
_;
}
modifier withinDeadline(uint256 _deadline) {
require(block.timestamp <= _deadline, "Deadline passed");
_;
}
modifier hasNotVoted(uint256 _disputeId) {
require(!hasVoted[_disputeId][msg.sender], "Already voted");
_;
}
// Constructor
constructor(
address _aiPowerRental,
address _paymentProcessor,
address _performanceVerifier
) {
aiPowerRental = AIPowerRental(_aiPowerRental);
paymentProcessor = AITBCPaymentProcessor(_paymentProcessor);
performanceVerifier = PerformanceVerifier(_performanceVerifier);
disputeCounter = 0;
}
/**
* @dev Files a new dispute
* @param _agreementId ID of the agreement being disputed
* @param _respondent The other party in the dispute
* @param _disputeType Type of dispute
* @param _reason Reason for the dispute
* @param _evidenceHash Hash of initial evidence
*/
function fileDispute(
uint256 _agreementId,
address _respondent,
DisputeType _disputeType,
string memory _reason,
bytes32 _evidenceHash
) external nonReentrant whenNotPaused returns (uint256) {
require(_respondent != address(0), "Invalid respondent");
require(_respondent != msg.sender, "Cannot dispute yourself");
require(bytes(_reason).length > 0, "Reason required");
// Verify agreement exists and get participants
(, address provider, address consumer, , , , , , , ) = aiPowerRental.getRentalAgreement(_agreementId);
require(provider != address(0), "Invalid agreement");
// Verify caller is a participant
require(
msg.sender == provider || msg.sender == consumer,
"Not agreement participant"
);
// Verify respondent is the other participant
address otherParticipant = msg.sender == provider ? consumer : provider;
require(_respondent == otherParticipant, "Respondent not in agreement");
uint256 disputeId = disputeCounter++;
disputes[disputeId] = Dispute({
disputeId: disputeId,
agreementId: _agreementId,
initiator: msg.sender,
respondent: _respondent,
status: DisputeStatus.Filed,
disputeType: _disputeType,
reason: _reason,
evidenceHash: _evidenceHash,
filingTime: block.timestamp,
evidenceDeadline: block.timestamp + evidenceSubmissionPeriod,
arbitrationDeadline: block.timestamp + evidenceSubmissionPeriod + arbitrationPeriod,
resolutionAmount: 0,
winner: address(0),
resolutionReason: "",
arbitratorCount: 0,
isEscalated: false,
escalationLevel: 1
});
userDisputes[msg.sender].push(disputeId);
userDisputes[_respondent].push(disputeId);
agreementDisputes[_agreementId] = disputeId;
activeDisputes.push(disputeId);
emit DisputeFiled(disputeId, _agreementId, msg.sender, _respondent, _disputeType, _reason);
return disputeId;
}
/**
* @dev Submits evidence for a dispute
* @param _disputeId ID of the dispute
* @param _evidenceType Type of evidence
* @param _evidenceData Evidence data (can be IPFS hash, URL, etc.)
*/
function submitEvidence(
uint256 _disputeId,
string memory _evidenceType,
string memory _evidenceData
) external disputeExists(_disputeId) onlyParticipant(_disputeId) withinDeadline(disputes[_disputeId].evidenceDeadline) nonReentrant {
Dispute storage dispute = disputes[_disputeId];
require(dispute.status == DisputeStatus.Filed || dispute.status == DisputeStatus.EvidenceSubmitted, "Cannot submit evidence");
uint256 evidenceId = disputeEvidence[_disputeId].length;
bytes32 evidenceHash = keccak256(abi.encodePacked(_evidenceData, msg.sender, block.timestamp));
disputeEvidence[_disputeId].push(Evidence({
evidenceId: evidenceId,
disputeId: _disputeId,
submitter: msg.sender,
evidenceType: _evidenceType,
evidenceData: _evidenceData,
evidenceHash: evidenceHash,
submissionTime: block.timestamp,
isValid: false,
verificationScore: 0,
verifiedBy: address(0)
}));
dispute.status = DisputeStatus.EvidenceSubmitted;
emit EvidenceSubmitted(_disputeId, evidenceId, msg.sender, _evidenceType, evidenceHash);
}
/**
* @dev Verifies evidence submitted in a dispute
* @param _disputeId ID of the dispute
* @param _evidenceId ID of the evidence
* @param _isValid Whether the evidence is valid
* @param _verificationScore Verification score (0-100)
*/
function verifyEvidence(
uint256 _disputeId,
uint256 _evidenceId,
bool _isValid,
uint256 _verificationScore
) external onlyAuthorizedArbitrator disputeExists(_disputeId) nonReentrant {
require(_evidenceId < disputeEvidence[_disputeId].length, "Invalid evidence ID");
Evidence storage evidence = disputeEvidence[_disputeId][_evidenceId];
evidence.isValid = _isValid;
evidence.verificationScore = _verificationScore;
evidence.verifiedBy = msg.sender;
emit EvidenceVerified(_disputeId, _evidenceId, _isValid, _verificationScore);
}
/**
* @dev Assigns arbitrators to a dispute
* @param _disputeId ID of the dispute
* @param _arbitrators Array of arbitrator addresses
*/
function assignArbitrators(
uint256 _disputeId,
address[] memory _arbitrators
) external onlyOwner disputeExists(_disputeId) nonReentrant {
Dispute storage dispute = disputes[_disputeId];
require(_arbitrators.length >= minArbitrators && _arbitrators.length <= maxArbitrators, "Invalid arbitrator count");
for (uint256 i = 0; i < _arbitrators.length; i++) {
require(authorizedArbitrators[_arbitrators[i]], "Arbitrator not authorized");
require(_arbitrators[i] != dispute.initiator && _arbitrators[i] != dispute.respondent, "Conflict of interest");
}
dispute.arbitratorCount = _arbitrators.length;
dispute.status = DisputeStatus.ArbitrationInProgress;
for (uint256 i = 0; i < _arbitrators.length; i++) {
arbitratorDisputes[_arbitrators[i]].push(_disputeId);
emit ArbitratorAssigned(_disputeId, _arbitrators[i], dispute.escalationLevel);
}
}
/**
* @dev Submits arbitration vote
* @param _disputeId ID of the dispute
* @param _voteInFavorOfInitiator Vote for initiator
* @param _confidence Confidence level (0-100)
* @param _reasoning Reasoning for the vote
*/
function submitArbitrationVote(
uint256 _disputeId,
bool _voteInFavorOfInitiator,
uint256 _confidence,
string memory _reasoning
) external onlyAuthorizedArbitrator disputeExists(_disputeId) validStatus(_disputeId, DisputeStatus.ArbitrationInProgress) hasNotVoted(_disputeId) withinDeadline(disputes[_disputeId].arbitrationDeadline) nonReentrant {
Dispute storage dispute = disputes[_disputeId];
// Verify arbitrator is assigned to this dispute
bool isAssigned = false;
for (uint256 i = 0; i < arbitratorDisputes[msg.sender].length; i++) {
if (arbitratorDisputes[msg.sender][i] == _disputeId) {
isAssigned = true;
break;
}
}
require(isAssigned, "Arbitrator not assigned");
arbitrationVotes[_disputeId].push(ArbitrationVote({
disputeId: _disputeId,
arbitrator: msg.sender,
voteInFavorOfInitiator: _voteInFavorOfInitiator,
confidence: _confidence,
reasoning: _reasoning,
voteTime: block.timestamp,
isValid: true
}));
hasVoted[_disputeId][msg.sender] = true;
// Update arbitrator stats
Arbitrator storage arbitrator = arbitrators[msg.sender];
arbitrator.totalDisputes++;
arbitrator.lastActiveTime = block.timestamp;
emit ArbitrationVoteSubmitted(_disputeId, msg.sender, _voteInFavorOfInitiator, _confidence);
// Check if all arbitrators have voted
if (arbitrationVotes[_disputeId].length == dispute.arbitratorCount) {
_resolveDispute(_disputeId);
}
}
/**
* @dev Escalates a dispute to higher level
* @param _disputeId ID of the dispute
* @param _escalationReason Reason for escalation
*/
function escalateDispute(
uint256 _disputeId,
string memory _escalationReason
) external onlyOwner disputeExists(_disputeId) nonReentrant {
Dispute storage dispute = disputes[_disputeId];
require(dispute.status == DisputeStatus.Resolved, "Cannot escalate unresolved dispute");
require(dispute.escalationLevel < 3, "Max escalation level reached");
dispute.escalationLevel++;
dispute.isEscalated = true;
dispute.status = DisputeStatus.Escalated;
escalations[_disputeId] = EscalationRecord({
disputeId: _disputeId,
escalationLevel: dispute.escalationLevel,
escalatedBy: msg.sender,
escalationReason: _escalationReason,
escalationTime: block.timestamp,
assignedArbitrators: new address[](0)
});
emit DisputeEscalated(_disputeId, dispute.escalationLevel, msg.sender, _escalationReason);
}
/**
* @dev Authorizes an arbitrator
* @param _arbitrator Address of the arbitrator
* @param _reputationScore Initial reputation score
*/
function authorizeArbitrator(address _arbitrator, uint256 _reputationScore) external onlyOwner {
require(_arbitrator != address(0), "Invalid arbitrator address");
require(!authorizedArbitrators[_arbitrator], "Arbitrator already authorized");
authorizedArbitrators[_arbitrator] = true;
authorizedArbitratorList.push(_arbitrator);
arbitrators[_arbitrator] = Arbitrator({
arbitratorAddress: _arbitrator,
isAuthorized: true,
reputationScore: _reputationScore,
totalDisputes: 0,
successfulResolutions: 0,
lastActiveTime: block.timestamp,
status: ArbitratorStatus.Active
});
emit ArbitratorAuthorized(_arbitrator, _reputationScore);
}
/**
* @dev Revokes arbitrator authorization
* @param _arbitrator Address of the arbitrator
* @param _reason Reason for revocation
*/
function revokeArbitrator(address _arbitrator, string memory _reason) external onlyOwner {
require(authorizedArbitrators[_arbitrator], "Arbitrator not authorized");
authorizedArbitrators[_arbitrator] = false;
arbitrators[_arbitrator].status = ArbitratorStatus.Suspended;
emit ArbitratorRevoked(_arbitrator, _reason);
}
// Internal functions
function _resolveDispute(uint256 _disputeId) internal {
Dispute storage dispute = disputes[_disputeId];
ArbitrationVote[] storage votes = arbitrationVotes[_disputeId];
uint256 votesForInitiator = 0;
uint256 votesForRespondent = 0;
uint256 totalConfidence = 0;
uint256 weightedVotesForInitiator = 0;
// Calculate weighted votes
for (uint256 i = 0; i < votes.length; i++) {
ArbitrationVote storage vote = votes[i];
totalConfidence += vote.confidence;
if (vote.voteInFavorOfInitiator) {
votesForInitiator++;
weightedVotesForInitiator += vote.confidence;
} else {
votesForRespondent++;
}
}
// Determine winner based on weighted votes
bool initiatorWins = weightedVotesForInitiator > (totalConfidence / 2);
dispute.winner = initiatorWins ? dispute.initiator : dispute.respondent;
dispute.status = DisputeStatus.Resolved;
// Calculate resolution amount based on agreement
(, address provider, address consumer, uint256 duration, uint256 price, , , , , ) = aiPowerRental.getRentalAgreement(dispute.agreementId);
if (initiatorWins) {
dispute.resolutionAmount = price; // Full refund/compensation
} else {
dispute.resolutionAmount = 0; // No compensation
}
// Update arbitrator success rates
for (uint256 i = 0; i < votes.length; i++) {
ArbitrationVote storage vote = votes[i];
Arbitrator storage arbitrator = arbitrators[vote.arbitrator];
if ((vote.voteInFavorOfInitiator && initiatorWins) || (!vote.voteInFavorOfInitiator && !initiatorWins)) {
arbitrator.successfulResolutions++;
}
}
dispute.resolutionReason = initiatorWins ? "Evidence and reasoning support initiator" : "Evidence and reasoning support respondent";
emit DisputeResolved(_disputeId, dispute.winner, dispute.resolutionAmount, dispute.resolutionReason);
}
// View functions
/**
* @dev Gets dispute details
* @param _disputeId ID of the dispute
*/
function getDispute(uint256 _disputeId)
external
view
disputeExists(_disputeId)
returns (Dispute memory)
{
return disputes[_disputeId];
}
/**
* @dev Gets evidence for a dispute
* @param _disputeId ID of the dispute
*/
function getDisputeEvidence(uint256 _disputeId)
external
view
disputeExists(_disputeId)
returns (Evidence[] memory)
{
return disputeEvidence[_disputeId];
}
/**
* @dev Gets arbitration votes for a dispute
* @param _disputeId ID of the dispute
*/
function getArbitrationVotes(uint256 _disputeId)
external
view
disputeExists(_disputeId)
returns (ArbitrationVote[] memory)
{
return arbitrationVotes[_disputeId];
}
/**
* @dev Gets arbitrator information
* @param _arbitrator Address of the arbitrator
*/
function getArbitrator(address _arbitrator)
external
view
returns (Arbitrator memory)
{
return arbitrators[_arbitrator];
}
/**
* @dev Gets all disputes for a user
* @param _user Address of the user
*/
function getUserDisputes(address _user)
external
view
returns (uint256[] memory)
{
return userDisputes[_user];
}
/**
* @dev Gets all disputes for an arbitrator
* @param _arbitrator Address of the arbitrator
*/
function getArbitratorDisputes(address _arbitrator)
external
view
returns (uint256[] memory)
{
return arbitratorDisputes[_arbitrator];
}
/**
* @dev Gets all authorized arbitrators
*/
function getAuthorizedArbitrators()
external
view
returns (address[] memory)
{
address[] memory activeArbitrators = new address[](authorizedArbitratorList.length);
uint256 activeCount = 0;
for (uint256 i = 0; i < authorizedArbitratorList.length; i++) {
if (authorizedArbitrators[authorizedArbitratorList[i]]) {
activeArbitrators[activeCount] = authorizedArbitratorList[i];
activeCount++;
}
}
// Resize array to active count
assembly {
mstore(activeArbitrators, activeCount)
}
return activeArbitrators;
}
/**
* @dev Gets active disputes
*/
function getActiveDisputes()
external
view
returns (uint256[] memory)
{
uint256[] memory active = new uint256[](activeDisputes.length);
uint256 activeCount = 0;
for (uint256 i = 0; i < activeDisputes.length; i++) {
if (disputes[activeDisputes[i]].status != DisputeStatus.Resolved &&
disputes[activeDisputes[i]].status != DisputeStatus.Rejected) {
active[activeCount] = activeDisputes[i];
activeCount++;
}
}
// Resize array to active count
assembly {
mstore(active, activeCount)
}
return active;
}
/**
* @dev Emergency pause function
*/
function pause() external onlyOwner {
_pause();
}
/**
* @dev Unpause function
*/
function unpause() external onlyOwner {
_unpause();
}
}

View File

@@ -0,0 +1,757 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./AIPowerRental.sol";
import "./PerformanceVerifier.sol";
/**
* @title Dynamic Pricing
* @dev Advanced dynamic pricing contract with supply/demand analysis and automated price adjustment
* @notice Implements data-driven pricing for AI power marketplace with ZK-based verification
*/
contract DynamicPricing is Ownable, ReentrancyGuard, Pausable {
// State variables
AIPowerRental public aiPowerRental;
PerformanceVerifier public performanceVerifier;
IERC20 public aitbcToken;
uint256 public priceUpdateCounter;
uint256 public basePricePerHour = 1e16; // 0.01 AITBC per hour
uint256 public minPricePerHour = 1e15; // 0.001 AITBC minimum
uint256 public maxPricePerHour = 1e18; // 0.1 AITBC maximum
uint256 public priceVolatilityThreshold = 2000; // 20% in basis points
uint256 public priceUpdateInterval = 3600; // 1 hour
uint256 public marketDataRetentionPeriod = 7 days;
uint256 public smoothingFactor = 50; // 50% smoothing in basis points
uint256 public surgeMultiplier = 300; // 3x surge pricing max
uint256 public discountMultiplier = 50; // 50% minimum price
// Structs
struct MarketData {
uint256 totalSupply;
uint256 totalDemand;
uint256 activeProviders;
uint256 activeConsumers;
uint256 averagePrice;
uint256 priceVolatility;
uint256 utilizationRate;
uint256 lastUpdateTime;
uint256 totalVolume;
uint256 transactionCount;
uint256 averageResponseTime;
uint256 averageAccuracy;
uint256 marketSentiment;
bool isMarketActive;
}
struct PriceHistory {
uint256 timestamp;
uint256 price;
uint256 supply;
uint256 demand;
uint256 volume;
PriceChangeType changeType;
uint256 changePercentage;
}
struct ProviderPricing {
address provider;
uint256 currentPrice;
uint256 basePrice;
uint256 reputationScore;
uint256 utilizationRate;
uint256 performanceScore;
uint256 demandScore;
uint256 supplyScore;
uint256 lastUpdateTime;
PricingStrategy strategy;
uint256 priceAdjustmentFactor;
}
struct RegionalPricing {
string region;
uint256 regionalMultiplier;
uint256 localSupply;
uint256 localDemand;
uint256 averagePrice;
uint256 lastUpdateTime;
uint256 competitionLevel;
uint256 infrastructureCost;
}
struct DemandForecast {
uint256 forecastPeriod;
uint256 predictedDemand;
uint256 confidence;
uint256 forecastTime;
uint256 actualDemand;
uint256 forecastAccuracy;
}
struct PriceAlert {
uint256 alertId;
address subscriber;
PriceAlertType alertType;
uint256 thresholdPrice;
uint256 currentPrice;
bool isActive;
uint256 lastTriggered;
string notificationMethod;
}
// Enums
enum PriceChangeType {
Increase,
Decrease,
Stable,
Surge,
Discount
}
enum PricingStrategy {
Fixed,
Dynamic,
Competitive,
PerformanceBased,
TimeBased,
DemandBased
}
enum MarketCondition {
Oversupply,
Balanced,
Undersupply,
Surge,
Crash
}
enum PriceAlertType {
PriceAbove,
PriceBelow,
VolatilityHigh,
TrendChange
}
// Mappings
mapping(uint256 => MarketData) public marketDataHistory;
mapping(uint256 => PriceHistory[]) public priceHistory;
mapping(address => ProviderPricing) public providerPricing;
mapping(string => RegionalPricing) public regionalPricing;
mapping(uint256 => DemandForecast) public demandForecasts;
mapping(uint256 => PriceAlert) public priceAlerts;
mapping(address => uint256[]) public providerPriceHistory;
mapping(string => uint256[]) public regionalPriceHistory;
mapping(address => bool) public authorizedPriceOracles;
mapping(uint256 => bool) public isValidPriceUpdate;
// Arrays for tracking
string[] public supportedRegions;
uint256[] public activePriceAlerts;
uint256[] public recentPriceUpdates;
// Events
event MarketDataUpdated(
uint256 indexed timestamp,
uint256 totalSupply,
uint256 totalDemand,
uint256 averagePrice,
MarketCondition marketCondition
);
event PriceCalculated(
uint256 indexed timestamp,
uint256 newPrice,
uint256 oldPrice,
PriceChangeType changeType,
uint256 changePercentage
);
event ProviderPriceUpdated(
address indexed provider,
uint256 newPrice,
PricingStrategy strategy,
uint256 adjustmentFactor
);
event RegionalPriceUpdated(
string indexed region,
uint256 newMultiplier,
uint256 localSupply,
uint256 localDemand
);
event DemandForecastCreated(
uint256 indexed forecastPeriod,
uint256 predictedDemand,
uint256 confidence,
uint256 forecastTime
);
event PriceAlertTriggered(
uint256 indexed alertId,
address indexed subscriber,
PriceAlertType alertType,
uint256 currentPrice,
uint256 thresholdPrice
);
event SurgePricingActivated(
uint256 surgeMultiplier,
uint256 duration,
string reason
);
event DiscountPricingActivated(
uint256 discountMultiplier,
uint256 duration,
string reason
);
event MarketConditionChanged(
MarketCondition oldCondition,
MarketCondition newCondition,
uint256 timestamp
);
event PriceOracleAuthorized(
address indexed oracle,
uint256 reputationScore
);
event PriceOracleRevoked(
address indexed oracle,
string reason
);
// Modifiers
modifier onlyAuthorizedPriceOracle() {
require(authorizedPriceOracles[msg.sender], "Not authorized price oracle");
_;
}
modifier validPriceUpdate(uint256 _timestamp) {
require(block.timestamp - _timestamp <= priceUpdateInterval, "Price update too old");
_;
}
modifier validProvider(address _provider) {
require(_provider != address(0), "Invalid provider address");
_;
}
modifier validRegion(string memory _region) {
require(bytes(_region).length > 0, "Invalid region");
_;
}
// Constructor
constructor(
address _aiPowerRental,
address _performanceVerifier,
address _aitbcToken
) {
aiPowerRental = AIPowerRental(_aiPowerRental);
performanceVerifier = PerformanceVerifier(_performanceVerifier);
aitbcToken = IERC20(_aitbcToken);
priceUpdateCounter = 0;
// Initialize supported regions
supportedRegions.push("us-east");
supportedRegions.push("us-west");
supportedRegions.push("eu-central");
supportedRegions.push("eu-west");
supportedRegions.push("ap-southeast");
supportedRegions.push("ap-northeast");
}
/**
* @dev Updates market data and recalculates prices
* @param _totalSupply Total compute power supply
* @param _totalDemand Total compute power demand
* @param _activeProviders Number of active providers
* @param _activeConsumers Number of active consumers
* @param _totalVolume Total transaction volume
* @param _transactionCount Number of transactions
* @param _averageResponseTime Average response time
* @param _averageAccuracy Average accuracy
* @param _marketSentiment Market sentiment score (0-100)
*/
function updateMarketData(
uint256 _totalSupply,
uint256 _totalDemand,
uint256 _activeProviders,
uint256 _activeConsumers,
uint256 _totalVolume,
uint256 _transactionCount,
uint256 _averageResponseTime,
uint256 _averageAccuracy,
uint256 _marketSentiment
) external onlyAuthorizedPriceOracle nonReentrant whenNotPaused {
require(_totalSupply > 0, "Invalid supply");
require(_totalDemand > 0, "Invalid demand");
uint256 timestamp = block.timestamp;
uint256 priceUpdateId = priceUpdateCounter++;
// Calculate utilization rate
uint256 utilizationRate = (_totalDemand * 10000) / _totalSupply;
// Get previous market data for comparison
MarketData storage previousData = marketDataHistory[priceUpdateId > 0 ? priceUpdateId - 1 : 0];
// Calculate new average price
uint256 newAveragePrice = _calculateDynamicPrice(
_totalSupply,
_totalDemand,
utilizationRate,
_marketSentiment,
previousData.averagePrice
);
// Calculate price volatility
uint256 priceVolatility = 0;
if (previousData.averagePrice > 0) {
if (newAveragePrice > previousData.averagePrice) {
priceVolatility = ((newAveragePrice - previousData.averagePrice) * 10000) / previousData.averagePrice;
} else {
priceVolatility = ((previousData.averagePrice - newAveragePrice) * 10000) / previousData.averagePrice;
}
}
// Store market data
marketDataHistory[priceUpdateId] = MarketData({
totalSupply: _totalSupply,
totalDemand: _totalDemand,
activeProviders: _activeProviders,
activeConsumers: _activeConsumers,
averagePrice: newAveragePrice,
priceVolatility: priceVolatility,
utilizationRate: utilizationRate,
lastUpdateTime: timestamp,
totalVolume: _totalVolume,
transactionCount: _transactionCount,
averageResponseTime: _averageResponseTime,
averageAccuracy: _averageAccuracy,
marketSentiment: _marketSentiment,
isMarketActive: _activeProviders > 0 && _activeConsumers > 0
});
// Determine market condition
MarketCondition currentCondition = _determineMarketCondition(utilizationRate, priceVolatility);
// Store price history
PriceChangeType changeType = _determinePriceChangeType(previousData.averagePrice, newAveragePrice);
uint256 changePercentage = previousData.averagePrice > 0 ?
((newAveragePrice - previousData.averagePrice) * 10000) / previousData.averagePrice : 0;
priceHistory[priceUpdateId].push(PriceHistory({
timestamp: timestamp,
price: newAveragePrice,
supply: _totalSupply,
demand: _totalDemand,
volume: _totalVolume,
changeType: changeType,
changePercentage: changePercentage
}));
// Update provider prices
_updateProviderPrices(newAveragePrice, utilizationRate);
// Update regional prices
_updateRegionalPrices(_totalSupply, _totalDemand);
// Check price alerts
_checkPriceAlerts(newAveragePrice);
// Apply surge or discount pricing if needed
_applySpecialPricing(currentCondition, priceVolatility);
isValidPriceUpdate[priceUpdateId] = true;
recentPriceUpdates.push(priceUpdateId);
emit MarketDataUpdated(timestamp, _totalSupply, _totalDemand, newAveragePrice, currentCondition);
emit PriceCalculated(timestamp, newAveragePrice, previousData.averagePrice, changeType, changePercentage);
}
/**
* @dev Calculates dynamic price based on market conditions
* @param _supply Total supply
* @param _demand Total demand
* @param _utilizationRate Utilization rate in basis points
* @param _marketSentiment Market sentiment (0-100)
* @param _previousPrice Previous average price
*/
function _calculateDynamicPrice(
uint256 _supply,
uint256 _demand,
uint256 _utilizationRate,
uint256 _marketSentiment,
uint256 _previousPrice
) internal view returns (uint256) {
// Base price calculation
uint256 newPrice = basePricePerHour;
// Supply/demand adjustment
if (_demand > _supply) {
uint256 demandPremium = ((_demand - _supply) * 10000) / _supply;
newPrice = (newPrice * (10000 + demandPremium)) / 10000;
} else if (_supply > _demand) {
uint256 supplyDiscount = ((_supply - _demand) * 10000) / _supply;
newPrice = (newPrice * (10000 - supplyDiscount)) / 10000;
}
// Utilization rate adjustment
if (_utilizationRate > 8000) { // > 80% utilization
uint256 utilizationPremium = (_utilizationRate - 8000) / 2;
newPrice = (newPrice * (10000 + utilizationPremium)) / 10000;
} else if (_utilizationRate < 2000) { // < 20% utilization
uint256 utilizationDiscount = (2000 - _utilizationRate) / 4;
newPrice = (newPrice * (10000 - utilizationDiscount)) / 10000;
}
// Market sentiment adjustment
if (_marketSentiment > 70) { // High sentiment
newPrice = (newPrice * 10500) / 10000; // 5% premium
} else if (_marketSentiment < 30) { // Low sentiment
newPrice = (newPrice * 9500) / 10000; // 5% discount
}
// Smoothing with previous price
if (_previousPrice > 0) {
newPrice = (newPrice * (10000 - smoothingFactor) + _previousPrice * smoothingFactor) / 10000;
}
// Apply price bounds
if (newPrice < minPricePerHour) {
newPrice = minPricePerHour;
} else if (newPrice > maxPricePerHour) {
newPrice = maxPricePerHour;
}
return newPrice;
}
/**
* @dev Updates provider-specific pricing
* @param _marketAveragePrice Current market average price
* @param _marketUtilizationRate Market utilization rate
*/
function _updateProviderPrices(uint256 _marketAveragePrice, uint256 _marketUtilizationRate) internal {
// This would typically iterate through all active providers
// For now, we'll update based on provider performance and reputation
// Implementation would include:
// 1. Get provider performance metrics
// 2. Calculate provider-specific adjustments
// 3. Update provider pricing based on strategy
// 4. Emit ProviderPriceUpdated events
}
/**
* @dev Updates regional pricing
* @param _totalSupply Total supply
* @param _totalDemand Total demand
*/
function _updateRegionalPrices(uint256 _totalSupply, uint256 _totalDemand) internal {
for (uint256 i = 0; i < supportedRegions.length; i++) {
string memory region = supportedRegions[i];
RegionalPricing storage regional = regionalPricing[region];
// Calculate regional supply/demand (simplified)
uint256 regionalSupply = (_totalSupply * regionalPricing[region].localSupply) / 100;
uint256 regionalDemand = (_totalDemand * regionalPricing[region].localDemand) / 100;
// Calculate regional multiplier
uint256 newMultiplier = 10000; // Base multiplier
if (regionalDemand > regionalSupply) {
newMultiplier = (newMultiplier * 11000) / 10000; // 10% premium
} else if (regionalSupply > regionalDemand) {
newMultiplier = (newMultiplier * 9500) / 10000; // 5% discount
}
regional.regionalMultiplier = newMultiplier;
regional.lastUpdateTime = block.timestamp;
emit RegionalPriceUpdated(region, newMultiplier, regionalSupply, regionalDemand);
}
}
/**
* @dev Determines market condition based on utilization and volatility
* @param _utilizationRate Utilization rate in basis points
* @param _priceVolatility Price volatility in basis points
*/
function _determineMarketCondition(uint256 _utilizationRate, uint256 _priceVolatility) internal pure returns (MarketCondition) {
if (_utilizationRate > 9000) {
return MarketCondition.Surge;
} else if (_utilizationRate > 7000) {
return MarketCondition.Undersupply;
} else if (_utilizationRate > 3000) {
return MarketCondition.Balanced;
} else if (_utilizationRate > 1000) {
return MarketCondition.Oversupply;
} else {
return MarketCondition.Crash;
}
}
/**
* @dev Determines price change type
* @param _oldPrice Previous price
* @param _newPrice New price
*/
function _determinePriceChangeType(uint256 _oldPrice, uint256 _newPrice) internal pure returns (PriceChangeType) {
if (_oldPrice == 0) {
return PriceChangeType.Stable;
}
uint256 changePercentage = 0;
if (_newPrice > _oldPrice) {
changePercentage = ((_newPrice - _oldPrice) * 10000) / _oldPrice;
} else {
changePercentage = ((_oldPrice - _newPrice) * 10000) / _oldPrice;
}
if (changePercentage < 500) { // < 5%
return PriceChangeType.Stable;
} else if (changePercentage > 2000) { // > 20%
return _newPrice > _oldPrice ? PriceChangeType.Surge : PriceChangeType.Discount;
} else {
return _newPrice > _oldPrice ? PriceChangeType.Increase : PriceChangeType.Decrease;
}
}
/**
* @dev Applies special pricing based on market conditions
* @param _condition Current market condition
* @param _volatility Price volatility
*/
function _applySpecialPricing(MarketCondition _condition, uint256 _volatility) internal {
if (_condition == MarketCondition.Surge) {
emit SurgePricingActivated(surgeMultiplier, 3600, "High demand detected");
} else if (_condition == MarketCondition.Crash) {
emit DiscountPricingActivated(discountMultiplier, 3600, "Low demand detected");
}
}
/**
* @dev Creates demand forecast
* @param _forecastPeriod Period to forecast (in seconds)
* @param _predictedDemand Predicted demand
* @param _confidence Confidence level (0-100)
*/
function createDemandForecast(
uint256 _forecastPeriod,
uint256 _predictedDemand,
uint256 _confidence
) external onlyAuthorizedPriceOracle nonReentrant whenNotPaused {
require(_forecastPeriod > 0, "Invalid forecast period");
require(_predictedDemand > 0, "Invalid predicted demand");
require(_confidence <= 100, "Invalid confidence");
uint256 forecastId = priceUpdateCounter++;
demandForecasts[forecastId] = DemandForecast({
forecastPeriod: _forecastPeriod,
predictedDemand: _predictedDemand,
confidence: _confidence,
forecastTime: block.timestamp,
actualDemand: 0,
forecastAccuracy: 0
});
emit DemandForecastCreated(_forecastPeriod, _predictedDemand, _confidence, block.timestamp);
}
/**
* @dev Creates price alert
* @param _subscriber Address to notify
* @param _alertType Type of alert
* @param _thresholdPrice Threshold price
* @param _notificationMethod Notification method
*/
function createPriceAlert(
address _subscriber,
PriceAlertType _alertType,
uint256 _thresholdPrice,
string memory _notificationMethod
) external nonReentrant whenNotPaused returns (uint256) {
require(_subscriber != address(0), "Invalid subscriber");
require(_thresholdPrice > 0, "Invalid threshold price");
uint256 alertId = priceUpdateCounter++;
priceAlerts[alertId] = PriceAlert({
alertId: alertId,
subscriber: _subscriber,
alertType: _alertType,
thresholdPrice: _thresholdPrice,
currentPrice: 0,
isActive: true,
lastTriggered: 0,
notificationMethod: _notificationMethod
});
activePriceAlerts.push(alertId);
return alertId;
}
/**
* @dev Gets current market price
* @param _provider Provider address (optional, for provider-specific pricing)
* @param _region Region (optional, for regional pricing)
*/
function getMarketPrice(address _provider, string memory _region)
external
view
returns (uint256)
{
uint256 basePrice = basePricePerHour;
// Get latest market data
if (priceUpdateCounter > 0) {
basePrice = marketDataHistory[priceUpdateCounter - 1].averagePrice;
}
// Apply regional multiplier if specified
if (bytes(_region).length > 0) {
RegionalPricing storage regional = regionalPricing[_region];
basePrice = (basePrice * regional.regionalMultiplier) / 10000;
}
// Apply provider-specific pricing if specified
if (_provider != address(0)) {
ProviderPricing storage provider = providerPricing[_provider];
if (provider.currentPrice > 0) {
basePrice = provider.currentPrice;
}
}
return basePrice;
}
/**
* @dev Gets market data
* @param _timestamp Timestamp to get data for (0 for latest)
*/
function getMarketData(uint256 _timestamp)
external
view
returns (MarketData memory)
{
if (_timestamp == 0 && priceUpdateCounter > 0) {
return marketDataHistory[priceUpdateCounter - 1];
}
// Find closest timestamp
for (uint256 i = priceUpdateCounter; i > 0; i--) {
if (marketDataHistory[i - 1].lastUpdateTime <= _timestamp) {
return marketDataHistory[i - 1];
}
}
revert("No market data found for timestamp");
}
/**
* @dev Gets price history
* @param _count Number of historical entries to return
*/
function getPriceHistory(uint256 _count)
external
view
returns (PriceHistory[] memory)
{
uint256 startIndex = priceUpdateCounter > _count ? priceUpdateCounter - _count : 0;
uint256 length = priceUpdateCounter - startIndex;
PriceHistory[] memory history = new PriceHistory[](length);
for (uint256 i = 0; i < length; i++) {
history[i] = priceHistory[startIndex + i][0];
}
return history;
}
/**
* @dev Authorizes a price oracle
* @param _oracle Address of the oracle
*/
function authorizePriceOracle(address _oracle) external onlyOwner {
require(_oracle != address(0), "Invalid oracle address");
authorizedPriceOracles[_oracle] = true;
emit PriceOracleAuthorized(_oracle, 0);
}
/**
* @dev Revokes price oracle authorization
* @param _oracle Address of the oracle
*/
function revokePriceOracle(address _oracle) external onlyOwner {
authorizedPriceOracles[_oracle] = false;
emit PriceOracleRevoked(_oracle, "Authorization revoked");
}
/**
* @dev Updates base pricing parameters
* @param _basePrice New base price
* @param _minPrice New minimum price
* @param _maxPrice New maximum price
*/
function updateBasePricing(
uint256 _basePrice,
uint256 _minPrice,
uint256 _maxPrice
) external onlyOwner {
require(_minPrice > 0 && _maxPrice > _minPrice, "Invalid price bounds");
require(_basePrice >= _minPrice && _basePrice <= _maxPrice, "Base price out of bounds");
basePricePerHour = _basePrice;
minPricePerHour = _minPrice;
maxPricePerHour = _maxPrice;
}
/**
* @dev Emergency pause function
*/
function pause() external onlyOwner {
_pause();
}
/**
* @dev Unpause function
*/
function unpause() external onlyOwner {
_unpause();
}
// Internal function to check price alerts
function _checkPriceAlerts(uint256 _currentPrice) internal {
for (uint256 i = 0; i < activePriceAlerts.length; i++) {
uint256 alertId = activePriceAlerts[i];
PriceAlert storage alert = priceAlerts[alertId];
if (!alert.isActive) continue;
bool shouldTrigger = false;
if (alert.alertType == PriceAlertType.PriceAbove && _currentPrice > alert.thresholdPrice) {
shouldTrigger = true;
} else if (alert.alertType == PriceAlertType.PriceBelow && _currentPrice < alert.thresholdPrice) {
shouldTrigger = true;
}
if (shouldTrigger) {
alert.lastTriggered = block.timestamp;
emit PriceAlertTriggered(alertId, alert.subscriber, alert.alertType, _currentPrice, alert.thresholdPrice);
}
}
}
}

880
contracts/EscrowService.sol Normal file
View File

@@ -0,0 +1,880 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./AIPowerRental.sol";
import "./AITBCPaymentProcessor.sol";
/**
* @title Escrow Service
* @dev Advanced escrow service with multi-signature, time-locks, and conditional releases
* @notice Secure payment holding with automated release conditions and dispute protection
*/
contract EscrowService is Ownable, ReentrancyGuard, Pausable {
// State variables
IERC20 public aitbcToken;
AIPowerRental public aiPowerRental;
AITBCPaymentProcessor public paymentProcessor;
uint256 public escrowCounter;
uint256 public minEscrowAmount = 1e15; // 0.001 AITBC minimum
uint256 public maxEscrowAmount = 1e22; // 10,000 AITBC maximum
uint256 public minTimeLock = 300; // 5 minutes minimum
uint256 public maxTimeLock = 86400 * 30; // 30 days maximum
uint256 public defaultReleaseDelay = 3600; // 1 hour default
uint256 public emergencyReleaseDelay = 86400; // 24 hours for emergency
uint256 public platformFeePercentage = 50; // 0.5% in basis points
// Structs
struct EscrowAccount {
uint256 escrowId;
address depositor;
address beneficiary;
address arbiter;
uint256 amount;
uint256 platformFee;
uint256 releaseTime;
uint256 creationTime;
bool isReleased;
bool isRefunded;
bool isFrozen;
EscrowType escrowType;
ReleaseCondition releaseCondition;
bytes32 conditionHash;
string conditionDescription;
uint256 releaseAttempts;
uint256 lastReleaseAttempt;
address[] signatories;
mapping(address => bool) hasSigned;
uint256 requiredSignatures;
uint256 currentSignatures;
}
struct ConditionalRelease {
uint256 escrowId;
bytes32 condition;
bool conditionMet;
address oracle;
uint256 verificationTime;
string conditionData;
uint256 confidence;
}
struct MultiSigRelease {
uint256 escrowId;
address[] requiredSigners;
uint256 signaturesRequired;
mapping(address => bool) hasSigned;
uint256 currentSignatures;
uint256 deadline;
bool isExecuted;
}
struct TimeLockRelease {
uint256 escrowId;
uint256 lockStartTime;
uint256 lockDuration;
uint256 releaseWindow;
bool canEarlyRelease;
uint256 earlyReleaseFee;
bool isReleased;
}
struct EmergencyRelease {
uint256 escrowId;
address initiator;
string reason;
uint256 requestTime;
uint256 votingDeadline;
mapping(address => bool) hasVoted;
uint256 votesFor;
uint256 votesAgainst;
uint256 totalVotes;
bool isApproved;
bool isExecuted;
}
// Enums
enum EscrowType {
Standard,
MultiSignature,
TimeLocked,
Conditional,
PerformanceBased,
MilestoneBased,
Emergency
}
enum ReleaseCondition {
Manual,
Automatic,
OracleVerified,
PerformanceMet,
TimeBased,
MultiSignature,
Emergency
}
enum EscrowStatus {
Created,
Funded,
Locked,
ConditionPending,
Approved,
Released,
Refunded,
Disputed,
Frozen,
Expired
}
// Mappings
mapping(uint256 => EscrowAccount) public escrowAccounts;
mapping(uint256 => ConditionalRelease) public conditionalReleases;
mapping(uint256 => MultiSigRelease) public multiSigReleases;
mapping(uint256 => TimeLockRelease) public timeLockReleases;
mapping(uint256 => EmergencyRelease) public emergencyReleases;
mapping(address => uint256[]) public depositorEscrows;
mapping(address => uint256[]) public beneficiaryEscrows;
mapping(bytes32 => uint256) public conditionEscrows;
mapping(address => bool) public authorizedOracles;
mapping(address => bool) public authorizedArbiters;
// Arrays for tracking
uint256[] public activeEscrows;
uint256[] public pendingReleases;
// Events
event EscrowCreated(
uint256 indexed escrowId,
address indexed depositor,
address indexed beneficiary,
uint256 amount,
EscrowType escrowType,
ReleaseCondition releaseCondition
);
event EscrowFunded(
uint256 indexed escrowId,
uint256 amount,
uint256 platformFee
);
event EscrowReleased(
uint256 indexed escrowId,
address indexed beneficiary,
uint256 amount,
string reason
);
event EscrowRefunded(
uint256 indexed escrowId,
address indexed depositor,
uint256 amount,
string reason
);
event ConditionSet(
uint256 indexed escrowId,
bytes32 indexed condition,
address indexed oracle,
string conditionDescription
);
event ConditionMet(
uint256 indexed escrowId,
bytes32 indexed condition,
bool conditionMet,
uint256 verificationTime
);
event MultiSignatureRequired(
uint256 indexed escrowId,
address[] requiredSigners,
uint256 signaturesRequired
);
event SignatureSubmitted(
uint256 indexed escrowId,
address indexed signer,
uint256 currentSignatures,
uint256 requiredSignatures
);
event TimeLockSet(
uint256 indexed escrowId,
uint256 lockDuration,
uint256 releaseWindow,
bool canEarlyRelease
);
event EmergencyReleaseRequested(
uint256 indexed escrowId,
address indexed initiator,
string reason,
uint256 votingDeadline
);
event EmergencyReleaseApproved(
uint256 indexed escrowId,
uint256 votesFor,
uint256 votesAgainst,
bool approved
);
event EscrowFrozen(
uint256 indexed escrowId,
address indexed freezer,
string reason
);
event EscrowUnfrozen(
uint256 indexed escrowId,
address indexed unfreezer,
string reason
);
event PlatformFeeCollected(
uint256 indexed escrowId,
uint256 feeAmount,
address indexed collector
);
// Modifiers
modifier onlyAuthorizedOracle() {
require(authorizedOracles[msg.sender], "Not authorized oracle");
_;
}
modifier onlyAuthorizedArbiter() {
require(authorizedArbiters[msg.sender], "Not authorized arbiter");
_;
}
modifier escrowExists(uint256 _escrowId) {
require(_escrowId < escrowCounter, "Escrow does not exist");
_;
}
modifier onlyParticipant(uint256 _escrowId) {
require(
msg.sender == escrowAccounts[_escrowId].depositor ||
msg.sender == escrowAccounts[_escrowId].beneficiary ||
msg.sender == escrowAccounts[_escrowId].arbiter,
"Not escrow participant"
);
_;
}
modifier sufficientBalance(address _user, uint256 _amount) {
require(aitbcToken.balanceOf(_user) >= _amount, "Insufficient balance");
_;
}
modifier sufficientAllowance(address _user, uint256 _amount) {
require(aitbcToken.allowance(_user, address(this)) >= _amount, "Insufficient allowance");
_;
}
modifier escrowNotFrozen(uint256 _escrowId) {
require(!escrowAccounts[_escrowId].isFrozen, "Escrow is frozen");
_;
}
modifier escrowNotReleased(uint256 _escrowId) {
require(!escrowAccounts[_escrowId].isReleased, "Escrow already released");
_;
}
modifier escrowNotRefunded(uint256 _escrowId) {
require(!escrowAccounts[_escrowId].isRefunded, "Escrow already refunded");
_;
}
// Constructor
constructor(
address _aitbcToken,
address _aiPowerRental,
address _paymentProcessor
) {
aitbcToken = IERC20(_aitbcToken);
aiPowerRental = AIPowerRental(_aiPowerRental);
paymentProcessor = AITBCPaymentProcessor(_paymentProcessor);
escrowCounter = 0;
}
/**
* @dev Creates a new escrow account
* @param _beneficiary Beneficiary address
* @param _arbiter Arbiter address (can be zero address for no arbiter)
* @param _amount Amount to lock in escrow
* @param _escrowType Type of escrow
* @param _releaseCondition Release condition
* @param _releaseTime Release time (0 for no time limit)
* @param _conditionDescription Description of release conditions
*/
function createEscrow(
address _beneficiary,
address _arbiter,
uint256 _amount,
EscrowType _escrowType,
ReleaseCondition _releaseCondition,
uint256 _releaseTime,
string memory _conditionDescription
) external sufficientBalance(msg.sender, _amount) sufficientAllowance(msg.sender, _amount) nonReentrant whenNotPaused returns (uint256) {
require(_beneficiary != address(0), "Invalid beneficiary");
require(_beneficiary != msg.sender, "Cannot be own beneficiary");
require(_amount >= minEscrowAmount && _amount <= maxEscrowAmount, "Invalid amount");
require(_releaseTime == 0 || _releaseTime > block.timestamp, "Invalid release time");
uint256 escrowId = escrowCounter++;
uint256 platformFee = (_amount * platformFeePercentage) / 10000;
uint256 totalAmount = _amount + platformFee;
escrowAccounts[escrowId] = EscrowAccount({
escrowId: escrowId,
depositor: msg.sender,
beneficiary: _beneficiary,
arbiter: _arbiter,
amount: _amount,
platformFee: platformFee,
releaseTime: _releaseTime,
creationTime: block.timestamp,
isReleased: false,
isRefunded: false,
isFrozen: false,
escrowType: _escrowType,
releaseCondition: _releaseCondition,
conditionHash: bytes32(0),
conditionDescription: _conditionDescription,
releaseAttempts: 0,
lastReleaseAttempt: 0,
signatories: new address[](0),
requiredSignatures: 0,
currentSignatures: 0
});
depositorEscrows[msg.sender].push(escrowId);
beneficiaryEscrows[_beneficiary].push(escrowId);
activeEscrows.push(escrowId);
// Transfer tokens to contract
require(
aitbcToken.transferFrom(msg.sender, address(this), totalAmount),
"Escrow funding failed"
);
emit EscrowCreated(escrowId, msg.sender, _beneficiary, _amount, _escrowType, _releaseCondition);
emit EscrowFunded(escrowId, _amount, platformFee);
// Setup specific escrow type configurations
if (_escrowType == EscrowType.TimeLocked) {
_setupTimeLock(escrowId, _releaseTime - block.timestamp);
} else if (_escrowType == EscrowType.MultiSignature) {
_setupMultiSignature(escrowId);
}
return escrowId;
}
/**
* @dev Sets release condition for escrow
* @param _escrowId ID of the escrow
* @param _condition Condition hash
* @param _oracle Oracle address for verification
* @param _conditionData Condition data
*/
function setReleaseCondition(
uint256 _escrowId,
bytes32 _condition,
address _oracle,
string memory _conditionData
) external escrowExists(_escrowId) onlyParticipant(_escrowId) escrowNotFrozen(_escrowId) escrowNotReleased(_escrowId) {
require(authorizedOracles[_oracle] || _oracle == address(0), "Invalid oracle");
EscrowAccount storage escrow = escrowAccounts[_escrowId];
escrow.conditionHash = _condition;
conditionalReleases[_escrowId] = ConditionalRelease({
escrowId: _escrowId,
condition: _condition,
conditionMet: false,
oracle: _oracle,
verificationTime: 0,
conditionData: _conditionData,
confidence: 0
});
conditionEscrows[_condition] = _escrowId;
emit ConditionSet(_escrowId, _condition, _oracle, _conditionData);
}
/**
* @dev Verifies and meets release condition
* @param _escrowId ID of the escrow
* @param _conditionMet Whether condition is met
* @param _confidence Confidence level (0-100)
*/
function verifyCondition(
uint256 _escrowId,
bool _conditionMet,
uint256 _confidence
) external onlyAuthorizedOracle escrowExists(_escrowId) escrowNotFrozen(_escrowId) escrowNotReleased(_escrowId) {
ConditionalRelease storage condRelease = conditionalReleases[_escrowId];
require(condRelease.oracle == msg.sender, "Not assigned oracle");
condRelease.conditionMet = _conditionMet;
condRelease.verificationTime = block.timestamp;
condRelease.confidence = _confidence;
emit ConditionMet(_escrowId, condRelease.condition, _conditionMet, block.timestamp);
if (_conditionMet) {
_releaseEscrow(_escrowId, "Condition verified and met");
}
}
/**
* @dev Submits signature for multi-signature release
* @param _escrowId ID of the escrow
*/
function submitSignature(uint256 _escrowId)
external
escrowExists(_escrowId)
onlyParticipant(_escrowId)
escrowNotFrozen(_escrowId)
escrowNotReleased(_escrowId)
{
EscrowAccount storage escrow = escrowAccounts[_escrowId];
MultiSigRelease storage multiSig = multiSigReleases[_escrowId];
require(!escrow.hasSigned[msg.sender], "Already signed");
require(multiSig.requiredSigners > 0, "Multi-signature not setup");
escrow.hasSigned[msg.sender] = true;
escrow.currentSignatures++;
multiSig.currentSignatures++;
emit SignatureSubmitted(_escrowId, msg.sender, escrow.currentSignatures, multiSig.requiredSigners);
if (escrow.currentSignatures >= multiSig.requiredSigners) {
_releaseEscrow(_escrowId, "Multi-signature requirement met");
}
}
/**
* @dev Releases escrow to beneficiary
* @param _escrowId ID of the escrow
* @param _reason Reason for release
*/
function releaseEscrow(uint256 _escrowId, string memory _reason)
external
escrowExists(_escrowId)
escrowNotFrozen(_escrowId)
escrowNotReleased(_escrowId)
escrowNotRefunded(_escrowId)
nonReentrant
{
EscrowAccount storage escrow = escrowAccounts[_escrowId];
require(
msg.sender == escrow.depositor ||
msg.sender == escrow.beneficiary ||
msg.sender == escrow.arbiter ||
msg.sender == owner(),
"Not authorized to release"
);
// Check release conditions
if (escrow.releaseCondition == ReleaseCondition.Manual) {
require(msg.sender == escrow.depositor || msg.sender == escrow.arbiter, "Manual release requires depositor or arbiter");
} else if (escrow.releaseCondition == ReleaseCondition.TimeBased) {
require(block.timestamp >= escrow.releaseTime, "Release time not reached");
} else if (escrow.releaseCondition == ReleaseCondition.OracleVerified) {
require(conditionalReleases[_escrowId].conditionMet, "Condition not met");
} else if (escrow.releaseCondition == ReleaseCondition.MultiSignature) {
require(escrow.currentSignatures >= escrow.requiredSignatures, "Insufficient signatures");
}
_releaseEscrow(_escrowId, _reason);
}
/**
* @dev Refunds escrow to depositor
* @param _escrowId ID of the escrow
* @param _reason Reason for refund
*/
function refundEscrow(uint256 _escrowId, string memory _reason)
external
escrowExists(_escrowId)
escrowNotFrozen(_escrowId)
escrowNotReleased(_escrowId)
escrowNotRefunded(_escrowId)
nonReentrant
{
EscrowAccount storage escrow = escrowAccounts[_escrowId];
require(
msg.sender == escrow.depositor ||
msg.sender == escrow.arbiter ||
msg.sender == owner(),
"Not authorized to refund"
);
// Check refund conditions
if (escrow.releaseCondition == ReleaseCondition.TimeBased) {
require(block.timestamp < escrow.releaseTime, "Release time passed, cannot refund");
} else if (escrow.releaseCondition == ReleaseCondition.OracleVerified) {
require(!conditionalReleases[_escrowId].conditionMet, "Condition met, cannot refund");
}
escrow.isRefunded = true;
require(
aitbcToken.transfer(escrow.depositor, escrow.amount),
"Refund transfer failed"
);
emit EscrowRefunded(_escrowId, escrow.depositor, escrow.amount, _reason);
}
/**
* @dev Requests emergency release
* @param _escrowId ID of the escrow
* @param _reason Reason for emergency release
*/
function requestEmergencyRelease(uint256 _escrowId, string memory _reason)
external
escrowExists(_escrowId)
onlyParticipant(_escrowId)
escrowNotFrozen(_escrowId)
escrowNotReleased(_escrowId)
escrowNotRefunded(_escrowId)
{
EscrowAccount storage escrow = escrowAccounts[_escrowId];
EmergencyRelease storage emergency = emergencyReleases[_escrowId];
require(emergency.requestTime == 0, "Emergency release already requested");
emergencyReleases[_escrowId] = EmergencyRelease({
escrowId: _escrowId,
initiator: msg.sender,
reason: _reason,
requestTime: block.timestamp,
votingDeadline: block.timestamp + emergencyReleaseDelay,
votesFor: 0,
votesAgainst: 0,
totalVotes: 0,
isApproved: false,
isExecuted: false
});
emit EmergencyReleaseRequested(_escrowId, msg.sender, _reason, block.timestamp + emergencyReleaseDelay);
}
/**
* @dev Votes on emergency release
* @param _escrowId ID of the escrow
* @param _vote True to approve, false to reject
*/
function voteEmergencyRelease(uint256 _escrowId, bool _vote)
external
escrowExists(_escrowId)
onlyAuthorizedArbiter
{
EmergencyRelease storage emergency = emergencyReleases[_escrowId];
require(emergency.requestTime > 0, "No emergency release requested");
require(block.timestamp <= emergency.votingDeadline, "Voting deadline passed");
require(!emergency.hasVoted[msg.sender], "Already voted");
emergency.hasVoted[msg.sender] = true;
emergency.totalVotes++;
if (_vote) {
emergency.votesFor++;
} else {
emergency.votesAgainst++;
}
// Check if voting is complete and approved
if (emergency.totalVotes >= 3 && emergency.votesFor > emergency.votesAgainst) {
emergency.isApproved = true;
emit EmergencyReleaseApproved(_escrowId, emergency.votesFor, emergency.votesAgainst, true);
_releaseEscrow(_escrowId, "Emergency release approved");
}
}
/**
* @dev Freezes an escrow account
* @param _escrowId ID of the escrow
* @param _reason Reason for freezing
*/
function freezeEscrow(uint256 _escrowId, string memory _reason)
external
escrowExists(_escrowId)
escrowNotFrozen(_escrowId)
{
require(
msg.sender == escrowAccounts[_escrowId].arbiter ||
msg.sender == owner(),
"Not authorized to freeze"
);
escrowAccounts[_escrowId].isFrozen = true;
emit EscrowFrozen(_escrowId, msg.sender, _reason);
}
/**
* @dev Unfreezes an escrow account
* @param _escrowId ID of the escrow
* @param _reason Reason for unfreezing
*/
function unfreezeEscrow(uint256 _escrowId, string memory _reason)
external
escrowExists(_escrowId)
{
require(
msg.sender == escrowAccounts[_escrowId].arbiter ||
msg.sender == owner(),
"Not authorized to unfreeze"
);
escrowAccounts[_escrowId].isFrozen = false;
emit EscrowUnfrozen(_escrowId, msg.sender, _reason);
}
/**
* @dev Authorizes an oracle
* @param _oracle Address of the oracle
*/
function authorizeOracle(address _oracle) external onlyOwner {
require(_oracle != address(0), "Invalid oracle address");
authorizedOracles[_oracle] = true;
}
/**
* @dev Revokes oracle authorization
* @param _oracle Address of the oracle
*/
function revokeOracle(address _oracle) external onlyOwner {
authorizedOracles[_oracle] = false;
}
/**
* @dev Authorizes an arbiter
* @param _arbiter Address of the arbiter
*/
function authorizeArbiter(address _arbiter) external onlyOwner {
require(_arbiter != address(0), "Invalid arbiter address");
authorizedArbiters[_arbiter] = true;
}
/**
* @dev Revokes arbiter authorization
* @param _arbiter Address of the arbiter
*/
function revokeArbiter(address _arbiter) external onlyOwner {
authorizedArbiters[_arbiter] = false;
}
// Internal functions
function _setupTimeLock(uint256 _escrowId, uint256 _duration) internal {
require(_duration >= minTimeLock && _duration <= maxTimeLock, "Invalid duration");
timeLockReleases[_escrowId] = TimeLockRelease({
escrowId: _escrowId,
lockStartTime: block.timestamp,
lockDuration: _duration,
releaseWindow: _duration / 10, // 10% of lock duration as release window
canEarlyRelease: false,
earlyReleaseFee: 1000, // 10% fee for early release
isReleased: false
});
emit TimeLockSet(_escrowId, _duration, _duration / 10, false);
}
function _setupMultiSignature(uint256 _escrowId) internal {
EscrowAccount storage escrow = escrowAccounts[_escrowId];
// Default to requiring depositor + beneficiary signatures
address[] memory requiredSigners = new address[](2);
requiredSigners[0] = escrow.depositor;
requiredSigners[1] = escrow.beneficiary;
multiSigReleases[_escrowId] = MultiSigRelease({
escrowId: _escrowId,
requiredSigners: requiredSigners,
signaturesRequired: 2,
currentSignatures: 0,
deadline: block.timestamp + 7 days,
isExecuted: false
});
escrow.requiredSignatures = 2;
emit MultiSignatureRequired(_escrowId, requiredSigners, 2);
}
function _releaseEscrow(uint256 _escrowId, string memory _reason) internal {
EscrowAccount storage escrow = escrowAccounts[_escrowId];
escrow.isReleased = true;
escrow.lastReleaseAttempt = block.timestamp;
// Transfer amount to beneficiary
require(
aitbcToken.transfer(escrow.beneficiary, escrow.amount),
"Escrow release failed"
);
// Transfer platform fee to owner
if (escrow.platformFee > 0) {
require(
aitbcToken.transfer(owner(), escrow.platformFee),
"Platform fee transfer failed"
);
emit PlatformFeeCollected(_escrowId, escrow.platformFee, owner());
}
emit EscrowReleased(_escrowId, escrow.beneficiary, escrow.amount, _reason);
}
// View functions
/**
* @dev Gets escrow account details
* @param _escrowId ID of the escrow
*/
function getEscrowAccount(uint256 _escrowId)
external
view
escrowExists(_escrowId)
returns (EscrowAccount memory)
{
return escrowAccounts[_escrowId];
}
/**
* @dev Gets conditional release details
* @param _escrowId ID of the escrow
*/
function getConditionalRelease(uint256 _escrowId)
external
view
returns (ConditionalRelease memory)
{
return conditionalReleases[_escrowId];
}
/**
* @dev Gets multi-signature release details
* @param _escrowId ID of the escrow
*/
function getMultiSigRelease(uint256 _escrowId)
external
view
returns (MultiSigRelease memory)
{
return multiSigReleases[_escrowId];
}
/**
* @dev Gets time-lock release details
* @param _escrowId ID of the escrow
*/
function getTimeLockRelease(uint256 _escrowId)
external
view
returns (TimeLockRelease memory)
{
return timeLockReleases[_escrowId];
}
/**
* @dev Gets emergency release details
* @param _escrowId ID of the escrow
*/
function getEmergencyRelease(uint256 _escrowId)
external
view
returns (EmergencyRelease memory)
{
return emergencyReleases[_escrowId];
}
/**
* @dev Gets all escrows for a depositor
* @param _depositor Address of the depositor
*/
function getDepositorEscrows(address _depositor)
external
view
returns (uint256[] memory)
{
return depositorEscrows[_depositor];
}
/**
* @dev Gets all escrows for a beneficiary
* @param _beneficiary Address of the beneficiary
*/
function getBeneficiaryEscrows(address _beneficiary)
external
view
returns (uint256[] memory)
{
return beneficiaryEscrows[_beneficiary];
}
/**
* @dev Gets active escrows
*/
function getActiveEscrows()
external
view
returns (uint256[] memory)
{
uint256[] memory active = new uint256[](activeEscrows.length);
uint256 activeCount = 0;
for (uint256 i = 0; i < activeEscrows.length; i++) {
EscrowAccount storage escrow = escrowAccounts[activeEscrows[i]];
if (!escrow.isReleased && !escrow.isRefunded) {
active[activeCount] = activeEscrows[i];
activeCount++;
}
}
// Resize array to active count
assembly {
mstore(active, activeCount)
}
return active;
}
/**
* @dev Emergency pause function
*/
function pause() external onlyOwner {
_pause();
}
/**
* @dev Unpause function
*/
function unpause() external onlyOwner {
_unpause();
}
}

View File

@@ -0,0 +1,675 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "./ZKReceiptVerifier.sol";
import "./Groth16Verifier.sol";
import "./AIPowerRental.sol";
/**
* @title Performance Verifier
* @dev Advanced performance verification contract with ZK proofs and oracle integration
* @notice Verifies AI service performance metrics and enforces SLA compliance
*/
contract PerformanceVerifier is Ownable, ReentrancyGuard, Pausable {
// State variables
ZKReceiptVerifier public zkVerifier;
Groth16Verifier public groth16Verifier;
AIPowerRental public aiPowerRental;
uint256 public verificationCounter;
uint256 public minResponseTime = 100; // 100ms minimum
uint256 public maxResponseTime = 5000; // 5 seconds maximum
uint256 public minAccuracy = 90; // 90% minimum accuracy
uint256 public minAvailability = 95; // 95% minimum availability
uint256 public verificationWindow = 3600; // 1 hour verification window
uint256 public penaltyPercentage = 500; // 5% penalty in basis points
uint256 public rewardPercentage = 200; // 2% reward in basis points
// Optimistic Rollup / Dispute variables
uint256 public disputeWindow = 3600; // 1 hour dispute window before execution is final
mapping(uint256 => uint256) public verificationFinalizedAt;
// Structs
struct PerformanceMetrics {
uint256 verificationId;
uint256 agreementId;
address provider;
uint256 responseTime;
uint256 accuracy;
uint256 availability;
uint256 computePower;
uint256 throughput;
uint256 memoryUsage;
uint256 energyEfficiency;
bool withinSLA;
uint256 timestamp;
bytes32 zkProof;
bytes32 groth16Proof;
VerificationStatus status;
uint256 penaltyAmount;
uint256 rewardAmount;
}
struct SLAParameters {
uint256 maxResponseTime;
uint256 minAccuracy;
uint256 minAvailability;
uint256 minComputePower;
uint256 maxMemoryUsage;
uint256 minEnergyEfficiency;
bool isActive;
uint256 lastUpdated;
}
struct OracleData {
address oracleAddress;
uint256 lastUpdateTime;
bool isAuthorized;
uint256 reputationScore;
uint256 totalReports;
uint256 accurateReports;
}
struct PerformanceHistory {
uint256 totalVerifications;
uint256 successfulVerifications;
uint256 averageResponseTime;
uint256 averageAccuracy;
uint256 averageAvailability;
uint256 lastVerificationTime;
uint256 currentStreak;
uint256 bestStreak;
}
// Enums
enum VerificationStatus {
Submitted,
Pending,
Verified,
Rejected,
Expired,
Disputed
}
enum MetricType {
ResponseTime,
Accuracy,
Availability,
ComputePower,
Throughput,
MemoryUsage,
EnergyEfficiency
}
// Mappings
mapping(uint256 => PerformanceMetrics) public performanceMetrics;
mapping(uint256 => SLAParameters) public slaParameters;
mapping(address => OracleData) public oracles;
mapping(address => PerformanceHistory) public providerHistory;
mapping(uint256 => uint256[]) public agreementVerifications;
mapping(address => uint256[]) public providerVerifications;
mapping(bytes32 => uint256) public proofToVerification;
// Arrays for authorized oracles
address[] public authorizedOracles;
// Events
event PerformanceSubmitted(
uint256 indexed verificationId,
uint256 indexed agreementId,
address indexed provider,
uint256 responseTime,
uint256 accuracy,
uint256 availability
);
event PerformanceVerified(
uint256 indexed verificationId,
bool withinSLA,
uint256 penaltyAmount,
uint256 rewardAmount
);
event PerformanceRejected(
uint256 indexed verificationId,
string reason,
bytes32 invalidProof
);
event SLAParametersUpdated(
uint256 indexed agreementId,
uint256 maxResponseTime,
uint256 minAccuracy,
uint256 minAvailability
);
event OracleAuthorized(
address indexed oracle,
uint256 reputationScore
);
event OracleRevoked(
address indexed oracle,
string reason
);
event OracleReportSubmitted(
address indexed oracle,
uint256 indexed verificationId,
bool accurate
);
event PenaltyApplied(
uint256 indexed agreementId,
address indexed provider,
uint256 penaltyAmount
);
event RewardIssued(
uint256 indexed agreementId,
address indexed provider,
uint256 rewardAmount
);
event PerformanceThresholdUpdated(
MetricType indexed metricType,
uint256 oldValue,
uint256 newValue
);
// Modifiers
modifier onlyAuthorizedOracle() {
require(oracles[msg.sender].isAuthorized, "Not authorized oracle");
_;
}
modifier verificationExists(uint256 _verificationId) {
require(_verificationId < verificationCounter, "Verification does not exist");
_;
}
modifier validStatus(uint256 _verificationId, VerificationStatus _requiredStatus) {
require(performanceMetrics[_verificationId].status == _requiredStatus, "Invalid verification status");
_;
}
modifier withinVerificationWindow(uint256 _timestamp) {
require(block.timestamp - _timestamp <= verificationWindow, "Verification window expired");
_;
}
// Constructor
constructor(
address _zkVerifier,
address _groth16Verifier,
address _aiPowerRental
) {
zkVerifier = ZKReceiptVerifier(_zkVerifier);
groth16Verifier = Groth16Verifier(_groth16Verifier);
aiPowerRental = AIPowerRental(_aiPowerRental);
verificationCounter = 0;
}
/**
* @dev Submits performance metrics for verification
* @param _agreementId ID of the rental agreement
* @param _responseTime Response time in milliseconds
* @param _accuracy Accuracy percentage (0-100)
* @param _availability Availability percentage (0-100)
* @param _computePower Compute power utilized
* @param _throughput Throughput in requests per second
* @param _memoryUsage Memory usage in MB
* @param _energyEfficiency Energy efficiency score
* @param _zkProof Zero-knowledge proof for performance verification
* @param _groth16Proof Groth16 proof for additional verification
*/
function submitPerformance(
uint256 _agreementId,
uint256 _responseTime,
uint256 _accuracy,
uint256 _availability,
uint256 _computePower,
uint256 _throughput,
uint256 _memoryUsage,
uint256 _energyEfficiency,
bytes memory _zkProof,
bytes memory _groth16Proof
) external nonReentrant whenNotPaused returns (uint256) {
require(_responseTime >= minResponseTime && _responseTime <= maxResponseTime, "Invalid response time");
require(_accuracy <= 100, "Invalid accuracy");
require(_availability <= 100, "Invalid availability");
// Get agreement details
(, address provider, , , , , , , , ) = aiPowerRental.getRentalAgreement(_agreementId);
require(provider != address(0), "Invalid agreement");
uint256 verificationId = verificationCounter++;
performanceMetrics[verificationId] = PerformanceMetrics({
verificationId: verificationId,
agreementId: _agreementId,
provider: provider,
responseTime: _responseTime,
accuracy: _accuracy,
availability: _availability,
computePower: _computePower,
throughput: _throughput,
memoryUsage: _memoryUsage,
energyEfficiency: _energyEfficiency,
withinSLA: false,
timestamp: block.timestamp,
zkProof: keccak256(_zkProof),
groth16Proof: keccak256(_groth16Proof),
status: VerificationStatus.Submitted,
penaltyAmount: 0,
rewardAmount: 0
});
agreementVerifications[_agreementId].push(verificationId);
providerVerifications[provider].push(verificationId);
proofToVerification[keccak256(_zkProof)] = verificationId;
emit PerformanceSubmitted(
verificationId,
_agreementId,
provider,
_responseTime,
_accuracy,
_availability
);
// Auto-verify if proofs are valid
if (_verifyProofs(_zkProof, _groth16Proof, verificationId)) {
_verifyPerformance(verificationId);
} else {
performanceMetrics[verificationId].status = VerificationStatus.Pending;
}
return verificationId;
}
/**
* @dev Verifies performance metrics (oracle verification)
* @param _verificationId ID of the verification
* @param _accurate Whether the metrics are accurate
* @param _additionalData Additional verification data
*/
function verifyPerformance(
uint256 _verificationId,
bool _accurate,
string memory _additionalData
) external onlyAuthorizedOracle verificationExists(_verificationId) validStatus(_verificationId, VerificationStatus.Pending) {
PerformanceMetrics storage metrics = performanceMetrics[_verificationId];
require(block.timestamp - metrics.timestamp <= verificationWindow, "Verification window expired");
// Update oracle statistics
OracleData storage oracle = oracles[msg.sender];
oracle.totalReports++;
if (_accurate) {
oracle.accurateReports++;
}
oracle.lastUpdateTime = block.timestamp;
if (_accurate) {
_verifyPerformance(_verificationId);
} else {
metrics.status = VerificationStatus.Rejected;
emit PerformanceRejected(_verificationId, _additionalData, metrics.zkProof);
}
emit OracleReportSubmitted(msg.sender, _verificationId, _accurate);
}
/**
* @dev Sets SLA parameters for an agreement
* @param _agreementId ID of the agreement
* @param _maxResponseTime Maximum allowed response time
* @param _minAccuracy Minimum required accuracy
* @param _minAvailability Minimum required availability
* @param _minComputePower Minimum required compute power
* @param _maxMemoryUsage Maximum allowed memory usage
* @param _minEnergyEfficiency Minimum energy efficiency
*/
function setSLAParameters(
uint256 _agreementId,
uint256 _maxResponseTime,
uint256 _minAccuracy,
uint256 _minAvailability,
uint256 _minComputePower,
uint256 _maxMemoryUsage,
uint256 _minEnergyEfficiency
) external onlyOwner {
slaParameters[_agreementId] = SLAParameters({
maxResponseTime: _maxResponseTime,
minAccuracy: _minAccuracy,
minAvailability: _minAvailability,
minComputePower: _minComputePower,
maxMemoryUsage: _maxMemoryUsage,
minEnergyEfficiency: _minEnergyEfficiency,
isActive: true,
lastUpdated: block.timestamp
});
emit SLAParametersUpdated(
_agreementId,
_maxResponseTime,
_minAccuracy,
_minAvailability
);
}
/**
* @dev Authorizes an oracle
* @param _oracle Address of the oracle
* @param _reputationScore Initial reputation score
*/
function authorizeOracle(address _oracle, uint256 _reputationScore) external onlyOwner {
require(_oracle != address(0), "Invalid oracle address");
require(!oracles[_oracle].isAuthorized, "Oracle already authorized");
oracles[_oracle] = OracleData({
oracleAddress: _oracle,
lastUpdateTime: block.timestamp,
isAuthorized: true,
reputationScore: _reputationScore,
totalReports: 0,
accurateReports: 0
});
authorizedOracles.push(_oracle);
emit OracleAuthorized(_oracle, _reputationScore);
}
/**
* @dev Revokes oracle authorization
* @param _oracle Address of the oracle
* @param _reason Reason for revocation
*/
function revokeOracle(address _oracle, string memory _reason) external onlyOwner {
require(oracles[_oracle].isAuthorized, "Oracle not authorized");
oracles[_oracle].isAuthorized = false;
emit OracleRevoked(_oracle, _reason);
}
/**
* @dev Updates performance thresholds
* @param _metricType Type of metric
* @param _newValue New threshold value
*/
function updatePerformanceThreshold(MetricType _metricType, uint256 _newValue) external onlyOwner {
uint256 oldValue;
if (_metricType == MetricType.ResponseTime) {
oldValue = maxResponseTime;
maxResponseTime = _newValue;
} else if (_metricType == MetricType.Accuracy) {
oldValue = minAccuracy;
minAccuracy = _newValue;
} else if (_metricType == MetricType.Availability) {
oldValue = minAvailability;
minAvailability = _newValue;
} else if (_metricType == MetricType.ComputePower) {
oldValue = minComputePower;
minComputePower = _newValue;
} else {
revert("Invalid metric type");
}
emit PerformanceThresholdUpdated(_metricType, oldValue, _newValue);
}
/**
* @dev Calculates penalty for SLA violation
* @param _verificationId ID of the verification
*/
function calculatePenalty(uint256 _verificationId)
external
view
verificationExists(_verificationId)
returns (uint256)
{
PerformanceMetrics memory metrics = performanceMetrics[_verificationId];
if (metrics.withinSLA) {
return 0;
}
// Get agreement details to calculate penalty amount
(, address provider, , uint256 duration, uint256 price, , , , , ) = aiPowerRental.getRentalAgreement(metrics.agreementId);
// Penalty based on severity of violation
uint256 penaltyAmount = (price * penaltyPercentage) / 10000;
// Additional penalties for severe violations
if (metrics.responseTime > maxResponseTime * 2) {
penaltyAmount += (price * 1000) / 10000; // Additional 10%
}
if (metrics.accuracy < minAccuracy - 10) {
penaltyAmount += (price * 1000) / 10000; // Additional 10%
}
return penaltyAmount;
}
/**
* @dev Calculates reward for exceeding SLA
* @param _verificationId ID of the verification
*/
function calculateReward(uint256 _verificationId)
external
view
verificationExists(_verificationId)
returns (uint256)
{
PerformanceMetrics memory metrics = performanceMetrics[_verificationId];
if (!metrics.withinSLA) {
return 0;
}
// Get agreement details
(, address provider, , uint256 duration, uint256 price, , , , , ) = aiPowerRental.getRentalAgreement(metrics.agreementId);
// Reward based on performance quality
uint256 rewardAmount = (price * rewardPercentage) / 10000;
// Additional rewards for exceptional performance
if (metrics.responseTime < maxResponseTime / 2) {
rewardAmount += (price * 500) / 10000; // Additional 5%
}
if (metrics.accuracy > minAccuracy + 5) {
rewardAmount += (price * 500) / 10000; // Additional 5%
}
return rewardAmount;
}
/**
* @dev Gets performance history for a provider
* @param _provider Address of the provider
*/
function getProviderHistory(address _provider)
external
view
returns (PerformanceHistory memory)
{
return providerHistory[_provider];
}
/**
* @dev Gets all verifications for an agreement
* @param _agreementId ID of the agreement
*/
function getAgreementVerifications(uint256 _agreementId)
external
view
returns (uint256[] memory)
{
return agreementVerifications[_agreementId];
}
/**
* @dev Gets all verifications for a provider
* @param _provider Address of the provider
*/
function getProviderVerifications(address _provider)
external
view
returns (uint256[] memory)
{
return providerVerifications[_provider];
}
/**
* @dev Gets oracle information
* @param _oracle Address of the oracle
*/
function getOracleInfo(address _oracle)
external
view
returns (OracleData memory)
{
return oracles[_oracle];
}
/**
* @dev Gets all authorized oracles
*/
function getAuthorizedOracles()
external
view
returns (address[] memory)
{
address[] memory activeOracles = new address[](authorizedOracles.length);
uint256 activeCount = 0;
for (uint256 i = 0; i < authorizedOracles.length; i++) {
if (oracles[authorizedOracles[i]].isAuthorized) {
activeOracles[activeCount] = authorizedOracles[i];
activeCount++;
}
}
// Resize array to active count
assembly {
mstore(activeOracles, activeCount)
}
return activeOracles;
}
// Internal functions
function _verifyProofs(
bytes memory _zkProof,
bytes memory _groth16Proof,
uint256 _verificationId
) internal view returns (bool) {
PerformanceMetrics memory metrics = performanceMetrics[_verificationId];
// Verify ZK proof
bool zkValid = zkVerifier.verifyPerformanceProof(
metrics.agreementId,
metrics.responseTime,
metrics.accuracy,
metrics.availability,
metrics.computePower,
_zkProof
);
// Verify Groth16 proof
bool groth16Valid = groth16Verifier.verifyProof(_groth16Proof);
return zkValid && groth16Valid;
}
function _verifyPerformance(uint256 _verificationId) internal {
PerformanceMetrics storage metrics = performanceMetrics[_verificationId];
// Setup optimistic rollup finalization time
verificationFinalizedAt[_verificationId] = block.timestamp + disputeWindow;
metrics.status = VerificationStatus.Verified;
emit PerformanceVerified(_verificationId, metrics.score, metrics.zkProof);
}
/**
* @dev Finalizes an optimistic verification after the dispute window has passed
* @param _verificationId ID of the verification
*/
function finalizeOptimisticVerification(uint256 _verificationId) external verificationExists(_verificationId) {
PerformanceMetrics storage metrics = performanceMetrics[_verificationId];
require(metrics.status == VerificationStatus.Verified, "Verification not in verified status");
require(block.timestamp >= verificationFinalizedAt[_verificationId], "Dispute window still open");
metrics.status = VerificationStatus.Completed;
// Execute SLA logic (distribute rewards/penalties)
if (metrics.score >= minAccuracy) {
_rewardProvider(metrics.provider, metrics.agreementId);
} else {
_penalizeProvider(metrics.provider, metrics.agreementId);
}
}
/**
* @dev Challenge an optimistic verification within the dispute window
* @param _verificationId ID of the verification
* @param _challengeData Evidence of invalid performance
*/
function challengeVerification(uint256 _verificationId, string memory _challengeData) external verificationExists(_verificationId) {
PerformanceMetrics storage metrics = performanceMetrics[_verificationId];
require(metrics.status == VerificationStatus.Verified, "Verification not in verified status");
require(block.timestamp < verificationFinalizedAt[_verificationId], "Dispute window closed");
// A watcher node challenges the verification
// Switch to manual review or on-chain full ZK validation
metrics.status = VerificationStatus.Challenged;
emit VerificationChallenged(_verificationId, msg.sender, _challengeData);
}
function _updateProviderHistory(address _provider, bool _withinSLA) internal {
PerformanceHistory storage history = providerHistory[_provider];
history.totalVerifications++;
if (_withinSLA) {
history.successfulVerifications++;
history.currentStreak++;
if (history.currentStreak > history.bestStreak) {
history.bestStreak = history.currentStreak;
}
} else {
history.currentStreak = 0;
}
history.lastVerificationTime = block.timestamp;
// Update averages (simplified calculation)
// In a real implementation, you'd want to maintain running averages
}
/**
* @dev Emergency pause function
*/
function pause() external onlyOwner {
_pause();
}
/**
* @dev Unpause function
*/
function unpause() external onlyOwner {
_unpause();
}
}