Skip to content

Commit

Permalink
fix: registry tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Amuhar committed Aug 6, 2023
1 parent 50727f3 commit 9c7edf1
Show file tree
Hide file tree
Showing 42 changed files with 3,119 additions and 18 deletions.
11 changes: 6 additions & 5 deletions src/common/registry/fetch/key-batch.fetch.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { Injectable } from '@nestjs/common';
import { Registry__factory } from '@lido-nestjs/contracts';
import { Inject, Injectable } from '@nestjs/common';
import { REGISTRY_CONTRACT_TOKEN, Registry } from '@lido-nestjs/contracts';
import { CallOverrides } from './interfaces/overrides.interface';
import { KeyBatchRecord, RegistryKey } from './interfaces/key.interface';
import { RegistryOperatorFetchService } from './operator.fetch';
import { KEYS_LENGTH, SIGNATURE_LENGTH } from './key-batch.constants';
import { ExecutionProvider } from 'common/execution-provider';

@Injectable()
export class RegistryKeyBatchFetchService {
constructor(
protected readonly provider: ExecutionProvider,
protected readonly operatorsService: RegistryOperatorFetchService,
@Inject(REGISTRY_CONTRACT_TOKEN) private contract: Registry,
) {}

private getContract(moduleAddress: string) {
return Registry__factory.connect(moduleAddress, this.provider);
return this.contract.attach(moduleAddress);

// return Registry__factory.connect(moduleAddress, this.provider);
}

/**
Expand Down
13 changes: 9 additions & 4 deletions src/common/registry/fetch/key.fetch.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Injectable } from '@nestjs/common';
import { Inject, Injectable } from '@nestjs/common';
import { rangePromise } from '@lido-nestjs/utils';
import { Registry__factory } from '@lido-nestjs/contracts';
// import { Registry__factory } from '@lido-nestjs/contracts';
import { CallOverrides } from './interfaces/overrides.interface';
import { RegistryKey } from './interfaces/key.interface';
import { RegistryOperatorFetchService } from './operator.fetch';
import { REGISTRY_KEY_BATCH_SIZE } from './key.constants';
import { ExecutionProvider } from 'common/execution-provider';
import { REGISTRY_CONTRACT_TOKEN, Registry__factory, Registry } from '@lido-nestjs/contracts';

@Injectable()
export class RegistryKeyFetchService {
constructor(protected readonly provider: ExecutionProvider, private operatorsService: RegistryOperatorFetchService) {}
constructor(
@Inject(REGISTRY_CONTRACT_TOKEN) private contract: Registry,
private operatorsService: RegistryOperatorFetchService,
) {}

private getContract(moduleAddress: string) {
return Registry__factory.connect(moduleAddress, this.provider);
return this.contract.attach(moduleAddress);
// return Registry__factory.connect(moduleAddress, this.provider);
}

/** fetches one key */
Expand Down
10 changes: 6 additions & 4 deletions src/common/registry/fetch/meta.fetch.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Injectable } from '@nestjs/common';
import { Registry__factory } from '@lido-nestjs/contracts';
import { Inject, Injectable } from '@nestjs/common';
// import { Registry__factory } from '@lido-nestjs/contracts';
import { CallOverrides } from './interfaces/overrides.interface';
import { ExecutionProvider } from 'common/execution-provider';
import { REGISTRY_CONTRACT_TOKEN, Registry__factory, Registry } from '@lido-nestjs/contracts';

@Injectable()
export class RegistryMetaFetchService {
constructor(protected readonly provider: ExecutionProvider) {}
constructor(@Inject(REGISTRY_CONTRACT_TOKEN) private contract: Registry) {}

private getContract(moduleAddress: string) {
return Registry__factory.connect(moduleAddress, this.provider);
return this.contract.attach(moduleAddress);
// return Registry__factory.connect(moduleAddress, this.provider);
}

/** fetches keys operation index */
Expand Down
9 changes: 5 additions & 4 deletions src/common/registry/fetch/operator.fetch.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Injectable } from '@nestjs/common';
import { Inject, Injectable } from '@nestjs/common';
import { rangePromise } from '@lido-nestjs/utils';
import { Registry__factory } from '@lido-nestjs/contracts';
import { REGISTRY_CONTRACT_TOKEN, Registry__factory, Registry } from '@lido-nestjs/contracts';
import { CallOverrides } from './interfaces/overrides.interface';
import { RegistryOperator } from './interfaces/operator.interface';
import { REGISTRY_OPERATORS_BATCH_SIZE } from './operator.constants';
import { ExecutionProvider } from 'common/execution-provider';

@Injectable()
export class RegistryOperatorFetchService {
constructor(protected readonly provider: ExecutionProvider) {}
constructor(@Inject(REGISTRY_CONTRACT_TOKEN) private contract: Registry) {}

private getContract(moduleAddress: string) {
return Registry__factory.connect(moduleAddress, this.provider);
return this.contract.attach(moduleAddress);
// return Registry__factory.connect(moduleAddress, this.provider);
}

/** fetches number of operators */
Expand Down
1 change: 1 addition & 0 deletions src/common/registry/fetch/registry-fetch.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export class RegistryFetchModule {
module: RegistryFetchModule,
imports: [
...(options.imports || []),
// TODO: why we need it here?
LidoContractModule.forFeatureAsync({
async useFactory(...args) {
const config = await options.useFactory(...args);
Expand Down
2 changes: 2 additions & 0 deletions src/common/registry/main/abstract-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export abstract class AbstractRegistryService {
const overrides = { blockTag: { blockHash } };
// TODO: use feature flag
const result = await this.keyBatchFetch.fetch(moduleAddress, operatorIndex, fromIndex, toIndex, overrides);

// add moduleAddress
const operatorKeys = result.filter((key) => key);

Expand Down Expand Up @@ -170,6 +171,7 @@ export abstract class AbstractRegistryService {
await Promise.all(
// remove all keys from the database that are greater than the total number of keys
// it's needed to clear the list in db when removing keys from the contract

currentOperators.map(async (operator) => {
await entityManager.nativeDelete(RegistryKey, {
index: { $gte: operator.totalSigningKeys },
Expand Down
61 changes: 61 additions & 0 deletions src/common/registry/test/fetch/async.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { DynamicModule, Injectable, Module } from '@nestjs/common';
import { ModuleMetadata } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { getNetwork } from '@ethersproject/networks';
import { getDefaultProvider, BaseProvider } from '@ethersproject/providers';
import { RegistryFetchModule, RegistryFetchService } from '../../';

@Injectable()
class TestService {
provider: BaseProvider;

constructor() {
this.provider = getDefaultProvider(process.env.PROVIDERS_URLS);
jest.spyOn(this.provider, 'detectNetwork').mockImplementation(async () => getNetwork('mainnet'));
}
}
@Module({
providers: [TestService],
exports: [TestService],
})
class TestModule {
static forRoot(): DynamicModule {
return {
module: TestModule,
global: true,
};
}
}

describe('Async module initializing', () => {
const testModules = async (imports: ModuleMetadata['imports']) => {
const moduleRef = await Test.createTestingModule({ imports }).compile();
const fetchService: RegistryFetchService = moduleRef.get(RegistryFetchService);

expect(fetchService).toBeDefined();
};

test('forRootAsync', async () => {
await testModules([
TestModule.forRoot(),
RegistryFetchModule.forRootAsync({
async useFactory(testService: TestService) {
return { provider: testService.provider };
},
inject: [TestService],
}),
]);
});

test('forFeatureAsync', async () => {
await testModules([
TestModule.forRoot(),
RegistryFetchModule.forFeatureAsync({
async useFactory(testService: TestService) {
return { provider: testService.provider };
},
inject: [TestService],
}),
]);
});
});
56 changes: 56 additions & 0 deletions src/common/registry/test/fetch/key-batch.fetch.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Test } from '@nestjs/testing';
import { Registry__factory, REGISTRY_CONTRACT_ADDRESSES } from '@lido-nestjs/contracts';
import { getNetwork } from '@ethersproject/networks';
import { Interface } from '@ethersproject/abi';
import { getDefaultProvider } from '@ethersproject/providers';
import { keysResponse, usedStatuses, mergedKeys, mergedSignatures } from '../fixtures/key-batch.fixture';
import { RegistryFetchModule, RegistryKeyBatchFetchService } from '../../';
describe('Keys', () => {
const provider = getDefaultProvider(process.env.PROVIDERS_URLS);
if (!process.env.CHAIN_ID) {
console.error("CHAIN_ID wasn't provides");
process.exit(1);
}
const address = REGISTRY_CONTRACT_ADDRESSES[process.env.CHAIN_ID];
let fetchService: RegistryKeyBatchFetchService;

const mockCall = jest.spyOn(provider, 'call').mockImplementation(async () => '');

jest.spyOn(provider, 'detectNetwork').mockImplementation(async () => getNetwork('mainnet'));

beforeEach(async () => {
const imports = [RegistryFetchModule.forFeature({ provider })];
const moduleRef = await Test.createTestingModule({ imports }).compile();
fetchService = moduleRef.get(RegistryKeyBatchFetchService);
});

afterEach(async () => {
mockCall.mockReset();
});

test('fetch', async () => {
mockCall.mockImplementation(async () => {
const iface = new Interface(Registry__factory.abi);
return iface.encodeFunctionResult('getSigningKeys', keysResponse);
});
const result = await fetchService.fetch(address, 0, 0, usedStatuses.length);

const [firstKey] = result;

const isKeySatisfies = mergedKeys.startsWith(firstKey.key);
const isSignaturesSatisfies = mergedSignatures.startsWith(firstKey.depositSignature);
const isUseStatusSatisfies = firstKey.used === usedStatuses[0];

expect(result).toHaveLength(usedStatuses.length);

expect(isKeySatisfies).toBeTruthy();
expect(isSignaturesSatisfies).toBeTruthy();
expect(isUseStatusSatisfies).toBeTruthy();

expect(mockCall).toBeCalledTimes(1);
});

test('fetch. fromIndex > toIndex', async () => {
await expect(() => fetchService.fetch(address, 0, 2, 1)).rejects.toThrow();
});
});
59 changes: 59 additions & 0 deletions src/common/registry/test/fetch/key.fetch.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Test } from '@nestjs/testing';
import { JsonRpcBatchProvider } from '@ethersproject/providers';
import { RegistryFetchModule, RegistryKeyFetchService } from '../../';
import { REGISTRY_CONTRACT_ADDRESSES } from '@lido-nestjs/contracts';
import * as dotenv from 'dotenv';

dotenv.config();

describe('Keys', () => {
const provider = new JsonRpcBatchProvider(process.env.PROVIDERS_URLS);
if (!process.env.CHAIN_ID) {
console.error("CHAIN_ID wasn't provides");
process.exit(1);
}
const address = REGISTRY_CONTRACT_ADDRESSES[process.env.CHAIN_ID];

let fetchService: RegistryKeyFetchService;

beforeEach(async () => {
const imports = [RegistryFetchModule.forFeature({ provider })];
const moduleRef = await Test.createTestingModule({ imports }).compile();
fetchService = moduleRef.get(RegistryKeyFetchService);
});

test('fetch one key', async () => {
const key = await fetchService.fetchOne(address, 21, 0, { blockTag: 6912872 });

expect(key).toBeInstanceOf(Object);

expect(typeof key.operatorIndex).toBe('number');
expect(typeof key.index).toBe('number');
expect(typeof key.key).toBe('string');
expect(typeof key.depositSignature).toBe('string');
});

test('fetch operator keys', async () => {
const keys = await fetchService.fetch(address, 21, 0, -1, {
blockTag: 6912872,
});

expect(keys).toBeInstanceOf(Array);
expect(keys.length).toBe(3);
}, 15_000);

test('fetch multiply operators', async () => {
const keys = await fetchService.fetch(address, 21, 0, 2, {
blockTag: 6912872,
});

expect(keys).toBeInstanceOf(Array);
expect(keys.length).toBe(2);

expect(keys[0].operatorIndex).toBe(21);
expect(keys[1].operatorIndex).toBe(21);

expect(keys[0].index).toBe(0);
expect(keys[1].index).toBe(1);
});
});
88 changes: 88 additions & 0 deletions src/common/registry/test/fetch/key.fetch.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { Test } from '@nestjs/testing';
import { REGISTRY_CONTRACT_ADDRESSES, Registry__factory } from '@lido-nestjs/contracts';
import { getNetwork } from '@ethersproject/networks';
import { Interface } from '@ethersproject/abi';
import { getDefaultProvider } from '@ethersproject/providers';
import { operator, operatorFields } from '../fixtures/operator.fixture';
import { key, keyFields } from '../fixtures/key.fixture';
import { RegistryFetchModule, RegistryKeyFetchService } from '../../';

describe('Keys', () => {
const provider = getDefaultProvider(process.env.PROVIDERS_URLS);
let fetchService: RegistryKeyFetchService;
if (!process.env.CHAIN_ID) {
console.error("CHAIN_ID wasn't provides");
process.exit(1);
}
const address = REGISTRY_CONTRACT_ADDRESSES[process.env.CHAIN_ID];
const mockCall = jest.spyOn(provider, 'call').mockImplementation(async () => '');

jest.spyOn(provider, 'detectNetwork').mockImplementation(async () => getNetwork('mainnet'));

beforeEach(async () => {
const imports = [RegistryFetchModule.forFeature({ provider })];
const moduleRef = await Test.createTestingModule({ imports }).compile();
fetchService = moduleRef.get(RegistryKeyFetchService);
});

afterEach(async () => {
mockCall.mockReset();
});

test('fetchOne', async () => {
const expected = { operatorIndex: 0, index: 1, moduleAddress: address, ...key };

mockCall.mockImplementation(async () => {
const iface = new Interface(Registry__factory.abi);
return iface.encodeFunctionResult('getSigningKey', keyFields);
});
const result = await fetchService.fetchOne(address, expected.operatorIndex, expected.index);

expect(result).toEqual(expected);
expect(mockCall).toBeCalledTimes(1);
});

test('fetch', async () => {
const expectedFirst = { operatorIndex: 0, index: 1, moduleAddress: address, ...key };
const expectedSecond = { operatorIndex: 0, index: 2, moduleAddress: address, ...key };

mockCall.mockImplementation(async () => {
const iface = new Interface(Registry__factory.abi);
return iface.encodeFunctionResult('getSigningKey', keyFields);
});
const result = await fetchService.fetch(
address,
expectedFirst.operatorIndex,
expectedFirst.index,
expectedSecond.index + 1,
);

expect(result).toEqual([expectedFirst, expectedSecond]);
expect(mockCall).toBeCalledTimes(2);
});

test('fetch all operator keys', async () => {
const expected = { operatorIndex: 1, index: 0, moduleAddress: address, ...key };

mockCall
.mockImplementationOnce(async () => {
const iface = new Interface(Registry__factory.abi);
return iface.encodeFunctionResult(
'getNodeOperator',
operatorFields({ ...operator, moduleAddress: address, totalSigningKeys: 1 }),
);
})
.mockImplementation(async () => {
const iface = new Interface(Registry__factory.abi);
return iface.encodeFunctionResult('getSigningKey', keyFields);
});
const result = await fetchService.fetch(address, expected.operatorIndex);

expect(result).toEqual([expected]);
expect(mockCall).toBeCalledTimes(2);
});

test('fetch. fromIndex > toIndex', async () => {
await expect(() => fetchService.fetch(address, 0, 2, 1)).rejects.toThrow();
});
});
Loading

0 comments on commit 9c7edf1

Please sign in to comment.