← Blocktrails Profiles

Git-mark Profile (v0.2)

Git commits anchored to Bitcoin via Blocktrails output-key commitments.

Abstract

Git-mark binds Bitcoin UTXOs to Git commits using BIP-341 TapTweak derivation. Each commit hash is combined with the current pubkey to derive a new pubkey, creating a cryptographic chain linking repository history to Bitcoin.

1. Introduction

Git-mark creates a two-way binding between Git and Bitcoin:

Git-mark uses Git commit hashes as the state data, combined with BIP-341 tagged hashes for pubkey-dependent derivation.

2. Cryptographic Model

2.1 Chained Key Derivation

Git-mark is Blocktrails Core with the state being the commit hash as text: it follows the chained derivation model with pubkey-dependent tweaks, on the full point at every step (never its even-y lift). Every TXO, the first one included, carries the commit it was tweaked by; the base key itself is never an output.

Genesis: P₀ = P_base + scalar(P_base, commit₀) × G (tweaked by genesis commit) Commit 1: P₁ = P₀ + scalar(P₀, commit₁) × G (tweaked by first commit) Commit 2: P₂ = P₁ + scalar(P₁, commit₂) × G (tweaked by second commit) ... Commit n: Pₙ = Pₙ₋₁ + scalar(Pₙ₋₁, commitₙ) × G

Note: Each tweak depends on the current pubkey, not just the commit hash. The same commit at different positions produces different tweaks.

2.2 Scalar Function (Core scalar, state = the commit as text)

Git-mark uses the Core scalar function with the state serialized as the commit hash's 40 lowercase hex characters, as UTF-8 text:

scalar(P, commit):
  h = sha256(utf8(commit))                        // the 40 hex characters as bytes, not the 20 raw bytes
  t = tagged_hash("TapTweak", x_only(P) || h)
  return int(t, big-endian) mod n

This is the rule the live trails follow (checked against the chain: every sampled mark of a 68-mark trail of 1 October 2026, through 35 odd-y intermediate points). An earlier draft of this page hashed the 20 raw commit bytes and had no commit at genesis; no trail was made that way.

Where:

If scalar(P, commit) = 0, the commit MUST be rejected (probability ~2-256).

2.3 Private Key Derivation

The spending key for any state, where each tweak tᵢ = scalar(Pᵢ₋₁, commitᵢ):

d_n = d₀ + t₀ + t₁ + ... + tₙ  (mod n)

where t₀ = scalar(P_base, commit₀), t₁ = scalar(P₀, commit₁), etc.

3. TXO URI Format

Git-mark uses TXO URIs to record the on-chain state:

txo:<network>:<txid>:<vout>?amount=<sats>&commit=<git_hash>[&pubkey=<hex>]
FieldTypeDescription
networkstringBitcoin network (mainnet, tbtc4, signet)
txidhex64Transaction ID (64 hex characters)
voutintegerOutput index
amountintegerValue in satoshis
pubkeyhex64X-only pubkey (64 hex characters), optional: a verifier recomputes it from the base key and the commits
commithex40Git commit hash: every TXO carries one, the genesis TXO too

3.1 Genesis TXO

The genesis TXO carries the first commit; its pubkey is the base key tweaked by it, x(P_base + scalar(P_base, commit₀) × G). The base key itself is never an output: it is published with the trail (pubkeyBase in blocktrails.json) as a full compressed point, 02/03 + x, so a verifier has nothing to guess.

txo:tbtc4:51d87101...420eeacf:0?amount=999700&commit=9adc596c...20f16d0b

3.2 Subsequent TXOs

Each subsequent TXO includes the commit that tweaked to this pubkey:

txo:tbtc4:45fbcbd7...81a9f7d:0?amount=999000&pubkey=b5bc388c...5266c&commit=cf97baba...83d17d

4. State Transitions

4.1 Transition Flow

┌─────────────┐ │ Git Commit │ │ (state Δ) │ └──────┬──────┘ │ hash ▼ ┌──────────┐ tweak ┌──────────┐ │ P_old │────────────→│ P_new │ │ (UTXO₁) │ │ (UTXO₂) │ └──────────┘ └──────────┘ │ │ │ spend │ new output ▼ ▼ ┌────────────────────────────────────┐ │ Bitcoin Transaction │ └────────────────────────────────────┘

4.2 Making a Transition

  1. Create git commit with state changes
  2. Compute new pubkey: P_new = P_current + scalar(P_current, commit) × G
  3. Spend current UTXO to new pubkey
  4. Append new TXO URI to txo.json
  5. Push to git remote

5. Directory Structure

Git-mark state is stored in the repository:

.well-known/
└── txo/
    └── txo.json      # Array of TXO URIs
blocktrails.json      # the trail: the base key, the states, the TXO URIs (what a verifier reads)

5.1 txo.json Format

Simple JSON array of TXO URI strings:

[
  "txo:tbtc4:<txid>:0?amount=1000000&commit=<genesis_commit>",
  "txo:tbtc4:<txid>:0?amount=999000&commit=<hash>",
  "txo:tbtc4:<txid>:0?amount=998000&commit=<hash>"
]

5.2 blocktrails.json

The trail as a verifier reads it, served beside the repository (the same shape the live trails use):

{
  "@type": "Blocktrail",
  "version": "0.0.3",
  "profile": "gitmark",
  "pubkeyBase": "02…",          // the base key as a full compressed point (02/03 + x): nothing to guess
  "chain": "tbtc4",
  "states": ["<genesis_commit>", "<hash>", "<hash>"],   // one per TXO, in order
  "txo": ["txo:tbtc4:…", "txo:tbtc4:…", "txo:tbtc4:…"]
}

pubkeyBase is where a verifier obtains the base key; txo.json alone does not carry it.

6. Verification

6.1 Chain Validity

A git-mark chain is valid if:

  1. Genesis: First TXO pubkey = P_base + scalar(P_base, genesis_commit) × G
  2. Continuity: Each subsequent pubkey = prev_pubkey + scalar(prev_pubkey, commit) × G
  3. On-chain: Each UTXO exists (or existed) on Bitcoin
  4. Git validity: Commit hashes correspond to actual git commits

6.2 Verification Algorithm

function verify(pubkeyBase, txoUris):
  txos = parse(txoUris)

  // the base key is published with the trail as a full point: nothing to guess
  P = pubkeyBase                       // 02/03 || x

  // every link, not the head alone: the head commits only to the sum of the tweaks
  for i = 0 to txos.length - 1:
    commit = txos[i].commit
    if commit == null: return { valid: false, error: "a TXO without a commit" }
    t = scalar(P, commit)              // depends on the current point, kept as it is
    P = P + t × G
    if xonly(P) != witness_program(txos[i]):   // the output on-chain (or the pubkey the URI records)
      return { valid: false, error: "pubkey mismatch at " + i }

  return { valid: true }

6.3 Commit Selection and Ordering

Commits anchored to the TXO chain need not be consecutive in git history. Any commit may be selected for marking. However:

The EC_ADD verification ensures pubkey chain integrity. Git ancestry verification is an additional check that confirms the logical ordering of state transitions.

7. Comparison with Monochrome

AspectGit-markMonochrome
Tweak derivationBIP-341 TapTweak with commit hashBIP-341 TapTweak with sha256(JCS(state))
State storageGit tree objectsJSON state array
State formatAny git contentStructured JSON with ops
LinkingTXO URI in txo.jsonState contains prev hash
Use caseRepository anchoringApplication state machines

Both profiles use BIP-341 TapTweak for pubkey-dependent derivation. Git-mark leverages Git's existing content-addressed storage and commit graph. Monochrome defines its own state schema with explicit operations.

8. Security Considerations

8.1 Inherited from Blocktrails

8.2 Git-mark Specific

ThreatMitigation
Commit rewritingGit's content-addressing; verifiers check commit exists
History divergenceTXO chain anchors specific commit sequence
UTXO availabilityBitcoin full node or SPV verification
txo.json tamperingVerify pubkey derivation matches recorded commits

8.3 Trust Model

9. Implementation

Reference implementation: blocktrails/git-mark

npm install git-seal

git mark init
git mark genesis --txid <txid> --vout 0 --amount 1000000
git commit -m "state change"
git mark advance --txid <txid> --vout 0 --amount 999000
git mark verify

10. References