Skip to content

Commit

Permalink
Merge branch 'dev' into fix_bls_aggregation_multiple_quorums
Browse files Browse the repository at this point in the history
  • Loading branch information
pablodeymo authored Nov 21, 2024
2 parents 4e9c54f + 27225c8 commit 63ec01f
Show file tree
Hide file tree
Showing 10 changed files with 1,300 additions and 1,494 deletions.
66 changes: 0 additions & 66 deletions chainio/clients/elcontracts/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -356,69 +356,3 @@ func (w *ChainWriter) ProcessClaim(

return receipt, nil
}

func (w *ChainWriter) ForceDeregisterFromOperatorSets(
ctx context.Context,
operator gethcommon.Address,
avs gethcommon.Address,
operatorSetIds []uint32,
operatorSignature avsdirectory.ISignatureUtilsSignatureWithSaltAndExpiry,
waitForReceipt bool,
) (*gethtypes.Receipt, error) {
if w.avsDirectory == nil {
return nil, errors.New("AVSDirectory contract not provided")
}

noSendTxOpts, err := w.txMgr.GetNoSendTxOpts()
if err != nil {
return nil, utils.WrapError("failed to get no send tx opts", err)
}

tx, err := w.avsDirectory.ForceDeregisterFromOperatorSets(
noSendTxOpts,
operator,
avs,
operatorSetIds,
operatorSignature,
)

if err != nil {
return nil, utils.WrapError("failed to create ForceDeregisterFromOperatorSets tx", err)
}

receipt, err := w.txMgr.Send(ctx, tx, waitForReceipt)
if err != nil {
return nil, utils.WrapError("failed to send tx", err)
}

return receipt, nil
}

func (w *ChainWriter) SetOperatorCommissionBips(
ctx context.Context,
operatorSet rewardscoordinator.IAVSDirectoryOperatorSet,
rewardType uint8,
commissionBips uint16,
waitForReceipt bool,
) (*gethtypes.Receipt, error) {
if w.rewardsCoordinator == nil {
return nil, errors.New("RewardsCoordinator contract not provided")
}

noSendTxOpts, err := w.txMgr.GetNoSendTxOpts()
if err != nil {
return nil, utils.WrapError("failed to get no send tx opts", err)
}

tx, err := w.rewardsCoordinator.SetOperatorCommissionBips(noSendTxOpts, operatorSet, rewardType, commissionBips)
if err != nil {
return nil, utils.WrapError("failed to create SetOperatorCommissionBips tx", err)
}

receipt, err := w.txMgr.Send(ctx, tx, waitForReceipt)
if err != nil {
return nil, utils.WrapError("failed to send tx", err)
}

return receipt, nil
}
2 changes: 1 addition & 1 deletion contracts/bindings/DelegationManager/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion contracts/bindings/EigenPod/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion contracts/bindings/EigenPodManager/binding.go

Large diffs are not rendered by default.

1,154 changes: 35 additions & 1,119 deletions contracts/bindings/IAVSDirectory/binding.go

Large diffs are not rendered by default.

926 changes: 682 additions & 244 deletions contracts/bindings/IRewardsCoordinator/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion contracts/bindings/StrategyManager/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion contracts/lib/eigenlayer-middleware
Submodule eigenlayer-middleware updated 38 files
+1 −1 lib/eigenlayer-contracts
+13 −6 src/EjectionManager.sol
+174 −255 src/RegistryCoordinator.sol
+6 −1 src/RegistryCoordinatorStorage.sol
+112 −180 src/ServiceManagerBase.sol
+0 −2 src/ServiceManagerBaseStorage.sol
+52 −0 src/SocketRegistry.sol
+1 −1 src/interfaces/IEjectionManager.sol
+8 −0 src/interfaces/IRegistryCoordinator.sol
+30 −3 src/interfaces/IServiceManager.sol
+10 −0 src/interfaces/ISocketRegistry.sol
+0 −20 src/interfaces/ISocketUpdater.sol
+0 −61 src/libraries/LibMergeSort.sol
+0 −27 src/libraries/SignatureCheckerLib.sol
+73 −0 src/unaudited/ECDSAServiceManagerBase.sol
+70 −11 test/events/IServiceManagerBaseEvents.sol
+0 −68 test/harnesses/AVSDirectoryHarness.sol
+3 −2 test/harnesses/RegistryCoordinatorHarness.t.sol
+2 −1 test/integration/CoreRegistration.t.sol
+19 −31 test/integration/IntegrationDeployer.t.sol
+1 −1 test/integration/User.t.sol
+0 −21 test/integration/mocks/BeaconChainOracleMock.t.sol
+16 −15 test/integration/utils/Sort.t.sol
+9 −169 test/mocks/AVSDirectoryMock.sol
+27 −21 test/mocks/DelegationMock.sol
+1 −8 test/mocks/ECDSAServiceManagerMock.sol
+2 −0 test/mocks/RegistryCoordinatorMock.sol
+85 −61 test/mocks/RewardsCoordinatorMock.sol
+2 −1 test/unit/BLSSignatureCheckerUnit.t.sol
+2 −2 test/unit/EjectionManagerUnit.t.sol
+0 −188 test/unit/LibMergeSort.t.sol
+4 −4 test/unit/OperatorStateRetrieverUnit.t.sol
+0 −200 test/unit/RegistryCoordinatorMigration.t.sol
+30 −30 test/unit/RegistryCoordinatorUnit.t.sol
+646 −97 test/unit/ServiceManagerBase.t.sol
+0 −347 test/unit/ServiceManagerMigration.t.sol
+2 −1 test/unit/StakeRegistryUnit.t.sol
+19 −4 test/utils/MockAVSDeployer.sol
221 changes: 161 additions & 60 deletions services/bls_aggregation/blsagg.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,13 @@ type aggregatedOperators struct {
// BlsAggregationService is the interface provided to avs aggregator code for doing bls aggregation
// Currently its only implementation is the BlsAggregatorService, so see the comment there for more details
type BlsAggregationService interface {
// InitializeNewTask should be called whenever a new task is created. ProcessNewSignature will return an error
// if the task it is trying to process has not been initialized yet.
// quorumNumbers and quorumThresholdPercentages set the requirements for this task to be considered complete, which
// InitializeNewTask creates a new task goroutine meant to process new signed task responses for that task
// (that are sent via ProcessNewSignature) and adds a channel to a.taskChans to send the signed task responses to
// it. The quorumNumbers and quorumThresholdPercentages set the requirements for this task to be considered
// complete, which
// happens when a particular TaskResponseDigest (received via the a.taskChans[taskIndex]) has been signed by signers
// whose stake
// in each of the listed quorums adds up to at least quorumThresholdPercentages[i] of the total stake in that quorum
// whose stake in each of the listed quorums adds up to at least quorumThresholdPercentages[i] of the total stake in
// that quorum
InitializeNewTask(
taskIndex types.TaskIndex,
taskCreatedBlock uint32,
Expand All @@ -95,6 +96,27 @@ type BlsAggregationService interface {
timeToExpiry time.Duration,
) error

// InitializeNewTaskWithWindow creates a new task goroutine meant to process new signed task responses for that task
// (that are sent via ProcessNewSignature) and adds a channel to a.taskChans to send the signed task responses to
// it. The quorumNumbers and quorumThresholdPercentages set the requirements for this task to be considered
// complete, which
// happens when a particular TaskResponseDigest (received via the a.taskChans[taskIndex]) has been signed by signers
// whose stake in each of the listed quorums adds up to at least quorumThresholdPercentages[i] of the total stake in
// that quorum.
// Once the quorum is reached, the task is still open for a window of `windowDuration` time to receive more
// signatures,
// before sending the aggregation response through the aggregatedResponsesC channel.
// If the task expiration is reached before the window finishes, the task response will still be sent to the
// aggregatedResponsesC channel.
InitializeNewTaskWithWindow(
taskIndex types.TaskIndex,
taskCreatedBlock uint32,
quorumNumbers types.QuorumNums,
quorumThresholdPercentages types.QuorumThresholdPercentages,
timeToExpiry time.Duration,
windowDuration time.Duration,
) error

// ProcessNewSignature processes a new signature over a taskResponseDigest for a particular taskIndex by a
// particular operator It verifies that the signature is correct and returns an error if it is not, and then
// aggregates the signature and stake of
Expand Down Expand Up @@ -185,17 +207,43 @@ func (a *BlsAggregatorService) GetResponseChannel() <-chan BlsAggregationService
}

// InitializeNewTask creates a new task goroutine meant to process new signed task responses for that task
// (that are sent via ProcessNewSignature) and adds a channel to a.taskChans to send the signed task responses to it
// quorumNumbers and quorumThresholdPercentages set the requirements for this task to be considered complete, which
// happens
// when a particular TaskResponseDigest (received via the a.taskChans[taskIndex]) has been signed by signers whose stake
// in each of the listed quorums adds up to at least quorumThresholdPercentages[i] of the total stake in that quorum
// (that are sent via ProcessNewSignature) and adds a channel to a.taskChans to send the signed task responses to it.
// The quorumNumbers and quorumThresholdPercentages set the requirements for this task to be considered complete, which
// happens when a particular TaskResponseDigest (received via the a.taskChans[taskIndex]) has been signed by signers
// whose stake in each of the listed quorums adds up to at least quorumThresholdPercentages[i] of the total stake in
// that quorum
func (a *BlsAggregatorService) InitializeNewTask(
taskIndex types.TaskIndex,
taskCreatedBlock uint32,
quorumNumbers types.QuorumNums,
quorumThresholdPercentages types.QuorumThresholdPercentages,
timeToExpiry time.Duration,
) error {
return a.InitializeNewTaskWithWindow(
taskIndex,
taskCreatedBlock,
quorumNumbers,
quorumThresholdPercentages,
timeToExpiry,
0,
)
}

// InitializeNewTaskWithWindow creates a new task goroutine meant to process new signed task responses for that task
// (that are sent via ProcessNewSignature) and adds a channel to a.taskChans to send the signed task responses to it.
// The quorumNumbers and quorumThresholdPercentages set the requirements for this task to be considered complete, which
// happens when a particular TaskResponseDigest (received via the a.taskChans[taskIndex]) has been signed by signers
// whose stake in each of the listed quorums adds up to at least quorumThresholdPercentages[i] of the total stake in
// that quorum.
// Once the quorum is reached, the task is still open for a window of `windowDuration` time to receive more signatures,
// before sending the aggregation response through the aggregatedResponsesC channel.
func (a *BlsAggregatorService) InitializeNewTaskWithWindow(
taskIndex types.TaskIndex,
taskCreatedBlock uint32,
quorumNumbers types.QuorumNums,
quorumThresholdPercentages types.QuorumThresholdPercentages,
timeToExpiry time.Duration,
windowDuration time.Duration,
) error {
a.logger.Debug(
"AggregatorService initializing new task",
Expand Down Expand Up @@ -225,6 +273,7 @@ func (a *BlsAggregatorService) InitializeNewTask(
quorumNumbers,
quorumThresholdPercentages,
timeToExpiry,
windowDuration,
signedTaskRespsC,
)
return nil
Expand Down Expand Up @@ -271,6 +320,7 @@ func (a *BlsAggregatorService) singleTaskAggregatorGoroutineFunc(
quorumNumbers types.QuorumNums,
quorumThresholdPercentages []types.QuorumThresholdPercentage,
timeToExpiry time.Duration,
windowDuration time.Duration,
signedTaskRespsC <-chan types.SignedTaskResponseDigest,
) {
a.logger.Debug("AggregatorService goroutine processing new task",
Expand Down Expand Up @@ -342,6 +392,13 @@ func (a *BlsAggregatorService) singleTaskAggregatorGoroutineFunc(
taskExpiredTimer := time.NewTimer(timeToExpiry)

aggregatedOperatorsDict := map[types.TaskResponseDigest]aggregatedOperators{}
// The windowTimer is initialized to be longer than the taskExpiredTimer as it will
// be overwritten once the stake threshold is met
windowTimer := time.NewTimer(timeToExpiry + 1*time.Second)
openWindow := false
var lastSignedTaskResponseDigest types.SignedTaskResponseDigest
var lastDigestAggregatedOperators aggregatedOperators
var lastTaskResponseDigest types.TaskResponseDigest
for {
select {
case signedTaskResponseDigest := <-signedTaskRespsC:
Expand Down Expand Up @@ -426,11 +483,17 @@ func (a *BlsAggregatorService) singleTaskAggregatorGoroutineFunc(
digestAggregatedOperators.signersTotalStakePerQuorum[quorumNum].Add(digestAggregatedOperators.signersTotalStakePerQuorum[quorumNum], stake)
}
}

// update the buffer variables to be used when the window timer fires
lastDigestAggregatedOperators = digestAggregatedOperators
lastTaskResponseDigest = taskResponseDigest
lastSignedTaskResponseDigest = signedTaskResponseDigest

// update the aggregatedOperatorsDict. Note that we need to assign the whole struct value at once,
// because of https://github.com/golang/go/issues/3117
aggregatedOperatorsDict[taskResponseDigest] = digestAggregatedOperators

if checkIfStakeThresholdsMet(
if !openWindow && checkIfStakeThresholdsMet(
a.logger,
digestAggregatedOperators.signersTotalStakePerQuorum,
totalStakePerQuorum,
Expand All @@ -440,67 +503,105 @@ func (a *BlsAggregatorService) singleTaskAggregatorGoroutineFunc(
"taskIndex", taskIndex,
"taskResponseDigest", taskResponseDigest)

nonSignersOperatorIds := []types.OperatorId{}
for operatorId := range operatorsAvsStateDict {
if _, operatorSigned := digestAggregatedOperators.signersOperatorIdsSet[operatorId]; !operatorSigned {
nonSignersOperatorIds = append(nonSignersOperatorIds, operatorId)
}
}

// the contract requires a sorted nonSignersOperatorIds
sort.SliceStable(nonSignersOperatorIds, func(i, j int) bool {
iOprInt := new(big.Int).SetBytes(nonSignersOperatorIds[i][:])
jOprInt := new(big.Int).SetBytes(nonSignersOperatorIds[j][:])
return iOprInt.Cmp(jOprInt) == -1
})

nonSignersG1Pubkeys := []*bls.G1Point{}
for _, operatorId := range nonSignersOperatorIds {
operator := operatorsAvsStateDict[operatorId]
nonSignersG1Pubkeys = append(nonSignersG1Pubkeys, operator.OperatorInfo.Pubkeys.G1Pubkey)
}

indices, err := a.avsRegistryService.GetCheckSignaturesIndices(
&bind.CallOpts{},
openWindow = true
windowTimer = time.NewTimer(windowDuration)
a.logger.Debug("Window timer started")
}
case <-taskExpiredTimer.C:
if openWindow {
a.sendAggregatedResponse(
operatorsAvsStateDict,
taskIndex,
taskCreatedBlock,
lastSignedTaskResponseDigest,
lastDigestAggregatedOperators,
quorumNumbers,
nonSignersOperatorIds,
lastTaskResponseDigest,
quorumApksG1,
)
if err != nil {
a.aggregatedResponsesC <- BlsAggregationServiceResponse{
Err: utils.WrapError(errors.New("Failed to get check signatures indices"), err),
TaskIndex: taskIndex,
}
return
}

blsAggregationServiceResponse := BlsAggregationServiceResponse{
Err: nil,
TaskIndex: taskIndex,
TaskResponse: signedTaskResponseDigest.TaskResponse,
TaskResponseDigest: taskResponseDigest,
NonSignersPubkeysG1: nonSignersG1Pubkeys,
QuorumApksG1: quorumApksG1,
SignersApkG2: digestAggregatedOperators.signersApkG2,
SignersAggSigG1: digestAggregatedOperators.signersAggSigG1,
NonSignerQuorumBitmapIndices: indices.NonSignerQuorumBitmapIndices,
QuorumApkIndices: indices.QuorumApkIndices,
TotalStakeIndices: indices.TotalStakeIndices,
NonSignerStakeIndices: indices.NonSignerStakeIndices,
}
a.aggregatedResponsesC <- blsAggregationServiceResponse
taskExpiredTimer.Stop()
return
}
case <-taskExpiredTimer.C:

a.aggregatedResponsesC <- BlsAggregationServiceResponse{
Err: TaskExpiredErrorFn(taskIndex),
TaskIndex: taskIndex,
}
return
case <-windowTimer.C:
a.logger.Debug("Window timer expired")
a.sendAggregatedResponse(
operatorsAvsStateDict,
taskIndex,
taskCreatedBlock,
lastSignedTaskResponseDigest,
lastDigestAggregatedOperators,
quorumNumbers,
lastTaskResponseDigest,
quorumApksG1,
)
return
}
}
}

func (a *BlsAggregatorService) sendAggregatedResponse(
operatorsAvsStateDict map[types.OperatorId]types.OperatorAvsState,
taskIndex types.TaskIndex,
taskCreatedBlock uint32,
signedTaskResponseDigest types.SignedTaskResponseDigest,
digestAggregatedOperators aggregatedOperators,
quorumNumbers types.QuorumNums,
taskResponseDigest types.TaskResponseDigest,
quorumApksG1 []*bls.G1Point,
) {
nonSignersOperatorIds := []types.OperatorId{}
for operatorId := range operatorsAvsStateDict {
if _, operatorSigned := digestAggregatedOperators.signersOperatorIdsSet[operatorId]; !operatorSigned {
nonSignersOperatorIds = append(nonSignersOperatorIds, operatorId)
}
}

// the contract requires a sorted nonSignersOperatorIds
sort.SliceStable(nonSignersOperatorIds, func(i, j int) bool {
iOprInt := new(big.Int).SetBytes(nonSignersOperatorIds[i][:])
jOprInt := new(big.Int).SetBytes(nonSignersOperatorIds[j][:])
return iOprInt.Cmp(jOprInt) == -1
})

nonSignersG1Pubkeys := []*bls.G1Point{}
for _, operatorId := range nonSignersOperatorIds {
operator := operatorsAvsStateDict[operatorId]
nonSignersG1Pubkeys = append(nonSignersG1Pubkeys, operator.OperatorInfo.Pubkeys.G1Pubkey)
}

indices, err := a.avsRegistryService.GetCheckSignaturesIndices(
&bind.CallOpts{},
taskCreatedBlock,
quorumNumbers,
nonSignersOperatorIds,
)
if err != nil {
a.aggregatedResponsesC <- BlsAggregationServiceResponse{
Err: utils.WrapError(errors.New("Failed to get check signatures indices"), err),
TaskIndex: taskIndex,
}
return
}

blsAggregationServiceResponse := BlsAggregationServiceResponse{
Err: nil,
TaskIndex: taskIndex,
TaskResponse: signedTaskResponseDigest.TaskResponse,
TaskResponseDigest: taskResponseDigest,
NonSignersPubkeysG1: nonSignersG1Pubkeys,
QuorumApksG1: quorumApksG1,
SignersApkG2: digestAggregatedOperators.signersApkG2,
SignersAggSigG1: digestAggregatedOperators.signersAggSigG1,
NonSignerQuorumBitmapIndices: indices.NonSignerQuorumBitmapIndices,
QuorumApkIndices: indices.QuorumApkIndices,
TotalStakeIndices: indices.TotalStakeIndices,
NonSignerStakeIndices: indices.NonSignerStakeIndices,
}
a.aggregatedResponsesC <- blsAggregationServiceResponse
}

// closeTaskGoroutine is run when the goroutine processing taskIndex's task responses ends (for whatever reason)
Expand Down
Loading

0 comments on commit 63ec01f

Please sign in to comment.