Skip to content

Commit

Permalink
Merge pull request #300 from lidofinance/develop
Browse files Browse the repository at this point in the history
Release 2.1.0
  • Loading branch information
Amuhar authored Oct 2, 2024
2 parents 33141b1 + b2133fa commit c5b7238
Show file tree
Hide file tree
Showing 120 changed files with 2,539 additions and 1,329 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module.exports = {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js', 'chronix.config.ts', 'chronix-init.js'],
ignorePatterns: ['.eslintrc.js', 'chronix.config.ts', 'scripts'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
- name: Run tests
run: yarn test
- name: Run E2E tests
run: yarn test:e2e:docker
run: yarn test:e2e
env:
PROVIDERS_URLS: ${{ secrets.PROVIDERS_URLS }}
CL_API_URLS: "https://e2e-test.lido.fi,"
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,6 @@ lerna-debug.log*

# benchmarks
/benchmarks/output.json


artifacts
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"name": "Debug Jest e2e",
"runtimeExecutable": "yarn",
"runtimeArgs": [
"test:e2e:docker:debug",
"test:e2e:debug",
],
"cwd": "${workspaceFolder}/src",
"autoAttachChildProcesses": true,
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.e2e
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ RUN yarn install --frozen-lockfile --non-interactive && yarn cache clean
COPY ./src ./src
RUN yarn typechain

CMD ["yarn", "test:e2e:docker"]
CMD ["yarn", "test:e2e"]
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,35 @@ For configuring Grafana, navigate to http://localhost:8000/dashboards. Here, you

To view the list of alerts in Prometheus, visit http://localhost:9090/alerts. For checking the list of fired alerts in Alertmanager, go to http://localhost:9093/#/alerts.

## Update CSM module ABI

Clone CSM repo

```sh
git clone https://github.com/lidofinance/community-staking-module
cd community-staking-module
```

Execute the command in the CSM module repository

```sh
forge build --force
```
Copy CSModule.json

```sh
cp community-staking-module/out/CSModule.sol/CSModule.json ./artifacts/CSMModule.json
```

Execute the ABI preparation command

```sh
yarn prepare:csm
```

## E2E tests

`$ yarn test:e2e`
`$ yarn test:e2e:docker`

## Environment variable

Expand Down
17 changes: 14 additions & 3 deletions docker-compose.e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@ services:
image: postgres:14-alpine
restart: unless-stopped
environment:
- POSTGRES_DB=${DB_NAME}
- POSTGRES_USER=${DB_USER}
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=node_operator_keys_service_db
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
expose:
- 5432:5432
volumes:
- ./.volumes/pgdata-${CHAIN_ID}/:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5

e2e_keys_api:
container_name: e2e_keys_api
Expand All @@ -21,7 +26,13 @@ services:
dockerfile: Dockerfile.e2e
environment:
- NODE_ENV=production
- DB_NAME=node_operator_keys_service_db
- DB_PORT=5432
- DB_HOST=e2e_pgdb
- DB_USER=postgres
- DB_PASSWORD=postgres
depends_on:
e2e_pgdb:
condition: service_healthy
links:
- e2e_pgdb
21 changes: 11 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "lido-keys-api",
"version": "1.0.2",
"version": "2.1.0",
"description": "Lido Node Operators keys service",
"author": "Lido team",
"private": true,
Expand Down Expand Up @@ -28,26 +28,27 @@
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node --max-old-space-size=4096 dist/main",
"start:prod": "test -f dist/main.js && node --max-old-space-size=4096 dist/main || node --max-old-space-size=4096 dist/src/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "docker-compose --env-file=./.env -f docker-compose.e2e.yml up --build --abort-on-container-exit",
"test:e2e:docker": "mikro-orm schema:drop -r && mikro-orm migration:up && jest -i --config jest-e2e.json && chronix compile && chronix test",
"test:e2e:docker:debug": "mikro-orm schema:drop -r && mikro-orm migration:up && node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest -i --config jest-e2e.json && chronix compile && chronix test",
"test:e2e": "mikro-orm schema:drop -r && mikro-orm migration:up && jest -i --config jest-e2e.json && chronix compile && chronix test",
"test:e2e:debug": "mikro-orm schema:drop -r && mikro-orm migration:up && node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest -i --config jest-e2e.json && chronix compile && chronix test",
"test:e2e:docker": "docker-compose -f docker-compose.e2e.yml up --build --abort-on-container-exit",
"typechain": "typechain --target=ethers-v5 --out-dir ./src/generated ./src/staking-router-modules/contracts/abi/*.json",
"chronix:compile": "chronix compile",
"chronix:test": "chronix test",
"test:alerts": "promtool test rules alerts/*.test.yml",
"chronix:start": "chronix start",
"fork:init": "node chronix-init"
"fork:init": "node chronix-init",
"prepare:csm": "node scripts/prepare-csm-abi.js"
},
"dependencies": {
"@lido-nestjs/consensus": "^1.5.0",
"@lido-nestjs/constants": "^5.2.0",
"@lido-nestjs/contracts": "^9.3.0",
"@lido-nestjs/constants": "^5.2.1",
"@lido-nestjs/contracts": "^9.5.1",
"@lido-nestjs/execution": "^1.11.0",
"@lido-nestjs/fetch": "^1.4.0",
"@lido-nestjs/logger": "^1.3.2",
Expand All @@ -69,6 +70,7 @@
"@nestjs/throttler": "^2.0.0",
"@sentry/node": "^6.16.1",
"@typechain/ethers-v5": "^9.0.0",
"@types/k6": "~0.44.2",
"@willsoto/nestjs-prometheus": "^4.4.0",
"cache-manager": "^3.6.1",
"class-transformer": "^0.5.1",
Expand All @@ -81,8 +83,7 @@
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.5.2",
"typechain": "^7.0.0",
"@types/k6": "~0.44.2"
"typechain": "^7.0.0"
},
"devDependencies": {
"@nestjs/cli": "^8.2.5",
Expand Down
File renamed without changes.
37 changes: 37 additions & 0 deletions scripts/prepare-csm-abi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const fs = require('fs');

const getCSMArtifact = () => {
const artifact = JSON.parse(fs.readFileSync('./artifacts/CSModule.json', 'utf-8'));
return artifact;
};

const PARTS = [
'getNodeOperatorIsActive',
'getNodeOperator',
'getSigningKey',
'getNonce',
'getNodeOperatorsCount',
'getSigningKeysWithSignatures',
'addNodeOperatorETH',
'activatePublicRelease',
'grantRole',
'MODULE_MANAGER_ROLE',
'decreaseVettedSigningKeysCount',
'STAKING_ROUTER_ROLE',
'NodeOperatorAdded',
'RESUME_ROLE',
'resume',
'NodeOperatorRewardAddressChanged',
];

const getCSMAbi = () => {
const { abi } = getCSMArtifact();

return abi.filter((node) => PARTS.includes(node.name) || node.type === 'error');
};

fs.writeFileSync(
'./src/staking-router-modules/contracts/abi/csm.json',
JSON.stringify(getCSMAbi(), null, '\t'),
'utf-8',
);
7 changes: 7 additions & 0 deletions src/app/app-testing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { KeyRegistryModule } from 'common/registry';
import { StakingRouterModule } from 'staking-router-modules';
import { KeysUpdateModule } from 'jobs/keys-update';
import { DatabaseE2ETestingModule } from './database-e2e-testing.module';
import { CSMKeyRegistryModule } from 'common/registry-csm';

@Module({
imports: [
Expand All @@ -31,6 +32,12 @@ import { DatabaseE2ETestingModule } from './database-e2e-testing.module';
return { provider };
},
}),
CSMKeyRegistryModule.forRootAsync({
inject: [SimpleFallbackJsonRpcBatchProvider],
async useFactory(provider) {
return { provider };
},
}),
StakingRouterModule,
KeysUpdateModule,
],
Expand Down
7 changes: 7 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { LoggerModule } from '@lido-nestjs/logger';
import { SimpleFallbackJsonRpcBatchProvider } from '@lido-nestjs/execution';
import { KeyRegistryModule } from '../common/registry';
import { StakingRouterModule } from '../staking-router-modules';
import { CSMKeyRegistryModule } from 'common/registry-csm';

@Module({
imports: [
Expand Down Expand Up @@ -51,6 +52,12 @@ import { StakingRouterModule } from '../staking-router-modules';
return { provider, keysBatchSize: configService.get('KEYS_FETCH_BATCH_SIZE') };
},
}),
CSMKeyRegistryModule.forRootAsync({
inject: [SimpleFallbackJsonRpcBatchProvider, ConfigService],
async useFactory(provider: SimpleFallbackJsonRpcBatchProvider, configService: ConfigService) {
return { provider, keysBatchSize: configService.get('KEYS_FETCH_BATCH_SIZE') };
},
}),
StakingRouterModule,
ValidatorsModule,
JobsModule,
Expand Down
9 changes: 7 additions & 2 deletions src/app/simple-dvt-deploy.e2e-chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ describe('Simple DVT deploy', () => {
session = await sdk.env.hardhat({
fork: forkUrl,
chainId: 1,
forkBlockNumber: 19282216
forkBlockNumber: 19282216,
});

moduleRef = await Test.createTestingModule({ imports: [AppModule] })
Expand All @@ -84,6 +84,11 @@ describe('Simple DVT deploy', () => {
validatorsRegistryLastBlockNumber: jest.fn(),
validatorsRegistryLastSlot: jest.fn(),
validatorsEnabled: jest.fn(),
updateDurationByModule: {
labels: () => {
return { observe: () => ({}) };
},
},
})
.compile();

Expand All @@ -100,7 +105,7 @@ describe('Simple DVT deploy', () => {
stakingRouterService = moduleRef.get(StakingRouterService);

jest.spyOn(configService, 'get').mockImplementation((path: any) => {
if (path === 'LIDO_LOCATOR_ADDRESS') {
if (path === 'LIDO_LOCATOR_DEVNET_ADDRESS') {
return '0xC1d0b3DE6792Bf6b4b37EccdcC24e45978Cfd2Eb';
}

Expand Down
4 changes: 4 additions & 0 deletions src/common/config/env.validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ export class EnvironmentVariables {
@IsPositive()
@Transform(({ value }) => parseInt(value, 10))
STREAM_TIMEOUT = 60_000;

@IsOptional()
@IsString()
LIDO_LOCATOR_DEVNET_ADDRESS = '';
}

export function validate(config: Record<string, unknown>) {
Expand Down
1 change: 1 addition & 0 deletions src/common/execution-provider/execution-provider.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { ExecutionProviderService } from './execution-provider.service';
maxConcurrentRequests: configService.get('PROVIDER_CONCURRENT_REQUESTS'),
batchAggregationWaitMs: configService.get('PROVIDER_BATCH_AGGREGATION_WAIT_MS'),
},
logRetries: true,
fetchMiddlewares: [
async (next) => {
const endTimer = prometheusService.elRpcRequestDuration.startTimer();
Expand Down
8 changes: 8 additions & 0 deletions src/common/prometheus/prometheus.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ export class PrometheusService {
labelNames: ['result', 'job'],
});

public updateDurationByModule = this.getOrCreateMetric('Histogram', {
prefix: true,
name: 'sr_module_key_update_duration_seconds',
help: 'Time taken to update a certain number of keys for a staking module',
buckets: [0.2, 0.6, 1, 2, 3, 5, 8, 13, 30, 60, 120, 180],
labelNames: ['srModuleAddress', 'totalKeysAmount'],
});

public registryLastUpdate = this.getOrCreateMetric('Gauge', {
prefix: true,
name: 'last_update_timestamp',
Expand Down
14 changes: 14 additions & 0 deletions src/common/registry-csm/fetch/interfaces/key.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export interface RegistryKey {
index: number;
operatorIndex: number;
depositSignature: string;
key: string;
used: boolean;
moduleAddress: string;
vetted: boolean;
}

export type KeyBatchRecord = [string, string] & {
keys: string;
signatures: string;
};
20 changes: 20 additions & 0 deletions src/common/registry-csm/fetch/interfaces/module.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { ModuleMetadata } from '@nestjs/common';
import { Signer } from 'ethers';
import { Provider } from '@ethersproject/providers';

export const REGISTRY_FETCH_OPTIONS_TOKEN = Symbol('registryFetchOptionsToken');

export interface RegistryFetchOptions {
registryAddress?: string;
lidoAddress?: string;
provider?: Provider | Signer;
keysBatchSize?: number;
}

export interface RegistryFetchModuleSyncOptions extends Pick<ModuleMetadata, 'imports'>, RegistryFetchOptions {}

export interface RegistryFetchModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
useFactory: (...args: any[]) => Promise<RegistryFetchOptions>;
inject?: any[];
}
12 changes: 12 additions & 0 deletions src/common/registry-csm/fetch/interfaces/operator.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export interface RegistryOperator {
index: number;
active: boolean;
name: string;
rewardAddress: string;
stakingLimit: number;
stoppedValidators: number;
totalSigningKeys: number;
usedSigningKeys: number;
moduleAddress: string;
finalizedUsedSigningKeys: number;
}
11 changes: 11 additions & 0 deletions src/common/registry-csm/fetch/interfaces/overrides.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { CallOverrides as CallOverridesSource } from '@ethersproject/contracts';

export type BlockTagWith1898 =
| string
| number
| { blockNumber: string }
| { blockHash: string; requireCanonical?: boolean };

export interface CallOverrides extends Omit<CallOverridesSource, 'blockTag'> {
blockTag?: BlockTagWith1898;
}
8 changes: 8 additions & 0 deletions src/common/registry-csm/fetch/key-batch.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* keys 96 = 48byte
* signatures 192 = 96byte
* https://github.com/lidofinance/lido-dao/blob/539a0faf33807d04444047d0905dce2b45260dfa/contracts/0.4.24/lib/SigningKeys.sol#L213-L238
*/
export const KEYS_LENGTH = 96;
export const SIGNATURE_LENGTH = 192;
export const KEYS_BATCH_SIZE = 1100;
Loading

0 comments on commit c5b7238

Please sign in to comment.