diff --git a/chainio/clients/avsregistry/reader.go b/chainio/clients/avsregistry/reader.go index c714364a3..694fe0a25 100644 --- a/chainio/clients/avsregistry/reader.go +++ b/chainio/clients/avsregistry/reader.go @@ -2,7 +2,6 @@ package avsregistry import ( "context" - "errors" "math" "math/big" @@ -10,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/Layr-Labs/eigensdk-go/chainio/clients/elcontracts" "github.com/Layr-Labs/eigensdk-go/chainio/clients/eth" apkreg "github.com/Layr-Labs/eigensdk-go/contracts/bindings/BLSApkRegistry" opstateretriever "github.com/Layr-Labs/eigensdk-go/contracts/bindings/OperatorStateRetriever" @@ -18,7 +18,6 @@ import ( "github.com/Layr-Labs/eigensdk-go/crypto/bls" "github.com/Layr-Labs/eigensdk-go/logging" "github.com/Layr-Labs/eigensdk-go/types" - "github.com/Layr-Labs/eigensdk-go/utils" ) // DefaultQueryBlockRange different node providers have different eth_getLogs range limits. @@ -73,7 +72,8 @@ func NewReaderFromConfig( ) (*ChainReader, error) { bindings, err := NewBindingsFromConfig(cfg, client, logger) if err != nil { - return nil, err + wrappedError := elcontracts.CreateForNestedError("NewBindingsFromConfig", err) + return nil, wrappedError } return NewChainReader( @@ -90,9 +90,17 @@ func NewReaderFromConfig( // 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") + wrappedError := elcontracts.CreateErrorForMissingContract("RegistryCoordinator") + return 0, wrappedError } - return r.registryCoordinator.QuorumCount(opts) + + cont, err := r.registryCoordinator.QuorumCount(opts) + if err != nil { + wrappedError := elcontracts.CreateForBindingError("registryCoordinator.QuorumCount", err) + return 0, wrappedError + } + + return cont, nil } // Returns, for each quorum in `quorumNumbers`, a vector of the operators registered for @@ -106,12 +114,21 @@ func (r *ChainReader) GetOperatorsStakeInQuorumsAtCurrentBlock( } curBlock, err := r.ethClient.BlockNumber(opts.Context) if err != nil { - return nil, utils.WrapError("Cannot get current block number", err) + wrappedError := elcontracts.CreateForBindingError("ethClient.BlockNumber", err) + return nil, wrappedError } if curBlock > math.MaxUint32 { - return nil, utils.WrapError("Current block number is too large to be converted to uint32", err) + wrappedError := elcontracts.CreateForOtherError("Current block number is too large to fit into an uint32", err) + return nil, wrappedError } - return r.GetOperatorsStakeInQuorumsAtBlock(opts, quorumNumbers, uint32(curBlock)) + + operatorStakes, err := r.GetOperatorsStakeInQuorumsAtBlock(opts, quorumNumbers, uint32(curBlock)) + if err != nil { + wrappedError := elcontracts.CreateForNestedError("GetOperatorsStakeInQuorumsAtBlock", err) + return nil, wrappedError + } + + return operatorStakes, nil } // the contract stores historical state, so blockNumber should be the block number of the state you want to query @@ -122,7 +139,8 @@ func (r *ChainReader) GetOperatorsStakeInQuorumsAtBlock( blockNumber uint32, ) ([][]opstateretriever.OperatorStateRetrieverOperator, error) { if r.operatorStateRetriever == nil { - return nil, errors.New("OperatorStateRetriever contract not provided") + wrappedError := elcontracts.CreateErrorForMissingContract("OperatorStateRetriever") + return nil, wrappedError } operatorStakes, err := r.operatorStateRetriever.GetOperatorState( @@ -131,7 +149,8 @@ func (r *ChainReader) GetOperatorsStakeInQuorumsAtBlock( quorumNumbers.UnderlyingType(), blockNumber) if err != nil { - return nil, utils.WrapError("Failed to get operators state", err) + wrappedError := elcontracts.CreateForBindingError("operatorStateRetriever.GetOperatorState", err) + return nil, wrappedError } return operatorStakes, nil } @@ -143,17 +162,20 @@ func (r *ChainReader) GetOperatorAddrsInQuorumsAtCurrentBlock( quorumNumbers types.QuorumNums, ) ([][]common.Address, error) { if r.operatorStateRetriever == nil { - return nil, errors.New("OperatorStateRetriever contract not provided") + wrappedError := elcontracts.CreateErrorForMissingContract("OperatorStateRetriever") + return nil, wrappedError } if opts.Context == nil { opts.Context = context.Background() } curBlock, err := r.ethClient.BlockNumber(opts.Context) if err != nil { - return nil, utils.WrapError("Failed to get current block number", err) + wrappedError := elcontracts.CreateForBindingError("ethClient.BlockNumber", err) + return nil, wrappedError } if curBlock > math.MaxUint32 { - return nil, utils.WrapError("Current block number is too large to be converted to uint32", err) + wrappedError := elcontracts.CreateForOtherError("Current block number is too large to fit into an uint32", err) + return nil, wrappedError } operatorStakes, err := r.operatorStateRetriever.GetOperatorState( opts, @@ -162,7 +184,8 @@ func (r *ChainReader) GetOperatorAddrsInQuorumsAtCurrentBlock( uint32(curBlock), ) if err != nil { - return nil, utils.WrapError("Failed to get operators state", err) + wrappedError := elcontracts.CreateForBindingError("operatorStateRetriever.GetOperatorState", err) + return nil, wrappedError } var quorumOperatorAddrs [][]common.Address for _, quorum := range operatorStakes { @@ -173,7 +196,6 @@ func (r *ChainReader) GetOperatorAddrsInQuorumsAtCurrentBlock( quorumOperatorAddrs = append(quorumOperatorAddrs, operatorAddrs) } return quorumOperatorAddrs, nil - } // Returns a tuple containing @@ -186,7 +208,8 @@ func (r *ChainReader) GetOperatorsStakeInQuorumsOfOperatorAtBlock( blockNumber uint32, ) (types.QuorumNums, [][]opstateretriever.OperatorStateRetrieverOperator, error) { if r.operatorStateRetriever == nil { - return nil, nil, errors.New("OperatorStateRetriever contract not provided") + wrappedError := elcontracts.CreateErrorForMissingContract("OperatorStateRetriever") + return nil, nil, wrappedError } quorumBitmap, operatorStakes, err := r.operatorStateRetriever.GetOperatorState0( @@ -195,7 +218,8 @@ func (r *ChainReader) GetOperatorsStakeInQuorumsOfOperatorAtBlock( operatorId, blockNumber) if err != nil { - return nil, nil, utils.WrapError("Failed to get operators state", err) + wrappedError := elcontracts.CreateForBindingError("operatorStateRetriever.GetOperatorState0", err) + return nil, nil, wrappedError } quorums := types.BitmapToQuorumIds(quorumBitmap) return quorums, operatorStakes, nil @@ -212,13 +236,21 @@ func (r *ChainReader) GetOperatorsStakeInQuorumsOfOperatorAtCurrentBlock( } curBlock, err := r.ethClient.BlockNumber(opts.Context) if err != nil { - return nil, nil, utils.WrapError("Failed to get current block number", err) + wrappedError := elcontracts.CreateForBindingError("ethClient.BlockNumber", err) + return nil, nil, wrappedError } if curBlock > math.MaxUint32 { - return nil, nil, utils.WrapError("Current block number is too large to be converted to uint32", err) + wrappedError := elcontracts.CreateForOtherError("Current block number is too large to fit into an uint32", err) + return nil, nil, wrappedError } opts.BlockNumber = big.NewInt(int64(curBlock)) - return r.GetOperatorsStakeInQuorumsOfOperatorAtBlock(opts, operatorId, uint32(curBlock)) + quorums, operatorsStake, err := r.GetOperatorsStakeInQuorumsOfOperatorAtBlock(opts, operatorId, uint32(curBlock)) + if err != nil { + wrappedError := elcontracts.CreateForNestedError("GetOperatorsStakeInQuorumsOfOperatorAtBlock", err) + return nil, nil, wrappedError + } + + return quorums, operatorsStake, nil } // To avoid a possible race condition, this method must assure that all the calls @@ -231,11 +263,13 @@ func (r *ChainReader) GetOperatorStakeInQuorumsOfOperatorAtCurrentBlock( operatorId types.OperatorId, ) (map[types.QuorumNum]types.StakeAmount, error) { if r.registryCoordinator == nil { - return nil, errors.New("RegistryCoordinator contract not provided") + wrappedError := elcontracts.CreateErrorForMissingContract("RegistryCoordinator") + return nil, wrappedError } if r.stakeRegistry == nil { - return nil, errors.New("StakeRegistry contract not provided") + wrappedError := elcontracts.CreateErrorForMissingContract("StakeRegistry") + return nil, wrappedError } // check if opts parameter has not a block number set (BlockNumber) @@ -247,14 +281,16 @@ func (r *ChainReader) GetOperatorStakeInQuorumsOfOperatorAtCurrentBlock( } latestBlock, err := r.ethClient.BlockNumber(opts.Context) if err != nil { - return nil, utils.WrapError("Failed to get latest block number", err) + wrappedError := elcontracts.CreateForBindingError("ethClient.BlockNumber", err) + return nil, wrappedError } opts.BlockNumber = big.NewInt(int64(latestBlock)) } quorumBitmap, err := r.registryCoordinator.GetCurrentQuorumBitmap(opts, operatorId) if err != nil { - return nil, utils.WrapError("Failed to get operator quorums", err) + wrappedError := elcontracts.CreateForBindingError("registryCoordinator.GetCurrentQuorumBitmap", err) + return nil, wrappedError } quorums := types.BitmapToQuorumIds(quorumBitmap) quorumStakes := make(map[types.QuorumNum]types.StakeAmount) @@ -265,7 +301,8 @@ func (r *ChainReader) GetOperatorStakeInQuorumsOfOperatorAtCurrentBlock( uint8(quorum), ) if err != nil { - return nil, utils.WrapError("Failed to get operator stake", err) + wrappedError := elcontracts.CreateForBindingError("stakeRegistry.GetCurrentStake", err) + return nil, wrappedError } quorumStakes[quorum] = stake } @@ -281,9 +318,8 @@ func (r *ChainReader) GetCheckSignaturesIndices( nonSignerOperatorIds []types.OperatorId, ) (opstateretriever.OperatorStateRetrieverCheckSignaturesIndices, error) { if r.operatorStateRetriever == nil { - return opstateretriever.OperatorStateRetrieverCheckSignaturesIndices{}, errors.New( - "OperatorStateRetriever contract not provided", - ) + wrappedError := elcontracts.CreateErrorForMissingContract("OperatorStateRetriever") + return opstateretriever.OperatorStateRetrieverCheckSignaturesIndices{}, wrappedError } nonSignerOperatorIdsBytes := make([][32]byte, len(nonSignerOperatorIds)) @@ -298,10 +334,8 @@ func (r *ChainReader) GetCheckSignaturesIndices( nonSignerOperatorIdsBytes, ) if err != nil { - return opstateretriever.OperatorStateRetrieverCheckSignaturesIndices{}, utils.WrapError( - "Failed to get check signatures indices", - err, - ) + wrappedError := elcontracts.CreateForBindingError("operatorStateRetriever.GetCheckSignaturesIndices", err) + return opstateretriever.OperatorStateRetrieverCheckSignaturesIndices{}, wrappedError } return checkSignatureIndices, nil } @@ -312,7 +346,8 @@ func (r *ChainReader) GetOperatorId( operatorAddress common.Address, ) ([32]byte, error) { if r.registryCoordinator == nil { - return [32]byte{}, errors.New("RegistryCoordinator contract not provided") + wrappedError := elcontracts.CreateErrorForMissingContract("RegistryCoordinator") + return [32]byte{}, wrappedError } operatorId, err := r.registryCoordinator.GetOperatorId( @@ -320,7 +355,8 @@ func (r *ChainReader) GetOperatorId( operatorAddress, ) if err != nil { - return [32]byte{}, utils.WrapError("Failed to get operator id", err) + wrappedError := elcontracts.CreateForBindingError("registryCoordinator.GetOperatorId", err) + return [32]byte{}, wrappedError } return operatorId, nil } @@ -331,7 +367,8 @@ func (r *ChainReader) GetOperatorFromId( operatorId types.OperatorId, ) (common.Address, error) { if r.registryCoordinator == nil { - return common.Address{}, errors.New("RegistryCoordinator contract not provided") + wrappedError := elcontracts.CreateErrorForMissingContract("RegistryCoordinator") + return common.Address{}, wrappedError } operatorAddress, err := r.registryCoordinator.GetOperatorFromId( @@ -339,7 +376,8 @@ func (r *ChainReader) GetOperatorFromId( operatorId, ) if err != nil { - return common.Address{}, utils.WrapError("Failed to get operator address", err) + wrappedError := elcontracts.CreateForBindingError("registryCoordinator.GetOperatorFromId", err) + return common.Address{}, wrappedError } return operatorAddress, nil } @@ -352,11 +390,13 @@ func (r *ChainReader) QueryRegistrationDetail( ) ([]bool, error) { operatorId, err := r.GetOperatorId(opts, operatorAddress) if err != nil { - return nil, utils.WrapError("Failed to get operator id", err) + wrappedError := elcontracts.CreateForNestedError("GetOperatorId", err) + return nil, wrappedError } value, err := r.registryCoordinator.GetCurrentQuorumBitmap(opts, operatorId) if err != nil { - return nil, utils.WrapError("Failed to get operator quorums", err) + wrappedError := elcontracts.CreateForBindingError("registryCoordinator.GetCurrentQuorumBitmap", err) + return nil, wrappedError } numBits := value.BitLen() var quorums []bool @@ -366,7 +406,8 @@ func (r *ChainReader) QueryRegistrationDetail( if len(quorums) == 0 { numQuorums, err := r.GetQuorumCount(opts) if err != nil { - return nil, utils.WrapError("Failed to get quorum count", err) + wrappedError := elcontracts.CreateForNestedError("GetQuorumCount", err) + return nil, wrappedError } for i := uint8(0); i < numQuorums; i++ { quorums = append(quorums, false) @@ -381,12 +422,14 @@ func (r *ChainReader) IsOperatorRegistered( operatorAddress common.Address, ) (bool, error) { if r.registryCoordinator == nil { - return false, errors.New("RegistryCoordinator contract not provided") + wrappedError := elcontracts.CreateErrorForMissingContract("RegistryCoordinator") + return false, wrappedError } operatorStatus, err := r.registryCoordinator.GetOperatorStatus(opts, operatorAddress) if err != nil { - return false, utils.WrapError("Failed to get operator status", err) + wrappedError := elcontracts.CreateForBindingError("registryCoordinator.GetOperatorStatus", err) + return false, wrappedError } // 0 = NEVER_REGISTERED, 1 = REGISTERED, 2 = DEREGISTERED @@ -405,7 +448,8 @@ func (r *ChainReader) QueryExistingRegisteredOperatorPubKeys( ) ([]types.OperatorAddr, []types.OperatorPubkeys, error) { blsApkRegistryAbi, err := apkreg.ContractBLSApkRegistryMetaData.GetAbi() if err != nil { - return nil, nil, utils.WrapError("Cannot get Abi", err) + wrappedError := elcontracts.CreateForOtherError("Failed to get bls apk registry ABI", err) + return nil, nil, wrappedError } if startBlock == nil { @@ -414,7 +458,8 @@ func (r *ChainReader) QueryExistingRegisteredOperatorPubKeys( if stopBlock == nil { curBlockNum, err := r.ethClient.BlockNumber(ctx) if err != nil { - return nil, nil, utils.WrapError("Cannot get current block number", err) + wrappedError := elcontracts.CreateForBindingError("ethClient.BlockNumber", err) + return nil, nil, wrappedError } stopBlock = new(big.Int).SetUint64(curBlockNum) } @@ -446,7 +491,8 @@ func (r *ChainReader) QueryExistingRegisteredOperatorPubKeys( logs, err := r.ethClient.FilterLogs(ctx, query) if err != nil { - return nil, nil, utils.WrapError("Cannot filter logs", err) + wrappedError := elcontracts.CreateForBindingError("ethClient.FilterLogs", err) + return nil, nil, wrappedError } r.logger.Debug( "avsRegistryChainReader.QueryExistingRegisteredOperatorPubKeys", @@ -465,7 +511,8 @@ func (r *ChainReader) QueryExistingRegisteredOperatorPubKeys( event, err := blsApkRegistryAbi.Unpack("NewPubkeyRegistration", vLog.Data) if err != nil { - return nil, nil, utils.WrapError("Cannot unpack event data", err) + wrappedError := elcontracts.CreateForOtherError("Failed to unpack event data", err) + return nil, nil, wrappedError } G1Pubkey := event[0].(struct { @@ -506,7 +553,8 @@ func (r *ChainReader) QueryExistingRegisteredOperatorSockets( blockRange *big.Int, ) (map[types.OperatorId]types.Socket, error) { if r.registryCoordinator == nil { - return nil, errors.New("RegistryCoordinator contract not provided") + wrappedError := elcontracts.CreateErrorForMissingContract("RegistryCoordinator") + return nil, wrappedError } if startBlock == nil { @@ -515,7 +563,8 @@ func (r *ChainReader) QueryExistingRegisteredOperatorSockets( if stopBlock == nil { curBlockNum, err := r.ethClient.BlockNumber(ctx) if err != nil { - return nil, utils.WrapError("Cannot get current block number", err) + wrappedError := elcontracts.CreateForBindingError("ethClient.BlockNumber", err) + return nil, wrappedError } stopBlock = new(big.Int).SetUint64(curBlockNum) } @@ -544,7 +593,8 @@ func (r *ChainReader) QueryExistingRegisteredOperatorSockets( } socketUpdates, err := r.registryCoordinator.FilterOperatorSocketUpdate(filterOpts, nil) if err != nil { - return nil, utils.WrapError("Cannot filter operator socket updates", err) + wrappedError := elcontracts.CreateForBindingError("registryCoordinator.FilterOperatorSocketUpdate", err) + return nil, wrappedError } numSocketUpdates := 0 diff --git a/chainio/clients/avsregistry/reader_test.go b/chainio/clients/avsregistry/reader_test.go index 375551a2d..f3ae53676 100644 --- a/chainio/clients/avsregistry/reader_test.go +++ b/chainio/clients/avsregistry/reader_test.go @@ -2,19 +2,33 @@ package avsregistry_test import ( "context" + "fmt" "math/big" "testing" + "github.com/Layr-Labs/eigensdk-go/chainio/clients/avsregistry" + "github.com/Layr-Labs/eigensdk-go/chainio/clients/elcontracts" + "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/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/require" ) func TestReaderMethods(t *testing.T) { clients, _ := testclients.BuildTestClients(t) chainReader := clients.ReadClients.AvsRegistryChainReader + chainWriter := clients.AvsRegistryChainWriter + + keypair, err := bls.NewKeyPairFromString("0x01") + require.NoError(t, err) + + operatorAddr := common.HexToAddress(testutils.ANVIL_FIRST_ADDRESS) + ecdsaPrivateKey, err := crypto.HexToECDSA(testutils.ANVIL_FIRST_PRIVATE_KEY) + require.NoError(t, err) quorumNumbers := types.QuorumNums{0} @@ -42,8 +56,7 @@ func TestReaderMethods(t *testing.T) { require.NotNil(t, addresses) }) - t.Run( - "get operators stake in quorums of operator at block returns error for non-registered operator", + t.Run("get operators stake in quorums of operator at block returns error for non-registered operator", func(t *testing.T) { operatorAddress := common.Address{0x1} operatorId, err := chainReader.GetOperatorId(&bind.CallOpts{}, operatorAddress) @@ -51,11 +64,14 @@ func TestReaderMethods(t *testing.T) { _, _, err = chainReader.GetOperatorsStakeInQuorumsOfOperatorAtBlock(&bind.CallOpts{}, operatorId, 100) require.Error(t, err) - require.Contains(t, err.Error(), "Failed to get operators state") + require.Equal( + t, + err.Error(), + "Binding error(0) - Error happened while calling operatorStateRetriever.GetOperatorState0: execution reverted: revert: RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId", + ) }) - t.Run( - "get single operator stake in quorums of operator at current block returns error for non-registered operator", + t.Run("get single operator stake in quorums of operator at current block returns error for non-registered operator", func(t *testing.T) { operatorAddress := common.Address{0x1} operatorId, err := chainReader.GetOperatorId(&bind.CallOpts{}, operatorAddress) @@ -77,7 +93,11 @@ func TestReaderMethods(t *testing.T) { quorumNumbers, []types.OperatorId{operatorId}, ) - require.Contains(t, err.Error(), "Failed to get check signatures indices") + require.Contains( + t, + err.Error(), + "Binding error(0) - Error happened while calling operatorStateRetriever.GetCheckSignaturesIndices: execution reverted: revert: RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId", + ) }) t.Run("get operator id", func(t *testing.T) { @@ -111,18 +131,17 @@ func TestReaderMethods(t *testing.T) { require.False(t, isRegistered) }) - t.Run( - "query existing registered operator pub keys", func(t *testing.T) { - addresses, pubKeys, err := chainReader.QueryExistingRegisteredOperatorPubKeys( - context.Background(), - big.NewInt(0), - nil, - nil, - ) - require.NoError(t, err) - require.Equal(t, 0, len(pubKeys)) - require.Equal(t, 0, len(addresses)) - }) + t.Run("query existing registered operator pub keys", func(t *testing.T) { + addresses, pubKeys, err := chainReader.QueryExistingRegisteredOperatorPubKeys( + context.Background(), + big.NewInt(0), + nil, + nil, + ) + require.NoError(t, err) + require.Equal(t, 0, len(pubKeys)) + require.Equal(t, 0, len(addresses)) + }) t.Run( "query existing registered operator sockets", func(t *testing.T) { @@ -135,4 +154,209 @@ func TestReaderMethods(t *testing.T) { require.NoError(t, err) require.Equal(t, 0, len(address_to_sockets)) }) + + t.Run("get operators stake in quorums", func(t *testing.T) { + receipt, err := chainWriter.RegisterOperator( + context.Background(), + ecdsaPrivateKey, + keypair, + quorumNumbers, + "", + true, + ) + require.NoError(t, err) + require.NotNil(t, receipt) + + blockNumber := uint32(receipt.BlockNumber.Uint64()) + + operatorId, err := chainReader.GetOperatorId(&bind.CallOpts{}, operatorAddr) + require.NoError(t, err) + + t.Run("get operators stake in quorums at block", func(t *testing.T) { + stake, operators, err := chainReader.GetOperatorsStakeInQuorumsOfOperatorAtBlock( + &bind.CallOpts{}, + operatorId, + blockNumber, + ) + require.NoError(t, err) + require.Equal(t, 1, len(stake)) + require.Equal(t, 1, len(operators)) + }) + + t.Run("get operators stake in quorums at current block", func(t *testing.T) { + stake, operators, err := chainReader.GetOperatorsStakeInQuorumsOfOperatorAtCurrentBlock( + &bind.CallOpts{}, + operatorId, + ) + require.NoError(t, err) + require.Equal(t, 1, len(stake)) + require.Equal(t, 1, len(operators)) + }) + + t.Run("get operator stake in quorums at current block", func(t *testing.T) { + stakeMap, err := chainReader.GetOperatorStakeInQuorumsOfOperatorAtCurrentBlock(&bind.CallOpts{}, operatorId) + require.NoError(t, err) + require.Equal(t, 1, len(stakeMap)) + }) + + t.Run("get check signatures indices ", func(t *testing.T) { + indices, err := chainReader.GetCheckSignaturesIndices( + &bind.CallOpts{}, + blockNumber, + quorumNumbers, + []types.OperatorId{operatorId}, + ) + require.NoError(t, err) + require.NotNil(t, indices) + }) + + t.Run( + "query existing registered operator pub keys", func(t *testing.T) { + addresses, pubKeys, err := chainReader.QueryExistingRegisteredOperatorPubKeys( + context.Background(), + big.NewInt(0), + nil, + nil, + ) + require.NoError(t, err) + require.Equal(t, 1, len(pubKeys)) + require.Equal(t, 1, len(addresses)) + }) + }) +} + +// Test that the reader returns an error when the configuration is invalid. +func TestReaderWithInvalidConfiguration(t *testing.T) { + _, anvilHttpEndpoint := testclients.BuildTestClients(t) + + config := avsregistry.Config{} + chainReader, err := testclients.NewTestAvsRegistryReaderFromConfig(anvilHttpEndpoint, config) + require.NoError(t, err) + + quorumNumbers := types.QuorumNums{0} + randomOperatorId := types.OperatorId{99} + + tests := []struct { + name string + runFunc func() error + expectError bool + expectedErrorString string + }{ + { + name: "get operator id", + runFunc: func() error { + _, err := chainReader.GetOperatorId(&bind.CallOpts{}, common.Address{}) + return err + }, + expectError: true, + expectedErrorString: elcontracts.CommonErrorMissingContract("RegistryCoordinator"), + }, + { + name: "get operator from id", + runFunc: func() error { + _, err := chainReader.GetOperatorFromId(&bind.CallOpts{}, randomOperatorId) + return err + }, + expectError: true, + expectedErrorString: elcontracts.CommonErrorMissingContract("RegistryCoordinator"), + }, + { + name: "check if operator is registered", + runFunc: func() error { + _, err := chainReader.IsOperatorRegistered(&bind.CallOpts{}, common.Address{}) + return err + }, + expectError: true, + expectedErrorString: elcontracts.CommonErrorMissingContract("RegistryCoordinator"), + }, + { + name: "get quorum state", + runFunc: func() error { + _, err := chainReader.GetQuorumCount(&bind.CallOpts{}) + return err + }, + expectError: true, + expectedErrorString: elcontracts.CommonErrorMissingContract("RegistryCoordinator"), + }, + { + name: "get operator stake in quorums at current block", + runFunc: func() error { + _, err := chainReader.GetOperatorsStakeInQuorumsAtBlock(&bind.CallOpts{}, quorumNumbers, 100) + return err + }, + expectError: true, + expectedErrorString: elcontracts.CommonErrorMissingContract("OperatorStateRetriever"), + }, + { + name: "get operator address in quorums at current block", + runFunc: func() error { + _, err := chainReader.GetOperatorAddrsInQuorumsAtCurrentBlock(&bind.CallOpts{}, quorumNumbers) + return err + }, + expectError: true, + expectedErrorString: elcontracts.CommonErrorMissingContract("OperatorStateRetriever"), + }, + { + name: "get operators stake in quorums of operator at block", + runFunc: func() error { + _, _, err := chainReader.GetOperatorsStakeInQuorumsOfOperatorAtBlock( + &bind.CallOpts{}, + randomOperatorId, + 100, + ) + return err + }, + expectError: true, + expectedErrorString: elcontracts.CommonErrorMissingContract("OperatorStateRetriever"), + }, + { + name: "get single operator stake in quorums of operator at current block", + runFunc: func() error { + _, err := chainReader.GetOperatorStakeInQuorumsOfOperatorAtCurrentBlock( + &bind.CallOpts{}, + randomOperatorId, + ) + return err + }, + expectError: true, + expectedErrorString: elcontracts.CommonErrorMissingContract("RegistryCoordinator"), + }, { + name: "check signatures indices", + runFunc: func() error { + _, err := chainReader.GetCheckSignaturesIndices( + &bind.CallOpts{}, + 100, + quorumNumbers, + []types.OperatorId{randomOperatorId}, + ) + return err + }, + expectError: true, + expectedErrorString: elcontracts.CommonErrorMissingContract("OperatorStateRetriever"), + }, + { + name: "query registered operator sockets", + runFunc: func() error { + _, err := chainReader.QueryExistingRegisteredOperatorSockets( + context.Background(), + big.NewInt(0), + nil, + nil, + ) + return err + }, + expectError: true, + expectedErrorString: elcontracts.CommonErrorMissingContract("RegistryCoordinator"), + }, + } + + for _, tc := range tests { + t.Run(fmt.Sprintf("%s with invalid config", tc.name), func(t *testing.T) { + err := tc.runFunc() + if tc.expectError { + require.Error(t, err, "Expected error for %s", tc.name) + require.Equal(t, err.Error(), tc.expectedErrorString) + } + }) + } } diff --git a/chainio/clients/elcontracts/error.go b/chainio/clients/elcontracts/error.go new file mode 100644 index 000000000..e662955ac --- /dev/null +++ b/chainio/clients/elcontracts/error.go @@ -0,0 +1,59 @@ +package elcontracts + +import "fmt" + +type Error struct { + code int + message string + description string + cause error +} + +func (e Error) Error() string { + if e.cause != nil { + return fmt.Sprintf("%s(%d) - %s: %s", e.message, e.code, e.description, e.cause.Error()) + } + return fmt.Sprintf("%s(%d) - %s", e.message, e.code, e.description) +} + +func (e Error) Unwrap() error { + return e.cause +} + +func CreateErrorForMissingContract(contractName string) Error { + errDescription := fmt.Sprintf("%s contract not provided", contractName) + return Error{1, "Missing needed contract", errDescription, nil} +} + +func CreateForBindingError(bindingName string, errorCause error) Error { + errDescription := fmt.Sprintf("Error happened while calling %s", bindingName) + return Error{ + 0, + "Binding error", + errDescription, + errorCause, + } +} + +func CreateForNestedError(functionName string, errorCause error) Error { + errDescription := fmt.Sprintf("Error happened while calling %s", functionName) + return Error{ + 2, + "Nested error", + errDescription, + errorCause, + } +} + +func CreateForOtherError(errDescription string, errorCause error) Error { + return Error{ + 3, + "Other error", + errDescription, + errorCause, + } +} + +func CommonErrorMissingContract(contractName string) string { + return fmt.Sprintf("Missing needed contract(1) - %s contract not provided", contractName) +} diff --git a/testutils/testclients/testclients.go b/testutils/testclients/testclients.go index 6798e4511..6a5fbf767 100644 --- a/testutils/testclients/testclients.go +++ b/testutils/testclients/testclients.go @@ -238,3 +238,26 @@ func NewTestAvsRegistryWriterFromConfig( } return testWriter, nil } + +// Creates a testing AVSRegistrer ChainReader from an httpEndpoint, private key and config. +func NewTestAvsRegistryReaderFromConfig( + httpEndpoint string, + config avsregistry.Config, +) (*avsregistry.ChainReader, error) { + testConfig := testutils.GetDefaultTestConfig() + logger := logging.NewTextSLogger(os.Stdout, &logging.SLoggerOptions{Level: testConfig.LogLevel}) + ethHttpClient, err := ethclient.Dial(httpEndpoint) + if err != nil { + return nil, utils.WrapError("Failed to create eth client", err) + } + + testReader, err := avsregistry.NewReaderFromConfig( + config, + ethHttpClient, + logger, + ) + if err != nil { + return nil, utils.WrapError("Failed to create chain reader from config", err) + } + return testReader, nil +}