Skip to content

Commit 5c5d5d9

Browse files
committed
tapdb: add test to ensure pre commitment spend detection works
1 parent b2195f0 commit 5c5d5d9

File tree

1 file changed

+141
-16
lines changed

1 file changed

+141
-16
lines changed

tapdb/supply_commit_test.go

Lines changed: 141 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,10 @@ 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,
218+
mintTxid chainhash.Hash) (int64, wire.OutPoint) {
218219

219220
h.t.Helper()
220221

@@ -228,7 +229,16 @@ func (h *supplyCommitTestHarness) addTestMintAnchorUniCommitment(
228229
)
229230
require.NoError(h.t, err)
230231

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

233243
anchorCommitID, err := h.db.UpsertMintAnchorUniCommitment(
234244
h.ctx, sqlc.UpsertMintAnchorUniCommitmentParams{
@@ -237,11 +247,12 @@ func (h *supplyCommitTestHarness) addTestMintAnchorUniCommitment(
237247
TaprootInternalKeyID: internalKeyID,
238248
GroupKey: h.groupKeyBytes,
239249
SpentBy: spentBy,
250+
Outpoint: outpointBuf.Bytes(),
240251
},
241252
)
242253
require.NoError(h.t, err)
243254

244-
return anchorCommitID
255+
return anchorCommitID, outpoint
245256
}
246257

247258
// currentState fetches the current state of the state machine via FetchState.
@@ -687,7 +698,8 @@ func (h *supplyCommitTestHarness) confirmChainTx(txID int64, txidBytes,
687698
// list of updates applied, the generated keys, the commit TX, and the simulated
688699
// chain proof details for assertion purposes.
689700
func (h *supplyCommitTestHarness) performSingleTransition(
690-
updates []supplycommit.SupplyUpdateEvent) stateTransitionOutput {
701+
updates []supplycommit.SupplyUpdateEvent,
702+
preCommitOutpoints []wire.OutPoint) stateTransitionOutput {
691703

692704
h.t.Helper()
693705

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

@@ -1255,7 +1274,10 @@ func TestBindDanglingUpdatesToTransition(t *testing.T) {
12551274
// To create dangling updates, we first need a state machine and a
12561275
// finalized transition.
12571276
updates1 := []supplycommit.SupplyUpdateEvent{h.randMintEvent()}
1258-
stateTransition1 := h.performSingleTransition(updates1)
1277+
// Pass empty outpoints since this test doesn't need pre-commitments
1278+
stateTransition1 := h.performSingleTransition(
1279+
updates1, []wire.OutPoint{},
1280+
)
12591281
h.assertTransitionApplied(stateTransition1)
12601282

12611283
// Now, with the machine in DefaultState, we'll manually insert some
@@ -1640,6 +1662,47 @@ func TestSupplyCommitApplyStateTransition(t *testing.T) {
16401662

16411663
h := newSupplyCommitTestHarness(t)
16421664

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

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

16651785
// TestSupplyCommitUnspentPrecommits tests the UnspentPrecommits method.
@@ -1681,8 +1801,10 @@ func TestSupplyCommitUnspentPrecommits(t *testing.T) {
16811801
require.Empty(t, precommits)
16821802

16831803
// 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{})
1804+
batchKeyBytes, _, mintTx1, mintTxidBytes, _ := h.addTestMintingBatch()
1805+
var mintTxid chainhash.Hash
1806+
copy(mintTxid[:], mintTxidBytes)
1807+
_, _ = h.addTestMintAnchorUniCommitment(batchKeyBytes, sql.NullInt64{}, mintTxid)
16861808

16871809
// At this point, we should find a single pre commitment on disk.
16881810
precommitsRes = h.commitMachine.UnspentPrecommits(h.ctx, spec)
@@ -1694,12 +1816,15 @@ func TestSupplyCommitUnspentPrecommits(t *testing.T) {
16941816
// Next, we'll add another pre commitment, and this time associate it
16951817
// (spend it) by a supply commitment.
16961818
//nolint:lll
1697-
batchKeyBytes, commitTxDbID2, _, commitTxid2, commitRawTx2 :=
1698-
h.addTestMintingBatch()
1819+
batchKeyBytes, commitTxDbID2, _, commitTxidBytes2, commitRawTx2 := h.addTestMintingBatch()
1820+
var commitTxid2 chainhash.Hash
1821+
copy(commitTxid2[:], commitTxidBytes2)
16991822
commitID2 := h.addTestSupplyCommitment(
1700-
commitTxDbID2, commitTxid2, commitRawTx2, false,
1823+
commitTxDbID2, commitTxidBytes2, commitRawTx2, false,
1824+
)
1825+
_, _ = h.addTestMintAnchorUniCommitment(
1826+
batchKeyBytes, sqlInt64(commitID2), commitTxid2,
17011827
)
1702-
_ = h.addTestMintAnchorUniCommitment(batchKeyBytes, sqlInt64(commitID2))
17031828

17041829
// We should now find two pre-commitments.
17051830
precommitsRes = h.commitMachine.UnspentPrecommits(h.ctx, spec)
@@ -1713,7 +1838,7 @@ func TestSupplyCommitUnspentPrecommits(t *testing.T) {
17131838
blockHeight := sqlInt32(123)
17141839
txIndex := sqlInt32(1)
17151840
_, err = h.db.UpsertChainTx(h.ctx, sqlc.UpsertChainTxParams{
1716-
Txid: commitTxid2,
1841+
Txid: commitTxid2[:],
17171842
RawTx: commitRawTx2,
17181843
ChainFees: 0,
17191844
BlockHash: blockHash,

0 commit comments

Comments
 (0)