Snowblossom Docs
Docs › Protocol › Snow fields

Snow fields

A snow field is a large file of deterministic pseudo-random data that miners read during the proof of work. Fields are generated from a seed string with the SnowFall program, summarised by a 16-byte merkle root that is fixed in the network parameters, and accompanied by “deck” files that let miners build proofs quickly. Nodes and wallets never need them.

Mainnet fields

Twelve fields are defined for mainnet (NetworkParamsProd). Size doubles and the activation difficulty rises by two bits per field. The “activated” column is the block at which the chain's activated_field first reached that field, taken from the explorer; current field: 9 (hippo), average difficulty -.

#NameSizeWordsActivation diff.Merkle root (Skein-256-128)DecksActivated on mainnet
0cricket1 GiB226252c363d33550f5c4da16279d1fa89f7a9a, bblock 0 · 22 May 2018
1shrew2 GiB227274626ba6c78e0d777d35ae2044f8882faa, bblock 454 · 23 May 2018
2stoat4 GiB228292948b321266dfdec11fbdc45f46cf959a, bblock 1,278 · 27 May 2018
3ocelot8 GiB2293133a2fb16f08347c2dc4cbcecb4f97aeba, bblock 1,872 · 29 May 2018
4pudu16 GiB230330cdcb8629bef77fbe1f9b740ec3897c9a, bblock 3,303 · 6 Jun 2018
5badger32 GiB23135e9abff32aa7f74795be2aa539a079489a, b, cblock 3,774 · 7 Jun 2018
6capybara64 GiB23237e68678fadb1750feedfa11522270497fa, b, cblock 4,224 · 8 Jun 2018
7llama128 GiB23339147d379701f621ebe53f5a511bd6c380a, b, cblock 6,655 · 23 Jun 2018
8bugbear256 GiB23441533ae42a04a609c4b45464b1aa9e6924a, b, cblock 34,630 · 2 Jan 2019
9hippo512 GiB23543ecdb28f1912e2266a71e71de601117d2a, b, cblock 171,069 · 5 Aug 2021 current
10shai-hulud1 TiB23645cc883468a08f48b592a342a2cdf5bcbaa, b, cnot yet (needs avg. difficulty 45)
11avanc2 TiB23747a2a4076f6cde947935db06e5fc5bbd14a, b, cnot yet

Testnet (“teapot”) has eleven MiB-sized fields (1 MiB … 1 GiB) with the same names (field 8 is spelled “bumbear” there), activating one bit apart from difficulty 22 to 32, so testnet exercises every snow storm at toy scale. Regtest and the shard test networks use the same sizes with their own roots; see Networks → snow fields for every root hash.

Activation - the “snow storm”

The chain's activated_field is part of each BlockSummary. When the exponentially weighted average target of the previous block (target_average, weight 1 % per block) is at or below the next field's activation target - in difficulty terms, when the average difficulty reaches the next activation difficulty - the field index increases by one. It is evaluated once per block and never decreases: a field, once activated, stays required even if hash rate and difficulty later fall (which is exactly what happened after bugbear and hippo). Blocks must then use that field or a larger one. Miners see it coming in the explorer's mining page (next field, required difficulty, current hash rate as a fraction of what the next field needs: -) and should have the next field ready in advance - the miner warns “When the next snow storm occurs, we will be unable to mine. No higher fields working.”

Files on disk

Each field lives in its own directory named after its seed, <network>.<n>, under the miner's snow_path:

snow/
└── snowblossom.9/
    ├── snowblossom.9.snow        512 GiB   the field (or snowblossom.9.snow.0000 … .01ff, 1 GiB chunks)
    ├── snowblossom.9.deck.a      512 MiB   hash of every 1024-word (16 KiB) span
    ├── snowblossom.9.deck.b      512 KiB   hash of every 1024²-word (16 MiB) span
    └── snowblossom.9.deck.c      512 B     hash of every 1024³-word (16 GiB) span

The same string snowblossom.9 is the directory name, the file base name and the seed of the generator. All miners read the single blob; the chunked form (tools/split.sh, 4-hex-digit chunk index) only matters for Arktika when a field must be spread across several devices or machines.

Obtaining a field

MethodHowNotes
Torrentsnowblossom.org/snowfields lists a torrent (and a chunked torrent) per field, with the root hash to compare against.Fastest for large fields. Verify afterwards with SnowMerkle or simply let the miner's start-up self-test fail if something is corrupt.
auto_snow=trueIf the miner finds no usable field ≥ the required one, it runs SnowFall then SnowMerkle itself, then rescans.Correct but slow - see the cost below; mining is paused meanwhile.
Scriptstools/snowfall-snowblossom.sh <dir> generates fields 0–9 (1 GiB × 2n) and writes the roots to hashes-snowblossom.txt; snowfall-teapot.sh etc. for other networks.Fields 10 and 11 are not in the script; run SnowFall by hand with seed snowblossom.10, size 1,048,576 MiB.
ManualSnowFall snow/snowblossom.9/snowblossom.9.snow snowblossom.9 524288 then SnowMerkle snow/snowblossom.9 snowblossom.9Arguments: file, seed, size in MiB. SnowMerkle prints the root and writes the decks.

Generation: SnowFall

The goal (from the class comment): “create a deterministic large file… hard to generate without having the entire file… hard to quickly generate a certain page in memory because you need random access to the entire file to build.” lib/src/SnowFall.java does this with a keyed generator whose state is deliberately enormous.

Generator

  • PRNGStream(seed): Skein-1024-1024(seed) → 128 bytes → 32 big-endian ints → a WELL44497b generator (Apache commons-math3; 44,497-bit state, 5,564 bytes when serialised).
  • mixBytes(b) re-seeds the generator from b concatenated with fresh output of the current generator, so injected data completely reshapes the state.
  • The snow monster: a FIFO of 262,144 blocks of 1 KiB (256 MiB) of generator output. At every step the oldest block is mixed back into the generator and a new one appended. The effective generator state is therefore 256 MiB of history: a checkpoint that could regenerate a region of the file must be 256 MiB, not 5.5 KB.

Pass 0 - sequential fill

for each 1 MiB block of the file:
    write 1 MiB of generator output
    mixBytes(oldest snow-monster block); refill the monster

Passes 1…7 - random read-modify-write

page_count = size / 4096
batches    = size × 7 / 4096 / 128              // every page rewritten 7 times on average
for each batch:
    for m in 0..127:  mix oldest monster block; new_page[m] = 4096 random bytes; loc[m] = 8 random bytes
    page[m] = loc[m] mod page_count  (linear probe if already chosen in this batch)
    read the 128 existing pages (in parallel)
    for m in 0..127 in order:
        mixBytes(existing_page[m])
        new_page[m] ^= existing_page[m]
        write new_page[m] back at the same offset
        mixBytes(new_page[m])

Every later page depends on the content of every page touched before it, through the generator state. Thread count and IO batching do not change the output - all generator calls are single-threaded and ordered - so anyone with the seed reproduces the exact same file and root. Constants: PASSES = 7, PAGESIZE = 4096, MULTIPLICITY = 128, monster 256 MiB.

Cost

Sequential write of the whole file, plus 7 × size / 4096 random 4 KiB reads and the same number of random writes. For hippo (512 GiB) that is about 940 million random reads and 940 million random writes; for avanc four times that. RAM needed is modest (≈ 256 MiB monster + buffers); what matters is random-write IOPS. SnowFall logs its write rate and an estimated total runtime every 10 s. On a hard disk this takes weeks; on a good NVMe drive, days. This is why torrents exist.

Merkle tree and decks: SnowMerkle

  • Leaves are the raw 16-byte words (not hashed). Internal nodes are Skein-256-128(left ‖ right). The tree is strictly binary and the word count must be a power of two (all defined fields are).
  • SnowMerkle streams the file once sequentially with O(log N) memory, computing N−1 hashes and printing the 16-byte root, which must match the network table.
  • While streaming it writes the decks: deck k (letter a+k) contains, in order, every internal node whose subtree spans 1024k+1 words. Number of decks = how many times the word count can be divided by 1024 while staying above 1024: two for 1–16 GiB fields, three for 32 GiB – 2 TiB. Loaders check deck.length == snow.length / 1024^(k+1).
  • A deck file is itself a valid snow file with the same root (the unit tests verify this), which is a neat way to sanity-check one without the full field.

Why decks matter: a PoW proof needs the sibling hashes along the path from a word to the root. Without decks the miner would have to hash the entire field for every proof; with decks it reads the 1024 words around the target (16 KiB) and one contiguous run of entries from each deck. Arktika refuses to start without decks (“No sources seem to have the deck files”).

Verifying a field

  • At start every miner runs FieldScan: for each field directory present it opens the field and decks, checks their lengths, and performs 64 random self-tests (pick a word, build a proof, verify it against the published root). Fields that fail are logged and skipped - a corrupt download shows up immediately.
  • For a full check, run SnowMerkle <dir> <base> and compare the printed root with the table above; it rewrites the decks as a side effect.
  • Test vectors for the generator (seed “zing”): 1 MiB → c58564b6208329ae2317ec606bbc7f4a, 8 MiB → 10d0a3be329d4b490d18c6295b08f77e, 64 MiB → f8ca73a8cc7076dc9823caebbcadbf79; testnet field 3 (teapot.3, 8 MiB) → 97f8303394d267dfe4bf65243bd3740e.

Memory and IO strategies

How a miner reads the field decides its hash rate:

ModeOptionBehaviour
Direct file reads(default)Each thread does a positioned 16-byte read per pass. Rate = device random-read IOPS ÷ 6. The OS page cache helps only if RAM exceeds the field.
Field in heapmemfield=trueThe field is loaded into the JVM heap in 1 MiB blocks on first touch. Needs -Xmx larger than the field. Mainly for Windows; Linux's page cache usually achieves the same with enough RAM.
Partial cachememfield_precache_gb=N + min_depth_to_disk=dThe first N GiB are read into RAM; an attempt whose first d words are not all in the cached part is abandoned. Trades attempts for IO so a small RAM can still serve a large field well.
DistributedArktika layers1 GiB chunks served from files, RAM or remote machines; partial work is queued to whichever layer holds the next chunk.
Sequential wavesSurfMinerStreams the field chunk by chunk and advances all queued attempts that need that chunk.