Blocktrails Profiles (v0.1)
Application-layer profiles defining state formats and validation rules.
Abstract
This document defines the Blocktrails profile system and the Monochrome profile framework for building client-validated state contracts on Bitcoin. It includes MRC20, an issuer-controlled fungible token profile.
1. Introduction
Blocktrails defines a minimal primitive for anchoring and ordering off-chain state using Bitcoin P2TR output-key commitments. It provides ordering and integrity but does not define application semantics.
Profiles extend Blocktrails with meaning. A profile specifies:
- State format
- Validation rules
- Operation semantics
Profiles do not modify Blocktrails Core verification.
1.1 Available Profiles
| Profile | Description | Tweak Source |
|---|---|---|
| Monochrome | State machine framework with JCS serialization | sha256(JCS(state)) |
| MRC20 | Issuer-controlled fungible tokens | sha256(JCS(state)) |
| Git-mark | Git commits anchored to Bitcoin | sha256(commit_hash) |
2. Profile Model
2.1 Identification
A profile is identified by a stable string:
profile_id := <namespace>.<name>.<version>
Examples:
mono.monochrome.v0.1
mono.mrc20.v0.1
2.2 Requirements
A profile MUST:
- Be compatible with Blocktrails Core verification
- Operate on a single linear state chain
- Use client-side validation only
- Use JCS for canonical serialization
A profile MUST NOT:
- Require tapscript or script-path spends
- Require on-chain rule enforcement
- Alter output-key commitment semantics
3. Canonical Serialization
Profiles use JCS (JSON Canonicalization Scheme, RFC 8785) for deterministic hashing.
3.1 Rules
- UTF-8 encoding
- No whitespace between tokens
- Object keys sorted lexicographically
- No duplicate keys
- Numbers as shortest decimal (no scientific notation)
3.2 State Hash and Scalar
Profiles define serialize(state) := jcs(state). The Blocktrails Core scalar() function is then:
scalar(state) := int(sha256(jcs(state)), big-endian) mod n
Serialization MUST be canonical and byte-stable: the same logical state MUST always produce identical bytes. JCS provides this guarantee.
The resulting tweak MUST be in the range [1, n-1]. If scalar(state) = 0, the state MUST be rejected (probability ~2-256).
Note on collisions: A hash collision is catastrophic by construction — it breaks commitment binding, not merely on-chain distinguishability. Two different states producing the same scalar() value would share an output key, creating semantic ambiguity: verifiers cannot determine which state was intended.
4. Common Encodings
4.1 Public Keys
<pubkey> := 32-byte x-only secp256k1 public key, hex-encoded (64 characters, lowercase)
Profiles use x-only pubkeys as account identifiers (e.g., token holders in MRC20). Issuer authority is separate — it derives from the Blocktrails spend signer (d_base holder), not from account pubkeys.
Nostr npub keys can be converted by bech32-decoding to the 32-byte x-only key; no parity adjustment is needed.
4.2 Hashes
<hex64> := 32-byte SHA-256 hash, hex-encoded (64 characters, lowercase)
4.3 Integers
Integers are JSON numbers with no fractional component. Values MUST NOT exceed 253-1 (JSON safe integer limit). Values above this limit are invalid.
Note: While fields are documented as <uint64> for clarity, the JSON-safe subset applies.
5. URN Vocabulary
Operations MUST use URN identifiers:
| URN | Operation |
|---|---|
| urn:mono:op:set | Set key-value |
| urn:mono:op:mint | Create units |
| urn:mono:op:transfer | Move units |
| urn:mono:op:burn | Destroy units |
| urn:mono:op:append | Append to log |
Short-form operation names (e.g., "op": "mint") are invalid. Implementations MUST reject states using non-URN operation identifiers.
5.1 URN Extensibility
The urn:mono: namespace is currently unregistered and defined by convention.
Normative constraint: Third parties MUST NOT define new operations under urn:mono:op: without governance by the Blocktrails/Monochrome specification maintainers. Namespace squatting creates ambiguity for verifiers.
Profile authors MAY define additional operations under their own namespace (e.g., urn:myorg:op:custom).
6. Monochrome Profile Framework (v0.1)
6.1 Purpose
Monochrome is a minimal profile framework for linear client-side state contracts. One active state at a time, no branching, no concealment, no concurrency.
6.2 Profile ID
mono.monochrome.v0.1
6.3 Contract Identity
contract_id := sha256("blocktrails.contract" || genesis_outpoint || P_base_xonly || profile_id)
The "blocktrails.contract" prefix provides domain separation to prevent cross-protocol identifier collisions.
Rationale: The parent spec explicitly omits domain separation for state commitments (simplicity; context is already bound to outpoint + base key). Contract identifiers are different: they are cross-protocol references used outside the commitment context, so domain separation is warranted here.
6.4 Base State Schema
{
"profile": <string>,
"seq": <uint64>,
"prev": <hex64>,
"ops": [ <operation> ]
}
| Field | Type | Description |
|---|---|---|
| profile | string | Profile identifier |
| seq | integer | Sequence number, starts at 0 |
| prev | hex64 | sha256(serialize(previous_state)), 64 zeros for genesis |
| ops | array | Operations applied in this transition |
The ops array contains only the operations applied in this state transition, not a cumulative log.
6.5 Base Validation
A transition is valid if:
- Blocktrails Core verification succeeds
seqincrements by exactly 1prevequalssha256(serialize(previous_state))- All operations use valid URN identifiers
- All operations are well-formed per profile schema
6.6 Authorization
The signer of the Blocktrails spend is the sole authority for state transitions. This is a single-writer model.
7. MRC20: Fungible Token Profile (v0.1)
7.1 Purpose
MRC20 is a minimal fungible token profile built on Monochrome.
7.2 Authority Model
MRC20 is an issuer-controlled ledger.
The holder of the Blocktrails base key (d_base) has full authority over all operations, including:
- Minting to any address
- Transferring from any address
- Burning from any address
This is analogous to a centralized ledger with cryptographic auditability. The issuer cannot forge history, but can authorize any valid state transition.
Implications:
- Users trust the issuer not to act maliciously
- All operations are publicly auditable
- The issuer cannot rewrite confirmed history
7.2.1 Trust Through Observed Auditability
MRC20 operates on an earned trust model. The issuer cannot:
- Forge historical states (hash-bound)
- Reorder past transitions (Bitcoin-anchored)
- Hide operations from verifiers (explicit state)
Trust is established through:
- Complete history visibility — Any party can replay and verify all transitions
- Immutable audit trail — Misbehavior is permanently recorded
- Reputation at stake — Observed honesty over time builds confidence
This differs from trustless systems (e.g., Bitcoin L1) where rules are enforced by consensus. In MRC20, rules are enforced by social accountability — the issuer can misbehave, but cannot hide misbehavior.
Practical implication: Users should verify issuer history before accepting tokens. Long-running contracts with clean histories are more trustworthy than new ones.
7.3 Profile ID
mono.mrc20.v0.1
7.4 State Schema
{
"profile": "mono.mrc20.v0.1",
"seq": <uint64>,
"prev": <hex64>,
"ops": [ <operation> ],
"balances": { "<pubkey>": <uint64> },
"supply": <uint64>,
"name": <string>,
"ticker": <string>,
"decimals": <uint8>
}
| Field | Type | Required | Mutable |
|---|---|---|---|
| profile | string | Yes | No |
| seq | integer | Yes | Yes |
| prev | hex64 | Yes | Yes |
| ops | array | Yes | Yes |
| balances | object | Yes | Yes |
| supply | integer | Yes | Yes |
| name | string | Yes | No |
| ticker | string | Yes | No |
| decimals | integer | Yes | No |
Integer limits: Per Section 4.3, all integer values are constrained to 253-1. For MRC20, this means maximum representable balance or supply per contract is 9,007,199,254,740,991 base units. With 8 decimals, this is ~90 million tokens.
7.5 State Computation Model
ops is authoritative. balances and supply are computed state.
Verifiers MUST:
- Start from genesis state (
balances: {},supply: 0) - Apply each operation in sequence
- Verify computed
balancesandsupplymatch the state exactly
Transition verification is local: it only requires the previous state S and the new state S'. Full-chain replay from genesis is also valid and produces equivalent results.
7.6 Operations
MINT
{ "op": "urn:mono:op:mint", "to": "<pubkey>", "amt": <uint64> }
TRANSFER
{ "op": "urn:mono:op:transfer", "from": "<pubkey>", "to": "<pubkey>", "amt": <uint64> }
BURN
{ "op": "urn:mono:op:burn", "from": "<pubkey>", "amt": <uint64> }
7.7 Validation Rules
A transition from S to S' is valid if:
S'.profile == "mono.mrc20.v0.1"S'.seq == S.seq + 1S'.prev == sha256(serialize(S))- Immutable fields unchanged:
name,ticker,decimals - All operations use URN identifiers
- For each operation, computed deltas are valid
S'.balancesequals computed balances after applying all opsS'.supplyequals computed supply after applying all ops- Blocktrails spend signed by issuer (
d_baseholder)
| Op | Precondition | Effect |
|---|---|---|
| MINT | — | balances[to] += amt; supply += amt |
| TRANSFER | balances[from] >= amt | balances[from] -= amt; balances[to] += amt |
| BURN | balances[from] >= amt | balances[from] -= amt; supply -= amt |
7.8 Genesis State
{
"profile": "mono.mrc20.v0.1",
"seq": 0,
"prev": "0000000000000000000000000000000000000000000000000000000000000000",
"ops": [],
"balances": {},
"supply": 0,
"name": "Example Token",
"ticker": "EXT",
"decimals": 8
}
8. Security Considerations
8.1 Inherited from Blocktrails Core
- Hash preimage resistance → state binding
- Discrete log hardness → output key security
- Bitcoin consensus → spend ordering
8.2 Profile-Specific
| Threat | Mitigation |
|---|---|
| Invalid state accepted | Client MUST validate all transitions |
| Balance manipulation | Recompute from ops, verify match |
| Issuer misbehavior | Publicly auditable; cannot rewrite history |
| State withholding | Redundant state distribution |
8.3 Trust Boundaries
9. Non-Goals
This specification does not provide:
- Confidentiality
- Zero-knowledge proofs
- DAG state graphs
- Script-enforced logic
- Multi-writer concurrency
- Self-sovereign account authorization
- Allowances/approvals
10. Relationship to RGB
Monochrome occupies similar design space to RGB with different trade-offs:
| Aspect | Monochrome/MRC20 | RGB |
|---|---|---|
| State model | Linear | DAG |
| Visibility | Explicit | Concealed |
| Complexity | Minimal | Expressive |
| Authority | Issuer-controlled | Self-sovereign |
| Validation | JSON + JCS | Strict types |
11. References
- RFC 8785: JSON Canonicalization Scheme
- Blocktrails Core Specification
- BIP-340: Schnorr Signatures
- BIP-341: Taproot
12. Summary
| Layer | Purpose |
|---|---|
| Blocktrails Core | Ordering and anchoring |
| Monochrome | Profile framework |
| MRC20 | Issuer-controlled fungible tokens |