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:
566
contracts/AIPowerRental.sol
Normal file
566
contracts/AIPowerRental.sol
Normal 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
|
||||
}
|
||||
}
|
||||
696
contracts/AITBCPaymentProcessor.sol
Normal file
696
contracts/AITBCPaymentProcessor.sol
Normal 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];
|
||||
}
|
||||
}
|
||||
730
contracts/DisputeResolution.sol
Normal file
730
contracts/DisputeResolution.sol
Normal 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();
|
||||
}
|
||||
}
|
||||
757
contracts/DynamicPricing.sol
Normal file
757
contracts/DynamicPricing.sol
Normal 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
880
contracts/EscrowService.sol
Normal 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();
|
||||
}
|
||||
}
|
||||
675
contracts/PerformanceVerifier.sol
Normal file
675
contracts/PerformanceVerifier.sol
Normal 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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user