Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Does bitcoinjs lib has instruments for creating MuSig key-path spend wallets with n-n scheme? #2034

Open
IvanKodak opened this issue Jan 11, 2024 · 10 comments

Comments

@IvanKodak
Copy link

Could someone help me please.
I want to create a taproot MuSig wallet with an n-n scheme and make a payment from this wallet. And I have a few questions:

  1. I didn't find a function to combine pubkeys. Does it exist or not in lib?
  2. I want to extract an unsigned transaction hash for signing because my public keys will be stored in different places and I need to send a hash to all of them and then combine signatures into one and set it in the transaction's input witness. Do you know how I can do this?

Thanks a lot for a response

@leo42
Copy link

leo42 commented Jan 17, 2024

I would also like to know about this.

@leo42
Copy link

leo42 commented Jan 19, 2024

Found it finnaly

To extract your signatures :
console.log(txb.data.inputs[0].partialSig[0].pubkey.toString('hex')) console.log(txb.data.inputs[0].partialSig[0].signature.toString('hex'))

To import signatures:
txb.data.inputs[0].partialSig.push({ pubkey: Buffer.from('0305cc4dceb1d8f8ef19f084cfbbc01fc60a91410c4d95617466183b098b781993', 'hex'), signature: Buffer.from('304402207dad5ee98ff738d627d17dd0b4021d45f370caca141a7cd71aacff429891c17d022009c276d1db69e920f6c4857f04cc203bb53f5488f1e121b6baa32b3f8498ead001', 'hex'), });

To extract the Transaction:
txb.toHex()

To import the Transaction:
const txb = bitcoin.Psbt.fromHex(<TXHex>);

@Andrej656
Copy link

In a MuSig wallet, the public keys need to be aggregated before generating the combined public key. The specific functions for combining public keys may depend on the cryptographic library or framework you are using.

Assuming you are using a library like BitcoinJS, you would typically perform the following steps:

javascript
Copy code
const pubkeys = [pubkey1, pubkey2, pubkey3]; // Replace with your actual public keys

// Combine public keys
const combinedPubkey = bitcoin.payments.p2tr({pubkeys}).output;

// Use combinedPubkey in your transaction
Always refer to the documentation of the library you are using for the exact method to combine public keys.

@IvanKodak
Copy link
Author

Found it finnaly

To extract your signatures : console.log(txb.data.inputs[0].partialSig[0].pubkey.toString('hex')) console.log(txb.data.inputs[0].partialSig[0].signature.toString('hex'))

To import signatures: txb.data.inputs[0].partialSig.push({ pubkey: Buffer.from('0305cc4dceb1d8f8ef19f084cfbbc01fc60a91410c4d95617466183b098b781993', 'hex'), signature: Buffer.from('304402207dad5ee98ff738d627d17dd0b4021d45f370caca141a7cd71aacff429891c17d022009c276d1db69e920f6c4857f04cc203bb53f5488f1e121b6baa32b3f8498ead001', 'hex'), });

To extract the Transaction: txb.toHex()

To import the Transaction: const txb = bitcoin.Psbt.fromHex(<TXHex>);

Thanks for the response, I will check this solution. Have you found a way to properly sign the transaction?

For now, in your solution, I found two questions:

  1. How did you create your aggregated pub key? Did you use a specific tweak?
  2. How did you sign the tx data and create an aggregated signature? Did you use nonce for signature?
    NOTE: Did you use only bitcoinjs instruments or not?

@IvanKodak
Copy link
Author

In a MuSig wallet, the public keys need to be aggregated before generating the combined public key. The specific functions for combining public keys may depend on the cryptographic library or framework you are using.

Assuming you are using a library like BitcoinJS, you would typically perform the following steps:

javascript Copy code const pubkeys = [pubkey1, pubkey2, pubkey3]; // Replace with your actual public keys

// Combine public keys const combinedPubkey = bitcoin.payments.p2tr({pubkeys}).output;

// Use combinedPubkey in your transaction Always refer to the documentation of the library you are using for the exact method to combine public keys.

Thanks for the response, do you know the way how can I create an aggregated signature for MuSig wallet using bitcoinjs lib?

@leo42
Copy link

leo42 commented Jan 31, 2024

In a MuSig wallet, the public keys need to be aggregated before generating the combined public key. The specific functions for combining public keys may depend on the cryptographic library or framework you are using.
Assuming you are using a library like BitcoinJS, you would typically perform the following steps:
javascript Copy code const pubkeys = [pubkey1, pubkey2, pubkey3]; // Replace with your actual public keys
// Combine public keys const combinedPubkey = bitcoin.payments.p2tr({pubkeys}).output;
// Use combinedPubkey in your transaction Always refer to the documentation of the library you are using for the exact method to combine public keys.

Thanks for the response, do you know the way how can I create an aggregated signature for MuSig wallet using bitcoinjs lib?

Just txb.finalizeAllInputs(); after you added all the signatures

  • How did you create your aggregated pub key? Did you use a specific tweak?

To generate the address controlled by the multisig you can

   const p2shAddress = bitcoin.payments.p2wsh({
           redeem: bitcoin.payments.p2ms({ m: config.m , pubkeys:[ <Publickey list>] ,
           network: bitcoin.networks[config.Bitcoin.network], }),
           network: bitcoin.networks[config.Bitcoin.network],
       });
   
       return p2shAddress.address; 

You create the tx object using :
const txb = new bitcoin.Psbt({network : bitcoin.networks[config.Bitcoin.network] });

and consume inputs from the multisig address like this :

txb.addInput({
                hash: addressUtxos[i].txid,
                index: addressUtxos[i].vout,
                witnessUtxo: {
                    script: Buffer.from(addressUtxos[i].scriptPubKey, 'hex'),
                    value: Math.round(addressUtxos[i].amount * 100_000_000),
                },
                witnessScript: redeemScript,
            });
        }

sign all the inputs with a single private key :

 txb.signAllInputs(this.watcherKey);

And then you finalize and get the completed hex string:

txb.finalizeAllInputs();
        const tx = txb.extractTransaction();

        const txHex = tx.toHex();

If you make progress on this I really want to know if there is a way to generate multiple address with the same multisig using BitcoinJS (working for a Typescript project that needs it atm)

@IvanKodak
Copy link
Author

In a MuSig wallet, the public keys need to be aggregated before generating the combined public key. The specific functions for combining public keys may depend on the cryptographic library or framework you are using.
Assuming you are using a library like BitcoinJS, you would typically perform the following steps:
javascript Copy code const pubkeys = [pubkey1, pubkey2, pubkey3]; // Replace with your actual public keys
// Combine public keys const combinedPubkey = bitcoin.payments.p2tr({pubkeys}).output;
// Use combinedPubkey in your transaction Always refer to the documentation of the library you are using for the exact method to combine public keys.

Thanks for the response, do you know the way how can I create an aggregated signature for MuSig wallet using bitcoinjs lib?

Just txb.finalizeAllInputs(); after you added all the signatures

  • How did you create your aggregated pub key? Did you use a specific tweak?

To generate the address controlled by the multisig you can

   const p2shAddress = bitcoin.payments.p2wsh({
           redeem: bitcoin.payments.p2ms({ m: config.m , pubkeys:[ <Publickey list>] ,
           network: bitcoin.networks[config.Bitcoin.network], }),
           network: bitcoin.networks[config.Bitcoin.network],
       });
   
       return p2shAddress.address; 

You create the tx object using : const txb = new bitcoin.Psbt({network : bitcoin.networks[config.Bitcoin.network] });

and consume inputs from the multisig address like this :

txb.addInput({
                hash: addressUtxos[i].txid,
                index: addressUtxos[i].vout,
                witnessUtxo: {
                    script: Buffer.from(addressUtxos[i].scriptPubKey, 'hex'),
                    value: Math.round(addressUtxos[i].amount * 100_000_000),
                },
                witnessScript: redeemScript,
            });
        }

sign all the inputs with a single private key :

 txb.signAllInputs(this.watcherKey);

And then you finalize and get the completed hex string:

txb.finalizeAllInputs();
        const tx = txb.extractTransaction();

        const txHex = tx.toHex();

If you make progress on this I really want to know if there is a way to generate multiple address with the same multisig using BitcoinJS (working for a Typescript project that needs it atm)

Thanks for the detailed response, but how did you compute this.watcherKey? About your question, unfortenly I didn't make progress with it, because I changed direction for some time, but If you want to create different MuSig wallets using the same public keys, you should use tweak operation when you create your wallet and then when you create an aggregated signature, but I don't know how to implement it using bitcoins lib.

@leo42
Copy link

leo42 commented Feb 1, 2024

In a MuSig wallet, the public keys need to be aggregated before generating the combined public key. The specific functions for combining public keys may depend on the cryptographic library or framework you are using.
Assuming you are using a library like BitcoinJS, you would typically perform the following steps:
javascript Copy code const pubkeys = [pubkey1, pubkey2, pubkey3]; // Replace with your actual public keys
// Combine public keys const combinedPubkey = bitcoin.payments.p2tr({pubkeys}).output;
// Use combinedPubkey in your transaction Always refer to the documentation of the library you are using for the exact method to combine public keys.

Thanks for the response, do you know the way how can I create an aggregated signature for MuSig wallet using bitcoinjs lib?

Just txb.finalizeAllInputs(); after you added all the signatures

  • How did you create your aggregated pub key? Did you use a specific tweak?

To generate the address controlled by the multisig you can

   const p2shAddress = bitcoin.payments.p2wsh({
           redeem: bitcoin.payments.p2ms({ m: config.m , pubkeys:[ <Publickey list>] ,
           network: bitcoin.networks[config.Bitcoin.network], }),
           network: bitcoin.networks[config.Bitcoin.network],
       });
   
       return p2shAddress.address; 

You create the tx object using : const txb = new bitcoin.Psbt({network : bitcoin.networks[config.Bitcoin.network] });
and consume inputs from the multisig address like this :

txb.addInput({
                hash: addressUtxos[i].txid,
                index: addressUtxos[i].vout,
                witnessUtxo: {
                    script: Buffer.from(addressUtxos[i].scriptPubKey, 'hex'),
                    value: Math.round(addressUtxos[i].amount * 100_000_000),
                },
                witnessScript: redeemScript,
            });
        }

sign all the inputs with a single private key :

 txb.signAllInputs(this.watcherKey);

And then you finalize and get the completed hex string:

txb.finalizeAllInputs();
        const tx = txb.extractTransaction();

        const txHex = tx.toHex();

If you make progress on this I really want to know if there is a way to generate multiple address with the same multisig using BitcoinJS (working for a Typescript project that needs it atm)

Thanks for the detailed response, but how did you compute this.watcherKey? About your question, unfortenly I didn't make progress with it, because I changed direction for some time, but If you want to create different MuSig wallets using the same public keys, you should use tweak operation when you create your wallet and then when you create an aggregated signature, but I don't know how to implement it using bitcoins lib.

import {ECPairFactory}  from 'ecpair'

const ECPair =  ECPairFactory(ecc);
this.watcherKey = ECPair.fromPrivateKey(Buffer.from(config.Bitcoin.BTCPrivKey,'hex'), { network: bitcoin.networks[config.Bitcoin.network] })

Does tweak work by changing the order of address ? I cannot find anything implemented in the BitcoinJS library called tweak.

@IvanKodak
Copy link
Author

IvanKodak commented Feb 1, 2024

@leo42

export function tweakKey(
  pubKey: Buffer,
  h: Buffer | undefined,
): TweakedPublicKey | null {
  if (!NBuffer.isBuffer(pubKey)) return null;
  if (pubKey.length !== 32) return null;
  if (h && h.length !== 32) return null;

  const tweakHash = tapTweakHash(pubKey, h);

  const res = getEccLib().xOnlyPointAddTweak(pubKey, tweakHash);
  if (!res || res.xOnlyPubkey === null) return null;

  return {
    parity: res.parity,
    x: NBuffer.from(res.xOnlyPubkey),
  };
}`

In bitcoinjs I found this function for tweaking. And this resource can explain tweak operation in more detail.
https://github.com/bitcoinops/taproot-workshop/blob/master/2.2-taptweak.ipynb

And about your last response, are you sure you created a correct key-path spend n-n MuSig wallet?
Because I can't understand where you create aggregated signature.
In my case, the scenario looks like this:

  1. create aggregated pub key
  2. build transaction which have MuSig input
  3. then need to extract transaction hash for signing(I also didn't find correct way to do it with bitcoinjs)
  4. sign transaction hash with each private key of MuSig wallet
  5. get signatures and aggregate it into one(this also i don't know how to do with bitcoinjs)
  6. add this signature to input
  7. finalize transaction & broadcast

Do you have the same scenario?

@jasonandjay
Copy link
Collaborator

@leo42 @IvanKodak
Here are the things you care about:

  1. get MuSig address: you can use p2ms
    https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts#L94-L103

  2. extract transaction hash for signing: you can use transaction.hashForSignature
    https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/ts_src/transaction.ts#L272

  3. aggregate signatures into one:Indeed, there is none. You can refer to taproot's sortSignatures.
    https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/ts_src/psbt/bip371.ts#L400

Let me know if this helps you or if you have any questions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants