// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract PureVersation { // --- 0. BRANDING --- string public constant name = "PureVersation Research Registry"; string public constant symbol = "VERSATION"; // --- 1. IDENTITY & REPUTATION --- struct Researcher { bool isRegistered; uint256 reputationScore; string role; // Dynamic: "Initiate", "Scout", "Linguist", "Archivist", "Oracle", or "Admin" } mapping(address => Researcher) public researchers; address public labAdmin; // --- 2. REGISTRY & LICENSING --- struct DialectEntry { uint256 id; string phrase; string dialect; string ipfsCid; address contributor; string licenseType; uint256 verificationCount; bool isVerified; } uint256 public entryCounter; mapping(uint256 => DialectEntry) public entries; // --- 3. BACKUP SYSTEM --- struct DataSnapshot { uint256 timestamp; string dataType; // "Database_CSV" or "Persona_JSON" string ipfsCid; string description; address uploader; } mapping(string => DataSnapshot[]) public backups; // 🟢 NEW MAPPINGS FOR SECURITY mapping(uint256 => mapping(address => bool)) public hasVerified; mapping(uint256 => mapping(address => bool)) public hasRejected; mapping(uint256 => uint256) public rejectionCounts; // --- 4. EVENTS --- event EntryProposed(uint256 indexed id, string phrase, address indexed proposer); event EntryVerified(uint256 indexed id, address indexed verifier); event EntryRejected(uint256 indexed id, address indexed rejector); // 🟢 NEW EVENT event ReputationMinted(address indexed researcher, uint256 amount); event RankUpgraded(address indexed researcher, string newRole); event BackupCreated(string indexed dataKey, string ipfsCid, uint256 timestamp); constructor() { labAdmin = msg.sender; researchers[msg.sender] = Researcher(true, 100, "Admin"); } // --- MODIFIERS --- modifier onlyAdmin() { require(msg.sender == labAdmin, "Only Lab Admin can do this"); _; } modifier onlyRegistered() { require(researchers[msg.sender].isRegistered, "Must be registered researcher"); _; } // --- CORE FUNCTIONS --- function registerResearcher(address _user, string memory _initialRole) public onlyAdmin { researchers[_user] = Researcher(true, 10, _initialRole); _updateRank(_user); } function proposeEntry(string memory _phrase, string memory _dialect, string memory _ipfsCid, string memory _license, address _contributor) public onlyRegistered { entryCounter++; entries[entryCounter] = DialectEntry(entryCounter, _phrase, _dialect, _ipfsCid, _contributor, _license, 0, false); emit EntryProposed(entryCounter, _phrase, _contributor); if (!researchers[_contributor].isRegistered) { researchers[_contributor] = Researcher(true, 0, "Initiate"); } } function verifyEntry(uint256 _id) public onlyRegistered { require(entries[_id].contributor != msg.sender, "Cannot verify own entry"); require(!entries[_id].isVerified, "Already verified"); if (msg.sender == labAdmin) { // Admin can verify instantly _applyVerification(_id); } else { // Peer consensus require(!hasVerified[_id][msg.sender], "Already verified by you"); hasVerified[_id][msg.sender] = true; entries[_id].verificationCount++; if (entries[_id].verificationCount >= 2) { _applyVerification(_id); } } // Reward the verifier _mintReputation(msg.sender, 10); } // 🟢 Helper for verification function _applyVerification(uint256 _id) internal { entries[_id].isVerified = true; _mintReputation(entries[_id].contributor, 50); emit EntryVerified(_id, msg.sender); } // 🟢 FIX 2: Admin or Peer consensus rejection function rejectEntry(uint256 _id) public onlyRegistered { require(!entries[_id].isVerified, "Cannot reject an already verified entry"); if (msg.sender == labAdmin) { // Admin can reject instantly _applyRejection(_id); } else { // Peer consensus (3 out of 5 typical) require(!hasRejected[_id][msg.sender], "Already rejected by you"); hasRejected[_id][msg.sender] = true; rejectionCounts[_id]++; if (rejectionCounts[_id] >= 3) { _applyRejection(_id); } } } // 🟢 Helper for rejection function _applyRejection(uint256 _id) internal { address badActor = entries[_id].contributor; _burnReputation(badActor, 10); emit EntryRejected(_id, msg.sender); } function createBackup(string memory _dataKey, string memory _dataType, string memory _ipfsCid, string memory _description) public onlyRegistered { DataSnapshot memory newSnap = DataSnapshot(block.timestamp, _dataType, _ipfsCid, _description, msg.sender); backups[_dataKey].push(newSnap); emit BackupCreated(_dataKey, _ipfsCid, block.timestamp); } // --- HELPER & VIEW FUNCTIONS --- function getLatestBackup(string memory _dataKey) public view returns (string memory ipfsCid, uint256 timestamp) { require(backups[_dataKey].length > 0, "No backups found"); DataSnapshot memory snap = backups[_dataKey][backups[_dataKey].length - 1]; return (snap.ipfsCid, snap.timestamp); } function getLicenseInfo(uint256 _id) public view returns (string memory license, address contributor) { return (entries[_id].licenseType, entries[_id].contributor); } // --- INTERNAL REPUTATION & RANKING LOGIC --- function _mintReputation(address _user, uint256 _amount) internal { researchers[_user].reputationScore += _amount; emit ReputationMinted(_user, _amount); _updateRank(_user); } function _burnReputation(address _user, uint256 _amount) internal { if (researchers[_user].reputationScore >= _amount) { researchers[_user].reputationScore -= _amount; } else { researchers[_user].reputationScore = 0; } _updateRank(_user); } function _updateRank(address _user) internal { Researcher storage r = researchers[_user]; if (keccak256(abi.encodePacked(r.role)) == keccak256(abi.encodePacked("Admin"))) { return; } uint256 score = r.reputationScore; string memory newRole; if (score >= 1500) { newRole = "Oracle"; } else if (score >= 800) { newRole = "Archivist"; } else if (score >= 300) { newRole = "Linguist"; } else if (score >= 100) { newRole = "Scout"; } else { newRole = "Initiate"; } if (keccak256(abi.encodePacked(r.role)) != keccak256(abi.encodePacked(newRole))) { r.role = newRole; emit RankUpgraded(_user, newRole); } } }