lisk-framework
Version:
Lisk blockchain application platform
178 lines • 8.13 kB
JavaScript
"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