navskit
Version:
Deploy TypeScript logic on Ethereum. Includes core library, CLI tools, and utilities.
104 lines (103 loc) • 4.71 kB
JavaScript
;
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;
}
};