UNPKG

navskit

Version:

Deploy TypeScript logic on Ethereum. Includes core library, CLI tools, and utilities.

104 lines (103 loc) 4.71 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.consensus = void 0; exports.consensus = { exactMatch: (args) => { const { quorum } = args; if (!quorum) { // no quorum was reached. report an error throw new Error('no consensus reached'); } return quorum.decoded; }, exactMatchEjectIncorrect: (args) => { const { quorum, responses, tools } = args; if (!quorum) { // no quorum was reached. report an error throw new Error('no consensus reached'); } // Eject any operators that don't match quorum (check via .raw) Object.entries(responses).forEach(([operatorAddress, response]) => { if (response.raw !== quorum.raw) { tools.eject(operatorAddress, `Response did not match quorum. Expected: ${quorum.raw}, Got: ${response.raw}`); } }); return quorum.decoded; }, exactMatchSlashIncorrect: (args) => { const { quorum, responses, tools, stake: _stake } = args; if (!quorum) { // no quorum was reached. report an error throw new Error('no consensus reached'); } // Slash any operators that don't match quorum (check via .raw) Object.entries(responses).forEach(([operatorAddress, response]) => { if (response.raw !== quorum.raw) { // Slash 10% of the operator's stake tools.slash(operatorAddress, 10, "did not match quorum"); } }); return quorum.decoded; }, numericalAverage: (args) => { const { quorum, responses, tools: _tools } = args; if (!quorum) { // no quorum was reached. report an error throw new Error('no consensus reached'); } // Calculate numerical average over all responses const values = Object.values(responses).map(response => { // Ensure the response can be converted to a number const value = Number(response.decoded); if (isNaN(value)) { throw new Error('Cannot calculate numerical average: response is not a number'); } return value; }); // Calculate the average const average = values.reduce((sum, value) => sum + value, 0) / values.length; // Use the quorum response format but replace the decoded value with our average return average; }, numericalAverageEjectOutliers: (args) => { const { quorum, responses, tools } = args; if (!quorum) { // no quorum was reached. report an error throw new Error('no consensus reached'); } // Get operators and their numerical values const operatorValues = Object.entries(responses).map(([operatorAddress, response]) => { const value = Number(response.decoded); if (isNaN(value)) { throw new Error('Cannot calculate numerical average: response is not a number'); } return [operatorAddress, value]; }); const values = operatorValues.map(([_, value]) => value); // Calculate mean const mean = values.reduce((sum, value) => sum + value, 0) / values.length; // Calculate standard deviation const squaredDiffs = values.map(value => Math.pow(value - mean, 2)); const variance = squaredDiffs.reduce((sum, value) => sum + value, 0) / values.length; const stdDev = Math.sqrt(variance); // Filter out values more than 1 standard deviation away from the mean const filteredOperatorValues = operatorValues.filter(([_, value]) => { const distance = Math.abs(value - mean); return distance <= stdDev; }); // Eject operators with outlier values operatorValues.forEach(([operatorAddress, value]) => { const distance = Math.abs(value - mean); if (distance > stdDev) { tools.eject(operatorAddress, `Response is an outlier: ${value}. Mean: ${mean}, StdDev: ${stdDev}, Threshold: ${stdDev}`); } }); // Calculate new average from filtered values const filteredValues = filteredOperatorValues.map(([_, value]) => value); const filteredAverage = filteredValues.length > 0 ? filteredValues.reduce((sum, value) => sum + value, 0) / filteredValues.length : mean; // If all values were ejected, use the original mean // Use the quorum response format but replace the decoded value with our filtered average return filteredAverage; } };