Snowblossom Docs
Docs › Running software › gRPC API (node)

gRPC API (node)

Every node exposes UserService over gRPC on its service ports (2338 plaintext, 2348 TLS on mainnet). It is what wallets, miners, pools and explorers use, and it is the API to program against directly when the wallet's JSON-RPC is not enough. The definitions are in protolib/snowblossom.proto; generated clients exist for any language gRPC supports.

git clone https://github.com/snowblossomcoin/snowblossom
# Python example
pip install grpcio grpcio-tools
python -m grpc_tools.protoc -I snowblossom --python_out=. --grpc_python_out=. \
    snowblossom/protolib/snowblossom.proto snowblossom/protolib/trie.proto
python - <<'EOF'
import grpc, protolib.snowblossom_pb2 as pb, protolib.snowblossom_pb2_grpc as rpc
stub = rpc.UserServiceStub(grpc.insecure_channel('localhost:2338'))
st = stub.GetNodeStatus(pb.NullRequest())
print(st.head_summary.header.block_height, st.connected_peers, dict(st.version_map))
EOF

No authentication, no rate limiting - protect public nodes at the network level if needed. Maximum message size is the network's max block size + 1,000,000 bytes. Several calls return onNext(null) for unknown items, which surfaces to clients as a gRPC error rather than an empty message.

Status and chain data

RPCRequest → responseNotes
GetNodeStatusNullRequest → NodeStatusmem_pool_size, connected_peers, estimated_nodes (distinct node ids in the peer list), node_version, version_map (version → node count), network, head_summary (shard 0's head BlockSummary incl. header, work_sum, averages, activated_field), net_shard_head_map, shard_head_map, network_active_shards, interest_shards.
GetBlockRequestBlock{block_hash | block_height, shard_id} → BlockFull block by hash or by (shard, height). Unknown → error.
GetBlockHeaderRequestBlockHeader{block_height | block_hash, shard_id} → BlockHeaderHeader only; also answers for headers the node has seen but not validated.
GetBlockSummaryRequestBlockSummary{block_hash} → BlockSummaryThe node's per-block state (summary fields).
GetTransactionRequestTransaction{tx_hash} → TransactionFrom the transaction index (needs tx_index) or the mempool.
GetTransactionStatusRequestTransaction → TransactionStatus{unknown | mempool | confirmed, height_confirmed, confirmations}Needs tx_index; consults shard 0's index only. confirmations = head − height + 1.
GetFeeEstimateNullRequest → FeeEstimate{fee_per_byte, shard_map}Flakes per byte; minimum 2.5, recomputed every 30 s from the mempool. fee_per_byte is shard 0; shard_map has every building shard.
GetPeerListPeerListRequest{desired_results, trustnet_ids} → PeerList{peers}Up to min(100, desired_results) random known peers. Added in 2.2; releases up to 2.2.0 never complete the call (missing onCompleted) - use an async stub with a deadline.

Addresses and the UTXO set

RPCRequest → responseNotes
GetUTXONodeGetUTXONodeRequest{prefix, include_proof, max_results, utxo_root_hash | shard_id} → GetUTXONodeReply{utxo_root_hash, answer[TrieNode], proof[TrieNode]}Enumerate UTXO trie nodes under a key prefix - pass the 20-byte address to list an address's outputs (keys are address ‖ tx_id ‖ idx). max_results capped at 10,000 (0 = 10,000); with include_proof the nodes from the root down are returned so the client can verify every hash against a header's utxo_root_hash. Specify utxo_root_hash to query a past state or keep a consistent view across calls. (“Series of Queries will be my new anti-folk acapella band.”)
GetUTXONodeMultisame request with all_shards=trueGetUTXOReplyList{reply_map}One reply per building shard.
GetAddressHistoryRequestAddress{address_spec_hash} → HistoryList{entries[{block_height, tx_hash, block_hash, shard}], not_enabled}Needs addr_index; up to 10,000 entries per shard, unordered. Without the index the list is simply empty.
GetMempoolTransactionList / …MapRequestAddress → TransactionHashList / TransactionShardMapUnconfirmed transactions touching the address (inputs or outputs), optionally grouped by shard.
SubscribeAddressUpdatesRequestAddress → stream AddressUpdate{address, mempool_changes, current_utxo_root}Push notification: one update immediately, then one whenever a block or mempool transaction involves the address. Combine with GetAddressHistory/mempool calls to fetch details.
GetFBOListRequestAddress → TxOutList{out_list[{out, tx_hash, out_idx}]}Unspent outputs marked for-benefit-of the address.
GetIDListRequestNameID{name_type, name} → TxOutListUnspent outputs claiming a username or channel name, oldest first.

Submitting

RPCRequest → responseNotes
SubmitTransactionTransaction → SubmitReply{success, error_message}Validates and adds to the mempool, then relays. Errors: validation text, “mempool is full”, “mempool is too full for low fee transactions”, “no mempool accepted” (duplicate or no shard took it).
SubmitBlockBlock → SubmitReplyFull validation and ingestion; on failure error_message = "Rejecting block: …".
SubscribeBlockTemplateSubscribeBlockTemplateRequest → stream BlockTemplates for miners; see mining protocols. Nothing is sent until the node is synced.
SubscribeBlockTemplateStream / …Extendedstream request → stream Block / stream BlockTemplateBidirectional variants: the client updates its request (pay ratios, extras) on the same stream; Extended adds advances_shard.

Key messages

message NodeStatus {
  int32 mem_pool_size = 1;  int32 connected_peers = 2;  BlockSummary head_summary = 3;
  int32 estimated_nodes = 4;  map<string,int32> version_map = 5;  string node_version = 6;
  string network = 7;  map<int32,bytes> net_shard_head_map = 8;  map<int32,bytes> shard_head_map = 9;
  repeated int32 network_active_shards = 10;  repeated int32 interest_shards = 11;
}
message BlockSummary {
  string work_sum = 1;  int64 blocktime_average_ms = 2;  string target_average = 3;
  int32 activated_field = 4;  BlockHeader header = 5;  int64 total_transactions = 6;
  bytes chain_index_trie_hash = 7;  int32 summary_version = 8;
  map<int32, BlockHeader> imported_shards = 9;  int64 tx_size_average = 10;  int32 shard_length = 11; …
}
message TrieNode { bytes hash; bytes prefix; repeated ChildEntry children{key, hash}; bool is_leaf; bytes leaf_data; }

Verifying UTXO answers

A light client should never take balances on trust. The reference implementation (client/src/GetUTXOUtil.java) does: (1) GetNodeStatus → per-shard head hashes; (2) GetBlockHeader → the head's utxo_root_hash; (3) GetUTXONode{prefix=address, include_proof=true, utxo_root_hash=root}; (4) recompute the hash of every returned node (SHA-256(prefix ‖ leaf_data ‖ child_key ‖ child_hash …), see the UTXO trie) and descend from the root checking that each child hash matches its parent's entry; (5) if the 10,000-node cap was hit, query sub-prefixes. Racing several nodes and taking the one with the most work further limits what a single bad node can do.

Connecting with TLS

Use a TLS channel with a custom trust manager that implements the certificate check described under TLS and node identities - the Java SnowTrustManagerFactorySpi is the reference - or, from other languages, connect with plaintext over a trusted network/tunnel. Public nodes with pinned identities are listed on Networks.