P2P networking & TLS
Nodes talk to each other over a single bidirectional gRPC stream carrying protobuf messages. The same ports also serve wallets, miners and explorers (UserService). Connections can be plaintext or TLS, and TLS certificates are bound to Snowblossom keys rather than to hostnames, so a client can pin the identity of the node it talks to.
Transport and ports
| Mainnet | Testnet | Regtest | |
|---|---|---|---|
Plaintext gRPC (service_port) | 2338 | 2339 | 2340 |
TLS gRPC (tls_service_port) | 2348 | 2349 | 2350 |
| Public seed nodes additionally | 80 (grpc) and 443 (grpc+tls) | ||
| MrPlow pool | 23380 TCP / 23382 TLS | ||
| Arktika word server | 2311 | ||
Both services (PeerService for nodes, UserService for everyone else) are bound on every configured port. Inbound connections need the port reachable; the node also attempts a UPnP mapping named “snowblossom.node” at start and warns if the port is mapped to another host. Message size limit: max block size + 1,000,000 bytes (4.8 MB on mainnet). gRPC is HTTP/2, so a reverse proxy in front of a node must speak HTTP/2 end-to-end, and it cannot terminate TLS if clients expect the Snowblossom certificate extension described below.
Peer protocol
service PeerService { rpc SubscribePeering(stream PeerMessage) returns (stream PeerMessage); } - after the stream is open both sides are symmetric; nobody cares who dialled. Each direction carries PeerMessages with one of:
| Message | Purpose |
|---|---|
tip (PeerChainTip) | Periodic announcement: network name, the sender's head header for one shard, up to 10 peers from its rumour list (itself first), its software version and, if it has a trust-network key, a signed PeerTipInfo. Sent when registering, whenever a shard head changes, and every 12 s for up to 16 random active shards. |
req_block / block | Fetch a full block by hash. Received blocks are ingested; a block that fails validation closes the link. |
req_header / header | Fetch a header by hash or by (shard, height); reply echoes the shard in req_header_shard_id. |
req_preview_chain / preview_chain | Walk backwards from a hash, returning up to 100 BlockPreview{height, prev_hash, snow_hash, shard_id}. The 2.0 synchronisation primitive. |
tx | Gossip a transaction; the receiver tries to add it to its mempool and relays it if new. |
req_cluster | Ask for a transaction and its unconfirmed ancestors when a gossiped transaction references unknown parents. |
req_import_block / import_block | Fetch an ImportedBlock (header + exported outputs) of a shard the receiver does not fully validate (sharding). |
A tip from a peer whose network_name differs (e.g. a testnet node dialling mainnet) is silently dropped and the link closed. Any other unexpected exception in a handler closes the link (“Some bs from …”); validation errors are only logged.
Synchronisation
Sync is header-first using previews. When a tip or header mentions a block whose parent the node does not have, it asks for the preview chain of the parent (up to 100 entries) and keeps walking back until it reaches a block it knows; then it requests the missing blocks forward, parents before children (requests are de-duplicated for 15 s and tracked in a “desire map” so that when a block arrives its waiting children are fetched next). Blocks of shards outside the interest set are ignored. A node considers itself synced when the best header it has seen is less than 10 blocks above its own head (areWeSynced); until then it refuses to give miners block templates (“We are not yet synced, refusing to send block template”). bypass_sync_check=true overrides this for private networks.
Peer discovery
- The node keeps a rumour list of
PeerInfoentries keyedhost:port, persisted in the database (special/peerlist, saved every 60 s) and pruned of entries older than 3 days. - On start it learns: its own addresses (public IPv4/IPv6 obtained from
ipv4-lookup.snowblossom.org/ipv6-lookup…, one entry per service port), anyseed_urisfrom the config, all A/AAAA records of the network's DNS seeds (marked with versionseedand a short expiry, so real gossip overrides them) and on mainnetsnow-tx1.snowblossom.org:443. Mainnet DNS seeds:seed.snowblossom.org,node.snowblossom.cluelessperson.com,snow-tx1.snowblossom.org,snow-de1.snowblossom.org,snow-a.1209k.com,snow-b.1209k.com. - Every tip carries up to 10 peers; sane ones (≤ 16 KB, valid host/port, timestamps not in the future, shard ids within range, a claimed trust-network address must be backed by a valid signature) are merged, keeping the newest
learnedand the latestlast_checked/last_passed. PeerInfofields:host,port,last_checked,last_passed(last successful contact),learned,version,node_id(8 random bytes, regenerated on every start so cloned databases do not collide; used to estimate the number of distinct nodes),node_snow_address(TLS identity),connection_type(GRPC_TCP / GRPC_TLS),shard_id_set(interest shards),trustnet_addressand a signed copy of the whole record.
Connection management
Every 12 s the maintenance thread prunes dead links (no message for 300 s), and if below the targets picks up to four candidates by a utility score: +1 while under peer_count (default 8), +1 per shard in the node's interest set that has fewer than interest_peer_count (4) links, +1 per other shard where a trusted-signer peer would help (trust_peer_count, 4). Peers attempted in the last 5 minutes are skipped; there is no ban list. Every 2 hours one random link is closed to keep the graph mixing. Light clients can ask a node for a shuffled sample of its rumour list with GetPeerList (≤ 100 entries, optionally filtered by trust network).
TLS and node identities
Rather than certificate authorities, Snowblossom binds a TLS endpoint to a node key - an ordinary wallet key whose address is written with the label node::
- With
tls_service_portset, the node loads (or creates) a one-key wallet attls_key_path; the address is logged as “My TLS address: node:…” and written toaddress.txtin that directory. - At every start it generates a fresh RSA-2048 TLS key and a self-signed X.509 certificate (
CN=node:<address>, O=Snowblossom, 10 years). The certificate carries a critical extension (OID2.5.29.134) containing aSignedMessage: the node key's AddressSpec, a timestamp and the RSA public key, signed by the node key. - A client's trust manager (
SnowTrustManagerFactorySpi) ignores hostnames and CAs. It requires a single-certificate chain with that extension, verifies the signature and the timestamp (≤ 45 s skew), checks that the RSA key in the payload is the certificate's key, and - if the client asked for a specific address - that the claimed address matches (“Server did not claim the expected address”).
URIs: grpc://host[:port] is plaintext; grpc+tls://host[:port] is encrypted and accepts any properly signed Snowblossom certificate; grpc+tls://host?key=node:… additionally pins the server identity. The built-in mainnet seed URIs are pinned, e.g. grpc+tls://snow-a.1209k.com?key=node:eaws55vusncn05dedphrval0apyfw7vg7s22ldlg. Node-to-node connections pin the peer's advertised node_snow_address. TLS needs a 64-bit JVM. MrPlow can offer the same TLS to miners (2.2).
The trust network
A second identity, trustnet_key_path, lets a node vouch for chain data. Three uses:
- Signed tips. Every tip carries
signed_head: aPeerTipInfowith previews of the blocks around the node's head, the blocks the coordinator has imported for that shard, and the coordinator head, signed with the trust-network key. - Signed peer records. The node's own
PeerInfocarries itstrustnet_addressplus a signed copy, so nobody can claim membership of a trust network they do not hold the key for. - Consumers list the addresses they trust:
trustnet_signers=node:a,node:b. For shards outside its interest set a node then accepts block hashes, coordinator heads andImportedBlocks only when announced in a tip signed by one of those keys (recorded in thetrustmap). This is what allows a node that validates only some shards to import outputs from the others. Without signers configured nothing is trusted; signed previews are still used as hints (“take anything here as a pack of usual p2p lies”).
For a node following all shards (the default) the trust network is optional: it simply signs tips for others. SignedMessage supports single-key specs only.
Connecting as a client
Wallets, pools and the explorer use StubUtil: given node_uri with several URIs it fires GetNodeStatus at all of them in parallel and keeps the first to answer (60 s timeout). Given nothing, it uses the network's built-in TLS seed list, falling back to plaintext seeds on 32-bit JVMs. GetUTXONode answers are verified against the UTXO root of a header, so a malicious node cannot invent balances - it can at most withhold data or serve a stale root, which racing several nodes mitigates.
Time
Block timestamps may be at most 45 s ahead of a validator's clock and signed messages use the same bound. Every 5 minutes a node compares its clock with https://timecheck.snowblossom.org/time and warns when off by more than 5 s. Run NTP.