Node
SnowBlossomNode is the full node: it maintains peer links, downloads and validates every block of the shards it follows, keeps the UTXO trie and the optional indexes, and answers wallets, miners, pools and explorers over gRPC. It holds no funds - only identity keys for TLS and the trust network - so it can safely live on a different machine than the wallet.
Configuration reference
Keys read by the node (all overridable with snowblossom_<key> environment variables, see Configuration):
| Key | Default | Meaning |
|---|---|---|
network | snowblossom | Network to join (list). |
db_type | required | rocksdb (recommended, 64-bit only), lobstack (pure Java, for 32-bit JVMs; no mutation sets) or atomic_file (one file per key; fine for MrPlow, unsuitable for a node). |
db_path | required | Database directory, created if missing. |
db_separate | false | RocksDB: one instance per map instead of one shared DB. Loses cross-map write ordering on crash; not recommended. |
service_port | none | Plaintext gRPC port(s), comma-separated. Convention 2338 / 2339 / 2340. If unset no plaintext port is opened. |
tls_service_port | none | TLS gRPC port(s); requires tls_key_path. Convention 2348 / 2349 / 2350. |
tls_key_path | Directory holding the node's one-key identity wallet; created with a fresh key if missing, address written to address.txt (node:…). | |
trustnet_key_path | Directory for the trust-network signing key (signs tips and the node's own peer record). Optional; auto-created. | |
trustnet_signers | Comma list of node:… addresses whose signed tips this node trusts for shards it does not validate itself (trust network). | |
shards | 0 and all below | Shard ids to follow; the interest set is their cover sets plus all ancestors. Default follows the whole network. |
addr_index | false | Index address → transactions (needed for GetAddressHistory, wallets' history, seed recovery, explorers). Not retroactive: enable before the first sync or resync. |
tx_index | false | Store every transaction and a tx → block index (needed for GetTransaction / GetTransactionStatus). Same retroactivity caveat. |
peer_count | 8 | Target number of open peer links. |
interest_peer_count | 4 | Target links per shard in the interest set. |
trust_peer_count | 4 | Target links to trusted-signer peers per other shard. |
seed_uris | Extra peers to try at start (grpc://host:port, grpc+tls://host:port), merged with the built-in DNS seeds. | |
peer_log | Path; on start the persisted peer list is dumped there, one PeerInfo per line. | |
bypass_sync_check | false | Serve block templates even when not synced (private networks). |
mempool_reject_p2p_tx | false | Accept transactions only from local SubmitTransaction, not from peers. |
log_config_file | java.util.logging properties (logging). | |
metric_log | Path for structured per-operation metrics (duckutil MetricLogger). | |
profiler_log, profiler_period | - / 20000 | Append timing reports every period ms. |
Example: public mainnet node with indexes
network=snowblossom
log_config_file=configs/logging.properties
db_type=rocksdb
db_path=node_db/mainnet
addr_index=true
tx_index=true
service_port=2338
tls_service_port=2348
tls_key_path=node_db/tls_mainnet
trustnet_key_path=node_db/trustnet
Requirements
- 64-bit JVM, Java 17+ recommended. The Bazel target pins
-Xms4g -Xmx4g; when running the jar yourself use at least-Xmx4g. RocksDB uses native memory on top, so plan 6–8 GB for the node. - Disk: an indexed mainnet database is ~100 GB (2026) and grows with the chain; nothing is pruned. SSD strongly recommended - validation, the UTXO trie and address-history lookups are random-access heavy. The one-time summary rebuild of the 2.0 upgrade needed extra space until compaction ran.
- Network: inbound on the service ports for other peers (optional but good for the network), outbound to peers, DNS seeds,
ipv4-lookup.snowblossom.org/ipv6-lookup.snowblossom.org(own IP discovery) andtimecheck.snowblossom.org(clock check). - Clock: NTP. Blocks more than 45 s in the future are rejected; a warning is logged when the clock is off by more than 5 s.
Start-up and what happens inside
- Registers BouncyCastle, loads the config, requires
db_type, logs “Starting SnowBlossomNode version …”. - Loads network parameters and the shard interest set (“Shard interest set: […]”).
- Opens the database - a shutdown hook flushes RocksDB cleanly (“RocksDB flush started/completed”), so stop the node with SIGTERM, not SIGKILL, and give it time.
- Loads the peerage (persisted peer list + seeds), transaction broadcaster, forge, UTXO importer, mempools and maintenance thread.
- Opens a
BlockIngestorper shard with a stored head (“Loaded chain tip: shard 0 height …”), recomputing summaries if their version is old (“Reindexing …”). - Binds the ports (“Ports: [2338] [2348]”), tries UPnP, starts the gRPC services.
- Starts peering, the clock watcher and the broadcaster; then “SnowBlossomNode started”.
Threads
| Thread | Period | Job |
|---|---|---|
| PeerageMaintThread | 12 s | Prune dead links, connect to new peers, send tips for up to 16 shards, re-learn seeds hourly, save peer list every 60 s. |
| TxBroadcaster | 2 tx/s, burst 5 s | Relays accepted transactions to all links (queue 2,500). |
| MemPool Tickler / TicklerBroadcast | 300 s / 5 s | Rebuild priorities after a new head; re-gossip one random pool transaction. |
| SnowUserService Tickler | on new block, ≤ 30 s | Push block templates to subscribed miners; send address updates to subscribers. |
| ShardBlockForge ConceptUpdateThread | 15 s | Recompute candidate next blocks; idles after 5 min without template requests. |
| DBMaintThread | check every 60 s | Runs a full RocksDB compaction every 2,160–4,320 blocks (15–30 days, randomised); logs “Running db maint” / “Compaction run in N seconds”. |
| TimeWatcher | 300 s | Clock check against timecheck.snowblossom.org. |
Database layout
All maps share one RocksDB with prefixed keys:
| Map | Content |
|---|---|
block | full blocks by hash |
blocksummary | BlockSummary per block; keys head / head-<shard> hold the current tips |
tx | transactions by id (with tx_index) |
u | UTXO hashed-trie nodes (all historical states) |
cit | chain-index trie: a2tx address history, tx2b tx → block, fbo2/id2o indexes |
height | (shard, height) → block hash |
bh, ib, xshm, trust | headers learned from peers, cached imported blocks, external shard heads, trusted block hashes (sharding) |
bbm, cbms | best work at (shard, height); parent → children set (forge) |
special | peerlist, node_id, db_maint_height |
Because the summary is written last, after the block, its transactions and the UTXO nodes, a crash can at worst leave orphaned nodes, never a half-applied block.
Monitoring sync and health
- Log lines: “Got first tip from a remote peer”, “Requesting block: …”, “New block: Shard 0 Height H … (tx:N sz:B)”, “New chain tip: Shard 0 Height H hash” followed by “The activated field is 9 (hippo). This block was age ago” - the age tells you how far behind you are.
SnowBlossomClient client.conf nodestatusprints theNodeStatusJSON: head height per shard,connected_peers(should approachpeer_count),estimated_nodes,mem_pool_size, peer version histogram. Compare the height with the explorer.- Miners and pools get “We are not yet synced, refusing to send block template” until the node is within 10 blocks of the best header it has seen.
- Peers on the wrong network are dropped silently (visible at FINE level: “Peer has wrong name”).
Deployment
systemd
[Unit]
Description=Snowblossom node
After=network.target
[Service]
User=snowblossom
WorkingDirectory=/var/snowblossom
ExecStart=/usr/bin/java -Xmx4g -jar /var/snowblossom/SnowBlossomNode_deploy.jar configs/node.conf
Restart=on-failure
TimeoutStopSec=120
[Install]
WantedBy=multi-user.target
(example/systemd/ in the repository has the original units; they run a node.sh wrapper and have no Restart=.) Logs: journalctl -u snowblossom-node -f plus the rotating files in logs/.
Docker
docker run -d --restart always --name snowblossom.node --network host \
-v snownode:/data \
-e snowblossom_addr_index=true -e snowblossom_tx_index=true \
snowblossom/node:latest
docker logs -f snowblossom.node
docker container stop -t 90 snowblossom.node # let RocksDB flush
Testnet: add -e snowblossom_network=testnet -e snowblossom_service_port=2339 -e snowblossom_tls_service_port=2349. The images keep everything under /data; SNOWBLOSSOM_JAVA_OPTIONS passes JVM flags.
Reverse proxies and firewalls
Expose 2338/2348 directly. The seed operators also listen on 80/443 for users behind restrictive firewalls; that works because gRPC over HTTP/2 is just TCP - but a proxy that terminates TLS with its own certificate breaks the identity check for grpc+tls:// clients. Web explorers run on their own HTTP port and are the thing to put behind Apache or nginx.
Upgrading
- Stop the node cleanly, replace the jar, start. The database format has been stable since 1.6; summaries with an old
summary_versionare recomputed at start automatically. - 2.0.0 rebuilt the summary table on first start (up to an hour, heavy IO, temporary extra disk until compaction). Nodes had to be upgraded by block 211,600 (May 2022).
- 2.2.0 added the post-quantum signatures (mandatory by block 358,700, March 2025), trust-network signed peer info,
GetPeerList, TLS for pool miners. - Turning on
addr_index/tx_indexlater means deletingdb_pathand resyncing. - Older milestones: 1.6.0 moved the indexes into the hashed trie (minutes of reindexing); 1.0.8 changed the default DB location; 1.7.0 changed the template protocol so MrPlow needs a node ≥ 1.7.
Troubleshooting
| Symptom | Cause / fix |
|---|---|
| “rocksdb does not work with 32-bit jvm” | Install a 64-bit Java, or use db_type=lobstack (slower, no TLS). |
| “Missing required key: …” | Add db_type, db_path or tls_key_path (when a TLS port is set). |
| “FAILED TO INITIALIZE LOGGING” | Create the logs/ directory named in the logging properties. |
| “Block too far into future” | Your clock is wrong; fix NTP. |
| “Local clock seems to be off by N ms” | Same - more than 5 s drift. |
| Port mapped to another host (UPnP warning) | Another machine on the LAN already mapped the port; configure the router manually or change service_port. |
| No peers / stuck sync | Check outbound connectivity and DNS; add seed_uris=grpc+tls://snow-a.1209k.com; make sure network= matches. |
| Miner says “not yet synced” | Wait for the head to come within 10 blocks of the network; on a private network set bypass_sync_check=true. |
| Wallet history empty / “node does not support address history” | The node lacks addr_index; enable it and resync, or use a public node that has it. |
GetPeerList hangs (blocking clients) | Released node versions up to 2.2.0 never complete that RPC (SnowUserService.getPeerList lacks ob.onCompleted()); call it with a deadline or an async stub, or apply the one-line fix to your node. |
JVM crash in librocksdbjni | Seen with old JDK 11 builds; use Java 17+ and a restart loop / Restart=on-failure. |