-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
290 changed files
with
79,091 additions
and
1,937 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
package libwallet | ||
|
||
import ( | ||
"encoding/hex" | ||
"fmt" | ||
|
||
"github.com/muun/libwallet/addresses" | ||
"github.com/muun/libwallet/btcsuitew/txscriptw" | ||
"github.com/muun/libwallet/musig" | ||
|
||
"github.com/btcsuite/btcd/btcec" | ||
"github.com/btcsuite/btcd/chaincfg" | ||
"github.com/btcsuite/btcd/txscript" | ||
"github.com/btcsuite/btcd/wire" | ||
"github.com/btcsuite/btcutil" | ||
) | ||
|
||
// CreateAddressV5 returns a P2TR MuunAddress using Musig with the signing and cosigning keys. | ||
func CreateAddressV5(userKey, muunKey *HDPublicKey) (MuunAddress, error) { | ||
return addresses.CreateAddressV5(&userKey.key, &muunKey.key, userKey.Path, userKey.Network.network) | ||
} | ||
|
||
type coinV5 struct { | ||
Network *chaincfg.Params | ||
OutPoint wire.OutPoint | ||
KeyPath string | ||
Amount btcutil.Amount | ||
UserSessionId [32]byte | ||
MuunPubNonce [66]byte | ||
MuunPartialSig [32]byte | ||
SigHashes *txscriptw.TaprootSigHashes | ||
} | ||
|
||
func (c *coinV5) SignInput(index int, tx *wire.MsgTx, userKey *HDPrivateKey, muunKey *HDPublicKey) error { | ||
derivedUserKey, err := userKey.DeriveTo(c.KeyPath) | ||
if err != nil { | ||
return fmt.Errorf("failed to derive user private key: %w", err) | ||
} | ||
|
||
derivedMuunKey, err := muunKey.DeriveTo(c.KeyPath) | ||
if err != nil { | ||
return fmt.Errorf("failed to derive muun public key: %w", err) | ||
} | ||
|
||
userEcPriv, err := derivedUserKey.key.ECPrivKey() | ||
if err != nil { | ||
return fmt.Errorf("failed to obtain ECPrivKey from derivedUserKey") // TODO: necessary handling? | ||
} | ||
|
||
muunEcPub, err := derivedMuunKey.key.ECPubKey() | ||
if err != nil { | ||
return fmt.Errorf("failed to obtain ECPubKey from derivedMuunKey") // TODO: necessary handling? | ||
} | ||
|
||
sigHash, err := txscriptw.CalcTaprootSigHash(tx, c.SigHashes, index, txscript.SigHashAll) | ||
if err != nil { | ||
return fmt.Errorf("failed to create sigHash: %w", err) | ||
} | ||
var toSign [32]byte | ||
copy(toSign[:], sigHash) | ||
|
||
return c.signSecondWith(index, tx, userEcPriv, muunEcPub, c.UserSessionId, toSign) | ||
} | ||
|
||
func (c *coinV5) FullySignInput(index int, tx *wire.MsgTx, userKey, muunKey *HDPrivateKey) error { | ||
derivedUserKey, err := userKey.DeriveTo(c.KeyPath) | ||
if err != nil { | ||
return fmt.Errorf("failed to derive user private key: %w", err) | ||
} | ||
|
||
derivedMuunKey, err := muunKey.DeriveTo(c.KeyPath) | ||
if err != nil { | ||
return fmt.Errorf("failed to derive muun private key: %w", err) | ||
} | ||
|
||
userEcPriv, err := derivedUserKey.key.ECPrivKey() | ||
if err != nil { | ||
return fmt.Errorf("failed to obtain ECPrivKey from derivedUserKey") // TODO: necessary handling? | ||
} | ||
|
||
muunEcPriv, err := derivedMuunKey.key.ECPrivKey() | ||
if err != nil { | ||
return fmt.Errorf("failed to obtain ECPrivKey from derivedMuunKey") // TODO: necessary handling? | ||
} | ||
|
||
sigHash, err := txscriptw.CalcTaprootSigHash(tx, c.SigHashes, index, txscript.SigHashAll) | ||
if err != nil { | ||
return fmt.Errorf("failed to create sigHash: %w", err) | ||
} | ||
var toSign [32]byte | ||
copy(toSign[:], sigHash) | ||
|
||
userPubNonce := musig.GeneratePubNonce(c.UserSessionId) | ||
|
||
err = c.signFirstWith(index, tx, userEcPriv.PubKey(), muunEcPriv, userPubNonce, toSign) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return c.signSecondWith(index, tx, userEcPriv, muunEcPriv.PubKey(), c.UserSessionId, toSign) | ||
} | ||
|
||
func (c *coinV5) signFirstWith( | ||
index int, | ||
tx *wire.MsgTx, | ||
userPub *btcec.PublicKey, | ||
muunPriv *btcec.PrivateKey, | ||
userPubNonce [66]byte, | ||
toSign [32]byte, | ||
) error { | ||
|
||
// NOTE: | ||
// This will only be called in a recovery context, where both private keys are provided by the | ||
// user. We call the variables below "muunSessionId" and "muunPubNonce" to follow convention, | ||
// but Muun servers play no role in this code path and both are locally generated. | ||
muunSessionId := musig.RandomSessionId() | ||
muunPubNonce := musig.GeneratePubNonce(muunSessionId) | ||
|
||
muunPartialSig, err := musig.ComputeMuunPartialSignature( | ||
toSign, | ||
userPub, | ||
muunPriv, | ||
userPubNonce, | ||
muunSessionId, | ||
nil, | ||
) | ||
if err != nil { | ||
return fmt.Errorf("failed to add first signature: %w", err) | ||
} | ||
|
||
c.MuunPubNonce = muunPubNonce | ||
c.MuunPartialSig = muunPartialSig | ||
|
||
return nil | ||
} | ||
|
||
func (c *coinV5) signSecondWith( | ||
index int, | ||
tx *wire.MsgTx, | ||
userPriv *btcec.PrivateKey, | ||
muunPub *btcec.PublicKey, | ||
userSessionId [32]byte, | ||
toSign [32]byte, | ||
) error { | ||
|
||
rawCombinedSig, err := musig.AddUserSignatureAndCombine( | ||
toSign, | ||
userPriv, | ||
muunPub, | ||
c.MuunPartialSig, | ||
c.MuunPubNonce, | ||
userSessionId, | ||
nil, | ||
) | ||
if err != nil { | ||
return fmt.Errorf("failed to add second signature and combine: %w", err) | ||
} | ||
|
||
sig := append(rawCombinedSig[:], byte(txscript.SigHashAll)) | ||
|
||
tx.TxIn[index].Witness = wire.TxWitness{sig} | ||
return nil | ||
} | ||
|
||
type MusigNonces struct { | ||
sessionIds [][32]byte | ||
publicNonces [][66]byte | ||
} | ||
|
||
func (m *MusigNonces) GetPubnonceHex(index int) string { | ||
return hex.EncodeToString(m.publicNonces[index][:]) | ||
} | ||
|
||
func GenerateMusigNonces(count int) *MusigNonces { | ||
sessionIds := make([][32]byte, 0) | ||
publicNonces := make([][66]byte, 0) | ||
|
||
for i := 0; i < count; i += 1 { | ||
sessionIds = append(sessionIds, musig.RandomSessionId()) | ||
publicNonces = append(publicNonces, musig.GeneratePubNonce(sessionIds[i])) | ||
} | ||
|
||
return &MusigNonces{ | ||
sessionIds, | ||
publicNonces, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ const ( | |
V2 = 2 | ||
V3 = 3 | ||
V4 = 4 | ||
V5 = 5 | ||
SubmarineSwapV1 = 101 | ||
SubmarineSwapV2 = 102 | ||
IncomingSwap = 201 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package addresses | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/btcsuite/btcd/chaincfg" | ||
"github.com/btcsuite/btcutil/hdkeychain" | ||
"github.com/muun/libwallet/btcsuitew/btcutilw" | ||
"github.com/muun/libwallet/musig" | ||
) | ||
|
||
// CreateAddressV5 returns a P2TR WalletAddress using Musig with the signing and cosigning keys. | ||
func CreateAddressV5(userKey, muunKey *hdkeychain.ExtendedKey, path string, network *chaincfg.Params) (*WalletAddress, error) { | ||
witnessProgram, err := CreateWitnessScriptV5(userKey, muunKey) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to generate witness script v5: %w", err) | ||
} | ||
|
||
address, err := btcutilw.NewAddressTaprootKey(witnessProgram, network) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &WalletAddress{ | ||
address: address.EncodeAddress(), | ||
version: V5, | ||
derivationPath: path, | ||
}, nil | ||
} | ||
|
||
func CreateWitnessScriptV5(userKey, muunKey *hdkeychain.ExtendedKey) ([]byte, error) { | ||
userPublicKey, err := userKey.ECPubKey() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
muunPublicKey, err := muunKey.ECPubKey() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
combined, err := musig.CombinePubKeysWithTweak(userPublicKey, muunPublicKey, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
xOnlyCombined := combined.SerializeCompressed()[1:] | ||
|
||
return xOnlyCombined, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package addresses | ||
|
||
import ( | ||
"reflect" | ||
"testing" | ||
) | ||
|
||
func TestCreateAddressV5(t *testing.T) { | ||
const ( | ||
addressPath = "m/schema:1'/recovery:1'/external:1/17" | ||
|
||
v5Address = "bcrt1pvqngr85tm8hmsv2hjyrejlpsy7u65f7vke8mmrxnyuj3aj3xsapqvh8yrf" | ||
basePK = "tpubDBf5wCeqg3KrLJiXaveDzD5JtFJ1ss9NVvFMx4RYS73SjwPEEawcAQ7V1B5DGM4gunWDeYNrnkc49sUaf7mS1wUKiJJQD6WEctExUQoLvrg" | ||
baseCosigningPK = "tpubDB22PFkUaHoB7sgxh7exCivV5rAevVSzbB8WkFCCdbHq39r8xnYexiot4NGbi8PM6E1ySVeaHsoDeMYb6EMndpFrzVmuX8iQNExzwNpU61B" | ||
basePath = "m/schema:1'/recovery:1'" | ||
) | ||
|
||
baseMuunKey := parseKey(baseCosigningPK) | ||
muunKey := derive(baseMuunKey, basePath, addressPath) | ||
|
||
baseUserKey := parseKey(basePK) | ||
userKey := derive(baseUserKey, basePath, addressPath) | ||
|
||
expectedAddr := &WalletAddress{address: v5Address, derivationPath: addressPath, version: V5} | ||
|
||
actualAddr, err := CreateAddressV5(userKey, muunKey, addressPath, network) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
if !reflect.DeepEqual(actualAddr, expectedAddr) { | ||
t.Errorf("Created v5 address %v, expected %v", actualAddr, expectedAddr) | ||
} | ||
} |
Oops, something went wrong.