UNPKG

lisk-framework

Version:

Lisk blockchain application platform

239 lines 13.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MainchainInteroperabilityModule = void 0; const lisk_utils_1 = require("@liskhq/lisk-utils"); const lisk_validator_1 = require("@liskhq/lisk-validator"); const lisk_codec_1 = require("@liskhq/lisk-codec"); const base_interoperability_module_1 = require("../base_interoperability_module"); const method_1 = require("./method"); const cc_method_1 = require("./cc_method"); const endpoint_1 = require("./endpoint"); const schemas_1 = require("../schemas"); const chain_account_1 = require("../stores/chain_account"); const channel_data_1 = require("../stores/channel_data"); const own_chain_account_1 = require("../stores/own_chain_account"); const terminated_state_1 = require("../stores/terminated_state"); const terminated_outbox_1 = require("../stores/terminated_outbox"); const commands_1 = require("./commands"); const internal_method_1 = require("./internal_method"); const initialize_message_recovery_1 = require("./commands/initialize_message_recovery"); const cc_commands_1 = require("./cc_commands"); const recover_state_1 = require("./commands/recover_state"); const constants_1 = require("../constants"); const utils_1 = require("../utils"); const registered_names_1 = require("../stores/registered_names"); const errors_1 = require("../errors"); class MainchainInteroperabilityModule extends base_interoperability_module_1.BaseInteroperabilityModule { constructor() { super(...arguments); this.crossChainMethod = new cc_method_1.MainchainCCMethod(this.stores, this.events); this.internalMethod = new internal_method_1.MainchainInteroperabilityInternalMethod(this.stores, this.events, this.interoperableCCMethods); this.method = new method_1.MainchainInteroperabilityMethod(this.stores, this.events, this.interoperableCCMethods, this.internalMethod); this.endpoint = new endpoint_1.MainchainInteroperabilityEndpoint(this.stores, this.offchainStores); this._sidechainRegistrationCommand = new commands_1.RegisterSidechainCommand(this.stores, this.events, this.interoperableCCMethods, this.interoperableCCCommands, this.internalMethod); this._messageRecoveryInitializationCommand = new initialize_message_recovery_1.InitializeMessageRecoveryCommand(this.stores, this.events, this.interoperableCCMethods, this.interoperableCCCommands, this.internalMethod); this._crossChainUpdateCommand = new commands_1.SubmitMainchainCrossChainUpdateCommand(this.stores, this.events, this.interoperableCCMethods, this.interoperableCCCommands, this.internalMethod); this._messageRecoveryCommand = new commands_1.RecoverMessageCommand(this.stores, this.events, this.interoperableCCMethods, this.interoperableCCCommands, this.internalMethod); this._stateRecoveryCommand = new recover_state_1.RecoverStateCommand(this.stores, this.events, this.interoperableCCMethods, this.interoperableCCCommands, this.internalMethod); this._terminateSidechainForLivenessCommand = new commands_1.TerminateSidechainForLivenessCommand(this.stores, this.events, this.interoperableCCMethods, this.interoperableCCCommands, this.internalMethod); this.commands = [ this._crossChainUpdateCommand, this._messageRecoveryInitializationCommand, this._messageRecoveryCommand, this._sidechainRegistrationCommand, this._stateRecoveryCommand, this._terminateSidechainForLivenessCommand, ]; this.crossChainCommand = [ new cc_commands_1.MainchainCCRegistrationCommand(this.stores, this.events, this.interoperableCCMethods, this.internalMethod), new cc_commands_1.MainchainCCChannelTerminatedCommand(this.stores, this.events, this.interoperableCCMethods, this.internalMethod), ]; } addDependencies(tokenMethod, feeMethod) { this._sidechainRegistrationCommand.addDependencies(feeMethod, tokenMethod); this._crossChainUpdateCommand.init(this.method, tokenMethod); this.internalMethod.addDependencies(tokenMethod); this.tokenMethod = tokenMethod; } metadata() { return { ...this.baseMetadata(), endpoints: [ { name: this.endpoint.getChainAccount.name, request: schemas_1.getChainAccountRequestSchema, response: chain_account_1.chainDataSchema, }, { name: this.endpoint.getAllChainAccounts.name, request: schemas_1.getChainAccountRequestSchema, response: chain_account_1.allChainAccountsSchema, }, { name: this.endpoint.getChannel.name, request: schemas_1.getChannelRequestSchema, response: channel_data_1.channelSchema, }, { name: this.endpoint.getOwnChainAccount.name, response: own_chain_account_1.ownChainAccountSchema, }, { name: this.endpoint.getTerminatedStateAccount.name, request: schemas_1.getTerminatedStateAccountRequestSchema, response: terminated_state_1.terminatedStateSchema, }, { name: this.endpoint.getTerminatedOutboxAccount.name, request: schemas_1.getTerminatedOutboxAccountRequestSchema, response: terminated_outbox_1.terminatedOutboxSchema, }, { name: this.endpoint.getRegistrationFee.name, response: schemas_1.getRegistrationFeeSchema, }, { name: this.endpoint.getMinimumMessageFee.name, response: schemas_1.getMinimumMessageFeeResponseSchema, }, { name: this.endpoint.getChainValidators.name, request: schemas_1.getChainValidatorsRequestSchema, response: schemas_1.getChainValidatorsResponseSchema, }, { name: this.endpoint.isChainIDAvailable.name, request: schemas_1.isChainIDAvailableRequestSchema, response: schemas_1.isChainIDAvailableResponseSchema, }, { name: this.endpoint.isChainNameAvailable.name, request: schemas_1.isChainNameAvailableRequestSchema, response: schemas_1.isChainNameAvailableResponseSchema, }, { name: this.endpoint.getCCMSchema.name, response: schemas_1.getCCMSchemaResponseSchema, }, ], assets: [ { version: 0, data: schemas_1.genesisInteroperabilitySchema, }, ], }; } async initGenesisState(ctx) { const genesisBlockAssetBytes = ctx.assets.getAsset(constants_1.MODULE_NAME_INTEROPERABILITY); if (!genesisBlockAssetBytes) { return; } const genesisInteroperability = lisk_codec_1.codec.decode(schemas_1.genesisInteroperabilitySchema, genesisBlockAssetBytes); lisk_validator_1.validator.validate(schemas_1.genesisInteroperabilitySchema, genesisInteroperability); const { ownChainName, ownChainNonce, chainInfos, terminatedStateAccounts, terminatedOutboxAccounts, } = genesisInteroperability; const mainchainID = (0, utils_1.getMainchainID)(ctx.chainID); if (ownChainName !== constants_1.CHAIN_NAME_MAINCHAIN) { throw new Error(`ownChainName must be equal to ${constants_1.CHAIN_NAME_MAINCHAIN}.`); } if (chainInfos.length === 0 && ownChainNonce !== BigInt(0)) { throw new Error(`ownChainNonce must be 0 if chainInfos is empty.`); } else if (chainInfos.length !== 0 && ownChainNonce <= BigInt(0)) { throw new Error(`ownChainNonce must be positive if chainInfos is not empty.`); } this._verifyChainInfos(ctx, chainInfos, terminatedStateAccounts); this._verifyTerminatedStateAccounts(chainInfos, terminatedStateAccounts, mainchainID); this._verifyTerminatedOutboxAccounts(chainInfos, terminatedStateAccounts, terminatedOutboxAccounts); await this.processGenesisState(ctx, genesisInteroperability); } _verifyChainInfos(ctx, chainInfos, terminatedStateAccounts) { const chainIDs = chainInfos.map(info => info.chainID); if (!lisk_utils_1.objects.bufferArrayUniqueItems(chainIDs)) { throw new Error(`chainInfos doesn't hold unique chainID.`); } if (!lisk_utils_1.objects.isBufferArrayOrdered(chainIDs)) { throw new Error('chainInfos is not ordered lexicographically by chainID.'); } const chainDataNames = chainInfos.map(info => info.chainData.name); if (new Set(chainDataNames).size !== chainDataNames.length) { throw new Error(`chainData.name must be pairwise distinct.`); } const mainchainID = (0, utils_1.getMainchainID)(ctx.chainID); for (const chainInfo of chainInfos) { this._verifyChainID(chainInfo.chainID, mainchainID, 'chainInfo.'); this._verifyChainData(ctx, chainInfo, terminatedStateAccounts); this._verifyChannelData(ctx, chainInfo); this._verifyChainValidators(chainInfo); } } _verifyChainData(ctx, chainInfo, terminatedStateAccounts) { const validStatuses = [0, 1, 2]; const { chainData } = chainInfo; if (chainData.lastCertificate.timestamp >= ctx.header.timestamp) { throw new Error(`chainData.lastCertificate.timestamp must be less than header.timestamp.`); } if (!(0, utils_1.isValidName)(chainData.name)) { throw new errors_1.InvalidNameError('chainData.name'); } if (!validStatuses.includes(chainData.status)) { throw new Error(`chainData.status must be one of ${validStatuses.join(', ')}`); } if (chainData.status === 2) { const accountWithChainID = terminatedStateAccounts.find(accountWithChainIDTemp => accountWithChainIDTemp.chainID.equals(chainInfo.chainID)); if (!accountWithChainID) { throw new Error('For each chainInfo with status terminated there should be a corresponding entry in terminatedStateAccounts.'); } } } _verifyTerminatedStateAccounts(chainInfos, terminatedStateAccounts, mainchainID) { this._verifyTerminatedStateAccountsIDs(terminatedStateAccounts.map(a => a.chainID)); for (const terminatedStateAccountWithChainID of terminatedStateAccounts) { this._verifyChainID(terminatedStateAccountWithChainID.chainID, mainchainID, 'stateAccount.'); const correspondingChainInfo = chainInfos.find(chainInfo => chainInfo.chainID.equals(terminatedStateAccountWithChainID.chainID)); if (!correspondingChainInfo || correspondingChainInfo.chainData.status !== 2) { throw new Error('For each terminatedStateAccount there should be a corresponding chainInfo at TERMINATED state.'); } const stateAccount = terminatedStateAccountWithChainID.terminatedStateAccount; if (!stateAccount.stateRoot.equals(correspondingChainInfo.chainData.lastCertificate.stateRoot)) { throw new Error("stateAccount.stateRoot doesn't match chainInfo.chainData.lastCertificate.stateRoot."); } if (!stateAccount.mainchainStateRoot.equals(constants_1.EMPTY_HASH)) { throw new Error(`stateAccount.mainchainStateRoot is not equal to ${constants_1.EMPTY_HASH.toString('hex')}.`); } if (!stateAccount.initialized) { throw new Error('stateAccount is not initialized.'); } } } _verifyTerminatedOutboxAccounts(_chainInfos, terminatedStateAccounts, terminatedOutboxAccounts) { const chainIDs = terminatedOutboxAccounts.map(a => a.chainID); if (!lisk_utils_1.objects.bufferArrayUniqueItems(chainIDs)) { throw new Error('terminatedOutboxAccounts do not hold unique chainID.'); } if (!lisk_utils_1.objects.isBufferArrayOrdered(chainIDs)) { throw new Error('terminatedOutboxAccounts must be ordered lexicographically by chainID.'); } for (const outboxAccount of terminatedOutboxAccounts) { if (terminatedStateAccounts.find(a => a.chainID.equals(outboxAccount.chainID)) === undefined) { throw new Error(`Each entry outboxAccount in terminatedOutboxAccounts must have a corresponding entry in terminatedStateAccount. outboxAccount with chainID: ${outboxAccount.chainID.toString('hex')} does not exist in terminatedStateAccounts`); } } } async processGenesisState(ctx, genesisInteroperability) { await super.processGenesisState(ctx, genesisInteroperability); const { chainInfos } = genesisInteroperability; const namesSubStore = this.stores.get(registered_names_1.RegisteredNamesStore); for (const chainInfo of chainInfos) { await namesSubStore.set(ctx, Buffer.from(chainInfo.chainData.name, 'ascii'), { chainID: chainInfo.chainID, }); } await namesSubStore.set(ctx, Buffer.from(constants_1.CHAIN_NAME_MAINCHAIN, 'ascii'), { chainID: (0, utils_1.getMainchainID)(ctx.chainID), }); } } exports.MainchainInteroperabilityModule = MainchainInteroperabilityModule; //# sourceMappingURL=module.js.map