Skip to content

Commit 6a09d43

Browse files
committed
tapchannel: add HTLC index tweak to sign desc tweak
If we're signing a first-level HTLC leaf, we need to add the HTLC index tweak to the single tweak.
1 parent 93d4df8 commit 6a09d43

File tree

3 files changed

+105
-10
lines changed

3 files changed

+105
-10
lines changed

tapchannel/aux_leaf_signer.go

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -414,12 +414,20 @@ func verifyHtlcSignature(chainParams *address.ChainParams,
414414
"verify second level: %w", err)
415415
}
416416

417+
// Because we're verifying an HTLC first-level output, we need
418+
// to apply the same tweak to the key as we did when creating
419+
// the script key in the allocation. That tweak was applied to
420+
// the script key to guarantee uniqueness on the asset level.
421+
verifyKey := TweakPubKeyWithIndex(
422+
keyRing.RemoteHtlcKey, baseJob.HTLC.HtlcIndex,
423+
)
424+
417425
leafToVerify := txscript.TapLeaf{
418426
Script: htlcScript.WitnessScriptToSign(),
419427
LeafVersion: txscript.BaseLeafVersion,
420428
}
421429
validator := &schnorrSigValidator{
422-
pubKey: *keyRing.RemoteHtlcKey,
430+
pubKey: *verifyKey,
423431
tapLeaf: lfn.Some(leafToVerify),
424432
signMethod: input.TaprootScriptSpendSignMethod,
425433
}
@@ -439,8 +447,9 @@ func verifyHtlcSignature(chainParams *address.ChainParams,
439447
// that should be used to verify the generated signature, and also the leaf to
440448
// be signed.
441449
func applySignDescToVIn(signDesc input.SignDescriptor, vIn *tappsbt.VInput,
442-
chainParams *address.ChainParams,
443-
tapscriptRoot []byte) (btcec.PublicKey, txscript.TapLeaf) {
450+
chainParams *address.ChainParams, tapscriptRoot []byte,
451+
index input.HtlcIndex, tweakWithIndex bool) (btcec.PublicKey,
452+
txscript.TapLeaf) {
444453

445454
leafToSign := txscript.TapLeaf{
446455
Script: signDesc.WitnessScript,
@@ -466,20 +475,26 @@ func applySignDescToVIn(signDesc input.SignDescriptor, vIn *tappsbt.VInput,
466475
vIn.SighashType = signDesc.HashType
467476
vIn.TaprootMerkleRoot = tapscriptRoot
468477

478+
// We might need to apply another tweak to the public key if this is an
479+
// HTLC first-level output, where we've applied a tweak to the script
480+
// key to guarantee uniqueness.
481+
singleTweak := signDesc.SingleTweak
482+
if tweakWithIndex {
483+
singleTweak = AddTweakWithIndex(singleTweak, index)
484+
}
485+
469486
// Apply single or double tweaks if present in the sign
470487
// descriptor. At the same time, we apply the tweaks to a copy
471488
// of the public key, so we can validate the produced signature.
472489
signingKey := signDesc.KeyDesc.PubKey
473-
if len(signDesc.SingleTweak) > 0 {
490+
if len(singleTweak) > 0 {
474491
key := btcwallet.PsbtKeyTypeInputSignatureTweakSingle
475492
vIn.Unknowns = append(vIn.Unknowns, &psbt.Unknown{
476493
Key: key,
477-
Value: signDesc.SingleTweak,
494+
Value: singleTweak,
478495
})
479496

480-
signingKey = input.TweakPubKeyWithTweak(
481-
signingKey, signDesc.SingleTweak,
482-
)
497+
signingKey = input.TweakPubKeyWithTweak(signingKey, singleTweak)
483498
}
484499
if signDesc.DoubleTweak != nil {
485500
key := btcwallet.PsbtKeyTypeInputSignatureTweakDouble
@@ -542,6 +557,7 @@ func (s *AuxLeafSigner) generateHtlcSignature(chanState lnwallet.AuxChanState,
542557

543558
signingKey, leafToSign := applySignDescToVIn(
544559
signDesc, vIn, s.cfg.ChainParams, tapscriptRoot,
560+
baseJob.HTLC.HtlcIndex, true,
545561
)
546562

547563
// We can now sign this virtual packet, as we've given the
@@ -832,3 +848,23 @@ func TweakHtlcTree(tree input.ScriptTree,
832848
TapscriptRoot: tree.TapscriptRoot,
833849
}
834850
}
851+
852+
// AddTweakWithIndex adds the given index to the given tweak. If the tweak is
853+
// empty, the index is used as the tweak directly. The value of 1 is always
854+
// added to the index to make sure this value is always non-zero.
855+
func AddTweakWithIndex(maybeTweak []byte, index input.HtlcIndex) []byte {
856+
indexTweak := HtlcIndexAsScriptKeyTweak(index)
857+
858+
// If we don't already have a tweak, we just use the index as the tweak.
859+
if len(maybeTweak) == 0 {
860+
return fn.ByteSlice(indexTweak.Bytes())
861+
}
862+
863+
// If we have a tweak, we need to parse/decode it as a scalar, then add
864+
// the index as a scalar, and encode it back to a byte slice.
865+
tweak := new(secp256k1.ModNScalar)
866+
_ = tweak.SetByteSlice(maybeTweak)
867+
newTweak := tweak.Add(indexTweak)
868+
869+
return fn.ByteSlice(newTweak.Bytes())
870+
}

tapchannel/aux_leaf_signer_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ import (
77
"encoding/hex"
88
"fmt"
99
"math"
10+
"math/big"
1011
"testing"
1112

1213
"github.com/btcsuite/btcd/btcec/v2"
1314
"github.com/btcsuite/btcd/txscript"
1415
"github.com/decred/dcrd/dcrec/secp256k1/v4"
1516
"github.com/lightninglabs/taproot-assets/asset"
17+
"github.com/lightninglabs/taproot-assets/fn"
1618
"github.com/lightninglabs/taproot-assets/internal/test"
1719
cmsg "github.com/lightninglabs/taproot-assets/tapchannelmsg"
1820
"github.com/lightningnetwork/lnd/input"
@@ -394,3 +396,61 @@ func TestTweakHtlcTree(t *testing.T) {
394396
})
395397
}
396398
}
399+
400+
// TestAddTweakWithIndex tests the AddTweakWithIndex function.
401+
func TestAddTweakWithIndex(t *testing.T) {
402+
var (
403+
bufMaxUint64 = make([]byte, 8)
404+
maxUint64 = new(secp256k1.ModNScalar)
405+
)
406+
binary.BigEndian.PutUint64(bufMaxUint64, math.MaxUint64)
407+
_ = maxUint64.SetByteSlice(bufMaxUint64)
408+
maxUint64Double := new(secp256k1.ModNScalar).
409+
Set(maxUint64).Add(maxUint64)
410+
411+
testCases := []struct {
412+
name string
413+
tweak []byte
414+
index uint64
415+
result *secp256k1.ModNScalar
416+
}{
417+
{
418+
name: "empty tweak, index 0",
419+
index: 0,
420+
result: new(secp256k1.ModNScalar).SetInt(1),
421+
},
422+
{
423+
name: "five as tweak, index 123",
424+
tweak: []byte{0x05},
425+
index: 123,
426+
result: new(secp256k1.ModNScalar).SetInt(129),
427+
},
428+
{
429+
name: "all zero tweak, index 123",
430+
tweak: bytes.Repeat([]byte{0}, 32),
431+
index: 123,
432+
result: new(secp256k1.ModNScalar).SetInt(124),
433+
},
434+
{
435+
name: "tweak math.MaxUint64, index math.MaxUint64-1",
436+
tweak: fn.ByteSlice(maxUint64.Bytes()),
437+
index: math.MaxUint64 - 1,
438+
result: maxUint64Double,
439+
},
440+
}
441+
442+
for _, tc := range testCases {
443+
t.Run(tc.name, func(t *testing.T) {
444+
tweak := AddTweakWithIndex(tc.tweak, tc.index)
445+
resultBytes := tc.result.Bytes()
446+
resultBigInt := new(big.Int).SetBytes(resultBytes[:])
447+
tweakBigInt := new(big.Int).SetBytes(tweak)
448+
// 36893488147419103230
449+
// 55340232221128654845
450+
451+
require.Equalf(t, resultBytes[:], tweak, "expected: "+
452+
"%s, got: %s", resultBigInt.String(),
453+
tweakBigInt.String())
454+
})
455+
}
456+
}

tapchannel/aux_sweeper.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,8 +365,7 @@ func (a *AuxSweeper) signSweepVpackets(vPackets []*tappsbt.VPacket,
365365
// tweaks to generate the key we'll use to verify the
366366
// signature.
367367
signingKey, leafToSign := applySignDescToVIn(
368-
signDesc, vIn, &a.cfg.ChainParams,
369-
tapTweak,
368+
signDesc, vIn, &a.cfg.ChainParams, tapTweak, 0, false,
370369
)
371370

372371
// In this case, the witness isn't special, so we'll set the

0 commit comments

Comments
 (0)