bitcoinjs-lib icon indicating copy to clipboard operation
bitcoinjs-lib copied to clipboard

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

Open IvanKodak opened this issue 1 year ago • 10 comments
trafficstars

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

IvanKodak avatar Jan 11 '24 16:01 IvanKodak

I would also like to know about this.

leo42 avatar Jan 17 '24 14:01 leo42

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>);

leo42 avatar Jan 19 '24 15:01 leo42

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.

Andrej656 avatar Jan 24 '24 12:01 Andrej656

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 avatar Jan 26 '24 15:01 IvanKodak

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?

IvanKodak avatar Jan 26 '24 18:01 IvanKodak

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)

leo42 avatar Jan 31 '24 17:01 leo42

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.

IvanKodak avatar Feb 01 '24 08:02 IvanKodak

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.

leo42 avatar Feb 01 '24 13:02 leo42

@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?

IvanKodak avatar Feb 01 '24 13:02 IvanKodak

@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

jasonandjay avatar Apr 30 '24 09:04 jasonandjay