lisk-framework
Version:
Lisk blockchain application platform
176 lines • 8.75 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.RegisterMainchainCommand = void 0;
const lisk_codec_1 = require("@liskhq/lisk-codec");
const lisk_cryptography_1 = require("@liskhq/lisk-cryptography");
const constants_1 = require("../../constants");
const schemas_1 = require("../../schemas");
const state_machine_1 = require("../../../../state_machine");
const utils_1 = require("../../utils");
const base_interoperability_command_1 = require("../../base_interoperability_command");
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 own_chain_account_1 = require("../../stores/own_chain_account");
const chain_account_updated_1 = require("../../events/chain_account_updated");
const invalid_registration_signature_1 = require("../../events/invalid_registration_signature");
const ccm_send_success_1 = require("../../events/ccm_send_success");
const errors_1 = require("../../errors");
class RegisterMainchainCommand extends base_interoperability_command_1.BaseInteroperabilityCommand {
constructor() {
super(...arguments);
this.schema = schemas_1.mainchainRegParams;
}
addDependencies(validatorsMethod) {
this._validatorsMethod = validatorsMethod;
}
async verify(context) {
const { ownName, mainchainValidators, mainchainCertificateThreshold, ownChainID } = context.params;
const mainchainID = (0, utils_1.getMainchainID)(context.chainID);
const chainAccountSubstore = this.stores.get(chain_account_1.ChainAccountStore);
const mainchainAccountExists = await chainAccountSubstore.has(context, mainchainID);
if (mainchainAccountExists) {
return {
status: state_machine_1.VerifyStatus.FAIL,
error: new Error('Mainchain has already been registered.'),
};
}
if (!ownChainID.equals(context.chainID)) {
return {
status: state_machine_1.VerifyStatus.FAIL,
error: new Error(`Invalid ownChainID property.`),
};
}
if (!(0, utils_1.isValidName)(ownName)) {
return {
status: state_machine_1.VerifyStatus.FAIL,
error: new errors_1.InvalidNameError('ownName'),
};
}
let totalWeight = BigInt(0);
for (let i = 0; i < mainchainValidators.length; i += 1) {
const currentValidator = mainchainValidators[i];
if (mainchainValidators[i + 1] &&
currentValidator.blsKey.compare(mainchainValidators[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 positive integer.'),
};
}
totalWeight += currentValidator.bftWeight;
if (totalWeight > constants_1.MAX_UINT64) {
return {
status: state_machine_1.VerifyStatus.FAIL,
error: new Error('Total BFT weight exceeds maximum value.'),
};
}
}
if (mainchainCertificateThreshold < totalWeight / BigInt(3) + BigInt(1)) {
return {
status: state_machine_1.VerifyStatus.FAIL,
error: new Error('Certificate threshold is too small.'),
};
}
if (mainchainCertificateThreshold > totalWeight) {
return {
status: state_machine_1.VerifyStatus.FAIL,
error: new Error('Certificate threshold is too large.'),
};
}
return {
status: state_machine_1.VerifyStatus.OK,
};
}
async execute(context) {
const { getMethodContext, params: { ownChainID, ownName, mainchainValidators, mainchainCertificateThreshold, aggregationBits, signature, }, } = context;
const methodContext = getMethodContext();
const { validators, certificateThreshold } = await this._validatorsMethod.getValidatorsParams(getMethodContext());
const activeValidators = validators.filter(v => v.bftWeight > BigInt(0));
const keyList = [];
const weights = [];
(0, utils_1.sortValidatorsByBLSKey)(activeValidators);
for (const v of activeValidators) {
keyList.push(v.blsKey);
weights.push(v.bftWeight);
}
const message = lisk_codec_1.codec.encode(schemas_1.registrationSignatureMessageSchema, {
ownName,
ownChainID,
mainchainValidators,
mainchainCertificateThreshold,
});
if (!lisk_cryptography_1.bls.verifyWeightedAggSig(keyList, aggregationBits, signature, constants_1.MESSAGE_TAG_CHAIN_REG, ownChainID, message, weights, certificateThreshold)) {
this.events.get(invalid_registration_signature_1.InvalidRegistrationSignatureEvent).error(context, ownChainID);
throw new Error('Invalid signature property.');
}
const mainchainTokenID = (0, utils_1.getTokenIDLSK)(context.chainID);
const chainSubstore = this.stores.get(chain_account_1.ChainAccountStore);
const mainchainAccount = {
name: constants_1.CHAIN_NAME_MAINCHAIN,
lastCertificate: {
height: 0,
timestamp: 0,
stateRoot: constants_1.EMPTY_HASH,
validatorsHash: (0, utils_1.computeValidatorsHash)(mainchainValidators, mainchainCertificateThreshold),
},
status: 0,
};
const mainchainID = (0, utils_1.getMainchainID)(context.chainID);
await chainSubstore.set(context, mainchainID, mainchainAccount);
const channelSubstore = this.stores.get(channel_data_1.ChannelDataStore);
await channelSubstore.set(context, mainchainID, {
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, mainchainID, {
activeValidators: mainchainValidators,
certificateThreshold: mainchainCertificateThreshold,
});
const outboxRootSubstore = this.stores.get(outbox_root_1.OutboxRootStore);
await outboxRootSubstore.set(context, mainchainID, { root: constants_1.EMPTY_HASH });
this.events.get(chain_account_updated_1.ChainAccountUpdatedEvent).log(methodContext, mainchainID, mainchainAccount);
const encodedParams = lisk_codec_1.codec.encode(schemas_1.registrationCCMParamsSchema, {
name: constants_1.CHAIN_NAME_MAINCHAIN,
chainID: mainchainID,
messageFeeTokenID: mainchainTokenID,
minReturnFeePerByte: constants_1.MIN_RETURN_FEE_PER_BYTE_BEDDOWS,
});
const ownChainAccount = {
name: ownName,
chainID: ownChainID,
nonce: BigInt(0),
};
const ccm = {
nonce: ownChainAccount.nonce,
module: constants_1.MODULE_NAME_INTEROPERABILITY,
crossChainCommand: constants_1.CROSS_CHAIN_COMMAND_REGISTRATION,
sendingChainID: ownChainAccount.chainID,
receivingChainID: mainchainID,
fee: BigInt(0),
status: 0,
params: encodedParams,
};
await this.internalMethod.addToOutbox(context, mainchainID, ccm);
ownChainAccount.nonce += BigInt(1);
await this.stores.get(own_chain_account_1.OwnChainAccountStore).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, mainchainID, ccmID, {
ccm,
});
}
}
exports.RegisterMainchainCommand = RegisterMainchainCommand;
//# sourceMappingURL=register_mainchain.js.map