@holographxyz/cli
Version:
Holograph operator CLI
115 lines (114 loc) • 6.24 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.OperatorJobAwareCommand = void 0;
const bignumber_1 = require("@ethersproject/bignumber");
const units_1 = require("@ethersproject/units");
const utils_1 = require("./utils");
const healthcheck_1 = require("../base-commands/healthcheck");
class OperatorJobAwareCommand extends healthcheck_1.HealthCheck {
networkMonitor;
operatorStatus = {
address: '',
active: {},
currentPod: {},
podIndex: {},
podSize: {},
};
operatorJobs = {};
getTargetTime(network, jobDetails) {
let targetTime = new Date(bignumber_1.BigNumber.from(jobDetails.startTimestamp).toNumber() * 1000).getTime();
if (jobDetails.operator !== utils_1.zeroAddress && jobDetails.operator !== this.operatorStatus.address) {
// operator is not selected
// add +60 seconds to target time
targetTime += 60 * 1000;
// ignore where operator is not in same pod
if (jobDetails.pod === this.operatorStatus.currentPod[network]) {
for (let i = 0; i < 5; i++) {
if (jobDetails.fallbackOperators[i] >= this.operatorStatus.podSize[network] ||
jobDetails.fallbackOperators[i] === 0) {
// anyone from that pod can operate
break;
}
else if (jobDetails.fallbackOperators[i] === this.operatorStatus.podIndex[network]) {
// operator has been selected as the fallback
break;
}
// add +60 seconds to target time
targetTime += 60 * 1000;
}
}
else {
// add time delay for 5 fallback operators to have a chance first
targetTime += 60 * 1000 * 5;
}
}
return targetTime;
}
async decodeOperatorJob(network, operatorJobHash, operatorJobPayload, tags) {
const contract = this.networkMonitor.operatorContract.connect(this.networkMonitor.providers[network]);
const rawJobDetails = await contract.getJobDetails(operatorJobHash);
const jobDetails = {
pod: rawJobDetails[0],
blockTimes: rawJobDetails[1],
operator: rawJobDetails[2].toLowerCase(),
startBlock: rawJobDetails[3],
startTimestamp: bignumber_1.BigNumber.from(rawJobDetails[4]),
fallbackOperators: rawJobDetails[5],
};
if (jobDetails.startBlock > 0) {
this.networkMonitor.structuredLog(network, `Decoded valid job ${operatorJobHash}`, tags);
this.networkMonitor.structuredLog(network, `Selected operator for job is ${jobDetails.operator}`, tags);
const targetTime = this.getTargetTime(network, jobDetails);
// extract gasLimit and gasPrice from payload
const gasLimit = bignumber_1.BigNumber.from('0x' + operatorJobPayload.slice(-128, -64));
this.networkMonitor.structuredLog(network, `Job gas limit is ${gasLimit.toNumber()}`, tags);
const gasPrice = bignumber_1.BigNumber.from('0x' + operatorJobPayload.slice(-64));
this.networkMonitor.structuredLog(network, `Job maximum gas price is ${(0, units_1.formatUnits)(gasPrice, 'gwei')} GWEI`, tags);
const remainingTime = Math.round((targetTime - Date.now()) / 1000);
this.networkMonitor.structuredLog(network, `Job can be operated ${remainingTime <= 0 ? 'immediately' : 'in ' + remainingTime + ' seconds'}`, tags);
this.operatorJobs[operatorJobHash] = {
network,
hash: operatorJobHash,
payload: operatorJobPayload,
targetTime,
gasLimit,
gasPrice,
jobDetails,
};
// process.stdout.write('\n\n' + JSON.stringify(this.operatorJobs[operatorJobHash],undefined,2) + '\n\n')
return this.operatorJobs[operatorJobHash];
}
this.networkMonitor.structuredLogError(network, `Could not decode job ${operatorJobHash} (invalid or already completed)`, tags);
return undefined;
}
updateJobTimes() {
for (const hash of Object.keys(this.operatorJobs)) {
const job = this.operatorJobs[hash];
this.operatorJobs[hash].targetTime = this.getTargetTime(job.network, job.jobDetails);
}
}
/*
@dev defining some of the current values like: if operator is bonded on the network, which pod they are in,
which index position inside of the pod doe they hold (used for fallback operator calculations),
the current pod size (used for fallback operator calculations).
*/
async updateOperatorStatus(network) {
const contract = this.networkMonitor.operatorContract.connect(this.networkMonitor.providers[network]);
this.operatorStatus.active[network] = !bignumber_1.BigNumber.from(await contract.getBondedAmount(this.operatorStatus.address)).isZero();
this.operatorStatus.currentPod[network] = bignumber_1.BigNumber.from(await contract.getBondedPod(this.operatorStatus.address)).toNumber();
this.operatorStatus.podIndex[network] = bignumber_1.BigNumber.from(await contract.getBondedPodIndex(this.operatorStatus.address)).toNumber();
if (this.operatorStatus.currentPod[network] > 0) {
this.operatorStatus.podSize[network] = bignumber_1.BigNumber.from(await contract.getPodOperatorsLength(this.operatorStatus.currentPod[network])).toNumber();
}
}
async checkJobStatus(operatorJobHash) {
if (operatorJobHash !== undefined && operatorJobHash !== '' && operatorJobHash in this.operatorJobs) {
const job = this.operatorJobs[operatorJobHash];
if ((await this.decodeOperatorJob(job.network, job.hash, job.payload, [])) === undefined) {
// job is no longer active/valid, need to remove it from list
delete this.operatorJobs[job.hash];
}
}
}
}
exports.OperatorJobAwareCommand = OperatorJobAwareCommand;