@swaptoshi/governance-module
Version:
Klayr governance on-chain module
167 lines • 7.79 kB
JavaScript
;
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