Skip to content

Commit 0656c4f

Browse files
gijswijsguggero
authored andcommitted
multi: produce TransitionV1 proofs in unit tests
This commit adds v1 inclusion proof verification.
1 parent e341586 commit 0656c4f

File tree

3 files changed

+148
-22
lines changed

3 files changed

+148
-22
lines changed

proof/append_test.go

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func TestAppendTransition(t *testing.T) {
7171
withBip86Change: true,
7272
},
7373
{
74-
name: "normal with change",
74+
name: "normal with change (with split)",
7575
assetType: asset.Normal,
7676
amt: 100,
7777
withSplit: true,
@@ -135,6 +135,14 @@ func runAppendTransitionTest(t *testing.T, assetType asset.Type, amt uint64,
135135

136136
// Add some alt leaves to the commitment anchoring the asset transfer.
137137
altLeaves := asset.ToAltLeaves(asset.RandAltLeaves(t, true))
138+
139+
// Commit to the stxo of the previous asset. Otherwise, the inclusion
140+
// proofs will fail.
141+
stxoAsset, err := asset.MakeSpentAsset(newAsset.PrevWitnesses[0])
142+
require.NoError(t, err)
143+
144+
stxoLeaf := asset.ToAltLeaves([]*asset.Asset{stxoAsset})
145+
altLeaves = append(altLeaves, stxoLeaf...)
138146
err = tapCommitment.MergeAltLeaves(altLeaves)
139147
require.NoError(t, err)
140148

@@ -213,6 +221,7 @@ func runAppendTransitionTest(t *testing.T, assetType asset.Type, amt uint64,
213221
// Append the new transition to the genesis blob.
214222
transitionBlob, transitionProof, err := AppendTransition(
215223
genesisBlob, transitionParams, MockVerifierCtx,
224+
WithVersion(TransitionV1),
216225
)
217226
require.NoError(t, err)
218227
require.Greater(t, len(transitionBlob), len(genesisBlob))
@@ -293,8 +302,17 @@ func runAppendTransitionTest(t *testing.T, assetType asset.Type, amt uint64,
293302
nil, split1Commitment,
294303
)
295304
require.NoError(t, err)
305+
306+
// Commit to the stxo of the previous asset. Otherwise, the inclusion
307+
// proofs will fail. With splits this is only needed for the root asset.
308+
stxoAsset1, err := asset.MakeSpentAsset(split1Asset.PrevWitnesses[0])
309+
require.NoError(t, err)
310+
311+
stxoLeaf1 := asset.ToAltLeaves([]*asset.Asset{stxoAsset1})
312+
split1AltLeaves = append(split1AltLeaves, stxoLeaf1...)
296313
err = tap1Commitment.MergeAltLeaves(split1AltLeaves)
297314
require.NoError(t, err)
315+
298316
tap2Commitment, err := commitment.NewTapCommitment(
299317
nil, split2Commitment,
300318
)
@@ -361,12 +379,34 @@ func runAppendTransitionTest(t *testing.T, assetType asset.Type, amt uint64,
361379
split1Asset.AssetCommitmentKey(),
362380
)
363381
require.NoError(t, err)
382+
383+
// For the transfer root we also need to the stxo exclusion proofs.
384+
_, stxo1In2exclusionProof, err := tap2Commitment.Proof(
385+
stxoAsset1.TapCommitmentKey(),
386+
stxoAsset1.AssetCommitmentKey(),
387+
)
388+
require.NoError(t, err)
389+
390+
stxoID := asset.ToSerialized(stxoAsset1.ScriptKey.PubKey)
391+
stxo1In2Proofs := make(map[asset.SerializedKey]commitment.Proof, 1)
392+
stxo1In2Proofs[stxoID] = *stxo1In2exclusionProof
393+
364394
_, split1In3ExclusionProof, err := tap3Commitment.Proof(
365395
split1Asset.TapCommitmentKey(),
366396
split1Asset.AssetCommitmentKey(),
367397
)
368398
require.NoError(t, err)
369399

400+
// For the transfer root we also need to the stxo exclusion proofs.
401+
_, stxo1In3exclusionProof, err := tap3Commitment.Proof(
402+
stxoAsset1.TapCommitmentKey(),
403+
stxoAsset1.AssetCommitmentKey(),
404+
)
405+
require.NoError(t, err)
406+
407+
stxo1In3Proofs := make(map[asset.SerializedKey]commitment.Proof, 1)
408+
stxo1In3Proofs[stxoID] = *stxo1In3exclusionProof
409+
370410
_, split2In1ExclusionProof, err := tap1Commitment.Proof(
371411
split2Asset.TapCommitmentKey(),
372412
split2Asset.AssetCommitmentKey(),
@@ -406,13 +446,15 @@ func runAppendTransitionTest(t *testing.T, assetType asset.Type, amt uint64,
406446
OutputIndex: 1,
407447
InternalKey: internalKey2,
408448
CommitmentProof: &CommitmentProof{
409-
Proof: *split1In2ExclusionProof,
449+
Proof: *split1In2ExclusionProof,
450+
STXOProofs: stxo1In2Proofs,
410451
},
411452
}, {
412453
OutputIndex: 2,
413454
InternalKey: internalKey3,
414455
CommitmentProof: &CommitmentProof{
415-
Proof: *split1In3ExclusionProof,
456+
Proof: *split1In3ExclusionProof,
457+
STXOProofs: stxo1In3Proofs,
416458
},
417459
}},
418460
},
@@ -421,6 +463,7 @@ func runAppendTransitionTest(t *testing.T, assetType asset.Type, amt uint64,
421463

422464
split1Blob, split1Proof, err := AppendTransition(
423465
transitionBlob, split1Params, MockVerifierCtx,
466+
WithVersion(TransitionV1),
424467
)
425468
require.NoError(t, err)
426469
require.Greater(t, len(split1Blob), len(transitionBlob))
@@ -463,6 +506,7 @@ func runAppendTransitionTest(t *testing.T, assetType asset.Type, amt uint64,
463506

464507
split2Blob, split2Proof, err := AppendTransition(
465508
transitionBlob, split2Params, MockVerifierCtx,
509+
WithVersion(TransitionV1),
466510
)
467511
require.NoError(t, err)
468512
require.Greater(t, len(split2Blob), len(transitionBlob))
@@ -506,6 +550,7 @@ func runAppendTransitionTest(t *testing.T, assetType asset.Type, amt uint64,
506550

507551
split3Blob, split3Proof, err := AppendTransition(
508552
transitionBlob, split3Params, MockVerifierCtx,
553+
WithVersion(TransitionV1),
509554
)
510555
require.NoError(t, err)
511556
require.Greater(t, len(split3Blob), len(transitionBlob))

tapsend/proof_test.go

Lines changed: 77 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,30 @@ var (
2727
// TestCreateProofSuffix tests the creation of suffix proofs for a given anchor
2828
// transaction.
2929
func TestCreateProofSuffix(t *testing.T) {
30+
testCases := []struct {
31+
name string
32+
stxoProof bool
33+
expectedErr string
34+
}{
35+
{
36+
name: "Correct inclusion and exclusion proofs",
37+
stxoProof: true,
38+
},
39+
{
40+
name: "No stxo proof",
41+
stxoProof: false,
42+
expectedErr: "error verifying STXO proof: missing " +
43+
"asset proof",
44+
},
45+
}
46+
for _, tc := range testCases {
47+
t.Run(tc.name, func(tt *testing.T) {
48+
createProofSuffix(t, tc.stxoProof, tc.expectedErr)
49+
})
50+
}
51+
}
52+
53+
func createProofSuffix(t *testing.T, stxoProof bool, expectedErr string) {
3054
testAssets := []*asset.Asset{
3155
asset.RandAsset(t, asset.RandAssetType(t)),
3256
asset.RandAsset(t, asset.RandAssetType(t)),
@@ -83,7 +107,10 @@ func TestCreateProofSuffix(t *testing.T) {
83107
}
84108
outputCommitments := make(map[uint32]*commitment.TapCommitment)
85109

86-
addOutputCommitment(t, anchorTx, outputCommitments, testPackets...)
110+
addOutputCommitment(
111+
t, anchorTx, outputCommitments, stxoProof,
112+
testPackets...,
113+
)
87114
addBip86Output(t, anchorTx.FundedPsbt.Pkt)
88115

89116
// Create a proof suffix for all 4 packets now and validate it.
@@ -92,6 +119,7 @@ func TestCreateProofSuffix(t *testing.T) {
92119
proofSuffix, err := CreateProofSuffix(
93120
pkt.UnsignedTx, pkt.Outputs, vPkt,
94121
outputCommitments, outIdx, testPackets,
122+
proof.WithVersion(proof.TransitionV1),
95123
)
96124
require.NoError(t, err)
97125

@@ -106,18 +134,34 @@ func TestCreateProofSuffix(t *testing.T) {
106134
)
107135

108136
// Checking the transfer witness is the very last step
109-
// of the proof verification. Since we didn't properly
137+
// of the proof verification. Since we don't properly
110138
// sign the transfer, we expect the witness to be
111139
// invalid. But if we get to that point, we know that
112-
// all inclusion and exclusion proofs are correct (which
113-
// is what this test is testing).
140+
// all inclusion and exclusion proofs are correct. So
141+
// for successful cases we still expect an error, namely
142+
// the invalid witness error.
143+
errCode := txscript.ErrTaprootSigInvalid
114144
invalidWitnessErr := vm.Error{
115145
Kind: vm.ErrInvalidTransferWitness,
116146
Inner: txscript.Error{
117-
ErrorCode: txscript.ErrTaprootSigInvalid,
147+
ErrorCode: errCode,
118148
},
119149
}
120-
require.ErrorIs(t, err, invalidWitnessErr)
150+
if expectedErr == "" {
151+
require.ErrorIs(t, err, invalidWitnessErr)
152+
153+
continue
154+
}
155+
156+
if vPkt.Outputs[outIdx].Asset.IsTransferRoot() {
157+
require.ErrorContains(
158+
t, err, expectedErr,
159+
)
160+
} else {
161+
require.ErrorIs(
162+
t, err, invalidWitnessErr,
163+
)
164+
}
121165
}
122166
}
123167
}
@@ -206,7 +250,7 @@ func createPacket(t *testing.T, a *asset.Asset, split bool,
206250

207251
func addOutputCommitment(t *testing.T, anchorTx *AnchorTransaction,
208252
outputCommitments map[uint32]*commitment.TapCommitment,
209-
vPackets ...*tappsbt.VPacket) {
253+
stxoProof bool, vPackets ...*tappsbt.VPacket) {
210254

211255
packet := anchorTx.FundedPsbt.Pkt
212256

@@ -222,27 +266,42 @@ func addOutputCommitment(t *testing.T, anchorTx *AnchorTransaction,
222266
}
223267
}
224268

225-
for idx, assets := range assetsByOutput {
226-
for idx := range assets {
227-
if !assets[idx].HasSplitCommitmentWitness() {
269+
stxoAssetsByOutput := make(map[uint32][]asset.AltLeaf[asset.Asset])
270+
for idx1, assets := range assetsByOutput {
271+
for idx2 := range assets {
272+
if assets[idx2].IsTransferRoot() {
273+
stxoAssets, err := asset.CollectSTXO(
274+
assets[idx2],
275+
)
276+
stxoAssetsByOutput[idx1] = append(
277+
stxoAssetsByOutput[idx1], stxoAssets...,
278+
)
279+
require.NoError(t, err)
280+
}
281+
282+
if !assets[idx2].HasSplitCommitmentWitness() {
228283
continue
229284
}
230-
assets[idx] = assets[idx].Copy()
231-
assets[idx].PrevWitnesses[0].SplitCommitment = nil
285+
286+
assets[idx2] = assets[idx2].Copy()
287+
assets[idx2].PrevWitnesses[0].SplitCommitment = nil
232288
}
233289

234290
c, err := commitment.FromAssets(nil, assets...)
235291
require.NoError(t, err)
292+
if stxoProof {
293+
err = c.MergeAltLeaves(stxoAssetsByOutput[idx1])
294+
require.NoError(t, err)
295+
}
236296

237-
internalKey := keyByOutput[idx]
297+
internalKey := keyByOutput[idx1]
238298
script, err := tapscript.PayToAddrScript(*internalKey, nil, *c)
239299
require.NoError(t, err)
240300

241-
packet.UnsignedTx.TxOut[idx].PkScript = script
242-
packet.Outputs[idx].TaprootInternalKey = schnorr.SerializePubKey(
243-
internalKey,
244-
)
245-
outputCommitments[idx] = c
301+
packet.UnsignedTx.TxOut[idx1].PkScript = script
302+
packet.Outputs[idx1].TaprootInternalKey =
303+
schnorr.SerializePubKey(internalKey)
304+
outputCommitments[idx1] = c
246305
}
247306
}
248307

tapsend/send_test.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1697,6 +1697,23 @@ func createProofParams(t *testing.T, genesisTxIn wire.TxIn, state spendData,
16971697
senderAsset.AssetCommitmentKey(),
16981698
)
16991699
require.NoError(t, err)
1700+
1701+
// The sender gets the transfer root, so we also need to the stxo
1702+
// exclusion proofs.
1703+
senderSTXOAsset, err := asset.MakeSpentAsset(
1704+
senderAsset.PrevWitnesses[0],
1705+
)
1706+
require.NoError(t, err)
1707+
_, senderSTXOExclusionProof, err := receiverTapTree.Proof(
1708+
senderSTXOAsset.TapCommitmentKey(),
1709+
senderSTXOAsset.AssetCommitmentKey(),
1710+
)
1711+
require.NoError(t, err)
1712+
1713+
senderSTXOID := asset.ToSerialized(senderSTXOAsset.ScriptKey.PubKey)
1714+
senderSTXOProofs := make(map[asset.SerializedKey]commitment.Proof, 1)
1715+
senderSTXOProofs[senderSTXOID] = *senderSTXOExclusionProof
1716+
17001717
_, receiverExclusionProof, err := senderTapTree.Proof(
17011718
receiverAsset.TapCommitmentKey(),
17021719
receiverAsset.AssetCommitmentKey(),
@@ -1719,7 +1736,8 @@ func createProofParams(t *testing.T, genesisTxIn wire.TxIn, state spendData,
17191736
OutputIndex: 1,
17201737
InternalKey: &state.receiverPubKey,
17211738
CommitmentProof: &proof.CommitmentProof{
1722-
Proof: *senderExclusionProof,
1739+
Proof: *senderExclusionProof,
1740+
STXOProofs: senderSTXOProofs,
17231741
},
17241742
}},
17251743
},
@@ -1803,6 +1821,7 @@ func TestProofVerify(t *testing.T) {
18031821
// Create a proof for each receiver and verify it.
18041822
senderBlob, _, err := proof.AppendTransition(
18051823
genesisProofBlob, &proofParams[0], proof.MockVerifierCtx,
1824+
proof.WithVersion(proof.TransitionV1),
18061825
)
18071826
require.NoError(t, err)
18081827
senderFile := proof.NewEmptyFile(proof.V0)
@@ -1814,6 +1833,7 @@ func TestProofVerify(t *testing.T) {
18141833

18151834
receiverBlob, _, err := proof.AppendTransition(
18161835
genesisProofBlob, &proofParams[1], proof.MockVerifierCtx,
1836+
proof.WithVersion(proof.TransitionV1),
18171837
)
18181838
require.NoError(t, err)
18191839
receiverFile, err := proof.NewFile(proof.V0)
@@ -1870,6 +1890,7 @@ func TestProofVerifyFullValueSplit(t *testing.T) {
18701890
// Create a proof for each receiver and verify it.
18711891
senderBlob, _, err := proof.AppendTransition(
18721892
genesisProofBlob, &proofParams[0], proof.MockVerifierCtx,
1893+
proof.WithVersion(proof.TransitionV1),
18731894
)
18741895
require.NoError(t, err)
18751896
senderFile, err := proof.NewFile(proof.V0)
@@ -1880,6 +1901,7 @@ func TestProofVerifyFullValueSplit(t *testing.T) {
18801901

18811902
receiverBlob, _, err := proof.AppendTransition(
18821903
genesisProofBlob, &proofParams[1], proof.MockVerifierCtx,
1904+
proof.WithVersion(proof.TransitionV1),
18831905
)
18841906
require.NoError(t, err)
18851907
receiverFile := proof.NewEmptyFile(proof.V0)

0 commit comments

Comments
 (0)