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

Commit

Permalink
🌱 Update sidechain ccu command
Browse files Browse the repository at this point in the history
  • Loading branch information
shuse2 committed Nov 16, 2022
1 parent 9832315 commit 584c946
Show file tree
Hide file tree
Showing 6 changed files with 968 additions and 1,095 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import { codec } from '@liskhq/lisk-codec';
import { utils } from '@liskhq/lisk-cryptography';
import { CommandExecuteContext, CommandVerifyContext } from '../../state_machine';
import { ImmutableStoreGetter, StoreGetter } from '../base_store';
import { BaseInteroperabilityCommand } from './base_interoperability_command';
import { BaseInteroperabilityInternalMethod } from './base_interoperability_internal_methods';
Expand All @@ -22,7 +23,16 @@ import { CCMStatusCode, MIN_RETURN_FEE } from './constants';
import { CCMProcessedCode, CcmProcessedEvent, CCMProcessedResult } from './events/ccm_processed';
import { CcmSendSuccessEvent } from './events/ccm_send_success';
import { ccmSchema, crossChainUpdateTransactionParams } from './schemas';
import { CrossChainMessageContext, TokenMethod } from './types';
import { ChainAccountStore, ChainStatus } from './stores/chain_account';
import { ChainValidatorsStore } from './stores/chain_validators';
import { ChannelDataStore } from './stores/channel_data';
import {
CCMsg,
CrossChainMessageContext,
CrossChainUpdateTransactionParams,
TokenMethod,
} from './types';
import { isInboxUpdateEmpty, validateFormat } from './utils';

export abstract class BaseCrossChainUpdateCommand extends BaseInteroperabilityCommand {
public schema = crossChainUpdateTransactionParams;
Expand All @@ -35,6 +45,111 @@ export abstract class BaseCrossChainUpdateCommand extends BaseInteroperabilityCo
this._interopsMethod = interopsMethod;
}

protected async verifyCommon(context: CommandVerifyContext<CrossChainUpdateTransactionParams>) {
const { params } = context;
const internalMethod = this.getInteroperabilityInternalMethod(context);
const sendingChainAccount = await this.stores
.get(ChainAccountStore)
.get(context, params.sendingChainID);
if (sendingChainAccount.status === ChainStatus.REGISTERED && params.certificate.length === 0) {
throw new Error('The first CCU must contain a non-empty certificate.');
}
if (params.certificate.length > 0) {
await internalMethod.verifyCertificate(context, params, context.header.timestamp);
}
const sendingChainValidators = await this.stores
.get(ChainValidatorsStore)
.get(context, params.sendingChainID);
if (
params.activeValidatorsUpdate.length > 0 ||
params.certificateThreshold !== sendingChainValidators.certificateThreshold
) {
await internalMethod.verifyValidatorsUpdate(context, params);
}

if (!isInboxUpdateEmpty(params.inboxUpdate)) {
await internalMethod.verifyPartnerChainOutboxRoot(context, params);
}
}

protected async executeCommon(
context: CommandExecuteContext<CrossChainUpdateTransactionParams>,
isMainchain: boolean,
): Promise<[CCMsg[], boolean]> {
const { params, transaction } = context;
const internalMethod = this.getInteroperabilityInternalMethod(context);

await internalMethod.verifyCertificateSignature(context, params);

if (!isInboxUpdateEmpty(params.inboxUpdate)) {
// Initialize the relayer account for the message fee token.
// This is necessary to ensure that the relayer can receive the CCM fees
// If the account already exists, nothing is done.
const messageFeeTokenID = await this._interopsMethod.getMessageFeeTokenID(
context,
params.sendingChainID,
);
// FIXME: When updating fee logic, the fix value should be removed.
await this._tokenMethod.initializeUserAccount(
context,
transaction.senderAddress,
messageFeeTokenID,
transaction.senderAddress,
BigInt(500000000),
);
}

const decodedCCMs = [];
for (const ccmBytes of params.inboxUpdate.crossChainMessages) {
try {
const ccm = codec.decode<CCMsg>(ccmSchema, ccmBytes);
validateFormat(ccm);
decodedCCMs.push(ccm);
if (!ccm.sendingChainID.equals(params.sendingChainID)) {
throw new Error('CCM is not from the sending chain.');
}
if (ccm.sendingChainID.equals(ccm.receivingChainID)) {
throw new Error('Sending and receiving chains must differ.');
}
if (isMainchain && ccm.status === CCMStatusCode.CHANNEL_UNAVAILABLE) {
throw new Error('CCM status channel unavailable can only be set on the mainchain.');
}
} catch (error) {
await internalMethod.terminateChainInternal(params.sendingChainID, context);
const ccmID = utils.hash(ccmBytes);
this.events.get(CcmProcessedEvent).log(context, params.sendingChainID, context.chainID, {
ccmID,
code: CCMProcessedCode.INVALID_CCM_VALIDATION_EXCEPTION,
result: CCMProcessedResult.DISCARDED,
});
return [[], false];
}
}

const sendingChainValidators = await this.stores
.get(ChainValidatorsStore)
.get(context, params.sendingChainID);
if (
params.activeValidatorsUpdate.length > 0 ||
params.certificateThreshold !== sendingChainValidators.certificateThreshold
) {
await internalMethod.updateValidators(context, params);
}
if (params.certificate.length > 0) {
await internalMethod.updateCertificate(context, params);
}
if (!isInboxUpdateEmpty(params.inboxUpdate)) {
await this.stores
.get(ChannelDataStore)
.updatePartnerChainOutboxRoot(
context,
params.sendingChainID,
params.inboxUpdate.messageWitnessHashes,
);
}
return [decodedCCMs, true];
}

protected async apply(context: CrossChainMessageContext): Promise<void> {
const { ccm, logger } = context;
const encodedCCM = codec.encode(ccmSchema, ccm);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,8 @@ import {
sidechainTerminatedCCMParamsSchema,
} from '../../schemas';
import { ChainAccount, ChainAccountStore, ChainStatus } from '../../stores/chain_account';
import { ChainValidatorsStore } from '../../stores/chain_validators';
import { ChannelDataStore } from '../../stores/channel_data';
import { CCMsg, CrossChainMessageContext, CrossChainUpdateTransactionParams } from '../../types';
import { getMainchainID, isInboxUpdateEmpty, validateFormat } from '../../utils';
import { CrossChainMessageContext, CrossChainUpdateTransactionParams } from '../../types';
import { getMainchainID } from '../../utils';
import { MainchainInteroperabilityInternalMethod } from '../store';

export class MainchainCCUpdateCommand extends BaseCrossChainUpdateCommand {
Expand All @@ -64,28 +62,7 @@ export class MainchainCCUpdateCommand extends BaseCrossChainUpdateCommand {
throw new Error('The sending chain is not live.');
}

const sendingChainAccount = await this.stores
.get(ChainAccountStore)
.get(context, params.sendingChainID);
if (sendingChainAccount.status === ChainStatus.REGISTERED && params.certificate.length === 0) {
throw new Error('The first CCU must contain a non-empty certificate.');
}
if (params.certificate.length > 0) {
await internalMethod.verifyCertificate(context, params, context.header.timestamp);
}
const sendingChainValidators = await this.stores
.get(ChainValidatorsStore)
.get(context, params.sendingChainID);
if (
params.activeValidatorsUpdate.length > 0 ||
params.certificateThreshold !== sendingChainValidators.certificateThreshold
) {
await internalMethod.verifyValidatorsUpdate(context, params);
}

if (!isInboxUpdateEmpty(params.inboxUpdate)) {
await internalMethod.verifyPartnerChainOutboxRoot(context, params);
}
await this.verifyCommon(context);

return {
status: VerifyStatus.OK,
Expand All @@ -95,77 +72,12 @@ export class MainchainCCUpdateCommand extends BaseCrossChainUpdateCommand {
public async execute(
context: CommandExecuteContext<CrossChainUpdateTransactionParams>,
): Promise<void> {
const { params, transaction } = context;
const internalMethod = this.getInteroperabilityInternalMethod(context);

await internalMethod.verifyCertificateSignature(context, params);

if (!isInboxUpdateEmpty(params.inboxUpdate)) {
// Initialize the relayer account for the message fee token.
// This is necessary to ensure that the relayer can receive the CCM fees
// If the account already exists, nothing is done.
const messageFeeTokenID = await this._interopsMethod.getMessageFeeTokenID(
context,
params.sendingChainID,
);
// FIXME: When updating fee logic, the fix value should be removed.
await this._tokenMethod.initializeUserAccount(
context,
transaction.senderAddress,
messageFeeTokenID,
transaction.senderAddress,
BigInt(500000000),
);
}

const decodedCCMs = [];
for (const ccmBytes of params.inboxUpdate.crossChainMessages) {
try {
const ccm = codec.decode<CCMsg>(ccmSchema, ccmBytes);
validateFormat(ccm);
decodedCCMs.push(ccm);
if (!ccm.sendingChainID.equals(params.sendingChainID)) {
throw new Error('CCM is not from the sending chain.');
}
if (ccm.sendingChainID.equals(ccm.receivingChainID)) {
throw new Error('Sending and receiving chains must differ.');
}
if (ccm.status === CCMStatusCode.CHANNEL_UNAVAILABLE) {
throw new Error('CCM status channel unavailable can only be set on the mainchain.');
}
} catch (error) {
await internalMethod.terminateChainInternal(params.sendingChainID, context);
const ccmID = utils.hash(ccmBytes);
this.events.get(CcmProcessedEvent).log(context, params.sendingChainID, context.chainID, {
ccmID,
code: CCMProcessedCode.INVALID_CCM_VALIDATION_EXCEPTION,
result: CCMProcessedResult.DISCARDED,
});
return;
}
}

const sendingChainValidators = await this.stores
.get(ChainValidatorsStore)
.get(context, params.sendingChainID);
if (
params.activeValidatorsUpdate.length > 0 ||
params.certificateThreshold !== sendingChainValidators.certificateThreshold
) {
await internalMethod.updateValidators(context, params);
}
if (params.certificate.length > 0) {
await internalMethod.updateCertificate(context, params);
}
if (!isInboxUpdateEmpty(params.inboxUpdate)) {
await this.stores
.get(ChannelDataStore)
.updatePartnerChainOutboxRoot(
context,
params.sendingChainID,
params.inboxUpdate.messageWitnessHashes,
);
const [decodedCCMs, ok] = await this.executeCommon(context, true);
if (!ok) {
return;
}
const { params } = context;
const internalMethod = this.getInteroperabilityInternalMethod(context);

for (let i = 0; i < decodedCCMs.length; i += 1) {
const ccm = decodedCCMs[i];
Expand Down
Loading

0 comments on commit 584c946

Please sign in to comment.