Skip to content

Commit

Permalink
Implement rpc method eth_syncing (#531)
Browse files Browse the repository at this point in the history
* feat: add eth_syncing RPC method

* fix: add mock tests for eth_syncing

* fix: update comment for RPC methods to implement

* fix: add test for eth_syncing

* fix: add test for eth_syncing

* fix: proper type for eth_syncing.ts

---------

Co-authored-by: rodolfopietro97 <[email protected]>
  • Loading branch information
fabiorigam and rodolfopietro97 authored Feb 1, 2024
1 parent 2a22a5c commit 716bb75
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 59 deletions.
4 changes: 2 additions & 2 deletions packages/provider/src/utils/const/rpc-mapper/rpc-methods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ enum RPC_METHODS {
eth_getTransactionByHash = 'eth_getTransactionByHash',
eth_getTransactionCount = 'eth_getTransactionCount', // TEMPORARY COMMENT - IMPLEMENTED (Understand only if count is better, instead of nonce)
eth_getTransactionReceipt = 'eth_getTransactionReceipt',
eth_sendTransaction = 'eth_sendTransaction', // TEMPORARY COMMENT - TO IMPLEMENT
eth_syncing = 'eth_syncing', // TEMPORARY COMMENT - TO IMPLEMENT
eth_sendTransaction = 'eth_sendTransaction', // TEMPORARY COMMENT - TO IMPLEMENT (WALLET NEEDED)
eth_syncing = 'eth_syncing',
net_version = 'net_version',
web3_clientVersion = 'web3_clientVersion', // TEMPORARY COMMENT -IMPLEMENTED (Better understand if 'thor' is ok)
eth_subscribe = 'eth_subscribe', // TEMPORARY COMMENT - TO IMPLEMENT
Expand Down
11 changes: 10 additions & 1 deletion packages/provider/src/utils/formatter/blocks/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,13 @@ interface BlocksRPC extends BlockHeaderRPC {
mixHash: string;
}

export { type BlockHeaderRPC, type BlocksRPC };
/**
* Return type of eth_syncing for RPC method.
*/
interface SyncBlockRPC {
startingBlock: null;
currentBlock: BlocksRPC | null;
highestBlock: string | null;
}

export { type BlockHeaderRPC, type BlocksRPC, type SyncBlockRPC };
Original file line number Diff line number Diff line change
@@ -1,32 +1,65 @@
import { type ThorClient } from '@vechain/vechain-sdk-network';
import { buildError, FUNCTION } from '@vechain/vechain-sdk-errors';
import {
blocksFormatter,
RPC_METHODS,
RPCMethodsMap,
type SyncBlockRPC
} from '../../../../provider';
import { JSONRPC, buildProviderError } from '@vechain/vechain-sdk-errors';

/**
* RPC Method eth_syncing implementation
*
* @link [eth_syncing](https://docs.infura.io/networks/ethereum/json-rpc-methods/eth_syncing)
*
* @param thorClient - The thor client instance to use.
* @param params - The standard array of rpc call parameters.
* @note:
* * params[0]: ...
* * params[1]: ...
* * params[n]: ...
*
* @returns Returns an object with the sync status of the node if the node is out-of-sync and is syncing. Returns false when the node is already in sync.
*/
const ethSyncing = async (
thorClient: ThorClient,
params: unknown[]
): Promise<void> => {
// To avoid eslint error
await Promise.resolve(0);
thorClient: ThorClient
): Promise<boolean | SyncBlockRPC> => {
try {
const bestBlock = await thorClient.blocks.getBestBlock();
const genesisBlock = await thorClient.blocks.getGenesisBlock();

// Not implemented yet
throw buildError(
FUNCTION.NOT_IMPLEMENTED,
'Method "eth_syncing" not not implemented yet',
{
params,
thorClient
// Check if the node is already in sync
if (
bestBlock != null &&
Math.floor(Date.now() / 1000) - bestBlock.timestamp < 11000
) {
return false;
}
);

// Calculate the chainId
const chainId = (await RPCMethodsMap(thorClient)[
RPC_METHODS.eth_chainId
]([])) as string;

const highestBlock =
genesisBlock != null
? Math.floor((Date.now() - genesisBlock.timestamp) / 10000)
: null;

return {
startingBlock: null,
currentBlock:
bestBlock != null
? blocksFormatter.formatToRPCStandard(bestBlock, chainId)
: null,
highestBlock:
highestBlock != null ? highestBlock.toString(16) : null
};
} catch (e) {
throw buildProviderError(
JSONRPC.INTERNAL_ERROR,
`Method 'eth_syncing' failed: Error while getting last block\n
URL: ${thorClient.httpClient.baseURL}`,
{
innerError: JSON.stringify(e)
}
);
}
};

export { ethSyncing };
7 changes: 5 additions & 2 deletions packages/provider/src/utils/rpc-mapper/rpc-mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ import { RPC_METHODS } from '../const';
import {
type BlocksRPC,
type SendRawTransactionResultRPC,
type SyncBlockRPC,
type TransactionReceiptRPC,
type TransactionRPC
} from '../formatter';
Expand Down Expand Up @@ -184,8 +185,10 @@ const RPCMethodsMap = (
await ethSendTransaction(thorClient, params);
},

[RPC_METHODS.eth_syncing]: async (params) => {
await ethSyncing(thorClient, params);
[RPC_METHODS.eth_syncing]: async (): Promise<
boolean | SyncBlockRPC
> => {
return await ethSyncing(thorClient);
},

[RPC_METHODS.net_version]: async (): Promise<string> => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { beforeEach, describe, expect, jest, test } from '@jest/globals';
import { ProviderRpcError } from '@vechain/vechain-sdk-errors';
import { RPC_METHODS, RPCMethodsMap } from '../../../../src';
import { ThorClient } from '@vechain/vechain-sdk-network';
import { soloNetwork } from '../../../fixture';

/**
* RPC Mapper integration tests for 'eth_syncing' method with Solo Network and mocked functionality
*
* @group integration/rpc-mapper/methods/eth_syncing
*/
describe('RPC Mapper - eth_syncing method tests', () => {
/**
* Thor client instance
*/
let thorClient: ThorClient;

/**
* Init thor client before each test
*/
beforeEach(() => {
// Init thor client
thorClient = new ThorClient(soloNetwork);
});

/**
* eth_syncing RPC call tests - Negative cases
*/
describe('eth_syncing - Negative cases', () => {
/**
* Test case that mocks an error thrown by the getBestBlock method
*/
test('Should throw `ProviderRpcError` if an error occurs while retrieving the best block', async () => {
// Mock the getGenesisBlock method to return null
jest.spyOn(thorClient.blocks, 'getBestBlock').mockRejectedValue(
new Error()
);

await expect(
RPCMethodsMap(thorClient)[RPC_METHODS.eth_syncing]([])
).rejects.toThrowError(ProviderRpcError);
});

/**
* Test case that mocks an error thrown by the getGenesisBlock method
*/
test('Should throw `ProviderRpcError` if an error occurs while retrieving the genesis block', async () => {
// Mock the getGenesisBlock method to return null
jest.spyOn(thorClient.blocks, 'getGenesisBlock').mockRejectedValue(
new Error()
);

await expect(
RPCMethodsMap(thorClient)[RPC_METHODS.eth_syncing]([])
).rejects.toThrowError(ProviderRpcError);
});

/**
* Test case where the best block is not defined
*/
test('Should return an object with the sync status of the node if the node is out-of-sync', async () => {
// Mock the getBestBlock method to return null
jest.spyOn(thorClient.blocks, 'getBestBlock').mockResolvedValue(
null
);

const status = (await RPCMethodsMap(thorClient)[
RPC_METHODS.eth_syncing
]([])) as string;

expect(status).not.toBe(false);
});

/**
* Test case where the best block and genesis block are not defined
*/
test('Should return an object with the sync status of the node if the node is out-of-sync', async () => {
// Mock the getBestBlock method to return null
jest.spyOn(thorClient.blocks, 'getBestBlock').mockResolvedValue(
null
);

// Mock the getGenesisBlock method to return null
jest.spyOn(thorClient.blocks, 'getGenesisBlock').mockResolvedValue(
null
);

const status = (await RPCMethodsMap(thorClient)[
RPC_METHODS.eth_syncing
]([])) as string;

expect(status).not.toBe(false);
});
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { afterEach, beforeEach, describe, expect, test } from '@jest/globals';
import { NotImplementedError } from '@vechain/vechain-sdk-errors';
import { beforeEach, describe, expect, test } from '@jest/globals';
import { RPC_METHODS, RPCMethodsMap } from '../../../../src';
import { ThorClient } from '@vechain/vechain-sdk-network';
import { testNetwork } from '../../../fixture';
Expand All @@ -23,46 +22,18 @@ describe('RPC Mapper - eth_syncing method tests', () => {
thorClient = new ThorClient(testNetwork);
});

/**
* Destroy thor client after each test
*/
afterEach(() => {
thorClient.destroy();
});

/**
* eth_syncing RPC call tests - Positive cases
*/
describe('eth_syncing - Positive cases', () => {
/**
* Positive case 1 - ... Description ...
* Positive case 1 - Check status
*/
test('eth_syncing - positive case 1', async () => {
// NOT IMPLEMENTED YET!
await expect(
async () =>
await RPCMethodsMap(thorClient)[RPC_METHODS.eth_syncing]([
-1
])
).rejects.toThrowError(NotImplementedError);
});
});

/**
* eth_syncing RPC call tests - Negative cases
*/
describe('eth_syncing - Negative cases', () => {
/**
* Negative case 1 - ... Description ...
*/
test('eth_syncing - negative case 1', async () => {
// NOT IMPLEMENTED YET!
await expect(
async () =>
await RPCMethodsMap(thorClient)[RPC_METHODS.eth_syncing]([
'SOME_RANDOM_PARAM'
])
).rejects.toThrowError(NotImplementedError);
const status = await RPCMethodsMap(thorClient)[
RPC_METHODS.eth_syncing
]([]);
expect(status).toBe(false);
});
});
});

1 comment on commit 716bb75

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test Coverage

Summary

Lines Statements Branches Functions
Coverage: 100%
100% (2338/2338) 99.52% (421/423) 99.8% (499/500)
Title Tests Skipped Failures Errors Time
core 406 0 💤 0 ❌ 0 🔥 1m 28s ⏱️
network 192 0 💤 0 ❌ 0 🔥 2m 58s ⏱️
errors 43 0 💤 0 ❌ 0 🔥 11.325s ⏱️

Please sign in to comment.