Skip to content

Commit 92c96c2

Browse files
committed
tapdb: add test to ensure pre commitment spend detection works
1 parent 8af377b commit 92c96c2

File tree

1 file changed

+131
-15
lines changed

1 file changed

+131
-15
lines changed

tapdb/supply_commit_test.go

Lines changed: 131 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,9 @@ func newSupplyCommitTestHarness(t *testing.T) *supplyCommitTestHarness {
212212
}
213213

214214
// addTestMintAnchorUniCommitment inserts a mint_anchor_uni_commitments record
215-
// using harness data.
215+
// using harness data and returns both the commitment ID and the outpoint.
216216
func (h *supplyCommitTestHarness) addTestMintAnchorUniCommitment(
217-
batchKeyBytes []byte, spentBy sql.NullInt64) int64 {
217+
batchKeyBytes []byte, spentBy sql.NullInt64, mintTxid chainhash.Hash) (int64, wire.OutPoint) {
218218

219219
h.t.Helper()
220220

@@ -228,7 +228,16 @@ func (h *supplyCommitTestHarness) addTestMintAnchorUniCommitment(
228228
)
229229
require.NoError(h.t, err)
230230

231-
txOutputIndex := int32(test.RandInt[uint32]() % 100)
231+
txOutputIndex := int32(test.RandInt[uint32]())
232+
233+
outpoint := wire.OutPoint{
234+
Hash: mintTxid,
235+
Index: uint32(txOutputIndex),
236+
}
237+
238+
var outpointBuf bytes.Buffer
239+
err = wire.WriteOutPoint(&outpointBuf, 0, 0, &outpoint)
240+
require.NoError(h.t, err)
232241

233242
anchorCommitID, err := h.db.UpsertMintAnchorUniCommitment(
234243
h.ctx, sqlc.UpsertMintAnchorUniCommitmentParams{
@@ -237,11 +246,12 @@ func (h *supplyCommitTestHarness) addTestMintAnchorUniCommitment(
237246
TaprootInternalKeyID: internalKeyID,
238247
GroupKey: h.groupKeyBytes,
239248
SpentBy: spentBy,
249+
Outpoint: outpointBuf.Bytes(),
240250
},
241251
)
242252
require.NoError(h.t, err)
243253

244-
return anchorCommitID
254+
return anchorCommitID, outpoint
245255
}
246256

247257
// currentState fetches the current state of the state machine via FetchState.
@@ -687,7 +697,8 @@ func (h *supplyCommitTestHarness) confirmChainTx(txID int64, txidBytes,
687697
// list of updates applied, the generated keys, the commit TX, and the simulated
688698
// chain proof details for assertion purposes.
689699
func (h *supplyCommitTestHarness) performSingleTransition(
690-
updates []supplycommit.SupplyUpdateEvent) stateTransitionOutput {
700+
updates []supplycommit.SupplyUpdateEvent,
701+
preCommitOutpoints []wire.OutPoint) stateTransitionOutput {
691702

692703
h.t.Helper()
693704

@@ -709,6 +720,13 @@ func (h *supplyCommitTestHarness) performSingleTransition(
709720
// Next, we'll generate a new "fake" commitment transaction along with
710721
// sample internal and output keys.
711722
commitTx := randTx(h.t, 1)
723+
// Add inputs to the transaction that spend the pre-commitment outputs.
724+
for _, outpoint := range preCommitOutpoints {
725+
commitTx.TxIn = append(commitTx.TxIn, &wire.TxIn{
726+
PreviousOutPoint: outpoint,
727+
})
728+
}
729+
712730
internalKey, _ := test.RandKeyDesc(h.t)
713731
outputKey := test.RandPubKey(h.t)
714732

@@ -1255,7 +1273,8 @@ func TestBindDanglingUpdatesToTransition(t *testing.T) {
12551273
// To create dangling updates, we first need a state machine and a
12561274
// finalized transition.
12571275
updates1 := []supplycommit.SupplyUpdateEvent{h.randMintEvent()}
1258-
stateTransition1 := h.performSingleTransition(updates1)
1276+
// Pass empty outpoints since this test doesn't need pre-commitments
1277+
stateTransition1 := h.performSingleTransition(updates1, []wire.OutPoint{})
12591278
h.assertTransitionApplied(stateTransition1)
12601279

12611280
// Now, with the machine in DefaultState, we'll manually insert some
@@ -1640,6 +1659,46 @@ func TestSupplyCommitApplyStateTransition(t *testing.T) {
16401659

16411660
h := newSupplyCommitTestHarness(t)
16421661

1662+
// First, let's create some pre-commitments that should be spent
1663+
// when we apply the state transition. We'll create mint transactions
1664+
// and track their outpoints.
1665+
batchKeyBytes1, _, _, mintTxidBytes1, _ := h.addTestMintingBatch()
1666+
var mintTxid1 chainhash.Hash
1667+
copy(mintTxid1[:], mintTxidBytes1)
1668+
_, outpoint1 := h.addTestMintAnchorUniCommitment(
1669+
batchKeyBytes1, sql.NullInt64{}, mintTxid1,
1670+
)
1671+
batchKeyBytes2, _, _, mintTxidBytes2, _ := h.addTestMintingBatch()
1672+
var mintTxid2 chainhash.Hash
1673+
copy(mintTxid2[:], mintTxidBytes2)
1674+
_, outpoint2 := h.addTestMintAnchorUniCommitment(
1675+
batchKeyBytes2, sql.NullInt64{}, mintTxid2,
1676+
)
1677+
1678+
// Create an additional pre-commitment that should NOT be spent
1679+
// This tests that we're only marking the specific pre-commitments
1680+
// referenced in the transaction inputs as spent.
1681+
batchKeyBytesExtra, _, _, mintTxidBytesExtra, _ := h.addTestMintingBatch()
1682+
var mintTxidExtra chainhash.Hash
1683+
copy(mintTxidExtra[:], mintTxidBytesExtra)
1684+
_, outpointExtra := h.addTestMintAnchorUniCommitment(
1685+
batchKeyBytesExtra, sql.NullInt64{}, mintTxidExtra,
1686+
)
1687+
1688+
// Collect only the first two outpoints for the transaction inputs
1689+
// The extra one should remain unspent
1690+
preCommitOutpoints := []wire.OutPoint{outpoint1, outpoint2}
1691+
1692+
// Verify we have all three unspent pre-commitments before the
1693+
// transition.
1694+
precommitsRes := h.commitMachine.UnspentPrecommits(h.ctx, h.assetSpec)
1695+
precommits, err := precommitsRes.Unpack()
1696+
require.NoError(t, err)
1697+
require.Len(
1698+
t, precommits, 3, "should have 3 unspent pre-commitments "+
1699+
"before transition",
1700+
)
1701+
16431702
// To kick off our test, we'll perform a single state transition. This
16441703
// entails: adding a set of pending updates, committing the signed
16451704
// commit tx, and finally applying the state transition. After
@@ -1649,17 +1708,70 @@ func TestSupplyCommitApplyStateTransition(t *testing.T) {
16491708
updates1 := []supplycommit.SupplyUpdateEvent{
16501709
h.randMintEvent(), h.randBurnEvent(),
16511710
}
1652-
stateTransition1 := h.performSingleTransition(updates1)
1711+
stateTransition1 := h.performSingleTransition(updates1, preCommitOutpoints)
16531712
h.assertTransitionApplied(stateTransition1)
16541713

1714+
// After the first transition, only the two pre-commitments that were
1715+
// included in the transaction inputs should be marked as spent.
1716+
// The extra pre-commitment should remain unspent.
1717+
precommitsRes = h.commitMachine.UnspentPrecommits(h.ctx, h.assetSpec)
1718+
precommits, err = precommitsRes.Unpack()
1719+
require.NoError(t, err)
1720+
require.Len(
1721+
t, precommits, 1, "should have 1 unspent pre-commitment after "+
1722+
"first transition (the one not included in tx inputs)",
1723+
)
1724+
1725+
// Verify that the remaining unspent pre-commitment is the extra one
1726+
// by checking its outpoint
1727+
remainingPrecommit := precommits[0]
1728+
remainingOutpoint := wire.OutPoint{
1729+
Hash: remainingPrecommit.MintingTxn.TxHash(),
1730+
Index: remainingPrecommit.OutIdx,
1731+
}
1732+
1733+
require.Equal(
1734+
t, outpointExtra, remainingOutpoint,
1735+
"the remaining unspent pre-commitment should be the extra one",
1736+
)
1737+
1738+
// Now create new pre-commitments for the second transition.
1739+
batchKeyBytes3, _, _, mintTxidBytes3, _ := h.addTestMintingBatch()
1740+
var mintTxid3 chainhash.Hash
1741+
copy(mintTxid3[:], mintTxidBytes3)
1742+
_, outpoint3 := h.addTestMintAnchorUniCommitment(
1743+
batchKeyBytes3, sql.NullInt64{}, mintTxid3,
1744+
)
1745+
1746+
// Verify we have the extra one from before plus the new one.
1747+
precommitsRes = h.commitMachine.UnspentPrecommits(h.ctx, h.assetSpec)
1748+
precommits, err = precommitsRes.Unpack()
1749+
require.NoError(t, err)
1750+
require.Len(
1751+
t, precommits, 2, "should have 2 unspent pre-commitments "+
1752+
"before second transition (extra from first + new one)",
1753+
)
1754+
16551755
// To ensure that we can perform multiple transitions, we'll now do
16561756
// another one, with a new set of events, and then assert that it's been
1657-
// applied properly.
1757+
// applied properly. This time we'll spend both the extra pre-commitment
1758+
// from the first round and the new one.
16581759
updates2 := []supplycommit.SupplyUpdateEvent{
16591760
h.randMintEvent(), h.randIgnoreEvent(),
16601761
}
1661-
stateTransition2 := h.performSingleTransition(updates2)
1762+
preCommitOutpoints2 := []wire.OutPoint{outpointExtra, outpoint3}
1763+
stateTransition2 := h.performSingleTransition(updates2, preCommitOutpoints2)
16621764
h.assertTransitionApplied(stateTransition2)
1765+
1766+
// After the second transition, the new pre-commitment should also be spent.
1767+
// Finally, verify that no unspent pre-commitments remain.
1768+
precommitsRes = h.commitMachine.UnspentPrecommits(h.ctx, h.assetSpec)
1769+
precommits, err = precommitsRes.Unpack()
1770+
require.NoError(t, err)
1771+
require.Empty(
1772+
t, precommits, "should have no unspent pre-commitments after "+
1773+
"all transitions",
1774+
)
16631775
}
16641776

16651777
// TestSupplyCommitUnspentPrecommits tests the UnspentPrecommits method.
@@ -1681,8 +1793,10 @@ func TestSupplyCommitUnspentPrecommits(t *testing.T) {
16811793
require.Empty(t, precommits)
16821794

16831795
// Next, we'll add a new minting batch, and a pre-commit along with it.
1684-
batchKeyBytes, _, mintTx1, _, _ := h.addTestMintingBatch()
1685-
_ = h.addTestMintAnchorUniCommitment(batchKeyBytes, sql.NullInt64{})
1796+
batchKeyBytes, _, mintTx1, mintTxidBytes, _ := h.addTestMintingBatch()
1797+
var mintTxid chainhash.Hash
1798+
copy(mintTxid[:], mintTxidBytes)
1799+
_, _ = h.addTestMintAnchorUniCommitment(batchKeyBytes, sql.NullInt64{}, mintTxid)
16861800

16871801
// At this point, we should find a single pre commitment on disk.
16881802
precommitsRes = h.commitMachine.UnspentPrecommits(h.ctx, spec)
@@ -1694,12 +1808,14 @@ func TestSupplyCommitUnspentPrecommits(t *testing.T) {
16941808
// Next, we'll add another pre commitment, and this time associate it
16951809
// (spend it) by a supply commitment.
16961810
//nolint:lll
1697-
batchKeyBytes, commitTxDbID2, _, commitTxid2, commitRawTx2 :=
1811+
batchKeyBytes, commitTxDbID2, _, commitTxidBytes2, commitRawTx2 :=
16981812
h.addTestMintingBatch()
1813+
var commitTxid2 chainhash.Hash
1814+
copy(commitTxid2[:], commitTxidBytes2)
16991815
commitID2 := h.addTestSupplyCommitment(
1700-
commitTxDbID2, commitTxid2, commitRawTx2, false,
1816+
commitTxDbID2, commitTxidBytes2, commitRawTx2, false,
17011817
)
1702-
_ = h.addTestMintAnchorUniCommitment(batchKeyBytes, sqlInt64(commitID2))
1818+
_, _ = h.addTestMintAnchorUniCommitment(batchKeyBytes, sqlInt64(commitID2), commitTxid2)
17031819

17041820
// We should now find two pre-commitments.
17051821
precommitsRes = h.commitMachine.UnspentPrecommits(h.ctx, spec)
@@ -1713,7 +1829,7 @@ func TestSupplyCommitUnspentPrecommits(t *testing.T) {
17131829
blockHeight := sqlInt32(123)
17141830
txIndex := sqlInt32(1)
17151831
_, err = h.db.UpsertChainTx(h.ctx, sqlc.UpsertChainTxParams{
1716-
Txid: commitTxid2,
1832+
Txid: commitTxid2[:],
17171833
RawTx: commitRawTx2,
17181834
ChainFees: 0,
17191835
BlockHash: blockHash,

0 commit comments

Comments
 (0)