diff --git a/core/eth/tx.go b/core/eth/tx.go index d1367527ca..4383175f9f 100644 --- a/core/eth/tx.go +++ b/core/eth/tx.go @@ -10,6 +10,7 @@ import ( "github.com/Layr-Labs/eigenda/api/grpc/churner" "github.com/Layr-Labs/eigenda/common" "github.com/Layr-Labs/eigenda/core" + "github.com/gammazero/workerpool" avsdir "github.com/Layr-Labs/eigenda/contracts/bindings/AVSDirectory" blsapkreg "github.com/Layr-Labs/eigenda/contracts/bindings/BLSApkRegistry" @@ -27,7 +28,8 @@ import ( ) var ( - maxNumberOfQuorums = 192 + maxNumberOfQuorums = 192 + maxNumWorkerPoolThreads = 8 ) type Transactor struct { @@ -535,12 +537,87 @@ func (t *Transactor) OperatorIDToAddress(ctx context.Context, operatorId core.Op }, operatorId) } +func (t *Transactor) BatchOperatorIDToAddress(ctx context.Context, operatorIds []core.OperatorID) ([]gethcommon.Address, error) { + type AddressOrError struct { + address gethcommon.Address + index int + err error + } + resultChan := make(chan AddressOrError, len(operatorIds)) + pool := workerpool.New(maxNumWorkerPoolThreads) + for i, operatorId := range operatorIds { + idx := i + op := operatorId + pool.Submit(func() { + addr, err := t.Bindings.BLSApkRegistry.PubkeyHashToOperator(&bind.CallOpts{ + Context: ctx, + }, op) + resultChan <- AddressOrError{address: addr, index: idx, err: err} + }) + } + pool.StopWait() + close(resultChan) + + addresses := make([]gethcommon.Address, len(operatorIds)) + for result := range resultChan { + if result.err != nil { + return nil, result.err + } + addresses[result.index] = result.address + } + return addresses, nil +} + func (t *Transactor) GetCurrentQuorumBitmapByOperatorId(ctx context.Context, operatorId core.OperatorID) (*big.Int, error) { return t.Bindings.RegistryCoordinator.GetCurrentQuorumBitmap(&bind.CallOpts{ Context: ctx, }, operatorId) } +func (t *Transactor) GetQuorumBitmapForOperatorsAtBlockNumber(ctx context.Context, operatorIds []core.OperatorID, blockNumber uint32) ([]*big.Int, error) { + // Get indices in batch (1 RPC). + byteOperatorIds := make([][32]byte, len(operatorIds)) + for i := range operatorIds { + byteOperatorIds[i] = [32]byte(operatorIds[i]) + } + indices, err := t.Bindings.RegistryCoordinator.GetQuorumBitmapIndicesAtBlockNumber(&bind.CallOpts{ + Context: ctx, + }, blockNumber, byteOperatorIds) + if err != nil { + return nil, err + } + + // Get bitmaps in N RPCs, but in parallel. + type BitmapOrError struct { + bitmap *big.Int + index uint32 + err error + } + resultChan := make(chan BitmapOrError, len(indices)) + pool := workerpool.New(maxNumWorkerPoolThreads) + for i, index := range indices { + idx := index + op := operatorIds[i] + pool.Submit(func() { + bm, err := t.Bindings.RegistryCoordinator.GetQuorumBitmapAtBlockNumberByIndex(&bind.CallOpts{ + Context: ctx, + }, op, blockNumber, big.NewInt(int64(idx))) + resultChan <- BitmapOrError{bitmap: bm, index: idx, err: err} + }) + } + pool.StopWait() + close(resultChan) + + bitmaps := make([]*big.Int, len(indices)) + for result := range resultChan { + if result.err != nil { + return nil, result.err + } + bitmaps[result.index] = result.bitmap + } + return bitmaps, nil +} + func (t *Transactor) GetOperatorSetParams(ctx context.Context, quorumID core.QuorumID) (*core.OperatorSetParam, error) { operatorSetParams, err := t.Bindings.RegistryCoordinator.GetOperatorSetParams(&bind.CallOpts{ diff --git a/core/mock/tx.go b/core/mock/tx.go index 8cf0476a68..bf92312c7e 100644 --- a/core/mock/tx.go +++ b/core/mock/tx.go @@ -110,6 +110,18 @@ func (t *MockTransactor) OperatorIDToAddress(ctx context.Context, operatorId cor return result.(gethcommon.Address), args.Error(1) } +func (t *MockTransactor) BatchOperatorIDToAddress(ctx context.Context, operatorIds []core.OperatorID) ([]gethcommon.Address, error) { + args := t.Called() + result := args.Get(0) + return result.([]gethcommon.Address), args.Error(1) +} + +func (t *MockTransactor) GetQuorumBitmapForOperatorsAtBlockNumber(ctx context.Context, operatorIds []core.OperatorID, blockNumber uint32) ([]*big.Int, error) { + args := t.Called() + result := args.Get(0) + return result.([]*big.Int), args.Error(1) +} + func (t *MockTransactor) GetCurrentQuorumBitmapByOperatorId(ctx context.Context, operatorId core.OperatorID) (*big.Int, error) { args := t.Called() result := args.Get(0) diff --git a/core/tx.go b/core/tx.go index be5eadf2ca..c0c54d42a1 100644 --- a/core/tx.go +++ b/core/tx.go @@ -95,9 +95,16 @@ type Transactor interface { // OperatorIDToAddress returns the address of the operator from the operator id. OperatorIDToAddress(ctx context.Context, operatorId OperatorID) (gethcommon.Address, error) + // BatchOperatorIDToAddress returns the addresses of the operators from the operator id. + BatchOperatorIDToAddress(ctx context.Context, operatorIds []OperatorID) ([]gethcommon.Address, error) + // GetCurrentQuorumBitmapByOperatorId returns the current quorum bitmap for the operator. GetCurrentQuorumBitmapByOperatorId(ctx context.Context, operatorId OperatorID) (*big.Int, error) + // GetQuorumBitmapForOperatorsAtBlockNumber returns the quorum bitmaps for the operators + // at the given block number. + GetQuorumBitmapForOperatorsAtBlockNumber(ctx context.Context, operatorId []OperatorID, blockNumber uint32) ([]*big.Int, error) + // GetOperatorSetParams returns operator set params for the quorum. GetOperatorSetParams(ctx context.Context, quorumID QuorumID) (*OperatorSetParam, error)