// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; /** * @title AERE Consensus Contract * @dev Manages consensus mechanism and validator operations */ contract AereConsensus is Ownable, ReentrancyGuard { struct Validator { address validatorAddress; uint256 stake; uint256 votingPower; bool isActive; uint256 joinedAt; uint256 rewardsEarned; } struct ConsensusRound { uint256 roundNumber; address proposer; uint256 blockHeight; uint256 timestamp; bool finalized; } // State variables mapping(address => Validator) public validators; mapping(uint256 => ConsensusRound) public consensusRounds; address[] public validatorList; uint256 public constant MINIMUM_STAKE = 100000 * 10**18; // 100K AERE uint256 public constant BLOCK_TIME = 12; // 12 seconds uint256 public currentRound = 0; uint256 public totalStaked = 0; // Events event ValidatorJoined(address indexed validator, uint256 stake); event ValidatorLeft(address indexed validator); event ValidatorSlashed(address indexed validator, uint256 amount, string reason); event ConsensusReached(uint256 indexed round, address indexed proposer); event BlockProposed(uint256 indexed blockHeight, address indexed proposer); constructor() {} /** * @dev Join as validator with minimum stake */ function joinValidator() external payable nonReentrant { require(msg.value >= MINIMUM_STAKE, "Insufficient stake"); require(!validators[msg.sender].isActive, "Already a validator"); validators[msg.sender] = Validator({ validatorAddress: msg.sender, stake: msg.value, votingPower: calculateVotingPower(msg.value), isActive: true, joinedAt: block.timestamp, rewardsEarned: 0 }); validatorList.push(msg.sender); totalStaked += msg.value; emit ValidatorJoined(msg.sender, msg.value); } /** * @dev Leave validator set and withdraw stake */ function leaveValidator() external nonReentrant { require(validators[msg.sender].isActive, "Not an active validator"); Validator storage validator = validators[msg.sender]; uint256 stakeToReturn = validator.stake; validator.isActive = false; totalStaked -= stakeToReturn; // Remove from validator list for (uint i = 0; i < validatorList.length; i++) { if (validatorList[i] == msg.sender) { validatorList[i] = validatorList[validatorList.length - 1]; validatorList.pop(); break; } } payable(msg.sender).transfer(stakeToReturn); emit ValidatorLeft(msg.sender); } /** * @dev Propose new block (only active validators) */ function proposeBlock(uint256 blockHeight) external { require(validators[msg.sender].isActive, "Not an active validator"); require(isValidProposer(msg.sender), "Not selected as proposer"); currentRound++; consensusRounds[currentRound] = ConsensusRound({ roundNumber: currentRound, proposer: msg.sender, blockHeight: blockHeight, timestamp: block.timestamp, finalized: false }); emit BlockProposed(blockHeight, msg.sender); } /** * @dev Finalize consensus round */ function finalizeRound(uint256 roundNumber) external onlyOwner { require(consensusRounds[roundNumber].proposer != address(0), "Round does not exist"); require(!consensusRounds[roundNumber].finalized, "Round already finalized"); consensusRounds[roundNumber].finalized = true; // Distribute rewards to proposer address proposer = consensusRounds[roundNumber].proposer; validators[proposer].rewardsEarned += calculateBlockReward(); emit ConsensusReached(roundNumber, proposer); } /** * @dev Slash validator for malicious behavior */ function slashValidator(address validatorAddress, uint256 slashAmount, string memory reason) external onlyOwner { require(validators[validatorAddress].isActive, "Validator not active"); require(slashAmount <= validators[validatorAddress].stake, "Slash amount too high"); validators[validatorAddress].stake -= slashAmount; totalStaked -= slashAmount; // If stake falls below minimum, remove validator if (validators[validatorAddress].stake < MINIMUM_STAKE) { validators[validatorAddress].isActive = false; } emit ValidatorSlashed(validatorAddress, slashAmount, reason); } /** * @dev Calculate voting power based on stake */ function calculateVotingPower(uint256 stake) public pure returns (uint256) { return stake / (10**18); // 1 voting power per AERE } /** * @dev Calculate block reward */ function calculateBlockReward() public view returns (uint256) { return 16 * 10**18; // 16 AERE per block } /** * @dev Check if address is valid proposer for current round */ function isValidProposer(address proposer) public view returns (bool) { if (!validators[proposer].isActive) return false; // Simple round-robin selection based on block number uint256 proposerIndex = block.number % validatorList.length; return validatorList[proposerIndex] == proposer; } /** * @dev Get validator info */ function getValidator(address validatorAddress) external view returns (Validator memory) { return validators[validatorAddress]; } /** * @dev Get active validator count */ function getActiveValidatorCount() external view returns (uint256) { uint256 count = 0; for (uint i = 0; i < validatorList.length; i++) { if (validators[validatorList[i]].isActive) { count++; } } return count; } /** * @dev Get consensus stats */ function getConsensusStats() external view returns ( uint256 _totalValidators, uint256 _totalStaked, uint256 _currentRound, uint256 _blockTime ) { return ( validatorList.length, totalStaked, currentRound, BLOCK_TIME ); } }