package keeper import ( "context" "fmt" "cosmossdk.io/errors" "cosmossdk.io/orm/types/ormerrors" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/query" "github.com/ipfs/go-cid" apiv1 "github.com/sonr-io/sonr/api/dwn/v1" "github.com/sonr-io/sonr/x/dwn/types" ) var _ types.QueryServer = Querier{} type Querier struct { Keeper } func NewQuerier(keeper Keeper) Querier { return Querier{Keeper: keeper} } func (k Querier) Params( c context.Context, req *types.QueryParamsRequest, ) (*types.QueryParamsResponse, error) { ctx := sdk.UnwrapSDKContext(c) p, err := k.Keeper.Params.Get(ctx) if err != nil { return nil, err } return &types.QueryParamsResponse{Params: &p}, nil } // Records queries DWN records with filters func (k Querier) Records( c context.Context, req *types.QueryRecordsRequest, ) (*types.QueryRecordsResponse, error) { if req == nil { return nil, types.ErrRequestCannotBeNil } if req.Target == "" { return nil, types.ErrTargetDIDEmpty } ctx := sdk.UnwrapSDKContext(c) // Build index key based on filters var indexKey apiv1.DWNRecordIndexKey if req.Protocol != "" { indexKey = apiv1.DWNRecordTargetProtocolIndexKey{}.WithTargetProtocol( req.Target, req.Protocol, ) } else if req.Schema != "" { indexKey = apiv1.DWNRecordTargetSchemaIndexKey{}.WithTargetSchema(req.Target, req.Schema) } else if req.ParentId != "" { indexKey = apiv1.DWNRecordParentIdIndexKey{}.WithParentId(req.ParentId) } else { indexKey = apiv1.DWNRecordTargetProtocolIndexKey{}.WithTarget(req.Target) } // Query with pagination pageReq := req.Pagination if pageReq == nil { pageReq = &query.PageRequest{Limit: 100} } records := []types.DWNRecord{} pageRes := &query.PageResponse{} iter, err := k.OrmDB.DWNRecordTable().List(ctx, indexKey) if err != nil { return nil, errors.Wrap(err, "failed to list records") } defer iter.Close() count := uint64(0) offset := pageReq.Offset limit := pageReq.Limit for iter.Next() { record, err := iter.Value() if err != nil { continue } // Apply published filter if req.PublishedOnly && !record.Published { continue } count++ // Handle pagination if count <= offset { continue } if uint64(len(records)) >= limit { pageRes.NextKey = []byte(record.RecordId) break } records = append(records, types.ConvertAPIRecordToType(record)) } pageRes.Total = count return &types.QueryRecordsResponse{ Records: records, Pagination: pageRes, }, nil } // Record queries a specific DWN record by ID func (k Querier) Record( c context.Context, req *types.QueryRecordRequest, ) (*types.QueryRecordResponse, error) { if req == nil { return nil, types.ErrRequestCannotBeNil } if req.Target == "" { return nil, types.ErrTargetDIDEmpty } if req.RecordId == "" { return nil, types.ErrRecordIDEmpty } ctx := sdk.UnwrapSDKContext(c) record, err := k.OrmDB.DWNRecordTable().Get(ctx, req.RecordId) if err != nil { if ormerrors.IsNotFound(err) { return nil, errors.Wrapf(types.ErrRecordNotFound, "record %s not found", req.RecordId) } return nil, errors.Wrap(err, "failed to get record") } // Verify the record belongs to the target DWN if record.Target != req.Target { return nil, errors.Wrap(sdkerrors.ErrUnauthorized, "record does not belong to target DWN") } rec := types.ConvertAPIRecordToType(record) return &types.QueryRecordResponse{ Record: &rec, }, nil } // Protocols queries DWN protocols func (k Querier) Protocols( c context.Context, req *types.QueryProtocolsRequest, ) (*types.QueryProtocolsResponse, error) { if req == nil { return nil, types.ErrRequestCannotBeNil } if req.Target == "" { return nil, types.ErrTargetDIDEmpty } ctx := sdk.UnwrapSDKContext(c) // Query with pagination pageReq := req.Pagination if pageReq == nil { pageReq = &query.PageRequest{Limit: 100} } protocols := []types.DWNProtocol{} pageRes := &query.PageResponse{} indexKey := apiv1.DWNProtocolTargetProtocolUriIndexKey{}.WithTarget(req.Target) iter, err := k.OrmDB.DWNProtocolTable().List(ctx, indexKey) if err != nil { return nil, errors.Wrap(err, "failed to list protocols") } defer iter.Close() count := uint64(0) offset := pageReq.Offset limit := pageReq.Limit for iter.Next() { protocol, err := iter.Value() if err != nil { continue } // Apply published filter if req.PublishedOnly && !protocol.Published { continue } count++ // Handle pagination if count <= offset { continue } if uint64(len(protocols)) >= limit { pageRes.NextKey = []byte(protocol.ProtocolUri) break } protocols = append(protocols, types.ConvertAPIProtocolToType(protocol)) } pageRes.Total = count return &types.QueryProtocolsResponse{ Protocols: protocols, Pagination: pageRes, }, nil } // Protocol queries a specific DWN protocol func (k Querier) Protocol( c context.Context, req *types.QueryProtocolRequest, ) (*types.QueryProtocolResponse, error) { if req == nil { return nil, types.ErrRequestCannotBeNil } if req.Target == "" { return nil, types.ErrTargetDIDEmpty } if req.ProtocolUri == "" { return nil, types.ErrProtocolURIEmpty } ctx := sdk.UnwrapSDKContext(c) protocol, err := k.OrmDB.DWNProtocolTable().Get(ctx, req.Target, req.ProtocolUri) if err != nil { if ormerrors.IsNotFound(err) { return nil, errors.Wrapf( types.ErrProtocolNotFound, "protocol %s not found", req.ProtocolUri, ) } return nil, errors.Wrap(err, "failed to get protocol") } prot := types.ConvertAPIProtocolToType(protocol) return &types.QueryProtocolResponse{ Protocol: &prot, }, nil } // Permissions queries DWN permissions func (k Querier) Permissions( c context.Context, req *types.QueryPermissionsRequest, ) (*types.QueryPermissionsResponse, error) { if req == nil { return nil, types.ErrRequestCannotBeNil } if req.Target == "" { return nil, types.ErrTargetDIDEmpty } ctx := sdk.UnwrapSDKContext(c) // Build index key based on filters var indexKey apiv1.DWNPermissionIndexKey if req.Grantor != "" && req.Grantee != "" { indexKey = apiv1.DWNPermissionGrantorGranteeIndexKey{}.WithGrantorGrantee( req.Grantor, req.Grantee, ) } else if req.Grantor != "" { indexKey = apiv1.DWNPermissionGrantorGranteeIndexKey{}.WithGrantor(req.Grantor) } else if req.InterfaceName != "" && req.Method != "" { indexKey = apiv1.DWNPermissionTargetInterfaceNameMethodIndexKey{}.WithTargetInterfaceNameMethod(req.Target, req.InterfaceName, req.Method) } else if req.InterfaceName != "" { indexKey = apiv1.DWNPermissionTargetInterfaceNameMethodIndexKey{}.WithTargetInterfaceName(req.Target, req.InterfaceName) } else { indexKey = apiv1.DWNPermissionTargetInterfaceNameMethodIndexKey{}.WithTarget(req.Target) } // Query with pagination pageReq := req.Pagination if pageReq == nil { pageReq = &query.PageRequest{Limit: 100} } permissions := []types.DWNPermission{} pageRes := &query.PageResponse{} iter, err := k.OrmDB.DWNPermissionTable().List(ctx, indexKey) if err != nil { return nil, errors.Wrap(err, "failed to list permissions") } defer iter.Close() count := uint64(0) offset := pageReq.Offset limit := pageReq.Limit for iter.Next() { permission, err := iter.Value() if err != nil { continue } // Apply filters if permission.Target != req.Target { continue } if !req.IncludeRevoked && permission.Revoked { continue } count++ // Handle pagination if count <= offset { continue } if uint64(len(permissions)) >= limit { pageRes.NextKey = []byte(permission.PermissionId) break } permissions = append(permissions, types.ConvertAPIPermissionToType(permission)) } pageRes.Total = count return &types.QueryPermissionsResponse{ Permissions: permissions, Pagination: pageRes, }, nil } // Vault queries a specific vault func (k Querier) Vault( c context.Context, req *types.QueryVaultRequest, ) (*types.QueryVaultResponse, error) { if req == nil { return nil, types.ErrRequestCannotBeNil } if req.VaultId == "" { return nil, types.ErrVaultIDEmpty } ctx := sdk.UnwrapSDKContext(c) vault, err := k.OrmDB.VaultStateTable().Get(ctx, req.VaultId) if err != nil { if ormerrors.IsNotFound(err) { return nil, errors.Wrapf(types.ErrVaultNotFound, "vault %s not found", req.VaultId) } return nil, errors.Wrap(err, "failed to get vault") } vlt := types.ConvertAPIVaultToType(vault) return &types.QueryVaultResponse{ Vault: &vlt, }, nil } // Vaults queries vaults by owner func (k Querier) Vaults( c context.Context, req *types.QueryVaultsRequest, ) (*types.QueryVaultsResponse, error) { if req == nil { return nil, types.ErrRequestCannotBeNil } ctx := sdk.UnwrapSDKContext(c) // Query with pagination pageReq := req.Pagination if pageReq == nil { pageReq = &query.PageRequest{Limit: 100} } vaults := []types.VaultState{} pageRes := &query.PageResponse{} var iter apiv1.VaultStateIterator var err error if req.Owner != "" { indexKey := apiv1.VaultStateOwnerIndexKey{}.WithOwner(req.Owner) iter, err = k.OrmDB.VaultStateTable().List(ctx, indexKey) } else { // List all vaults indexKey := apiv1.VaultStatePrimaryKey{} iter, err = k.OrmDB.VaultStateTable().List(ctx, indexKey) } if err != nil { return nil, errors.Wrap(err, "failed to list vaults") } defer iter.Close() count := uint64(0) offset := pageReq.Offset limit := pageReq.Limit for iter.Next() { vault, err := iter.Value() if err != nil { continue } count++ // Handle pagination if count <= offset { continue } if uint64(len(vaults)) >= limit { pageRes.NextKey = []byte(vault.VaultId) break } vaults = append(vaults, types.ConvertAPIVaultToType(vault)) } pageRes.Total = count return &types.QueryVaultsResponse{ Vaults: vaults, Pagination: pageRes, }, nil } // TODO: Implement IPFS query functionality - connects to IPFS nodes and retrieves status information // Should integrate with internal/ipfs package for IPFS client operations // Query IPFS node health, connectivity, and peer information // Return storage statistics and pinned content summary // Support multiple IPFS endpoints with failover capability // IPFS implements types.QueryServer. func (k Querier) IPFS( goCtx context.Context, req *types.QueryIPFSRequest, ) (*types.QueryIPFSResponse, error) { // Check if IPFS client is available if k.ipfsClient == nil { k.Logger().Debug("IPFS client not available") return &types.QueryIPFSResponse{ Status: &types.IPFSStatus{ PeerId: "", PeerName: "unavailable", PeerType: "ipfs", Version: "unknown", }, }, nil } // Get node status using the new NodeStatus method nodeStatus, err := k.ipfsClient.NodeStatus() if err != nil { k.Logger().Error("Failed to get IPFS node status", "error", err) return &types.QueryIPFSResponse{ Status: &types.IPFSStatus{ PeerId: "", PeerName: "connection_failed", PeerType: "ipfs", Version: "unknown", }, }, nil } // Convert from internal NodeStatus to types.IPFSStatus status := &types.IPFSStatus{ PeerId: nodeStatus.PeerID, PeerName: "kubo-node", PeerType: nodeStatus.PeerType, Version: nodeStatus.Version, } // Log successful status retrieval for debugging k.Logger().Debug("IPFS node status retrieved successfully", "peer_id", status.PeerId, "version", status.Version, "peer_type", status.PeerType, "connected_peers", nodeStatus.ConnectedPeers, ) return &types.QueryIPFSResponse{ Status: status, }, nil } // TODO: Implement CID query functionality - retrieves data from IPFS using content identifiers // Should validate CID format and check if content exists in IPFS // Retrieve content metadata without downloading full data // Support different CID versions and hash algorithms // Include content size, availability, and pin status // CID implements types.QueryServer. func (k Querier) CID( goCtx context.Context, req *types.QueryCIDRequest, ) (*types.QueryCIDResponse, error) { // Validate input if req == nil || req.Cid == "" { return &types.QueryCIDResponse{ StatusCode: 400, // Bad Request Data: nil, }, nil } // Validate CID format using go-cid library _, err := cid.Parse(req.Cid) if err != nil { k.Logger().Debug("Invalid CID format", "cid", req.Cid, "error", err) return &types.QueryCIDResponse{ StatusCode: 400, // Bad Request Data: nil, }, nil } // Get IPFS client with connectivity check ipfsClient, err := k.GetIPFSClient() if err != nil { k.Logger().Error("IPFS client not available", "error", err) return &types.QueryCIDResponse{ StatusCode: 500, // Internal Server Error Data: nil, }, nil } // Check if content exists in IPFS exists, err := ipfsClient.Exists(req.Cid) if err != nil { k.Logger().Error("Error checking CID existence", "cid", req.Cid, "error", err) return &types.QueryCIDResponse{ StatusCode: 500, // Internal Server Error Data: nil, }, nil } if !exists { k.Logger().Debug("CID not found in IPFS", "cid", req.Cid) return &types.QueryCIDResponse{ StatusCode: 404, // Not Found Data: nil, }, nil } // Retrieve content from IPFS data, err := ipfsClient.Get(req.Cid) if err != nil { k.Logger().Error("Error retrieving content from IPFS", "cid", req.Cid, "error", err) return &types.QueryCIDResponse{ StatusCode: 500, // Internal Server Error Data: nil, }, nil } k.Logger().Debug("Successfully retrieved content from IPFS", "cid", req.Cid, "size", len(data)) return &types.QueryCIDResponse{ StatusCode: 200, // Success Data: data, }, nil } // EncryptedRecord queries a specific encrypted record with automatic decryption func (k Querier) EncryptedRecord( c context.Context, req *types.QueryEncryptedRecordRequest, ) (*types.QueryEncryptedRecordResponse, error) { if req == nil { return nil, types.ErrRequestCannotBeNil } if req.Target == "" { return nil, types.ErrTargetDIDEmpty } if req.RecordId == "" { return nil, types.ErrRecordIDEmpty } ctx := sdk.UnwrapSDKContext(c) // Get the record first record, err := k.OrmDB.DWNRecordTable().Get(ctx, req.RecordId) if err != nil { if ormerrors.IsNotFound(err) { return nil, errors.Wrapf(types.ErrRecordNotFound, "record %s not found", req.RecordId) } return nil, errors.Wrap(err, "failed to get record") } // Verify the record belongs to the target DWN if record.Target != req.Target { return nil, errors.Wrap(sdkerrors.ErrUnauthorized, "record does not belong to target DWN") } rec := types.ConvertAPIRecordToType(record) // Check if the record has encryption metadata if record.EncryptionMetadata == nil { return &types.QueryEncryptedRecordResponse{ Record: &rec, WasDecrypted: false, }, nil } // If return_encrypted is true, return the encrypted record without decryption if req.ReturnEncrypted { return &types.QueryEncryptedRecordResponse{ Record: &rec, EncryptionMetadata: types.ConvertAPIEncryptionMetadataToType(record.EncryptionMetadata), WasDecrypted: false, }, nil } // Attempt to decrypt the record data if we have encryption subkeeper if k.encryptionSubkeeper != nil && len(rec.Data) > 0 { decryptedData, err := k.encryptionSubkeeper.DecryptWithConsensusKey( c, rec.Data, types.ConvertAPIEncryptionMetadataToType(record.EncryptionMetadata), ) if err != nil { k.Logger().Error("Failed to decrypt record data", "record_id", req.RecordId, "error", err, ) // Return the encrypted record if decryption fails return &types.QueryEncryptedRecordResponse{ Record: &rec, EncryptionMetadata: types.ConvertAPIEncryptionMetadataToType( record.EncryptionMetadata, ), WasDecrypted: false, }, nil } // Update record with decrypted data rec.Data = decryptedData return &types.QueryEncryptedRecordResponse{ Record: &rec, EncryptionMetadata: types.ConvertAPIEncryptionMetadataToType(record.EncryptionMetadata), WasDecrypted: true, }, nil } // No encryption subkeeper or no encrypted data return &types.QueryEncryptedRecordResponse{ Record: &rec, WasDecrypted: false, }, nil } // EncryptionStatus queries current encryption key state and version func (k Querier) EncryptionStatus( c context.Context, req *types.QueryEncryptionStatusRequest, ) (*types.QueryEncryptionStatusResponse, error) { if req == nil { return nil, types.ErrRequestCannotBeNil } // Default response with minimal information response := &types.QueryEncryptionStatusResponse{ CurrentKeyVersion: 0, ValidatorSet: []string{}, SingleNodeMode: true, LastRotation: 0, NextRotation: 0, TotalEncryptedRecords: 0, } // If we have encryption subkeeper, get detailed status if k.encryptionSubkeeper != nil { // Get current key version response.CurrentKeyVersion = k.encryptionSubkeeper.GetCurrentKeyVersion(c) // Check if single node mode response.SingleNodeMode = k.encryptionSubkeeper.isSingleNodeMode(c) // Get validator set validators, err := k.encryptionSubkeeper.getActiveValidators(c) if err == nil { validatorAddrs := make([]string, len(validators)) for i, v := range validators { validatorAddrs[i] = fmt.Sprintf("%v", v) } response.ValidatorSet = validatorAddrs } // Get stored key state for rotation timestamps keyState, keyErr := k.encryptionSubkeeper.getStoredKeyState(c) if keyErr == nil { response.LastRotation = keyState.LastRotation response.NextRotation = keyState.NextRotation } // Get encryption statistics from the encryption subkeeper stats, statsErr := k.encryptionSubkeeper.GetEncryptionStats(c) if statsErr == nil { // Safely convert int64 to uint64 if stats.TotalEncryptedRecords < 0 { response.TotalEncryptedRecords = 0 } else { response.TotalEncryptedRecords = uint64(stats.TotalEncryptedRecords) } } } k.Logger().Debug("Retrieved encryption status", "key_version", response.CurrentKeyVersion, "single_node_mode", response.SingleNodeMode, "validator_count", len(response.ValidatorSet), ) return response, nil } // VRFContributions lists VRF contributions for current consensus round func (k Querier) VRFContributions( c context.Context, req *types.QueryVRFContributionsRequest, ) (*types.QueryVRFContributionsResponse, error) { if req == nil { return nil, types.ErrRequestCannotBeNil } ctx := sdk.UnwrapSDKContext(c) // Query VRF contributions from the database using filters var indexKey apiv1.VRFContributionIndexKey switch { case req.ValidatorAddress != "" && req.BlockHeight > 0: // Filter by both validator address and block height indexKey = apiv1.VRFContributionValidatorAddressBlockHeightIndexKey{}. WithValidatorAddressBlockHeight(req.ValidatorAddress, req.BlockHeight) case req.ValidatorAddress != "": // Filter by validator address only indexKey = apiv1.VRFContributionValidatorAddressBlockHeightIndexKey{}. WithValidatorAddress(req.ValidatorAddress) case req.BlockHeight > 0: // Filter by block height only indexKey = apiv1.VRFContributionBlockHeightIndexKey{}. WithBlockHeight(req.BlockHeight) default: // No filters, list all contributions indexKey = apiv1.VRFContributionPrimaryKey{} } // Query with pagination pageReq := req.Pagination if pageReq == nil { pageReq = &query.PageRequest{Limit: 100} } contributions := []types.VRFContribution{} iter, err := k.OrmDB.VRFContributionTable().List(c, indexKey) if err != nil { return nil, fmt.Errorf("failed to list VRF contributions: %w", err) } defer iter.Close() count := uint64(0) offset := pageReq.Offset limit := pageReq.Limit for iter.Next() { contrib, iterErr := iter.Value() if iterErr != nil { continue } count++ // Handle pagination if count <= offset { continue } if uint64(len(contributions)) >= limit { break } // Convert from API type to types contributions = append(contributions, types.VRFContribution{ ValidatorAddress: contrib.ValidatorAddress, Randomness: contrib.Randomness, Proof: contrib.Proof, BlockHeight: contrib.BlockHeight, Timestamp: contrib.Timestamp, }) } // Get current consensus round information from database blockHeight := ctx.BlockHeight() var roundNumber uint64 if blockHeight > 0 { // Safe conversion: blockHeight is positive int64, division result fits in uint64 roundNumber = uint64(blockHeight) / 100 } var currentRound *types.VRFConsensusRound // Try to get stored consensus round from database storedRound, roundErr := k.OrmDB.VRFConsensusRoundTable().Get(c, roundNumber) if roundErr == nil { // Convert from API type currentRound = &types.VRFConsensusRound{ RoundNumber: storedRound.RoundNumber, RequiredContributions: storedRound.RequiredContributions, ReceivedContributions: storedRound.ReceivedContributions, Status: storedRound.Status, ExpiryHeight: storedRound.ExpiryHeight, } } else { // Create new round information if not found in database // Safely convert contribution count to uint32 var receivedContributions uint32 contributionCount := len(contributions) switch { case contributionCount > 4294967295: // Max uint32 receivedContributions = 4294967295 case contributionCount < 0: receivedContributions = 0 default: receivedContributions = uint32(contributionCount) } currentRound = &types.VRFConsensusRound{ RoundNumber: roundNumber, RequiredContributions: 1, ReceivedContributions: receivedContributions, Status: "waiting_for_contributions", ExpiryHeight: ctx.BlockHeight() + 100, } // Calculate required contributions based on active validators if k.encryptionSubkeeper != nil { validators, validatorErr := k.encryptionSubkeeper.getActiveValidators(c) if validatorErr == nil { validatorCount := len(validators) if validatorCount > 1 { // Byzantine fault tolerance: need 2/3 + 1 contributions bftThreshold := (validatorCount * 2 / 3) + 1 if bftThreshold > 0 && bftThreshold <= int(^uint32(0)) { currentRound.RequiredContributions = uint32(bftThreshold) } } // Update status based on single node mode if k.encryptionSubkeeper.isSingleNodeMode(c) { currentRound.Status = "single_node_mode" currentRound.ReceivedContributions = 1 } else if currentRound.ReceivedContributions >= currentRound.RequiredContributions { currentRound.Status = "completed" } } } } pageRes := &query.PageResponse{ Total: uint64(len(contributions)), } k.Logger().Debug("Retrieved VRF contributions", "contributions_count", len(contributions), "round_number", currentRound.RoundNumber, "required_contributions", currentRound.RequiredContributions, ) return &types.QueryVRFContributionsResponse{ Contributions: contributions, CurrentRound: currentRound, Pagination: pageRes, }, nil }