Skip to content

Commit

Permalink
Merge pull request #165 from lidofinance/test/cross-duplicates
Browse files Browse the repository at this point in the history
test: e2e find cross modules duplicates
  • Loading branch information
Amuhar authored Jan 10, 2024
2 parents b8e1d22 + 83a38e5 commit 21aefe7
Show file tree
Hide file tree
Showing 3 changed files with 208 additions and 2 deletions.
2 changes: 1 addition & 1 deletion test/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const SECURITY_MODULE_OWNER =
export const STAKING_ROUTER = '0xa3Dbd317E53D363176359E10948BA0b1c0A4c820';
export const NOP_REGISTRY = '0x9D4AF1Ee19Dad8857db3a45B0374c81c8A1C6320';
export const DEPOSIT_CONTRACT = '0xff50ed3d0ec03aC01D4C79aAd74928BFF48a7b2b';

export const FAKE_SIMPLE_DVT = '0x0000000000000000000000000000000000000123';
// Withdrawal credentials
export const GOOD_WC =
'0x010000000000000000000000dc62f9e8c34be08501cdef4ebde0a280f576d762';
Expand Down
52 changes: 51 additions & 1 deletion test/helpers/mockKeysApi.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import ethers from 'ethers';

import { KeysApiService } from '../../src/keys-api/keys-api.service';
import { NOP_REGISTRY } from './../constants';
import { FAKE_SIMPLE_DVT, NOP_REGISTRY } from './../constants';
import { RegistryOperator } from 'keys-api/interfaces/RegistryOperator';
import { SRModule } from 'keys-api/interfaces';
import { ELBlockSnapshot } from 'keys-api/interfaces/ELBlockSnapshot';
Expand All @@ -28,6 +28,27 @@ export const mockedModule = (
active: true,
});

export const mockedModuleDvt = (
block: ethers.providers.Block,
lastChangedBlockHash: string,
nonce = 6046,
): SRModule => ({
nonce,
type: 'grouped-onchain-v1',
id: 2,
stakingModuleAddress: FAKE_SIMPLE_DVT,
moduleFee: 10,
treasuryFee: 10,
targetShare: 10,
status: 1,
name: 'NodeOperatorRegistrySimpleDvt',
lastDepositAt: block.timestamp,
lastDepositBlock: block.number,
lastChangedBlockHash,
exitedValidatorsCount: 0,
active: true,
});

export const mockedMeta = (
block: ethers.providers.Block,
lastChangedBlockHash: string,
Expand All @@ -52,6 +73,20 @@ export const mockedOperators: RegistryOperator[] = [
},
];

export const mockedDvtOperators: RegistryOperator[] = [
{
name: 'Dev DVT team',
rewardAddress: '0x6D725DAe055287f913661ee0b79dE6B21F12A459',
stakingLimit: 12,
stoppedValidators: 0,
totalSigningKeys: 12,
usedSigningKeys: 10,
index: 0,
active: true,
moduleAddress: FAKE_SIMPLE_DVT,
},
];

export const mockedKeysApiOperators = (
keysApiService: KeysApiService,
mockedOperators: RegistryOperator[],
Expand All @@ -68,6 +103,21 @@ export const mockedKeysApiOperators = (
}));
};

export const mockedKeysApiOperatorsMany = (
keysApiService: KeysApiService,
data: { operators: RegistryOperator[]; module: SRModule }[],
mockedMeta: ELBlockSnapshot,
) => {
jest
.spyOn(keysApiService, 'getOperatorListWithModule')
.mockImplementation(async () => ({
data: data,
meta: {
elBlockSnapshot: mockedMeta,
},
}));
};

export const mockedKeysApiUnusedKeys = (
keysApiService: KeysApiService,
mockedKeys: RegistryKey[],
Expand Down
156 changes: 156 additions & 0 deletions test/manifest.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ import { fromHexString, toHexString } from '@chainsafe/ssz';
// Helpers
import {
computeRoot,
mockedDvtOperators,
mockedKeysApiOperators,
mockedKeysApiOperatorsMany,
mockedKeysApiUnusedKeys,
mockedKeysWithDuplicates,
mockedMeta,
mockedModule,
mockedModuleDvt,
mockedOperators,
} from './helpers';

Expand All @@ -34,6 +37,7 @@ import {
sk,
pk,
NOP_REGISTRY,
FAKE_SIMPLE_DVT,
} from './constants';

// Ganache
Expand Down Expand Up @@ -85,6 +89,8 @@ import { KeyValidatorInterface } from '@lido-nestjs/key-validation';
// Mock rabbit straight away
jest.mock('../src/transport/stomp/stomp.client.ts');

jest.setTimeout(10_000);

describe('ganache e2e tests', () => {
let server: ReturnType<typeof makeServer>;

Expand Down Expand Up @@ -962,6 +968,156 @@ describe('ganache e2e tests', () => {
TESTS_TIMEOUT,
);

test(
'skip deposit if find duplicated key in another staking module',
async () => {
const tempProvider = new ethers.providers.JsonRpcProvider(
`http://127.0.0.1:${GANACHE_PORT}`,
);
const currentBlock = await tempProvider.getBlock('latest');

// this key should be used in kapi
const goodDepositMessage = {
pubkey: pk,
withdrawalCredentials: fromHexString(GOOD_WC),
amount: 32000000000, // gwei!
};
const goodSigningRoot = computeRoot(goodDepositMessage);
const goodSig = sk.sign(goodSigningRoot).toBytes();

const goodDepositData = {
...goodDepositMessage,
signature: goodSig,
};
const goodDepositDataRoot = DepositData.hashTreeRoot(goodDepositData);

if (!process.env.WALLET_PRIVATE_KEY) throw new Error(NO_PRIVKEY_MESSAGE);
const wallet = new ethers.Wallet(process.env.WALLET_PRIVATE_KEY);

// Make a deposit
const signer = wallet.connect(providerService.provider);
const depositContract = DepositAbi__factory.connect(
DEPOSIT_CONTRACT,
signer,
);
await depositContract.deposit(
goodDepositData.pubkey,
goodDepositData.withdrawalCredentials,
goodDepositData.signature,
goodDepositDataRoot,
{ value: ethers.constants.WeiPerEther.mul(32) },
);

await depositService.setCachedEvents({
data: [],
headers: {
startBlock: currentBlock.number,
endBlock: currentBlock.number,
version: '1',
},
});

// mocked curated module
const stakingModule = mockedModule(currentBlock, currentBlock.hash);
const stakingDvtModule = mockedModuleDvt(currentBlock, currentBlock.hash);
const meta = mockedMeta(currentBlock, currentBlock.hash);

mockedKeysApiOperatorsMany(
keysApiService,
[
{ operators: mockedOperators, module: stakingModule },
{ operators: mockedDvtOperators, module: stakingDvtModule },
],
meta,
);

// list of keys for /keys?used=false mock
const unusedKeys = [
{
key: '0xa9bfaa8207ee6c78644c079ffc91b6e5abcc5eede1b7a06abb8fb40e490a75ea269c178dd524b65185299d2bbd2eb7b2',
depositSignature:
'0xaa5f2a1053ba7d197495df44d4a32b7ae10265cf9e38560a16b782978c0a24271a113c9538453b7e45f35cb64c7adb460d7a9fe8c8ce6b8c80ca42fd5c48e180c73fc08f7d35ba32e39f32c902fd333faf47611827f0b7813f11c4c518dd2e59',
operatorIndex: 0,
used: false,
index: 0,
moduleAddress: NOP_REGISTRY,
},
{
key: '0xa9bfaa8207ee6c78644c079ffc91b6e5abcc5eede1b7a06abb8fb40e490a75ea269c178dd524b65185299d2bbd2eb7b2',
depositSignature:
'0xaa5f2a1053ba7d197495df44d4a32b7ae10265cf9e38560a16b782978c0a24271a113c9538453b7e45f35cb64c7adb460d7a9fe8c8ce6b8c80ca42fd5c48e180c73fc08f7d35ba32e39f32c902fd333faf47611827f0b7813f11c4c518dd2e59',
operatorIndex: 0,
used: false,
index: 0,
moduleAddress: FAKE_SIMPLE_DVT,
},
];

mockedKeysApiUnusedKeys(keysApiService, unusedKeys, meta);

// Check that module was not paused
const routerContract = StakingRouterAbi__factory.connect(
STAKING_ROUTER,
providerService.provider,
);
const isOnPause = await routerContract.getStakingModuleIsDepositsPaused(
1,
);
expect(isOnPause).toBe(false);

await guardianService.handleNewBlock();

// just skip on this iteration deposit for staking module
expect(sendDepositMessage).toBeCalledTimes(0);
expect(sendPauseMessage).toBeCalledTimes(0);

// after deleting duplicates in staking module,
// council will resume deposits to module
const unusedKeysWithoutDuplicates = [
{
key: '0xa9bfaa8207ee6c78644c079ffc91b6e5abcc5eede1b7a06abb8fb40e490a75ea269c178dd524b65185299d2bbd2eb7b2',
depositSignature:
'0xaa5f2a1053ba7d197495df44d4a32b7ae10265cf9e38560a16b782978c0a24271a113c9538453b7e45f35cb64c7adb460d7a9fe8c8ce6b8c80ca42fd5c48e180c73fc08f7d35ba32e39f32c902fd333faf47611827f0b7813f11c4c518dd2e59',
operatorIndex: 0,
used: false,
index: 0,
moduleAddress: NOP_REGISTRY,
},
];

const newBlock = await tempProvider.getBlock('latest');
const newMeta = mockedMeta(newBlock, newBlock.hash);
const newStakingModule = mockedModule(currentBlock, newBlock.hash);

mockedKeysApiOperators(
keysApiService,
mockedOperators,
newStakingModule,
newMeta,
);

mockedKeysApiUnusedKeys(
keysApiService,
unusedKeysWithoutDuplicates,
newMeta,
);

await guardianService.handleNewBlock();

expect(sendDepositMessage).toBeCalledTimes(1);

expect(sendDepositMessage).toHaveBeenLastCalledWith(
expect.objectContaining({
blockNumber: newBlock.number,
guardianAddress: wallet.address,
guardianIndex: 9,
stakingModuleId: 1,
}),
);
},
TESTS_TIMEOUT,
);

test(
'inconsistent kapi requests data',
async () => {
Expand Down

0 comments on commit 21aefe7

Please sign in to comment.