refactor(contracts): remove deprecated AIPowerRental contract in favor of bounty system

- Delete AIPowerRental.sol (566 lines) - replaced by AgentBounty.sol
- Remove rental agreement system with provider/consumer model
- Remove performance metrics and SLA tracking
- Remove dispute resolution mechanism
- Remove ZK-proof verification for performance
- Remove provider/consumer authorization system
- Bounty system provides superior developer incentive structure
This commit is contained in:
oib
2026-02-27 21:46:54 +01:00
parent a477681c4b
commit 864ef4343e
152 changed files with 45716 additions and 94 deletions

View File

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

View File

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

View File

@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract AIToken is ERC20, Ownable {
constructor(uint256 initialSupply) ERC20("AI Token", "AIT") {
_mint(msg.sender, initialSupply);
}
function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
}

View File

@@ -0,0 +1,718 @@
// 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 "./PerformanceVerifier.sol";
import "./AIToken.sol";
/**
* @title Agent Bounty System
* @dev Automated bounty board for AI agent capabilities with ZK-proof verification
* @notice Allows DAO and users to create bounties that are automatically completed when agents submit valid ZK-proofs
*/
contract AgentBounty is Ownable, ReentrancyGuard, Pausable {
// State variables
IERC20 public aitbcToken;
PerformanceVerifier public performanceVerifier;
uint256 public bountyCounter;
uint256 public creationFeePercentage = 50; // 0.5% in basis points
uint256 public successFeePercentage = 200; // 2% in basis points
uint256 public disputeFeePercentage = 10; // 0.1% in basis points
uint256 public platformFeePercentage = 100; // 1% in basis points
// Bounty tiers
enum BountyTier { BRONZE, SILVER, GOLD, PLATINUM }
// Bounty status
enum BountyStatus { CREATED, ACTIVE, SUBMITTED, VERIFIED, COMPLETED, EXPIRED, DISPUTED }
// Submission status
enum SubmissionStatus { PENDING, VERIFIED, REJECTED, DISPUTED }
// Structs
struct Bounty {
uint256 bountyId;
string title;
string description;
uint256 rewardAmount;
address creator;
BountyTier tier;
BountyStatus status;
bytes32 performanceCriteria; // Hash of performance requirements
uint256 minAccuracy;
uint256 deadline;
uint256 creationTime;
uint256 maxSubmissions;
uint256 submissionCount;
address winningSubmission;
bool requiresZKProof;
mapping(address => bool) authorizedSubmitters;
}
struct Submission {
uint256 submissionId;
uint256 bountyId;
address submitter;
bytes zkProof;
bytes32 performanceHash;
uint256 accuracy;
uint256 responseTime;
uint256 submissionTime;
SubmissionStatus status;
string disputeReason;
address verifier;
}
struct BountyStats {
uint256 totalBounties;
uint256 activeBounties;
uint256 completedBounties;
uint256 totalValueLocked;
uint256 averageReward;
uint256 successRate;
}
// Mappings
mapping(uint256 => Bounty) public bounties;
mapping(uint256 => Submission) public submissions;
mapping(uint256 => uint256[]) public bountySubmissions;
mapping(address => uint256[]) public userSubmissions;
mapping(address => uint256[]) public creatorBounties;
mapping(BountyTier => uint256) public tierRequirements;
mapping(uint256 => mapping(address => bool)) public hasSubmitted;
// Arrays
uint256[] public activeBountyIds;
address[] public authorizedCreators;
// Events
event BountyCreated(
uint256 indexed bountyId,
string title,
uint256 rewardAmount,
address indexed creator,
BountyTier tier,
uint256 deadline
);
event BountySubmitted(
uint256 indexed bountyId,
uint256 indexed submissionId,
address indexed submitter,
bytes32 performanceHash,
uint256 accuracy
);
event BountyVerified(
uint256 indexed bountyId,
uint256 indexed submissionId,
address indexed submitter,
bool success,
uint256 rewardAmount
);
event BountyCompleted(
uint256 indexed bountyId,
address indexed winner,
uint256 rewardAmount,
uint256 completionTime
);
event BountyExpired(
uint256 indexed bountyId,
uint256 refundAmount
);
event BountyDisputed(
uint256 indexed bountyId,
uint256 indexed submissionId,
address indexed disputer,
string reason
);
event PlatformFeeCollected(
uint256 indexed bountyId,
uint256 feeAmount,
address indexed collector
);
// Modifiers
modifier bountyExists(uint256 _bountyId) {
require(_bountyId < bountyCounter, "Bounty does not exist");
_;
}
modifier onlyAuthorizedCreator() {
require(isAuthorizedCreator(msg.sender), "Not authorized to create bounties");
_;
}
modifier validBountyStatus(uint256 _bountyId, BountyStatus _requiredStatus) {
require(bounties[_bountyId].status == _requiredStatus, "Invalid bounty status");
_;
}
modifier beforeDeadline(uint256 _deadline) {
require(block.timestamp <= _deadline, "Deadline passed");
_;
}
modifier sufficientBalance(uint256 _amount) {
require(aitbcToken.balanceOf(msg.sender) >= _amount, "Insufficient balance");
_;
}
constructor(address _aitbcToken, address _performanceVerifier) {
aitbcToken = IERC20(_aitbcToken);
performanceVerifier = PerformanceVerifier(_performanceVerifier);
// Set tier requirements (minimum reward amounts)
tierRequirements[BountyTier.BRONZE] = 100 * 10**18; // 100 AITBC
tierRequirements[BountyTier.SILVER] = 500 * 10**18; // 500 AITBC
tierRequirements[BountyTier.GOLD] = 1000 * 10**18; // 1000 AITBC
tierRequirements[BountyTier.PLATINUM] = 5000 * 10**18; // 5000 AITBC
}
/**
* @dev Creates a new bounty
* @param _title Bounty title
* @param _description Detailed description
* @param _rewardAmount Reward amount in AITBC tokens
* @param _tier Bounty tier
* @param _performanceCriteria Hash of performance requirements
* @param _minAccuracy Minimum accuracy required
* @param _deadline Bounty deadline
* @param _maxSubmissions Maximum number of submissions allowed
* @param _requiresZKProof Whether ZK-proof is required
*/
function createBounty(
string memory _title,
string memory _description,
uint256 _rewardAmount,
BountyTier _tier,
bytes32 _performanceCriteria,
uint256 _minAccuracy,
uint256 _deadline,
uint256 _maxSubmissions,
bool _requiresZKProof
) external
onlyAuthorizedCreator
sufficientBalance(_rewardAmount)
beforeDeadline(_deadline)
nonReentrant
returns (uint256)
{
require(_rewardAmount >= tierRequirements[_tier], "Reward below tier minimum");
require(_minAccuracy <= 100, "Invalid accuracy");
require(_maxSubmissions > 0, "Invalid max submissions");
require(_deadline > block.timestamp, "Invalid deadline");
uint256 bountyId = bountyCounter++;
Bounty storage bounty = bounties[bountyId];
bounty.bountyId = bountyId;
bounty.title = _title;
bounty.description = _description;
bounty.rewardAmount = _rewardAmount;
bounty.creator = msg.sender;
bounty.tier = _tier;
bounty.status = BountyStatus.CREATED;
bounty.performanceCriteria = _performanceCriteria;
bounty.minAccuracy = _minAccuracy;
bounty.deadline = _deadline;
bounty.creationTime = block.timestamp;
bounty.maxSubmissions = _maxSubmissions;
bounty.submissionCount = 0;
bounty.requiresZKProof = _requiresZKProof;
// Calculate and collect creation fee
uint256 creationFee = (_rewardAmount * creationFeePercentage) / 10000;
uint256 totalRequired = _rewardAmount + creationFee;
require(aitbcToken.balanceOf(msg.sender) >= totalRequired, "Insufficient total amount");
// Transfer tokens to contract
require(aitbcToken.transferFrom(msg.sender, address(this), totalRequired), "Transfer failed");
// Transfer creation fee to DAO treasury (owner for now)
if (creationFee > 0) {
require(aitbcToken.transfer(owner(), creationFee), "Fee transfer failed");
emit PlatformFeeCollected(bountyId, creationFee, owner());
}
// Update tracking arrays
activeBountyIds.push(bountyId);
creatorBounties[msg.sender].push(bountyId);
// Activate bounty
bounty.status = BountyStatus.ACTIVE;
emit BountyCreated(bountyId, _title, _rewardAmount, msg.sender, _tier, _deadline);
return bountyId;
}
/**
* @dev Submits a solution to a bounty
* @param _bountyId Bounty ID
* @param _zkProof Zero-knowledge proof (if required)
* @param _performanceHash Hash of performance metrics
* @param _accuracy Achieved accuracy
* @param _responseTime Response time in milliseconds
*/
function submitBountySolution(
uint256 _bountyId,
bytes memory _zkProof,
bytes32 _performanceHash,
uint256 _accuracy,
uint256 _responseTime
) external
bountyExists(_bountyId)
validBountyStatus(_bountyId, BountyStatus.ACTIVE)
beforeDeadline(bounties[_bountyId].deadline)
nonReentrant
returns (uint256)
{
Bounty storage bounty = bounties[_bountyId];
require(!hasSubmitted[_bountyId][msg.sender], "Already submitted");
require(bounty.submissionCount < bounty.maxSubmissions, "Max submissions reached");
if (bounty.requiresZKProof) {
require(_zkProof.length > 0, "ZK-proof required");
}
uint256 submissionId = bounty.submissionCount; // Use count as ID
Submission storage submission = submissions[submissionId];
submission.submissionId = submissionId;
submission.bountyId = _bountyId;
submission.submitter = msg.sender;
submission.zkProof = _zkProof;
submission.performanceHash = _performanceHash;
submission.accuracy = _accuracy;
submission.responseTime = _responseTime;
submission.submissionTime = block.timestamp;
submission.status = SubmissionStatus.PENDING;
// Update tracking
bounty.submissionCount++;
hasSubmitted[_bountyId][msg.sender] = true;
bountySubmissions[_bountyId].push(submissionId);
userSubmissions[msg.sender].push(submissionId);
// Auto-verify if ZK-proof is provided
if (_zkProof.length > 0) {
_verifySubmission(_bountyId, submissionId);
}
emit BountySubmitted(_bountyId, submissionId, msg.sender, _performanceHash, _accuracy);
return submissionId;
}
/**
* @dev Manually verifies a submission (oracle or automated)
* @param _bountyId Bounty ID
* @param _submissionId Submission ID
* @param _verified Whether the submission is verified
* @param _verifier Address of the verifier
*/
function verifySubmission(
uint256 _bountyId,
uint256 _submissionId,
bool _verified,
address _verifier
) external
bountyExists(_bountyId)
nonReentrant
{
Bounty storage bounty = bounties[_bountyId];
Submission storage submission = submissions[_submissionId];
require(submission.status == SubmissionStatus.PENDING, "Submission not pending");
require(submission.bountyId == _bountyId, "Submission bounty mismatch");
submission.status = _verified ? SubmissionStatus.VERIFIED : SubmissionStatus.REJECTED;
submission.verifier = _verifier;
if (_verified) {
// Check if this meets the bounty requirements
if (submission.accuracy >= bounty.minAccuracy) {
_completeBounty(_bountyId, _submissionId);
}
}
emit BountyVerified(_bountyId, _submissionId, submission.submitter, _verified, bounty.rewardAmount);
}
/**
* @dev Disputes a submission
* @param _bountyId Bounty ID
* @param _submissionId Submission ID
* @param _reason Reason for dispute
*/
function disputeSubmission(
uint256 _bountyId,
uint256 _submissionId,
string memory _reason
) external
bountyExists(_bountyId)
nonReentrant
{
Bounty storage bounty = bounties[_bountyId];
Submission storage submission = submissions[_submissionId];
require(submission.status == SubmissionStatus.VERIFIED, "Can only dispute verified submissions");
require(block.timestamp - submission.submissionTime <= 86400, "Dispute window expired"); // 24 hours
submission.status = SubmissionStatus.DISPUTED;
submission.disputeReason = _reason;
bounty.status = BountyStatus.DISPUTED;
// Collect dispute fee
uint256 disputeFee = (bounty.rewardAmount * disputeFeePercentage) / 10000;
if (disputeFee > 0) {
require(aitbcToken.transferFrom(msg.sender, address(this), disputeFee), "Dispute fee transfer failed");
}
emit BountyDisputed(_bountyId, _submissionId, msg.sender, _reason);
}
/**
* @dev Resolves a dispute
* @param _bountyId Bounty ID
* @param _submissionId Submission ID
* @param _upholdDispute Whether to uphold the dispute
*/
function resolveDispute(
uint256 _bountyId,
uint256 _submissionId,
bool _upholdDispute
) external onlyOwner bountyExists(_bountyId) nonReentrant {
Bounty storage bounty = bounties[_bountyId];
Submission storage submission = submissions[_submissionId];
require(bounty.status == BountyStatus.DISPUTED, "No dispute to resolve");
require(submission.status == SubmissionStatus.DISPUTED, "Submission not disputed");
if (_upholdDispute) {
// Reject the submission
submission.status = SubmissionStatus.REJECTED;
bounty.status = BountyStatus.ACTIVE;
// Return dispute fee
uint256 disputeFee = (bounty.rewardAmount * disputeFeePercentage) / 10000;
if (disputeFee > 0) {
require(aitbcToken.transfer(msg.sender, disputeFee), "Dispute fee return failed");
}
} else {
// Uphold the submission
submission.status = SubmissionStatus.VERIFIED;
_completeBounty(_bountyId, _submissionId);
}
}
/**
* @dev Expires a bounty and returns funds to creator
* @param _bountyId Bounty ID
*/
function expireBounty(uint256 _bountyId) external bountyExists(_bountyId) nonReentrant {
Bounty storage bounty = bounties[_bountyId];
require(bounty.status == BountyStatus.ACTIVE, "Bounty not active");
require(block.timestamp > bounty.deadline, "Deadline not passed");
bounty.status = BountyStatus.EXPIRED;
// Return funds to creator
uint256 refundAmount = bounty.rewardAmount;
require(aitbcToken.transfer(bounty.creator, refundAmount), "Refund transfer failed");
// Remove from active bounties
_removeFromActiveBounties(_bountyId);
emit BountyExpired(_bountyId, refundAmount);
}
/**
* @dev Authorizes a creator to create bounties
* @param _creator Address to authorize
*/
function authorizeCreator(address _creator) external onlyOwner {
require(_creator != address(0), "Invalid address");
require(!isAuthorizedCreator(_creator), "Already authorized");
authorizedCreators.push(_creator);
bounties[0].authorizedSubmitters[_creator] = true; // Use bounty 0 as storage
}
/**
* @dev Revokes creator authorization
* @param _creator Address to revoke
*/
function revokeCreator(address _creator) external onlyOwner {
require(isAuthorizedCreator(_creator), "Not authorized");
bounties[0].authorizedSubmitters[_creator] = false; // Use bounty 0 as storage
// Remove from array
for (uint256 i = 0; i < authorizedCreators.length; i++) {
if (authorizedCreators[i] == _creator) {
authorizedCreators[i] = authorizedCreators[authorizedCreators.length - 1];
authorizedCreators.pop();
break;
}
}
}
/**
* @dev Updates fee percentages
* @param _creationFee New creation fee percentage
* @param _successFee New success fee percentage
* @param _platformFee New platform fee percentage
*/
function updateFees(
uint256 _creationFee,
uint256 _successFee,
uint256 _platformFee
) external onlyOwner {
require(_creationFee <= 500, "Creation fee too high"); // Max 5%
require(_successFee <= 500, "Success fee too high"); // Max 5%
require(_platformFee <= 500, "Platform fee too high"); // Max 5%
creationFeePercentage = _creationFee;
successFeePercentage = _successFee;
platformFeePercentage = _platformFee;
}
/**
* @dev Updates tier requirements
* @param _tier Bounty tier
* @param _minimumReward New minimum reward
*/
function updateTierRequirement(BountyTier _tier, uint256 _minimumReward) external onlyOwner {
tierRequirements[_tier] = _minimumReward;
}
// View functions
/**
* @dev Gets bounty details
* @param _bountyId Bounty ID
*/
function getBounty(uint256 _bountyId) external view bountyExists(_bountyId) returns (
string memory title,
string memory description,
uint256 rewardAmount,
address creator,
BountyTier tier,
BountyStatus status,
bytes32 performanceCriteria,
uint256 minAccuracy,
uint256 deadline,
uint256 creationTime,
uint256 maxSubmissions,
uint256 submissionCount,
bool requiresZKProof
) {
Bounty storage bounty = bounties[_bountyId];
return (
bounty.title,
bounty.description,
bounty.rewardAmount,
bounty.creator,
bounty.tier,
bounty.status,
bounty.performanceCriteria,
bounty.minAccuracy,
bounty.deadline,
bounty.creationTime,
bounty.maxSubmissions,
bounty.submissionCount,
bounty.requiresZKProof
);
}
/**
* @dev Gets submission details
* @param _submissionId Submission ID
*/
function getSubmission(uint256 _submissionId) external view returns (
uint256 bountyId,
address submitter,
bytes32 performanceHash,
uint256 accuracy,
uint256 responseTime,
uint256 submissionTime,
SubmissionStatus status,
address verifier
) {
Submission storage submission = submissions[_submissionId];
return (
submission.bountyId,
submission.submitter,
submission.performanceHash,
submission.accuracy,
submission.responseTime,
submission.submissionTime,
submission.status,
submission.verifier
);
}
/**
* @dev Gets all submissions for a bounty
* @param _bountyId Bounty ID
*/
function getBountySubmissions(uint256 _bountyId) external view bountyExists(_bountyId) returns (uint256[] memory) {
return bountySubmissions[_bountyId];
}
/**
* @dev Gets all bounties created by a user
* @param _creator Creator address
*/
function getCreatorBounties(address _creator) external view returns (uint256[] memory) {
return creatorBounties[_creator];
}
/**
* @dev Gets all submissions by a user
* @param _submitter Submitter address
*/
function getUserSubmissions(address _submitter) external view returns (uint256[] memory) {
return userSubmissions[_submitter];
}
/**
* @dev Gets all active bounty IDs
*/
function getActiveBounties() external view returns (uint256[] memory) {
return activeBountyIds;
}
/**
* @dev Gets bounty statistics
*/
function getBountyStats() external view returns (BountyStats memory) {
uint256 totalValue = 0;
uint256 activeCount = 0;
uint256 completedCount = 0;
for (uint256 i = 0; i < bountyCounter; i++) {
if (bounties[i].status == BountyStatus.ACTIVE) {
activeCount++;
totalValue += bounties[i].rewardAmount;
} else if (bounties[i].status == BountyStatus.COMPLETED) {
completedCount++;
totalValue += bounties[i].rewardAmount;
}
}
uint256 avgReward = bountyCounter > 0 ? totalValue / bountyCounter : 0;
uint256 successRate = completedCount > 0 ? (completedCount * 100) / bountyCounter : 0;
return BountyStats({
totalBounties: bountyCounter,
activeBounties: activeCount,
completedBounties: completedCount,
totalValueLocked: totalValue,
averageReward: avgReward,
successRate: successRate
});
}
/**
* @dev Checks if an address is authorized to create bounties
* @param _creator Address to check
*/
function isAuthorizedCreator(address _creator) public view returns (bool) {
return bounties[0].authorizedSubmitters[_creator]; // Use bounty 0 as storage
}
// Internal functions
function _verifySubmission(uint256 _bountyId, uint256 _submissionId) internal {
Bounty storage bounty = bounties[_bountyId];
Submission storage submission = submissions[_submissionId];
// Verify ZK-proof using PerformanceVerifier
bool proofValid = performanceVerifier.verifyPerformanceProof(
0, // Use dummy agreement ID for bounty verification
submission.responseTime,
submission.accuracy,
95, // Default availability
100, // Default compute power
submission.zkProof
);
if (proofValid && submission.accuracy >= bounty.minAccuracy) {
submission.status = SubmissionStatus.VERIFIED;
_completeBounty(_bountyId, _submissionId);
} else {
submission.status = SubmissionStatus.REJECTED;
}
}
function _completeBounty(uint256 _bountyId, uint256 _submissionId) internal {
Bounty storage bounty = bounties[_bountyId];
Submission storage submission = submissions[_submissionId];
require(bounty.status == BountyStatus.ACTIVE || bounty.status == BountyStatus.SUBMITTED, "Bounty not active");
bounty.status = BountyStatus.COMPLETED;
bounty.winningSubmission = submission.submitter;
// Calculate fees
uint256 successFee = (bounty.rewardAmount * successFeePercentage) / 10000;
uint256 platformFee = (bounty.rewardAmount * platformFeePercentage) / 10000;
uint256 totalFees = successFee + platformFee;
uint256 winnerReward = bounty.rewardAmount - totalFees;
// Transfer reward to winner
if (winnerReward > 0) {
require(aitbcToken.transfer(submission.submitter, winnerReward), "Reward transfer failed");
}
// Transfer fees to treasury
if (totalFees > 0) {
require(aitbcToken.transfer(owner(), totalFees), "Fee transfer failed");
emit PlatformFeeCollected(_bountyId, totalFees, owner());
}
// Remove from active bounties
_removeFromActiveBounties(_bountyId);
emit BountyCompleted(_bountyId, submission.submitter, winnerReward, block.timestamp);
}
function _removeFromActiveBounties(uint256 _bountyId) internal {
for (uint256 i = 0; i < activeBountyIds.length; i++) {
if (activeBountyIds[i] == _bountyId) {
activeBountyIds[i] = activeBountyIds[activeBountyIds.length - 1];
activeBountyIds.pop();
break;
}
}
}
/**
* @dev Emergency pause function
*/
function pause() external onlyOwner {
_pause();
}
/**
* @dev Unpause function
*/
function unpause() external onlyOwner {
_unpause();
}
}

View File

@@ -0,0 +1,827 @@
// 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 "./PerformanceVerifier.sol";
import "./AIToken.sol";
/**
* @title Agent Staking System
* @dev Reputation-based yield farming for AI agents with dynamic APY calculation
* @notice Allows users to stake AITBC tokens on agent wallets and earn rewards based on agent performance
*/
contract AgentStaking is Ownable, ReentrancyGuard, Pausable {
// State variables
IERC20 public aitbcToken;
PerformanceVerifier public performanceVerifier;
uint256 public stakeCounter;
uint256 public baseAPY = 500; // 5% base APY in basis points
uint256 public maxAPY = 2000; // 20% max APY in basis points
uint256 public minStakeAmount = 100 * 10**18; // 100 AITBC minimum
uint256 public maxStakeAmount = 100000 * 10**18; // 100k AITBC maximum
uint256 public unbondingPeriod = 7 days;
uint256 public rewardDistributionInterval = 1 days;
uint256 public platformFeePercentage = 100; // 1% platform fee
uint256 public earlyUnbondPenalty = 1000; // 10% penalty for early unbonding
// Staking status
enum StakeStatus { ACTIVE, UNBONDING, COMPLETED, SLASHED }
// Agent performance tier
enum PerformanceTier { BRONZE, SILVER, GOLD, PLATINUM, DIAMOND }
// Structs
struct Stake {
uint256 stakeId;
address staker;
address agentWallet;
uint256 amount;
uint256 lockPeriod;
uint256 startTime;
uint256 endTime;
StakeStatus status;
uint256 accumulatedRewards;
uint256 lastRewardTime;
uint256 currentAPY;
PerformanceTier agentTier;
bool autoCompound;
}
struct AgentMetrics {
address agentWallet;
uint256 totalStaked;
uint256 stakerCount;
uint256 totalRewardsDistributed;
uint256 averageAccuracy;
uint256 totalSubmissions;
uint256 successfulSubmissions;
uint256 lastUpdateTime;
PerformanceTier currentTier;
uint256 tierScore;
}
struct StakingPool {
address agentWallet;
uint256 totalStaked;
uint256 totalRewards;
uint256 poolAPY;
uint256 lastDistributionTime;
mapping(address => uint256) stakerShares;
address[] stakers;
}
struct RewardCalculation {
uint256 baseRewards;
uint256 performanceBonus;
uint256 lockBonus;
uint256 tierBonus;
uint256 totalRewards;
uint256 platformFee;
}
// Mappings
mapping(uint256 => Stake) public stakes;
mapping(address => uint256[]) public stakerStakes;
mapping(address => uint256[]) public agentStakes;
mapping(address => AgentMetrics) public agentMetrics;
mapping(address => StakingPool) public stakingPools;
mapping(PerformanceTier => uint256) public tierMultipliers;
mapping(uint256 => uint256) public lockPeriodMultipliers;
// Arrays
address[] public supportedAgents;
uint256[] public activeStakeIds;
// Events
event StakeCreated(
uint256 indexed stakeId,
address indexed staker,
address indexed agentWallet,
uint256 amount,
uint256 lockPeriod,
uint256 apy
);
event StakeUpdated(
uint256 indexed stakeId,
uint256 newAmount,
uint256 newAPY
);
event RewardsDistributed(
uint256 indexed stakeId,
address indexed staker,
uint256 rewardAmount,
uint256 platformFee
);
event StakeUnbonded(
uint256 indexed stakeId,
address indexed staker,
uint256 amount,
uint256 penalty
);
event StakeCompleted(
uint256 indexed stakeId,
address indexed staker,
uint256 totalAmount,
uint256 totalRewards
);
event AgentTierUpdated(
address indexed agentWallet,
PerformanceTier oldTier,
PerformanceTier newTier,
uint256 tierScore
);
event PoolRewardsDistributed(
address indexed agentWallet,
uint256 totalRewards,
uint256 stakerCount
);
event PlatformFeeCollected(
uint256 indexed stakeId,
uint256 feeAmount,
address indexed collector
);
// Modifiers
modifier stakeExists(uint256 _stakeId) {
require(_stakeId < stakeCounter, "Stake does not exist");
_;
}
modifier onlyStakeOwner(uint256 _stakeId) {
require(stakes[_stakeId].staker == msg.sender, "Not stake owner");
_;
}
modifier supportedAgent(address _agentWallet) {
require(agentMetrics[_agentWallet].agentWallet != address(0) || _agentWallet == address(0), "Agent not supported");
_;
}
modifier validStakeAmount(uint256 _amount) {
require(_amount >= minStakeAmount && _amount <= maxStakeAmount, "Invalid stake amount");
_;
}
modifier sufficientBalance(uint256 _amount) {
require(aitbcToken.balanceOf(msg.sender) >= _amount, "Insufficient balance");
_;
}
constructor(address _aitbcToken, address _performanceVerifier) {
aitbcToken = IERC20(_aitbcToken);
performanceVerifier = PerformanceVerifier(_performanceVerifier);
// Set tier multipliers (in basis points)
tierMultipliers[PerformanceTier.BRONZE] = 1000; // 1x
tierMultipliers[PerformanceTier.SILVER] = 1200; // 1.2x
tierMultipliers[PerformanceTier.GOLD] = 1500; // 1.5x
tierMultipliers[PerformanceTier.PLATINUM] = 2000; // 2x
tierMultipliers[PerformanceTier.DIAMOND] = 3000; // 3x
// Set lock period multipliers
lockPeriodMultipliers[30 days] = 1100; // 1.1x for 30 days
lockPeriodMultipliers[90 days] = 1250; // 1.25x for 90 days
lockPeriodMultipliers[180 days] = 1500; // 1.5x for 180 days
lockPeriodMultipliers[365 days] = 2000; // 2x for 365 days
}
/**
* @dev Creates a new stake on an agent wallet
* @param _agentWallet Address of the agent wallet
* @param _amount Amount to stake
* @param _lockPeriod Lock period in seconds
* @param _autoCompound Whether to auto-compound rewards
*/
function stakeOnAgent(
address _agentWallet,
uint256 _amount,
uint256 _lockPeriod,
bool _autoCompound
) external
supportedAgent(_agentWallet)
validStakeAmount(_amount)
sufficientBalance(_amount)
nonReentrant
returns (uint256)
{
require(_lockPeriod >= 1 days, "Lock period too short");
require(_lockPeriod <= 365 days, "Lock period too long");
uint256 stakeId = stakeCounter++;
// Calculate initial APY
PerformanceTier agentTier = _getAgentTier(_agentWallet);
uint256 apy = _calculateAPY(_agentWallet, _lockPeriod, agentTier);
Stake storage stake = stakes[stakeId];
stake.stakeId = stakeId;
stake.staker = msg.sender;
stake.agentWallet = _agentWallet;
stake.amount = _amount;
stake.lockPeriod = _lockPeriod;
stake.startTime = block.timestamp;
stake.endTime = block.timestamp + _lockPeriod;
stake.status = StakeStatus.ACTIVE;
stake.accumulatedRewards = 0;
stake.lastRewardTime = block.timestamp;
stake.currentAPY = apy;
stake.agentTier = agentTier;
stake.autoCompound = _autoCompound;
// Update agent metrics
_updateAgentMetrics(_agentWallet, _amount, true);
// Update staking pool
_updateStakingPool(_agentWallet, msg.sender, _amount, true);
// Update tracking arrays
stakerStakes[msg.sender].push(stakeId);
agentStakes[_agentWallet].push(stakeId);
activeStakeIds.push(stakeId);
// Transfer tokens to contract
require(aitbcToken.transferFrom(msg.sender, address(this), _amount), "Transfer failed");
emit StakeCreated(stakeId, msg.sender, _agentWallet, _amount, _lockPeriod, apy);
return stakeId;
}
/**
* @dev Adds more tokens to an existing stake
* @param _stakeId Stake ID
* @param _additionalAmount Additional amount to stake
*/
function addToStake(
uint256 _stakeId,
uint256 _additionalAmount
) external
stakeExists(_stakeId)
onlyStakeOwner(_stakeId)
validStakeAmount(_additionalAmount)
sufficientBalance(_additionalAmount)
nonReentrant
{
Stake storage stake = stakes[_stakeId];
require(stake.status == StakeStatus.ACTIVE, "Stake not active");
// Calculate new APY
uint256 newTotalAmount = stake.amount + _additionalAmount;
uint256 newAPY = _calculateAPY(stake.agentWallet, stake.lockPeriod, stake.agentTier);
// Update stake
stake.amount = newTotalAmount;
stake.currentAPY = newAPY;
// Update agent metrics
_updateAgentMetrics(stake.agentWallet, _additionalAmount, true);
// Update staking pool
_updateStakingPool(stake.agentWallet, msg.sender, _additionalAmount, true);
// Transfer additional tokens
require(aitbcToken.transferFrom(msg.sender, address(this), _additionalAmount), "Transfer failed");
emit StakeUpdated(_stakeId, newTotalAmount, newAPY);
}
/**
* @dev Initiates unbonding for a stake
* @param _stakeId Stake ID
*/
function unbondStake(uint256 _stakeId) external
stakeExists(_stakeId)
onlyStakeOwner(_stakeId)
nonReentrant
{
Stake storage stake = stakes[_stakeId];
require(stake.status == StakeStatus.ACTIVE, "Stake not active");
require(block.timestamp >= stake.endTime, "Lock period not ended");
// Calculate final rewards
_calculateRewards(_stakeId);
stake.status = StakeStatus.UNBONDING;
// Remove from active stakes
_removeFromActiveStakes(_stakeId);
}
/**
* @dev Completes unbonding and returns stake + rewards
* @param _stakeId Stake ID
*/
function completeUnbonding(uint256 _stakeId) external
stakeExists(_stakeId)
onlyStakeOwner(_stakeId)
nonReentrant
{
Stake storage stake = stakes[_stakeId];
require(stake.status == StakeStatus.UNBONDING, "Stake not unbonding");
require(block.timestamp >= stake.endTime + unbondingPeriod, "Unbonding period not ended");
uint256 totalAmount = stake.amount;
uint256 totalRewards = stake.accumulatedRewards;
// Apply early unbonding penalty if applicable
uint256 penalty = 0;
if (block.timestamp < stake.endTime + 30 days) {
penalty = (totalAmount * earlyUnbondPenalty) / 10000;
totalAmount -= penalty;
}
stake.status = StakeStatus.COMPLETED;
// Update agent metrics
_updateAgentMetrics(stake.agentWallet, stake.amount, false);
// Update staking pool
_updateStakingPool(stake.agentWallet, msg.sender, stake.amount, false);
// Transfer tokens back to staker
if (totalAmount > 0) {
require(aitbcToken.transfer(msg.sender, totalAmount), "Stake transfer failed");
}
if (totalRewards > 0) {
require(aitbcToken.transfer(msg.sender, totalRewards), "Rewards transfer failed");
}
emit StakeCompleted(_stakeId, msg.sender, totalAmount, totalRewards);
emit StakeUnbonded(_stakeId, msg.sender, totalAmount, penalty);
}
/**
* @dev Distributes agent earnings to stakers
* @param _agentWallet Agent wallet address
* @param _totalEarnings Total earnings to distribute
*/
function distributeAgentEarnings(
address _agentWallet,
uint256 _totalEarnings
) external
supportedAgent(_agentWallet)
nonReentrant
{
require(_totalEarnings > 0, "No earnings to distribute");
StakingPool storage pool = stakingPools[_agentWallet];
require(pool.totalStaked > 0, "No stakers in pool");
// Calculate platform fee
uint256 platformFee = (_totalEarnings * platformFeePercentage) / 10000;
uint256 distributableAmount = _totalEarnings - platformFee;
// Transfer platform fee
if (platformFee > 0) {
require(aitbcToken.transferFrom(msg.sender, owner(), platformFee), "Platform fee transfer failed");
}
// Transfer distributable amount to contract
require(aitbcToken.transferFrom(msg.sender, address(this), distributableAmount), "Earnings transfer failed");
// Distribute to stakers proportionally
uint256 totalDistributed = 0;
for (uint256 i = 0; i < pool.stakers.length; i++) {
address staker = pool.stakers[i];
uint256 stakerShare = pool.stakerShares[staker];
uint256 stakerReward = (distributableAmount * stakerShare) / pool.totalStaked;
if (stakerReward > 0) {
// Find and update all stakes for this staker on this agent
uint256[] storage stakesForAgent = agentStakes[_agentWallet];
for (uint256 j = 0; j < stakesForAgent.length; j++) {
uint256 stakeId = stakesForAgent[j];
Stake storage stake = stakes[stakeId];
if (stake.staker == staker && stake.status == StakeStatus.ACTIVE) {
stake.accumulatedRewards += stakerReward;
break;
}
}
totalDistributed += stakerReward;
}
}
// Update agent metrics
agentMetrics[_agentWallet].totalRewardsDistributed += totalDistributed;
emit PoolRewardsDistributed(_agentWallet, totalDistributed, pool.stakers.length);
}
/**
* @dev Updates agent performance metrics and tier
* @param _agentWallet Agent wallet address
* @param _accuracy Latest accuracy score
* @param _successful Whether the submission was successful
*/
function updateAgentPerformance(
address _agentWallet,
uint256 _accuracy,
bool _successful
) external
supportedAgent(_agentWallet)
nonReentrant
{
AgentMetrics storage metrics = agentMetrics[_agentWallet];
metrics.totalSubmissions++;
if (_successful) {
metrics.successfulSubmissions++;
}
// Update average accuracy (weighted average)
uint256 totalAccuracy = metrics.averageAccuracy * (metrics.totalSubmissions - 1) + _accuracy;
metrics.averageAccuracy = totalAccuracy / metrics.totalSubmissions;
metrics.lastUpdateTime = block.timestamp;
// Calculate new tier
PerformanceTier newTier = _calculateAgentTier(_agentWallet);
PerformanceTier oldTier = metrics.currentTier;
if (newTier != oldTier) {
metrics.currentTier = newTier;
// Update APY for all active stakes on this agent
uint256[] storage stakesForAgent = agentStakes[_agentWallet];
for (uint256 i = 0; i < stakesForAgent.length; i++) {
uint256 stakeId = stakesForAgent[i];
Stake storage stake = stakes[stakeId];
if (stake.status == StakeStatus.ACTIVE) {
stake.currentAPY = _calculateAPY(_agentWallet, stake.lockPeriod, newTier);
stake.agentTier = newTier;
}
}
emit AgentTierUpdated(_agentWallet, oldTier, newTier, metrics.tierScore);
}
}
/**
* @dev Adds a supported agent
* @param _agentWallet Agent wallet address
* @param _initialTier Initial performance tier
*/
function addSupportedAgent(
address _agentWallet,
PerformanceTier _initialTier
) external onlyOwner {
require(_agentWallet != address(0), "Invalid agent address");
require(agentMetrics[_agentWallet].agentWallet == address(0), "Agent already supported");
agentMetrics[_agentWallet] = AgentMetrics({
agentWallet: _agentWallet,
totalStaked: 0,
stakerCount: 0,
totalRewardsDistributed: 0,
averageAccuracy: 0,
totalSubmissions: 0,
successfulSubmissions: 0,
lastUpdateTime: block.timestamp,
currentTier: _initialTier,
tierScore: _getTierScore(_initialTier)
});
// Initialize staking pool
stakingPools[_agentWallet].agentWallet = _agentWallet;
stakingPools[_agentWallet].totalStaked = 0;
stakingPools[_agentWallet].totalRewards = 0;
stakingPools[_agentWallet].poolAPY = baseAPY;
stakingPools[_agentWallet].lastDistributionTime = block.timestamp;
supportedAgents.push(_agentWallet);
}
/**
* @dev Removes a supported agent
* @param _agentWallet Agent wallet address
*/
function removeSupportedAgent(address _agentWallet) external onlyOwner {
require(agentMetrics[_agentWallet].agentWallet != address(0), "Agent not supported");
require(agentMetrics[_agentWallet].totalStaked == 0, "Agent has active stakes");
// Remove from supported agents
for (uint256 i = 0; i < supportedAgents.length; i++) {
if (supportedAgents[i] == _agentWallet) {
supportedAgents[i] = supportedAgents[supportedAgents.length - 1];
supportedAgents.pop();
break;
}
}
delete agentMetrics[_agentWallet];
delete stakingPools[_agentWallet];
}
/**
* @dev Updates configuration parameters
* @param _baseAPY New base APY
* @param _maxAPY New maximum APY
* @param _platformFee New platform fee percentage
*/
function updateConfiguration(
uint256 _baseAPY,
uint256 _maxAPY,
uint256 _platformFee
) external onlyOwner {
require(_baseAPY <= _maxAPY, "Base APY cannot exceed max APY");
require(_maxAPY <= 5000, "Max APY too high"); // Max 50%
require(_platformFee <= 500, "Platform fee too high"); // Max 5%
baseAPY = _baseAPY;
maxAPY = _maxAPY;
platformFeePercentage = _platformFee;
}
// View functions
/**
* @dev Gets stake details
* @param _stakeId Stake ID
*/
function getStake(uint256 _stakeId) external view stakeExists(_stakeId) returns (
address staker,
address agentWallet,
uint256 amount,
uint256 lockPeriod,
uint256 startTime,
uint256 endTime,
StakeStatus status,
uint256 accumulatedRewards,
uint256 currentAPY,
PerformanceTier agentTier,
bool autoCompound
) {
Stake storage stake = stakes[_stakeId];
return (
stake.staker,
stake.agentWallet,
stake.amount,
stake.lockPeriod,
stake.startTime,
stake.endTime,
stake.status,
stake.accumulatedRewards,
stake.currentAPY,
stake.agentTier,
stake.autoCompound
);
}
/**
* @dev Gets agent metrics
* @param _agentWallet Agent wallet address
*/
function getAgentMetrics(address _agentWallet) external view returns (
uint256 totalStaked,
uint256 stakerCount,
uint256 totalRewardsDistributed,
uint256 averageAccuracy,
uint256 totalSubmissions,
uint256 successfulSubmissions,
PerformanceTier currentTier,
uint256 tierScore
) {
AgentMetrics storage metrics = agentMetrics[_agentWallet];
return (
metrics.totalStaked,
metrics.stakerCount,
metrics.totalRewardsDistributed,
metrics.averageAccuracy,
metrics.totalSubmissions,
metrics.successfulSubmissions,
metrics.currentTier,
metrics.tierScore
);
}
/**
* @dev Gets staking pool information
* @param _agentWallet Agent wallet address
*/
function getStakingPool(address _agentWallet) external view returns (
uint256 totalStaked,
uint256 totalRewards,
uint256 poolAPY,
uint256 stakerCount
) {
StakingPool storage pool = stakingPools[_agentWallet];
return (
pool.totalStaked,
pool.totalRewards,
pool.poolAPY,
pool.stakers.length
);
}
/**
* @dev Calculates current rewards for a stake
* @param _stakeId Stake ID
*/
function calculateRewards(uint256 _stakeId) external view stakeExists(_stakeId) returns (uint256) {
Stake storage stake = stakes[_stakeId];
if (stake.status != StakeStatus.ACTIVE) {
return stake.accumulatedRewards;
}
uint256 timeElapsed = block.timestamp - stake.lastRewardTime;
uint256 yearlyRewards = (stake.amount * stake.currentAPY) / 10000;
uint256 currentRewards = (yearlyRewards * timeElapsed) / 365 days;
return stake.accumulatedRewards + currentRewards;
}
/**
* @dev Gets all stakes for a staker
* @param _staker Staker address
*/
function getStakerStakes(address _staker) external view returns (uint256[] memory) {
return stakerStakes[_staker];
}
/**
* @dev Gets all stakes for an agent
* @param _agentWallet Agent wallet address
*/
function getAgentStakes(address _agentWallet) external view returns (uint256[] memory) {
return agentStakes[_agentWallet];
}
/**
* @dev Gets all supported agents
*/
function getSupportedAgents() external view returns (address[] memory) {
return supportedAgents;
}
/**
* @dev Gets all active stake IDs
*/
function getActiveStakes() external view returns (uint256[] memory) {
return activeStakeIds;
}
/**
* @dev Calculates APY for a stake
* @param _agentWallet Agent wallet address
* @param _lockPeriod Lock period
* @param _agentTier Agent performance tier
*/
function calculateAPY(
address _agentWallet,
uint256 _lockPeriod,
PerformanceTier _agentTier
) external view returns (uint256) {
return _calculateAPY(_agentWallet, _lockPeriod, _agentTier);
}
// Internal functions
function _calculateAPY(
address _agentWallet,
uint256 _lockPeriod,
PerformanceTier _agentTier
) internal view returns (uint256) {
uint256 tierMultiplier = tierMultipliers[_agentTier];
uint256 lockMultiplier = lockPeriodMultipliers[_lockPeriod];
uint256 apy = (baseAPY * tierMultiplier * lockMultiplier) / (10000 * 10000);
// Cap at maximum APY
return apy > maxAPY ? maxAPY : apy;
}
function _calculateRewards(uint256 _stakeId) internal {
Stake storage stake = stakes[_stakeId];
if (stake.status != StakeStatus.ACTIVE) {
return;
}
uint256 timeElapsed = block.timestamp - stake.lastRewardTime;
uint256 yearlyRewards = (stake.amount * stake.currentAPY) / 10000;
uint256 currentRewards = (yearlyRewards * timeElapsed) / 365 days;
stake.accumulatedRewards += currentRewards;
stake.lastRewardTime = block.timestamp;
// Auto-compound if enabled
if (stake.autoCompound && currentRewards >= minStakeAmount) {
stake.amount += currentRewards;
stake.accumulatedRewards = 0;
}
}
function _getAgentTier(address _agentWallet) internal view returns (PerformanceTier) {
AgentMetrics storage metrics = agentMetrics[_agentWallet];
return metrics.currentTier;
}
function _calculateAgentTier(address _agentWallet) internal view returns (PerformanceTier) {
AgentMetrics storage metrics = agentMetrics[_agentWallet];
uint256 successRate = metrics.totalSubmissions > 0 ?
(metrics.successfulSubmissions * 100) / metrics.totalSubmissions : 0;
uint256 score = (metrics.averageAccuracy * 50) / 100 + (successRate * 50) / 100;
if (score >= 95) return PerformanceTier.DIAMOND;
if (score >= 90) return PerformanceTier.PLATINUM;
if (score >= 80) return PerformanceTier.GOLD;
if (score >= 70) return PerformanceTier.SILVER;
return PerformanceTier.BRONZE;
}
function _getTierScore(PerformanceTier _tier) internal pure returns (uint256) {
if (_tier == PerformanceTier.DIAMOND) return 95;
if (_tier == PerformanceTier.PLATINUM) return 90;
if (_tier == PerformanceTier.GOLD) return 80;
if (_tier == PerformanceTier.SILVER) return 70;
return 60;
}
function _updateAgentMetrics(address _agentWallet, uint256 _amount, bool _isStake) internal {
AgentMetrics storage metrics = agentMetrics[_agentWallet];
if (_isStake) {
metrics.totalStaked += _amount;
if (metrics.totalStaked == _amount) {
metrics.stakerCount = 1;
}
} else {
metrics.totalStaked -= _amount;
if (metrics.totalStaked == 0) {
metrics.stakerCount = 0;
}
}
metrics.currentTier = _calculateAgentTier(_agentWallet);
metrics.tierScore = _getTierScore(metrics.currentTier);
}
function _updateStakingPool(address _agentWallet, address _staker, uint256 _amount, bool _isStake) internal {
StakingPool storage pool = stakingPools[_agentWallet];
if (_isStake) {
if (pool.stakerShares[_staker] == 0) {
pool.stakers.push(_staker);
}
pool.stakerShares[_staker] += _amount;
pool.totalStaked += _amount;
} else {
pool.stakerShares[_staker] -= _amount;
pool.totalStaked -= _amount;
// Remove staker from array if no shares left
if (pool.stakerShares[_staker] == 0) {
for (uint256 i = 0; i < pool.stakers.length; i++) {
if (pool.stakers[i] == _staker) {
pool.stakers[i] = pool.stakers[pool.stakers.length - 1];
pool.stakers.pop();
break;
}
}
}
}
// Update pool APY
if (pool.totalStaked > 0) {
pool.poolAPY = _calculateAPY(_agentWallet, 30 days, agentMetrics[_agentWallet].currentTier);
}
}
function _removeFromActiveStakes(uint256 _stakeId) internal {
for (uint256 i = 0; i < activeStakeIds.length; i++) {
if (activeStakeIds[i] == _stakeId) {
activeStakeIds[i] = activeStakeIds[activeStakeIds.length - 1];
activeStakeIds.pop();
break;
}
}
}
/**
* @dev Emergency pause function
*/
function pause() external onlyOwner {
_pause();
}
/**
* @dev Unpause function
*/
function unpause() external onlyOwner {
_unpause();
}
}

View File

@@ -0,0 +1,616 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "./AgentBounty.sol";
import "./AgentStaking.sol";
import "./PerformanceVerifier.sol";
import "./AIToken.sol";
/**
* @title Bounty Integration Layer
* @dev Bridges PerformanceVerifier with bounty and staking contracts
* @notice Handles automatic bounty completion detection and cross-contract event handling
*/
contract BountyIntegration is Ownable, ReentrancyGuard {
// State variables
AgentBounty public agentBounty;
AgentStaking public agentStaking;
PerformanceVerifier public performanceVerifier;
AIToken public aitbcToken;
uint256 public integrationCounter;
uint256 public autoVerificationThreshold = 90; // 90% accuracy for auto-verification
uint256 public batchProcessingLimit = 50;
uint256 public gasOptimizationThreshold = 100000;
// Integration status
enum IntegrationStatus { PENDING, PROCESSING, COMPLETED, FAILED }
// Performance to bounty mapping
struct PerformanceMapping {
uint256 mappingId;
bytes32 performanceHash;
uint256 bountyId;
uint256 submissionId;
IntegrationStatus status;
uint256 createdAt;
uint256 processedAt;
string errorMessage;
}
// Batch processing
struct BatchRequest {
uint256 batchId;
uint256[] bountyIds;
uint256[] submissionIds;
bytes32[] performanceHashes;
uint256[] accuracies;
uint256[] responseTimes;
IntegrationStatus status;
uint256 createdAt;
uint256 processedAt;
uint256 successCount;
uint256 failureCount;
}
// Event handlers
struct EventHandler {
bytes32 eventType;
address targetContract;
bytes4 functionSelector;
bool isActive;
uint256 priority;
}
// Mappings
mapping(uint256 => PerformanceMapping) public performanceMappings;
mapping(bytes32 => uint256) public performanceHashToMapping;
mapping(uint256 => BatchRequest) public batchRequests;
mapping(bytes32 => EventHandler) public eventHandlers;
mapping(address => bool) public authorizedIntegrators;
// Arrays
uint256[] public pendingMappings;
bytes32[] public performanceHashes;
address[] public authorizedIntegratorList;
// Events
event PerformanceMapped(
uint256 indexed mappingId,
bytes32 indexed performanceHash,
uint256 indexed bountyId,
uint256 submissionId
);
event BountyAutoCompleted(
uint256 indexed bountyId,
uint256 indexed submissionId,
address indexed submitter,
uint256 rewardAmount
);
event StakingRewardsTriggered(
address indexed agentWallet,
uint256 totalEarnings,
uint256 stakerCount
);
event BatchProcessed(
uint256 indexed batchId,
uint256 successCount,
uint256 failureCount,
uint256 gasUsed
);
event IntegrationFailed(
uint256 indexed mappingId,
string errorMessage,
bytes32 indexed performanceHash
);
event EventHandlerRegistered(
bytes32 indexed eventType,
address indexed targetContract,
bytes4 functionSelector
);
// Modifiers
modifier mappingExists(uint256 _mappingId) {
require(_mappingId < integrationCounter, "Mapping does not exist");
_;
}
modifier onlyAuthorizedIntegrator() {
require(authorizedIntegrators[msg.sender], "Not authorized integrator");
_;
}
modifier validPerformanceHash(bytes32 _performanceHash) {
require(_performanceHash != bytes32(0), "Invalid performance hash");
_;
}
constructor(
address _agentBounty,
address _agentStaking,
address _performanceVerifier,
address _aitbcToken
) {
agentBounty = AgentBounty(_agentBounty);
agentStaking = AgentStaking(_agentStaking);
performanceVerifier = PerformanceVerifier(_performanceVerifier);
aitbcToken = AIToken(_aitbcToken);
// Register default event handlers
_registerEventHandler(
keccak256("BOUNTY_COMPLETED"),
_agentStaking,
AgentStaking.distributeAgentEarnings.selector
);
_registerEventHandler(
keccak256("PERFORMANCE_VERIFIED"),
_agentBounty,
AgentBounty.verifySubmission.selector
);
}
/**
* @dev Maps performance verification to bounty completion
* @param _performanceHash Hash of performance metrics
* @param _bountyId Bounty ID
* @param _submissionId Submission ID
*/
function mapPerformanceToBounty(
bytes32 _performanceHash,
uint256 _bountyId,
uint256 _submissionId
) external
onlyAuthorizedIntegrator
validPerformanceHash(_performanceHash)
nonReentrant
returns (uint256)
{
require(performanceHashToMapping[_performanceHash] == 0, "Performance already mapped");
uint256 mappingId = integrationCounter++;
PerformanceMapping storage perfMap = performanceMappings[mappingId];
perfMap.mappingId = mappingId;
perfMap.performanceHash = _performanceHash;
perfMap.bountyId = _bountyId;
perfMap.submissionId = _submissionId;
perfMap.status = IntegrationStatus.PENDING;
perfMap.createdAt = block.timestamp;
performanceHashToMapping[_performanceHash] = mappingId;
pendingMappings.push(mappingId);
performanceHashes.push(_performanceHash);
emit PerformanceMapped(mappingId, _performanceHash, _bountyId, _submissionId);
// Attempt auto-processing
_processMapping(mappingId);
return mappingId;
}
/**
* @dev Processes a single performance mapping
* @param _mappingId Mapping ID
*/
function processMapping(uint256 _mappingId) external
onlyAuthorizedIntegrator
mappingExists(_mappingId)
nonReentrant
{
_processMapping(_mappingId);
}
/**
* @dev Processes multiple mappings in a batch
* @param _mappingIds Array of mapping IDs
*/
function processBatchMappings(uint256[] calldata _mappingIds) external
onlyAuthorizedIntegrator
nonReentrant
{
require(_mappingIds.length <= batchProcessingLimit, "Batch too large");
uint256 batchId = integrationCounter++;
BatchRequest storage batch = batchRequests[batchId];
batch.batchId = batchId;
batch.bountyIds = new uint256[](_mappingIds.length);
batch.submissionIds = new uint256[](_mappingIds.length);
batch.performanceHashes = new bytes32[](_mappingIds.length);
batch.accuracies = new uint256[](_mappingIds.length);
batch.responseTimes = new uint256[](_mappingIds.length);
batch.status = IntegrationStatus.PROCESSING;
batch.createdAt = block.timestamp;
uint256 gasStart = gasleft();
uint256 successCount = 0;
uint256 failureCount = 0;
for (uint256 i = 0; i < _mappingIds.length; i++) {
try this._processMappingInternal(_mappingIds[i]) {
successCount++;
} catch {
failureCount++;
}
}
batch.successCount = successCount;
batch.failureCount = failureCount;
batch.processedAt = block.timestamp;
batch.status = IntegrationStatus.COMPLETED;
uint256 gasUsed = gasStart - gasleft();
emit BatchProcessed(batchId, successCount, failureCount, gasUsed);
}
/**
* @dev Auto-verifies bounty submissions based on performance metrics
* @param _bountyId Bounty ID
* @param _submissionId Submission ID
* @param _accuracy Achieved accuracy
* @param _responseTime Response time
*/
function autoVerifyBountySubmission(
uint256 _bountyId,
uint256 _submissionId,
uint256 _accuracy,
uint256 _responseTime
) external
onlyAuthorizedIntegrator
nonReentrant
{
// Get bounty details
(,,,,,, bytes32 performanceCriteria, uint256 minAccuracy,,,, bool requiresZKProof) = agentBounty.getBounty(_bountyId);
// Check if auto-verification conditions are met
if (_accuracy >= autoVerificationThreshold && _accuracy >= minAccuracy) {
// Verify the submission
agentBounty.verifySubmission(_bountyId, _submissionId, true, address(this));
// Get submission details to calculate rewards
(address submitter,,,,,,,) = agentBounty.getSubmission(_submissionId);
// Trigger staking rewards if applicable
_triggerStakingRewards(submitter, _accuracy);
emit BountyAutoCompleted(_bountyId, _submissionId, submitter, 0); // Reward amount will be set by bounty contract
}
}
/**
* @dev Handles performance verification events
* @param _verificationId Performance verification ID
* @param _accuracy Accuracy achieved
* @param _responseTime Response time
* @param _performanceHash Hash of performance metrics
*/
function handlePerformanceVerified(
uint256 _verificationId,
uint256 _accuracy,
uint256 _responseTime,
bytes32 _performanceHash
) external
onlyAuthorizedIntegrator
nonReentrant
{
// Check if this performance is mapped to any bounties
uint256 mappingId = performanceHashToMapping[_performanceHash];
if (mappingId > 0) {
PerformanceMapping storage perfMap = performanceMappings[mappingId];
// Update agent staking metrics
(address submitter,,,,,,,) = agentBounty.getSubmission(perfMap.submissionId);
agentStaking.updateAgentPerformance(submitter, _accuracy, _accuracy >= autoVerificationThreshold);
// Auto-verify bounty if conditions are met
_autoVerifyBounty(perfMap.bountyId, perfMap.submissionId, _accuracy, _responseTime);
}
}
/**
* @dev Registers an event handler for cross-contract communication
* @param _eventType Event type identifier
* @param _targetContract Target contract address
* @param _functionSelector Function selector to call
*/
function registerEventHandler(
bytes32 _eventType,
address _targetContract,
bytes4 _functionSelector
) external onlyOwner {
require(_targetContract != address(0), "Invalid target contract");
require(_functionSelector != bytes4(0), "Invalid function selector");
eventHandlers[_eventType] = EventHandler({
eventType: _eventType,
targetContract: _targetContract,
functionSelector: _functionSelector,
isActive: true,
priority: 0
});
emit EventHandlerRegistered(_eventType, _targetContract, _functionSelector);
}
/**
* @dev Authorizes an integrator address
* @param _integrator Address to authorize
*/
function authorizeIntegrator(address _integrator) external onlyOwner {
require(_integrator != address(0), "Invalid integrator address");
require(!authorizedIntegrators[_integrator], "Already authorized");
authorizedIntegrators[_integrator] = true;
authorizedIntegratorList.push(_integrator);
}
/**
* @dev Revokes integrator authorization
* @param _integrator Address to revoke
*/
function revokeIntegrator(address _integrator) external onlyOwner {
require(authorizedIntegrators[_integrator], "Not authorized");
authorizedIntegrators[_integrator] = false;
// Remove from list
for (uint256 i = 0; i < authorizedIntegratorList.length; i++) {
if (authorizedIntegratorList[i] == _integrator) {
authorizedIntegratorList[i] = authorizedIntegratorList[authorizedIntegratorList.length - 1];
authorizedIntegratorList.pop();
break;
}
}
}
/**
* @dev Updates configuration parameters
* @param _autoVerificationThreshold New auto-verification threshold
* @param _batchProcessingLimit New batch processing limit
* @param _gasOptimizationThreshold New gas optimization threshold
*/
function updateConfiguration(
uint256 _autoVerificationThreshold,
uint256 _batchProcessingLimit,
uint256 _gasOptimizationThreshold
) external onlyOwner {
require(_autoVerificationThreshold <= 100, "Invalid threshold");
require(_batchProcessingLimit <= 100, "Batch limit too high");
autoVerificationThreshold = _autoVerificationThreshold;
batchProcessingLimit = _batchProcessingLimit;
gasOptimizationThreshold = _gasOptimizationThreshold;
}
// View functions
/**
* @dev Gets performance mapping details
* @param _mappingId Mapping ID
*/
function getPerformanceMapping(uint256 _mappingId) external view mappingExists(_mappingId) returns (
bytes32 performanceHash,
uint256 bountyId,
uint256 submissionId,
IntegrationStatus status,
uint256 createdAt,
uint256 processedAt,
string memory errorMessage
) {
PerformanceMapping storage perfMap = performanceMappings[_mappingId];
return (
perfMap.performanceHash,
perfMap.bountyId,
perfMap.submissionId,
perfMap.status,
perfMap.createdAt,
perfMap.processedAt,
perfMap.errorMessage
);
}
/**
* @dev Gets batch request details
* @param _batchId Batch ID
*/
function getBatchRequest(uint256 _batchId) external view returns (
uint256[] memory bountyIds,
uint256[] memory submissionIds,
IntegrationStatus status,
uint256 createdAt,
uint256 processedAt,
uint256 successCount,
uint256 failureCount
) {
BatchRequest storage batch = batchRequests[_batchId];
return (
batch.bountyIds,
batch.submissionIds,
batch.status,
batch.createdAt,
batch.processedAt,
batch.successCount,
batch.failureCount
);
}
/**
* @dev Gets pending mappings
*/
function getPendingMappings() external view returns (uint256[] memory) {
return pendingMappings;
}
/**
* @dev Gets all performance hashes
*/
function getPerformanceHashes() external view returns (bytes32[] memory) {
return performanceHashes;
}
/**
* @dev Gets authorized integrators
*/
function getAuthorizedIntegrators() external view returns (address[] memory) {
return authorizedIntegratorList;
}
/**
* @dev Checks if an address is authorized
* @param _integrator Address to check
*/
function isAuthorizedIntegrator(address _integrator) external view returns (bool) {
return authorizedIntegrators[_integrator];
}
/**
* @dev Gets integration statistics
*/
function getIntegrationStats() external view returns (
uint256 totalMappings,
uint256 pendingCount,
uint256 completedCount,
uint256 failedCount,
uint256 averageProcessingTime
) {
uint256 completed = 0;
uint256 failed = 0;
uint256 totalTime = 0;
uint256 processedCount = 0;
for (uint256 i = 0; i < integrationCounter; i++) {
PerformanceMapping storage perfMap = performanceMappings[i];
if (perfMap.status == IntegrationStatus.COMPLETED) {
completed++;
totalTime += perfMap.processedAt - perfMap.createdAt;
processedCount++;
} else if (perfMap.status == IntegrationStatus.FAILED) {
failed++;
}
}
uint256 avgTime = processedCount > 0 ? totalTime / processedCount : 0;
return (
integrationCounter,
pendingMappings.length,
completed,
failed,
avgTime
);
}
// Internal functions
function _processMapping(uint256 _mappingId) internal {
PerformanceMapping storage perfMap = performanceMappings[_mappingId];
if (perfMap.status != IntegrationStatus.PENDING) {
return;
}
try this._processMappingInternal(_mappingId) {
perfMap.status = IntegrationStatus.COMPLETED;
perfMap.processedAt = block.timestamp;
} catch Error(string memory reason) {
perfMap.status = IntegrationStatus.FAILED;
perfMap.errorMessage = reason;
perfMap.processedAt = block.timestamp;
emit IntegrationFailed(_mappingId, reason, perfMap.performanceHash);
} catch {
perfMap.status = IntegrationStatus.FAILED;
perfMap.errorMessage = "Unknown error";
perfMap.processedAt = block.timestamp;
emit IntegrationFailed(_mappingId, "Unknown error", perfMap.performanceHash);
}
// Remove from pending
_removeFromPending(_mappingId);
}
function _processMappingInternal(uint256 _mappingId) external {
PerformanceMapping storage perfMap = performanceMappings[_mappingId];
// Get bounty details
(,,,,,, bytes32 performanceCriteria, uint256 minAccuracy,,,, bool requiresZKProof) = agentBounty.getBounty(perfMap.bountyId);
// Get submission details
(address submitter, bytes32 submissionHash, uint256 accuracy, uint256 responseTime,,,) = agentBounty.getSubmission(perfMap.submissionId);
// Verify performance criteria match
require(perfMap.performanceHash == submissionHash, "Performance hash mismatch");
// Check if accuracy meets requirements
require(accuracy >= minAccuracy, "Accuracy below minimum");
// Auto-verify if conditions are met
if (accuracy >= autoVerificationThreshold) {
agentBounty.verifySubmission(perfMap.bountyId, perfMap.submissionId, true, address(this));
// Update agent staking metrics
agentStaking.updateAgentPerformance(submitter, accuracy, true);
// Trigger staking rewards
_triggerStakingRewards(submitter, accuracy);
}
}
function _autoVerifyBounty(
uint256 _bountyId,
uint256 _submissionId,
uint256 _accuracy,
uint256 _responseTime
) internal {
if (_accuracy >= autoVerificationThreshold) {
agentBounty.verifySubmission(_bountyId, _submissionId, true, address(this));
}
}
function _triggerStakingRewards(address _agentWallet, uint256 _accuracy) internal {
// Calculate earnings based on accuracy
uint256 baseEarnings = (_accuracy * 100) * 10**18; // Simplified calculation
// Distribute to stakers
try agentStaking.distributeAgentEarnings(_agentWallet, baseEarnings) {
emit StakingRewardsTriggered(_agentWallet, baseEarnings, 0);
} catch {
// Handle staking distribution failure
}
}
function _registerEventHandler(
bytes32 _eventType,
address _targetContract,
bytes4 _functionSelector
) internal {
eventHandlers[_eventType] = EventHandler({
eventType: _eventType,
targetContract: _targetContract,
functionSelector: _functionSelector,
isActive: true,
priority: 0
});
}
function _removeFromPending(uint256 _mappingId) internal {
for (uint256 i = 0; i < pendingMappings.length; i++) {
if (pendingMappings[i] == _mappingId) {
pendingMappings[i] = pendingMappings[pendingMappings.length - 1];
pendingMappings.pop();
break;
}
}
}
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,68 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/**
* @title Groth16Verifier
* @dev Auto-generated Groth16 proof verifier for the SimpleReceipt circuit.
*
* To regenerate from the actual circuit:
* cd apps/zk-circuits
* npx snarkjs groth16 setup receipt_simple.r1cs pot12_final.ptau circuit_0000.zkey
* npx snarkjs zkey contribute circuit_0000.zkey circuit_final.zkey --name="AITBC" -v
* npx snarkjs zkey export solidityverifier circuit_final.zkey ../../contracts/Groth16Verifier.sol
*
* This file is a functional stub that matches the interface expected by
* ZKReceiptVerifier.sol. Replace with the snarkjs-generated version for production.
*/
contract Groth16Verifier {
// Verification key points (placeholder — replace with real VK from snarkjs export)
uint256 constant ALPHA_X = 0x0000000000000000000000000000000000000000000000000000000000000001;
uint256 constant ALPHA_Y = 0x0000000000000000000000000000000000000000000000000000000000000002;
uint256 constant BETA_X1 = 0x0000000000000000000000000000000000000000000000000000000000000001;
uint256 constant BETA_X2 = 0x0000000000000000000000000000000000000000000000000000000000000002;
uint256 constant BETA_Y1 = 0x0000000000000000000000000000000000000000000000000000000000000003;
uint256 constant BETA_Y2 = 0x0000000000000000000000000000000000000000000000000000000000000004;
uint256 constant GAMMA_X1 = 0x0000000000000000000000000000000000000000000000000000000000000001;
uint256 constant GAMMA_X2 = 0x0000000000000000000000000000000000000000000000000000000000000002;
uint256 constant GAMMA_Y1 = 0x0000000000000000000000000000000000000000000000000000000000000003;
uint256 constant GAMMA_Y2 = 0x0000000000000000000000000000000000000000000000000000000000000004;
uint256 constant DELTA_X1 = 0x0000000000000000000000000000000000000000000000000000000000000001;
uint256 constant DELTA_X2 = 0x0000000000000000000000000000000000000000000000000000000000000002;
uint256 constant DELTA_Y1 = 0x0000000000000000000000000000000000000000000000000000000000000003;
uint256 constant DELTA_Y2 = 0x0000000000000000000000000000000000000000000000000000000000000004;
// IC points for 1 public signal (SimpleReceipt: receiptHash)
uint256 constant IC0_X = 0x0000000000000000000000000000000000000000000000000000000000000001;
uint256 constant IC0_Y = 0x0000000000000000000000000000000000000000000000000000000000000002;
uint256 constant IC1_X = 0x0000000000000000000000000000000000000000000000000000000000000003;
uint256 constant IC1_Y = 0x0000000000000000000000000000000000000000000000000000000000000004;
/**
* @dev Verify a Groth16 proof.
* @param a Proof element a (G1 point)
* @param b Proof element b (G2 point)
* @param c Proof element c (G1 point)
* @param input Public signals array (1 element for SimpleReceipt)
* @return r Whether the proof is valid
*
* NOTE: This stub always returns true for development/testing.
* Replace with the snarkjs-generated verifier for production use.
*/
function verifyProof(
uint[2] calldata a,
uint[2][2] calldata b,
uint[2] calldata c,
uint[1] calldata input
) public view returns (bool r) {
// Production: pairing check using bn256 precompiles
// ecPairing(a, b, alpha, beta, vk_x, gamma, c, delta)
//
// Stub: validate proof elements are non-zero
if (a[0] == 0 && a[1] == 0) return false;
if (c[0] == 0 && c[1] == 0) return false;
if (input[0] == 0) return false;
return true;
}
}

View File

@@ -0,0 +1,684 @@
// 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 agreement.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 _newValue;
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 agreement.providerHistory;
mapping(uint256 => uint256[]) public agreementVerifications;
mapping(address => uint256[]) public agreement.providerVerifications;
mapping(bytes32 => uint256) public proofToVerification;
// Arrays for authorized oracles
address[] public authorizedOracles;
// Events
event PerformanceSubmitted(
uint256 indexed verificationId,
uint256 indexed agreementId,
address indexed agreement.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 agreement.provider,
uint256 penaltyAmount
);
event RewardIssued(
uint256 indexed agreementId,
address indexed agreement.provider,
uint256 rewardAmount
);
event VerificationChallenged(uint256 indexed verificationId, address indexed challenger, string challengeData);
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
AIPowerRental.RentalAgreement memory agreement = aiPowerRental.getRentalAgreement(_agreementId);
require(agreement.provider != address(0), "Invalid agreement");
uint256 verificationId = verificationCounter++;
performanceMetrics[verificationId] = PerformanceMetrics({
verificationId: verificationId,
agreementId: _agreementId,
agreement.provider: agreement.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);
agreement.providerVerifications[agreement.provider].push(verificationId);
proofToVerification[keccak256(_zkProof)] = verificationId;
emit PerformanceSubmitted(
verificationId,
_agreementId,
agreement.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 __newValue 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 __newValue,
uint256 _maxMemoryUsage,
uint256 _minEnergyEfficiency
) external onlyOwner {
slaParameters[_agreementId] = SLAParameters({
maxResponseTime: _maxResponseTime,
minAccuracy: _minAccuracy,
minAvailability: _minAvailability,
_newValue: __newValue,
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 = _newValue;
_newValue = _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
AIPowerRental.RentalAgreement memory agreement = aiPowerRental.getRentalAgreement(metrics.agreementId);
// Penalty based on severity of violation
uint256 penaltyAmount = (agreement.price * penaltyPercentage) / 10000;
// Additional penalties for severe violations
if (metrics.responseTime > maxResponseTime * 2) {
penaltyAmount += (agreement.price * 1000) / 10000; // Additional 10%
}
if (metrics.accuracy < minAccuracy - 10) {
penaltyAmount += (agreement.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
AIPowerRental.RentalAgreement memory agreement = aiPowerRental.getRentalAgreement(metrics.agreementId);
// Reward based on performance quality
uint256 rewardAmount = (agreement.price * rewardPercentage) / 10000;
// Additional rewards for exceptional performance
if (metrics.responseTime < maxResponseTime / 2) {
rewardAmount += (agreement.price * 500) / 10000; // Additional 5%
}
if (metrics.accuracy > minAccuracy + 5) {
rewardAmount += (agreement.price * 500) / 10000; // Additional 5%
}
return rewardAmount;
}
/**
* @dev Gets performance history for a agreement.provider
* @param _agreement.provider Address of the agreement.provider
*/
function getProviderHistory(address _agreement.provider)
external
view
returns (PerformanceHistory memory)
{
return agreement.providerHistory[_agreement.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 agreement.provider
* @param _agreement.provider Address of the agreement.provider
*/
function getProviderVerifications(address _agreement.provider)
external
view
returns (uint256[] memory)
{
return agreement.providerVerifications[_agreement.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 = true; // Placeholder for Groth16 verification
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(agreement.agreement.provider, metrics.agreementId);
} else {
_penalizeProvider(agreement.agreement.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 _agreement.provider, bool _withinSLA) internal {
PerformanceHistory storage history = agreement.providerHistory[_agreement.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 _rewardProvider(address _agreement.provider, uint256 _agreementId) internal {
emit RewardIssued(_agreementId, _agreement.provider, 0);
}
function _penalizeProvider(address _agreement.provider, uint256 _agreementId) internal {
emit PenaltyApplied(_agreementId, _agreement.provider, 0);
}
function unpause() external onlyOwner {
_unpause();
}
}

View File

@@ -0,0 +1,259 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
// Note: Groth16Verifier is generated by snarkjs from the circuit's verification key
// Run: snarkjs zkey export solidityverifier circuit_final.zkey Groth16Verifier.sol
import "./Groth16Verifier.sol";
/**
* @title ZKReceiptVerifier
* @dev Contract for verifying zero-knowledge proofs for receipt attestation
*/
contract ZKReceiptVerifier is Groth16Verifier {
// Events
event ProofVerified(
bytes32 indexed receiptHash,
uint256 settlementAmount,
uint256 timestamp,
address indexed verifier
);
event ProofVerificationFailed(
bytes32 indexed receiptHash,
string reason
);
// Mapping to prevent double-spending
mapping(bytes32 => bool) public verifiedReceipts;
// Mapping for authorized verifiers
mapping(address => bool) public authorizedVerifiers;
// Address of the settlement contract
address public settlementContract;
// Circuit version
uint256 public constant CIRCUIT_VERSION = 1;
// Minimum settlement amount
uint256 public constant MIN_SETTLEMENT_AMOUNT = 0;
// Maximum timestamp drift (in seconds)
uint256 public constant MAX_TIMESTAMP_DRIFT = 3600; // 1 hour
modifier onlyAuthorized() {
require(
authorizedVerifiers[msg.sender] ||
msg.sender == settlementContract,
"ZKReceiptVerifier: Unauthorized"
);
_;
}
modifier onlySettlementContract() {
require(
msg.sender == settlementContract,
"ZKReceiptVerifier: Only settlement contract"
);
_;
}
constructor() {
// Deployer is initially authorized
authorizedVerifiers[msg.sender] = true;
}
/**
* @dev Verify a ZK proof for receipt attestation
* @param a Proof parameter a (G1 point)
* @param b Proof parameter b (G2 point)
* @param c Proof parameter c (G1 point)
* @param publicSignals Public signals [receiptHash] - matches receipt_simple circuit
* @return valid Whether the proof is valid
*/
function verifyReceiptProof(
uint[2] calldata a,
uint[2][2] calldata b,
uint[2] calldata c,
uint[1] calldata publicSignals
) external view returns (bool valid) {
// Extract public signal - receiptHash only for SimpleReceipt circuit
bytes32 receiptHash = bytes32(publicSignals[0]);
// Validate receipt hash is not zero
if (receiptHash == bytes32(0)) {
return false;
}
// Verify the proof using Groth16 (inherited from Groth16Verifier)
return _verifyProof(a, b, c, publicSignals);
}
/**
* @dev Verify and record a proof for settlement
* @param a Proof parameter a (G1 point)
* @param b Proof parameter b (G2 point)
* @param c Proof parameter c (G1 point)
* @param publicSignals Public signals [receiptHash]
* @param settlementAmount Amount to settle (passed separately, not in proof)
* @return success Whether verification succeeded
*/
function verifyAndRecord(
uint[2] calldata a,
uint[2][2] calldata b,
uint[2] calldata c,
uint[1] calldata publicSignals,
uint256 settlementAmount
) external onlyAuthorized returns (bool success) {
// Extract public signal
bytes32 receiptHash = bytes32(publicSignals[0]);
// Check if receipt already verified (prevent double-spend)
if (verifiedReceipts[receiptHash]) {
emit ProofVerificationFailed(receiptHash, "Receipt already verified");
return false;
}
// Validate receipt hash
if (receiptHash == bytes32(0)) {
emit ProofVerificationFailed(receiptHash, "Invalid receipt hash");
return false;
}
// Verify the proof
bool valid = _verifyProof(a, b, c, publicSignals);
if (valid) {
// Mark as verified
verifiedReceipts[receiptHash] = true;
// Emit event with settlement amount
emit ProofVerified(receiptHash, settlementAmount, block.timestamp, msg.sender);
return true;
} else {
emit ProofVerificationFailed(receiptHash, "Invalid proof");
return false;
}
}
/**
* @dev Internal proof verification - calls inherited Groth16 verifier
* @param a Proof parameter a
* @param b Proof parameter b
* @param c Proof parameter c
* @param publicSignals Public signals array
* @return valid Whether the proof is valid
*/
function _verifyProof(
uint[2] calldata a,
uint[2][2] calldata b,
uint[2] calldata c,
uint[1] calldata publicSignals
) internal view returns (bool valid) {
// Convert to format expected by Groth16Verifier
// The Groth16Verifier.verifyProof is generated by snarkjs
return Groth16Verifier.verifyProof(a, b, c, publicSignals);
}
/**
* @dev Set the settlement contract address
* @param _settlementContract Address of the settlement contract
*/
function setSettlementContract(address _settlementContract) external {
require(authorizedVerifiers[msg.sender], "ZKReceiptVerifier: Unauthorized");
settlementContract = _settlementContract;
}
/**
* @dev Add an authorized verifier
* @param verifier Address to authorize
*/
function addAuthorizedVerifier(address verifier) external {
require(authorizedVerifiers[msg.sender], "ZKReceiptVerifier: Unauthorized");
authorizedVerifiers[verifier] = true;
}
/**
* @dev Remove an authorized verifier
* @param verifier Address to remove
*/
function removeAuthorizedVerifier(address verifier) external {
require(authorizedVerifiers[msg.sender], "ZKReceiptVerifier: Unauthorized");
authorizedVerifiers[verifier] = false;
}
/**
* @dev Check if a receipt has been verified
* @param receiptHash Hash of the receipt
* @return verified Whether the receipt has been verified
*/
function isReceiptVerified(bytes32 receiptHash) external view returns (bool verified) {
return verifiedReceipts[receiptHash];
}
/**
* @dev Batch verify multiple proofs
* @param proofs Array of proof data
* @return results Array of verification results
*/
function batchVerify(
BatchProof[] calldata proofs
) external view returns (bool[] memory results) {
results = new bool[](proofs.length);
for (uint256 i = 0; i < proofs.length; i++) {
results[i] = _verifyProof(
proofs[i].a,
proofs[i].b,
proofs[i].c,
proofs[i].publicSignals
);
}
}
// Struct for batch verification
struct BatchProof {
uint[2] a;
uint[2][2] b;
uint[2] c;
uint[1] publicSignals; // Matches SimpleReceipt circuit
}
/**
* @dev Verify a performance proof
* @return valid Whether the proof is valid
*/
function verifyPerformanceProof(
uint256 agreementId,
uint256 responseTime,
uint256 accuracy,
uint256 availability,
uint256 computePower,
bytes memory zkProof
) external pure returns (bool valid) {
// Implementation for performance proof verification
// This is a placeholder since the actual implementation would need
// to parse the zkProof bytes and call the appropriate Circom verifier
return true;
}
/**
* @dev Verify a performance proof
* @return valid Whether the proof is valid
*/
function verifyPerformanceProof(
uint256 agreementId,
uint256 responseTime,
uint256 accuracy,
uint256 availability,
uint256 computePower,
bytes memory zkProof
) external pure returns (bool valid) {
// Implementation for performance proof verification
// This is a placeholder since the actual implementation would need
// to parse the zkProof bytes and call the appropriate Circom verifier
return true;
}
}