UNPKG

lisk-framework

Version:

Lisk blockchain application platform

178 lines 8.13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RandomModule = void 0; const lisk_cryptography_1 = require("@liskhq/lisk-cryptography"); const lisk_codec_1 = require("@liskhq/lisk-codec"); const lisk_utils_1 = require("@liskhq/lisk-utils"); const lisk_validator_1 = require("@liskhq/lisk-validator"); const state_machine_1 = require("../../state_machine"); const base_module_1 = require("../base_module"); const method_1 = require("./method"); const constants_1 = require("./constants"); const endpoint_1 = require("./endpoint"); const schemas_1 = require("./schemas"); const used_hash_onions_1 = require("./stores/used_hash_onions"); const utils_1 = require("./utils"); const validator_reveals_1 = require("./stores/validator_reveals"); const hash_onion_1 = require("./stores/hash_onion"); class RandomModule extends base_module_1.BaseModule { constructor() { super(); this.method = new method_1.RandomMethod(this.stores, this.events, this.name); this.endpoint = new endpoint_1.RandomEndpoint(this.stores, this.offchainStores); this.stores.register(validator_reveals_1.ValidatorRevealsStore, new validator_reveals_1.ValidatorRevealsStore(this.name, 0)); this.offchainStores.register(hash_onion_1.HashOnionStore, new hash_onion_1.HashOnionStore(this.name)); this.offchainStores.register(used_hash_onions_1.UsedHashOnionsStore, new used_hash_onions_1.UsedHashOnionsStore(this.name)); } metadata() { return { ...this.baseMetadata(), endpoints: [ { name: this.endpoint.isSeedRevealValid.name, request: schemas_1.isSeedRevealValidRequestSchema, response: schemas_1.isSeedRevealValidResponseSchema, }, { name: this.endpoint.setHashOnion.name, request: schemas_1.hashOnionSchema, }, { name: this.endpoint.getHashOnionSeeds.name, response: schemas_1.hashOnionSchema, }, { name: this.endpoint.hasHashOnion.name, request: schemas_1.addressSchema, response: schemas_1.hasHashOnionResponseSchema, }, { name: this.endpoint.getHashOnionUsage.name, request: schemas_1.addressSchema, response: schemas_1.getHashOnionUsageResponse, }, ], assets: [ { version: 2, data: schemas_1.blockHeaderAssetRandomModule, }, ], }; } async init(args) { const { moduleConfig } = args; const config = lisk_utils_1.objects.mergeDeep({}, constants_1.defaultConfig, moduleConfig); lisk_validator_1.validator.validate(schemas_1.randomModuleConfig, config); this._maxLengthReveals = config.maxLengthReveals; } async insertAssets(context) { const usedHashOnionsStore = this.offchainStores.get(used_hash_onions_1.UsedHashOnionsStore); let usedHashOnions = []; try { const usedHashOnionsData = await usedHashOnionsStore.get(context, context.header.generatorAddress); usedHashOnions = usedHashOnionsData.usedHashOnions; } catch (error) { if (!(error instanceof state_machine_1.NotFoundError)) { throw error; } } const nextHashOnion = await this._getNextHashOnion(usedHashOnions, context.header.generatorAddress, context.header.height, context.logger, context); const nextUsedHashOnion = { count: nextHashOnion.count, height: context.header.height, }; context.assets.setAsset(this.name, lisk_codec_1.codec.encode(schemas_1.blockHeaderAssetRandomModule, { seedReveal: nextHashOnion.hash })); await usedHashOnionsStore.setLatest(context, context.getFinalizedHeight(), context.header.generatorAddress, nextUsedHashOnion, usedHashOnions); } async verifyAssets(context) { const encodedAsset = context.assets.getAsset(this.name); if (!encodedAsset) { throw new Error('Random module asset must exist.'); } const asset = lisk_codec_1.codec.decode(schemas_1.blockHeaderAssetRandomModule, encodedAsset); lisk_validator_1.validator.validate(schemas_1.blockHeaderAssetRandomModule, asset); } async initGenesisState(context) { const randomDataStore = this.stores.get(validator_reveals_1.ValidatorRevealsStore); await randomDataStore.set(context, constants_1.EMPTY_KEY, { validatorReveals: [] }); } async afterTransactionsExecute(context) { const encodedAsset = context.assets.getAsset(this.name); if (!encodedAsset) { throw new Error('Random module asset must exist.'); } const asset = lisk_codec_1.codec.decode(schemas_1.blockHeaderAssetRandomModule, encodedAsset); const randomDataStore = this.stores.get(validator_reveals_1.ValidatorRevealsStore); const { validatorReveals } = await randomDataStore.get(context, constants_1.EMPTY_KEY); const valid = (0, utils_1.isSeedValidInput)(context.header.generatorAddress, asset.seedReveal, validatorReveals); const nextReveals = validatorReveals.length === this._maxLengthReveals ? validatorReveals.slice(1) : validatorReveals; nextReveals.push({ seedReveal: asset.seedReveal, generatorAddress: context.header.generatorAddress, height: context.header.height, valid, }); await randomDataStore.set(context, constants_1.EMPTY_KEY, { validatorReveals: nextReveals }); } async _getNextHashOnion(usedHashOnions, address, height, logger, context) { const hashOnion = await this._getHashOnion(context, address); if (!hashOnion) { return { hash: lisk_cryptography_1.utils.generateHashOnionSeed(), count: 0, }; } const usedHashOnion = usedHashOnions.reduce((prev, current) => { if (current.height < height && (!prev || prev.height < current.height)) { return current; } return prev; }, undefined); if (!usedHashOnion) { return { hash: hashOnion.hashes[0], count: 0, }; } const newCount = usedHashOnion.count + 1; if (newCount > hashOnion.count) { logger.warn('All of the hash onion has been used already. Please update to the new hash onion.'); return { hash: lisk_cryptography_1.utils.generateHashOnionSeed(), count: usedHashOnion.count, }; } const distanceAfterCheckpoint = newCount % hashOnion.distance; if (distanceAfterCheckpoint === 0) { return { hash: hashOnion.hashes[newCount / hashOnion.distance], count: newCount, }; } const nextCheckpointIndex = Math.ceil(newCount / hashOnion.distance); const nextCheckpointHash = hashOnion.hashes[nextCheckpointIndex]; const hashesFromCheckpoint = lisk_cryptography_1.utils.hashOnion(nextCheckpointHash, hashOnion.distance - distanceAfterCheckpoint, 1); return { hash: hashesFromCheckpoint[0], count: newCount, }; } async _getHashOnion(context, address) { const hashOnionStore = this.offchainStores.get(hash_onion_1.HashOnionStore); try { return await hashOnionStore.get(context, address); } catch (error) { if (error instanceof state_machine_1.NotFoundError) { return undefined; } throw error; } } } exports.RandomModule = RandomModule; //# sourceMappingURL=module.js.map