-
Notifications
You must be signed in to change notification settings - Fork 100
SIP-038: Standard Trait Definition for Commitment-Based Private Metadata (Encrypted NFTs) #250
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
proofofgame
wants to merge
7
commits into
stacksgov:main
Choose a base branch
from
proofofgame:sip-enft-private-layer
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+270
−0
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
7d4f559
Create sip-enft-private-layer.md
proofofgame 84fafd0
Update sip-enft-private-layer.md
proofofgame 03c2a10
Improve clarity in notes and security considerations
proofofgame 86b7a24
Move SIP-038 to standard folder structure
proofofgame d4a17cd
Remove old file location (moved to sips/sip-038/)
proofofgame 2fb59ab
Revise terminology and enhance user experience guidance
proofofgame 602f055
Changing status from 'draft' to accepted'
clairetopalian File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,270 @@ | ||
| # SIP-038: Standard Trait Definition for Commitment-Based Private Metadata (Encrypted NFTs) | ||
|
|
||
| ## Preamble | ||
|
|
||
| SIP Number: 038 | ||
| Title: Standard Trait Definition for Commitment-Based Private Metadata (Encrypted NFTs) | ||
| Authors: Xenitron nortinex@gmail.com, Bogachev ogres2009anton@gmail.com | ||
| Status: Accepted | ||
| Consideration: Technical | ||
| Type: Standard | ||
| Created: 2026-01-10 | ||
| License: CC0-1.0 | ||
| Sign-off: | ||
| Layer: Trait | ||
| Discussions-To: https://forum.stacks.org/t/encrypted-nfts-on-stacks-a-standard-trait-for-commitment-based-private-layers-enft/18560 | ||
|
|
||
| # Abstract | ||
|
|
||
| This SIP proposes a minimal, composable trait interface for “Encrypted NFTs” (eNFTs): NFTs that commit on-chain to a private off-chain payload (“private metadata”) while keeping the payload itself off-chain. The standard defines: | ||
|
|
||
| 1) A public commitment interface, | ||
| 2) An optional owner-gated envelope descriptor for wallet user experience. | ||
| 3) Indexer-friendly event conventions. | ||
|
|
||
| This enables interoperable private-metadata NFT patterns across contracts, wallets, marketplaces, and indexers without requiring consensus changes. | ||
|
|
||
|
|
||
| # Copyright | ||
|
|
||
| This SIP is made available under the terms of the CC0-1.0 license. | ||
| This SIP’s copyright is waived to the extent possible under law. | ||
|
|
||
|
|
||
| ## Introduction | ||
|
|
||
| “Encrypted metadata / private metadata” NFTs on Stacks are currently implemented as project-specific patterns. This fragmentation makes it difficult for wallets, marketplaces, and indexers to support such NFTs uniformly. | ||
|
|
||
| This SIP standardizes a minimal interface for commitment-based private metadata on SIP-009 NFTs: | ||
|
|
||
| - A **public commitment** (hash) to the payload (or ciphertext), enabling anyone to verify integrity after reveal. | ||
| - An optional **owner-gated envelope descriptor** to guide wallet user experience (e.g., show “Reveal” only to the owner). | ||
| - A recommended **event format** so indexers can detect updates efficiently. | ||
|
|
||
| ### Relationship to SIP-009 | ||
|
|
||
| This trait is designed to be implemented **alongside** SIP-009-compliant NFT contracts, not as a replacement. An eNFT contract implements SIP-009 for ownership and transfer, and additionally implements this trait to expose commitment-based private metadata. | ||
|
|
||
| ## Goals | ||
|
|
||
| - Define a reusable trait for commitment-based private metadata for eNFTs. | ||
| - Make private-layer patterns discoverable and composable for wallets/indexers. | ||
| - Keep the standard minimal, additive, and backward-compatible. | ||
|
|
||
| ## Non-Goals | ||
|
|
||
| - Full on-chain confidentiality (not achievable in a public read-only model). | ||
| - Mandating a specific storage network, encryption scheme, or reveal flow. | ||
|
wileyj marked this conversation as resolved.
|
||
| - Defining a full game/puzzle “answer verification” protocol (out of scope). | ||
|
|
||
| **Note on encryption flexibility:** This SIP intentionally does not mandate a specific encryption scheme. Wallets and indexers interact only with the commitment layer — they verify `hash(payload) == on-chain commitment` and never perform decryption. Decryption occurs off-chain between the application and the asset owner, outside the trait scope. Different projects require different threat models (server-assisted reveal vs. client-side decryption with embedded key blob vs. proxy re-encryption). To prevent fragmentation, implementers using any non-default algorithm id (other than `u0`) **must** publish a stable, publicly available algorithm specification. Wallets **may** support only `u0` (SHA-256) and treat unknown algorithm ids as unsupported for verification while still supporting discovery. | ||
|
|
||
| # Specification | ||
|
|
||
| ## 1. Terminology | ||
|
|
||
| - **Payload**: The private off-chain data (JSON, image, encrypted blob, etc.). | ||
| - **Ciphertext**: An encrypted payload (still considered “payload bytes” for commitment purposes). | ||
| - **Commitment**: A hash over the payload bytes (often ciphertext bytes) recorded on-chain. | ||
| - **Envelope**: A small off-chain descriptor used for UX/access (e.g., retrieval URI, encrypted key blob, signed URL descriptor). | ||
| - **Reveal**: Off-chain delivery of payload/envelope to the user; the client verifies integrity by comparing hash(payload_bytes) to the on-chain commitment. | ||
| - **Algo ID**: An integer identifier that indicates how the commitment hash is computed. | ||
|
wileyj marked this conversation as resolved.
|
||
| - **Version**: A monotonically increasing version number for commitment/envelope encoding. | ||
| - **Token-id**: The SIP-009 NFT token identifier (`uint`). Each token-id uniquely identifies a single NFT within a contract. | ||
|
|
||
| ### 2. Commitment Semantics | ||
|
|
||
| Implementations **must** expose a commitment for a given token-id. | ||
|
|
||
| - The commitment **must** be computed exactly according to the declared algorithm id. | ||
| - The commitment **must** be stable: the same payload bytes must produce the same commitment. | ||
| - This SIP does not mandate a single algorithm, but **recommends**: | ||
| - `algo = u0` => SHA-256 over raw payload bytes | ||
|
proofofgame marked this conversation as resolved.
|
||
|
|
||
| ### Algorithm Registry (Informative) | ||
|
|
||
| | Algo ID | Algorithm | Notes | | ||
| |---------|-----------|-------| | ||
| | u0 | SHA-256 | Recommended default | | ||
| | u1-u99 | Reserved | For future standardization | | ||
| | u100+ | Application-defined | Projects may define custom algorithms | | ||
|
proofofgame marked this conversation as resolved.
|
||
|
|
||
| **Wallet implementation guidance:** Wallets and indexers are not required to support all algorithm ids. A conforming wallet may support only `u0` (SHA-256) and display a warning or informational notice for tokens using unrecognized algorithms. The registry enables organic ecosystem convergence without requiring a new SIP for each algorithm. | ||
|
|
||
| **Notes** | ||
| - If the payload is JSON, implementers should treat the committed object as raw bytes of a canonical encoding chosen by the application (this SIP does not standardize canonicalization). Consumers must verify against the exact bytes they receive on reveal. | ||
|
|
||
| --- | ||
|
|
||
| ### 3. Trait Interface (Clarity) | ||
|
|
||
| To make “optional” functionality actually optional in Clarity, this SIP defines **two traits**: | ||
|
|
||
| - A **required** commitment trait (baseline interoperability). | ||
| - An **optional** owner-envelope trait (wallet UX extension). | ||
|
|
||
| Contracts may implement the baseline trait alone, or both traits. | ||
|
|
||
| #### 3.1 Required Trait: Commitment | ||
|
|
||
| ```clarity | ||
| (define-trait enft-commitment-trait | ||
| ( | ||
| ;; Public: returns the commitment to the private payload (often ciphertext bytes) | ||
| (get-private-commitment (uint) | ||
| (response | ||
| (tuple | ||
| (commitment (buff 32)) ;; hash of payload bytes | ||
| (algo uint) ;; algorithm id (e.g., u0 = sha256) | ||
| (mime (optional (string-ascii 64))) ;; e.g. "application/json", "image/png" | ||
|
proofofgame marked this conversation as resolved.
|
||
| (size (optional uint)) ;; payload size in bytes (optional) | ||
| (commitment-version uint) ;; commitment scheme version | ||
| ) | ||
| uint | ||
| ) | ||
| ) | ||
| ) | ||
| ) | ||
| ``` | ||
|
|
||
| #### 3.2 Optional Trait: Owner-Gated Envelope (Wallet UX Extension) | ||
|
|
||
| ```clarity | ||
| (define-trait enft-owner-envelope-trait | ||
| ( | ||
| ;; user experience oriented: returns an owner-gated envelope descriptor. | ||
| ;; Implementations SHOULD check ownership for consistent wallet UX. | ||
| (get-owner-envelope (uint) | ||
| (response | ||
| (tuple | ||
| (envelope-uri (string-ascii 256)) ;; retrieval location (NOT a secrecy guarantee) | ||
| (envelope-hash (buff 32)) ;; integrity check for envelope blob/response | ||
| (algo uint) ;; algorithm id for envelope-hash | ||
| (envelope-version uint) ;; envelope scheme version | ||
| ) | ||
| uint | ||
| ) | ||
| ) | ||
| ) | ||
| ) | ||
| ``` | ||
|
|
||
| **Normative guidance** | ||
| - Contracts that claim eNFT support **must** implement `enft-commitment-trait`. | ||
| - Contracts **may** implement `enft-owner-envelope-trait` to provide a standardized “Reveal” UX path. | ||
| - Wallets/indexers should treat the presence of `enft-owner-envelope-trait` as an optional capability. | ||
|
|
||
| --- | ||
|
|
||
| ### 3.3 Error Codes (Recommended) | ||
|
|
||
| Implementations **may** choose their own error codes. This SIP **recommends**: | ||
|
|
||
| - `err u404` => token-id not found / no commitment set | ||
| - `err u401` => not authorized for `get-owner-envelope` (only relevant if `enft-owner-envelope-trait` is implemented) | ||
|
|
||
|
|
||
| ### 4. Events (Indexer Conventions) | ||
|
|
||
| When a private-metadata commitment is set or updated, contracts **should** emit a `print` event with a predictable structure: | ||
|
|
||
| ```clarity | ||
| (print { | ||
| notification: "enft-update", | ||
| token-id: token-id, | ||
| commitment: commitment, | ||
| algo: algo, | ||
| commitment-version: commitment-version | ||
| }) | ||
| ``` | ||
|
|
||
| If an envelope descriptor is set/updated, contracts **may** emit: | ||
|
|
||
| ```clarity | ||
| (print { | ||
| notification: "enft-envelope-update", | ||
| token-id: token-id, | ||
| envelope-hash: envelope-hash, | ||
| algo: algo, | ||
| envelope-version: envelope-version | ||
| }) | ||
| ``` | ||
|
|
||
| **Notes** | ||
| - Events are intended for indexers/wallets discovery and user interface updates. | ||
| - `envelope-uri` is intentionally excluded from the event for safety; wallets should fetch it via the read-only function if needed. | ||
| - Indexers should treat these events as hints and verify by reading the latest on-chain state as required. | ||
|
|
||
| --- | ||
|
|
||
| ## Backwards Compatibility | ||
|
|
||
| This SIP introduces no breaking changes. It is additive and does not modify existing SIP-009 NFT contracts. Projects can implement these traits alongside existing token URI / metadata patterns. Wallets and indexers can support eNFTs incrementally. | ||
|
|
||
| --- | ||
|
|
||
| ## Security Considerations | ||
|
|
||
| ### Read-only visibility and “owner gating” | ||
| `get-owner-envelope` ownership checks are primarily for **wallet UX consistency**, not cryptographic secrecy. Read-only calls can be simulated by any party running a node; therefore, **confidentiality must not rely solely on on-chain gating**. | ||
|
|
||
| If the envelope or payload must remain private, it **should** be protected off-chain via: | ||
| - encryption (e.g., encrypt payload/key per owner), and/or | ||
| - access control (signed challenges, bearer tokens, short-TTL URLs). | ||
|
|
||
| ### Integrity and server non-equivocation | ||
| Assuming collision-resistant hash functions, the on-chain commitment ensures the server cannot swap the underlying private payload after minting without being detected. Clients **must** verify `hash(payload_bytes) == commitment` after reveal. | ||
|
|
||
| ### Phishing / malicious URIs | ||
| Wallets should treat envelope URIs as untrusted input and apply standard URL safety practices (origin warnings, link previews sandboxing, and user confirmation flows where appropriate). | ||
|
wileyj marked this conversation as resolved.
|
||
|
|
||
| ### MIME type validation | ||
| The `mime` field is informational and does not instruct clients to execute content. Clients **should** validate MIME types before processing payloads and **should** reject executable types (e.g., `application/x-executable`, `application/x-msdownload`) by default. This follows standard content-security practices. | ||
|
|
||
| --- | ||
|
|
||
| ## Reference Implementation (Informative) | ||
|
|
||
| A reference contract can implement: | ||
| - a mapping `token-id -> commitment tuple` | ||
| - optionally a mapping `token-id -> envelope tuple` | ||
| - setters restricted to contract-defined roles (e.g., token owner or authorized operator) | ||
| - `print` events as specified above | ||
|
|
||
| --- | ||
|
|
||
| ## Related Work (Informative) | ||
|
|
||
| Commitments and commit–reveal schemes are widely used across blockchains to prevent equivocation. Some NFT ecosystems support “encrypted metadata” patterns, but these are often application-specific and lack a shared interface for wallets/indexers. | ||
|
|
||
| To the best of our knowledge, Stacks lacks a minimal, interoperable trait and event convention for commitment-anchored private metadata on SIP-009 NFTs; this SIP defines such a standard in a composable, non-consensus, and implementation-agnostic manner. | ||
|
|
||
| --- | ||
|
|
||
| ## Activation | ||
|
|
||
| No consensus changes are required. This SIP is considered | ||
| Active when all of the following conditions are met before | ||
| Bitcoin block height (H₀ + 12,960), where H₀ is the Bitcoin | ||
| block height at the time this SIP is merged, and 12,960 blocks | ||
| represents approximately 3 months: | ||
|
|
||
| 1) A reference trait contract implementing this specification is deployed on Stacks mainnet. | ||
|
|
||
| 2) At least one production NFT contract on mainnet implements the trait(s) exactly as specified in this SIP. | ||
|
|
||
| 3) At least one ecosystem integration — indexer, wallet, or marketplace — demonstrates consumption of the interface, | ||
| proving interoperability independent of the authors. | ||
|
|
||
| A trait that follows this specification will be made available | ||
| on mainnet upon status update to activation-in-progress, and its deployed address will be | ||
| recorded here. | ||
|
proofofgame marked this conversation as resolved.
|
||
|
|
||
| Until activation, the SIP remains Draft, and implementers may | ||
| ship experimental contracts under the proposed interface. | ||
|
|
||
| ## References | ||
|
|
||
| [1] [SIP-009 ](https://github.com/stacksgov/sips/blob/main/sips/sip-009/sip-009-nft-standard.md) | ||
|
|
||
| [2] [Forum discussion thread](https://forum.stacks.org/t/encrypted-nfts-on-stacks-a-standard-trait-for-commitment-based-private-layers-enft/18560) | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.