@kyve/core-beta
Version:
🚀 The base KYVE node implementation.
109 lines (95 loc) • 3.6 kB
text/typescript
import BigNumber from "bignumber.js";
import { Node } from "../..";
import { DataItem } from "../../types";
import { callWithBackoffStrategy, standardizeJSON, VOTE } from "../../utils";
/**
* saveLoadValidationBundle loads the bundle from the local
* cache for validation with the proposed bundle. If there is
* an error loading the bundle from cache the node instantly votes
* with abstain and continues to try to load the bundle
*
* @method saveLoadValidationBundle
* @param {Node} this
* @param {number} updatedAt
* @return {Promise<DataItem[] | null>}
*/
export async function saveLoadValidationBundle(
this: Node,
updatedAt: number
): Promise<DataItem[] | null> {
return await callWithBackoffStrategy(
async () => {
await this.syncPoolState();
const unixNow = new BigNumber(Date.now());
const unixIntervalEnd = new BigNumber(
this.pool.bundle_proposal!.updated_at
)
.plus(this.pool.data!.upload_interval)
.multipliedBy(1000);
// check if new proposal is available in the meantime
if (parseInt(this.pool.bundle_proposal!.updated_at) > updatedAt) {
return null;
}
// check if pool got inactive in the meantime
if (this.validateIsPoolActive()) {
return null;
}
// check if validator needs to upload
if (
this.pool.bundle_proposal!.next_uploader === this.staker &&
unixNow.gte(unixIntervalEnd)
) {
return null;
}
// load bundle from current pool current index to proposed index
const proposalStartIndex = parseInt(this.pool.data!.current_index);
const proposalTargetIndex =
proposalStartIndex + parseInt(this.pool.bundle_proposal!.bundle_size);
// attempt to load bundle from cache
const bundle: DataItem[] = [];
// in order to get the same bundle for validation as the one
// proposed the bundle is loaded with the proposed heights
this.logger.debug(
`Loading bundle from index ${proposalStartIndex} to index ${proposalTargetIndex}`
);
for (let i = proposalStartIndex; i < proposalTargetIndex; i++) {
try {
// try to get the data item from local cache
this.logger.debug(`this.cacheProvider.get(${i.toString()})`);
const item = await this.cacheProvider.get(i.toString());
bundle.push(item);
} catch {
// if a request data item can not be found abort and
// try again after a backoff time
throw new Error(
`Requested bundle could not be loaded from cache yet.`
);
}
}
this.logger.info(
`Successfully loaded validation bundle from CacheProvider:${this.cacheProvider.name}`
);
return standardizeJSON(bundle);
},
{ limitTimeoutMs: 5 * 60 * 1000, increaseByMs: 10 * 1000 },
async (err: any, ctx) => {
this.logger.info(
`Loading validation bundle from CacheProvider:${
this.cacheProvider.name
} was unsuccessful. Retrying in ${(ctx.nextTimeoutInMs / 1000).toFixed(
2
)}s ...`
);
this.logger.debug(standardizeJSON(err));
// vote abstain if validation bundle could not be loaded from cache.
// With voting abstain the network knows that the node
// is still online but just could not vote
if (!this.pool.bundle_proposal?.voters_abstain.includes(this.staker)) {
await this.voteBundleProposal(
this.pool.bundle_proposal!.storage_id,
VOTE.ABSTAIN
);
}
}
);
}