Skip to content

Commit

Permalink
Merge pull request #184 from lidofinance/develop
Browse files Browse the repository at this point in the history
dev to main
  • Loading branch information
eddort committed Feb 29, 2024
2 parents a1a3caf + bd37ce3 commit b94bd01
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 4 deletions.
2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "lido-council-daemon",
"version": "2.0.2",
"version": "2.1.0",
"description": "Lido Council Daemon",
"author": "Lido team",
"private": true,
Expand Down
19 changes: 17 additions & 2 deletions src/contracts/deposit/deposit.service.ts
Expand Up @@ -142,10 +142,25 @@ export class DepositService {
* to reduce the size of the information stored in the cache
*/
public formatEvent(rawEvent: DepositEventEvent): DepositEvent {
const { args, transactionHash: tx, blockNumber, blockHash } = rawEvent;
const {
args,
transactionHash: tx,
blockNumber,
blockHash,
logIndex,
} = rawEvent;
const { withdrawal_credentials: wc, pubkey, amount, signature } = args;

return { pubkey, wc, amount, signature, tx, blockNumber, blockHash };
return {
pubkey,
wc,
amount,
signature,
tx,
blockNumber,
blockHash,
logIndex,
};
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/contracts/deposit/interfaces/event.interface.ts
Expand Up @@ -6,6 +6,7 @@ export interface DepositEvent {
tx: string;
blockNumber: number;
blockHash: string;
logIndex: number;
}

export interface VerifiedDepositEvent extends DepositEvent {
Expand Down
105 changes: 104 additions & 1 deletion src/guardian/staking-module-guard/staking-module-guard.service.ts
Expand Up @@ -111,6 +111,107 @@ export class StakingModuleGuardService {
);
}

isFirstEventEarlier(
firstEvent: VerifiedDepositEvent,
secondEvent: VerifiedDepositEvent,
) {
const isSameBlock = firstEvent?.blockNumber === secondEvent.blockNumber;

let isFirstEventEarlier = false;

if (isSameBlock) {
isFirstEventEarlier = firstEvent?.logIndex < secondEvent.logIndex;
} else {
isFirstEventEarlier = firstEvent?.blockNumber < secondEvent.blockNumber;
}

return isFirstEventEarlier;
}
/**
* Method is not taking into account WC rotation since historical deposits were checked manually
* @param blockData
* @returns
*/
async getHistoricalFrontRun(blockData: BlockData) {
const { depositedEvents, lidoWC } = blockData;
const potentialLidoDepositsEvents = depositedEvents.events.filter(
({ wc, valid }) => wc === lidoWC && valid,
);

this.logger.log('potential lido deposits events count', {
count: potentialLidoDepositsEvents.length,
});

const potentialLidoDepositsKeysMap: Record<string, VerifiedDepositEvent> =
{};

potentialLidoDepositsEvents.forEach((event) => {
if (potentialLidoDepositsKeysMap[event.pubkey]) {
const existed = potentialLidoDepositsKeysMap[event.pubkey];
const isExisted = this.isFirstEventEarlier(existed, event);
// this should not happen, since Lido deposits once per key.
// but someone can still make such a deposit.
if (isExisted) return;
}
potentialLidoDepositsKeysMap[event.pubkey] = event;
});

const duplicatedDepositEvents: VerifiedDepositEvent[] = [];

depositedEvents.events.forEach((event) => {
if (potentialLidoDepositsKeysMap[event.pubkey] && event.wc !== lidoWC) {
duplicatedDepositEvents.push(event);
}
});

this.logger.log('duplicated deposit events', {
count: duplicatedDepositEvents.length,
});

const validDuplicatedDepositEvents = duplicatedDepositEvents.filter(
(event) => event.valid,
);

this.logger.log('valid duplicated deposit events', {
count: validDuplicatedDepositEvents.length,
});

const frontRunnedDepositEvents = validDuplicatedDepositEvents.filter(
(suspectedEvent) => {
// get event from lido map
const sameKeyLidoDeposit =
potentialLidoDepositsKeysMap[suspectedEvent.pubkey];

if (!sameKeyLidoDeposit) throw new Error('expected event not found');

return this.isFirstEventEarlier(suspectedEvent, sameKeyLidoDeposit);
},
);

this.logger.log('front runned deposit events', {
events: frontRunnedDepositEvents,
});

const frontRunnedDepositKeys = frontRunnedDepositEvents.map(
({ pubkey }) => pubkey,
);

if (!frontRunnedDepositKeys.length) {
return false;
}

const lidoDepositedKeys = await this.stakingRouterService.getKeysByPubkeys(
frontRunnedDepositKeys,
);

const isLidoDepositedKeys = lidoDepositedKeys.data.length;

if (isLidoDepositedKeys) {
this.logger.warn('historical front-run found');
}

return isLidoDepositedKeys;
}
/**
* Checks keys for intersections with previously deposited keys and handles the situation
* @param blockData - collected data from the current block
Expand Down Expand Up @@ -143,6 +244,8 @@ export class StakingModuleGuardService {
keysIntersections,
filteredIntersections,
);
// TODO: add metrics for getHistoricalFrontRun same as for keysIntersections
const historicalFrontRunFound = await this.getHistoricalFrontRun(blockData);

const isDepositsPaused = await this.securityService.isDepositsPaused(
stakingModuleData.stakingModuleId,
Expand All @@ -156,7 +259,7 @@ export class StakingModuleGuardService {
return;
}

if (isFilteredIntersectionsFound) {
if (isFilteredIntersectionsFound || historicalFrontRunFound) {
await this.handleKeysIntersections(stakingModuleData, blockData);
} else {
// it could throw error if kapi returned old data
Expand Down
14 changes: 14 additions & 0 deletions src/guardian/staking-module-guard/staking-module-guard.spec.ts
Expand Up @@ -86,6 +86,20 @@ describe('StakingModuleGuardService', () => {
jest.spyOn(loggerService, 'log').mockImplementation(() => undefined);
jest.spyOn(loggerService, 'warn').mockImplementation(() => undefined);
jest.spyOn(loggerService, 'debug').mockImplementation(() => undefined);

jest
.spyOn(stakingRouterService, 'getKeysByPubkeys')
.mockImplementation(async () => ({
data: [],
meta: {
elBlockSnapshot: {
blockNumber: 0,
blockHash: 'hash',
timestamp: 12345,
lastChangedBlockHash: 'lastHash',
},
},
}));
});

describe('getKeysIntersections', () => {
Expand Down
2 changes: 2 additions & 0 deletions test/manifest.e2e-spec.ts
Expand Up @@ -275,6 +275,7 @@ describe('ganache e2e tests', () => {
tx: '0x123',
blockHash: forkBlock.hash,
blockNumber: forkBlock.number,
logIndex: 1,
},
],
headers: {
Expand Down Expand Up @@ -425,6 +426,7 @@ describe('ganache e2e tests', () => {
tx: '0x123',
blockHash: forkBlock.hash,
blockNumber: forkBlock.number,
logIndex: 1,
},
],
headers: {
Expand Down

0 comments on commit b94bd01

Please sign in to comment.