Skip to content
This repository has been archived by the owner on Jun 11, 2024. It is now read-only.

Commit

Permalink
Merge pull request #7165 from LiskHQ/7038-7039-impl-ccu-mainchain
Browse files Browse the repository at this point in the history
Implement cross chain command for interoperability modules - Closes #7038 & #7039
  • Loading branch information
ishantiw authored May 27, 2022
2 parents b27244a + 4cb5d73 commit 3915d86
Show file tree
Hide file tree
Showing 18 changed files with 3,330 additions and 30 deletions.
3 changes: 3 additions & 0 deletions elements/lisk-tree/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import { verifyProof, verifyDataBlock } from './merkle_tree/verify_proof';
import { calculateRootFromUpdateData } from './merkle_tree/calculate';
import { calculateRootFromRightWitness, verifyRightWitness } from './merkle_tree/right_witness';
import { MerkleTree } from './merkle_tree/merkle_tree';
import { calculateMerkleRoot, calculateMerkleRootWithLeaves } from './merkle_tree/utils';
import { SparseMerkleTree } from './sparse_merkle_tree/sparse_merkle_tree';
Expand All @@ -26,6 +27,8 @@ export const regularMerkleTree = {
calculateMerkleRoot,
calculateMerkleRootWithLeaves,
MerkleTree,
calculateRootFromRightWitness,
verifyRightWitness,
};

export const sparseMerkleTree = {
Expand Down
4 changes: 3 additions & 1 deletion framework/src/modules/interoperability/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export const MAINCHAIN_NETWORK_ID = Buffer.from(
'03693f3126b9d0df3096c4ebd59e5c42af4a7f0e313cd7c96a07b6e9f8f54924',
'hex',
); // TBD
export const VALID_BLS_KEY_LENGTH = 48;
export const SMT_KEY_LENGTH = 38;
export const NUMBER_MAINCHAIN_VALIDATORS = 101;
export const TAG_CHAIN_REG_MESSAGE = 'LSK_CHAIN_REGISTRATION';
export const LIVENESS_LIMIT = 2592000; // 30*24*3600
Expand All @@ -37,7 +39,7 @@ export const MAX_LENGTH_NAME = 40;
export const MAX_UINT32 = 4294967295;
export const MAX_UINT64 = BigInt('18446744073709551615'); // BigInt((2 ** 64) - 1) - 1
export const THRESHOLD_MAINCHAIN = 68;
export const MESSAGE_TAG_CERTIFICATE = Buffer.from('LSK_CE_', 'utf-8');
export const MESSAGE_TAG_CERTIFICATE = 'LSK_CE_';

// Store prefixes
export const STORE_PREFIX_OUTBOX_ROOT = 0x0000;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ export class MainchainCCChannelTerminatedCommand extends BaseInteroperabilityCCC

public async execute(context: CCCommandExecuteContext): Promise<void> {
const interoperabilityStore = this.getInteroperabilityStore(context.getStore);
if (!context.ccm) {
throw new Error('CCM to execute channel terminated cross chain command is missing.');
}
await interoperabilityStore.createTerminatedStateAccount(context.ccm.sendingChainID);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ export class MainchainCCRegistrationCommand extends BaseInteroperabilityCCComman

public async execute(ctx: CCCommandExecuteContext): Promise<void> {
const { ccm } = ctx;
if (!ccm) {
throw new Error('CCM to execute registration cross chain command is missing.');
}
const decodedParams = codec.decode<CCMRegistrationParams>(
registrationCCMParamsSchema,
ccm.params,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ export class MainchainCCSidechainTerminatedCommand extends BaseInteroperabilityC

public async execute(context: CCCommandExecuteContext): Promise<void> {
const { ccm } = context;
if (!ccm) {
throw new Error('CCM to execute sidechain terminated cross chain command is missing.');
}
const decodedParams = codec.decode<CCMSidechainTerminatedParams>(
sidechainTerminatedCCMParamsSchema,
ccm.params,
Expand Down
291 changes: 283 additions & 8 deletions framework/src/modules/interoperability/mainchain/commands/cc_update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,293 @@
* Removal or modification of this copyright notice is prohibited.
*/

import { CommandExecuteContext } from '../../../../node/state_machine';
import { BaseCommand } from '../../../base_command';
import { COMMAND_ID_MAINCHAIN_CCU } from '../../constants';
import { crossChainUpdateTransactionParams } from '../../schema';
import { codec } from '@liskhq/lisk-codec';
import { LiskValidationError, validator } from '@liskhq/lisk-validator';
import { certificateSchema } from '../../../../node/consensus/certificate_generation/schema';
import { Certificate } from '../../../../node/consensus/certificate_generation/types';
import {
CommandExecuteContext,
CommandVerifyContext,
VerificationResult,
VerifyStatus,
} from '../../../../node/state_machine';
import { BaseInteroperabilityCommand } from '../../base_interoperability_command';
import {
CHAIN_ACTIVE,
CHAIN_REGISTERED,
CHAIN_TERMINATED,
COMMAND_ID_MAINCHAIN_CCU,
CROSS_CHAIN_COMMAND_ID_REGISTRATION,
MAINCHAIN_ID,
STORE_PREFIX_CHAIN_DATA,
STORE_PREFIX_CHAIN_VALIDATORS,
STORE_PREFIX_CHANNEL_DATA,
} from '../../constants';
import { createCCMsgBeforeSendContext } from '../../context';
import {
ccmSchema,
chainAccountSchema,
chainValidatorsSchema,
channelSchema,
crossChainUpdateTransactionParams,
} from '../../schema';
import {
CCMsg,
ChainAccount,
ChainValidators,
ChannelData,
CrossChainUpdateTransactionParams,
StoreCallback,
} from '../../types';
import {
checkActiveValidatorsUpdate,
checkCertificateTimestamp,
checkCertificateValidity,
checkInboxUpdateValidity,
checkLivenessRequirementFirstCCU,
checkValidatorsHashWithCertificate,
checkValidCertificateLiveness,
commonCCUExecutelogic,
getIDAsKeyForStore,
isInboxUpdateEmpty,
validateFormat,
verifyCertificateSignature,
} from '../../utils';
import { MainchainInteroperabilityStore } from '../store';

export class MainchainCCUpdateCommand extends BaseCommand {
export class MainchainCCUpdateCommand extends BaseInteroperabilityCommand {
public name = 'mainchainCCUpdate';
public id = COMMAND_ID_MAINCHAIN_CCU;
public schema = crossChainUpdateTransactionParams;

// eslint-disable-next-line @typescript-eslint/require-await
public async execute(_context: CommandExecuteContext<unknown>): Promise<void> {
throw new Error('Method not implemented.');
public async verify(
context: CommandVerifyContext<CrossChainUpdateTransactionParams>,
): Promise<VerificationResult> {
const { params: txParams, transaction, getStore } = context;
const errors = validator.validate(crossChainUpdateTransactionParams, context.params);

if (errors.length > 0) {
return {
status: VerifyStatus.FAIL,
error: new LiskValidationError(errors),
};
}

const partnerChainIDBuffer = getIDAsKeyForStore(txParams.sendingChainID);
const partnerChainStore = getStore(transaction.moduleID, STORE_PREFIX_CHAIN_DATA);
const partnerChainAccount = await partnerChainStore.getWithSchema<ChainAccount>(
partnerChainIDBuffer,
chainAccountSchema,
);

// Section: Liveness of Partner Chain
if (partnerChainAccount.status === CHAIN_TERMINATED) {
return {
status: VerifyStatus.FAIL,
error: new Error(`Sending partner chain ${txParams.sendingChainID} is terminated.`),
};
}
const interoperabilityStore = this.getInteroperabilityStore(getStore);
if (partnerChainAccount.status === CHAIN_ACTIVE) {
const isChainLive = await interoperabilityStore.isLive(partnerChainIDBuffer, Date.now());
if (!isChainLive) {
return {
status: VerifyStatus.FAIL,
error: new Error(`Sending partner chain ${txParams.sendingChainID} is not live.`),
};
}
}

// Section: Liveness Requirement for the First CCU
const livenessRequirementFirstCCU = checkLivenessRequirementFirstCCU(
partnerChainAccount,
txParams,
);
if (livenessRequirementFirstCCU.error) {
return livenessRequirementFirstCCU;
}
// Section: Certificate and Validators Update Validity
const certificateValidity = checkCertificateValidity(partnerChainAccount, txParams.certificate);

if (certificateValidity.error) {
return certificateValidity;
}

const partnerValidatorStore = context.getStore(this.moduleID, STORE_PREFIX_CHAIN_VALIDATORS);
const partnerValidators = await partnerValidatorStore.getWithSchema<ChainValidators>(
partnerChainIDBuffer,
chainValidatorsSchema,
);
// If params contains a non-empty activeValidatorsUpdate and non-empty certificate
const validatorsHashValidity = checkValidatorsHashWithCertificate(txParams, partnerValidators);
if (validatorsHashValidity.error) {
return validatorsHashValidity;
}

// If params contains a non-empty activeValidatorsUpdate
const activeValidatorsValidity = checkActiveValidatorsUpdate(txParams);
if (activeValidatorsValidity.error) {
return activeValidatorsValidity;
}

// When certificate is non-empty
const verifyCertificateSignatureResult = verifyCertificateSignature(
txParams,
partnerValidators,
partnerChainAccount,
);
if (verifyCertificateSignatureResult.error) {
return verifyCertificateSignatureResult;
}

const partnerChannelStore = context.getStore(transaction.moduleID, STORE_PREFIX_CHANNEL_DATA);
const partnerChannelData = await partnerChannelStore.getWithSchema<ChannelData>(
partnerChainIDBuffer,
channelSchema,
);
// Section: InboxUpdate Validity
const inboxUpdateValidity = checkInboxUpdateValidity(txParams, partnerChannelData);
if (inboxUpdateValidity.error) {
return inboxUpdateValidity;
}

return {
status: VerifyStatus.OK,
};
}

public async execute(
context: CommandExecuteContext<CrossChainUpdateTransactionParams>,
): Promise<void> {
const { header, params: txParams } = context;
const chainIDBuffer = getIDAsKeyForStore(txParams.sendingChainID);
const partnerChainStore = context.getStore(this.moduleID, STORE_PREFIX_CHAIN_DATA);
const partnerChainAccount = await partnerChainStore.getWithSchema<ChainAccount>(
chainIDBuffer,
chainAccountSchema,
);

const decodedCertificate = codec.decode<Certificate>(certificateSchema, txParams.certificate);

// if the CCU also contains a non-empty inboxUpdate, check the validity of certificate with liveness check
checkValidCertificateLiveness(txParams, header, decodedCertificate);

const partnerValidatorStore = context.getStore(this.moduleID, STORE_PREFIX_CHAIN_VALIDATORS);
const partnerValidators = await partnerValidatorStore.getWithSchema<ChainValidators>(
chainIDBuffer,
chainValidatorsSchema,
);

// Certificate timestamp Validity
checkCertificateTimestamp(txParams, decodedCertificate, header);

// CCM execution
const beforeSendContext = createCCMsgBeforeSendContext({
feeAddress: context.transaction.senderAddress,
eventQueue: context.eventQueue,
getAPIContext: context.getAPIContext,
logger: context.logger,
networkIdentifier: context.networkIdentifier,
getStore: context.getStore,
});
const interoperabilityStore = this.getInteroperabilityStore(context.getStore);
let decodedCCMs;
try {
decodedCCMs = txParams.inboxUpdate.crossChainMessages.map(ccm => ({
serialized: ccm,
deserialized: codec.decode<CCMsg>(ccmSchema, ccm),
}));
} catch (err) {
await interoperabilityStore.terminateChainInternal(
txParams.sendingChainID,
beforeSendContext,
);

throw err;
}
if (
partnerChainAccount.status === CHAIN_REGISTERED &&
!isInboxUpdateEmpty(txParams.inboxUpdate)
) {
// If the first CCM in inboxUpdate is a registration CCM
if (
decodedCCMs[0].deserialized.crossChainCommandID === CROSS_CHAIN_COMMAND_ID_REGISTRATION &&
decodedCCMs[0].deserialized.receivingChainID === MAINCHAIN_ID
) {
partnerChainAccount.status = CHAIN_ACTIVE;
} else {
await interoperabilityStore.terminateChainInternal(
txParams.sendingChainID,
beforeSendContext,
);

return; // Exit CCU processing
}
}

for (const ccm of decodedCCMs) {
if (txParams.sendingChainID !== ccm.deserialized.sendingChainID) {
await interoperabilityStore.terminateChainInternal(
txParams.sendingChainID,
beforeSendContext,
);

continue;
}
try {
validateFormat(ccm.deserialized);
} catch (error) {
await interoperabilityStore.terminateChainInternal(
txParams.sendingChainID,
beforeSendContext,
);

continue;
}
await interoperabilityStore.appendToInboxTree(
getIDAsKeyForStore(txParams.sendingChainID),
ccm.serialized,
);
if (ccm.deserialized.receivingChainID !== MAINCHAIN_ID) {
await interoperabilityStore.forward({
ccm: ccm.deserialized,
ccu: txParams,
eventQueue: context.eventQueue,
feeAddress: context.transaction.senderAddress,
getAPIContext: context.getAPIContext,
getStore: context.getStore,
logger: context.logger,
networkIdentifier: context.networkIdentifier,
});
} else {
await interoperabilityStore.apply(
{
ccm: ccm.deserialized,
ccu: txParams,
eventQueue: context.eventQueue,
feeAddress: context.transaction.senderAddress,
getAPIContext: context.getAPIContext,
getStore: context.getStore,
logger: context.logger,
networkIdentifier: context.networkIdentifier,
},
this.ccCommands,
);
}
}
// Common ccu execution logic
await commonCCUExecutelogic({
certificate: decodedCertificate,
chainIDBuffer,
context,
partnerChainAccount,
partnerChainStore,
partnerValidatorStore,
partnerValidators,
});
}

protected getInteroperabilityStore(getStore: StoreCallback): MainchainInteroperabilityStore {
return new MainchainInteroperabilityStore(this.moduleID, getStore, this.interoperableCCAPIs);
}
}
Loading

0 comments on commit 3915d86

Please sign in to comment.