Skip to content

Commit

Permalink
add view functions, refactors
Browse files Browse the repository at this point in the history
  • Loading branch information
mtabasco committed Apr 23, 2024
1 parent 03e1937 commit 1cadb43
Show file tree
Hide file tree
Showing 9 changed files with 195 additions and 119 deletions.
12 changes: 8 additions & 4 deletions contracts/SSVNetwork.sol
Original file line number Diff line number Diff line change
Expand Up @@ -138,20 +138,24 @@ contract SSVNetwork is
_delegate(SSVStorage.load().ssvContracts[SSVModules.SSV_OPERATORS_WHITELIST]);
}

function setOperatorsWhitelistingContract(
function removeOperatorMultipleWhitelists(
uint64[] calldata operatorIds,
ISSVWhitelistingContract whitelistingContract
address[] calldata whitelistAddresses
) external override {
_delegate(SSVStorage.load().ssvContracts[SSVModules.SSV_OPERATORS_WHITELIST]);
}

function removeOperatorMultipleWhitelists(
function setOperatorsWhitelistingContract(
uint64[] calldata operatorIds,
address[] calldata whitelistAddresses
ISSVWhitelistingContract whitelistingContract
) external override {
_delegate(SSVStorage.load().ssvContracts[SSVModules.SSV_OPERATORS_WHITELIST]);
}

function removeOperatorsWhitelistingContract(uint64[] calldata operatorIds) external override {
_delegate(SSVStorage.load().ssvContracts[SSVModules.SSV_OPERATORS_WHITELIST]);
}

function declareOperatorFee(uint64 operatorId, uint256 fee) external override {
_delegate(SSVStorage.load().ssvContracts[SSVModules.SSV_OPERATORS]);
}
Expand Down
7 changes: 7 additions & 0 deletions contracts/SSVNetworkViews.sol
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ contract SSVNetworkViews is UUPSUpgradeable, Ownable2StepUpgradeable, ISSVViews
return ssvNetwork.getOperatorById(operatorId);
}

function getWhitelistedOperators(
uint64[] calldata operatorIds,
address whitelistedAddress
) external view override returns (uint64[] memory whitelistedOperatorIds) {
return ssvNetwork.getWhitelistedOperators(operatorIds, whitelistedAddress);
}

/***********************************/
/* Cluster External View Functions */
/***********************************/
Expand Down
4 changes: 4 additions & 0 deletions contracts/interfaces/ISSVOperatorsWhitelist.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ interface ISSVOperatorsWhitelist is ISSVNetworkCore {
/// @param whitelistingContract The address of a whitelisting contract
function setOperatorsWhitelistingContract(uint64[] calldata operatorIds, ISSVWhitelistingContract whitelistingContract) external;

/// @notice Removes the whitelisting contract set for a list of operators
/// @param operatorIds The operator IDs to remove the whitelisting contract for
function removeOperatorsWhitelistingContract(uint64[] calldata operatorIds) external;

/**
* @dev Emitted when the whitelist of an operator is updated.
* @param operatorId operator's ID.
Expand Down
22 changes: 19 additions & 3 deletions contracts/interfaces/ISSVViews.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,31 @@ interface ISSVViews is ISSVNetworkCore {
/// @return owner The owner of the operator
/// @return fee The fee associated with the operator (SSV)
/// @return validatorCount The count of validators associated with the operator
/// @return whitelisted The whitelisted address of the operator, if any
/// @return isPrivate A boolean indicating if the operator is private
/// @return whitelistedContract The whitelisted contract address of the operator, if any
/// @return useWhitelistedContract A boolean indicating if the operator uses a whitelisted contract
/// @return active A boolean indicating if the operator is active
function getOperatorById(
uint64 operatorId
)
external
view
returns (address owner, uint256 fee, uint32 validatorCount, address whitelisted, bool isPrivate, bool active);
returns (
address owner,
uint256 fee,
uint32 validatorCount,
address whitelistedContract,
bool useWhitelistedContract,
bool active
);

/// @notice Gets the list of operators that have the given whitelisted address (EOA or generic contract)
/// @param operatorIds The list of operator IDs to check
/// @param whitelistedAddress The address whitelisted for the operators
/// @return whitelistedOperatorIds The list of operator IDs that have the given whitelisted address
function getWhitelistedOperators(
uint64[] calldata operatorIds,
address whitelistedAddress
) external view returns (uint64[] memory whitelistedOperatorIds);

/// @notice Checks if the cluster can be liquidated
/// @param owner The owner address of the cluster
Expand Down
72 changes: 38 additions & 34 deletions contracts/libraries/OperatorLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,6 @@ library OperatorLib {
}
}

function getBitmapIndexes(uint64 operatorId) internal pure returns (uint256 blockIndex, uint256 bitPosition) {
blockIndex = operatorId >> 8; // Equivalent to operatorId / 256
bitPosition = operatorId & 0xFF; // Equivalent to operatorId % 256
}

function updateMultipleWhitelists(
address[] calldata whitelistAddresses,
uint64[] calldata operatorIds,
Expand All @@ -129,41 +124,15 @@ library OperatorLib {
if (addressesLength == 0) revert ISSVNetworkCore.InvalidWhitelistAddressesLength();
if (operatorsLength == 0) revert ISSVNetworkCore.InvalidOperatorIdsLength();

uint64 currentOperatorId;
uint64 nextOperatorId;
uint256 blockIndex;
uint256 bitPosition;

// create the max number of masks that will be updated
uint256[] memory masks = new uint256[]((operatorIds[operatorsLength - 1] >> 8) + 1);

for (uint256 i = 0; i < operatorsLength - 1; ++i) {
currentOperatorId = operatorIds[i];

checkOwner(s.operators[currentOperatorId]);

if (i + 1 < operatorsLength) {
nextOperatorId = operatorIds[i + 1];
if (currentOperatorId >= nextOperatorId) {
if (currentOperatorId == nextOperatorId) {
revert ISSVNetworkCore.OperatorsListNotUnique();
}
revert ISSVNetworkCore.UnsortedOperatorsList();
}
}
(blockIndex, bitPosition) = getBitmapIndexes(currentOperatorId);

masks[blockIndex] |= (1 << bitPosition);

if (!s.operators[currentOperatorId].whitelisted) s.operators[currentOperatorId].whitelisted = true;
}
uint256[] memory masks = generateBlockMasks(operatorIds);

for (uint256 i = 0; i < addressesLength; ++i) {
address addr = whitelistAddresses[i];

if (isWhitelistingContract(addr)) revert ISSVNetworkCore.AddressIsWhitelistingContract(addr);

for (blockIndex = 0; blockIndex < masks.length; ++blockIndex) {
for (uint256 blockIndex; blockIndex < masks.length; ++blockIndex) {
// only update storage for updated masks
if (masks[blockIndex] != 0) {
if (addAddresses) {
Expand All @@ -190,14 +159,49 @@ library OperatorLib {
(uint256 blockIndex, uint256 bitPosition) = OperatorLib.getBitmapIndexes(operatorId);
delete s.operatorsWhitelist[operatorId];
s.addressWhitelistedForOperators[currentWhitelisted][blockIndex] |= (1 << bitPosition);
} else {
s.operators[operatorId].whitelisted = true;
}

s.operatorsWhitelist[operatorId] = address(whitelistingContract);
if (!s.operators[operatorId].whitelisted) s.operators[operatorId].whitelisted = true;
}

function getBitmapIndexes(uint64 operatorId) internal pure returns (uint256 blockIndex, uint256 bitPosition) {
blockIndex = operatorId >> 8; // Equivalent to operatorId / 256
bitPosition = operatorId & 0xFF; // Equivalent to operatorId % 256
}

function isWhitelistingContract(address whitelistingContract) internal view returns (bool) {
// TODO create type for whitelisting contracts?
return ERC165Checker.supportsInterface(whitelistingContract, type(ISSVWhitelistingContract).interfaceId);
}

function generateBlockMasks(uint64[] calldata operatorIds) internal pure returns (uint256[] memory masks) {
uint256 blockIndex;
uint256 bitPosition;

uint256 operatorsLength = operatorIds.length;

// create the max number of masks that will be updated
masks = new uint256[]((operatorIds[operatorsLength - 1] >> 8) + 1);

for (uint256 i = 0; i < operatorsLength; ++i) {
/* check if its not required to pass ordered operator ids
if (checkOwner) checkOwner(s.operators[currentOperatorId]);
if (i + 1 < operatorsLength) {
nextOperatorId = operatorIds[i + 1];
if (currentOperatorId >= nextOperatorId) {
if (currentOperatorId == nextOperatorId) {
revert ISSVNetworkCore.OperatorsListNotUnique();
}
revert ISSVNetworkCore.UnsortedOperatorsList();
}
}
*/
(blockIndex, bitPosition) = getBitmapIndexes(operatorIds[i]);

masks[blockIndex] |= (1 << bitPosition);
}
}
}
21 changes: 20 additions & 1 deletion contracts/modules/SSVOperatorsWhitelist.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ contract SSVOperatorsWhitelist is ISSVOperatorsWhitelist {
(uint256 blockIndex, uint256 bitPosition) = OperatorLib.getBitmapIndexes(operatorId);

s.addressWhitelistedForOperators[whitelistAddress][blockIndex] |= (1 << bitPosition);
if (!s.operators[operatorId].whitelisted) s.operators[operatorId].whitelisted = true;

emit OperatorWhitelistUpdated(operatorId, whitelistAddress);
}
Expand Down Expand Up @@ -62,4 +61,24 @@ contract SSVOperatorsWhitelist is ISSVOperatorsWhitelist {
// TODO test set event param type to ISSVOperatorsWhitelist
emit OperatorWhitelistingContractUpdated(operatorIds, address(whitelistingContract));
}

function removeOperatorsWhitelistingContract(uint64[] calldata operatorIds) external {
uint256 operatorsLength = operatorIds.length;
if (operatorsLength == 0) revert InvalidOperatorIdsLength();

StorageData storage s = SSVStorage.load();

Operator memory operator;
for (uint256 i = 0; i < operatorsLength; ++i) {
operator = s.operators[operatorIds[i]];

operator.checkOwner();
operator.whitelisted = false;

s.operatorsWhitelist[operatorIds[i]] = address(0);
s.operators[operatorIds[i]] = operator;
}

emit OperatorWhitelistingContractUpdated(operatorIds, address(0));
}
}
64 changes: 60 additions & 4 deletions contracts/modules/SSVViews.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,69 @@ contract SSVViews is ISSVViews {
);
}

function getOperatorById(uint64 operatorId) external view returns (address, uint256, uint32, address, bool, bool) {
function getOperatorById(
uint64 operatorId
) external view override returns (address, uint256, uint32, address, bool, bool) {
ISSVNetworkCore.Operator memory operator = SSVStorage.load().operators[operatorId];
address whitelisted = SSVStorage.load().operatorsWhitelist[operatorId];
bool isPrivate = whitelisted == address(0) ? false : true;

address whitelistedContract = SSVStorage.load().operatorsWhitelist[operatorId];
bool useWhitelistedContract = OperatorLib.isWhitelistingContract(whitelistedContract);
bool isActive = operator.snapshot.block == 0 ? false : true;

return (operator.owner, operator.fee.expand(), operator.validatorCount, whitelisted, isPrivate, isActive);
return (
operator.owner,
operator.fee.expand(),
operator.validatorCount,
whitelistedContract,
useWhitelistedContract,
isActive
);
}

function getWhitelistedOperators(
uint64[] calldata operatorIds,
address whitelistedAddress
) external view override returns (uint64[] memory whitelistedOperatorIds) {
uint256 operatorsLength = operatorIds.length;
if (operatorsLength == 0) return whitelistedOperatorIds;

StorageData storage s = SSVStorage.load();

// create the max number of masks that will be updated
uint256[] memory masks = OperatorLib.generateBlockMasks(operatorIds);

uint256 count = 0;
whitelistedOperatorIds = new uint64[](operatorsLength);

uint256 whitelistedMask;
uint256 matchedMask;

// Check whitelisting status for each mask
for (uint256 blockIndex; blockIndex < masks.length; ++blockIndex) {
// Only check blocks that have operator IDs
if (masks[blockIndex] != 0) {
whitelistedMask = s.addressWhitelistedForOperators[whitelistedAddress][blockIndex];

// This will give the matching whitelisted operators
matchedMask = whitelistedMask & masks[blockIndex];

// Now we need to extract operator IDs from matchedMask
uint256 blockPointer = blockIndex << 8;
for (uint256 bit = 0; bit < 256; bit++) {
if (matchedMask & (1 << bit) != 0) {
whitelistedOperatorIds[count++] = uint64(blockPointer + bit);
if (count == operatorsLength) {
return whitelistedOperatorIds; // Early termination
}
}
}
}
}

// Resize whitelistedOperatorIds to the actual number of whitelisted operators
assembly {
mstore(whitelistedOperatorIds, count)
}
}

/***********************************/
Expand Down
Loading

0 comments on commit 1cadb43

Please sign in to comment.