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

sendrawtransaction RPC error: {"code":-26,"message":"mandatory-script-verify-flag-failed (Invalid Schnorr signature)"} #2208

Open
dao0dev opened this issue Feb 6, 2025 · 0 comments
Assignees

Comments

@dao0dev
Copy link

dao0dev commented Feb 6, 2025

import * as ecc from 'tiny-secp256k1';
import * as bitcoin from "bitcoinjs-lib";
import * as cbor from 'cbor';
import { Wallet } from "./wallet";
import { Client } from "./client";
import { tweakSigner } from './utils';

bitcoin.initEccLib(ecc);

const SEND_UTXO_LIMIT = 100;

const TESTNET_FEERATE = 20;

export class Nft extends Client {
  collectionAddress: string;
  parentInscriptionTXID: string;
  
  constructor({ collectionAddress, parentInscriptionTXID }) {
    super();
    this.collectionAddress = collectionAddress;
    this.parentInscriptionTXID = parentInscriptionTXID;
  }

  async deploy(wallet: Wallet, { metadata, commonContentUrl,  itemOwnerAddress }) {
    const txidBuffer = Buffer.from(this.parentInscriptionTXID, 'hex');
    const inscriptionBuffer = txidBuffer.reverse();
    
    const metaProtocol: Buffer = Buffer.concat([Buffer.from("parcel.bitmap", "utf8")]);

    const pointer1: number = 546 * 1;
    const pointerBuffer1: Buffer = Buffer.from(pointer1.toString(16).padStart(4, '0'), 'hex').reverse();

    const contentBufferData: Buffer = this.contentBuffer(commonContentUrl);
    const contentBufferArray: Array<Buffer> = this.splitBuffer(contentBufferData, 400);

    const ordinal_script = this.createTapscript({
      metadata, 
      publicKey: wallet.internalPubkey,
      inscriptionBuffer,
      pointerBuffer1: pointerBuffer1,
      metaProtocol,
      contentBufferArray
    });

    const scriptTree = {
      output: ordinal_script,
    };

    const redeem = {
      output: ordinal_script,
      redeemVersion: 192,
    };

    const internalPubkey = wallet.internalPubkey;

    const ordinal_p2tr = bitcoin.payments.p2tr({
      internalPubkey,
      network: wallet.network,
      scriptTree,
      redeem,
    });

    let utxos, utxo, psbt;
    // utxos = await this.getUTXO(wallet.address);
    // utxo = utxos.find((utxo) => utxo.value > SEND_UTXO_LIMIT);
    // if (utxo === undefined) throw new Error("No btcs");
    // let redeemPsbt = wallet.redeemSendUTXOPsbt(utxo);
    // redeemPsbt = wallet.signPsbt(redeemPsbt);
    // let redeemFee = redeemPsbt.extractTransaction().virtualSize();
    // //4532b27cf67d922318ad473b601a67192ee212679e39e50130cdc517d3595c44
    // psbt = wallet.sendUTXOPsbt(utxo, redeemFee, ordinal_p2tr.address);
    // let signerOfPsbt = wallet.signPsbt(psbt)
    // const txHex = signerOfPsbt.extractTransaction().toHex();
    // await this.sendTransaction(txHex);

    // Получаем UTXO
    utxos = await this.getUTXO(ordinal_p2tr.address);

    // console.log(await this.waitUntilUTXO(ordinal_p2tr.address));
    if (!utxos || utxos.length === 0) {
      throw new Error("No UTXOs found.");
    }
  
    utxo = utxos.find((utxo: any) => utxo.value > SEND_UTXO_LIMIT);
    if (utxo === undefined) throw new Error("No btcs");
    psbt = new bitcoin.Psbt({ network: wallet.network });
    //tb1ppfqcpem2kd0l9dymat0uxdtv54cz4npty87yk2ctnj6vtk47zxjs9xac6d\


    //const parentInscriptionUTXOs = await this.getUtxos(this.collectionAddress);
    // console.log(this.collectionAddress, parentInscriptionUTXOs, "parentInscriptionUTXOs");
    // return;
    const parentInscriptionUTXO = {
      txid: this.parentInscriptionTXID,
      vout: 0,
      value: 526
    }
    psbt.addInput({
      hash: parentInscriptionUTXO.txid,
      index: parentInscriptionUTXO.vout,
      witnessUtxo: {
        value: parentInscriptionUTXO.value,
        script: wallet.output,
      },
      tapInternalKey: internalPubkey
    });

    psbt.addInput({
      hash: utxos[0].txid,
      index: utxos[0].vout,
      tapInternalKey: internalPubkey,
      witnessUtxo: { value: utxos[0].value, script: ordinal_p2tr.output! },
      tapLeafScript: [
        {
          leafVersion: redeem.redeemVersion,
          script: redeem.output,
          controlBlock: ordinal_p2tr.witness![ordinal_p2tr.witness!.length - 1],
        },
      ],
    });

    psbt.addOutput({
      address: itemOwnerAddress, //Destination Address
      value: 546,
    });

    psbt.addOutput({
      address: itemOwnerAddress, //Destination Address
      value: 546,
    });
    const fee = 1200000;

    const change = utxos[0].value - 546 * 1;
    console.log(change, 'change');

    const signer = tweakSigner(wallet, psbt);
    psbt.signInput(0, signer);
    psbt.signInput(1, wallet.keyPair);
    psbt.finalizeAllInputs()
    const tx = psbt.extractTransaction();
    console.log(tx.virtualSize())
    console.log(tx.toHex());
    return this.pushTransaction(tx.toHex());
  }
  
  contentBuffer = (content: string) => {
    return Buffer.from(content, 'utf8')
  }

  splitBuffer = (buffer: Buffer, chunkSize: number) => {
    let chunks = [];
    for (let i = 0; i < buffer.length; i += chunkSize) {
      const chunk = buffer.subarray(i, i + chunkSize);
      chunks.push(chunk);
    }
    return chunks;
  };

  createTapscript({
    metadata,
    publicKey,
    inscriptionBuffer,
    pointerBuffer1,
    metaProtocol,
    contentBufferArray
  }) {
    const metadataBuffer = cbor.encode(metadata);
    const childOrdinalStacks = [
      publicKey,
      bitcoin.opcodes.OP_CHECKSIG,
      bitcoin.opcodes.OP_FALSE,
      bitcoin.opcodes.OP_IF,
      Buffer.from("ord", "utf8"),
      1, 1,
      Buffer.concat([Buffer.from("text/plain;charset=utf-8", "utf8")]),
      1, 2,
      pointerBuffer1,
      1, 3,
      inscriptionBuffer,
      1, 5,
      metadataBuffer,
      1, 7,
      metaProtocol,
      bitcoin.opcodes.OP_0,
    ];

    contentBufferArray.forEach((item: Buffer) => {
      childOrdinalStacks.push(item)
    });
    childOrdinalStacks.push(bitcoin.opcodes.OP_ENDIF)
    console.log(childOrdinalStacks);
    return bitcoin.script.compile(childOrdinalStacks);
  }
}

export function tweakSigner(wallet: Wallet, opts: any = {}) {
  let privateKey: any = wallet.keyPair.privateKey;
  if (!privateKey) {
    throw new Error('Private key is required for tweaking signer!');
  }
  if (wallet.keyPair.publicKey[0] === 3) {
    privateKey = ecc.privateNegate(privateKey);
  }
  const tweakedPrivateKey = ecc.privateAdd(privateKey, tapTweakHash(wallet.internalPubkey, opts.tweakHash));
  if (!tweakedPrivateKey) {
    throw new Error('Invalid tweaked private key!');
  }
  return ECPair.fromPrivateKey(Buffer.from(tweakedPrivateKey), {
    network: wallet.network,
  });
}


function tapTweakHash(pubKey: Buffer, h: Buffer | undefined): Buffer {
  return bitcoin.crypto.taggedHash(
    "TapTweak",
    Buffer.concat(h ? [pubKey, h] : [pubKey])
  );
}```

my transaction 
```{
  "version": 2,
  "locktime": 0,
  "ins": [
    {
      "n": 0,
      "script": {
        "asm": "",
        "hex": ""
      },
      "sequence": 4294967295,
      "txid": "02cde20c6db772c9ddced410c52cb2bdcbf476016fa398cfa1ac5207f1ff462f",
      "witness": [
        "70a9429d26549e9c451f0ae1ea4855b7d7bbcd2f726179b72ca227c26c2833fad94026a13d25e23870d472a7013f395b3840b1fe6bcd86eb8d75189bf88eb951"
      ]
    },
    {
      "n": 0,
      "script": {
        "asm": "",
        "hex": ""
      },
      "sequence": 4294967295,
      "txid": "8de1ee52b9d8e22522e085ebdbbba4a445277be83c1c75b0cd4638c62acb283a",
      "witness": [
        "8a5a4bbcd7e41f664f467dc56cfcdb41d0c5fb46e4e0e57be30603b2cf54f53f600a92371070519e9fbce7c2519aea9cb3e6736cb84555ffe20c48233d1cd2c1",
        "206705021108c86f6f7249e85d233414afa8eeaadaea2bad863b2ccce504126879ac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d3801020222020103202f46fff10752aca1cf98a36f0176f4cbbdb22cc510d4ceddc972b76d0ce2cd02010528a264747970656b54657374204e46542023316b6465736372697074696f6e6954657374205465737401070d70617263656c2e6269746d6170003f68747470733a2f2f617277656176652e6e65742f4933326c517668673341514c583444632d334e48557773434e366e75382d6c78477352634e77653363723468",
        "c06705021108c86f6f7249e85d233414afa8eeaadaea2bad863b2ccce504126879"
      ]
    }
  ],
  "outs": [
    {
      "n": 0,
      "script": {
        "addresses": [],
        "asm": "OP_1 932a0391d2ec13cb8f303ded9297ece089f739b3ced40bc98eebdd277fdb9c9d",
        "hex": "5120932a0391d2ec13cb8f303ded9297ece089f739b3ced40bc98eebdd277fdb9c9d"
      },
      "value": 546
    },
    {
      "n": 1,
      "script": {
        "addresses": [],
        "asm": "OP_1 932a0391d2ec13cb8f303ded9297ece089f739b3ced40bc98eebdd277fdb9c9d",
        "hex": "5120932a0391d2ec13cb8f303ded9297ece089f739b3ced40bc98eebdd277fdb9c9d"
      },
      "value": 546
    }
  ],
  "hash": "d528bde069983f048ce011122f1c84cbfcfd4b7c4c0cfb7248871cb151daf2a3",
  "txid": "d528bde069983f048ce011122f1c84cbfcfd4b7c4c0cfb7248871cb151daf2a3"
}```
@jasonandjay jasonandjay self-assigned this Feb 19, 2025
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

2 participants