diff --git a/chainio/clients/avsregistry/reader.go b/chainio/clients/avsregistry/reader.go index 382cb27ba..c714364a3 100644 --- a/chainio/clients/avsregistry/reader.go +++ b/chainio/clients/avsregistry/reader.go @@ -30,6 +30,8 @@ type Config struct { OperatorStateRetrieverAddress common.Address } +// The ChainReader provides methods to call the +// AVS registry contract's view functions. type ChainReader struct { logger logging.Logger blsApkRegistryAddr common.Address @@ -40,6 +42,7 @@ type ChainReader struct { ethClient eth.HttpBackend } +// Creates a new instance of the ChainReader. func NewChainReader( registryCoordinatorAddr common.Address, blsApkRegistryAddr common.Address, @@ -84,6 +87,7 @@ func NewReaderFromConfig( ), nil } +// Returns the total quorum count read from the RegistryCoordinator func (r *ChainReader) GetQuorumCount(opts *bind.CallOpts) (uint8, error) { if r.registryCoordinator == nil { return 0, errors.New("RegistryCoordinator contract not provided") @@ -91,6 +95,8 @@ func (r *ChainReader) GetQuorumCount(opts *bind.CallOpts) (uint8, error) { return r.registryCoordinator.QuorumCount(opts) } +// Returns, for each quorum in `quorumNumbers`, a vector of the operators registered for +// that quorum at the current block, containing each operator's `operatorId` and `stake`. func (r *ChainReader) GetOperatorsStakeInQuorumsAtCurrentBlock( opts *bind.CallOpts, quorumNumbers types.QuorumNums, @@ -130,6 +136,8 @@ func (r *ChainReader) GetOperatorsStakeInQuorumsAtBlock( return operatorStakes, nil } +// Returns, for each quorum in `quorumNumbers`, a vector of the addresses of the +// operators registered for that quorum at the current block. func (r *ChainReader) GetOperatorAddrsInQuorumsAtCurrentBlock( opts *bind.CallOpts, quorumNumbers types.QuorumNums, @@ -137,7 +145,6 @@ func (r *ChainReader) GetOperatorAddrsInQuorumsAtCurrentBlock( if r.operatorStateRetriever == nil { return nil, errors.New("OperatorStateRetriever contract not provided") } - if opts.Context == nil { opts.Context = context.Background() } @@ -169,6 +176,10 @@ func (r *ChainReader) GetOperatorAddrsInQuorumsAtCurrentBlock( } +// Returns a tuple containing +// - An array with the quorum IDs in which the given operator is registered at the given block +// - An array that contains, for each quorum, an array with the address, id and stake +// of each operator registered in that quorum. func (r *ChainReader) GetOperatorsStakeInQuorumsOfOperatorAtBlock( opts *bind.CallOpts, operatorId types.OperatorId, @@ -261,6 +272,8 @@ func (r *ChainReader) GetOperatorStakeInQuorumsOfOperatorAtCurrentBlock( return quorumStakes, nil } +// Returns a struct containing the indices of the quorum members that signed, +// and the ones that didn't. func (r *ChainReader) GetCheckSignaturesIndices( opts *bind.CallOpts, referenceBlockNumber uint32, @@ -293,6 +306,7 @@ func (r *ChainReader) GetCheckSignaturesIndices( return checkSignatureIndices, nil } +// Given an operator address, returns its ID. func (r *ChainReader) GetOperatorId( opts *bind.CallOpts, operatorAddress common.Address, @@ -311,6 +325,7 @@ func (r *ChainReader) GetOperatorId( return operatorId, nil } +// Given an operator ID, returns its address. func (r *ChainReader) GetOperatorFromId( opts *bind.CallOpts, operatorId types.OperatorId, @@ -329,6 +344,8 @@ func (r *ChainReader) GetOperatorFromId( return operatorAddress, nil } +// Returns an array of booleans, where the boolean at index i represents +// whether the operator is registered for the quorum i. func (r *ChainReader) QueryRegistrationDetail( opts *bind.CallOpts, operatorAddress common.Address, @@ -358,6 +375,7 @@ func (r *ChainReader) QueryRegistrationDetail( return quorums, nil } +// Returns true if the operator is registered, false otherwise. func (r *ChainReader) IsOperatorRegistered( opts *bind.CallOpts, operatorAddress common.Address, @@ -376,6 +394,9 @@ func (r *ChainReader) IsOperatorRegistered( return registeredWithAvs, nil } +// Queries existing operators for a particular block range. +// Returns two arrays. The first one contains the addresses +// of the operators, and the second contains their corresponding public keys. func (r *ChainReader) QueryExistingRegisteredOperatorPubKeys( ctx context.Context, startBlock *big.Int, @@ -475,6 +496,9 @@ func (r *ChainReader) QueryExistingRegisteredOperatorPubKeys( return operatorAddresses, operatorPubkeys, nil } +// Queries existing operator sockets for a particular block range. +// Returns a mapping containing operator IDs as keys and their +// corresponding sockets as values. func (r *ChainReader) QueryExistingRegisteredOperatorSockets( ctx context.Context, startBlock *big.Int, diff --git a/chainio/clients/avsregistry/subscriber_test.go b/chainio/clients/avsregistry/subscriber_test.go new file mode 100644 index 000000000..0cdf2aaaf --- /dev/null +++ b/chainio/clients/avsregistry/subscriber_test.go @@ -0,0 +1,76 @@ +package avsregistry_test + +import ( + "context" + "testing" + "time" + + "github.com/Layr-Labs/eigensdk-go/crypto/bls" + "github.com/Layr-Labs/eigensdk-go/testutils" + "github.com/Layr-Labs/eigensdk-go/testutils/testclients" + "github.com/Layr-Labs/eigensdk-go/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSubscriberAvsRegistry(t *testing.T) { + client, _ := testclients.BuildTestClients(t) + chainSubscriber := client.AvsRegistryChainSubscriber + chainWriter := client.AvsRegistryChainWriter + + t.Run("subscribe to new pubkey registrations", func(t *testing.T) { + pubKeyRegistrationsC, event, err := chainSubscriber.SubscribeToNewPubkeyRegistrations() + require.NoError(t, err) + defer event.Unsubscribe() + + // Emit a NewPubkeyRegistration event creating a new operator + keypair, err := bls.NewKeyPairFromString("0x01") + require.NoError(t, err) + + ecdsaPrivateKey, err := crypto.HexToECDSA(testutils.ANVIL_FIRST_PRIVATE_KEY) + require.NoError(t, err) + + quorumNumbers := types.QuorumNums{0} + + receipt, err := chainWriter.RegisterOperator( + context.Background(), + ecdsaPrivateKey, + keypair, + quorumNumbers, + "", + true, + ) + require.NoError(t, err) + require.NotNil(t, receipt) + + select { + case newPubkeyRegistration := <-pubKeyRegistrationsC: + expectedOperator := crypto.PubkeyToAddress(ecdsaPrivateKey.PublicKey) + assert.Equal(t, expectedOperator, newPubkeyRegistration.Operator) + case <-time.After(10 * time.Second): + // Throw an error if the event is not received within 10 seconds, making the test fail + t.Fatal("Timed out waiting for NewPubkeyRegistration event") + } + }) + + t.Run("subscribe to operator socket updates", func(t *testing.T) { + socketC, event, err := chainSubscriber.SubscribeToOperatorSocketUpdates() + require.NoError(t, err) + defer event.Unsubscribe() + + // Emit a SocketUpdate event + socketUpdate := "socket-update" + receipt, err := chainWriter.UpdateSocket(context.Background(), types.Socket(socketUpdate), true) + require.NoError(t, err) + require.NotNil(t, receipt) + + select { + case operatorSocketUpdate := <-socketC: + assert.Equal(t, socketUpdate, operatorSocketUpdate.Socket) + case <-time.After(10 * time.Second): + // Throw an error if the event is not received within 10 seconds, making the test fail + t.Fatal("Timed out waiting for OperatorSocketUpdate event") + } + }) +} diff --git a/chainio/clients/avsregistry/writer.go b/chainio/clients/avsregistry/writer.go index 99e5ec1ac..aa22d12d9 100644 --- a/chainio/clients/avsregistry/writer.go +++ b/chainio/clients/avsregistry/writer.go @@ -35,6 +35,8 @@ type eLReader interface { ) ([32]byte, error) } +// The ChainWriter provides methods to call the +// AVS registry contract's state-changing functions. type ChainWriter struct { serviceManagerAddr gethcommon.Address registryCoordinator *regcoord.ContractRegistryCoordinator @@ -47,6 +49,7 @@ type ChainWriter struct { txMgr txmgr.TxManager } +// Returns a new instance of ChainWriter. func NewChainWriter( serviceManagerAddr gethcommon.Address, registryCoordinator *regcoord.ContractRegistryCoordinator, @@ -380,6 +383,8 @@ func (w *ChainWriter) UpdateStakesOfEntireOperatorSetForQuorums( } +// Updates the stakes of a the given `operators` for all the quorums. +// On success, returns the receipt of the transaction. func (w *ChainWriter) UpdateStakesOfOperatorSubsetForAllQuorums( ctx context.Context, operators []gethcommon.Address, @@ -408,6 +413,8 @@ func (w *ChainWriter) UpdateStakesOfOperatorSubsetForAllQuorums( return receipt, nil } +// Deregisters the caller from the quorums given by `quorumNumbers`. +// On success, returns the receipt of the transaction. func (w *ChainWriter) DeregisterOperator( ctx context.Context, quorumNumbers types.QuorumNums, @@ -435,6 +442,8 @@ func (w *ChainWriter) DeregisterOperator( return receipt, nil } +// Deregisters an operator from the given operator sets. +// On success, returns the receipt of the transaction. func (w *ChainWriter) DeregisterOperatorOperatorSets( ctx context.Context, operatorSetIds types.OperatorSetIds, @@ -465,6 +474,8 @@ func (w *ChainWriter) DeregisterOperatorOperatorSets( return receipt, nil } +// Updates the socket of the sender (if it is a registered operator). +// On success, returns the receipt of the transaction. func (w *ChainWriter) UpdateSocket( ctx context.Context, socket types.Socket,