lisk-framework
Version:
Lisk blockchain application platform
124 lines • 5.53 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseStateRecoveryCommand = void 0;
const lisk_utils_1 = require("@liskhq/lisk-utils");
const lisk_db_1 = require("@liskhq/lisk-db");
const lisk_cryptography_1 = require("@liskhq/lisk-cryptography");
const base_interoperability_command_1 = require("./base_interoperability_command");
const constants_1 = require("./constants");
const schemas_1 = require("./schemas");
const state_machine_1 = require("../../state_machine");
const terminated_state_1 = require("./stores/terminated_state");
const base_store_1 = require("../base_store");
const invalid_smt_verification_1 = require("./events/invalid_smt_verification");
class BaseStateRecoveryCommand extends base_interoperability_command_1.BaseInteroperabilityCommand {
constructor() {
super(...arguments);
this.schema = schemas_1.stateRecoveryParamsSchema;
}
async verify(context) {
const { params: { chainID, storeEntries, module }, } = context;
const terminatedStateSubstore = this.stores.get(terminated_state_1.TerminatedStateStore);
const terminatedStateAccountExists = await terminatedStateSubstore.has(context, chainID);
if (!terminatedStateAccountExists) {
return {
status: state_machine_1.VerifyStatus.FAIL,
error: new Error('The terminated state does not exist.'),
};
}
const terminatedStateAccount = await terminatedStateSubstore.get(context, chainID);
if (!terminatedStateAccount.initialized) {
return {
status: state_machine_1.VerifyStatus.FAIL,
error: new Error('The terminated state is not initialized.'),
};
}
const moduleMethod = this.interoperableCCMethods.get(module);
if (!moduleMethod) {
return {
status: state_machine_1.VerifyStatus.FAIL,
error: new Error('Module is not registered on the chain.'),
};
}
if (!moduleMethod.recover) {
return {
status: state_machine_1.VerifyStatus.FAIL,
error: new Error('Module is not recoverable.'),
};
}
const queryKeys = [];
for (const entry of storeEntries) {
const queryKey = Buffer.concat([entry.substorePrefix, entry.storeKey]);
queryKeys.push(queryKey);
}
if (!lisk_utils_1.objects.bufferArrayUniqueItems(queryKeys)) {
return {
status: state_machine_1.VerifyStatus.FAIL,
error: new Error('Recovered store keys are not pairwise distinct.'),
};
}
return {
status: state_machine_1.VerifyStatus.OK,
};
}
async execute(context) {
const { params: { chainID, storeEntries, module, siblingHashes }, } = context;
const storeQueriesVerify = [];
const queryKeys = [];
const storePrefix = (0, base_store_1.computeStorePrefix)(module);
for (const entry of storeEntries) {
const queryKey = Buffer.concat([
storePrefix,
entry.substorePrefix,
lisk_cryptography_1.utils.hash(entry.storeKey),
]);
queryKeys.push(queryKey);
storeQueriesVerify.push({
key: queryKey,
value: lisk_cryptography_1.utils.hash(entry.storeValue),
bitmap: entry.bitmap,
});
}
const terminatedStateAccount = await this.stores
.get(terminated_state_1.TerminatedStateStore)
.get(context, chainID);
const proofOfInclusionStores = { siblingHashes, queries: storeQueriesVerify };
const smtVerified = await new lisk_db_1.SparseMerkleTree().verifyInclusionProof(terminatedStateAccount.stateRoot, queryKeys, proofOfInclusionStores);
if (!smtVerified) {
this.events.get(invalid_smt_verification_1.InvalidSMTVerificationEvent).error(context);
throw new Error('State recovery proof of inclusion is not valid.');
}
const moduleMethod = this.interoperableCCMethods.get(module);
const storeQueriesUpdate = [];
for (const entry of storeEntries) {
try {
await moduleMethod.recover({
...context,
module,
terminatedChainID: chainID,
substorePrefix: entry.substorePrefix,
storeKey: entry.storeKey,
storeValue: entry.storeValue,
});
storeQueriesUpdate.push({
key: Buffer.concat([storePrefix, entry.substorePrefix, lisk_cryptography_1.utils.hash(entry.storeKey)]),
value: constants_1.RECOVERED_STORE_VALUE,
bitmap: entry.bitmap,
});
}
catch (err) {
throw new Error(`Recovery failed for module: ${module}`);
}
}
const root = await new lisk_db_1.SparseMerkleTree().calculateRoot({
queries: storeQueriesUpdate,
siblingHashes,
});
await this.stores.get(terminated_state_1.TerminatedStateStore).set(context, chainID, {
...terminatedStateAccount,
stateRoot: root,
});
}
}
exports.BaseStateRecoveryCommand = BaseStateRecoveryCommand;
//# sourceMappingURL=base_state_recovery.js.map
;