UNPKG

@swaptoshi/governance-module

Version:

Klayr governance on-chain module

167 lines 7.79 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BaseGovernableConfig = void 0; const klayr_framework_1 = require("klayr-framework"); const codec_1 = require("@klayr/codec"); const utils = require("@klayr/utils"); const validator = require("@klayr/validator"); const codec_2 = require("@klayr/codec"); const schema_1 = require("./schema"); const utils_1 = require("@swaptoshi/utils"); const config_updated_1 = require("./events/config_updated"); class BaseGovernableConfig extends klayr_framework_1.Modules.BaseStore { constructor(moduleName, index) { super(moduleName, index); this.storeKey = Buffer.alloc(0); this.governanceEvent = new klayr_framework_1.Modules.NamedRegistry(); this.module = ''; this.index = 0; this.registered = false; this.initialized = false; this.schema = codec_2.emptySchema; this.default = {}; this.module = moduleName; this.index = index; } get name() { const name = this.constructor.name.replace('GovernableConfig', ''); return name.charAt(0).toLowerCase() + name.substring(1); } register(events, governanceMethod, args) { this.governanceEvent = events; this.method = governanceMethod; this.genesisConfig = args.genesisConfig; this.init(args); this.registered = true; } unregister() { this.method = undefined; this.genesisConfig = undefined; this.registered = false; } beforeConfigInit(_genesisConfig) { } async beforeSetConfig(_context) { } async afterSetConfig(_context) { } async verify(_context) { return { status: klayr_framework_1.StateMachine.VerifyStatus.OK }; } init(args) { this.beforeConfigInit(args.genesisConfig); this.default = utils.objects.mergeDeep({}, this.default, args.moduleConfig); validator.validator.validate(utils_1.object.removeProperty(this.schema, ['governable']), this.default); utils_1.object.getUpdatedProperties({}, this.default, this.schema); this.initialized = true; } async initRegisteredConfig(context) { if (await this.has(context, this.storeKey)) return; if (Object.keys(this.schema.properties).length === 0) throw new Error(`schema for ${this.name} is not configured`); await this._setConfigHandler(context.getMethodContext(), this.default, true, false); } async getConfig(context) { if (!this.initialized) throw new Error(`${this.name} config not initialized. Call .init() in module.init() if not governable.`); if (this.registered) { const configStore = await this.get(context, this.storeKey); return codec_1.codec.decode(utils_1.object.removeProperty(this.schema, ['governable']), configStore.data); } return this.default; } async setConfig(context, value) { await this._setConfigHandler(context, value, true, true); } async getConfigWithPath(context, path) { const config = (await this.getConfig(context)); if (!utils_1.object.pathExists(config, path)) { throw new Error(`config with path ${path} on ${this.name} dosen't exists`); } const ret = utils_1.object.getValueFromPath(config, path); return ret; } async setConfigWithPath(context, path, value) { const config = (await this.getConfig(context)); if (!utils_1.object.pathExists(config, path)) { throw new Error(`config with path ${path} on ${this.name} dosen't exists`); } const updatedConfig = utils_1.object.updateValueFromPath(config, path, value); await this.setConfig(context, updatedConfig); } async dryRunSetConfig(context, value) { return this._setConfigHandler(context, value, false, true); } async dryRunSetConfigWithPath(context, path, value) { const config = (await this.getConfig(context)); if (!utils_1.object.pathExists(config, path)) { throw new Error(`config with path ${path} on ${this.name} dosen't exists`); } const updatedConfig = utils_1.object.updateValueFromPath(config, path, value); return this.dryRunSetConfig(context, updatedConfig); } async _setConfigHandler(context, value, mutateState, verifyGovernability) { if (!this.initialized) throw new Error(`${this.name} config not initialized. Call .init() in module.init() if not governable.`); if (!this.genesisConfig) throw new Error(`${this.name} genesis config is not registered`); const verify = await this.verify({ context, config: value, genesisConfig: this.genesisConfig }); if (verify.status !== klayr_framework_1.StateMachine.VerifyStatus.OK) throw new Error(`failed to verify governable config for ${this.name}: ${verify.error ? verify.error.message : 'unknown'}`); validator.validator.validate(utils_1.object.removeProperty(this.schema, ['governable']), value); let updatedPaths = []; let oldConfig = {}; if (await this.has(context, this.storeKey)) oldConfig = (await this.getConfig(context)); if (mutateState) await this.beforeSetConfig({ ...context, oldConfig, newConfig: value }); if (this.registered) { updatedPaths = utils_1.object.getUpdatedProperties(oldConfig, value, this.schema); if (verifyGovernability) { for (const updatedPath of updatedPaths) { if (utils_1.object.getSchemaByPath(this.schema, updatedPath.path).governable === false) { throw new Error(`attempt to modify non-governable config: ${updatedPath.path}`); } } } if (mutateState) { await this.set(context, this.storeKey, { data: codec_1.codec.encode(utils_1.object.removeProperty(this.schema, ['governable']), value) }); const events = this.governanceEvent.get(config_updated_1.ConfigUpdatedEvent); updatedPaths.forEach(updated => { events.add(context, { module: this.module, path: updated.path, old: updated.old, new: updated.new, type: updated.type, }, [this.storePrefix]); }); } } else if (mutateState) this.default = value; if (mutateState) await this.afterSetConfig({ ...context, oldConfig, newConfig: value }); return updatedPaths; } async get(context, key) { const subStore = context.getStore(this.storePrefix, this.subStorePrefix); return subStore.getWithSchema(key, schema_1.governableConfigSchema); } async has(context, key) { const subStore = context.getStore(this.storePrefix, this.subStorePrefix); return subStore.has(key); } async iterate(context, options) { const subStore = context.getStore(this.storePrefix, this.subStorePrefix); return subStore.iterateWithSchema(options, schema_1.governableConfigSchema); } async set(context, key, value) { const subStore = context.getStore(this.storePrefix, this.subStorePrefix); return subStore.setWithSchema(key, value, schema_1.governableConfigSchema); } async del(context, key) { const subStore = context.getStore(this.storePrefix, this.subStorePrefix); return subStore.del(key); } } exports.BaseGovernableConfig = BaseGovernableConfig; //# sourceMappingURL=base_governable_config.js.map