Skip to content

Commit caf0b4f

Browse files
committed
tapgarden+tapdb: misc review fixes, plus lint
Address golangci-lint findings (lll, gofmt, whitespace, exhaustive, gosec, govet copylocks) and two reviewer comments: use uint for mintpublish's batchSize so the negative-value case is ruled out at the type boundary, and use the already-computed batchKey in the Cultivator.Cancel default arm instead of re-deriving it from the live batch. The copylocks fixes switch the augmenter staging-batch construction from `*batch` to `batch.Copy()` so the atomic state field isn't copied by value. The exhaustive cases get explicit default arms on the BatchState switches in checkSingletonInvariant and RunRepairTool. Rename Migration62BackfillSupplyUpdateEventKeys to Migration62BackfillEventKeys so the aligned map entry fits the 80-column limit. Make TestMigration62BackfillSupplyUpdateEventKeys and TestMigration62BackfillDedupesLegacyDuplicates portable to Postgres: the first one used the SQLite-only `?` placeholder, the second used MAX(bytea) which isn't defined in Postgres. Switch to sqlc's InsertSupplyUpdateEvent and split COUNT + key lookup into two queries. Regenerate sqlc bindings whose source-comment references the event_key column's migration number. Add a [repair] section to sample-tapd.conf with an example for repair.cancel-duplicate-batches; the sample-conf check requires every tapd flag to have a default or example in the file.
1 parent 3e2fe7c commit caf0b4f

14 files changed

Lines changed: 146 additions & 86 deletions

sample-tapd.conf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,16 @@
524524
; required precision
525525
; experimental.rfq.mockoraclesatsperasset=
526526

527+
[repair]
528+
529+
; One-shot recovery: when set, tapd cancels all but the most recent
530+
; minting batch in BatchStatePending or BatchStateFrozen and then
531+
; exits. Used to recover a legacy database that violates the
532+
; singleton pre-broadcast batch invariant added in migration 000060
533+
; (e.g. one with duplicate pending batches that blocks the
534+
; migration). Default value is false.
535+
; repair.cancel-duplicate-batches=false
536+
527537
[healthcheck]
528538

529539
; The number of times we should attempt to check for certificate expiration before

tapcfg/repair.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func RunRepairTool(cfg *Config, cfgLogger btclog.Logger) error {
4444
case DatabaseBackendPostgres:
4545
pgCfg := *cfg.Postgres
4646
pgCfg.SkipMigrations = true
47-
cfgLogger.Infof("repair: opening postgres database "+
47+
cfgLogger.Infof("repair: opening postgres database " +
4848
"(migrations skipped)")
4949
db, err = tapdb.NewPostgresStore(&pgCfg)
5050

@@ -77,6 +77,10 @@ func RunRepairTool(cfg *Config, cfgLogger btclog.Logger) error {
7777
tapgarden.BatchStateFrozen:
7878

7979
preBroadcast = append(preBroadcast, batch)
80+
81+
default:
82+
// Post-broadcast or terminal states are outside the
83+
// singleton constraint and have nothing to repair.
8084
}
8185
}
8286

tapdb/asset_minting_test.go

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,9 @@ func TestCommitMintingBatchSeedlings(t *testing.T) {
512512
t, assetStore, ctx, mintingBatch.Seedlings,
513513
)
514514
_, randSiblingHash := addRandSiblingToBatch(t, mintingBatch)
515-
err := assetStore.CommitMintingBatch(ctx, mintingBatch, tapgarden.MockBindDataForBatch(mintingBatch))
515+
err := assetStore.CommitMintingBatch(
516+
ctx, mintingBatch, tapgarden.MockBindDataForBatch(mintingBatch),
517+
)
516518
require.NoError(t, err)
517519

518520
batchKey := mintingBatch.BatchKey.PubKey
@@ -616,7 +618,9 @@ func TestCommitMintingBatchSeedlings(t *testing.T) {
616618
mintingBatch = tapgarden.RandMintingBatch(
617619
t, tapgarden.WithTotalSeedlings(numSeedlings),
618620
)
619-
err = assetStore.CommitMintingBatch(ctx, mintingBatch, tapgarden.MockBindDataForBatch(mintingBatch))
621+
err = assetStore.CommitMintingBatch(
622+
ctx, mintingBatch, tapgarden.MockBindDataForBatch(mintingBatch),
623+
)
620624
require.NoError(t, err)
621625
mintingBatches = noError1(t, assetStore.FetchNonFinalBatches, ctx)
622626
assertSeedlingBatchLen(t, mintingBatches, 1, numSeedlings)
@@ -657,7 +661,9 @@ func TestInsertFetchUniCommitBatch(t *testing.T) {
657661
require.True(t, seedling.DelegationKey.IsSome())
658662

659663
// Commit the minting batch to the database.
660-
err := assetStore.CommitMintingBatch(ctx, batch, tapgarden.MockBindDataForBatch(batch))
664+
err := assetStore.CommitMintingBatch(
665+
ctx, batch, tapgarden.MockBindDataForBatch(batch),
666+
)
661667
require.NoError(t, err)
662668

663669
// Fetch the same batch from the database.
@@ -878,7 +884,9 @@ func TestAddSproutsToBatch(t *testing.T) {
878884
}
879885

880886
// First, we'll create a new batch, then add some sample seedlings.
881-
require.NoError(t, assetStore.CommitMintingBatch(ctx, mintingBatch, tapgarden.MockBindDataForBatch(mintingBatch)))
887+
require.NoError(t, assetStore.CommitMintingBatch(
888+
ctx, mintingBatch, tapgarden.MockBindDataForBatch(mintingBatch),
889+
))
882890

883891
// Now that the batch is on disk, we'll map those seedlings to an
884892
// actual asset commitment, then insert them into the DB as sprouts.
@@ -970,7 +978,9 @@ func addRandAssets(t *testing.T, ctx context.Context,
970978
t, assetStore, ctx, mintingBatch.Seedlings,
971979
)
972980
randSibling, randSiblingHash := addRandSiblingToBatch(t, mintingBatch)
973-
require.NoError(t, assetStore.CommitMintingBatch(ctx, mintingBatch, tapgarden.MockBindDataForBatch(mintingBatch)))
981+
require.NoError(t, assetStore.CommitMintingBatch(
982+
ctx, mintingBatch, tapgarden.MockBindDataForBatch(mintingBatch),
983+
))
974984

975985
genesisPacket := mintingBatch.GenesisPacket
976986
assetRoot := seedlingsToAssetRoot(
@@ -1496,7 +1506,9 @@ func TestGroupAnchors(t *testing.T) {
14961506
t, assetStore, ctx, mintingBatch.Seedlings,
14971507
)
14981508
addMultiAssetGroupToBatch(mintingBatch.Seedlings)
1499-
err := assetStore.CommitMintingBatch(ctx, mintingBatch, tapgarden.MockBindDataForBatch(mintingBatch))
1509+
err := assetStore.CommitMintingBatch(
1510+
ctx, mintingBatch, tapgarden.MockBindDataForBatch(mintingBatch),
1511+
)
15001512
require.NoError(t, err)
15011513

15021514
batchKey := mintingBatch.BatchKey.PubKey
@@ -2018,7 +2030,9 @@ func TestUpsertMintSupplyPreCommit(t *testing.T) {
20182030
storeSeedlingGroupGenesis(t, ctx, assetStore, seedling)
20192031

20202032
// Commit batch.
2021-
require.NoError(t, assetStore.CommitMintingBatch(ctx, mintingBatch, tapgarden.MockBindDataForBatch(mintingBatch)))
2033+
require.NoError(t, assetStore.CommitMintingBatch(
2034+
ctx, mintingBatch, tapgarden.MockBindDataForBatch(mintingBatch),
2035+
))
20222036

20232037
// Retrieve the batch key of the batch we just inserted.
20242038
var batchKey []byte
@@ -2073,13 +2087,13 @@ func TestUpsertMintSupplyPreCommit(t *testing.T) {
20732087
internalKey2, _ := test.RandKeyDesc(t)
20742088

20752089
storeMintSupplyPreCommit(
2076-
t, *assetStore, batchKey, preCommitBind.OutputIndex, internalKey2,
2077-
groupPubKeyBytes, preCommitOutpoint,
2090+
t, *assetStore, batchKey, preCommitBind.OutputIndex,
2091+
internalKey2, groupPubKeyBytes, preCommitOutpoint,
20782092
)
20792093

20802094
assertMintSupplyPreCommit(
2081-
t, *assetStore, batchKey, preCommitBind.OutputIndex, internalKey2,
2082-
groupPubKeyBytes, preCommitOutpoint,
2095+
t, *assetStore, batchKey, preCommitBind.OutputIndex,
2096+
internalKey2, groupPubKeyBytes, preCommitOutpoint,
20832097
)
20842098

20852099
// Upsert-ing a new group key for the same pre-commit outpoint should
@@ -2088,13 +2102,13 @@ func TestUpsertMintSupplyPreCommit(t *testing.T) {
20882102
groupPubKey2Bytes := schnorr.SerializePubKey(groupPubKey2)
20892103

20902104
storeMintSupplyPreCommit(
2091-
t, *assetStore, batchKey, preCommitBind.OutputIndex, internalKey2,
2092-
groupPubKey2Bytes, preCommitOutpoint,
2105+
t, *assetStore, batchKey, preCommitBind.OutputIndex,
2106+
internalKey2, groupPubKey2Bytes, preCommitOutpoint,
20932107
)
20942108

20952109
assertMintSupplyPreCommit(
2096-
t, *assetStore, batchKey, preCommitBind.OutputIndex, internalKey2,
2097-
groupPubKey2Bytes, preCommitOutpoint,
2110+
t, *assetStore, batchKey, preCommitBind.OutputIndex,
2111+
internalKey2, groupPubKey2Bytes, preCommitOutpoint,
20982112
)
20992113
}
21002114

@@ -2110,7 +2124,9 @@ func TestUpdateBatchStateMemoryCoherence(t *testing.T) {
21102124
ctx := context.Background()
21112125

21122126
mintingBatch := tapgarden.RandMintingBatch(t)
2113-
require.NoError(t, assetStore.CommitMintingBatch(ctx, mintingBatch, tapgarden.MockBindDataForBatch(mintingBatch)))
2127+
require.NoError(t, assetStore.CommitMintingBatch(
2128+
ctx, mintingBatch, tapgarden.MockBindDataForBatch(mintingBatch),
2129+
))
21142130
require.Equal(
21152131
t, tapgarden.BatchStatePending, mintingBatch.State(),
21162132
)
@@ -2155,12 +2171,17 @@ func TestSingletonPreBroadcastBatchConstraint(t *testing.T) {
21552171

21562172
// A first Pending batch is fine.
21572173
first := tapgarden.RandMintingBatch(t)
2158-
require.NoError(t, assetStore.CommitMintingBatch(ctx, first, tapgarden.MockBindDataForBatch(first)))
2174+
require.NoError(t, assetStore.CommitMintingBatch(
2175+
ctx, first, tapgarden.MockBindDataForBatch(first),
2176+
))
21592177

21602178
// A second Pending batch must be rejected: two rows in
21612179
// BatchStatePending violate the partial unique index.
21622180
secondPending := tapgarden.RandMintingBatch(t)
2163-
err := assetStore.CommitMintingBatch(ctx, secondPending, tapgarden.MockBindDataForBatch(secondPending))
2181+
err := assetStore.CommitMintingBatch(
2182+
ctx, secondPending,
2183+
tapgarden.MockBindDataForBatch(secondPending),
2184+
)
21642185
require.Error(t, err)
21652186

21662187
// Move the first batch to Frozen; it is still in the
@@ -2171,7 +2192,10 @@ func TestSingletonPreBroadcastBatchConstraint(t *testing.T) {
21712192
))
21722193

21732194
pendingWhileFrozen := tapgarden.RandMintingBatch(t)
2174-
err = assetStore.CommitMintingBatch(ctx, pendingWhileFrozen, tapgarden.MockBindDataForBatch(pendingWhileFrozen))
2195+
err = assetStore.CommitMintingBatch(
2196+
ctx, pendingWhileFrozen,
2197+
tapgarden.MockBindDataForBatch(pendingWhileFrozen),
2198+
)
21752199
require.Error(t, err)
21762200

21772201
// Move the first batch out of the pre-broadcast set into
@@ -2182,7 +2206,9 @@ func TestSingletonPreBroadcastBatchConstraint(t *testing.T) {
21822206
))
21832207

21842208
third := tapgarden.RandMintingBatch(t)
2185-
require.NoError(t, assetStore.CommitMintingBatch(ctx, third, tapgarden.MockBindDataForBatch(third)))
2209+
require.NoError(t, assetStore.CommitMintingBatch(
2210+
ctx, third, tapgarden.MockBindDataForBatch(third),
2211+
))
21862212

21872213
// And finally: two batches both in Committed must be
21882214
// permitted -- the constraint targets only the pre-broadcast

tapdb/migrations_test.go

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,12 +1435,17 @@ func TestMigration62BackfillSupplyUpdateEventKeys(t *testing.T) {
14351435
}
14361436

14371437
for _, s := range seeds {
1438-
_, err := db.ExecContext(ctx, `
1439-
INSERT INTO supply_update_events (
1440-
group_key, transition_id,
1441-
update_type_id, event_data
1442-
) VALUES (?, NULL, ?, ?)
1443-
`, groupKey, s.typeID, s.data)
1438+
// EventKey is intentionally nil so the row mimics what a
1439+
// legacy database holds before migration 62's backfill.
1440+
_, err := db.InsertSupplyUpdateEvent(
1441+
ctx, sqlc.InsertSupplyUpdateEventParams{
1442+
GroupKey: groupKey,
1443+
TransitionID: sql.NullInt64{},
1444+
UpdateTypeID: s.typeID,
1445+
EventData: s.data,
1446+
EventKey: nil,
1447+
},
1448+
)
14441449
require.NoError(t, err)
14451450
}
14461451

@@ -1537,13 +1542,18 @@ func TestMigration62BackfillDedupesLegacyDuplicates(t *testing.T) {
15371542
require.NoError(t, err)
15381543

15391544
// Exactly one row should survive, and its event_key should
1540-
// match the hash of the duplicated content.
1545+
// match the hash of the duplicated content. MAX(bytea) is not
1546+
// defined in Postgres, so fetch count and key separately.
15411547
var postCount int
1542-
var survivingKey []byte
15431548
require.NoError(t, db.QueryRowContext(ctx, `
1544-
SELECT COUNT(*), MAX(event_key) FROM supply_update_events
1545-
`).Scan(&postCount, &survivingKey))
1549+
SELECT COUNT(*) FROM supply_update_events
1550+
`).Scan(&postCount))
15461551
require.Equal(t, 1, postCount)
1552+
1553+
var survivingKey []byte
1554+
require.NoError(t, db.QueryRowContext(ctx, `
1555+
SELECT event_key FROM supply_update_events LIMIT 1
1556+
`).Scan(&survivingKey))
15471557
require.Equal(t,
15481558
supplyUpdateEventKey(groupKey, 0, payload),
15491559
survivingKey,

tapdb/programmatic_migrations.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ const (
2828
// witnesses.
2929
Migration51InsertAssetBurns = 51
3030

31-
// Migration62BackfillSupplyUpdateEventKeys is the version of the
31+
// Migration62BackfillEventKeys is the version of the
3232
// programmatic migration that computes the dedup content-hash for
3333
// every supply_update_events row that pre-dates the event_key
3434
// column. SQLite has no native SHA-256, so the work cannot be
3535
// expressed as portable SQL.
36-
Migration62BackfillSupplyUpdateEventKeys = 62
36+
Migration62BackfillEventKeys = 62
3737
)
3838

3939
// programmaticMigration is a function type for a function that performs a
@@ -46,9 +46,9 @@ var (
4646
// These functions are used to perform additional checks on the
4747
// database state that are not fully expressible in SQL.
4848
programmaticMigrations = map[uint]programmaticMigration{
49-
Migration50ScriptKeyType: determineAndAssignScriptKeyType,
50-
Migration51InsertAssetBurns: insertAssetBurns,
51-
Migration62BackfillSupplyUpdateEventKeys: backfillSupplyUpdateEventKeys,
49+
Migration50ScriptKeyType: determineAndAssignScriptKeyType,
50+
Migration51InsertAssetBurns: insertAssetBurns,
51+
Migration62BackfillEventKeys: backfillSupplyUpdateEventKeys,
5252
}
5353
)
5454

tapdb/sqlc/querier.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tapdb/sqlc/supply_commit.sql.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tapdb/supply_commit_test.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,12 @@ func (h *supplyCommitTestHarness) addTestMintingBatch() ([]byte, int64,
157157
// planter state machine, only the supply-commit logic, so the
158158
// specific state does not matter as long as it is not
159159
// Pending or Frozen.
160-
err = db.UpdateMintingBatchState(ctx, sqlc.UpdateMintingBatchStateParams{
161-
RawKey: batchKeyBytes,
162-
BatchState: int16(tapgarden.BatchStateFinalized),
163-
})
160+
err = db.UpdateMintingBatchState(
161+
ctx, sqlc.UpdateMintingBatchStateParams{
162+
RawKey: batchKeyBytes,
163+
BatchState: int16(tapgarden.BatchStateFinalized),
164+
},
165+
)
164166
require.NoError(h.t, err)
165167

166168
_, err = db.BindMintingBatchWithTx(
@@ -1537,7 +1539,8 @@ func TestBindDanglingUpdatesToTransition(t *testing.T) {
15371539

15381540
eventData := b.Bytes()
15391541
eventKey := supplyUpdateEventKey(
1540-
h.groupKeyBytes, updateTypeID, eventData,
1542+
h.groupKeyBytes, updateTypeID,
1543+
eventData,
15411544
)
15421545

15431546
rows, err := db.InsertSupplyUpdateEvent(

tapgarden/batch_test.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,6 @@ func TestCheckSingletonInvariant(t *testing.T) {
300300

301301
t.Run("error names offending keys and repair tool",
302302
func(t *testing.T) {
303-
304303
a := mkBatch(BatchStatePending)
305304
b := mkBatch(BatchStateFrozen)
306305

@@ -514,15 +513,12 @@ func TestSeedlingValidateCommitSplit(t *testing.T) {
514513
AssetType: asset.Normal,
515514
Amount: 1,
516515
SupplyCommitments: supplyCommitments,
517-
DelegationKey: fn.None[
518-
keychain.KeyDescriptor,
519-
](),
516+
DelegationKey: fn.None[keychain.KeyDescriptor](),
520517
}
521518
}
522519

523520
t.Run("validate on populated batch leaves it unchanged",
524521
func(t *testing.T) {
525-
526522
batch := RandMintingBatch(
527523
t, WithTotalSeedlings(3),
528524
)
@@ -547,7 +543,6 @@ func TestSeedlingValidateCommitSplit(t *testing.T) {
547543

548544
t.Run("commit on empty batch adopts SupplyCommitments",
549545
func(t *testing.T) {
550-
551546
batch := &MintingBatch{}
552547

553548
candidate := mkCandidate("first-seedling", false)

0 commit comments

Comments
 (0)