Skip to content

Commit 1db2104

Browse files
committed
tapdb: add supply_commit.md documentation
1 parent c9833cc commit 1db2104

File tree

1 file changed

+190
-0
lines changed

1 file changed

+190
-0
lines changed

tapdb/supply_commit.md

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
# Taproot Asset Supply Commitment Persistence
2+
3+
This document details the database schema and Go implementation (`tapdb/supply_commit.go`) that provides persistent storage for the Taproot Asset Supply Commitment State Machine (`universe/supplycommit`). This layer ensures the state machine's progress is durable across restarts and provides mechanisms for tracking historical and pending supply commitments.
4+
5+
## Purpose
6+
7+
The primary function of this persistence layer is to reliably store the state and associated data for the `universe/supplycommit` state machine. This state machine manages the process of creating and anchoring cryptographic commitments to the supply changes (mints, burns, ignores) of a specific asset group onto the Bitcoin blockchain.
8+
9+
This involves storing:
10+
11+
1. The current operational state of the state machine for each asset group.
12+
2. Details of past, successfully confirmed supply commitments.
13+
3. Information about pending state transitions, including the specific supply update events being processed.
14+
4. Details of the Bitcoin transactions used to anchor commitments.
15+
16+
## Schema Overview
17+
18+
Migration `000036_asset_commit.up.sql` introduces several tables to manage the state machine's lifecycle and data.
19+
20+
1. **`supply_commit_states`**: An enum-like table defining the possible states of the state machine (e.g., `DefaultState`, `UpdatesPendingState`, `CommitTxCreateState`).
21+
* `id` (INTEGER PK): Numeric ID for the state.
22+
* `state_name` (TEXT UNIQUE): Human-readable name of the state.
23+
24+
2. **`supply_commit_update_types`**: An enum-like table defining the types of supply updates (e.g., `mint`, `burn`, `ignore`).
25+
* `id` (INTEGER PK): Numeric ID for the update type.
26+
* `update_type_name` (TEXT UNIQUE): Human-readable name of the update type.
27+
28+
3. **`supply_commitments`**: Stores the details of a specific, potentially confirmed, supply commitment anchored on-chain.
29+
* `commit_id` (INTEGER PK): Unique identifier for this commitment instance.
30+
* `group_key` (BLOB): The tweaked group key identifying the asset group.
31+
* `chain_txn_id` (BIGINT FK -> `chain_txns.txn_id`): Reference to the Bitcoin transaction containing this commitment.
32+
* `output_index` (INTEGER): The output index within the `chain_txn_id` transaction.
33+
* `internal_key_id` (BIGINT FK -> `internal_keys.key_id`): The internal key used for the commitment output's Taproot derivation.
34+
* `output_key` (BLOB): The final tweaked Taproot output key.
35+
* `block_header` (BLOB): Header of the block confirming the commitment (NULL if unconfirmed).
36+
* `block_height` (INTEGER): Height of the confirming block (NULL if unconfirmed).
37+
* `merkle_proof` (BLOB): Merkle proof of the transaction's inclusion in the block (NULL if unconfirmed).
38+
* `supply_root_hash` (BLOB): The MS-SMT root hash of the supply tree at this commitment (NULL until finalized).
39+
* `supply_root_sum` (BIGINT): The MS-SMT root sum (total supply value) at this commitment (NULL until finalized).
40+
41+
4. **`supply_commit_state_machines`**: Tracks the current state for each asset group's state machine instance.
42+
* `group_key` (BLOB PK): The tweaked group key, uniquely identifying the state machine instance.
43+
* `current_state_id` (INTEGER FK -> `supply_commit_states.id`): The current operational state.
44+
* `latest_commitment_id` (BIGINT FK -> `supply_commitments.commit_id`): Reference to the most recently *finalized* and confirmed commitment (NULL if none).
45+
46+
5. **`supply_commit_transitions`**: Records an active attempt to transition the supply state. Acts like a Write-Ahead Log (WAL) entry for the state machine.
47+
* `transition_id` (INTEGER PK): Unique identifier for this transition attempt.
48+
* `state_machine_group_key` (BLOB FK -> `supply_commit_state_machines.group_key`): Links back to the state machine instance.
49+
* `old_commitment_id` (BIGINT FK -> `supply_commitments.commit_id`): The commitment being replaced (NULL for the first commitment).
50+
* `new_commitment_id` (BIGINT FK -> `supply_commitments.commit_id`): The new commitment being created by this transition (NULL initially).
51+
* `pending_commit_txn_id` (BIGINT FK -> `chain_txns.txn_id`): The Bitcoin transaction intended to confirm this transition (NULL until created/signed).
52+
* `finalized` (BOOLEAN): Indicates if the transition completed successfully and was applied. Defaults to `FALSE`.
53+
* `creation_time` (TIMESTAMP): When the transition was initiated.
54+
* `UNIQUE INDEX ... WHERE finalized = 0`: Crucially ensures only *one* non-finalized (pending) transition can exist per `state_machine_group_key` at any time.
55+
56+
6. **`supply_update_events`**: Stores the individual mint, burn, or ignore events associated with a *pending* transition.
57+
* `event_id` (INTEGER PK): Unique identifier for the event record.
58+
* `transition_id` (BIGINT FK -> `supply_commit_transitions.transition_id` ON DELETE CASCADE): Links to the parent transition. Cascade delete ensures events are cleaned up if a transition is aborted/deleted.
59+
* `update_type_id` (INTEGER FK -> `supply_commit_update_types.id`): Type of the update event.
60+
* `event_data` (BLOB): Serialized data specific to the event type (e.g., `NewMintEvent`, `NewBurnEvent`).
61+
62+
7. **`ALTER TABLE mint_anchor_uni_commitments`**: Adds a `spent_by` column (FK -> `supply_commitments.commit_id`) to track which supply commitment transaction spent a given minting pre-commitment output.
63+
64+
### Schema Relationships (Mermaid Diagram)
65+
66+
```mermaid
67+
erDiagram
68+
supply_commit_state_machines ||--o{ supply_commit_transitions : "has_pending"
69+
supply_commit_state_machines }|--|| supply_commit_states : "uses_state"
70+
supply_commit_state_machines }|--|| supply_commitments : "tracks_latest"
71+
supply_commit_transitions ||--o{ supply_update_events : "includes_events"
72+
supply_commit_transitions }|--|| supply_commitments : "replaces_old"
73+
supply_commit_transitions }|--|| supply_commitments : "creates_new"
74+
supply_commit_transitions }|--|| chain_txns : "uses_pending_tx"
75+
supply_update_events ||--|| supply_commit_update_types : "uses_type"
76+
supply_commitments ||--|| chain_txns : "included_in_tx"
77+
supply_commitments ||--|| internal_keys : "uses_internal_key"
78+
mint_anchor_uni_commitments }|--|| supply_commitments : "spent_by_commit"
79+
80+
supply_commit_states {
81+
INTEGER id PK
82+
TEXT state_name UK
83+
}
84+
85+
supply_commit_update_types {
86+
INTEGER id PK
87+
TEXT update_type_name UK
88+
}
89+
90+
supply_commitments {
91+
INTEGER commit_id PK
92+
BLOB group_key "Idx"
93+
BIGINT chain_txn_id FK
94+
INTEGER output_index
95+
BIGINT internal_key_id FK
96+
BLOB output_key
97+
BLOB supply_root_hash
98+
BIGINT supply_root_sum
99+
}
100+
101+
supply_commit_state_machines {
102+
BLOB group_key PK
103+
INTEGER current_state_id FK
104+
BIGINT latest_commitment_id FK
105+
}
106+
107+
supply_commit_transitions {
108+
INTEGER transition_id PK
109+
BLOB state_machine_group_key FK
110+
BIGINT old_commitment_id FK
111+
BIGINT new_commitment_id FK
112+
BIGINT pending_commit_txn_id FK
113+
BOOLEAN finalized "Default=0"
114+
TIMESTAMP creation_time
115+
}
116+
117+
supply_update_events {
118+
INTEGER event_id PK
119+
BIGINT transition_id FK
120+
INTEGER update_type_id FK
121+
BLOB event_data
122+
}
123+
124+
chain_txns {
125+
INTEGER txn_id PK
126+
BLOB txid UK
127+
BLOB raw_tx
128+
INTEGER block_height
129+
BLOB block_hash
130+
INTEGER tx_index
131+
}
132+
133+
internal_keys {
134+
INTEGER key_id PK
135+
BLOB raw_key UK
136+
}
137+
138+
mint_anchor_uni_commitments {
139+
INTEGER id PK
140+
BIGINT batch_id FK
141+
INTEGER tx_output_index
142+
BLOB taproot_internal_key
143+
BLOB group_key
144+
BIGINT spent_by FK
145+
}
146+
```
147+
148+
## State Machine Persistence Logic
149+
150+
The database tables work together to provide durable storage and a recovery mechanism for the supply commitment state machine.
151+
152+
* **Current State:** The `supply_commit_state_machines` table acts as the primary indicator of the current operational state (`current_state_id`) for a given asset group (`group_key`). It also points to the `latest_commitment_id` that has been successfully processed and confirmed.
153+
154+
* **Pending Transitions (WAL):** The `supply_commit_transitions` table is key to the persistence strategy. Due to the unique index `supply_commit_transitions_single_pending_idx` (on `state_machine_group_key` WHERE `finalized = 0`), only one *active* transition can exist per asset group. This record acts like a Write-Ahead Log (WAL) entry:
155+
* It captures the *intent* to move from `old_commitment_id` to `new_commitment_id`.
156+
* It aggregates all necessary information for the transition *before* it's fully finalized.
157+
* The `supply_update_events` associated with this transition (linked via `transition_id`) store the specific data (mints, burns, ignores) driving the change.
158+
* References to the `new_commitment_id` (in `supply_commitments`) and `pending_commit_txn_id` (in `chain_txns`) are added as the state machine progresses through transaction creation and signing.
159+
160+
* **Commitment Data:** The `supply_commitments` table stores the immutable details of *each* commitment attempt once it reaches the stage of having a potential on-chain transaction. Initially, confirmation details (`block_height`, `merkle_proof`, etc.) and the final SMT root (`supply_root_hash`, `supply_root_sum`) are NULL.
161+
162+
* **Lifecycle & Recovery:**
163+
1. **Initiation (`InsertPendingUpdate`):** When the first `SupplyUpdateEvent` arrives for an idle state machine, a new row is inserted into `supply_commit_transitions` (`finalized=0`), and the event is stored in `supply_update_events`. The state machine's state in `supply_commit_state_machines` is set to `UpdatesPendingState`. Subsequent events for this group add more rows to `supply_update_events` linked to the *same* pending transition.
164+
2. **Transaction Creation/Signing (`InsertSignedCommitTx`):** When the state machine creates and signs the commitment transaction, a new row is added to `chain_txns`, a new row is added to `supply_commitments` (with NULL confirmation/root details), and the *pending* `supply_commit_transitions` row is updated with `new_commitment_id` and `pending_commit_txn_id`. The state machine's state moves to `CommitBroadcastState`.
165+
3. **Confirmation & Finalization (`ApplyStateTransition`):** Once the transaction (`pending_commit_txn_id`) confirms:
166+
* The corresponding `supply_commitments` row (`new_commitment_id`) is updated with block details, merkle proof, and the final calculated `supply_root_hash` and `supply_root_sum` (derived by applying the `supply_update_events` to the SMTs via `applySupplyUpdatesInternal`).
167+
* The corresponding `chain_txns` row is updated with block hash/height/index.
168+
* The `supply_commit_transitions` row is marked `finalized = 1`.
169+
* The `supply_commit_state_machines` row is updated: `current_state_id` becomes `DefaultState`, and `latest_commitment_id` is set to the `new_commitment_id` of the just-finalized transition.
170+
4. **Restart:** Upon restart, the system queries `supply_commit_state_machines` for the current state. If it's not `DefaultState`, it queries `supply_commit_transitions` for the single pending (`finalized=0`) transition and reconstructs the `SupplyStateTransition` object (including fetching associated `supply_update_events` and commitment details) to resume operation from the correct point (e.g., re-broadcasting, waiting for confirmation, or finalizing).
171+
172+
## Implementation (`tapdb/SupplyCommitMachine`)
173+
174+
The `tapdb.SupplyCommitMachine` struct implements the `supplycommit.CommitmentTracker` and `supplycommit.StateMachineStore` interfaces, bridging the gap between the abstract state machine logic and the SQL database.
175+
176+
* **Core:** It embeds a `BatchedSupplyCommitStore`, which provides access to the necessary SQLc queries and transaction management (`ExecTx`).
177+
* **Interface Mapping:**
178+
* `UnspentPrecommits`: Queries `mint_anchor_uni_commitments` filtering by `group_key` and `spent_by IS NULL`.
179+
* `SupplyCommit`: Queries `supply_commit_state_machines` to get `latest_commitment_id` for the group, then queries `supply_commitments` using that ID.
180+
* `InsertPendingUpdate`: Manages the logic described in "Lifecycle & Recovery - Initiation", using `UpsertSupplyCommitStateMachine`, `QueryExistingPendingTransition`, `InsertSupplyCommitTransition`, `InsertSupplyUpdateEvent`.
181+
* `InsertSignedCommitTx`: Implements the logic from "Lifecycle & Recovery - Transaction Creation/Signing", using `QueryPendingSupplyCommitTransition`, `UpsertChainTx`, `UpsertInternalKey`, `InsertSupplyCommitment`, `UpdateSupplyCommitTransitionCommitment`, `UpsertSupplyCommitStateMachine`.
182+
* `CommitState`: Updates `current_state_id` in `supply_commit_state_machines` via `UpsertSupplyCommitStateMachine`.
183+
* `FetchState`: Reconstructs the current state and the pending `SupplyStateTransition` (if any) by querying `supply_commit_state_machines`, `supply_commit_transitions`, `supply_update_events`, `supply_commitments`, `chain_txns`, and `internal_keys`. It handles deserializing event data and reconstructing commitment objects.
184+
* `ApplyStateTransition`: Executes the finalization logic ("Lifecycle & Recovery - Confirmation & Finalization"). It calls `applySupplyUpdatesInternal` (from `supply_tree.go`) to persist SMT changes, then uses `UpdateSupplyCommitmentRoot`, `UpdateSupplyCommitmentChainDetails`, `UpsertChainTx`, `FinalizeSupplyCommitTransition`, and `UpsertSupplyCommitStateMachine` to update the database records accordingly.
185+
186+
## Atomicity and Recovery
187+
188+
All multi-step database modifications within `SupplyCommitMachine` methods are wrapped in database transactions using `db.ExecTx`. This ensures that operations like inserting a transition and its first event, or updating commitment details and finalizing the transition, are atomic.
189+
190+
The persistence of the current state in `supply_commit_state_machines` and the detailed logging of the single pending transition in `supply_commit_transitions` (acting as a WAL) allow the state machine to reliably recover and resume its operation after restarts, preventing duplicate commitments or loss of pending updates.

0 commit comments

Comments
 (0)