aere-contracts/contracts/AereConsensus.sol
Aere Network a13a649b77
Some checks are pending
contracts-ci / Install (lockfile) → compile → full test suite (push) Waiting to run
contracts-ci / PQC known-answer tests (NIST vectors) (push) Waiting to run
contracts-ci / Coverage (scoped, with artifacts) (push) Waiting to run
Initial public release
Aere Network public source. Everything here can be checked against the live
chain (chain id 2800, https://rpc.aere.network).

Scope note, stated up front rather than buried: consensus on chain 2800 is
classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at
the signature, precompile, account and transport layers. Nothing here makes the
consensus post-quantum, and no document in it should be read as claiming so.
2026-07-20 01:02:37 +03:00

212 lines
6.7 KiB
Solidity

// 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
);
}
}