UNPKG

@kyve/core-beta

Version:

🚀 The base KYVE node implementation.

154 lines (153 loc) • 8.94 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.validateBundleProposal = void 0; const tx_1 = require("@kyve/proto-beta/client/kyve/bundles/v1beta1/tx"); const utils_1 = require("../../utils"); /** * validateBundleProposal validates a proposed bundle proposal * by first downloading the proposed data bundle from the storage * provider and then comparing it with a locally created validation * bundle. Furthermore, custom validation from the runtime is applied * at the end. * * @method validateBundleProposal * @param {Node} this * @param {number} updatedAt * @return {Promise<void>} */ async function validateBundleProposal(updatedAt) { var _a, _b, _c, _d; try { this.logger.info(`Validating bundle proposal = ${this.pool.bundle_proposal.storage_id}`); // retrieve the data of the bundle proposal in a save way // by retrying the retrieval if it fails const storageProviderResult = await this.saveBundleDownload(updatedAt); // if no bundle got returned it means that the pool is not active anymore // or a new bundle proposal round has started if (storageProviderResult === null) { return; } // vote invalid if data size does not match with proposed data size this.logger.debug(`Validating bundle proposal by data size`); this.logger.debug(`Proposed = ${this.pool.bundle_proposal.data_size}`); this.logger.debug(`Actual = ${storageProviderResult.byteLength}`); if (parseInt(this.pool.bundle_proposal.data_size) !== storageProviderResult.byteLength) { this.logger.info(`Found different byte size on bundle downloaded from storage provider`); await this.voteBundleProposal(this.pool.bundle_proposal.storage_id, utils_1.VOTE.INVALID); return; } this.logger.info(`Found matching data size = ${this.pool.bundle_proposal.data_size} Bytes`); // vote invalid if data hash does not match with proposed data hash this.logger.debug(`Validating bundle proposal by data hash`); this.logger.debug(`Proposed = ${this.pool.bundle_proposal.data_hash}`); this.logger.debug(`Actual = ${(0, utils_1.sha256)(storageProviderResult)}`); if (this.pool.bundle_proposal.data_hash !== (0, utils_1.sha256)(storageProviderResult)) { this.logger.info(`Found different hash on bundle downloaded from storage provider`); await this.voteBundleProposal(this.pool.bundle_proposal.storage_id, utils_1.VOTE.INVALID); return; } this.logger.info(`Found matching data hash = ${this.pool.bundle_proposal.data_hash}`); const validationBundle = await this.saveLoadValidationBundle(updatedAt); // if no bundle got returned it means that the pool is not active anymore // or a new bundle proposal round has started if (validationBundle === null) { return; } // vote invalid if bundle key does not match with proposed from key this.logger.debug(`Validating bundle proposal by bundle from_key`); this.logger.debug(`Proposed = ${this.pool.bundle_proposal.from_key}`); this.logger.debug(`Actual = ${(_a = validationBundle.at(0)) === null || _a === void 0 ? void 0 : _a.key}`); if (this.pool.bundle_proposal.from_key !== ((_b = validationBundle.at(0)) === null || _b === void 0 ? void 0 : _b.key)) { this.logger.info(`Found different value on proposed bundle from_key`); await this.voteBundleProposal(this.pool.bundle_proposal.storage_id, utils_1.VOTE.INVALID); return; } this.logger.info(`Found matching from key = ${this.pool.bundle_proposal.from_key}`); // vote invalid if bundle key does not match with proposed to key this.logger.debug(`Validating bundle proposal by bundle to_key`); this.logger.debug(`Proposed = ${this.pool.bundle_proposal.to_key}`); this.logger.debug(`Actual = ${(_c = validationBundle.at(-1)) === null || _c === void 0 ? void 0 : _c.key}`); if (this.pool.bundle_proposal.to_key !== ((_d = validationBundle.at(-1)) === null || _d === void 0 ? void 0 : _d.key)) { this.logger.info(`Found different value on proposed bundle to_key`); await this.voteBundleProposal(this.pool.bundle_proposal.storage_id, utils_1.VOTE.INVALID); return; } this.logger.info(`Found matching to key = ${this.pool.bundle_proposal.to_key}`); // vote invalid if bundle summary does not match with proposed summary this.logger.debug(`Validating bundle proposal by bundle summary`); this.logger.debug(`this.runtime.summarizeDataBundle($VALIDATION_BUNDLE)`); const bundleSummary = await this.runtime .summarizeDataBundle(this, validationBundle) .catch((err) => { this.logger.error(`Unexpected error summarizing bundle with runtime. Voting abstain ...`); this.logger.error((0, utils_1.standardizeJSON)(err)); return null; }); // vote abstain if bundleSummary is null if (bundleSummary === null) { await this.voteBundleProposal(this.pool.bundle_proposal.storage_id, utils_1.VOTE.ABSTAIN); return; } this.logger.debug(`Proposed = ${this.pool.bundle_proposal.bundle_summary}`); this.logger.debug(`Actual = ${bundleSummary}`); if (this.pool.bundle_proposal.bundle_summary !== bundleSummary) { this.logger.info(`Found different value on proposed bundle summary`); await this.voteBundleProposal(this.pool.bundle_proposal.storage_id, utils_1.VOTE.INVALID); return; } this.logger.info(`Found matching bundle summary = ${this.pool.bundle_proposal.bundle_summary}`); // if storage provider result is empty skip runtime validation if (storageProviderResult.byteLength) { // decompress the bundle with the specified compression type // and convert the bytes into a JSON format const proposedBundle = await this.saveBundleDecompress(storageProviderResult); try { // perform custom runtime bundle validation this.logger.debug(`Validating bundle proposal by custom runtime validation`); // validate if bundle size matches let valid = proposedBundle.length === validationBundle.length; // validate each data item in bundle with custom runtime validation for (let i = 0; i < proposedBundle.length; i++) { if (valid) { this.logger.debug(`this.runtime.validateDataItem($THIS, $PROPOSED_DATA_ITEM, $VALIDATION_DATA_ITEM)`); valid = await this.runtime.validateDataItem(this, proposedBundle[i], validationBundle[i]); this.logger.debug(`Validated data item: index:${i} - key:${proposedBundle[i].key} - result:${valid}`); } else { // abort further validation if an invalid data item was // found in bundle break; } } this.logger.debug(`Finished validating bundle by custom runtime validation. Result = ${valid}`); // vote with either valid or invalid const vote = valid ? tx_1.VoteType.VOTE_TYPE_VALID : tx_1.VoteType.VOTE_TYPE_INVALID; await this.voteBundleProposal(this.pool.bundle_proposal.storage_id, vote); } catch (err) { this.logger.error(`Unexpected error validating data items with runtime. Voting abstain ...`); this.logger.error((0, utils_1.standardizeJSON)(err)); await this.voteBundleProposal(this.pool.bundle_proposal.storage_id, tx_1.VoteType.VOTE_TYPE_ABSTAIN); } // update metrics this.m.bundles_amount.inc(); this.m.bundles_data_items.set(proposedBundle.length); this.m.bundles_byte_size.set(storageProviderResult.byteLength); } else { await this.voteBundleProposal(this.pool.bundle_proposal.storage_id, tx_1.VoteType.VOTE_TYPE_VALID); // update metrics this.m.bundles_amount.inc(); this.m.bundles_data_items.set(parseInt(this.pool.bundle_proposal.bundle_size)); this.m.bundles_byte_size.set(storageProviderResult.byteLength); } } catch (err) { this.logger.error(`Unexpected error validating bundle proposal. Skipping validation ...`); this.logger.error((0, utils_1.standardizeJSON)(err)); } } exports.validateBundleProposal = validateBundleProposal;