Documentation
¶
Overview ¶
Package tlog implements PRD 1 § 5.4: deterministic reconstruction of `t_root`, the Merkle commitment over a pod invocation's tool-call log `T_log`, from an ordered sequence of `proof_tool_calls`-equivalent rows.
`t_root` is one of 13 public inputs to the konareef-pod-step-v1 Nova IVC step circuit (PRD 1 § 5.1). It is byte-interoperable across the Go CLI verifier (this package), reef-core's Elixir ReefCore.Proofs.TLog (the writer side), the TypeScript SDK verifier, and the (v1.5) BSV Script verifier. Any drift between implementations breaks every proof in flight, so the leaf/inner/ length-bind encoding here is treated as wire-format spec — not "implementation detail".
Public surface:
- Row — a single tool-call record (matches the writer-side `proof_tool_calls` row schema)
- LeafHash — SHA-256(0x00 || record_bytes_i)
- TRootFromRows — full t_root from an unsorted []Row
- Z — the all-empty-T_log padding sentinel SHA-256(0x00 || 0x00)
- LeafTag, InternalTag, LengthBindTag — PRD 1 § 5.4 tag set
All constants in this package are normative per PRD 1 § 5.4. Do not modify without a counter-amendment to the upstream PRD and the `tlog-from-db-rows` conformance vector.
internal/tlog/tlog.go — public surface for PRD 1 § 5.4 T_log reconstruction. The record_bytes encoding here is normative across every konareef verifier; treat it as wire-format spec.
Hash construction (post Lever-1, PRD 1 § 5.4): the tool-log Merkle tree is built over Poseidon-over-Pallas-Fq, NOT SHA-256. Each leaf is poseidon.LeafHash(poseidon.RecordToField(record_bytes)); internal nodes use poseidon.InternalHash; the count is bound by the 0x03 length element. All hashing delegates to internal/poseidon (the drift-gated, neptune-parity primitive); this package owns only the record_bytes encoding and the call_index ordering.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func LeafHash ¶
LeafHash returns the PRD 1 § 5.4 Poseidon leaf hash for a single Row: poseidon.LeafHash(poseidon.RecordToField(RecordBytes(r))). It returns an error if the record exceeds the pinned circuit cap (fail-closed: such a record could not have been proven).
func RecordBytes ¶
RecordBytes returns the canonical PRD 1 § 5.4 per-record encoding (amended 2026-07-05 — decision `2026-07-05-konareef-digest-tool-ids`), byte-identical to paygate-zk's konareef_circuit troot_sha::encode_record:
record_bytes = 0x20 // ULEB128(32), constant under the digest shape
|| tool_id_digest (32 B) // SHA-256(tool_id_utf8)
|| args_hash (32 B)
|| result_hash (32 B)
|| ts (8 B big-endian u64, microseconds since Unix epoch)
|| sats (8 B big-endian u64)
Every record is a FIXED 113 bytes: the variable-length name form is superseded (it committed the raw name and had no sats). CallIndex is intentionally not part of record_bytes — it is the out-of-band sort key (PRD 4 § 4.1.4).
func TRootFromRows ¶
TRootFromRows reconstructs PRD 1 § 5.4's Poseidon t_root from an unordered set of Rows:
- Sort by Row.CallIndex ASC (PRD 4 § 4.1.4 — the sole sort key).
- Build record_bytes for each row.
- t_root = poseidon.TRoot(records) — the Z-padded balanced Poseidon tree of leaf_hash(record_to_field(record)) leaves, bound by the 0x03 length element.
It does not mutate the input slice (sort happens on an internal copy) and returns an error if the log exceeds the pinned record/count caps.
Types ¶
type Row ¶
type Row struct {
// ToolID is the UTF-8 NFC tool identifier. Length is bound by
// LEB128(len(tool_id_utf8)) at the start of record_bytes.
ToolID string
// ArgsHash is the 32-byte SHA-256 of the canonical tool-call
// argument bytes. Canonicalization is the runtime's responsibility;
// PRD 1 only binds the hash.
ArgsHash [32]byte
// ResultHash is the 32-byte SHA-256 of the canonical tool-call
// result bytes. Canonicalization is the runtime's responsibility;
// PRD 1 only binds the hash.
ResultHash [32]byte
// TS is the wall-clock timestamp of the call. Encoded as u64
// big-endian microseconds since the Unix epoch (UTC). PRD 1 § 5.4
// SHOULD be monotone non-decreasing within a single T_log, but the
// helper does not enforce monotonicity (PRD 4 § 4.1.4 makes that
// the writer's responsibility).
TS time.Time
// CallIndex is the writer-assigned ordering key. Rows are sorted by
// CallIndex ASC before leaves are hashed; it is NOT part of the
// leaf preimage. PRD 4 § 4.1.4 makes (proof_id, call_index) the
// uniqueness constraint.
CallIndex uint32
// Sats is the satoshis paid for this tool call (0 for unpaid/local
// tools, e.g. `bash`). Encoded big-endian as the trailing 8 bytes of
// record_bytes (PRD 1 § 5.4 amended 2026-07-05). The circuit binds
// Σ(sats) ≤ c; the writer is responsible for that invariant.
Sats uint64
}
Row is a single proof_tool_calls-equivalent row, matching the writer-side schema in reef-core's `proof_tool_calls` table (P1.1). CallIndex is the SOLE sort key for T_log leaf order (PRD 4 § 4.1.4); TS is for audit/debugging only and is not used to order leaves.