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

Commit

Permalink
Update verifyCertificate and verifyCertificateSignatre methods (#7767)
Browse files Browse the repository at this point in the history
### What was the problem?

This PR resolves #7687 and resolves #7688

### How was it solved?

- Move verifyCertificateSignature to internalMethod and update per LIP
- Add verifyCertificate to internalMethod per LIP

### How was it tested?

- Add unit tests to cover all the cases

Co-authored-by: !shan <[email protected]>
  • Loading branch information
shuse2 and ishantiw authored Nov 15, 2022
1 parent bd914a7 commit 5dffd21
Show file tree
Hide file tree
Showing 8 changed files with 252 additions and 148 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
*/

import { codec } from '@liskhq/lisk-codec';
import { utils } from '@liskhq/lisk-cryptography';
import { bls, utils } from '@liskhq/lisk-cryptography';
import { regularMerkleTree } from '@liskhq/lisk-tree';
import { objects } from '@liskhq/lisk-utils';
import {
Expand All @@ -23,6 +23,7 @@ import {
CROSS_CHAIN_COMMAND_NAME_CHANNEL_TERMINATED,
MODULE_NAME_INTEROPERABILITY,
CCMStatusCode,
MESSAGE_TAG_CERTIFICATE,
} from './constants';
import { ccmSchema } from './schemas';
import {
Expand Down Expand Up @@ -323,6 +324,57 @@ export abstract class BaseInteroperabilityInternalMethod extends BaseInternalMet
}
}

public async verifyCertificate(
context: ImmutableMethodContext,
params: CrossChainUpdateTransactionParams,
blockTimestamp: number,
): Promise<void> {
const certificate = codec.decode<Certificate>(certificateSchema, params.certificate);
const partnerchainAccount = await this.stores
.get(ChainAccountStore)
.get(context, params.sendingChainID);

if (certificate.height <= partnerchainAccount.lastCertificate.height) {
throw new Error('Certificate height is not greater than last certificate height.');
}
if (certificate.timestamp >= blockTimestamp) {
throw new Error(
'Certificate timestamp is not smaller than timestamp of the block including the CCU.',
);
}
}

public async verifyCertificateSignature(
context: ImmutableMethodContext,
params: CrossChainUpdateTransactionParams,
): Promise<void> {
const certificate = codec.decode<Certificate>(certificateSchema, params.certificate);
const chainValidators = await this.stores
.get(ChainValidatorsStore)
.get(context, params.sendingChainID);
const blsKeys = [];
const blsWeights = [];
for (const validator of chainValidators.activeValidators) {
blsKeys.push(validator.blsKey);
blsWeights.push(validator.bftWeight);
}

const verifySignature = bls.verifyWeightedAggSig(
blsKeys,
certificate.aggregationBits as Buffer,
certificate.signature as Buffer,
MESSAGE_TAG_CERTIFICATE,
params.sendingChainID,
params.certificate,
blsWeights,
params.newCertificateThreshold,
);

if (!verifySignature) {
throw new Error('Certificate is not a valid aggregate signature.');
}
}

// Different in mainchain and sidechain so to be implemented in each module store separately
public abstract isLive(chainID: Buffer, timestamp?: number): Promise<boolean>;
public abstract sendInternal(sendContext: SendInternalContext): Promise<boolean>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ import {
commonCCUExecutelogic,
isInboxUpdateEmpty,
validateFormat,
verifyCertificateSignature,
} from '../../utils';
import { MainchainInteroperabilityInternalMethod } from '../store';

Expand Down Expand Up @@ -145,14 +144,10 @@ export class MainchainCCUpdateCommand extends BaseCrossChainUpdateCommand {
}

// When certificate is non-empty
const verifyCertificateSignatureResult = verifyCertificateSignature(
await interoperabilityInternalMethod.verifyCertificateSignature(
context.getMethodContext(),
txParams,
partnerValidators,
txParams.sendingChainID,
);
if (verifyCertificateSignatureResult.error) {
return verifyCertificateSignatureResult;
}

const partnerChannelStore = this.stores.get(ChannelDataStore);
const partnerChannelData = await partnerChannelStore.get(context, txParams.sendingChainID);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ import {
commonCCUExecutelogic,
isInboxUpdateEmpty,
validateFormat,
verifyCertificateSignature,
} from '../../utils';
import { SidechainInteroperabilityInternalMethod } from '../store';

Expand Down Expand Up @@ -119,14 +118,10 @@ export class SidechainCCUpdateCommand extends BaseCrossChainUpdateCommand {
}

// When certificate is non-empty
const verifyCertificateSignatureResult = verifyCertificateSignature(
await interoperabilityInternalMethod.verifyCertificateSignature(
context.getMethodContext(),
txParams,
partnerValidators,
partnerChainIDBuffer,
);
if (verifyCertificateSignatureResult.error) {
return verifyCertificateSignatureResult;
}

const partnerChannelStore = this.stores.get(ChannelDataStore);
const partnerChannelData = await partnerChannelStore.get(context, partnerChainIDBuffer);
Expand Down
49 changes: 1 addition & 48 deletions framework/src/modules/interoperability/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import { regularMerkleTree, sparseMerkleTree } from '@liskhq/lisk-tree';
import { codec } from '@liskhq/lisk-codec';
import { utils, bls } from '@liskhq/lisk-cryptography';
import { utils } from '@liskhq/lisk-cryptography';
import { validator } from '@liskhq/lisk-validator';
import { dataStructures } from '@liskhq/lisk-utils';
import { NAME_REGEX } from '@liskhq/lisk-chain';
Expand All @@ -36,7 +36,6 @@ import {
MAX_CCM_SIZE,
MAX_NUM_VALIDATORS,
MAX_UINT64,
MESSAGE_TAG_CERTIFICATE,
MODULE_NAME_INTEROPERABILITY,
SMT_KEY_LENGTH,
HASH_LENGTH,
Expand Down Expand Up @@ -381,52 +380,6 @@ export const checkValidCertificateLiveness = (
}
};

export const verifyCertificateSignature = (
txParams: CrossChainUpdateTransactionParams,
partnerValidators: ChainValidators,
partnerChainId: Buffer,
): VerificationResult => {
// Only check when ceritificate is non-empty
if (txParams.certificate.equals(EMPTY_BYTES)) {
return {
status: VerifyStatus.OK,
};
}

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

if (isCertificateEmpty(decodedCertificate)) {
return {
status: VerifyStatus.FAIL,
error: new Error(
'Certificate should have all required values when activeValidatorsUpdate or newCertificateThreshold has a non-empty value.',
),
};
}
const { activeValidators, certificateThreshold } = partnerValidators;
const verifySignature = bls.verifyWeightedAggSig(
activeValidators.map(v => v.blsKey),
decodedCertificate.aggregationBits as Buffer,
decodedCertificate.signature as Buffer,
MESSAGE_TAG_CERTIFICATE,
partnerChainId,
txParams.certificate,
activeValidators.map(v => v.bftWeight),
certificateThreshold,
);

if (!verifySignature) {
return {
status: VerifyStatus.FAIL,
error: new Error('Certificate is invalid due to invalid signature.'),
};
}

return {
status: VerifyStatus.OK,
};
};

export const checkCertificateTimestamp = (
txParams: CrossChainUpdateTransactionParams,
certificate: Certificate,
Expand Down
Loading

0 comments on commit 5dffd21

Please sign in to comment.