Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add validator exit verifier contract #976

Draft
wants to merge 5 commits into
base: feat/val-1456-tw-vebo-part
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
472 changes: 472 additions & 0 deletions contracts/0.8.25/ValidatorExitVerifier.sol

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions contracts/0.8.25/interfaces/IStakingRouter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: 2024 Lido <[email protected]>
// SPDX-License-Identifier: GPL-3.0

// See contracts/COMPILERS.md
pragma solidity 0.8.25;

interface IStakingRouter {
function reportUnexitedValidator(
uint256 moduleId,
uint256 nodeOperatorId,
bytes calldata publicKey,
uint256 secondsSinceEligibleExitRequest
) external;
}
23 changes: 23 additions & 0 deletions contracts/0.8.25/interfaces/IValidatorsExitBusOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: 2025 Lido <[email protected]>
// SPDX-License-Identifier: GPL-3.0

// See contracts/COMPILERS.md
pragma solidity 0.8.25;

struct DeliveryHistory {
uint64 timestamp;
// Key index in exit request array
uint256 lastDeliveredKeyIndex;
}

struct RequestStatus {
uint256 totalItemsCount;
uint256 deliveredItemsCount;
uint256 reportDataFormat;
uint256 contractVersion;
DeliveryHistory[] deliveryHistory;
}

interface IValidatorsExitBusOracle {
function getExitRequestsStatus(bytes32 exitRequestsHash) external view returns (RequestStatus memory);
}
117 changes: 117 additions & 0 deletions contracts/0.8.25/lib/GIndex.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// SPDX-FileCopyrightText: 2024 Lido <[email protected]>
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.25;

type GIndex is bytes32;

using {isRoot, isParentOf, index, width, shr, shl, concat, unwrap, pow} for GIndex global;

error IndexOutOfRange();

/// @param gI Is a generalized index of a node in a tree.
/// @param p Is a power of a tree level the node belongs to.
/// @return GIndex
function pack(uint256 gI, uint8 p) pure returns (GIndex) {
if (gI > type(uint248).max) {
revert IndexOutOfRange();
}

// NOTE: We can consider adding additional metadata like a fork version.
return GIndex.wrap(bytes32((gI << 8) | p));
}

function unwrap(GIndex self) pure returns (bytes32) {
return GIndex.unwrap(self);
}

function isRoot(GIndex self) pure returns (bool) {
return index(self) == 1;
}

function index(GIndex self) pure returns (uint256) {
return uint256(unwrap(self)) >> 8;
}

function width(GIndex self) pure returns (uint256) {
return 1 << pow(self);
}

function pow(GIndex self) pure returns (uint8) {
return uint8(uint256(unwrap(self)));
}

/// @return Generalized index of the nth neighbor of the node to the right.
function shr(GIndex self, uint256 n) pure returns (GIndex) {
uint256 i = index(self);
uint256 w = width(self);

if ((i % w) + n >= w) {
revert IndexOutOfRange();
}

return pack(i + n, pow(self));
}

/// @return Generalized index of the nth neighbor of the node to the left.
function shl(GIndex self, uint256 n) pure returns (GIndex) {
uint256 i = index(self);
uint256 w = width(self);

if (i % w < n) {
revert IndexOutOfRange();
}

return pack(i - n, pow(self));
}

// See https://github.com/protolambda/remerkleable/blob/91ed092d08ef0ba5ab076f0a34b0b371623db728/remerkleable/tree.py#L46
function concat(GIndex lhs, GIndex rhs) pure returns (GIndex) {
uint256 lhsMSbIndex = fls(index(lhs));
uint256 rhsMSbIndex = fls(index(rhs));

if (lhsMSbIndex + 1 + rhsMSbIndex > 248) {
revert IndexOutOfRange();
}

return pack((index(lhs) << rhsMSbIndex) | (index(rhs) ^ (1 << rhsMSbIndex)), pow(rhs));
}

function isParentOf(GIndex self, GIndex child) pure returns (bool) {
uint256 parentIndex = index(self);
uint256 childIndex = index(child);

if (parentIndex >= childIndex) {
return false;
}

while (childIndex > 0) {
if (childIndex == parentIndex) {
return true;
}

childIndex = childIndex >> 1;
}

return false;
}

/// @dev From Solady LibBit, see https://github.com/Vectorized/solady/blob/main/src/utils/LibBit.sol.
/// @dev Find last set.
/// Returns the index of the most significant bit of `x`,
/// counting from the least significant bit position.
/// If `x` is zero, returns 256.
function fls(uint256 x) pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
// prettier-ignore
r := or(shl(8, iszero(x)), shl(7, lt(0xffffffffffffffffffffffffffffffff, x)))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// prettier-ignore
r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0x0706060506020504060203020504030106050205030304010505030400000000))
}
}
Loading
Loading