ZIP: ??? Title: Structured Attestation Protocol for Application-Layer Lifecycle Events (ZAP1) Owners: Frontier Compute ops@frontiercompute.io Credits: Zk-nd3r Status: Draft Category: Standards Track Created: 2026-03-28 License: MIT Discussions-To: https://github.com/zcash/zips/pull/1243 Pull-Request: https://github.com/zcash/zips/pull/1243
Terminology
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 when, and only when, they appear in all capitals, as shown here.
Abstract
This ZIP specifies an application-layer attestation format for lifecycle events committed to Zcash shielded transactions. It defines event typing, BLAKE2b hash construction rules, Merkle tree aggregation, and a verification procedure for on-chain commitments that keep participant-identifying data off-chain.
This draft is based on the deployed ZAP1 attestation protocol (formerly NSM1). In the reference deployment, event payloads
are hashed with BLAKE2b-256 using the personalization string
NordicShield_, inserted into an append-only Merkle tree, and periodically
anchored to Zcash using a transaction with type byte 0x09.
Relationship to ZIP 302
This ZIP defines application-layer attestation semantics. The carrier format is specified separately by ZIP 302 1 (Structured Memos, PR #638). When ZIP 302 is deployed, ZAP1 attestation payloads SHOULD be encoded as a ZIP 302 part type. Until then, the binary layout below serves as a transitional encoding within the raw 512-byte shielded memo field.
The parts of this ZIP that are independent of the container are: the event type registry, hash construction rules, Merkle tree aggregation, and the verification procedure.
Motivation
Zcash shielded memos can carry structured application commitments, but the ecosystem lacks a convention for typed lifecycle events that can be independently recomputed by external verifiers.
Exposing Zcash state and actions to agents is straightforward. A REST or GraphQL endpoint can be wrapped as an MCP server in a small amount of code, and wallet-layer MCP implementations can expose balances, signing workflows, sync state, and transaction construction. These surfaces share one property: the consumer must trust the server response. If the backend is wrong, compromised, or unavailable later, the agent has no independent receipt it can hand to a third party for verification.
ZAP1 closes that gap. It exposes no balances and moves no value. It produces a receipt: a typed event committed to a BLAKE2b Merkle tree, with the root anchored in Zcash memo data, verifiable from the public schema, proof material, and chain anchor without trusting the issuer's server.
The distinction is categorical. Tool servers answer "what does this backend say right now?" ZAP1 answers "what happened, provable later to another verifier?" The protocol exists for the proof artifact, not for tool exposure.
Applications that track ownership, deployment, billing, transfer, and exit events need:
- deterministic hash construction
- stable event typing
- a payload format that can be parsed without application-specific heuristics
- a way to commit large event histories without writing full plaintext records to the chain
This ZIP standardizes the event commitment rules used by one such deployment so that other builders can implement compatible tooling, verification, and future extensions.
Requirements
An implementation of this ZIP:
- MUST treat the attestation payload as a binary structure before any wallet-specific shielded memo encoding.
- MUST encode integers in big-endian byte order.
- MUST NOT place participant PII directly in the attestation payload.
- MUST derive event payload hashes deterministically from the underlying event fields.
- MUST preserve event insertion order when deriving Merkle roots.
- MUST allow verifiers to recompute both the event leaf hash and the anchored root from public artifacts plus application-provided witness data.
Specification
Binary Payload Layout
Before shielded memo encoding (or ZIP 302 part encoding), the binary attestation payload is:
byte 0 : version = 0x01
byte 1 : type = 0x01..0x0f
bytes 2..5 : cohort_id = u32 big-endian
bytes 6..37 : payload_hash = 32 bytes
bytes 38..45: timestamp = u64 big-endian unix seconds
bytes 46..77: serial_hash = 32 bytes, or 32 zero bytes when unused
bytes 78..n : label = UTF-8 human-readable label, optional
For human-readable transport, the payload SHOULD be rendered as:
ZAP1:{type}:{payload}
where:
ZAP1is the protocol marker{type}is the two-digit lowercase hexadecimal event type{payload}is the hexadecimal encoding of the full binary layout above
Event Types
This draft defines the following event type assignments:
| Type | Name | Payload definition | Status |
|---|---|---|---|
0x01 | PROGRAM_ENTRY | BLAKE2b_32(wallet_hash) | Deployed |
0x02 | OWNERSHIP_ATTEST | `BLAKE2b_32(wallet_hash | |
0x03 | CONTRACT_ANCHOR | `BLAKE2b_32(serial_number | |
0x04 | DEPLOYMENT | `BLAKE2b_32(serial_number | |
0x05 | HOSTING_PAYMENT | `BLAKE2b_32(serial_number | |
0x06 | SHIELD_RENEWAL | `BLAKE2b_32(wallet_hash | |
0x07 | TRANSFER | `BLAKE2b_32(old_wallet | |
0x08 | EXIT | `BLAKE2b_32(wallet_hash | |
0x09 | MERKLE_ROOT | raw 32-byte Merkle root | Deployed |
0x0A | STAKING_DEPOSIT | `BLAKE2b_32(wallet_hash | |
0x0B | STAKING_WITHDRAW | `BLAKE2b_32(wallet_hash | |
0x0C | STAKING_REWARD | `BLAKE2b_32(wallet_hash | |
0x0D | GOVERNANCE_PROPOSAL | `BLAKE2b_32(wallet_hash | |
0x0E | GOVERNANCE_VOTE | `BLAKE2b_32(wallet_hash | |
0x0F | GOVERNANCE_RESULT | `BLAKE2b_32(wallet_hash |
Implementations of the deployed ZAP1 flow currently use the first nine event
types in production. The staking and governance event types are reserved and
MUST NOT be assumed stable until separately activated.
Hash Construction
Field sizes for hash inputs:
wallet_hash: 32 bytes (application-derived identifier)serial_number: 32 bytescontract_sha256: 32 bytesfacility_id: 32 bytes (zero-padded if shorter)timestamp_be: u64 big-endian (8 bytes)month_be: u16 big-endian (2 bytes)year_be: u16 big-endian (2 bytes)amount_zat_be: u64 big-endian (8 bytes)epoch_be: u32 big-endian (4 bytes)validator_id: 32 bytesproposal_id: 32 bytesproposal_hash,vote_commitment,result_hash: 32 bytes eachold_wallet,new_wallet: 32 bytes each
Unless otherwise specified, event payload hashes use BLAKE2b with:
- digest length: 32 bytes
- personalization:
NordicShield_(13 bytes, zero-padded to 16 bytes per BLAKE2b spec)
The hash input for each event type is defined in the table above. The
MERKLE_ROOT event is a special case whose payload hash is the 32-byte Merkle
root itself, without an additional BLAKE2b compression step.
For event types that carry a serial number, the serial_hash payload field MUST
be:
BLAKE2b_32(serial_number)
with the same personalization string NordicShield_.
If no serial number is applicable, serial_hash MUST be 32 zero bytes.
Merkle Tree Commitments
Applications using this attestation protocol SHOULD aggregate event payload hashes into
an append-only binary Merkle tree. For the deployed ZAP1 protocol:
- each event produces one leaf
- leaves are ordered by insertion sequence
- parent nodes are
BLAKE2b_32(left || right) - Merkle node personalization is
NordicShield_MRK - if a level has odd cardinality, the final node is duplicated
An inclusion proof consists of:
- leaf hash
- sibling hashes
- sibling positions
- derived Merkle root
- anchor transaction identifier
- anchor block height
On-Chain Anchoring
When anchoring a Merkle root to Zcash:
- the event type MUST be
0x09 - the payload hash MUST be the raw current Merkle root
- the shielded memo field SHOULD be encoded as
ZAP1:09:{payload}
The deployed ZAP1 implementation broadcasts shielded memo commitments using
zingo-cli. The current deployment anchors every 10 events or every 24 hours,
whichever occurs first.
Verification Procedure
To verify a committed event, a verifier:
- recomputes the event payload hash from the application fields
- reconstructs the attestation payload and event leaf hash
- walks the Merkle proof path to derive the root
- retrieves the referenced anchor transaction
- confirms that the shielded memo field contains the same
0x09root commitment - confirms that the transaction is mined on the intended chain
Rationale
This design separates protocol semantics from wallet transport.
The binary layout is fixed-width for all critical fields so parsers can extract event type, cohort identifier, and proof-relevant material without needing to understand any free-form label text. BLAKE2b-256 is used for compactness and performance. Personalization strings provide domain separation between event payload hashing and Merkle internal-node hashing.
Anchoring Merkle roots rather than full event records minimizes chain load while preserving independent verifiability. The shielded memo field remains short and constant-sized for the root-commitment case even as the underlying event history grows.
Privacy Considerations
This protocol avoids writing participant-identifying plaintext
data to chain. Implementations MUST ensure that wallet_hash, serial_hash,
contract digests, and other payload inputs are derived values rather than directly
identifying customer records.
The optional label field may leak application metadata if misused. Protocols
using this ZIP SHOULD either omit the label field or constrain it to operational
strings that do not identify participants.
Security Considerations
This protocol provides integrity and auditability, not confidentiality of the underlying application database. Security depends on:
- correct construction of event hashes
- correct Merkle insertion ordering
- correct association between proof bundles and the anchored root that covers them
- correct wallet-side retrieval of the mined shielded-memo-bearing transaction
This ZIP does not address:
- fraudulent off-chain data entry before hashing
- theft of the key used to authorize anchor transactions
- application-level authorization of transfers or exits
The deployed ZAP1 stack currently uses single-operator anchor signing, with a
separate FROST migration design under development for threshold authorization of
future root anchors.
Backwards Compatibility
This ZIP codifies the deployed version-0x01 ZAP1 format. Existing proof
bundles remain valid as long as verifiers preserve the original event hash
construction, Merkle hashing rules, and anchor transaction references.
Future incompatible payload layouts MUST use a new version byte and SHOULD use a distinct human-readable protocol marker.
Reference Implementation
Reference implementations are available at:
- Frontier-Compute/zap1, which implements the deployed
ZAP1attestation protocol, Merkle tree maintenance, proof bundle generation, and root anchoring flow. - Frontier-Compute/zap1-verify 2, which provides a standalone Rust and WASM verifier for ZAP1 leaf hashes and Merkle proofs.
In the deployed zap1 implementation:
- event payload hashes are computed with BLAKE2b-256 and personalization
NordicShield_ - Merkle internal nodes use personalization
NordicShield_MRK - root anchors are transmitted as
ZAP1:09:{root}
Test Vectors
A companion test vector package SHOULD provide:
- one event hash vector for each deployed event type
0x01through0x09 - the exact input fields used to derive the payload hash
- the expected
leaf_hash - the hash personalization strings used
The deployed ZAP1 implementation 3 publishes those vectors separately as: