Skip to content

Commit

Permalink
Increase coverage. Update more comments
Browse files Browse the repository at this point in the history
  • Loading branch information
ltyu committed Mar 12, 2024
1 parent 9929125 commit ff1ef05
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 40 deletions.
12 changes: 10 additions & 2 deletions typescript/ccip-server/src/services/HyperlaneService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ enum API_ACTION {
GetMessages = 'get-messages',
}

enum API_MODULE {
Message = 'message',
}

class HyperlaneService {
constructor(readonly baseUrl: string) {}

Expand All @@ -22,12 +26,16 @@ class HyperlaneService {
async getOriginBlockByMessageId(id: string): Promise<MessageTx> {
info(`Fetching block for id: ${id}`);
const response = await fetch(
`${this.baseUrl}?module=message&action=${API_ACTION.GetMessages}&id=${id}`,
`${this.baseUrl}?module=${API_MODULE.Message}&action=${API_ACTION.GetMessages}&id=${id}`,
);
const responseAsJson: ApiResult<Message[]> = await response.json();
if (responseAsJson.status === '1') {
return responseAsJson.result[0]?.origin;
if (responseAsJson.result.length === 0) {
throw new Error(`No message found for id: ${id}`);
}
return responseAsJson.result[0].origin;
} else {
// Only happens if the module and action url parameters are malformed, which should not happen.
throw new Error(responseAsJson.message);
}
}
Expand Down
19 changes: 11 additions & 8 deletions typescript/ccip-server/src/services/LightClientService.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { log } from 'console';
import { BigNumber, ethers, utils } from 'ethers';

import { ILightClient } from '../../../../solidity/types';
import { ILightClient__factory } from '../../../../solidity/types';
import {
ILightClient,
ILightClient__factory,
} from '../../../../solidity/types';
import { TelepathyCcipReadIsmAbi } from '../abis/TelepathyCcipReadIsmAbi';

import { ProofStatus } from './constants/ProofStatusEnum';

export type SuccinctConfig = {
type SuccinctConfig = {
readonly lightClientAddress: string;
readonly stepFunctionId: string;
readonly platformUrl: string;
Expand Down Expand Up @@ -61,25 +64,25 @@ class LightClientService {
* Request the ZK proof from Succinct, given the sync committee poseidon, and a slot
* @param slot
* @param syncCommitteePoseidon
* @returns proof_id from succinct
*/
async requestProof(
syncCommitteePoseidon: string,
slot: BigNumber,
): Promise<string> {
console.log(`Requesting proof for${slot}`);
log(`Requesting ZK proof for ${slot}`);

// Note that Succinct will asynchronously call step() on the ISM/LightClient
const telepathyIface = new utils.Interface(TelepathyCcipReadIsmAbi);

const body = {
chainId: this.succinctConfig.chainId,
to: this.lightClientContract.address,
data: telepathyIface.encodeFunctionData('step', [slot]),
functionId: this.succinctConfig.stepFunctionId,
data: telepathyIface.encodeFunctionData('step', [slot]), // Tells Succinct to asynchronously call step() on the LightClient after the proof generation
input: utils.defaultAbiCoder.encode(
['bytes32', 'uint64'],
[syncCommitteePoseidon, slot],
),
functionId: this.succinctConfig.stepFunctionId,
retry: true,
};

Expand Down Expand Up @@ -110,4 +113,4 @@ class LightClientService {
}
}

export { LightClientService, ProofStatus };
export { LightClientService, ProofStatus, SuccinctConfig };
38 changes: 20 additions & 18 deletions typescript/ccip-server/src/services/__mocks__/HyperlaneService.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
import { MessageTx } from 'hyperlane-explorer/src/types';

const BLOCK: MessageTx = {
timestamp: 123456789,
hash: '0x123abc456def789',
from: '0x9876543210abcdef',
to: '0xabcdef0123456789',
blockHash: '0x456789abc123def',
blockNumber: 12345,
mailbox: '0xabcdef0123456789',
nonce: 0,
gasLimit: 1000000,
gasPrice: 100,
effectiveGasPrice: 90,
gasUsed: 50000,
cumulativeGasUsed: 1234567,
maxFeePerGas: 150,
maxPriorityPerGas: 100,
};

class HyperlaneService {
async getOriginBlockByMessageId(id: string): Promise<MessageTx> {
return {
timestamp: 123456789,
hash: '0x123abc456def789',
from: '0x9876543210abcdef',
to: '0xabcdef0123456789',
blockHash: '0x456789abc123def',
blockNumber: 12345,
mailbox: '0xabcdef0123456789',
nonce: 0,
gasLimit: 1000000,
gasPrice: 100,
effectiveGasPrice: 90,
gasUsed: 50000,
cumulativeGasUsed: 1234567,
maxFeePerGas: 150,
maxPriorityPerGas: 100,
};
return BLOCK;
}
}

export { HyperlaneService };
export { HyperlaneService, BLOCK };
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ enum ProofStatus {
error = 'error',
}

export const genesisTime = 1606824023;
export const slotsPerSecond = 12;
const GENESIS_TIME = 1606824023;
const SECONDS_PER_SLOT = 12;

class LightClientService {
proofStatus: ProofStatus = ProofStatus.running;
async calculateSlot(timestamp: BigNumber): Promise<BigNumber> {
return timestamp
.sub(BigNumber.from(genesisTime))
.div(BigNumber.from(slotsPerSecond));
.sub(BigNumber.from(GENESIS_TIME))
.div(BigNumber.from(SECONDS_PER_SLOT));
}

async requestProof(
Expand All @@ -33,4 +33,4 @@ class LightClientService {
}
}

export { LightClientService };
export { LightClientService, GENESIS_TIME, SECONDS_PER_SLOT };
41 changes: 37 additions & 4 deletions typescript/ccip-server/tests/services/HyperlaneService.test.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,51 @@
import { describe, expect, test } from '@jest/globals';
import { describe, expect, jest, test } from '@jest/globals';
import { Spied } from 'jest-mock';

import { HyperlaneService } from '../../src/services/HyperlaneService';

describe('HyperlaneServiceTest', () => {
let hyperlaneService: HyperlaneService;
let fetchSpied: Spied<typeof fetch>;
beforeEach(() => {
hyperlaneService = new HyperlaneService(
'https://explorer.hyperlane.xyz/api',
);

fetchSpied = jest.spyOn(global, 'fetch');
});

afterEach(() => {
jest.restoreAllMocks();
});
test('should get the block by messageId', async () => {
await hyperlaneService.getOriginBlockByMessageId(
test('should get the block timestamp by messageId', async () => {
const block = await hyperlaneService.getOriginBlockByMessageId(
'0xb0430e396f4014883c01bb3ee43df17ce93d8257a0a0b5778d9d3229a1bf02bb',
);
expect(true).toBe(true);
expect(block.timestamp).toBe(1708538979000);
});

test('should throw if messageId does not exist', async () => {
const badMessageId = '10xdeadbeef';
try {
await hyperlaneService.getOriginBlockByMessageId(badMessageId);
} catch (e: any) {
expect(e.message).toBe(`No message found for id: ${badMessageId}`);
}
});

test('should throw an error if module or action no longer exists', async () => {
fetchSpied.mockImplementation(() =>
Promise.resolve({
status: 200,
json: async () => ({ status: 0, message: 'Invalid module or action' }),
} as Response),
);
try {
await hyperlaneService.getOriginBlockByMessageId(
'0xb0430e396f4014883c01bb3ee43df17ce93d8257a0a0b5778d9d3229a1bf02bb',
);
} catch (e: any) {
expect(e.message).toBe('Invalid module or action');
}
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { BigNumber, ethers } from 'ethers';

import { LightClientService } from '../../src/services/LightClientService';
import { RPCService } from '../../src/services/RPCService';
import { genesisTime } from '../../src/services/__mocks__/LightClientService';
import { GENESIS_TIME } from '../../src/services/__mocks__/LightClientService';

// Fixtures
jest.mock('../../src/services/LightClientService');
Expand All @@ -29,7 +29,7 @@ describe('LightClientService', () => {
});
test('should return the correct proof status', async () => {
const results = await lightClientService.calculateSlot(
BigNumber.from(genesisTime + 100),
BigNumber.from(GENESIS_TIME + 100),
);
expect(results.toBigInt()).toBeGreaterThan(0);
});
Expand Down
4 changes: 3 additions & 1 deletion typescript/ccip-server/tests/services/ProofsService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,11 @@ describe('ProofsService', () => {
});

test('should set currentProofId, if proof is not ready', async () => {
// We need to try-catch because of forceRevert()
try {
await proofsService.getProofs([TARGET_ADDR, STORAGE_KEY, MESSAGE_ID]);
} catch (e) {
} catch (e: any) {
expect(e.message).toBe('Proof is not ready');
expect(proofsService.pendingProof.get(pendingProofKey)).toEqual(
PENDING_PROOF_ID,
);
Expand Down

0 comments on commit ff1ef05

Please sign in to comment.