Skip to content

Commit

Permalink
Merge pull request #1905 from Roasbeef/taptweak-mutation
Browse files Browse the repository at this point in the history
txscript: modify TweakTaprootPrivKey to operate on private key copy
  • Loading branch information
guggero authored Nov 8, 2022
2 parents 63835cb + fc47c31 commit 14bb56f
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 6 deletions.
2 changes: 1 addition & 1 deletion txscript/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func RawTxInTaprootSignature(tx *wire.MsgTx, sigHashes *TxSigHashes, idx int,

// Before we sign the sighash, we'll need to apply the taptweak to the
// private key based on the tapScriptRootHash.
privKeyTweak := TweakTaprootPrivKey(key, tapScriptRootHash)
privKeyTweak := TweakTaprootPrivKey(*key, tapScriptRootHash)

// With the sighash constructed, we can sign it with the specified
// private key.
Expand Down
4 changes: 2 additions & 2 deletions txscript/taproot.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,12 +296,12 @@ func ComputeTaprootKeyNoScript(internalKey *btcec.PublicKey) *btcec.PublicKey {
// but on the private key instead. The final key is derived as: privKey +
// h_tapTweak(internalKey || merkleRoot) % N, where N is the order of the
// secp256k1 curve, and merkleRoot is the root hash of the tapscript tree.
func TweakTaprootPrivKey(privKey *btcec.PrivateKey,
func TweakTaprootPrivKey(privKey btcec.PrivateKey,
scriptRoot []byte) *btcec.PrivateKey {

// If the corresponding public key has an odd y coordinate, then we'll
// negate the private key as specified in BIP 341.
privKeyScalar := &privKey.Key
privKeyScalar := privKey.Key
pubKeyBytes := privKey.PubKey().SerializeCompressed()
if pubKeyBytes[0] == secp.PubKeyFormatCompressedOdd {
privKeyScalar.Negate()
Expand Down
42 changes: 39 additions & 3 deletions txscript/taproot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@ func TestControlBlockParsing(t *testing.T) {
// key, then generating a public key from that. This test a quickcheck test to
// assert the following invariant:
//
// * taproot_tweak_pubkey(pubkey_gen(seckey), h)[1] ==
// pubkey_gen(taproot_tweak_seckey(seckey, h))
// - taproot_tweak_pubkey(pubkey_gen(seckey), h)[1] ==
// pubkey_gen(taproot_tweak_seckey(seckey, h))
func TestTaprootScriptSpendTweak(t *testing.T) {
t.Parallel()

Expand All @@ -186,7 +186,7 @@ func TestTaprootScriptSpendTweak(t *testing.T) {
tweakedPub := ComputeTaprootOutputKey(privKey.PubKey(), x[:])

// Now we'll generate the corresponding tweaked private key.
tweakedPriv := TweakTaprootPrivKey(privKey, x[:])
tweakedPriv := TweakTaprootPrivKey(*privKey, x[:])

// The public key for this private key should be the same as
// the tweaked public key we generate above.
Expand All @@ -204,6 +204,42 @@ func TestTaprootScriptSpendTweak(t *testing.T) {

}

// TestTaprootTweakNoMutation tests that the underlying private key passed into
// TweakTaprootPrivKey is never mutated.
func TestTaprootTweakNoMutation(t *testing.T) {
t.Parallel()

// Assert that given a random tweak, and a random private key, that if
// we tweak the private key it remains unaffected.
f := func(privBytes, tweak [32]byte) bool {
privKey, _ := btcec.PrivKeyFromBytes(privBytes[:])

// Now we'll generate the corresponding tweaked private key.
tweakedPriv := TweakTaprootPrivKey(*privKey, tweak[:])

// The tweaked private key and the original private key should
// NOT be the same.
if *privKey == *tweakedPriv {
t.Logf("private key was mutated")
return false
}

// We shuold be able to re-derive the private key from raw
// bytes and have that match up again.
privKeyCopy, _ := btcec.PrivKeyFromBytes(privBytes[:])
if *privKey != *privKeyCopy {
t.Logf("private doesn't match")
return false
}

return true
}

if err := quick.Check(f, nil); err != nil {
t.Fatalf("private key modified: %v", err)
}
}

// TestTaprootConstructKeyPath tests the key spend only taproot construction.
func TestTaprootConstructKeyPath(t *testing.T) {
checkPath := func(branch uint32, expectedAddresses []string) {
Expand Down

0 comments on commit 14bb56f

Please sign in to comment.