PureVersation / src /PureVersation.sol
GitHub Actions
🚀 Automated sync from GitHub
fab9c06
// 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);
}
}
}