Satoshi Hike Construction

Responsibility. Defines the object, the terrain, the leg, the stage and waypoint binding, the encodings, and the two-way Bitcoin commitment bind. Used with Conformance as the definitional pair. Producer and Measurement are process documents constrained by this definition.

Source. Satoshi Hike 2.3 (tag spec-2.3.0). Section numbers are the specification section numbers:

FRAMING (2026-10-24 relocation). §14 Verify (measurement procedure and verify-record shape) moved to 06-Measurement_Record_Process.md. Rule-index IDs A4/A5 still name those rules; their bodies live in Measurement. Part B / admission lever chatter that formerly sat in Status is not owned here — see Admission Process and policy instances outside this pack. Construction owns the Bitcoin bind (headers into the hike; anchors out). Waypoint Publication is the scoped batching channel for the anchor side.

1. Status

The status prose in this section is informative. The rule index is the ID-to-heading map. The subsection “Part A rule classes” is the normative classification.

This document is Satoshi Hike 2.3 Construction (Part A object definition). Part A is the construction: SAMPLE_BYTES 1024, STEPS_PER_LEG 2000000, KIND v2.2-pcoc-object, anchor-window offsets 2, 3, and 4, the NL4 terrain word, the §11.1 fold, and the asynchronous producer event model (Rule A6 lives in Producer Process). FRAMING. Unknown-kind measurement (Rule A5) and the full §14 measure procedure live in Measurement Record. Section 13.6 Bari waypoint service lives in Waypoint Publication. Part B admission levers, judgment, Rule B7/B9, and section 15.5 floor measuring method live in Admission Process (value-free process) and/or outside policy instances (gl-admit-*); this Construction file does not set lever values. Section 16.2 implementation requirements live in Conformance.

Rule index

Normative for the ID-to-heading map. The heading text in the section named is the rule. A test named for an ID tests that heading. This table does not add a second rule. Classes for these headings, and for unnumbered Part A sentences, are in “Part A rule classes” below.

ID Heading Section
A1 Rule A1. Event order at every stage boundary 13
A1.1 Rule A1.1 Legs, then the tip 13
A1.2 Rule A1.2 The interrupt ends the stage 13
A1.3 Rule A1.3 Waypoint byte order 13
A1.4 Rule A1.4 Post (asynchronous) 13
A1.5 Rule A1.5 Pivot 13
A6 Rule A6. Nothing pauses a leg 13
A2 Rule A2. Stages and where leg 0 starts 12.1
A2.1 Rule A2.1 Eighteen stages 12.1
A2.2 Rule A2.2 Leg 0 12.1
A2.3 Rule A2.3 Checkpoint 12.1
A2.4 Rule A2.4 First post 12.1
A3 Rule A3. What the object stores 8
A3.1 Rule A3.1 Headers 8
A3.2 Rule A3.2 Per stage 8
A3.3 Rule A3.3 Paths 8
A3.4 Rule A3.4 Produce stderr 13.3
A4 Rule A4. What verify measures against the portable index 14
A4.1 Rule A4.1 Shape 14.1
A4.2 Rule A4.2 Headers 14.2
A4.3 Rule A4.3 Chain 14.3
A4.4 Rule A4.4 Six-leg check 14.6
A4.5 Rule A4.5 Paths 14.3
A4.6 Rule A4.6 Bookends 14.4
A4.7 Rule A4.7 Bookend legs 14.4
A4.8 Rule A4.8 Bookend span 14.4
A4.9 Rule A4.9 Bookend leg rate 14.4
A4.10 Rule A4.10 Analysis 14.4
A4.11 Rule A4.11 Fewer than two anchored and fetched 14.4
A5 Rule A5. Unknown kind 14.7
B0 Rule B0. Which rate the admission service reads 15
B2 Rule B2. Leg-rate floor 15
B3 Rule B3. Span sanity 15
B4 Rule B4. Paths minimum 15
B6 Rule B6. Fixed start 15
B7 Rule B7. Resubmits 15
B8 Rule B8. Leg count 15
B9 Rule B9. Invariant refuses 15.2
BR1 Rule BR1. Paid waypoint 13.6
BR2 Rule BR2. Dedupe 13.6
BR3 Rule BR3. Pool and tree 13.6
BR4 Rule BR4. Path serve 13.6
I1 Rule I1. SDKs are the reference implementations 16.2
I2 Rule I2. Interfaces 16.2
I3 Rule I3. Producer model 16.2
I4 Rule I4. Tests 16.2
I5 Rule I5. Versioning and pin 16.2
I6 Rule I6. Cost 16.2

Part A rule classes

Normative.

This subsection is the normative classification of Part A. It does not add a second construction. Each row is one rule and one class. Informative prose, section 5, section 13.6 (Bari rules BR1–BR4, classed there), section 16.2 (Rules I1–I6), section 7.4, section 13.4, the expected-case list in section 13.1, and sections 17, 18, and 19 are not rows. A glossary sentence that only renames a row is not a second row. The Normative and Informative marks on the sections a row cites stay where those sections put them.

Rule ID scheme.

  • Heading IDs in the rule index stay A1–A5 and their dotted children. A heading that states one rule of one class is that row.
  • When one heading states more than one class, the rows are ID.n in SPEC order (n starts at 1). Those rows are the classification. The parent ID is the group and is not given a second class.
  • A2 is a group heading. A2.1–A2.4 are the rows. A2 is not a separate class.
  • A normative Part A sentence that has no heading ID gets P<section>.<n>, in reading order. The digits before the first dot are the SPEC section number. P marks a Part A construction paragraph. It does not renumber A1–A5 and it is not Part B. An ID that is not in this table is unassigned and is not used.
  • Classes are INVARIANT, CONSTANT, RANGE, and POLICY THRESHOLD. An INVARIANT holds byte for byte or as an exact measurement. A CONSTANT is a fixed parameter of this version. A RANGE is an allowed value or format. A POLICY THRESHOLD is admission only.
  • Part A has no POLICY THRESHOLD row. Sentences that can be misread as thresholds are listed after the table and stay in the class written here. Admission thresholds are named parameters in section 15. Rule B9 is the one section 15 row in this table. It classes those refuses. It is not a Part A measurement.
Rule ID Class One-line rule SPEC locus
P2.2 INVARIANT Produce and verify perform no signature check. The admission service checks the signature (section 15). §2, §15
P3.1 INVARIANT Ranges are half-open; 0^n is n zero bytes; ‖ is raw concatenation; H is FIPS 180-4 SHA-256. §3
P3.2 RANGE le64 and u64_le are 8 little-endian bytes of an unsigned integer below 2^64. §3
P3.3 INVARIANT mod is the non-negative remainder; mod 2^64 keeps the low 64 bits. §3
P4.1 RANGE Outcome tokens are the closed lowercase set. There is no token not obtained. §4
P4.2 INVARIANT Part A, the construction named in section 1, does not pass, fail, reject, deny, or admit. §4
P6.1 CONSTANT SLOT_BYTES is 16384. §6
P6.2 CONSTANT WORDS_PER_SLOT is 2048. §6
P6.3 CONSTANT TERRAIN_BYTES is 268435456. §6
P6.4 CONSTANT N_SLOTS is 16384. §6
P6.5 CONSTANT SAMPLE_BYTES is 1024. §6
P6.6 CONSTANT OFFSET_MODULUS is 268434433. §6
P6.7 CONSTANT STEPS_PER_LEG is 2000000. §6
P6.8 CONSTANT STAGES is 18. §6
P6.9 CONSTANT HASH_BYTES is 32. §6
P6.10 CONSTANT LEG_RESULT_BYTES is 8. §6
P6.11 CONSTANT PUBLIC_KEY_BYTES is 32. §6
P6.12 CONSTANT BEACON_BYTES is 32. §6
P6.13 CONSTANT SEAL_DOMAIN is the twelve bytes pcoc-walk-v1. §6
P6.15 CONSTANT KIND is v2.2-pcoc-object. §6
P6.16 CONSTANT FILL is dag2. §6
P6.17 CONSTANT MIX_ADD is 0x9E3779B97F4A7C15. §6
P6.18 CONSTANT MIX_MUL1 is 0xBF58476D1CE4E5B9. §6
P6.19 CONSTANT MIX_MUL2 is 0x94D049BB133111EB. §6
P6.20 CONSTANT WORD_XOR is 0xC0FFEE. §6
P6.21 CONSTANT PARENT_MUL is 0xD1B54A32D192ED03. §6
P6.22 CONSTANT SLOT_MUL is 0xA24BAED4963EE407. §6
P6.23 INVARIANT WORDS_PER_SLOT = SLOT_BYTES / 8, and the division is exact. §6
P6.24 INVARIANT N_SLOTS = TERRAIN_BYTES / SLOT_BYTES, and the division is exact. §6
P6.25 INVARIANT OFFSET_MODULUS = TERRAIN_BYTES − SAMPLE_BYTES + 1. §6
P6.26 INVARIANT This version has no AUDIT_K. The six-leg draw count is not a section 6 constant. §6
P7.1 INVARIANT The first eight bytes of a digest are b[0:8]. u64_le does not reverse a digest a second time. §7.1
P7.2 RANGE Hex uses 0-9, a-f, and A-F with no spaces. A 64-digit spelling is 32 bytes, byte 0 first. §7.2
P7.3 INVARIANT Produce emits one CBOR map in core deterministic encoding. §7.3
P7.4 INVARIANT Top-level and per-stage key orders are the orders section 7.3 lists. Stage 0 has no path key. §7.3
P7.5 RANGE Text is definite-length UTF-8. Byte fields, unsigned integers, and definite arrays are as section 7.3 states. §7.3
P7.6 RANGE Verify decodes any definite-length encoding of the same values, including a non-shortest unsigned integer. Key order is not a shape mismatch. §7.3
P7.7 INVARIANT Malformed CBOR is shape mismatch, and shape_item includes malformed cbor. Later measurements are still recorded. §7.3
A3 INVARIANT The hike object has exactly the top-level fields, types, and lengths in the section 8 table. §8 Rule A3
P8.1 INVARIANT Evidence is the hash-bound set in section 8. Convenience signposts are checked against the evidence or the index and never relied on. §8, §14
P8.2 INVARIANT Stored heights are convenience. d_g and delta_h subtract index-confirmed heights only. §8, §14.3, §14.4
A3.1 INVARIANT The object stores seed_beacon and all 18 interrupts, with strictly later heights and differing hashes, and stores no block time. §8 Rule A3.1
A3.2 INVARIANT Each stage map has exactly the listed fields. Array lengths equal leg_count. leg_count is at least 1. Stage 0’s waypoint is the seed. §8 Rule A3.2
A3.3.1 INVARIANT Stage 0 omits path. An empty byte string is not a stage-0 path. “not planted by design” is not a token. §8 Rule A3.3
A3.3.2 INVARIANT stages[17].path is always empty. W_17 is not asked, including on a jump. The record is not fetched by design. §8 Rule A3.3
A3.3.3 CONSTANT Sixteen waypoints, W_1 through W_16, are fetchable. W_17 is not. §8 Rule A3.3
A3.3.4 INVARIANT The object has no terrain field. A stored leg result is le64 of the section 11 u64. §8
A3.4 INVARIANT path_skip <g> and not fetched by design 17 are the exact stderr lines, in that order, at the close. That step does not delay the object. Empty bytes are not a silent skip. §13.3 Rule A3.4
P9.1 RANGE {tip-url}, {index-url}, and {bari-url} are absolute http or https URLs with no trailing slash. §9, §13
P9.2 INVARIANT A clock read is one GET, does not follow a redirect, and is successful only on status 200. §9
P9.3 RANGE A successful body is one UTF-8 JSON object, with no BOM, no second value, and no duplicate member name. §9
P9.4 RANGE header.hash is 64 hex digits in Bitcoin display order. The natural beacon reverses those 32 bytes. §9
P9.5 RANGE header.height is a JSON number in 0 … 2^53 − 1, not a string and not a non-integer JSON number. An index height above that bound is an unsuccessful read: not read. An integer above that bound in beacon_height or last_interrupt_height is section 14 shape_item height_range. §9, §14
P9.6 RANGE header.time is a JSON integer in 0 … 2^32 − 1 Unix seconds, not a string and not a non-integer JSON number. §9
P9.7 INVARIANT On mainnet, header.time equals the nTime of the block whose hash is header.hash. Section 14 measures the portable index. A direct Bitcoin read is outside that procedure. §9, §14
P9.8 CONSTANT With no named gossip peer, the starting list is the copied Bitcoin Core mainnet DNS seeds, in the listed order, port 8333. §9
P9.10 INVARIANT Produce Observe reads the plane tip sequence and does not GET an HTTP tip or the portable index. §9
P9.11 INVARIANT The interrupt is the first post-checkpoint tip read whose hash differs and whose height is strictly greater. The tip read returns at once the latest header the header source holds and never waits. There is no rebinding. §9, Rule A1
P9.12 INVARIANT No clock-read failure or other transitory network condition stops a run or withholds the object. Produce logs it (section 13.5) and the walk continues. An unsuccessful Hashcash, POST, or path-ask read is a miss, and produce continues. A run ends only at I_17, on a producer failure, or on explicit cancellation. §9, Rule A6
P9.13 INVARIANT Verify does not Observe, does not GET an HTTP tip, and does not GET a Bari path. It reads the portable index at each stored height, and it reads one path inclusion height only when that height is in the anchor window and that header is a stored interrupt. §9, §14
P9.14 INVARIANT The seed beacon is the natural beacon from the first successful Observe, before any leg. That Observe is the latest header already on the tip sequence. It does not wait for a header mined after produce starts. Until a seed read succeeds the run has not started, so no leg exists to pause. §9
P9.15 INVARIANT A height is not an input to SHA-256 in this construction. §9
P9.16 CONSTANT On the live plane the header source keeps at least HEADER_PEERS_MIN 2 gossip peers. A connected peer that has not announced a header within HEADER_PEER_SILENCE_SECONDS 120 of another connected peer announcing it is replaced. Below 2 peers the source reconnects asynchronously with backoff and writes an alert header_peers_low; the walk and the object are unaffected. §9
P9.17 INVARIANT Every Rule BR1 block-hash comparison (current, before, after) is natural order on both sides: SHA-256(SHA-256(header)) and the header’s previous-block field in wire order, the same bytes as header_hash. A display-hex tip hash is reversed once before comparing. A match only under reversal is a 402. §9, §13.6
P10.1 INVARIANT The seed preimage is public_key ‖ seed_beacon, 64 bytes. The seed is SHA-256 of that preimage, 32 bytes, no label, read as four little-endian lanes of 8 bytes each, P[0] … P[3]. §10
P10.2 INVARIANT mix64, the NL4 base word, the parent indices, and the dag2 fill are the section 10 construction. Every terrain word depends on all 256 bits of the seed. Terrain is filled once per terrain. The step does not write it. §10
P10.3 INVARIANT The seed equals stages[0].waypoint by design. §10, §12.1
P11.1 INVARIANT A step folds every word of the selected slot by §11.1 (acc starts at 0 and grows by wrapping addition; mix64 includes MIX_ADD) and sets the next state from SHA-256(SEAL_DOMAIN ‖ le64(S_folded)). §11.1
P11.2 INVARIANT The leg result is STEPS_PER_LEG steps from the first eight bytes of the previous checkpoint, stored as le64. §11.2
P11.3 INVARIANT The terrain sample is SAMPLE_BYTES at the offset taken from the previous checkpoint. It is not the waypoint slot sample. §11.3
A2.1 CONSTANT seed_beacon plus the 18 interrupts gives stages numbered 0–17. §12.1 Rule A2.1
A2.2 INVARIANT The seed is SHA-256(public_key ‖ seed_beacon), stored at stages[0].waypoint, never posted, and not one of W_1 … W_17. The seed equals that field by design. Leg 0 starts from it or from W_g. §12.1 Rule A2.2
A2.3 INVARIANT The checkpoint preimage is the previous checkpoint, the stage limb, the terrain sample, and le64(leg_result), in that order. §11.4, §12.1 Rule A2.3
A2.4 INVARIANT The first Bari post is W_1, started after I_0, alongside stage 1’s legs. Stage 0 has no plant and no path. §12.1 Rule A2.4
A1.3 INVARIANT W_{g+1} is SHA-256 of I_g, its slot sample, stages[g].waypoint, and the fold of that stage’s checkpoints. slot_index is u64_le of the first eight bytes of I_g, modulo N_SLOTS. No waypoint is built from I_17. §12.2, §13 Rule A1.3
A1 INVARIANT At each stage boundary the order is legs, then the tip read, then the interrupt, then the waypoint bytes, then the pivot. Stage g+1’s leg 0 starts at once from the pivot. The post is an asynchronous event started at the pivot and is not ordered before any leg. There is no rebinding. §13 Rule A1
A1.1 INVARIANT Legs in a stage run back to back. The tip is read after each checkpoint; that read returns at once and never waits. A leg already in progress finishes. §13 Rule A1.1
A1.2 INVARIANT When the tip read shows interrupt I_g, leg production in stage g stops. §13 Rule A1.2
A1.4 INVARIANT When W_{g+1} is known the producer starts its post to Bari with the Hashcash fee as an asynchronous event. The post does not delay, order, or pause any leg, and its outcome is not an input to any leg, checkpoint, leg result, or waypoint. §13 Rule A1.4
A1.5 INVARIANT W_{g+1} is the pivot. Stage g+1 leg 0 starts from it at once. §13 Rule A1.5
A6 INVARIANT No post, Hashcash solve, ask, peer or header-source read, Bari exchange, or log write pauses, orders, delays, or interrupts a leg. Each is an asynchronous event. The post-checkpoint tip read stays on the walk path and never waits. §13 Rule A6
P13.1 INVARIANT Produce sets no wall-clock bound, no steps-per-second quota, and no leg-count quota. leg_count ≥ 1 is the drive loop. §13, §13.3
P13.2 CONSTANT ANCHOR_WINDOW_OFFSETS is 2, 3, 4. That list is written once in §13.1. A plant posted at height H is asked only at H plus each offset. The path is gone one header after the last offset. §13.1
P13.3 INVARIANT Produce never pauses a leg: not before the first leg of a stage and not between legs (Rule A6). A stage that ends at one leg, when the next header is the interrupt, is a world event the producer recorded. §13.1
P13.4 INVARIANT The Hashcash challenge is SHA-256("bari-hc-v2" ‖ bari_origin ‖ header_hash) and the digest is SHA-256(challenge ‖ waypoint ‖ le64(nonce)), as section 13.1 defines. Leading zero bits are counted from the first digest byte. §13.1
P13.5 CONSTANT The Hashcash difficulty is 21 leading zero bits, the one value for produce and for Bari. §13.1
P13.6 INVARIANT The nonce search starts at 0. Produce sends one waypoint POST as an asynchronous event. Status 204 means Bari holds the waypoint. The only retry is one background resend 30 seconds after a transitory send failure (P13.13). Any other outcome is logged and does not withhold the object. H is the stage beacon height in §13.1. There is no fee bump. §13.1
P13.7 INVARIANT The leaf is 64 lowercase hex digits, byte 0 first. A due ask is sent, as an asynchronous event, when the producer’s header source learns the due header. There is no wait before an ask. §13.1
P13.8 INVARIANT A checked path is a status 200 canonical path body that validates for that waypoint and whose block is one height in the anchor window. Every other ask result is a miss. §13.1
P13.9 INVARIANT The cut-off is last_interrupt_height. Production ends when the tip read shows I_17, and the object closes at once with the checked paths that have arrived by then. There is no collection window. An ask in flight at the close is abandoned, and no ask is made after it. W_17 is not asked. §13.1
P13.10 INVARIANT A stage’s leg_count is a convenience copy of the length of that stage’s checkpoints. Produce does not invent path bytes. No metric reads leg_count. §13.3
P13.11 INVARIANT The live event log is not the hike object and is not an input to a leg, a checkpoint, a leg result, or a waypoint. §13.5
P13.12 RANGE A public-key input is 32 bytes, or 64 hex digits with an optional leading 0x. The object stores the 32 bytes. §13
P13.13 INVARIANT After a transitory send failure (a transport failure, section 13.1) the producer resends the identical waypoint POST exactly once, in the background, 30 seconds later, then drops it if the resend fails. Both attempts are logged. This is the only retry of a waypoint POST. §13.1
P13.14 INVARIANT Plant height H is stages[g].beacon_height for W_g, written once in §13.1. The fee header on the POST is not H. §13.1
P13.15 INVARIANT The live event log uses the six record types in section 13.5, in the key order that section lists. §13.5
P13.17 INVARIANT Event-log records are appended asynchronously. Produce does not wait for a record to be written, flushed, or synced. §13.5
P13.16 INVARIANT The waypoint POST is POST {bari-url}/waypoint. The body is the 32 waypoint bytes. Content-Type is application/octet-stream. The headers are X-Bari-Hashcash-Nonce and X-Bari-Hashcash-Header (the 80-byte fee header). §13.1
A4 INVARIANT Verify records measurements against the object and the portable index and does not judge. Section 15 is not applied in section 14. §14 Rule A4
P14.1 INVARIANT Anyone may read Bitcoin directly. That read is outside the section 14 procedure. Section 14 measures the portable index (P9.7). P9.7 is not a conformance-vector check. A LAN match is not a mainnet fact. §14
P14.2 RANGE The verify record is the JSON object section 14 defines: required keys, no JSON null, decimal integers, and the stated array lengths. §14 verify record
P14.3 RANGE shape_item uses only the 14 strings in that table, in that order. Malformed CBOR is exactly malformed cbor. height_range is an object-byte height above 2^53 − 1. §14 verify record
A4.1.1 INVARIANT shape is match only when every shape condition holds. Otherwise it is mismatch, and later measurements are still recorded. §14.1 Rule A4.1
A4.1.2 INVARIANT fill and the size fields equal the profile in force (a kind other than KIND is Rule A5), or shape_item includes constant. The profile in force is section 6, unless the verifier runs conformance-small. §14.1, §16.1
A4.1.3 INVARIANT Shape records the field set, widths, stage-0 path key, empty W_17 path, stages length, leg_count, seed_beacon, and header order. §14.1
A4.2.1 INVARIANT Verify reads every stored header from the portable index, in order, including last_interrupt. One header does not skip a later header. §14.2 Rule A4.2
A4.2.2 INVARIANT index_comparison is match, mismatch, or not read, from hash and height together. Time is not part of that token. §14.2
A4.2.3 INVARIANT T is header.time only when index_comparison is match. On mismatch, T is not read. §9, §14.2
A4.3.1 INVARIANT Verify recomputes each checkpoint digest and the seed and records match or mismatch. A missing leg result is mismatch. This check does not run a leg. §14.3 Rule A4.3
A4.3.2 INVARIANT Verify recomputes W_1 … W_17 from stored bytes under section 12.2. No waypoint is recomputed from last_interrupt. §14.3
A4.5.1 INVARIANT Empty path bytes on W_1 … W_16 are skip and plant n/a. Any other bytes are present. Empty W_17 is not fetched by design. §14.3 Rule A4.5
A4.5.2 INVARIANT Anchored and fetched is the anchors token anchored: in the section 13.1 window, an interrupt header stored on the object, and plant match. anchored_total is that count. This row records it and does not judge it. Rule B4, settled and normative, compares it with 6. §14.3, §14.5
A4.5.3 INVARIANT d_g subtracts index-confirmed heights only, and only when plant is match and the index comparison of I_{g−1} is match. Otherwise d_g is not defined. §4, §14.3
P14.4 INVARIANT A present path’s plant is compared with the portable index. A read that does not return a document is not read. A document that was read with no plant, or with no matching plant, is mismatch. Verify does not GET Bari. §14.3
P14.5 INVARIANT A returned plant document’s network is the string main or the string synthetic. Any other string, or a missing network, is plant mismatch. There is no verifier input that selects one of those two strings. Accepting synthetic does not make the measurement a mainnet fact. Two plants may share a block. tree_size is not compared. §14.3
P14.6 INVARIANT A present path that does not decode, or whose validate_for does not succeed, has plant mismatch and claimed_block not defined. block_height in a body that decodes is in 0 … 2^32 − 1, which is inside 0 … 2^53 − 1. A hash-verified canonical path body has no height above 2^53 − 1. That is not shape_item height_range. The hike object still decodes. §14.3
P14.7 INVARIANT An index GET spells the height in the shortest base-10 digits. §14.2
P14.8 INVARIANT A path on W_1 … W_16 is anchored only when its inclusion height is in the section 13.1 anchor window, that header is an interrupt header stored on the object, and the plant comparison against that one header is match. Otherwise the window record is not anchored. Verify does not scan other index heights for that path. §13.1, §14.3
A4.6 INVARIANT Bookends are the lowest and highest anchored-and-fetched indices among W_1 … W_16. §14.4 Rule A4.6
A4.7 INVARIANT N is the checkpoint count over stages a … b−1. It does not read leg_count. §14.4 Rule A4.7
A4.8.1 INVARIANT S is T(P_b) − T(I_{a−1}) only when both T values were taken on match. Otherwise S is not read. §14.4 Rule A4.8
A4.8.2 INVARIANT delta_h subtracts index-confirmed heights only. Otherwise delta_h is not defined. §14.4 Rule A4.8
A4.9 INVARIANT The verify record holds integers N and S and does not hold a floating-point rate. The bookend display is not a judgment and is not the admission rate. §14.4 Rule A4.9
A4.10 INVARIANT Analysis records the whole-hike count and span, each per-stage span, checkpoints outside the bookends, hike_legs, and hike_span. hike_legs counts stages 1–17. hike_span is nTime(I_17) − nTime(I_0), the raw header.time of those two blocks, the same integer as whole_hike_span. The seed beacon is not an endpoint. The bookend pair is not hike_legs or hike_span. §14.4 Rule A4.10
A4.11 INVARIANT Fewer than two valid paths (Rule A4.11 step 1) among W_1 … W_16 records the bookend fields not defined. An issued plant GET that returns no document records them not read, before the anchored count. A plant not read whose GET was not issued does not. Fewer than two anchored then records not defined. With two anchored, an unread I_{a−1} header or an unread P_b header records every bookend field not read. A returned mismatch does not. fixed_start stays an object. §14.4 Rule A4.11
A4.4.1 CONSTANT The six-leg draw count is 6. It is not STAGES and it is not AUDIT_K. §14.6 Rule A4.4
A4.4.2 INVARIANT The draw is six distinct global checkpoint indices by the unbiased draw in section 14.6. Live verify reads the operating-system CSPRNG. A conformance-small vector may supply draw bytes (section 16.1). Fewer than six legs is not defined. A short byte source is not read. When that draw is not read, six_leg_runs is not read. A short-byte six-leg draw stays re-readable. It is not not defined. An 8-byte draw at or above the limit is consumed. §14.6, §16.1
A4.4.3 INVARIANT Each chosen index is recorded match or mismatch in draw order. §14.6
A4.4.4 INVARIANT The index draw finishes before any leg result is recomputed. A verifier may recompute the six leg results concurrently, and may run Rule A4.3 checkpoint comparisons concurrently. How many Rule A4.3 comparisons run at once is a verifier setting. The verify record does not depend on that number. One at a time is conforming. Elements of six_leg_runs may execute in parallel. This file does not require a thread pool and does not set a pool size. One at a time is for this measurement record. It does not apply to thorough_rounds. §14.6
A5 INVARIANT An object whose kind text is not exactly KIND is recorded kind unknown kind. No other label is recognised. That record is not a decode failure and is not a shape mismatch. Sections 14.1–14.6 are not applied. A malformed CBOR value is section 7.3 malformed cbor, not this record. §14.7 Rule A5
B9 INVARIANT Rule B9 names the section 15.2 refuses that do not wait on a configured threshold: decode, waypoint rehash, shape mismatch including height_range, seed-value mismatch, a confirmed plant or header mismatch, a checkpoint-digest mismatch, a six-leg mismatch, and reason unknown_kind when section 14 records kind unknown kind. A cap hit is Rule B7. This row is a section 15 judgment. It is not a Part A measurement. §15.2 Rule B9

Sentences reviewed for POLICY THRESHOLD. None of the rows above is a POLICY THRESHOLD. The sentences a reader might take for one are:

  1. leg_count ≥ 1 (A3.2, A4.1.3, P13.1). It is the section 13.2 loop, recorded under shape. It is not an admission floor. Rule B8 sets no leg-count threshold.
  2. Hashcash difficulty 21 (P13.5). It is a produce and Bari constant. It is not an admission threshold.
  3. Section 9, “mean about 60 seconds.” That phrase describes the synth sequencer. It is not a section 6 constant, not a conformance-small parameter, and not an admission threshold.
  4. Section 13.1, a stage that ends at leg_count = 1 “is conforming.” That sentence means the producer followed section 13. Judgment of the record is section 14.
  5. Rule A4.9, the bookend pair (N, S). Section 14 records the integers. It does not apply leg_rate_floor. The admission rate is Rule B2 on (hike_legs, hike_span), not on (N, S).
  6. The informative note under Rule A4.11 about the two, three, or four blocks from I_{b−1} to P_b. Those sentences are not a Part A number. Rule B3 is not an admission input and does not adjust hike_span.

No Part A sentence is moved into section 15 by this classification. Where section 14 says an admission service may judge a recorded token, the judgment stays in section 15.

2. Scope

Normative.

This document specifies Satoshi Hike 2.3.

A hike has exactly eighteen stages (Rule A2.1).

Section 13 produces the object. Section 14 measures it. Section 15 is the admission service. Produce and verify perform no signature check. The admission service checks the signature (section 15.1).

3. Interpretation

Normative.

“Must” and “must not” apply to a conforming producer, a conforming verifier, and a conforming admission service.

In this file, “must”, “must not”, and “may”, in lowercase or in capitals, carry the meanings that BCP 14 (RFC 2119, RFC 8174) gives to MUST, MUST NOT, and MAY. They state requirements only in text marked Normative. In normative text, a plain statement (“is”, “does”, “uses”, “is not”) is a requirement as well. Text marked Informative states no requirement. Where informative text uses one of these words, it restates a rule of the normative section it names, and that section governs.

Byte ranges are half-open. b[i:j] is bytes i through j-1 of b, starting at index 0.

0^n is the byte string of n zero bytes.

‖ is raw concatenation of byte strings, in the written order. A concatenation has no length prefix, no tag, and no separator.

le64(x) is the 8-byte little-endian encoding of the unsigned integer x with 0 ≤ x < 2^64.

u64_le(b) is the unsigned integer represented by the 8-byte string b in little-endian order.

mod is the non-negative remainder. For a ≥ 0 and m > 0, a mod m is the integer r with 0 ≤ r < m and a = qm + r for some integer q.

mod 2^64 on a u64 quantity is ordinary wrapping arithmetic on a 64-bit unsigned integer. The result is the low 64 bits, and overflow is discarded. That is this mod with modulus 2^64. It is the rule for the NL4 word, the parent indices, the slot mix, and the step’s u64 state. A mod with any other modulus stays the remainder above.

SHA-256 is the function specified by FIPS 180-4. Its output is 32 bytes. In this document, H means SHA-256.

4. Glossary

Normative.

Term Meaning
Satoshi Hike 2.3 This construction. KIND is v2.2-pcoc-object. Satoshi Hike 2.2 is the same construction.
Individual The hiker. The individual is identified by a 32-byte BIP-340 x-only public key on secp256k1.
Seed D = SHA-256(public_key ‖ seed_beacon). It is 32 bytes. It has no domain label. Section 10 reads it as four little-endian lanes. Stage 0’s leg 0 starts from it. The object stores it at the wire key stages[0].waypoint. The seed equals stages[0].waypoint by design. That field is not a Rule A1 waypoint. It is never built from an interrupt and never posted. Stage 0 has no waypoint of its own.
Seed preimage public_key ‖ seed_beacon. It is 64 bytes: the 32-byte public key, then the 32-byte seed_beacon in natural byte order. It is the SHA-256 input that produces the seed. It is not stored as its own field.
Seed beacon The tip read at the start of produce. The object stores it as seed_beacon, and as stages[0].beacon. It can sit before the first interrupt. It is not the start of hike_span.
Interrupt I_g, g = 0 … 17. The tip as read at the first post-checkpoint tip check in stage g whose hash differs from I_{g−1} (from seed_beacon, on stage 0) and whose height is strictly greater. If the tip moved by more than one block before that check, I_g is the tip as read. There are 18 interrupts.
Waypoint W_{g+1}, g = 0 … 16: the 32-byte value Rule A1.3 builds after I_g and produce posts to Bari. The waypoints are W_1 … W_17. No waypoint is built after I_17. Stage 0 has no waypoint of its own.
Pivot What a waypoint does. W_{g+1} ends stage g’s record and starts stage g+1.
Path The canonical path body of a waypoint, showing that waypoint’s plant in a block (section 13.1). Only W_1 … W_17 have paths. Stage 0 has no path.
Anchored and fetched The anchors token anchored: the inclusion height is in the section 13.1 window, that header is an interrupt header stored on the object, and plant is match. Anything else among W_1 … W_16 is not anchored and fetched.
Skip Empty path bytes on W_1 … W_16. A skip is a normal produce outcome and is recorded. How skips count is admission-service policy (Rule B4).
P_g The block named in W_g’s path, where its plant was mined. Verify records P_g as authenticated only when that path is present and the plant comparison is match.
d_g A signed integer only when plant is match and the index comparison of I_{g−1} is match: index-confirmed height(P_g) minus index-confirmed height(I_{g−1}). Otherwise not defined. Not taken from a producer-stored height alone. Computed by verify; a verify-record field, not object content.
Bookends W_a and W_b: the anchored-and-fetched waypoints with the lowest index and the highest index.
Bookend legs N: the number of checkpoints over stages a … b−1. An integer when bookends exist. Computed by verify; a verify-record field, not object content.
Bookend span S = T(P_b) − T(I_{a−1}) when both T values are integers under Rule A4.2, so each index_comparison is match. Otherwise S is not read. Seconds, a signed integer when recorded. Computed by verify; a verify-record field, not object content.
Bookend leg rate Display only. N÷S when S > 0. Otherwise the display is not defined. The verify record holds the integers N and S. It does not hold a floating-point rate. Section 15 does not read (N, S) as the admission rate.
Δh Record key delta_h: index-confirmed height(P_b) minus index-confirmed height(I_{a−1}), only when both header comparisons are match. Otherwise not defined. Not taken from a producer-stored height alone. A signed integer in the bookend block when it is defined. delta_h stays in the bookend block. Rule B3 does not read it. Rule B3 is not an admission input. It is not an analysis value and it is not policy. Computed by verify; a verify-record field, not object content.
Analysis The whole-hike checkpoint count and span, each per-stage span and its rate display, checkpoints outside the bookends, hike_legs, and hike_span. Rule B0 reads hike_legs and hike_span for the floor. It does not read the bookend pair. Computed by verify; a verify-record field, not object content.
First beacon The first interrupt I_0, stored as stages[1].beacon. Verify record headers[1]. Not seed_beacon. Not headers[0].
Last beacon last_interrupt (I_17). Verify record headers[18].
Hike legs hike_legs: the number of checkpoints over stages 1 through 17. Stage 0 is not included. The same checkpoints as whole_hike_legs. Not bookend N.
Hike span hike_span: nTime(I_17) − nTime(I_0), the raw header.time of last_interrupt minus the raw header.time of the first interrupt I_0 (stages[1].beacon). The same integer as whole_hike_span. Not nTime(last_interrupt) − nTime(seed_beacon). Not bookend S. Median-time-past is not this span.
Median-time-past Bitcoin’s miner coordination rule. It is not hike_span, not bookend S, and not an admission input. The hike clock is header nTime at the two beacons Rule A4.10 names.
Portable index What verify measures against. At each height it holds the header hash and the height, and optionally waypoint anchoring. A row that carries Bitcoin nTime stores it as header.time. A row with no header.time is measured as T not read. This file does not name a cut height and does not backfill a row. The index is faithful to Bitcoin. Anyone may bypass the index and read Bitcoin directly (see §14). That direct read is not the section 14 procedure.
T(x) The Bitcoin nTime of block x, from the portable index member header.time, only when the index comparison for that header is match. Otherwise the measurement is not read. Unix seconds when it is an integer.
Block time That nTime: an unsigned 32-bit count of Unix seconds, read as it is.
Clock The plane's tip sequence. Produce Observe reads that sequence and returns the natural beacon and its height. The tip sets the order of events. It is not money, not a confirmation count, and not an archive of the chain. Block time is a measured input to section 14. It does not replace tip advances as that order. Wall-clock time is not an input. The verifier's wall clock is not an input.
Terrain The deterministic room derived from the individual’s public key and the seed beacon.
Hike The whole proof and produce object: one individual’s recorded journey over terrain under the clock.
Stage Legs from the seed (stage 0) or from waypoint W_g (stage g ≥ 1) until interrupt I_g. Stage 17 ends at I_17. No waypoint is built from I_17.
Leg One leg of exactly STEPS_PER_LEG steps, from a previous checkpoint to a leg result and a checkpoint.
Step One fold+seal over one slot of SLOT_BYTES bytes.
Leg result The u64 end state of a leg.
Checkpoint The 32-byte SHA-256 digest defined in section 11.
not fetched by design The path record for W_17 only, lowercase, with spaces. stages[17].path is the empty byte string. Verify identifies it by position.
Leg-rate floor Rule B2 policy parameter, not a measurement. A policy sets its value, in checkpoints per second (hike_legs over hike_span, with hike_span in Unix seconds). Admission-gate policy setting, configurable. This file gives it no default. One checkpoint is one leg.
Index-confirmed height header.height from an index document whose comparison is match (stored hash agrees and stored height agrees). For P_b that comparison is index_comparison_P_b: the header hash agrees and header.height equals P_b.block_height. A stored height alone is not this height.
Policy profile See section 15. Not a hike-object field.
Re-verify See section 15.
Marks as fast See section 15.
Friendly list See section 15.
Paths minimum Rule B4. anchored_total ≥ min_anchored_paths among W_1 … W_16, wherever those waypoints sit. Not a measurement.
Outcome token Lowercase, with spaces: match, mismatch, present, skip, not defined, not read, not fetched by design, n/a, anchored, not anchored, and unknown kind. Not not_defined. Not title case. There is no measurement token not obtained; that wording is not used (see below). unknown_kind is not in this set. It is a section 15 reason. n/a means a plant comparison does not apply because path_state is skip. n/a is not not defined. not read is an index document that is missing or unreadable, a header whose comparison is not match so T is not taken, a T that is missing or unusable on a match, an unreadable header, or anything short or missing from a network read. Short path bytes for a six-leg run, and a short-byte six-leg draw, are not read. A document that was read and has no plant for that block is plant mismatch. A path body that does not yield claimed_block is not defined. Fewer than six legs is not defined. Those not defined values are settled by the object's own bytes.
Comparison result The result of a Part B integer comparison (Rules B2 and B4). It is meets when the comparison holds. When it does not hold, the rule states the outcome: refuse, or not read and held where that rule says so. meets is not an outcome token of this table, not an admission outcome, and not a section 15.2 state. It means only that this comparison does not refuse; every other section 15 check still runs. Part A does not use these words.
Fee header The 80-byte Bitcoin block header the producer’s header source holds as its best tip when a Hashcash solve starts. header_hash is SHA-256(SHA-256(header)) in natural byte order. It is not a stored header and it is not H (section 13.1).
bari_origin The ASCII origin of {bari-url} as section 13.1 serializes it. Bari uses its own public origin, serialized the same way (section 13.6).
Asynchronous event Work the producer starts and does not wait for on the walk path: a post, a solve, an ask, a header-source or peer read, a Bari exchange, or a log write (Rule A6).
Close The moment the tip read shows I_17. Production ends and the object is encoded at once with the checked paths that have arrived (section 13.1).
Transitory send failure A waypoint POST that received no HTTP status line, or received status 502, 503, or 504 (section 13.1). A transport or proxy condition, not a Bari answer.

The public key is the 32-byte string used below. Produce and verify do not test a signature on it.

Wording not used. Use the term in the table.

Not used Use instead
opening header; the header that opened stage g; open header; close header seed_beacon on stage 0; I_{g−1} on stage g ≥ 1
a header arrives during the waypoint post there is no rebinding; the next interrupt is the later tip check (Rule A1)
deciding legs; deciding span; deciding leg rate hike_legs; hike_span; the Rule B2 rate. Bookend N and S are not that rate
opening value seed, or W_g
seed value seed
production_seed; the 64-byte “seed” seed preimage
a squeeze of the seed to one 64-bit fill pin; a domain label on the seed not this construction. Every terrain word depends on all 256 bits of the seed (section 10)
stage span bookend span S; a per-stage span is analysis (Rule A4.10) and is not an admission input
leg floor leg-rate floor
seed block seed_beacon
anchored and included anchored and fetched
planted anchored and fetched
not planted by design; any path field on stage 0 stage 0 has no path key (Rule A3.3)
walk or re-walk, for a leg leg, or recomputed leg result
not obtained, for an unsuccessful index read or a missing T not read
roll-forward cut A row with no header.time records T as not read. This file does not name a cut height.

SEAL_DOMAIN pcoc-walk-v1 is an exempt wire constant. It is not wording from that table. The verify-record key seed_value and the token seed-value mismatch are exempt wire names. Do not rename them.

Part A (sections 8–14) does not say that verify or the object passes, fails, rejects, denies, or admits, and it does not say accept, refuse, revoke, or held. Those outcomes are section 15. Do not say “minimum legs per stage.” A per-stage rate is analysis. It is not an admission test.

6. Constants

Normative.

Name Value
SLOT_BYTES 16384 (16 KiB)
WORDS_PER_SLOT 2048
TERRAIN_BYTES 268435456 (256 MiB)
N_SLOTS 16384
SAMPLE_BYTES 1024 (1 KiB)
OFFSET_MODULUS 268434433
STEPS_PER_LEG 2000000 (2_000_000)
STAGES 18
HASH_BYTES 32
LEG_RESULT_BYTES 8
PUBLIC_KEY_BYTES 32
BEACON_BYTES 32
SEAL_DOMAIN the 12 bytes pcoc-walk-v1
KIND v2.2-pcoc-object
FILL dag2
MIX_ADD 0x9E3779B97F4A7C15
MIX_MUL1 0xBF58476D1CE4E5B9
MIX_MUL2 0x94D049BB133111EB
WORD_XOR 0xC0FFEE
PARENT_MUL 0xD1B54A32D192ED03
SLOT_MUL 0xA24BAED4963EE407

The six-leg check draws 6 legs. That count is not a constant in this table.

WORDS_PER_SLOT = SLOT_BYTES / 8. The divisor is the width of one little-endian u64.

N_SLOTS = TERRAIN_BYTES / SLOT_BYTES, and the division is exact: TERRAIN_BYTES = N_SLOTS × SLOT_BYTES.

OFFSET_MODULUS = TERRAIN_BYTES − SAMPLE_BYTES + 1.

The section 6 numbers for WORDS_PER_SLOT, N_SLOTS, and OFFSET_MODULUS are that production evaluation. They are not a second free constant. Under conformance-small the same formulas apply to that profile’s SLOT_BYTES, TERRAIN_BYTES, and SAMPLE_BYTES.

SEAL_DOMAIN is the twelve ASCII bytes 70 63 6f 63 2d 77 61 6c 6b 2d 76 31. It is exactly those twelve bytes.

Normative. Domain identifiers. The byte strings pcoc-walk-v1 (SEAL_DOMAIN), bari-hc-v2 (the Hashcash challenge prefix), pcoc-gate-signin-v1 (the sign-in challenge domain), and the /v1 HTTP route prefix are wire-format domain identifiers. Their digits are not specification versions and do not change with the version of this file. The only specification-version marker in an object is the kind label v2.2-pcoc-object (KIND).

There is no seed domain label. A domain label on the seed, or a squeeze of the seed to fewer than 256 bits, is not this construction.

STAGES is 18. That count, and the beacon count that goes with it, is a profile constant. It is not a security number. The description of this arbitrary profile size is 18 stages and 19 beacons (seed_beacon plus I_0 … I_17). The object stores 18 stage maps, numbered 0–17. Admission does not treat 18, 17, or 19 as a threshold.

7. Encodings

Informative. Summary. How integers, hexadecimal strings, and the hike object are written as bytes. Section 7.1 covers little-endian integers, 7.2 hexadecimal, and 7.3 the deterministic CBOR of the object and the order of the decode checks (malformed, then decode failure, then Rule A5). Section 7.4 gives worked checks.

Normative.

7.1 Little-endian integers

le64 and u64_le are defined in section 3.

The first eight bytes of a 32-byte string b are b[0:8]. Those bytes are SHA-256 output order when b is a digest: byte 0 is the first byte of the digest. u64_le does not reverse a digest a second time.

7.2 Hexadecimal

A hexadecimal spelling uses the digits 0-9, a-f, and A-F. Letter case does not change the value. The spelling contains no spaces.

A 64-digit spelling denotes 32 bytes. The first two digits are byte 0. Natural order is that spelling: byte 0 first. Bitcoin display order is the one reversal in section 9. A digest is not reversed a second time.

7.3 CBOR

The hike object is one CBOR data item, as defined by RFC 8949. The item is a map.

Produce emits the core deterministic encoding:

  • definite lengths only
  • unsigned integers in the shortest encoding
  • no floating-point values, no tags, and no big-integer tags
  • map keys sorted by the bytewise lexicographic order of their definite-length encodings

Every field name in this object is at most 23 bytes, so the first byte of an encoded key is 0x60 plus the name length. For the fields in section 8, that order is:

Top-level map. Keys sort by encoded length, then bytewise. steps_per_leg is 13 bytes. last_interrupt is 14 bytes. last_interrupt_height is 21 bytes.

  1. fill
  2. kind
  3. stages
  4. n_slots
  5. public_key
  6. slot_bytes
  7. seed_beacon
  8. sample_bytes
  9. steps_per_leg
  10. last_interrupt
  11. last_interrupt_height

Stage 0 has no path key (Rule A3.3). Its map:

  1. beacon
  2. waypoint
  3. leg_count
  4. checkpoints
  5. leg_results
  6. beacon_height

Each stage map for stages 1–17:

  1. path
  2. beacon
  3. waypoint
  4. leg_count
  5. checkpoints
  6. leg_results
  7. beacon_height

Text values and map keys are definite-length UTF-8 text strings (CBOR major type 3). A length from 0 through 23 is the additional information in the first byte. A longer string uses additional information 24, 25, 26, or 27 and the length bytes that follow, then the UTF-8 bytes. Indefinite-length text is not a text string in this object. KIND is 16 bytes, so the kind value is the head byte 0x70 and those 16 bytes. Byte fields are byte strings (major type 2). Counts and heights are unsigned integers (major type 0). Arrays are definite arrays in the order defined in section 8.

Verification decodes one definite-length CBOR data item and records section 14. These are decoding rules. A definite-length encoding of the section 8 values decodes. A non-shortest definite-length unsigned integer decodes when the numeric value is the same. Map key order is not a shape mismatch. Indefinite lengths, tags, floating-point values, simple values, negative integers, a map key that is not a text string, a duplicate key, and any byte after that one data item are a malformed CBOR value. A text string whose bytes are not valid UTF-8 (RFC 3629) is also a malformed CBOR value, including a map key and a kind value. It is not Rule A5 and it is not a decode failure. A malformed CBOR value is shape mismatch, and shape_item includes malformed cbor. Later measurements are still recorded. Produce is what fixes the canonical bytes. Verification sorts the input bytes in this order. First, input that is not one definite-length CBOR data item under these rules is a malformed CBOR value, whatever its first data item holds. That includes an empty input, bytes that are not CBOR, bytes that end inside an item, and a malformed item that is not a map. Rule A5 is not applied to a malformed CBOR value, even when a kind text can be read from it. Second, one well-formed data item that is not a map, or a map with no kind key whose value is a text string, is a decode failure: there is no section 14 record, and the verify CLI exits with code 1 (section 18). Third, a map whose kind text is not exactly KIND is Rule A5. Every other input is measured by section 14.

7.4 Checks

Informative.

le64(1) = 01 00 00 00 00 00 00 00.

le64(0x0102030405060708) = 08 07 06 05 04 03 02 01.

FIPS 180-4’s SHA-256 example for the three bytes abc is ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad.

SHA-256(SEAL_DOMAIN ‖ le64(0)) is the 32-byte digest whose hexadecimal spelling, byte 0 first, is 4816bdaf23240476ca57e168301eceee8eb8170ec5446863855e4ceebb55b533. The first eight digest bytes, in that index order, are 48 16 bd af 23 24 04 76. Their spelling in index order is 4816bdaf23240476. When S_folded = 0, section 11 sets S' = u64_le(E[0:8]), the little-endian integer of those eight bytes. That integer is 0x76042423afbd1648. E is that digest. D is the seed, not this digest. The spelling 4816bdaf23240476 is the digest bytes in index order. It is not the hexadecimal spelling of the integer S'.

A seed preimage of 32 bytes of 0x11 followed by 32 bytes of 0x22 has seed 5189c77d29fe5d546a045ec46986852785fea5c13ac7da9c115ff5fb6edf817c (byte 0 first). The four lanes are P[0] = 0x545dfe297dc78951, P[1] = 0x27858669c45e046a, P[2] = 0x9cdac73ac1a5fe85, P[3] = 0x7c81df6efbf55f11. The NL4 base word at i = 0, j = 0 is 0xc7582082a632c2c8. There is no fill pin.

8. Hike object

Informative. Summary. The hike object is one CBOR map. It holds the top-level fields, 18 stage maps numbered 0–17 with their beacons, heights, checkpoints, leg count, leg results, and waypoint (and, on stages 1–17, path), and last_interrupt (Rule A3). Verify (section 14) measures it, and section 15 judges it.

Normative.

Rule A3. What the object stores

The object has exactly these top-level fields:

Field CBOR type Value
kind text KIND
fill text FILL
slot_bytes unsigned SLOT_BYTES
n_slots unsigned N_SLOTS
sample_bytes unsigned SAMPLE_BYTES
steps_per_leg unsigned STEPS_PER_LEG
public_key byte string, length 32 the individual’s public key
seed_beacon byte string, length 32 the seed beacon in natural order
stages array, length STAGES stage maps, stage 0 first
last_interrupt byte string, length 32 I_17, the interrupt that ended stage 17, in natural order
last_interrupt_height unsigned the height of I_17

Evidence and convenience. Evidence, the hash-bound values, is the seed preimage (public_key, seed_beacon), the checkpoints, the leg results, the waypoints W_1 … W_17, the Bitcoin headers (stages[g].beacon for g ≥ 1, and last_interrupt), the path’s leaf, Merkle path and root, which bind the path to the waypoint, stages[0].waypoint (the seed, SHA-256(public_key ‖ seed_beacon)), and any calculated field whose hash dependencies enforce order and cost. Convenience signposts are the path locators (txid, vout, block_hash, block_height), checked against the index, leg_count, beacon_height, last_interrupt_height, the section 6 constants, and stages[0].beacon (= seed_beacon). Verify records leg_count under shape: shape_item includes leg_count when it differs from len(checkpoints) (Rule A4.1). Section 6 constants are recorded as constant. Stored heights are recorded as index_comparison (Rule A4.2). They are convenience. d_g and delta_h do not subtract a stored height unless the index comparison that confirms it is match (Rules A4.5 and A4.8). Verify checks a convenience signpost against the evidence or the index and never relies on it. A broken signpost is a recorded mismatch. Section 15 is where an admission service may judge that recorded mismatch.

Rule A3.1 Headers

The field name beacon is kept.

  • stages[0].beacon = seed_beacon, and stages[0].beacon_height is that header’s height.
  • For g = 0 … 16, I_g = stages[g+1].beacon, and stages[g+1].beacon_height is the height of I_g.
  • I_17 = last_interrupt, and last_interrupt_height is its height.

stages[g].beacon for g ≥ 1 is I_{g−1}, the interrupt stage g started from. It is not a second header read after the waypoint post.

For 0 ≤ g < 17, stages[g+1].beacon_height > stages[g].beacon_height, and stages[g+1].beacon differs from stages[g].beacon.

last_interrupt_height > stages[17].beacon_height, and last_interrupt differs from stages[17].beacon.

The object therefore carries seed_beacon and all 18 interrupts. The object carries no block time. Section 14 reads each block time from the portable index (sections 9 and 14.2).

Rule A3.2 Per stage

Stage 0’s map has exactly these fields. It has no path key.

Field CBOR type Value
beacon byte string, length 32 seed_beacon in natural order
beacon_height unsigned the height of seed_beacon
leg_count unsigned, at least 1 number of legs in the stage
waypoint byte string, length 32 the seed, SHA-256(public_key ‖ seed_beacon). Wire key waypoint. Prose name: seed. Not a Rule A1 waypoint. Never posted.
checkpoints array of byte strings one 32-byte checkpoint per leg, leg 0 first
leg_results array of byte strings one 8-byte little-endian leg result per leg, leg 0 first

Stages 1–17 each have exactly these fields:

Field CBOR type Value
beacon byte string, length 32 I_{g−1} in natural order
beacon_height unsigned the height of I_{g−1}
leg_count unsigned, at least 1 number of legs in the stage
waypoint byte string, length 32 W_g, the waypoint stage g started from
path byte string the canonical path body for W_g, or the empty byte string section 13.3 stores for a skip or for W_17
checkpoints array of byte strings one 32-byte checkpoint per leg, leg 0 first
leg_results array of byte strings one 8-byte little-endian leg result per leg, leg 0 first

len(checkpoints) = len(leg_results) = leg_count.

An object-byte height above 2^53 − 1, including beacon_height and last_interrupt_height, is not a usable unsigned value on the verify record. Section 14 records shape mismatch, shape_item includes height_range, and the value is not defined. It is not not read. Do not clamp the value and do not record it.

checkpoints[j] and leg_results[j] belong to the same leg.

stages[0].beacon = seed_beacon.

stages[0].waypoint = SHA-256(public_key ‖ seed_beacon).

Rule A3.3 Paths

Stage 0 has no path. The stage-0 map omits the path key. Omission is the encoding. An empty byte string is not a stage-0 path. The words “not planted by design” are not a token in this file.

Stages 1–17 each carry the path of their waypoint W_g, or empty bytes for a skip.

stages[17].path (W_17) is always the empty byte string. W_17 is not asked, including when a jump would put an offset at or below the cut-off (section 13.1). The record is not fetched by design.

Sixteen waypoints, W_1 … W_16, are fetchable. W_17 is not one of them.

A path key on stage 0 is a shape mismatch (Rule A4.1). Those bytes are not a path. Verify does not give them path semantics.

The object has no terrain field. A verifier recomputes terrain from public_key and seed_beacon.

A leg result on the object is le64 of the u64 defined in section 11, stored as an 8-byte byte string.

9. Clock

Informative. Summary. The clock is Bitcoin headers. Produce reads its plane’s tip sequence (Observe) for the seed beacon and each stage’s interrupt. Verify reads the stored headers back from the portable index {index-url}. This section states the read and body rules, the byte order of the header hashes, and the live-plane header source.

Normative.

{tip-url} and {index-url} are absolute URLs with scheme http or https (ASCII case-insensitive) and no trailing slash.

A clock read is one GET of a URL below. The reader does not follow a redirect. A status other than 200 is not a successful read. The reader does not require a particular Content-Type.

A successful body is UTF-8 JSON: one JSON object and no second value. A byte order mark (U+FEFF, the bytes EF BB BF) is not a successful read and is not stripped. Bytes that are not UTF-8 are not a successful read.

A body is one JSON object and no second value. If the same member name occurs more than once in any JSON object in the document, including a nested object, the read is not successful. The reader does not keep one of the repeated values.

Header measurement and plant measurement are separate reads of GET {index-url}/index/{height}. Each read parses the body on its own. The header measurement uses only header.hash, header.height, and header.time. It is a successful header read only when header is a JSON object and header.hash and header.height follow the rules below. A body with no header member, or a header that is not that object, is an unsuccessful header read: section 14.2 records index_comparison not read and T not read. network, plant, plants, and every member other than header.hash, header.height, and header.time do not change index_comparison or T.

The plant measurement does not require header. A plant read that returns a JSON object is a returned document. Section 14.3 then records plant match or mismatch from network and the plant records in that object. A plant read that does not return a document is plant not read. One returned document can be a header not read and a plant match: the document has network and a matching plant and has no usable header. The anchor-window vector’s height 4 is that document. The header reads for that vector return no document. The one issued plant read returns a document with no header member. A returned document with a usable header and no matching plant is a successful header read and plant mismatch.

header.hash is a JSON string of exactly 64 hexadecimal digits under section 7.2, so the digits are 0-9, a-f, and A-F, and letter case does not change the value. The string has no 0x prefix. The digits are Bitcoin display order. Let display be the 32 bytes of that spelling under section 7.2. The natural beacon is the 32-byte string natural with

natural[i] = display[31 − i]    for i = 0 … 31

That formula is the single reversal rule. Bitcoin display order is the natural 32 bytes reversed: display[i] = natural[31 − i]. Apply it only when reading a Bitcoin display hex string into natural bytes, or when writing natural bytes as a Bitcoin display hex string. Do not reverse between two values that are already natural order. Do not reverse a digest a second time.

Where each hash is written, and the order it uses:

  • A SHA-256 digest in this file (the seed, a checkpoint, a waypoint, a fold, a Merkle leaf node, sibling, or root) is natural order. Byte 0 is the first byte of the digest. The object stores those 32 raw bytes. A hex spelling of them is byte 0 first.
  • seed_beacon, stages[g].beacon, and last_interrupt are natural-order raw bytes.
  • An index document’s header.hash is Bitcoin display hex. Reverse it once to natural order before comparing it with a stored beacon.
  • A canonical path body stores block_hash and txid as 32 raw bytes in natural order, the same order as the object beacon. The interrupt-header check is the same height and those same 32 bytes, compared with the stored beacon, with no reversal.
  • A path body’s root, leaf, and Merkle siblings are natural-order digest bytes. No reversal.
  • An index plant’s root is hex in natural order. Compare it with the path root with no reversal.
  • An index plant’s txid and block_hash are Bitcoin display hex. Reverse each once to natural order before comparing it with the path body.
  • The verify record’s stored_hash is natural-order hex, byte 0 first.
  • The verify record’s block_hash, on P_g, P_b, and claimed_block, is Bitcoin display hex: the reversal of the path body’s natural block_hash bytes. It is not a byte-0-first spelling of those bytes.
  • A produce event-log tip or waypoint is natural-order hex, byte 0 first. An event-log block is the path body’s block_hash, byte 0 first. It is not display order.
  • The Hashcash header_hash is natural order: SHA-256(SHA-256(header)) of the 80-byte fee header, not reversed. The challenge does not use display bytes. X-Bari-Hashcash-Header spells the 80 header bytes in wire order, byte 0 first.
  • Every block-hash comparison in Rule BR1 (section 13.6) is natural order on both sides, with no reversal. For current, the hash of Bari’s tip is SHA-256(SHA-256(header)) of Bari’s tip header, natural order, the same bytes as header_hash. For before, the previous-block hash of Bari’s tip is the 32 bytes at offset 4 of Bari’s tip header, in wire order, which is natural order: SHA-256(SHA-256(header)) of the block before the tip, the same bytes as that block’s header_hash. For after, the fee header’s previous-block field is the 32 bytes at offset 4 of the fee header, in wire order, compared with the natural-order hash of Bari’s tip. The after proof-of-work check reads the natural-order SHA-256(SHA-256(header)) as a little-endian 256-bit integer. A tip hash that Bari learns as Bitcoin display hex, from an RPC, an index document, or a log, is reversed once to natural order before any comparison. Neither side is ever compared in display order. A fee header that matches Bari’s tip only when one side is reversed is not current, before, or after, and the POST is answered 402.
  • The path-ask leaf is the waypoint’s hex, byte 0 first.

header.height is a JSON number in the range 0 … 2^53 − 1. It is not a JSON string. A JSON number written with a fractional part or an exponent is not a height. That bound is what this file states for a height. A height above 2^53 − 1 that an index document reports makes that read unsuccessful. Record not read. Do not clamp the value and do not record the oversized value. An integer above 2^53 − 1 in beacon_height or last_interrupt_height is recorded in section 14: shape mismatch, shape_item includes height_range, and the value is not defined. It is not not read. block_height in a body that decodes is in 0 … 2^32 − 1, which is inside 0 … 2^53 − 1. A hash-verified canonical path body has no height above 2^53 − 1. A body that does not decode, or whose validate_for does not succeed, has plant mismatch and claimed_block not defined. That is not shape_item height_range (P14.6). The hike object still decodes. not read is only for a read that is not hash-bound or whose hash does not agree.

header.time is the Bitcoin header nTime of the header at that height. It is a JSON integer in the range 0 … 2^32 − 1, in Unix seconds. It is not a JSON string. A JSON number written with a fractional part or an exponent is not a time. On the mainnet path, header.time equals the nTime of the block whose hash is header.hash. The portable index is faithful to Bitcoin: header hash, height, and nTime. Anyone may bypass the index and read Bitcoin directly (see §14). Section 14 measures the portable index. It does not read Bitcoin as its procedure. T(x) reads header.time from the portable index for block x only when Rule A4.2 records index_comparison match. Produce does not read header.time. Bari’s observed time (header.timestamp_unix_ms, when a document carries it) is a different member. Section 14 does not use it as T.

A read whose header has no time, or whose time breaks this rule, is still a successful read for hash and height when those members follow the rules above. T for that header is then not read. Section 14.2 records that token. A read that is not successful leaves both the index comparison and T as not read. T is taken only when section 14.2 records index_comparison match for that header. match requires the stored hash to agree with the index. A successful read whose hash does not agree is mismatch, and T is not read. Do not take that document’s time. A height disagreement is also mismatch, and T is not read on any mismatch. A successful read with a well-formed time does not record T when the hash does not agree.

The body rules above are the portable index. header.hash and header.height follow those rules for every header read. header.time follows them on a row that carries it. A plant read does not require them. A row with no header.time records T as not read. This file does not name a cut height and does not backfill a row. Produce Observe does not use that GET.

Produce Observe returns (natural, height) from the plane's one tip sequence. The default profile is the test plane. Its sequence is the synth sequencer (the synth tip sequence). Wire magic is regtest headers (header network magic for gossip). That magic is not a Bitcoin Core plant and not a tip clock. One cadence, mean about 60 seconds. “Mean about 60 seconds” describes the synth sequencer. It is not a section 6 constant, not a conformance-small parameter, and not an admission threshold. The natural beacon is that header's hash in natural byte order. Test Observe is that gossip peer. It does not GET an HTTP tip.

Synth headers. Every synth header carries the fixed nBits 0x207fffff. A synth header has valid proof of work when SHA-256(SHA-256(header)), read as a little-endian 256-bit integer, is at most the target that nBits encodes. On the test plane Bari applies that fixed nBits and that check for the Rule BR1 “after” case. The test plane keeps the same ±1 block contract as the live plane; only the block interval is shorter. Synth proof of work is cheap by design. A forged “after” header is valid only while Bari’s tip is unchanged, and it does not lower the 21-bit fee.

The live profile is explicit. Its sequence is the Bitcoin best chain, the chain with the most work, read by gossip. Live Observe does not GET an HTTP tip.

When no gossip peer is named, the starting list is a copy of Bitcoin Core's current mainnet DNS seeds (CMainParams::vSeeds in src/kernel/chainparams.cpp). The list is Core's own bootstrap. This specification does not invent a host. The copy is what lets a producer join gossip without naming one peer and without depending on any groundinglab node. A lookup of one seed returns node addresses. The producer connects to those nodes and asks them for more peers. An address that does not answer is dropped. That is how a producer stays independent of any single gossip peer.

Header source. On the live plane the producer’s header source keeps sessions with at least HEADER_PEERS_MIN gossip peers at the same time, for the whole run. HEADER_PEERS_MIN is 2. A peer announces a header when it relays that header or a header that builds on it. When one connected peer has announced a header and another connected peer has not announced it within HEADER_PEER_SILENCE_SECONDS of the first announcement, the silent peer is disconnected and replaced by a new peer from the starting list or from addresses that peers have given. HEADER_PEER_SILENCE_SECONDS is 120. Both values are constants of this file. They are not levers and not SDK options. While no peer announces a new header, no peer is replaced: a natural gap between blocks is not silence. When fewer than HEADER_PEERS_MIN peers are connected, the header source keeps connecting new peers asynchronously, backing off between attempts, and writes an alert record (section 13.5) named header_peers_low each time the count falls below HEADER_PEERS_MIN. The walk continues, the object is unaffected, and the run still follows this section. A header source built to use one peer does not follow this section. Keeping, replacing, and reconnecting peers is asynchronous work and never pauses a leg (Rule A6). The copied list is checked again against Bitcoin Core's mainnet chainparams when a producer release is cut.

The copy, in Core's order, is dnsseed.bluematt.me, seed.bitcoin.jonasschnelli.ch, seed.btc.petertodd.net, seed.bitcoin.sprovoost.nl, dnsseed.emzy.de, seed.bitcoin.wiz.biz, seed.mainnet.achownodes.xyz. The port is 8333. A named peer is still host:port when the producer supplies one. This specification does not name that host or port. The test profile does not use this list. Its Observe remains the synth peer.

Produce does not read {index-url}. Produce does not read an HTTP tip, including {tip-url}/index and {tip-url}/index/{height}.

Observe is that plane sequence and returns (natural, height).

The seed beacon is the natural beacon from the first successful Observe, taken at the start of produce, before any leg. It fixes the terrain. It is the header stage 0 starts from. That header is the latest one already on the tip sequence. Produce does not wait for a header mined after produce starts. A header that is minutes old is the seed when no later header has been read. After the terrain fill, stage 0’s legs begin from the seed. There is no Bari post before those legs. A later header is the interrupt I_0, read at the Observe after a checkpoint. It does not replace the seed. If the header source holds no header yet, the run has not started: produce keeps reading asynchronously until a seed read succeeds or the run is cancelled. No leg exists yet, so no leg is paused.

A stage ends at its interrupt. The interrupt is the first successful Observe, taken after a checkpoint has been recorded, that returns a natural beacon different from the header the stage started from and a height strictly greater than that header’s height. The header stage 0 started from is seed_beacon. The header stage g ≥ 1 started from is I_{g−1}. Both the hash condition and the height condition are required. If the tip moved by more than one block before that check, the interrupt is the tip as read. It is not an earlier header the producer did not read. The interrupt is a Bitcoin header interrupting the drive. The post-checkpoint tip read returns at once the latest header the producer’s header source already holds. It never waits for a header, a peer, or the network. A read that finds no newer header, or whose header source is unavailable, is not an interrupt: the stage continues with its next leg, and an unavailable source is logged (section 13.5).

The interrupt that ends stage g, for g from 0 through 16, is stored as stages[g+1].beacon. The waypoint W_{g+1} is built from that interrupt after it is read (Rule A1.3, section 12.2). The interrupt that ends stage 17 is last_interrupt. No second Observe replaces stages[g].beacon after the waypoint post. There is no rebinding.

A header newer than I_g is the next interrupt candidate. Produce reads it at the Observe after stage g+1’s first checkpoint, the same as any other header.

An Observe taken for the Hashcash fee header, a waypoint POST, or a path ask does not end a stage and is not I_g. Those reads, the POST, and the ask are asynchronous events (Rule A6). None of them pauses a leg.

Heights are stored on stages. A height is not an input to SHA-256 in this construction.

A clock read that does not succeed never stops the run and never withholds the object. A post-checkpoint tip read that does not succeed is not an interrupt: the walk continues with its next leg, and produce logs the transitory condition (section 13.5). An Observe for the fee header, a waypoint POST, or a path ask that does not succeed is a miss. Rule A3.4 records it and produce continues.

Verification does not Observe the tip sequence and does not GET an HTTP tip. Section 14.2 reads GET {index-url}/index/{height} under the body rules in this section, once for each stored header: each stage’s beacon_height, then last_interrupt_height. It records index_comparison and T for each. That read is part of the header measurement in section 14.2. It is not the only request verify may send: section 14.3 may read the same index for a present path’s plant facts. Verify does not GET a Bari path. The index is that plane's index. The default profile does not fill https://index.groundinglab.io.

10. Terrain

Normative.

seed_preimage = public_key ‖ seed_beacon          # 64 bytes, no separator, no label
D = SHA-256(seed_preimage)                        # the seed, 32 bytes
P[k] = u64_le(D[8k : 8k+8])                       # k = 0 … 3

The seed preimage is 64 bytes. It has no separator and no domain label. The seed is the 32-byte digest D. It is not a 64-bit pin. Lane P[k], for k = 0 … 3, is eight little-endian bytes of D. All 256 bits of D are the lanes. There is no fifth lane and no domain label on the seed. Informative. Leg 0 of stage 0 starts at S_0 = u64_le(D[0:8]) = P[0], and the first terrain-sample offset is P[0] mod OFFSET_MODULUS. The same 64 bits are lane 0, the first leg’s opening state, and that first sample offset. That reuse is the seed, by design. It is not a separate exploit.

Normative. Every terrain word depends on all 256 bits of the seed. A word map in which some word is unchanged when any one lane changes is not this construction. The NL4 base word below is that rule. Parent mixing and slot_mix do not remove it.

The seed equals stages[0].waypoint by design. Both are D. Nothing new is stored on the object.

Every integer step below is a u64. A sum or product written mod 2^64 wraps as section 3 defines: the low 64 bits are kept. The terrain is TERRAIN_BYTES bytes, filled below, and then left unchanged. Later beacons do not change it. Slot i, for 0 ≤ i < N_SLOTS, is

terrain[i × SLOT_BYTES : (i+1) × SLOT_BYTES]

Every load of slot i returns those same SLOT_BYTES bytes. The step does not write terrain.

10.1 mix64

mix64(x):
    y = (x + MIX_ADD) mod 2^64
    y = ((y XOR (y >> 30)) × MIX_MUL1) mod 2^64
    y = ((y XOR (y >> 27)) × MIX_MUL2) mod 2^64
    return y XOR (y >> 31)

>> is a logical right shift of a u64: vacated high bits are zero. It does not sign-extend. << is a left shift of a u64. The shift count is not reduced modulo 32. i << 32 is the slot index multiplied by 2^32 in that u64, and for every legal i the product fits in 64 bits.

10.2 Lanes

The four lanes are P[0], P[1], P[2], and P[3] from the seed, as section 10 states. A squeeze of the seed to one 64-bit fill pin is not this construction. Do not compute one. Do not use one.

Informative. Conformance does not require computing such a squeeze. The pack may publish a terrain digest made from a squeezed seed as a negative. An implementation that emits that digest does not conform.

10.3 Word rotation and parent indices

rotl(w, r) is w rotated left by r bits on a 64-bit word. Bits that leave the top re-enter at the bottom. rotl(w, 0) = w. In this section r is in 0 … 63.

floor_log2(n), for an integer n ≥ 1, is the greatest integer b with 2^b ≤ n.

reverse32(v) treats v as 32 bits, high bits zero when v < 2^32. Bit k of the result, with bit 0 the least significant bit, equals bit 31 − k of v.

For a slot index i with 1 ≤ i < N_SLOTS:

bits = max(floor_log2(i), 1)
bitrev_parent(i) = (reverse32(i) >> (32 − bits)) mod i

h = (i × MIX_ADD) mod 2^64
mul_parent(i) = floor(h / 2^32) mod i

Both parent values are integers in 0 … i − 1.

10.4 dag2 fill

Start from TERRAIN_BYTES zero bytes. For i from 0 through N_SLOTS − 1, build WORDS_PER_SLOT words and then store the slot.

The base word is NL4. For each j in 0 … WORDS_PER_SLOT − 1:

c = (i << 32) XOR j XOR WORD_XOR
t = mix64( P[j mod 4] XOR c )
t = mix64( t XOR rotl(P[(j + 1) mod 4], 17) )
t = mix64( t XOR rotl(P[(j + 2) mod 4], 31) )
t = mix64( t XOR rotl(P[(j + 3) mod 4], 47) )
w[j] = t

mix64 is §10.1, including the initial + MIX_ADD. A finalizer that omits MIX_ADD is not this function. i << 32 is the slot index multiplied by 2^32 in a u64, as under mix64 above: a left shift of a u64, and the shift count is not reduced modulo 32. j is the word index. j mod 4 selects a lane, and the next three steps fold the other three lanes in, so the word depends on P[0], P[1], P[2], and P[3].

A base word that uses one lane, two lanes, or a linear mix of the four lanes (including a map that is unchanged when two lanes are each replaced by their bitwise complement) is not NL4. Those maps are not conforming.

Informative. The one-lane map is mix64(P[j mod 4] XOR c). The linear map is one mix64 of P[j mod 4] XOR rotl(P[(j + 1) mod 4], 17) XOR rotl(P[(j + 2) mod 4], 31) XOR rotl(P[(j + 3) mod 4], 47) XOR c, with c as in the NL4 chain. Neither map is NL4.

Slot 0 keeps those words. It has no parent lanes and no slot mix.

When i > 0, take the three parents in this order, even if two of them are the same index:

  1. i − 1
  2. bitrev_parent(i)
  3. mul_parent(i)

For parent number k in 1, 2, 3, and for each j in 0 … WORDS_PER_SLOT − 1, let p be that parent index and let pw be the little-endian word already stored at word j of slot p:

rot = (j + k) mod 64
w[j] = mix64( (w[j] XOR rotl(pw, rot)) XOR ((k × PARENT_MUL) mod 2^64) )

After those three parents:

slot_mix = (i × SLOT_MUL) mod 2^64
w[j] = mix64(w[j] XOR slot_mix)    for j = 0 … WORDS_PER_SLOT − 1

For every slot, including slot 0, write le64(w[j]) into word j of slot i.

Terrain is computed once per terrain, from the seed, before the first leg. That is once per PCOC for the producer and once per gate check for a verifier that rebuilds the terrain. It is not once per leg. The public key and the seed beacon enter only through the seed preimage.

11. Leg

Normative.

A leg is STEPS_PER_LEG steps. It starts from the previous checkpoint defined in section 12. The leg result is the u64 end state of the leg. The checkpoint is the digest below. Every leg in a stage uses the same 32-byte second limb: the seed on stage 0, and waypoint W_g on stage g ≥ 1.

11.1 Step

Let S be the current u64 state. One step computes the next state S'.

i = S mod N_SLOTS
slot = terrain[i × SLOT_BYTES : (i+1) × SLOT_BYTES]

slot is SLOT_BYTES long and contains WORDS_PER_SLOT little-endian words. Word w, for w = 0 … WORDS_PER_SLOT − 1, is u64_le(slot[8w : 8w+8]). The word index is the integer w.

acc = 0
for w = 0 … WORDS_PER_SLOT − 1:
    acc = (acc + mix64(word(w) XOR S)) mod 2^64
S_folded = acc
E = SHA-256(SEAL_DOMAIN ‖ le64(S_folded))
S' = u64_le(E[0:8])

mix64 is exactly §10.1, including the initial + MIX_ADD. A function that omits MIX_ADD is not mix64. A port that used that shorter function produced a different step. acc starts at 0, not at S. Each step of the loop adds the mix64 output with wrapping addition in mod 2^64. Equal words do not cancel under addition. An XOR of those outputs is not this fold. The per-word input is word(w) XOR S. There is no w term. The loop is every word in the slot. Under section 6, WORDS_PER_SLOT is 2048, so the production bound is w = 0 … 2047. Profile conformance-small (section 16.1) uses that profile’s WORDS_PER_SLOT in this same sentence. It does not change the production constant.

Normative. Every input to mix64 is word(w) XOR S. No per-word term is independent of S, so nothing can be pre-folded into the stored terrain. A transformed terrain array is not the §10.4 terrain and is non-conforming. Equal words do not cancel under addition.

Informative. The last stage of mix64 is y XOR (y >> 31), which is linear over GF(2). An accumulator that XORs mix64 outputs lets a producer apply that stage once to the XOR of the pre-final values. Wrapping addition is not that linear map, so that hoist does not compute this fold. mix64 itself is unchanged.

The fold S_folded = S XOR (XOR over w of (word(w) XOR w)) is not conforming. The parenthesised XOR does not depend on S, so one 8-byte value per slot can stand in for the slot. It is the negative slot_summary_fold. The XOR accumulator acc = acc XOR mix64(word(w) XOR w XOR S) is also not conforming. It is the negative xor_accumulator_fold. The wrapping-add fold acc = (acc + mix64(word(w) XOR w XOR S)) mod 2^64 is also not conforming. word(w) XOR w does not depend on S. It is the negative xor_w_fold. The wrapping-add fold acc = (acc + mix64(word(w) XOR ((S + w) mod 2^64))) mod 2^64 is also not conforming. It is the negative s_plus_w_fold. At S = 0 those two folds match each other, because (S + w) equals w, and both differ from this section: this section mixes word(w) XOR S.

Normative. Christian's requirement, in his words:

a deliberately inefficient algorithm on the hash principle in reverse; a hash can't rehydrate the 16 KB, so each step must be driven by the full 16 KB slot, and no digest, lookup table or smaller data can stand in for it. No efficiencies, caching, tables or partial reads anywhere in hike/fold/terrain. A producer speed-up is a suspected break: flag it, never merge it.

Normative. A conforming producer and a conforming verifier, on every step of every leg, load all SLOT_BYTES bytes of the selected slot from the terrain of this PCOC and mix each of the WORDS_PER_SLOT words with the current state S by the §11.1 loop, including mix64 with MIX_ADD. The load is SLOT_BYTES bytes per step. A value computed on an earlier step does not satisfy the load on this step. A table, a digest, a per-slot summary, or any value shorter than the slot does not satisfy the load. Terrain is filled once for this PCOC from this seed, by §10.4, before the first leg, and is not shared with another PCOC and not reused from an earlier PCOC. The same rule applies to a gate recomputing a leg: one terrain for that check, then one full slot load mixed with S on every step of every recomputed leg. A producer or a verifier that emits the same leg, checkpoint, or waypoint bytes by a shorter load is non-conforming. A producer speed-up is a suspected break: flag it, and do not merge it. One terrain fill for this PCOC, shared by every leg of that run, is required. It is not a cache of an earlier step and it is not a cache shared with another PCOC. Running the legs of one round concurrently is not an efficiency. Each step still reads the full slot and mixes every word with S.

SEAL_DOMAIN ‖ le64(S_folded) is 20 bytes. That is one SHA-256 block.

Informative. The baseline that sets the floor and the gate policy is commodity hardware with SHA-256 instructions, x86 SHA-NI or Armv8 SHA2, of the commodity laptop with SHA instructions (for example an AMD Ryzen 5 5600H).

Normative. Where the host provides SHA-256 instructions (x86 SHA-NI or Armv8 SHA2), an implementation uses those instructions for every SHA-256 in this construction: the seed, the seal, the checkpoint, the waypoint fold, and the waypoint. A software SHA-256 is permitted only where the host has no such instructions. This requirement does not change a byte. A vector match does not excuse a software SHA-256 on a host that has the instructions. Hardware SHA-256 and software SHA-256 give identical bytes. A vector does not test which one ran. The rule is there so a measured rate is the rate of the baseline host.

E[0:8] is the first eight bytes of the seal digest E in index order, byte 0 first. S' = u64_le(E[0:8]) is the little-endian integer of those bytes. It is not the integer whose big-endian hexadecimal spelling is the same eight bytes. S and S' are 64-bit unsigned integers under the wrapping rule in section 3. The step does not keep a wider integer. Section 7.4 records the S_folded = 0 check: those eight digest bytes spell 4816bdaf23240476 in index order, and the integer S' is 0x76042423afbd1648.

The step reads terrain and does not write it.

11.2 Leg result

S_0 = u64_le(previous_checkpoint[0:8])
S_{t+1} = Step(S_t)    for t = 0 … STEPS_PER_LEG − 1
leg_result = S_{STEPS_PER_LEG}

leg_result is a u64. On the hike object it is the byte string le64(leg_result).

11.3 Terrain sample

The terrain sample at a previous checkpoint is a contiguous window of SAMPLE_BYTES bytes. It is not aligned to a slot boundary.

offset = u64_le(previous_checkpoint[0:8]) mod OFFSET_MODULUS
terrain_sample = terrain[offset : offset + SAMPLE_BYTES]

The offset is taken from the first eight bytes of the previous checkpoint. The window itself is the sample.

This window is not the waypoint’s slot sample. The slot sample is defined in section 12.

11.4 Checkpoint

Rule A2.3. The checkpoint bind, in order, is

H(previous checkpoint ‖ stage limb ‖ terrain sample(at previous checkpoint) ‖ leg result)

H is SHA-256. The stage limb is the seed on stage 0 and W_g on stage g ≥ 1. The four limbs are raw bytes:

Order Limb Bytes
1 previous checkpoint 32
2 seed on stage 0; W_g on stage g ≥ 1 32
3 terrain sample at the previous checkpoint SAMPLE_BYTES
4 leg result le64(leg_result), 8 bytes

The preimage is 32 + 32 + SAMPLE_BYTES + 8 bytes. Under section 6 that is 32 + 32 + 1024 + 8 = 1096 bytes. On stage 0 the second limb is the seed.

The stage path is not a limb of this preimage. Stage 0 has no path.

The checkpoint is the 32-byte digest. The hike object stores that digest and the matching leg_results limb. The first limb is the full 32-byte previous checkpoint. The eight bytes that select the terrain sample are not a substitute for that limb. The leg result appears only as the fourth limb.

12. Stage and waypoint

Normative.

Stages are stages[0] through stages[17].

A stage contains one or more legs. The count is how many legs the drive in section 13 recorded before interrupt I_g. Every leg in stage 0 uses the seed as its second checkpoint limb. Every leg in stage g ≥ 1 uses waypoint W_g.

12.1 Previous checkpoint

Rule A2. Stages and where leg 0 starts

Rule A2.1 Eighteen stages

seed_beacon plus the 18 interrupts gives 18 stages, numbered 0–17.

Rule A2.2 Leg 0

The seed is

stages[0].waypoint = SHA-256(public_key ‖ seed_beacon)   # the seed; 64-byte seed preimage; no label

The seed equals stages[0].waypoint by design. That field is the seed. It is not a Rule A1 waypoint. It is stored on the object. It is the stage limb of stage 0’s checkpoint preimages (section 11.4). It is the third limb of W_1’s preimage (section 12.2). It is never posted to Bari. Stage 0 has no waypoint of its own. The seed is not one of W_1 … W_17.

Leg 0 of stage 0 starts from the seed. Leg 0 of stage g ≥ 1 starts from W_g:

stage 0:           previous = stages[0].waypoint    # the seed
stage g, g ≥ 1:    previous = stages[g].waypoint    # W_g

Leg j for j ≥ 1 uses previous checkpoint checkpoints[j − 1] of that same stage.

The public key reaches every later checkpoint, fold, and waypoint through the seed.

A stage does not carry the last checkpoint of the previous stage forward. The previous stage’s checkpoints reach the next stage through the fold in its waypoint (section 12.2).

Rule A2.3 Checkpoint

The checkpoint preimage is section 11.4. The stage limb is the seed on stage 0 and W_g on stage g ≥ 1. Leg 0’s previous checkpoint is that same value. Leg j ≥ 1 uses checkpoints[j − 1] of that stage.

Rule A2.4 First post

The first Bari post is W_1, after I_0. Stage 0 has no plant and no path.

12.2 Waypoint

Waypoint preimage

Rule A1.3. For g from 0 through 16, after stage g has ended at interrupt I_g, produce computes W_{g+1} as follows. B is I_g, in natural order. It is stages[g+1].beacon. B is not stages[g].beacon. W is stages[g].waypoint: the seed when g = 0, and W_g when g ≥ 1. C[0] … C[n−1] are stages[g].checkpoints in leg order. n ≥ 1. fold_g is the fold below.

F_0 = 0^32
F_{j+1} = SHA-256(F_j ‖ C[j])    for j = 0 … n − 1
fold = F_n                        # fold_g

slot_index = u64_le(B[0:8]) mod N_SLOTS
slot_sample = terrain[slot_index × SLOT_BYTES : (slot_index + 1) × SLOT_BYTES]

W_{g+1} = SHA-256(B ‖ slot_sample ‖ W ‖ fold)

That concatenation order is normative. B is the interrupt I_g, in natural order, stored as stages[g+1].beacon. slot_sample is the terrain slot indexed by I_g’s hash. The preimage is 32 + SLOT_BYTES + 32 + 32 bytes. Under section 6 that is 32 + 16384 + 32 + 32 = 16480 bytes.

The stage path is not an input to this preimage.

F_0 is the start value of the fold. It is not itself hashed. When n ≥ 1, the first hash in the fold is SHA-256(0^32 ‖ C[0]).

Produce requires n ≥ 1. An empty checkpoint list is a shape mismatch (leg_count when that count differs from len(checkpoints)), not a skipped waypoint.

Verify still applies this fold when both stages of the pair are present and the checkpoint bytes are usable. n = 0 leaves fold = F_0 = 0^32 and performs no hash. Verify recomputes W_{g+1} from that fold and records mismatch when the digest differs from the stored waypoint. That stage’s legs is []. not defined is recorded only when the stage pair is absent or the bytes needed for the recompute are absent or unusable. An empty checkpoint list is usable. whole_hike_legs adds each stage’s checkpoint count, including zero, when every stage from 1 through 17 has a usable count. A missing stage leaves whole_hike_legs not defined. hike_legs counts those same checkpoints. Stage 0 is in neither count. six_leg is not defined when leg_total < 6.

W_{g+1} is the pivot. It ends stage g and starts stage g+1. Produce posts it to Bari with the Hashcash fee (section 13.1). Leg 0 of stage g+1 starts from it.

W_1 is built after I_0 from that interrupt, its slot sample, the seed, and the fold of stage 0’s checkpoints. It is the first waypoint posted to Bari.

Stage 17 ends at I_17. No waypoint is computed from stage 17 or from last_interrupt. Stage 17’s checkpoints are not folded into a waypoint.

12.3 What a stage stores

When stage g ends at I_g, the object records the header the stage started from (beacon and beacon_height), the seed or the waypoint the stage’s legs used, leg_count, checkpoints, and leg_results.

For g from 0 through 16, I_g is stored as stages[g+1].beacon and stages[g+1].beacon_height. I_17 is stored as last_interrupt and last_interrupt_height.

Stage 0 has no plant and no path key. No ask is made for stage 0.

The path ask window is section 13.1. No ask is made above the cut-off. A waypoint W_1 … W_16 with no checked path stores empty bytes and produce writes path_skip <g> (Rule A3.4). W_17 stores empty bytes and produce writes not fetched by design 17.

14. Verify — relocated

FRAMING (2026-10-24). The measurement procedure formerly inlined here as §14 Verify (Rule A4, §§14.1–14.7, Rule A5) was relocated without wording change to 06-Measurement_Record_Process.md. Construction defines the object; Measurement reads a finished object against Construction and emits the fact record. Rule-index rows A4/A5 above still identify those rules.

Republished from pcoc 1b9f632 (spec/ six-doc pack; same bytes at e8030ba). Raw markdown.