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 -.
| # | Name | Size | Words | Activation diff. | Merkle root (Skein-256-128) | Decks | Activated on mainnet |
|---|---|---|---|---|---|---|---|
| 0 | cricket | 1 GiB | 226 | 25 | 2c363d33550f5c4da16279d1fa89f7a9 | a, b | block 0 · 22 May 2018 |
| 1 | shrew | 2 GiB | 227 | 27 | 4626ba6c78e0d777d35ae2044f8882fa | a, b | block 454 · 23 May 2018 |
| 2 | stoat | 4 GiB | 228 | 29 | 2948b321266dfdec11fbdc45f46cf959 | a, b | block 1,278 · 27 May 2018 |
| 3 | ocelot | 8 GiB | 229 | 31 | 33a2fb16f08347c2dc4cbcecb4f97aeb | a, b | block 1,872 · 29 May 2018 |
| 4 | pudu | 16 GiB | 230 | 33 | 0cdcb8629bef77fbe1f9b740ec3897c9 | a, b | block 3,303 · 6 Jun 2018 |
| 5 | badger | 32 GiB | 231 | 35 | e9abff32aa7f74795be2aa539a079489 | a, b, c | block 3,774 · 7 Jun 2018 |
| 6 | capybara | 64 GiB | 232 | 37 | e68678fadb1750feedfa11522270497f | a, b, c | block 4,224 · 8 Jun 2018 |
| 7 | llama | 128 GiB | 233 | 39 | 147d379701f621ebe53f5a511bd6c380 | a, b, c | block 6,655 · 23 Jun 2018 |
| 8 | bugbear | 256 GiB | 234 | 41 | 533ae42a04a609c4b45464b1aa9e6924 | a, b, c | block 34,630 · 2 Jan 2019 |
| 9 | hippo | 512 GiB | 235 | 43 | ecdb28f1912e2266a71e71de601117d2 | a, b, c | block 171,069 · 5 Aug 2021 current |
| 10 | shai-hulud | 1 TiB | 236 | 45 | cc883468a08f48b592a342a2cdf5bcba | a, b, c | not yet (needs avg. difficulty 45) |
| 11 | avanc | 2 TiB | 237 | 47 | a2a4076f6cde947935db06e5fc5bbd14 | a, b, c | not 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
| Method | How | Notes |
|---|---|---|
| Torrent | snowblossom.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=true | If 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. |
| Scripts | tools/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. |
| Manual | SnowFall snow/snowblossom.9/snowblossom.9.snow snowblossom.9 524288 then SnowMerkle snow/snowblossom.9 snowblossom.9 | Arguments: 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 frombconcatenated 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). SnowMerklestreams 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 checkdeck.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:
| Mode | Option | Behaviour |
|---|---|---|
| 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 heap | memfield=true | The 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 cache | memfield_precache_gb=N + min_depth_to_disk=d | The 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. |
| Distributed | Arktika layers | 1 GiB chunks served from files, RAM or remote machines; partial work is queued to whichever layer holds the next chunk. |
| Sequential waves | SurfMiner | Streams the field chunk by chunk and advances all queued attempts that need that chunk. |