Skip to content

Commit b06b7af

Browse files
authored
Merge pull request #1568 from lightninglabs/verify-stxo-in-v0-proofs
proof: verify stxo proofs if present in v0 proofs
2 parents 608bed8 + 3192a7e commit b06b7af

File tree

9 files changed

+114
-20
lines changed

9 files changed

+114
-20
lines changed

proof/append.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,19 @@ import (
1414
// GenConfig is a struct that holds the configuration for creating Taproot Asset
1515
// proofs.
1616
type GenConfig struct {
17-
// transitionVersion is the version of the asset state transition proof
17+
// TransitionVersion is the version of the asset state transition proof
1818
// that is going to be used.
19-
transitionVersion TransitionVersion
19+
TransitionVersion TransitionVersion
20+
21+
// NoSTXOProofs indicates whether to skip the generation of STXO
22+
// inclusion and exclusion proofs for the transition proof.
23+
NoSTXOProofs bool
2024
}
2125

2226
// DefaultGenConfig returns a default proof generation configuration.
2327
func DefaultGenConfig() GenConfig {
2428
return GenConfig{
25-
transitionVersion: TransitionV0,
29+
TransitionVersion: TransitionV0,
2630
}
2731
}
2832

@@ -34,7 +38,15 @@ type GenOption func(*GenConfig)
3438
// given version.
3539
func WithVersion(v TransitionVersion) GenOption {
3640
return func(cfg *GenConfig) {
37-
cfg.transitionVersion = v
41+
cfg.TransitionVersion = v
42+
}
43+
}
44+
45+
// WithNoSTXOProofs is an option that can be used to skip the generation of
46+
// STXO inclusion and exclusion proofs for the transition proof.
47+
func WithNoSTXOProofs() GenOption {
48+
return func(cfg *GenConfig) {
49+
cfg.NoSTXOProofs = true
3850
}
3951
}
4052

@@ -158,7 +170,7 @@ func CreateTransitionProof(prevOut wire.OutPoint, params *TransitionParams,
158170
}
159171

160172
proof, err := baseProof(
161-
&params.BaseProofParams, prevOut, cfg.transitionVersion,
173+
&params.BaseProofParams, prevOut, cfg.TransitionVersion,
162174
)
163175
if err != nil {
164176
return nil, fmt.Errorf("error creating base proofs: %w", err)
@@ -194,7 +206,7 @@ func CreateTransitionProof(prevOut wire.OutPoint, params *TransitionParams,
194206
TapSiblingPreimage: params.TapscriptSibling,
195207
}
196208

197-
if proof.Asset.IsTransferRoot() {
209+
if proof.Asset.IsTransferRoot() && !cfg.NoSTXOProofs {
198210
stxoInclusionProofs := make(
199211
map[asset.SerializedKey]commitment.Proof,
200212
len(proof.Asset.PrevWitnesses),

proof/mint.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ func NewMintingBlobs(params *MintParams, vCtx VerifierCtx,
266266

267267
base, err := baseProof(
268268
&params.BaseProofParams, params.GenesisPoint,
269-
opts.genConfig.transitionVersion,
269+
opts.genConfig.TransitionVersion,
270270
)
271271
if err != nil {
272272
return nil, err

proof/verifier.go

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,12 @@ func (p *Proof) verifyInclusionProof() (*commitment.TapCommitment, error) {
206206
ErrStxoInputProofMissing)
207207
}
208208

209-
// We ignore the STXO proofs if the proof signals version 0, or if there
210-
// are no STXO proofs present (because they're not needed for this type
211-
// of asset).
212-
if p.IsVersionV0() || !hasStxoProofs {
209+
// We ignore the STXO proofs if they're not needed for this type of
210+
// asset or if there are no STXO proofs. At this point we can be sure
211+
// that if they are needed they also are present (because of the check
212+
// above). If they are not needed, but still present we verify them for
213+
// good measure.
214+
if !p.Asset.IsTransferRoot() || !hasStxoProofs {
213215
return v0Commitment, nil
214216
}
215217

@@ -292,7 +294,7 @@ func (p *Proof) verifyExclusionProofs() (*commitment.TapCommitmentVersion,
292294
maps.Clone(p2trOutputs),
293295
)
294296
if err != nil {
295-
return nil, fmt.Errorf("error veryfying v0 exclusion proof: %w",
297+
return nil, fmt.Errorf("error verifying v0 exclusion proof: %w",
296298
err)
297299
}
298300

@@ -318,10 +320,12 @@ func (p *Proof) verifyExclusionProofs() (*commitment.TapCommitmentVersion,
318320
ErrStxoInputProofMissing)
319321
}
320322

321-
// We ignore the STXO proofs if the proof signals version 0, or if there
322-
// are no STXO proofs present (because they're not needed for this type
323-
// of asset).
324-
if p.IsVersionV0() || !hasStxoProofs {
323+
// We ignore the STXO proofs if they're not needed for this type of
324+
// asset or if there are no STXO proofs. At this point we can be sure
325+
// that if they are needed they also are present (because of the check
326+
// above). If they are not needed, but still present we verify them for
327+
// good measure.
328+
if !p.Asset.IsTransferRoot() || !hasStxoProofs {
325329
return assertVersionConsistency(commitVersions)
326330
}
327331

proof/verifier_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,44 @@ func TestVerifyV1InclusionProof(t *testing.T) {
453453
},
454454
expectedErrContains: "error verifying STXO proof: invalid " +
455455
"taproot proof",
456+
}, {
457+
name: "valid v0 and invalid v1 proofs but with v0 version",
458+
makeProof: func(t *testing.T) []*Proof {
459+
p, rootCommitments := makeV0InclusionProof(t)
460+
461+
// We add an STXO asset to the root commitment.
462+
_, key := addV1InclusionProof(
463+
t, p[0], rootCommitments[0], withAddStxoProof(),
464+
withUpdateAnchorTx(),
465+
)
466+
467+
// At this point both proofs are valid. But we want to
468+
// invalidate just the v1 proof, so we replace it by one
469+
// that is derived from a different commitment. We can
470+
// achieve that by downgrading the commitment and
471+
// re-deriving the same proof, without updating the
472+
// anchor output transaction.
473+
clonedCommitment, err := rootCommitments[0].Downgrade()
474+
require.NoError(t, err)
475+
476+
_, v1ProofOld, err := clonedCommitment.Proof(
477+
asset.EmptyGenesisID,
478+
p[0].Asset.AssetCommitmentKey(),
479+
)
480+
require.NoError(t, err)
481+
482+
cp := p[0].InclusionProof.CommitmentProof
483+
cp.STXOProofs[key] = *v1ProofOld
484+
485+
// We now have a invalid v1 proof, but the proof's
486+
// version is still set to 0. We do however expect the
487+
// verification to fail, since the v1 proof is present
488+
// and will be verified..
489+
490+
return p
491+
},
492+
expectedErrContains: "error verifying STXO proof: invalid " +
493+
"taproot proof",
456494
}, {
457495
name: "stxo proof missing",
458496
makeProof: func(t *testing.T) []*Proof {
@@ -670,6 +708,31 @@ func TestVerifyV1ExclusionProof(t *testing.T) {
670708

671709
return p
672710
},
711+
}, {
712+
name: "invalid v1 exclusion proof but with v0 version",
713+
makeProof: func(t *testing.T) []*Proof {
714+
p, _ := makeV0InclusionProof(t)
715+
716+
// We now correctly prove that the asset isn't in the
717+
// second output.
718+
emptyCommitment, err := commitment.NewTapCommitment(nil)
719+
require.NoError(t, err)
720+
721+
addV0ExclusionOutput(
722+
t, p[0], emptyCommitment,
723+
p[0].Asset.TapCommitmentKey(),
724+
p[0].Asset.AssetCommitmentKey(), internalKey, 1,
725+
)
726+
727+
randAsset := makeTransferAsset(t)
728+
addV1ExclusionProof(
729+
t, p[0], *randAsset, emptyCommitment, 1,
730+
)
731+
732+
return p
733+
},
734+
expectedErrContains: "error verifying v1 exclusion proof: " +
735+
"missing STXO asset for key",
673736
}, {
674737
name: "multiple assets with an exclusion proof each",
675738
makeProof: func(t *testing.T) []*Proof {

tapchannel/aux_closer.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,7 @@ func (a *AuxChanCloser) FinalizeClose(desc chancloser.AuxCloseDesc,
726726
proofSuffix, err := tapsend.CreateProofSuffixCustom(
727727
closeTx, vPkt, closeInfo.outputCommitments,
728728
outIdx, closeInfo.vPackets, exclusionCreator,
729+
proof.WithNoSTXOProofs(),
729730
)
730731
if err != nil {
731732
return fmt.Errorf("unable to create proof "+

tapchannel/aux_funding_controller.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,7 @@ func (f *FundingController) anchorVPackets(fundedPkt *tapsend.FundedPsbt,
11141114
proofSuffix, err := tapsend.CreateProofSuffix(
11151115
fundedPkt.Pkt.UnsignedTx, fundedPkt.Pkt.Outputs,
11161116
vPkt, outputCommitments, vOutIdx, allPackets,
1117+
proof.WithNoSTXOProofs(),
11171118
)
11181119
if err != nil {
11191120
return nil, fmt.Errorf("unable to create "+

tapchannel/aux_sweeper.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1612,6 +1612,7 @@ func (a *AuxSweeper) importCommitTx(req lnwallet.ResolutionReq,
16121612
proofSuffix, err := tapsend.CreateProofSuffixCustom(
16131613
req.CommitTx, vPkt, outCommitments, outIdx,
16141614
vPackets, exclusionCreator,
1615+
proof.WithNoSTXOProofs(),
16151616
)
16161617
if err != nil {
16171618
return fmt.Errorf("unable to create "+
@@ -2450,7 +2451,7 @@ func (a *AuxSweeper) registerAndBroadcastSweep(req *sweep.BumpRequest,
24502451

24512452
proofSuffix, err := tapsend.CreateProofSuffixCustom(
24522453
sweepTx, vPkt, outCommitments, outIdx, allVpkts,
2453-
exclusionCreator,
2454+
exclusionCreator, proof.WithNoSTXOProofs(),
24542455
)
24552456
if err != nil {
24562457
return fmt.Errorf("unable to create proof "+

tapchannel/commitment.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,7 @@ func GenerateCommitmentAllocations(prevState *cmsg.Commitment,
612612
fakeCommitTx, vPkt, outCommitments, outIdx,
613613
vPackets, tapsend.NonAssetExclusionProofs(
614614
allocations,
615-
),
615+
), proof.WithNoSTXOProofs(),
616616
)
617617
if err != nil {
618618
return nil, nil, fmt.Errorf("unable to create "+

tapsend/proof.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,14 @@ func CreateProofSuffixCustom(finalTx *wire.MsgTx, vPacket *tappsbt.VPacket,
155155

156156
inputPrevID := vPacket.Inputs[0].PrevID
157157

158+
cfg := proof.DefaultGenConfig()
159+
for _, opt := range opts {
160+
opt(&cfg)
161+
}
162+
158163
params, err := proofParams(
159164
finalTx, vPacket, outputCommitments, outIndex,
160-
allAnchoredVPackets,
165+
allAnchoredVPackets, cfg.NoSTXOProofs,
161166
)
162167
if err != nil {
163168
return nil, err
@@ -233,7 +238,8 @@ func newParams(finalTx *wire.MsgTx, a *asset.Asset, outputIndex int,
233238
// proofs for the sender and receiver.
234239
func proofParams(finalTx *wire.MsgTx, vPkt *tappsbt.VPacket,
235240
outputCommitments tappsbt.OutputCommitments, outIndex int,
236-
allAnchoredVPackets []*tappsbt.VPacket) (*proof.TransitionParams, error) {
241+
allAnchoredVPackets []*tappsbt.VPacket,
242+
noStxoProofs bool) (*proof.TransitionParams, error) {
237243

238244
isSplit, err := vPkt.HasSplitCommitment()
239245
if err != nil {
@@ -269,6 +275,12 @@ func proofParams(finalTx *wire.MsgTx, vPkt *tappsbt.VPacket,
269275
outputCommitments,
270276
)
271277

278+
// If we don't require STXO exclusion proofs, then we are done
279+
// here.
280+
if noStxoProofs {
281+
return rootParams, err
282+
}
283+
272284
// Add STXO exclusion proofs for all the other outputs, for all
273285
// STXOs spent by _all_ VOutputs that anchor in this output.
274286
// First Collect all STXOs for this anchor output. Then add

0 commit comments

Comments
 (0)