@kyve/core-beta
Version:
🚀 The base KYVE node implementation.
201 lines (200 loc) • 9.62 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createBundleProposal = void 0;
const utils_1 = require("../../utils");
/**
* createBundleProposal assembles a bundle proposal by loading
* data from the local cache and uploading it to a storage provider.
* After the data was successfully saved the node submits the bundle
* proposal with the storage id and other information to the network
* so that other participants can validate and vote on it.
*
* If one of the steps fails the node should skip it's uploader role
* to prevent slashes.
*
* @method createBundleProposal
* @param {Node} this
* @return {Promise<void>}
*/
async function createBundleProposal() {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
try {
this.logger.info(`Creating a new bundle proposal for the next bundle proposal round`);
// create bundle proposal from the current bundle proposal index
const fromIndex = parseInt(this.pool.data.current_index) +
parseInt(this.pool.bundle_proposal.bundle_size);
// create the bundle proposal from the determined bundle start index
// and index all the way until the maximum bundle size is reached
const toIndex = fromIndex + parseInt(this.pool.data.max_bundle_size);
// load bundle proposal from local cache
const bundleProposal = [];
// here we try to fetch data items from the current index
// to the proposal index. If we fail before we simply
// abort and and submit the data collected we have available
// right now
this.logger.debug(`Loading bundle from index ${fromIndex} to index ${toIndex}`);
for (let i = fromIndex; i < toIndex; 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());
bundleProposal.push(item);
}
catch {
// if the data item was not found simply abort
// and submit what we just have now
break;
}
}
// if no data was found on the cache skip the uploader role
// so that this node does not receive an upload slash
if (!bundleProposal.length) {
this.logger.info(`No data was found on local cache from required range`);
await this.skipUploaderRole(fromIndex);
return;
}
this.logger.info(`Data was found on local cache from required range`);
// get the first key of the bundle proposal which gets
// included in the bundle proposal and saved on chain
// as from_key
const fromKey = (_b = (_a = bundleProposal.at(0)) === null || _a === void 0 ? void 0 : _a.key) !== null && _b !== void 0 ? _b : "";
// get the last key of the bundle proposal which gets
// included in the bundle proposal and saved on chain
// as to_key
const toKey = (_d = (_c = bundleProposal.at(-1)) === null || _c === void 0 ? void 0 : _c.key) !== null && _d !== void 0 ? _d : "";
// get the last value of the bundle proposal and format
// it so it can be included in the bundle proposal and
// saved on chain
this.logger.debug(`this.runtime.summarizeDataBundle($BUNDLE_PROPOSAL)`);
const bundleSummary = await this.runtime
.summarizeDataBundle(this, bundleProposal)
.catch((err) => {
this.logger.error(`Unexpected error summarizing bundle. Skipping Uploader Role ...`);
this.logger.error((0, utils_1.standardizeJSON)(err));
return null;
});
// skip uploader role if bundleSummary is null
if (bundleSummary === null) {
await this.skipUploaderRole(fromIndex);
return;
}
// get current compression defined on pool
this.logger.debug(`compressionFactory(${(_f = (_e = this.pool.data) === null || _e === void 0 ? void 0 : _e.current_compression_id) !== null && _f !== void 0 ? _f : 0})`);
const compression = this.compressionFactory((_h = (_g = this.pool.data) === null || _g === void 0 ? void 0 : _g.current_compression_id) !== null && _h !== void 0 ? _h : 0);
// if data was found on the cache proceed with compressing the
// bundle for the upload to the storage provider
this.logger.debug(`this.compression.compress($RAW_BUNDLE_PROPOSAL)`);
const storageProviderData = await compression
.compress((0, utils_1.bundleToBytes)(bundleProposal))
.catch((err) => {
this.logger.error(`Unexpected error compressing bundle. Skipping Uploader Role ...`);
this.logger.error((0, utils_1.standardizeJSON)(err));
return null;
});
// skip uploader role if compression returns null
if (storageProviderData === null) {
await this.skipUploaderRole(fromIndex);
return;
}
this.logger.info(`Successfully compressed bundle with Compression:${compression.name}`);
// create tags for bundle to make it easier to find KYVE data
// on the storage provider itself
const tags = [
{
name: "Content-Type",
value: compression.mimeType,
},
{
name: "Application",
value: "KYVE",
},
{
name: "ChainId",
value: await this.client.nativeClient.getChainId(),
},
{
name: "@kyve/core",
value: "KYVE",
},
{
name: this.runtime.name,
value: this.runtime.version,
},
{
name: "Pool",
value: this.poolId.toString(),
},
{
name: "Uploader",
value: this.client.account.address,
},
{
name: "FromIndex",
value: toIndex.toString(),
},
{
name: "ToIndex",
value: (toIndex + bundleProposal.length).toString(),
},
{
name: "BundleSize",
value: bundleProposal.length.toString(),
},
{
name: "FromKey",
value: fromKey,
},
{
name: "ToKey",
value: toKey,
},
{
name: "BundleSummary",
value: bundleSummary,
},
];
// try to upload the bundle proposal to the storage provider
// if the upload fails the node should immediately skip the
// uploader role to prevent upload slashes
try {
// get current storage provider defined on pool
this.logger.debug(`storageProviderFactory(${(_k = (_j = this.pool.data) === null || _j === void 0 ? void 0 : _j.current_storage_provider_id) !== null && _k !== void 0 ? _k : 0}, $STORAGE_PRIV)`);
const storageProvider = await this.storageProviderFactory((_m = (_l = this.pool.data) === null || _l === void 0 ? void 0 : _l.current_storage_provider_id) !== null && _m !== void 0 ? _m : 0);
// upload the bundle proposal to the storage provider
// and get a storage id. With that other participants in the
// network can retrieve the data again and validate it
this.logger.debug(`this.storageProvider.saveBundle($STORAGE_PROVIDER_DATA,$TAGS)`);
const { storageId, storageData } = await storageProvider.saveBundle(storageProviderData, tags);
// throw error if storage provider returns an empty storage id
if (!storageId) {
throw new Error("Storage Provider returned empty storageId");
}
// hash the raw data which gets uploaded to the storage provider
// with sha256
const dataSize = storageData.byteLength;
// hash the raw data which gets uploaded to the storage provider
// with sha256
const dataHash = (0, utils_1.sha256)(storageData);
this.m.storage_provider_save_successful.inc();
this.logger.info(`Successfully saved bundle on StorageProvider:${storageProvider.name}`);
// if the bundle was successfully uploaded to the storage provider
// the node can finally submit the actual bundle proposal to
// the network
await this.submitBundleProposal(storageId, dataSize, dataHash, fromIndex, bundleProposal.length, fromKey, toKey, bundleSummary);
this.logger.info(`Successfully submitted BundleProposal:${storageId}`);
}
catch (err) {
this.logger.info(`Saving bundle proposal on StorageProvider was unsucessful`);
this.logger.debug((0, utils_1.standardizeJSON)(err));
this.m.storage_provider_save_failed.inc();
// if the bundle fails to the uploaded to the storage provider
// let the node skip the uploader role and continue
await this.skipUploaderRole(fromIndex);
}
}
catch (err) {
this.logger.error(`Unexpected error creating bundle proposal. Skipping proposal ...`);
this.logger.error((0, utils_1.standardizeJSON)(err));
}
}
exports.createBundleProposal = createBundleProposal;