Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 36 additions & 3 deletions db/round_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,29 @@ type RoundStore interface {

MarkVTXOSpent(ctx context.Context, arg sqlc.MarkVTXOSpentParams) error

// VTXO lifecycle status queries.
ListLiveVTXOs(ctx context.Context) ([]VTXORow, error)

ListVTXOsByStatus(ctx context.Context, status int32) ([]VTXORow, error)

UpdateVTXOStatus(
ctx context.Context, arg sqlc.UpdateVTXOStatusParams,
) error

MarkVTXOForfeiting(
ctx context.Context, arg sqlc.MarkVTXOForfeitingParams,
) error

GetVTXOForfeitTx(
ctx context.Context, arg sqlc.GetVTXOForfeitTxParams,
) (sqlc.GetVTXOForfeitTxRow, error)

MarkVTXOForfeited(
ctx context.Context, arg sqlc.MarkVTXOForfeitedParams,
) error

DeleteVTXO(ctx context.Context, arg sqlc.DeleteVTXOParams) error

// Include BoardingStore methods for fetching boarding intent details.
GetBoardingIntent(
ctx context.Context, arg BoardingIntentKey,
Expand Down Expand Up @@ -1166,9 +1189,19 @@ func (s *RoundPersistenceStore) domainVTXOToInsertParams(
ClientPubkey: clientPubkey,
OperatorPubkey: operatorPubkey,
TreePath: treePathBytes,
Spent: false,
CreationTime: nowUnix,
LastUpdateTime: nowUnix,
// BatchExpiry, TreeDepth, CreatedHeight, and CommitmentTxid
// are not available in ClientVTXO. These are populated
// later when the VTXO manager creates full Descriptors from
// VTXOCreatedNotification. The InsertVTXO query uses ON
// CONFLICT DO UPDATE, so subsequent inserts with full
// metadata will update these fields.
BatchExpiry: 0,
TreeDepth: 0,
CreatedHeight: 0,
CommitmentTxid: []byte{},
Spent: false,
CreationTime: nowUnix,
LastUpdateTime: nowUnix,
}, nil
}

Expand Down
1 change: 1 addition & 0 deletions db/sqlc/migrations/000003_round_tables.down.sql
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
-- Round tables down migration.
-- Drops all tables created in the up migration in reverse order.

DROP INDEX IF EXISTS idx_vtxos_status;
DROP INDEX IF EXISTS idx_vtxos_creation_time;
DROP INDEX IF EXISTS idx_vtxos_spent;
DROP INDEX IF EXISTS idx_vtxos_round_id;
Expand Down
51 changes: 51 additions & 0 deletions db/sqlc/migrations/000003_round_tables.up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -240,9 +240,56 @@ CREATE TABLE IF NOT EXISTS vtxos (
-- tree_path is the TLV-encoded extracted tree.Tree path.
tree_path BLOB NOT NULL,

-- batch_expiry is the absolute block height at which the batch expires
-- (when the operator can sweep via the batch-level timelock). Zero value
-- is used for VTXOs created via the round store before the VTXO manager
-- fills in the full metadata via ON CONFLICT DO UPDATE.
batch_expiry INTEGER NOT NULL,

-- tree_depth is the depth of this VTXO in the VTXT (used for expiry
-- calculation based on TreeDepthMultiplier). Zero for same reason.
tree_depth INTEGER NOT NULL,

-- created_height is the block height when this VTXO was created.
-- Zero for same reason.
created_height INTEGER NOT NULL,

-- commitment_txid is the 32-byte txid of the commitment transaction that
-- anchors this VTXO's tree on-chain. Empty blob until the VTXO manager
-- fills in the full metadata via ON CONFLICT DO UPDATE.
commitment_txid BLOB NOT NULL,

-- spent indicates if this VTXO has been used.
spent BOOLEAN NOT NULL DEFAULT FALSE,

-- status tracks VTXO lifecycle (vtxo.VTXOStatus enum):
-- 0 = Live (default)
-- 1 = RefreshRequested
-- 2 = Forfeiting
-- 3 = Forfeited
Comment on lines +265 to +269

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add a forward migration for new vtxos columns

These new vtxos columns are introduced by editing migration 000003, but existing deployments that already ran 000003 will not re-run it, so their schema will still lack status/forfeit_* fields. All the new queries (e.g., ListLiveVTXOs, UpdateVTXOStatus) reference status and will start failing at runtime after upgrade. Please add a new forward migration (ALTER TABLE + backfill spent→status) instead of modifying a historical migration.

Useful? React with 👍 / 👎.

-- 4 = Spent
-- 5 = Expiring
-- 6 = Failed
status INTEGER NOT NULL DEFAULT 0,

-- forfeit_round_id is the round in which this VTXO is being forfeited.
-- NULL unless VTXO is in Forfeiting or Forfeited status.
forfeit_round_id TEXT,

-- forfeit_tx is the serialized wire.MsgTx (binary) of the forfeit tx.
-- Persisted when entering Forfeiting state for crash recovery.
forfeit_tx BLOB,

-- forfeit_txid is the 32-byte hash of the forfeit transaction.
-- Set when the forfeit is confirmed (transition to Forfeited state).
forfeit_txid BLOB,

-- replaced_by_hash is the outpoint hash of the replacement VTXO.
replaced_by_hash BLOB,

-- replaced_by_index is the outpoint index of the replacement VTXO.
replaced_by_index INTEGER,

-- creation_time is the unix epoch timestamp when this VTXO was created.
creation_time BIGINT NOT NULL,

Expand All @@ -265,3 +312,7 @@ CREATE INDEX IF NOT EXISTS idx_vtxos_spent
-- Index on creation_time for chronological queries.
CREATE INDEX IF NOT EXISTS idx_vtxos_creation_time
ON vtxos(creation_time DESC);

-- Index on status for efficient status-based queries (ListLiveVTXOs, etc.).
CREATE INDEX IF NOT EXISTS idx_vtxos_status
ON vtxos(status);
10 changes: 10 additions & 0 deletions db/sqlc/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 38 additions & 0 deletions db/sqlc/querier.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 18 additions & 4 deletions db/sqlc/queries/round.sql
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,25 @@ DELETE FROM client_tree_txids WHERE round_id = $1 AND client_key = $2;
-- VTXO queries.

-- name: InsertVTXO :exec
-- InsertVTXO creates or updates a VTXO. On conflict, metadata fields are
-- updated if the new values are non-zero/non-null (allowing the VTXO manager
-- to fill in BatchExpiry, TreeDepth, CreatedHeight, CommitmentTxid after the
-- round store creates the initial record).
INSERT INTO vtxos (
outpoint_hash, outpoint_index, round_id, amount, pk_script, expiry,
client_key_family, client_key_index, client_pubkey, operator_pubkey,
tree_path, spent, creation_time, last_update_time
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
ON CONFLICT (outpoint_hash, outpoint_index) DO NOTHING;
tree_path, batch_expiry, tree_depth, created_height, commitment_txid,
spent, creation_time, last_update_time
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15,
$16, $17, $18
)
ON CONFLICT (outpoint_hash, outpoint_index) DO UPDATE SET
batch_expiry = CASE WHEN excluded.batch_expiry != 0 THEN excluded.batch_expiry ELSE vtxos.batch_expiry END,
tree_depth = CASE WHEN excluded.tree_depth != 0 THEN excluded.tree_depth ELSE vtxos.tree_depth END,
created_height = CASE WHEN excluded.created_height != 0 THEN excluded.created_height ELSE vtxos.created_height END,
commitment_txid = CASE WHEN excluded.commitment_txid IS NOT NULL AND length(excluded.commitment_txid) > 0 THEN excluded.commitment_txid ELSE vtxos.commitment_txid END,
last_update_time = excluded.last_update_time;

-- name: GetVTXO :one
SELECT * FROM vtxos
Expand All @@ -136,7 +149,8 @@ SELECT * FROM vtxos WHERE spent = FALSE ORDER BY creation_time DESC;
SELECT * FROM vtxos WHERE round_id = $1 ORDER BY creation_time DESC;

-- name: MarkVTXOSpent :exec
UPDATE vtxos SET spent = TRUE, last_update_time = $3
-- Also sets status = 4 (Spent) to keep status in sync with spent flag.
UPDATE vtxos SET spent = TRUE, status = 4, last_update_time = $3
WHERE outpoint_hash = $1 AND outpoint_index = $2;

-- name: CountUnspentVTXOs :one
Expand Down
71 changes: 71 additions & 0 deletions db/sqlc/queries/vtxo.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
-- VTXO status and lifecycle queries.
-- These queries support the vtxo.VTXOStore interface for VTXO lifecycle
-- management, including status transitions and forfeit transaction tracking.

-- name: ListVTXOsByStatus :many
-- ListVTXOsByStatus returns all VTXOs with the specified status.
SELECT * FROM vtxos
WHERE status = $1
ORDER BY creation_time DESC;

-- name: ListLiveVTXOs :many
-- ListLiveVTXOs returns all VTXOs that are not in a terminal state.
-- Terminal states are: Forfeited (3), Spent (4), Expiring (5), Failed (6).
-- This is used during startup to recover active VTXO actors.
-- Also filter on spent = FALSE to handle VTXOs marked spent via the legacy
-- flag before the status field was introduced.
SELECT * FROM vtxos
WHERE status < 3 AND spent = FALSE
ORDER BY creation_time DESC;

-- name: UpdateVTXOStatus :exec
-- UpdateVTXOStatus atomically updates a VTXO's status. This is the primary
-- method for state transitions that don't require additional data.
UPDATE vtxos
SET status = $3, last_update_time = $4
WHERE outpoint_hash = $1 AND outpoint_index = $2;

-- name: MarkVTXOForfeiting :exec
-- MarkVTXOForfeiting transitions a VTXO to Forfeiting status and persists
-- the forfeit round ID and transaction for crash recovery. Called when
-- entering the forfeit flow.
UPDATE vtxos
SET status = 2, -- Forfeiting
forfeit_round_id = $3,
forfeit_tx = $4,
last_update_time = $5
WHERE outpoint_hash = $1 AND outpoint_index = $2;

-- name: GetVTXOForfeitTx :one
-- GetVTXOForfeitTx retrieves the persisted forfeit transaction for a VTXO.
-- Used during recovery to restore the ForfeitingState with its tx.
SELECT forfeit_tx, forfeit_round_id FROM vtxos
WHERE outpoint_hash = $1 AND outpoint_index = $2;

-- name: MarkVTXOForfeited :exec
-- MarkVTXOForfeited marks a VTXO as forfeited and records the forfeit
-- transaction ID and replacement VTXO outpoint. Called when the new round's
-- commitment transaction confirms.
UPDATE vtxos
SET status = 3, -- Forfeited
forfeit_txid = $3,
replaced_by_hash = $4,
replaced_by_index = $5,
last_update_time = $6
WHERE outpoint_hash = $1 AND outpoint_index = $2;

-- name: DeleteVTXO :exec
-- DeleteVTXO removes a VTXO from storage. Used for cleanup after terminal
-- states are reached and the VTXO is no longer needed.
DELETE FROM vtxos
WHERE outpoint_hash = $1 AND outpoint_index = $2;

-- name: GetVTXOReplacement :one
-- GetVTXOReplacement retrieves the replacement VTXO outpoint for a forfeited
-- VTXO. Returns NULL if not forfeited or no replacement recorded.
SELECT replaced_by_hash, replaced_by_index FROM vtxos
WHERE outpoint_hash = $1 AND outpoint_index = $2;

-- name: CountVTXOsByStatus :one
-- CountVTXOsByStatus returns the count of VTXOs with the specified status.
SELECT COUNT(*) FROM vtxos WHERE status = $1;
Loading
Loading