Skip to content

gRPC API Testing Guide with grpcurl #86

@cendhu

Description

@cendhu

gRPC API Testing Guide with grpcurl

This guide provides comprehensive examples for testing all gRPC services in the Fabric-X Common API using grpcurl. Examples are provided for three security configurations: no TLS, TLS, and mutual TLS (mTLS).

Prerequisites

Install grpcurl:

# macOS
brew install grpcurl

# Linux
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest

# Or download from: https://github.com/fullstorydev/grpcurl/releases

Certificate Setup

For TLS and mTLS examples, you'll need certificates. Generate test certificates using:

# Generate CA certificate
openssl req -x509 -newkey rsa:4096 -keyout ca-key.pem -out ca-cert.pem -days 365 -nodes -subj "/CN=Test CA"

# Generate server certificate
openssl req -newkey rsa:4096 -keyout server-key.pem -out server-csr.pem -nodes -subj "/CN=localhost"
openssl x509 -req -in server-csr.pem -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 365

# Generate client certificate (for mTLS)
openssl req -newkey rsa:4096 -keyout client-key.pem -out client-csr.pem -nodes -subj "/CN=client"
openssl x509 -req -in client-csr.pem -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -days 365

Environment Variables

Set these variables for easier command execution:

# Server endpoint
export SERVER_HOST="localhost:9443"

# Certificate paths (adjust as needed)
export CA_CERT="path/to/ca-cert.pem"
export CLIENT_CERT="path/to/client-cert.pem"
export CLIENT_KEY="path/to/client-key.pem"

# Proto import path (adjust to your fabric-x-common location)
export PROTO_PATH="/path/to/fabric-x-common"

1. BlockQueryService (committerpb)

Service Definition: committerpb.BlockQueryService
Proto File: api/committerpb/block_query.proto

This service provides read-only access to the blockchain ledger, allowing queries for blockchain information, blocks, and transactions.

1.1 GetBlockchainInfo

Retrieves information about the blockchain (height, current block hash, previous block hash).

No TLS

grpcurl -plaintext \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/block_query.proto \
  ${SERVER_HOST} \
  committerpb.BlockQueryService/GetBlockchainInfo

TLS

grpcurl \
  -cacert ${CA_CERT} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/block_query.proto \
  ${SERVER_HOST} \
  committerpb.BlockQueryService/GetBlockchainInfo

mTLS

grpcurl \
  -cacert ${CA_CERT} \
  -cert ${CLIENT_CERT} \
  -key ${CLIENT_KEY} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/block_query.proto \
  ${SERVER_HOST} \
  committerpb.BlockQueryService/GetBlockchainInfo

1.2 GetBlockByNumber

Retrieves a specific block by its block number.

No TLS

grpcurl -plaintext \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/block_query.proto \
  -d '{"number": 0}' \
  ${SERVER_HOST} \
  committerpb.BlockQueryService/GetBlockByNumber

TLS

grpcurl \
  -cacert ${CA_CERT} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/block_query.proto \
  -d '{"number": 0}' \
  ${SERVER_HOST} \
  committerpb.BlockQueryService/GetBlockByNumber

mTLS

grpcurl \
  -cacert ${CA_CERT} \
  -cert ${CLIENT_CERT} \
  -key ${CLIENT_KEY} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/block_query.proto \
  -d '{"number": 0}' \
  ${SERVER_HOST} \
  committerpb.BlockQueryService/GetBlockByNumber

1.3 GetBlockByTxID

Retrieves the block containing a specific transaction ID.

No TLS

grpcurl -plaintext \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/block_query.proto \
  -d '{"tx_id": "your-transaction-id-here"}' \
  ${SERVER_HOST} \
  committerpb.BlockQueryService/GetBlockByTxID

TLS

grpcurl \
  -cacert ${CA_CERT} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/block_query.proto \
  -d '{"tx_id": "your-transaction-id-here"}' \
  ${SERVER_HOST} \
  committerpb.BlockQueryService/GetBlockByTxID

mTLS

grpcurl \
  -cacert ${CA_CERT} \
  -cert ${CLIENT_CERT} \
  -key ${CLIENT_KEY} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/block_query.proto \
  -d '{"tx_id": "your-transaction-id-here"}' \
  ${SERVER_HOST} \
  committerpb.BlockQueryService/GetBlockByTxID

1.4 GetTxByID

Retrieves a specific transaction envelope by its transaction ID.

No TLS

grpcurl -plaintext \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/block_query.proto \
  -d '{"tx_id": "your-transaction-id-here"}' \
  ${SERVER_HOST} \
  committerpb.BlockQueryService/GetTxByID

TLS

grpcurl \
  -cacert ${CA_CERT} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/block_query.proto \
  -d '{"tx_id": "your-transaction-id-here"}' \
  ${SERVER_HOST} \
  committerpb.BlockQueryService/GetTxByID

mTLS

grpcurl \
  -cacert ${CA_CERT} \
  -cert ${CLIENT_CERT} \
  -key ${CLIENT_KEY} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/block_query.proto \
  -d '{"tx_id": "your-transaction-id-here"}' \
  ${SERVER_HOST} \
  committerpb.BlockQueryService/GetTxByID

2. Notifier Service (committerpb)

Service Definition: committerpb.Notifier
Proto File: api/committerpb/notify.proto

This service provides a bidirectional streaming API for subscribing to ledger events and receiving asynchronous notifications about transaction statuses.

2.1 OpenNotificationStream

Opens a bidirectional stream for transaction status notifications. This is a streaming RPC where the client sends subscription requests and receives notifications.

Note: Bidirectional streaming requires interactive mode or scripting. Below are examples for both approaches.

No TLS (Interactive)

grpcurl -plaintext \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/notify.proto \
  -d @ \
  ${SERVER_HOST} \
  committerpb.Notifier/OpenNotificationStream

Then input JSON requests interactively:

{"tx_status_request": {"tx_ids": ["tx-id-1", "tx-id-2"]}, "timeout": {"seconds": 30}}

TLS (Interactive)

grpcurl \
  -cacert ${CA_CERT} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/notify.proto \
  -d @ \
  ${SERVER_HOST} \
  committerpb.Notifier/OpenNotificationStream

mTLS (Interactive)

grpcurl \
  -cacert ${CA_CERT} \
  -cert ${CLIENT_CERT} \
  -key ${CLIENT_KEY} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/notify.proto \
  -d @ \
  ${SERVER_HOST} \
  committerpb.Notifier/OpenNotificationStream

Scripted Example (No TLS)

# Create a file with subscription requests
cat > notify_requests.json << EOF
{"tx_status_request": {"tx_ids": ["tx-id-1"]}, "timeout": {"seconds": 30}}
{"tx_status_request": {"tx_ids": ["tx-id-2"]}, "timeout": {"seconds": 30}}
EOF

# Send requests via pipe
cat notify_requests.json | grpcurl -plaintext \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/notify.proto \
  -d @ \
  ${SERVER_HOST} \
  committerpb.Notifier/OpenNotificationStream

Scripted Example (TLS)

cat notify_requests.json | grpcurl \
  -cacert ${CA_CERT} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/notify.proto \
  -d @ \
  ${SERVER_HOST} \
  committerpb.Notifier/OpenNotificationStream

Scripted Example (mTLS)

cat notify_requests.json | grpcurl \
  -cacert ${CA_CERT} \
  -cert ${CLIENT_CERT} \
  -key ${CLIENT_KEY} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/notify.proto \
  -d @ \
  ${SERVER_HOST} \
  committerpb.Notifier/OpenNotificationStream

3. QueryService (committerpb)

Service Definition: committerpb.QueryService
Proto File: api/committerpb/query.proto

This service provides query capabilities for reading state data, managing views (database snapshots), and retrieving configuration and transaction status information.

3.1 GetRows

Queries key-value pairs from specified namespaces, optionally within a view context.

No TLS

grpcurl -plaintext \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/query.proto \
  -d '{
    "namespaces": [
      {
        "ns_id": "namespace1",
        "keys": ["a2V5MQ==", "a2V5Mg=="]
      }
    ]
  }' \
  ${SERVER_HOST} \
  committerpb.QueryService/GetRows

Note: Keys must be base64-encoded. Example: echo -n "key1" | base64 produces a2V5MQ==

TLS

grpcurl \
  -cacert ${CA_CERT} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/query.proto \
  -d '{
    "namespaces": [
      {
        "ns_id": "namespace1",
        "keys": ["a2V5MQ==", "a2V5Mg=="]
      }
    ]
  }' \
  ${SERVER_HOST} \
  committerpb.QueryService/GetRows

mTLS

grpcurl \
  -cacert ${CA_CERT} \
  -cert ${CLIENT_CERT} \
  -key ${CLIENT_KEY} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/query.proto \
  -d '{
    "namespaces": [
      {
        "ns_id": "namespace1",
        "keys": ["a2V5MQ==", "a2V5Mg=="]
      }
    ]
  }' \
  ${SERVER_HOST} \
  committerpb.QueryService/GetRows

With View Context

# First create a view (see BeginView below), then use its ID
grpcurl -plaintext \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/query.proto \
  -d '{
    "view": {"id": "view-id-from-begin-view"},
    "namespaces": [
      {
        "ns_id": "namespace1",
        "keys": ["a2V5MQ=="]
      }
    ]
  }' \
  ${SERVER_HOST} \
  committerpb.QueryService/GetRows

3.2 BeginView

Creates a new view (database snapshot) with specified isolation level and parameters.

No TLS

grpcurl -plaintext \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/query.proto \
  -d '{
    "iso_level": "SERIALIZABLE",
    "non_deferrable": false,
    "timeout_milliseconds": 30000
  }' \
  ${SERVER_HOST} \
  committerpb.QueryService/BeginView

Isolation Levels:

  • ISO_LEVEL_UNSPECIFIED (defaults to SERIALIZABLE)
  • SERIALIZABLE
  • REPEATABLE_READ
  • READ_COMMITTED
  • READ_UNCOMMITTED

TLS

grpcurl \
  -cacert ${CA_CERT} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/query.proto \
  -d '{
    "iso_level": "SERIALIZABLE",
    "non_deferrable": false,
    "timeout_milliseconds": 30000
  }' \
  ${SERVER_HOST} \
  committerpb.QueryService/BeginView

mTLS

grpcurl \
  -cacert ${CA_CERT} \
  -cert ${CLIENT_CERT} \
  -key ${CLIENT_KEY} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/query.proto \
  -d '{
    "iso_level": "REPEATABLE_READ",
    "non_deferrable": true,
    "timeout_milliseconds": 60000
  }' \
  ${SERVER_HOST} \
  committerpb.QueryService/BeginView

3.3 EndView

Closes an active view and releases associated resources.

No TLS

grpcurl -plaintext \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/query.proto \
  -d '{"id": "view-id-from-begin-view"}' \
  ${SERVER_HOST} \
  committerpb.QueryService/EndView

TLS

grpcurl \
  -cacert ${CA_CERT} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/query.proto \
  -d '{"id": "view-id-from-begin-view"}' \
  ${SERVER_HOST} \
  committerpb.QueryService/EndView

mTLS

grpcurl \
  -cacert ${CA_CERT} \
  -cert ${CLIENT_CERT} \
  -key ${CLIENT_KEY} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/query.proto \
  -d '{"id": "view-id-from-begin-view"}' \
  ${SERVER_HOST} \
  committerpb.QueryService/EndView

3.4 GetNamespacePolicies

Retrieves the policies for all namespaces.

No TLS

grpcurl -plaintext \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/query.proto \
  ${SERVER_HOST} \
  committerpb.QueryService/GetNamespacePolicies

TLS

grpcurl \
  -cacert ${CA_CERT} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/query.proto \
  ${SERVER_HOST} \
  committerpb.QueryService/GetNamespacePolicies

mTLS

grpcurl \
  -cacert ${CA_CERT} \
  -cert ${CLIENT_CERT} \
  -key ${CLIENT_KEY} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/query.proto \
  ${SERVER_HOST} \
  committerpb.QueryService/GetNamespacePolicies

3.5 GetConfigTransaction

Retrieves the current configuration transaction envelope.

No TLS

grpcurl -plaintext \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/query.proto \
  ${SERVER_HOST} \
  committerpb.QueryService/GetConfigTransaction

TLS

grpcurl \
  -cacert ${CA_CERT} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/query.proto \
  ${SERVER_HOST} \
  committerpb.QueryService/GetConfigTransaction

mTLS

grpcurl \
  -cacert ${CA_CERT} \
  -cert ${CLIENT_CERT} \
  -key ${CLIENT_KEY} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/query.proto \
  ${SERVER_HOST} \
  committerpb.QueryService/GetConfigTransaction

3.6 GetTransactionStatus

Queries the status of one or more transactions, optionally within a view context.

No TLS

grpcurl -plaintext \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/query.proto \
  -d '{
    "tx_ids": ["tx-id-1", "tx-id-2", "tx-id-3"]
  }' \
  ${SERVER_HOST} \
  committerpb.QueryService/GetTransactionStatus

TLS

grpcurl \
  -cacert ${CA_CERT} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/query.proto \
  -d '{
    "tx_ids": ["tx-id-1", "tx-id-2"]
  }' \
  ${SERVER_HOST} \
  committerpb.QueryService/GetTransactionStatus

mTLS

grpcurl \
  -cacert ${CA_CERT} \
  -cert ${CLIENT_CERT} \
  -key ${CLIENT_KEY} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/query.proto \
  -d '{
    "tx_ids": ["tx-id-1"]
  }' \
  ${SERVER_HOST} \
  committerpb.QueryService/GetTransactionStatus

With View Context

grpcurl -plaintext \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/query.proto \
  -d '{
    "view": {"id": "view-id-from-begin-view"},
    "tx_ids": ["tx-id-1", "tx-id-2"]
  }' \
  ${SERVER_HOST} \
  committerpb.QueryService/GetTransactionStatus

Service Discovery

List all available services on a server:

No TLS

grpcurl -plaintext ${SERVER_HOST} list

TLS

grpcurl -cacert ${CA_CERT} ${SERVER_HOST} list

mTLS

grpcurl -cacert ${CA_CERT} -cert ${CLIENT_CERT} -key ${CLIENT_KEY} ${SERVER_HOST} list

Describe a specific service:

No TLS

grpcurl -plaintext \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/query.proto \
  ${SERVER_HOST} \
  describe committerpb.QueryService

TLS

grpcurl \
  -cacert ${CA_CERT} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/query.proto \
  ${SERVER_HOST} \
  describe committerpb.QueryService

mTLS

grpcurl \
  -cacert ${CA_CERT} \
  -cert ${CLIENT_CERT} \
  -key ${CLIENT_KEY} \
  -import-path ${PROTO_PATH} \
  -proto api/committerpb/query.proto \
  ${SERVER_HOST} \
  describe committerpb.QueryService

Common Options

Verbose Output

Add -v or -vv for verbose output:

grpcurl -plaintext -v ${SERVER_HOST} committerpb.QueryService/GetNamespacePolicies

Custom Headers

Add custom metadata headers:

grpcurl -plaintext \
  -H "authorization: Bearer token123" \
  -H "x-custom-header: value" \
  ${SERVER_HOST} \
  committerpb.QueryService/GetNamespacePolicies

Timeout

Set request timeout:

grpcurl -plaintext \
  -max-time 30 \
  ${SERVER_HOST} \
  committerpb.QueryService/GetNamespacePolicies

Output Formatting

Format output with jq:

grpcurl -plaintext ${SERVER_HOST} \
  committerpb.BlockQueryService/GetBlockchainInfo | jq .

Troubleshooting

Connection Refused

  • Verify the server is running: netstat -an | grep 9443
  • Check firewall rules
  • Verify the correct host and port

Certificate Errors

  • Ensure certificate paths are correct
  • Verify certificate validity: openssl x509 -in ${CA_CERT} -text -noout
  • Check certificate chain: openssl verify -CAfile ${CA_CERT} ${CLIENT_CERT}
  • For self-signed certificates, ensure the CA cert is properly specified

Proto Import Errors

  • Verify PROTO_PATH points to the fabric-x-common root directory
  • Ensure all proto dependencies are available
  • Check that proto files haven't been modified

Authentication Errors

  • For mTLS, verify both client cert and key are provided
  • Ensure client certificate is signed by the CA trusted by the server
  • Check that the certificate hasn't expired

Invalid Argument Errors

  • Validate JSON syntax: echo '{"key": "value"}' | jq .
  • Ensure field names match proto definitions exactly (case-sensitive)
  • Verify data types (strings, numbers, booleans)
  • For bytes fields, use base64 encoding

Additional Resources


Summary of Services

Service Proto File RPCs Description
BlockQueryService committerpb/block_query.proto 4 Read-only blockchain queries
Notifier committerpb/notify.proto 1 (streaming) Transaction status notifications
QueryService committerpb/query.proto 6 State queries and view management

Total: 3 services, 11 RPC methods (including 1 bidirectional streaming)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions