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