-
Notifications
You must be signed in to change notification settings - Fork 92
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
refactor: quorum creation is the divine right of the registry coordinator #28
Conversation
uint96 stake; | ||
} | ||
|
||
// EVENTS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2 event sections should be made 1
…te if last update was in current block
generally i just removed testing of specific stake histories in favor of testing net outcomes. we can revisit these tests in a few weeks.
596f20f
to
b4709cb
Compare
… naming and logic
…c and comments for deregistration
…ry. also fix tests
... which means pubkey parameters for various functions/structs can be removed
…t with other registry contracts
|
||
unchecked { | ||
++i; | ||
} | ||
} | ||
} | ||
|
||
// TODO - should this fail if apkUpdate.apkHash == 0? This will be the case for the first entry in each quorum |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good question. relatedly, should we actually push the hash of the zero point in the first update, rather than zero per se?
I think this probably shouldn't revert in this case, but am not strongly opinionated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question for @gpsanant
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good point, let's push the zero hash
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so to confirm, the change is to push the zero hash for the first update, and have this method fail if apkHash == 0
?
Should it also fail if apkHash == ZERO_HASH
?
src/IndexRegistry.sol
Outdated
_indexToOperatorIdHistory[quorumNumber][index].push(OperatorUpdate({ | ||
operatorId: operatorId, | ||
fromBlockNumber: uint32(block.number) | ||
})); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's weird that elsewhere we use 24 bytes (as in the BLSPubkeyRegistry) for the equivalent of operatorId, and here we use 32 bytes. Not sure why this line in particular made me notice this, but it's just...a bit odd?
should operatorIds perhaps be shorter, so we can do the same storage packing everywhere?
// TODO - not a huge fan of this dependency on the reg coord, but i do prefer this | ||
// over the stakereg also keeping its own count. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this makes sense. it's a bit ugly, but in my mind the registries are basically all "puppets" of the RegistryCoordinator, so calling into the coordinator to fetch a bit of data is fine.
I wouldn't be super opposed to just keeping count in the StakeRegistry either, as it'd be more efficient -- we could just increment a quorumCount
variable in the initializeQuorum()
function? Then the two can only be out-of-sync if the RegistryCoordinator either fails to call initializeQuorum
appropriately or calls it inappropriately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is marked TODO because I was intending a future PR to move updateStakes
into the registry coordinator. I'm working on that locally and will have something to share next week
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ahhh OK that works!
* @dev This function has no check to make sure that the strategies for a single quorum have the same underlying asset. This is a concious choice, | ||
* since a middleware may want, e.g., a stablecoin quorum that accepts USDC, USDT, DAI, etc. as underlying assets and trades them as "equivalent". |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realize these probably weren't part of this PR (it probably just moves the comments?) but:
typo: 'concious' => 'conscious'
and I think 'trades' should be 'treats'
src/StakeRegistry.sol
Outdated
/** | ||
* @notice Remove strategies and their associated weights from the quorum's considered strategies | ||
* @dev higher indices should be *first* in the list of @param indicesToRemove, since otherwise | ||
* the removal of lower index entries will cause a shift in the indices of the other strategies to remove | ||
*/ | ||
function removeStrategies( | ||
uint8 quorumNumber, | ||
uint256[] memory indicesToRemove | ||
) public virtual onlyServiceManagerOwner quorumExists(quorumNumber) { | ||
uint256 toRemoveLength = indicesToRemove.length; | ||
require(toRemoveLength > 0, "StakeRegistry.removeStrategyParams: no indices to remove provided"); | ||
|
||
StrategyAndWeightingMultiplier[] storage strategyParams = strategiesConsideredAndMultipliers[quorumNumber]; | ||
|
||
for (uint256 i = 0; i < toRemoveLength; i++) { | ||
emit StrategyRemovedFromQuorum(quorumNumber, strategyParams[indicesToRemove[i]].strategy); | ||
emit StrategyMultiplierUpdated(quorumNumber, strategyParams[indicesToRemove[i]].strategy, 0); | ||
|
||
// Replace index to remove with the last item in the list, then pop the last item | ||
strategyParams[indicesToRemove[i]] = strategyParams[strategyParams.length - 1]; | ||
strategyParams.pop(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wadealexc thoughts on the safety of this function?
it previously had a redundant parameter which it checked against, to ensure that the desired strategies were removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What redundant parameter? Here's the master branch: https://github.com/Layr-Labs/eigenlayer-middleware/blob/master/src/VoteWeigherBase.sol#L62
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I havent given any thought to the safety of the method... I think that'll come when we start working on integration tests. Essentially we should be testing that stake updates occur as expected even if we interweave strategy modifications in between updates.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry, wasn't related to this PR, probably should have asked elsewhere, but here's a link to the previous behavior I was thinking of https://github.com/Layr-Labs/eigenlayer-contracts/blob/bd211f12fe8ed84f691ca44db3f812412defcb1e/src/contracts/middleware/VoteWeigherBase.sol#L134-L138
this could be described as an "idiot-check", but I do have a feeling it'd be easy to call this function "incorrectly" (or at least, in a way that doesn't have the desired outcome) with the way it works now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gotcha. i think there's a footgun here if the method is called incorrectly, but since this is an admin function, i think it's ok. footguns inherently exist when performing "irregular state transitions" like this, and i'd rather keep the methods clean so it's clear what's going on - rather than adding checks that correct for mistakes we make off chain.
worst case we realize we did this incorrectly and we can pause/correct the mistake. but i think any time we call these we should do our due diligence and feel confident that we're doing it correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feels like a good change overall.
I made some smaller comments but don't think I have any major points.
Am in agreement with the small open items noted by @gpsanant
…nt with corresponding state lookup
refactor: simplify naming and logic in registries and registry coordinator
refactor: pubkey compendium stores pubkeys
Motivation:
RegistryCoordinator.createQuorum
and its associatedinitializeQuorum
methods in each registryThis PR:
createQuorum
to the registry coordinator, and adds quorum initialization logic for each registry contractinitializeQuorum
method)BLSPublicKeyCompendium
:operatorToPubkey
, which maps operator addresses toG1Point
BLSPublicKeyCompendium.getRegisteredPubkey(address)
which performs a pubkey lookup along with validation that the pubkey exists/isn't the zero pubkey.BLSPubkeyRegistry
BLSPubkeyRegistry
andBLSRegistryCoordinatorWithIndices
. The pubkey registry is able to look up pubkeys with just an operator address, now.For reviewers, this order may work best:
IndexRegistry
changes in the first commit, which contains the functional changes mentioned above, as well as readability/logical simplification. This is the worst commit to review, sorry! The diff looks intimidating, so I'd recommend opening the new/old IndexRegistry files side by side and comparing like that. 🙏Renaming changes:
IndexRegistry
state vars:operatorIdToIndex
->currentOperatorIndex
_totalOperatorsHistory
->_operatorCountHistory
_indexToOperatorIdHistory
->_indexHistory
IndexRegistry
functions:_getTotalOperatorsForQuorumAtBlockNumber
->_operatorCountAtBlockNumber
_getOperatorIdAtIndexForQuorumAtBlockNumber
->_operatorIdForIndexAtBlockNumber
getOperatorIndexUpdateOfIndexForQuorumAtIndex
->getOperatorUpdateAtIndex
getTotalOperatorsForQuorumAtBlockNumberByIndex
->getTotalOperatorsForIndexAtBlockNumber
getOperatorListForQuorumAtBlockNumber
->getOperatorListAtBlockNumber
StakeRegistry
state vars:operatorIdToStakeHistory
->operatorStakeHistory
strategiesConsideredAndMultipliers
->strategyParams
StakeRegistry
structs:StrategyAndWeightingMultiplier
->StrategyParams
StakeRegistry
functions:strategiesConsideredAndMultipliersLength
->strategyParamsLength
strategyAndWeightingMultiplierForQuorumByIndex
->strategyParamsByIndex
getOperatorIdToStakeHistory
->getOperatorStakeHistory
getLengthOfTotalStakeHistoryForQuorum
->getTotalStakeHistoryLength
getTotalStakeUpdateForQuorumFromIndex
->getTotalStakeUpdateAtIndex
getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber
->getStakeUpdateIndexForOperatorAtBlockNumber
getTotalStakeIndicesByQuorumNumbersAtBlockNumber
->getTotalStakeIndicesAtBlockNumber
getStakeUpdateForQuorumFromOperatorIdAndIndex
->getStakeUpdateForOperatorAtIndex
getStakeForQuorumAtBlockNumberFromOperatorIdAndIndex
->getOperatorStakeAtBlockNumberAndIndex
getStakeForOperatorIdForQuorumAtBlockNumber
->getOperatorStakeAtBlockNumber
getLengthOfOperatorIdStakeHistoryForQuorum
->getOperatorStakeHistoryLength
BLSRegistryCoordinatorWithIndices
state vars:_quorumOperatorSetParams
->_quorumParams
_operatorIdToQuorumBitmapHistory
->_operatorBitmapHistory
_operators
->_operatorInfo
BLSRegistryCoordinatorWithIndices
state modifying functions:registerOperatorWithCoordinator
->registerOperator
registerOperatorWithCoordinator
->registerOperatorWithChurn
deregisterOperatorWithCoordinator
->deregisterOperator
ejectOperatorFromCoordinator
->ejectOperator
BLSRegistryCoordinatorWithIndices
view functions:getQuorumBitmapIndicesByOperatorIdsAtBlockNumber
->getQuorumBitmapIndicesAtBlockNumber
getQuorumBitmapByOperatorIdAtBlockNumberByIndex
->getQuorumBitmapAtBlockNumberByIndex
getQuorumBitmapUpdateByOperatorIdByIndex
->getQuorumBitmapUpdateByIndex
getCurrentQuorumBitmapByOperatorId
->getCurrentQuorumBitmap
getQuorumBitmapUpdateByOperatorIdLength
->getQuorumBitmapHistoryLength
BLSPubkeyRegistry
state variable naming changes (these better match the rest of the contracts):quorumApk
->currentApk
quorumApkUpdates
->apkHistory
BLSPubkeyRegistry
view function naming changes:getApkIndicesForQuorumsAtBlockNumber
->getApkIndicesAtBlockNumber
getApkForQuorum
->getApk
getApkUpdateForQuorumByIndex
->getApkUpdateAtIndex
getApkHashForQuorumAtBlockNumberFromIndex
->getApkHashAtBlockNumberAndIndex
getQuorumApkHistoryLength
->getApkHistoryLength
IndexRegistry
changes:registerOperator
is now_increaseOperatorCount
+_assignOperatorToIndex
. Functional changes:QuorumUpdate/OperatorUpdate
is from this block, we update the last entry rather than pushing a new one.deregisterOperator
is now_decreaseOperatorCount
+_popLastOperator
+_assignOperatorToIndex
. Functional changes:currentOperatorIndex[quorum][OPERATOR_DOES_NOT_EXIST_ID]
to the removed index, and emitted an event for this update. This logic has been removed, since thecurrentOperatorIndex
of the null id doesn't matter. See previous logic vs new logic