lisk-framework
Version:
Lisk blockchain application platform
176 lines • 8.63 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.RegisterSidechainCommand = void 0;
const lisk_codec_1 = require("@liskhq/lisk-codec");
const base_interoperability_command_1 = require("../../base_interoperability_command");
const constants_1 = require("../../constants");
const schemas_1 = require("../../schemas");
const utils_1 = require("../../utils");
const state_machine_1 = require("../../../../state_machine");
const chain_account_1 = require("../../stores/chain_account");
const channel_data_1 = require("../../stores/channel_data");
const chain_validators_1 = require("../../stores/chain_validators");
const outbox_root_1 = require("../../stores/outbox_root");
const registered_names_1 = require("../../stores/registered_names");
const chain_account_updated_1 = require("../../events/chain_account_updated");
const own_chain_account_1 = require("../../stores/own_chain_account");
const ccm_send_success_1 = require("../../events/ccm_send_success");
const errors_1 = require("../../errors");
class RegisterSidechainCommand extends base_interoperability_command_1.BaseInteroperabilityCommand {
constructor() {
super(...arguments);
this.schema = schemas_1.sidechainRegParams;
}
addDependencies(feeMethod, tokenMethod) {
this._feeMethod = feeMethod;
this._tokenMethod = tokenMethod;
}
async verify(context) {
const { transaction, params: { sidechainValidators, sidechainCertificateThreshold, chainID, name }, } = context;
if (!(0, utils_1.isValidName)(name)) {
return {
status: state_machine_1.VerifyStatus.FAIL,
error: new errors_1.InvalidNameError(),
};
}
const nameSubstore = this.stores.get(registered_names_1.RegisteredNamesStore);
const nameExists = await nameSubstore.has(context, Buffer.from(name, 'ascii'));
if (nameExists) {
return {
status: state_machine_1.VerifyStatus.FAIL,
error: new Error('Name already registered.'),
};
}
const chainAccountSubstore = this.stores.get(chain_account_1.ChainAccountStore);
const chainAccountExists = await chainAccountSubstore.has(context, chainID);
if (chainAccountExists) {
return {
status: state_machine_1.VerifyStatus.FAIL,
error: new Error('Chain ID already registered.'),
};
}
if (chainID[0] !== context.chainID[0]) {
return {
status: state_machine_1.VerifyStatus.FAIL,
error: new Error('Chain ID does not match the mainchain network.'),
};
}
if (chainID.equals(context.chainID)) {
return {
status: state_machine_1.VerifyStatus.FAIL,
error: new Error('Chain ID cannot be the mainchain chain ID.'),
};
}
let totalBftWeight = BigInt(0);
for (let i = 0; i < sidechainValidators.length; i += 1) {
const currentValidator = sidechainValidators[i];
if (sidechainValidators[i + 1] &&
currentValidator.blsKey.compare(sidechainValidators[i + 1].blsKey) > -1) {
return {
status: state_machine_1.VerifyStatus.FAIL,
error: new Error('Validators blsKeys must be unique and lexicographically ordered'),
};
}
if (currentValidator.bftWeight <= BigInt(0)) {
return {
status: state_machine_1.VerifyStatus.FAIL,
error: new Error('Validator bft weight must be greater than 0'),
};
}
totalBftWeight += currentValidator.bftWeight;
}
if (totalBftWeight > constants_1.MAX_UINT64) {
return {
status: state_machine_1.VerifyStatus.FAIL,
error: new Error(`Validator bft weight must not exceed ${constants_1.MAX_UINT64}`),
};
}
if (sidechainCertificateThreshold < totalBftWeight / BigInt(3) + BigInt(1)) {
return {
status: state_machine_1.VerifyStatus.FAIL,
error: new Error('Certificate threshold below minimum bft weight '),
};
}
if (sidechainCertificateThreshold > totalBftWeight) {
return {
status: state_machine_1.VerifyStatus.FAIL,
error: new Error('Certificate threshold above maximum bft weight'),
};
}
if (transaction.fee < constants_1.CHAIN_REGISTRATION_FEE) {
return {
status: state_machine_1.VerifyStatus.FAIL,
error: new Error('Insufficient transaction fee.'),
};
}
return {
status: state_machine_1.VerifyStatus.OK,
};
}
async execute(context) {
const { getMethodContext, params: { sidechainCertificateThreshold, sidechainValidators, chainID, name }, } = context;
const methodContext = getMethodContext();
const chainSubstore = this.stores.get(chain_account_1.ChainAccountStore);
const sidechainAccount = {
name,
lastCertificate: {
height: 0,
timestamp: 0,
stateRoot: constants_1.EMPTY_HASH,
validatorsHash: (0, utils_1.computeValidatorsHash)(sidechainValidators, sidechainCertificateThreshold),
},
status: 0,
};
await chainSubstore.set(context, chainID, sidechainAccount);
const mainchainTokenID = (0, utils_1.getTokenIDLSK)(context.chainID);
const channelSubstore = this.stores.get(channel_data_1.ChannelDataStore);
await channelSubstore.set(context, chainID, {
inbox: { root: constants_1.EMPTY_HASH, appendPath: [], size: 0 },
outbox: { root: constants_1.EMPTY_HASH, appendPath: [], size: 0 },
partnerChainOutboxRoot: constants_1.EMPTY_HASH,
messageFeeTokenID: mainchainTokenID,
minReturnFeePerByte: constants_1.MIN_RETURN_FEE_PER_BYTE_BEDDOWS,
});
const chainValidatorsSubstore = this.stores.get(chain_validators_1.ChainValidatorsStore);
await chainValidatorsSubstore.set(context, chainID, {
activeValidators: sidechainValidators,
certificateThreshold: sidechainCertificateThreshold,
});
const outboxRootSubstore = this.stores.get(outbox_root_1.OutboxRootStore);
await outboxRootSubstore.set(context, chainID, { root: constants_1.EMPTY_HASH });
const registeredNamesSubstore = this.stores.get(registered_names_1.RegisteredNamesStore);
await registeredNamesSubstore.set(context, Buffer.from(name, 'ascii'), { chainID });
this._feeMethod.payFee(context.getMethodContext(), constants_1.CHAIN_REGISTRATION_FEE);
await this._tokenMethod.initializeEscrowAccount(context.getMethodContext(), chainID, mainchainTokenID);
this.events.get(chain_account_updated_1.ChainAccountUpdatedEvent).log(methodContext, chainID, sidechainAccount);
const encodedParams = lisk_codec_1.codec.encode(schemas_1.registrationCCMParamsSchema, {
name,
chainID,
messageFeeTokenID: mainchainTokenID,
minReturnFeePerByte: constants_1.MIN_RETURN_FEE_PER_BYTE_BEDDOWS,
});
const ownChainAccountSubstore = this.stores.get(own_chain_account_1.OwnChainAccountStore);
const ownChainAccount = await ownChainAccountSubstore.get(methodContext, constants_1.EMPTY_BYTES);
const ccm = {
nonce: ownChainAccount.nonce,
module: constants_1.MODULE_NAME_INTEROPERABILITY,
crossChainCommand: constants_1.CROSS_CHAIN_COMMAND_REGISTRATION,
sendingChainID: ownChainAccount.chainID,
receivingChainID: chainID,
fee: BigInt(0),
status: 0,
params: encodedParams,
};
await this.internalMethod.addToOutbox(context, chainID, ccm);
ownChainAccount.nonce += BigInt(1);
await ownChainAccountSubstore.set(context, constants_1.EMPTY_BYTES, ownChainAccount);
const { ccmID } = (0, utils_1.getEncodedCCMAndID)(ccm);
this.events
.get(ccm_send_success_1.CcmSendSuccessEvent)
.log(methodContext, ownChainAccount.chainID, chainID, ccmID, {
ccm,
});
}
}
exports.RegisterSidechainCommand = RegisterSidechainCommand;
//# sourceMappingURL=register_sidechain.js.map