Skip to content

Commit 6cc9a39

Browse files
committed
asset: fix split witness writeback
UpdateTxWitness mutates a pointer into a copied root asset when a split commitment is present, so the updated TxWitness never propagates back into SplitCommitment.RootAsset. That leaves the root witness empty and produces invalid split proofs/transactions. To mitigate this issue we write the updated witness back into the stored root asset when handling split commitments, and add a regression test that demonstrates the previous failure and now passes.
1 parent 96fac8d commit 6cc9a39

File tree

2 files changed

+42
-2
lines changed

2 files changed

+42
-2
lines changed

asset/asset.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1855,8 +1855,13 @@ func (a *Asset) UpdateTxWitness(prevWitnessIndex int,
18551855

18561856
targetPrevWitness := &a.PrevWitnesses[prevWitnessIndex]
18571857
if a.HasSplitCommitmentWitness() {
1858-
rootAsset := targetPrevWitness.SplitCommitment.RootAsset
1859-
targetPrevWitness = &rootAsset.PrevWitnesses[prevWitnessIndex]
1858+
// When a split commitment is present, the witness must be
1859+
// written back to the root asset within the split commitment
1860+
// itself to ensure the change is persisted.
1861+
rootAsset := &targetPrevWitness.SplitCommitment.RootAsset
1862+
rootAsset.PrevWitnesses[prevWitnessIndex].TxWitness = witness
1863+
1864+
return nil
18601865
}
18611866

18621867
targetPrevWitness.TxWitness = witness

asset/asset_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,41 @@ func TestAssetEncoding(t *testing.T) {
389389
test.WriteTestVectors(t, generatedTestVectorName, testVectors)
390390
}
391391

392+
// TestUpdateTxWitnessSplitCommitment ensures witnesses are written back to the
393+
// split commitment root asset.
394+
func TestUpdateTxWitnessSplitCommitment(t *testing.T) {
395+
t.Parallel()
396+
397+
// First, create a root asset that will be the root of our split
398+
// commitment. We'll nil out its witness to ensure our update is what
399+
// populates it.
400+
root := testRootAsset.Copy()
401+
root.PrevWitnesses[0].TxWitness = nil
402+
403+
// Next, create the split asset which contains a split commitment
404+
// pointing to our root asset.
405+
split := testSplitAsset.Copy()
406+
split.PrevWitnesses[0].SplitCommitment = &SplitCommitment{
407+
Proof: *mssmt.RandProof(t),
408+
RootAsset: *root,
409+
}
410+
411+
// Now, create a new witness and update the split asset.
412+
newWitness := wire.TxWitness{{1, 2, 3}}
413+
err := split.UpdateTxWitness(0, newWitness)
414+
require.NoError(t, err)
415+
416+
// Finally, assert that the witness was written to the root asset within
417+
// the split commitment, and that the split asset's own witness remains
418+
// empty.
419+
require.Equal(
420+
t, newWitness,
421+
split.PrevWitnesses[0].SplitCommitment.RootAsset.
422+
PrevWitnesses[0].TxWitness,
423+
)
424+
require.Empty(t, split.PrevWitnesses[0].TxWitness)
425+
}
426+
392427
// TestAltLeafEncoding runs a property test for AltLeaf validation, encoding,
393428
// and decoding.
394429
func TestAltLeafEncoding(t *testing.T) {

0 commit comments

Comments
 (0)