const express = require('express') const app = express() const port = 9700 const web3operatorAddress = "http://vps.playpoolstudios.com:2015/" const apiAddress = "https://vps.playpoolstudios.com/metahunt/api/" app.get('/', (req, res) => { res.send('Validator is validating') }) app.listen(port, () => { console.log(`Mhunt Validator is listening on port ${port}`) }) app.get('/validateSession', async (req, res) => { const { tournamentId, address } = req.query; if (!tournamentId || !address) { res.send("invalid params"); return; } let tournament = GetTournamentById(tournamentId); if (tournament == null) { await CheckForStartedTourneys(); tournament = GetTournamentById(tournamentId); } if (tournament == null) { console.log(`tourney id:${tournamentId} is not available`); //res.send("This tournament is not either started or valid"); // return; } tournament.displayDetails(); const found = tournament.participents.some(participant => participant == address); if (found) { return res.send("0"); } else { return res.send("-1"); } }) app.post('/updateBlock', async (req, res) => { const jsonData = req.body; const tournamentBlock = new TournamentBlockData(jsonData); tournamentBlocks.push(tournamentBlock); console.log(tournamentBlocks); res.sendStatus(200); }); /* ------------------------------------------------------------------------------- */ let tournamentsList ; let startedTournaments; let tournamentBlocks = []; /* ------------------------------------------------------------------------------- */ CheckForStartedTourneys(); setInterval(async () => { CheckForStartedTourneys(); }, 60000) async function CheckForStartedTourneys() { const tournamentsResponse = await fetch(apiAddress + "get_tournaments_raw.php"); const tournaments = await tournamentsResponse.json(); tournamentsList = []; startedTournaments = []; const now = new Date(); const tenMinutesAgo = new Date(now.getTime() - 10 * 60000); // 10 minutes ago from now tournaments.forEach(async tournament => { const tournamentDate = new Date(tournament.start_date); // Converts the string date to a Date object // Create a new TournamentData instance using the tournament JSON data const newTournament = new TournamentData( tournament.id, tournament.name, tournament.start_date, tournament.game_mode, tournament.reward, tournament.php_reward, tournament.is_test, tournament.ticket_count, tournament.owner_id ); if (tournamentDate >= tenMinutesAgo && tournamentDate <= now || true) { console.log(`Tournament "${tournament.name}" started within the last 10 minutes.`); const participentsUrl = web3operatorAddress + "getTournamentParticipants?id=" + newTournament.id; const participentsResponse = await fetch(participentsUrl); const participentsJson = await participentsResponse.json(); const participents = participentsJson["wallets"].split(','); try { participents.forEach(participent => { newTournament.participents.push(participent); }) //newTournament.displayDetails(); } catch { console.log(`tourneyId:${newTournament.id} has no participents. ${JSON.stringify(participents)}`) } startedTournaments.push(newTournament); } else if (tournamentDate > now) { console.log(`Tournament "${tournament.name}" is yet to come`) } else { // console.log(`Tournament "${tournament.name}" is expired`) } // newTournament.displayDetails(); // Push the new tournament instance into tournamentsList tournamentsList.push(newTournament); }); } /* ------------------------------------------------------------------------------- */ setInterval(async () => { ScanBlocks(); }, 60000) function ScanBlocks(){ for(let i=0; i < tournamentBlocks.length; i++){ let isValid = true; for(let j=0; j < startedTournaments.length; j++){ if(startedTournaments[j].id == tournamentBlocks[i].tournamentId){ //Found the matching tourney for this block if(!startedTournaments[j].participents.include(tournamentBlocks[i].owner)){ // isValid=false; console.log("***Block was sent by non-participent. Allowing this for now."); } break; } } } } /* ----------------------------------METHODS----------------------------------- */ function GetTournamentById(tournamentId) { const tournament = startedTournaments.find(tournament => tournament.id == tournamentId); return tournament; } /* ------------------------------------------------------------------------------- */ /* ------------------------------CUSTOM CLASSES------------------------------------ */ class TournamentData { constructor(id, name, start_date, game_mode, reward, php_reward, is_test, ticket_count, owner_id) { this.id = id; this.name = name; this.start_date = start_date; this.game_mode = game_mode; this.reward = reward; this.php_reward = php_reward; this.is_test = is_test; this.ticket_count = ticket_count; this.owner_id = owner_id; this.participents = []; } // Method to display tournament details displayDetails() { console.log(`Tournament ID: ${this.id}`); console.log(`Name: ${this.name}`); console.log(`Date: ${this.start_date}`); console.log(`Game Mode: ${this.game_mode}`); console.log(`Reward: ${this.reward}`); console.log(`PHP Reward: ${this.php_reward}`); console.log(`Test Tournament: ${this.is_test ? "Yes" : "No"}`); console.log(`Ticket Count: ${this.ticket_count}`); console.log(`Owner ID: ${this.owner_id}`); console.log(`Participents: ${this.participents}`); } } // Define a dictionary to store tournament leaderboards after validation const confirmedLeaderboards = {}; // Function to scan and analyze blocks function ScanBlocks() { const blockValidationCounts = {}; // First, validate each block and count valid ones for each tournament for (let i = 0; i < tournamentBlocks.length; i++) { let isValid = true; // Loop through the started tournaments to validate the block for (let j = 0; j < startedTournaments.length; j++) { if (startedTournaments[j].id === tournamentBlocks[i].tournamentId) { // Found the matching tournament for this block if (!startedTournaments[j].participants.includes(tournamentBlocks[i].owner)) { console.log("***Block was sent by non-participant. Allowing this for now."); isValid = false; break; } // Compare blocks with the same tournamentId and minute number for (let k = 0; k < tournamentBlocks.length; k++) { if ( k !== i && tournamentBlocks[k].tournamentId === tournamentBlocks[i].tournamentId && tournamentBlocks[k].minute === tournamentBlocks[i].minute ) { // Found another block with the same tournamentId and minute if (JSON.stringify(tournamentBlocks[k].leaderboard) !== JSON.stringify(tournamentBlocks[i].leaderboard)) { console.log(`***Mismatch detected in leaderboard for tournament ${tournamentBlocks[i].tournamentId} at minute ${tournamentBlocks[i].minute}`); isValid = false; break; } } } } } if (isValid) { console.log(`Block ${i + 1} is valid.`); // Track the valid blocks per tournament const tournamentId = tournamentBlocks[i].tournamentId; if (!blockValidationCounts[tournamentId]) { blockValidationCounts[tournamentId] = { count: 0, total: 0 }; } blockValidationCounts[tournamentId].count++; } else { console.log(`Block ${i + 1} is invalid.`); } // Track total blocks per tournament for comparison later const tournamentId = tournamentBlocks[i].tournamentId; if (!blockValidationCounts[tournamentId]) { blockValidationCounts[tournamentId] = { count: 0, total: 0 }; } blockValidationCounts[tournamentId].total++; } // After validation, confirm tournaments with more than 50% valid blocks for (const tournamentId in blockValidationCounts) { const { count, total } = blockValidationCounts[tournamentId]; if (count > total / 2) { // More than 50% of blocks are valid, store the leaderboard const validBlock = tournamentBlocks.find(block => block.tournamentId === parseInt(tournamentId)); if (validBlock) { confirmedLeaderboards[tournamentId] = validBlock.leaderboard; console.log(`*** Tournament ${tournamentId} confirmed with a valid leaderboard.`); } } else { console.log(`*** Tournament ${tournamentId} did not meet the 50% valid block requirement.`); } } }