You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
import*asbitcoinfrom"bitcoinjs-lib";import*aseccfrom"tiny-secp256k1";import{Taptree}from"bitcoinjs-lib/src/types";import{tapleafHash}from"bitcoinjs-lib/src/payments/bip341";functionmakeUnspendableInternalKey(provableNonce?: Buffer): Buffer{// This is the generator point of secp256k1. Private key is known (equal to 1)constG=Buffer.from("0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8","hex");// This is the hash of the uncompressed generator point.// It is also a valid X value on the curve, but we don't know what the private key is.// Since we know this X value (a fake "public key") is made from a hash of a well known value,// We can prove that the internalKey is unspendable.constHx=bitcoin.crypto.sha256(G);if(provableNonce){if(provableNonce.length!==32){thrownewError("provableNonce must be a 32 byte random value shared between script holders");}// Using a shared random value, we create an unspendable internalKey// P = H + int(hash_taptweak(provableNonce))*G// Since we don't know H's private key (see explanation above), we can't know P's private keyconsttapHash=bitcoin.crypto.taggedHash("TapTweak",provableNonce);constret=ecc.xOnlyPointAddTweak(Hx,tapHash);if(!ret){thrownewError("provableNonce produced an invalid key when tweaking the G hash");}returnBuffer.from(ret.xOnlyPubkey);}else{// The downside to using no shared provable nonce is that anyone viewing a spend// on the blockchain can KNOW that you CAN'T use key spend.// Most people would be ok with this being public, but some wallets (exchanges etc)// might not want ANY details about how their wallet works public.returnHx;}}exportclassTaprootMultisigWallet{privateleafScriptCache: Buffer|null=null;privateinternalPubkeyCache: Buffer|null=null;privatepaymentCache: bitcoin.Payment|null=null;privatereadonlypublicKeyCache: Buffer;network: bitcoin.Network;constructor(/** * A list of all the (x-only) pubkeys in the multisig */privatereadonlypubkeys: Buffer[],/** * The number of required signatures */privatereadonlyrequiredSigs: number,/** * The private key you hold. */privatereadonlyprivateKey: Buffer,/** * leaf version (0xc0 currently) */readonlyleafVersion: number,/** * Optional shared nonce. This should be used in wallets where * the fact that key-spend is unspendable should not be public, * BUT each signer must verify that it is unspendable to be safe. */privatereadonlysharedNonce?: Buffer){this.network=bitcoin.networks.bitcoin;constpubkey=ecc.pointFromScalar(privateKey);if(!pubkey)throw"Invalid Keys";this.publicKeyCache=Buffer.from(pubkey);// IMPORTANT: Make sure the pubkeys are sorted (To prevent ordering issues between wallet signers)this.pubkeys.sort((a,b)=>a.compare(b));}setNetwork(network: bitcoin.Network): this {this.network=network;returnthis;}// Required for Signer interface.// Prevent setting by using a getter.getpublicKey(): Buffer{returnthis.publicKeyCache;}/** * Lazily build the leafScript. A 2 of 3 would look like: * key1 OP_CHECKSIG key2 OP_CHECKSIGADD key3 OP_CHECKSIGADD OP_2 OP_GREATERTHANOREQUAL */getleafScript(): Buffer{if(this.leafScriptCache){returnthis.leafScriptCache;}constops=[];this.pubkeys.forEach((pubkey)=>{if(ops.length===0){ops.push(pubkey);ops.push(bitcoin.opcodes.OP_CHECKSIG);}else{ops.push(pubkey);ops.push(bitcoin.opcodes.OP_CHECKSIGADD);}});if(this.requiredSigs>16){ops.push(bitcoin.script.number.encode(this.requiredSigs));}else{ops.push(bitcoin.opcodes.OP_1-1+this.requiredSigs);}ops.push(bitcoin.opcodes.OP_GREATERTHANOREQUAL);this.leafScriptCache=bitcoin.script.compile(ops);returnthis.leafScriptCache;}getinternalPubkey(): Buffer{if(this.internalPubkeyCache){returnthis.internalPubkeyCache;}// See the helper function for explanationthis.internalPubkeyCache=makeUnspendableInternalKey(this.sharedNonce);returnthis.internalPubkeyCache;}getscriptTree(): Taptree{// If more complicated, maybe it should be cached.// (ie. if other scripts are created only to create the tree// and will only be stored in the tree.)return{output: this.leafScript,};}getredeem(): {output: Buffer;redeemVersion: number;}{return{output: this.leafScript,redeemVersion: this.leafVersion,};}privategetpayment(): bitcoin.Payment{if(this.paymentCache){returnthis.paymentCache;}this.paymentCache=bitcoin.payments.p2tr({internalPubkey: this.internalPubkey,scriptTree: this.scriptTree,redeem: this.redeem,network: this.network,});returnthis.paymentCache;}getoutput(): Buffer{returnthis.payment.output!;}getaddress(): string{returnthis.payment.address!;}getcontrolBlock(): Buffer{constwitness=this.payment.witness!;returnwitness[witness.length-1];}verifyInputScript(psbt: bitcoin.Psbt,index: number){if(index>=psbt.data.inputs.length)thrownewError("Invalid input index");constinput=psbt.data.inputs[index];if(!input.tapLeafScript)thrownewError("Input has no tapLeafScripts");consthasMatch=input.tapLeafScript.length===1&&input.tapLeafScript[0].leafVersion===this.leafVersion&&input.tapLeafScript[0].script.equals(this.leafScript)&&input.tapLeafScript[0].controlBlock.equals(this.controlBlock);if(!hasMatch)thrownewError("No matching leafScript, or extra leaf script. Refusing to sign.");}addInput(psbt: bitcoin.Psbt,hash: string|Buffer,index: number,value: number){psbt.addInput({
hash,
index,witnessUtxo: { value,script: this.output},});psbt.updateInput(psbt.inputCount-1,{tapLeafScript: [{leafVersion: this.leafVersion,script: this.leafScript,controlBlock: this.controlBlock,},],});}addDummySigs(psbt: bitcoin.Psbt){constleafHash=tapleafHash({output: this.leafScript,version: this.leafVersion,});for(constinputofpsbt.data.inputs){if(!input.tapScriptSig)continue;constsignedPubkeys=input.tapScriptSig.filter((ts)=>ts.leafHash.equals(leafHash)).map((ts)=>ts.pubkey);for(constpubkeyofthis.pubkeys){if(signedPubkeys.some((sPub)=>sPub.equals(pubkey)))continue;// Before finalizing, every key that did not sign must have an empty signature// in place where their signature would be.// In order to do this currently we need to construct a dummy signature manually.input.tapScriptSig.push({// This can be reused for each dummy signature
leafHash,// This is the pubkey that didn't sign
pubkey,// This must be an empty Buffer.signature: Buffer.from([]),});}}}// required for Signer interfacesign(hash: Buffer,_lowR?: boolean): Buffer{returnBuffer.from(ecc.sign(hash,this.privateKey));}// required for Signer interfacesignSchnorr(hash: Buffer): Buffer{returnBuffer.from(ecc.signSchnorr(hash,this.privateKey));}}
When I try to use above command, I get this error
"Can not sign for input #0 with the key 025321d780d099750e7fbcec128213184c9ae0a86241eeacc826c37bce7febc0b8"
Hope to get help about taproot multi-sig wallet
The text was updated successfully, but these errors were encountered:
I used above code for generate MultiSig Wallet
But after get PSBT, I can not sign use Unisat Wallet.
I tried to sign PSBT like below
When I try to use above command, I get this error
"Can not sign for input #0 with the key 025321d780d099750e7fbcec128213184c9ae0a86241eeacc826c37bce7febc0b8"
Hope to get help about taproot multi-sig wallet
The text was updated successfully, but these errors were encountered: