me-foundation / msigner

msigner is an open source Bitcoin Ordinals Partially Signed Bitcoin Transactions (PSBT) signer library. It supports atomic swap of the inscription and provides a simple and secure way to structure Bitcoin transactions for marketplaces.
Apache License 2.0
229 stars 76 forks source link

when i merge sellPsbt & buyerPsbt, i got error `mandatory-script-verify-flag-failed (Signature must be zero for failed CHECK(MULTI)SIG operation)` #15

Closed weixuefeng closed 9 months ago

weixuefeng commented 9 months ago

is there have some problem about my code?

async function sigleTransactionTest() {
  var buyerPriv = "d2dc8880c72520d61554cf3d90dcd54c98c0e04329c11c3c5b48e9a976ff4e29"
  var sellerPriv = "bd0003fcc25ac8c2dfaaa8413405156498114a9d1627486af8f5b27de35309f9"

  var seller = ECPair.fromPrivateKey(Buffer.from(sellerPriv, 'hex'))
  var buyerAddress = "n1EmAHAzvCgQYaKkGLm1e5taFauCiuNzHK";
  var buyer = ECPair.fromPrivateKey(Buffer.from(buyerPriv, 'hex'))
  var sellAddress = "miUpB4pCtoMSzgsv3k8XG69u6usGXhuPYi";

  // buyer
  const psbt = new bitcoin.Psbt({ network: bitcoin.networks.regtest });
  const sequence = 0xfffffffd;
   // first dummy
   psbt.addInput({
     hash: "1ce61e21ce48af9cce57c359936942126076380f839c3edf06f50384e08ce8d8",
     index: 3,
     sequence: sequence,
     sighashType: bitcoin.Transaction.SIGHASH_ALL,
     nonWitnessUtxo: Buffer.from("02000000046620cebc2814f3439c18d1e16d40d5abc9c9fddf0ff9e01b2ea75bee84547bd4000000006a47304402207f12a5882214abbacd5037494204094d67cde6aade644f8b3214cb64fd6d009d02204235515647a86c0904393cece98a3ae36190115be36c0fda444115648ee268e2012103b48aee4a6dec95c0e98e5dc31737bdd46b220b79f804a96d93eb06c1b71270effdffffff6620cebc2814f3439c18d1e16d40d5abc9c9fddf0ff9e01b2ea75bee84547bd4010000006b483045022100e73aa21a28217a580838c4ceb98ef73b6f51e85085eaf1d1fbd97cb51a97176b02203514668538ca5da8d8ef5b6938dfdf39719903b35bddca47691143d0f92cf255012103b48aee4a6dec95c0e98e5dc31737bdd46b220b79f804a96d93eb06c1b71270effdffffff02f55730a3dd6666b5d0035a3acbd9de4c6491086159aa1ab0793dfce9915b28000000006a47304402207835124ad8f85730fa8e11dcb80db616cef7a6efe508d85425cffcef0e92a4b9022060201b3eee52c47f482d09f502ad598928c69be643e20f5ba607db9f13501b08832102406cd7e5d61e4c27c7eddcb22af0019bd46c9250bd571bd750075678a84b3378fdffffff6620cebc2814f3439c18d1e16d40d5abc9c9fddf0ff9e01b2ea75bee84547bd4020000006b4830450221008d4dd48aa41d6c12f7927de691ca1e6d94756c7ed8ff1359d5a2ca17a24ace0702200d2855d7180a2a1ceb0625cd872290ea364b71b970d2a5f34a9705552be2801b012103b48aee4a6dec95c0e98e5dc31737bdd46b220b79f804a96d93eb06c1b71270effdffffff06b0040000000000001976a914d85290e927b8e20b1e48dbfeac9d8ea4c9a9955088ac22020000000000001976a914d85290e927b8e20b1e48dbfeac9d8ea4c9a9955088ac404b4c00000000001976a9142080c44ce0c1ad69e49b11e364b45848cf3da8cc88ac58020000000000001976a914d85290e927b8e20b1e48dbfeac9d8ea4c9a9955088ac58020000000000001976a914d85290e927b8e20b1e48dbfeac9d8ea4c9a9955088ac4c329a05000000001976a914d85290e927b8e20b1e48dbfeac9d8ea4c9a9955088ac00000000", "hex")
   });
   psbt.addInput({
    hash: "1ce61e21ce48af9cce57c359936942126076380f839c3edf06f50384e08ce8d8",
    index: 4,
    sequence: sequence,
    sighashType: bitcoin.Transaction.SIGHASH_ALL,
    nonWitnessUtxo: Buffer.from("02000000046620cebc2814f3439c18d1e16d40d5abc9c9fddf0ff9e01b2ea75bee84547bd4000000006a47304402207f12a5882214abbacd5037494204094d67cde6aade644f8b3214cb64fd6d009d02204235515647a86c0904393cece98a3ae36190115be36c0fda444115648ee268e2012103b48aee4a6dec95c0e98e5dc31737bdd46b220b79f804a96d93eb06c1b71270effdffffff6620cebc2814f3439c18d1e16d40d5abc9c9fddf0ff9e01b2ea75bee84547bd4010000006b483045022100e73aa21a28217a580838c4ceb98ef73b6f51e85085eaf1d1fbd97cb51a97176b02203514668538ca5da8d8ef5b6938dfdf39719903b35bddca47691143d0f92cf255012103b48aee4a6dec95c0e98e5dc31737bdd46b220b79f804a96d93eb06c1b71270effdffffff02f55730a3dd6666b5d0035a3acbd9de4c6491086159aa1ab0793dfce9915b28000000006a47304402207835124ad8f85730fa8e11dcb80db616cef7a6efe508d85425cffcef0e92a4b9022060201b3eee52c47f482d09f502ad598928c69be643e20f5ba607db9f13501b08832102406cd7e5d61e4c27c7eddcb22af0019bd46c9250bd571bd750075678a84b3378fdffffff6620cebc2814f3439c18d1e16d40d5abc9c9fddf0ff9e01b2ea75bee84547bd4020000006b4830450221008d4dd48aa41d6c12f7927de691ca1e6d94756c7ed8ff1359d5a2ca17a24ace0702200d2855d7180a2a1ceb0625cd872290ea364b71b970d2a5f34a9705552be2801b012103b48aee4a6dec95c0e98e5dc31737bdd46b220b79f804a96d93eb06c1b71270effdffffff06b0040000000000001976a914d85290e927b8e20b1e48dbfeac9d8ea4c9a9955088ac22020000000000001976a914d85290e927b8e20b1e48dbfeac9d8ea4c9a9955088ac404b4c00000000001976a9142080c44ce0c1ad69e49b11e364b45848cf3da8cc88ac58020000000000001976a914d85290e927b8e20b1e48dbfeac9d8ea4c9a9955088ac58020000000000001976a914d85290e927b8e20b1e48dbfeac9d8ea4c9a9955088ac4c329a05000000001976a914d85290e927b8e20b1e48dbfeac9d8ea4c9a9955088ac00000000", "hex")
  });
   // buyer receive dummy
   psbt.addOutput({
     script: bitcoin.address.toOutputScript(
       buyerAddress,
       bitcoin.networks.regtest
     ),
     value: DUMMY_UTXO_AMOUNT + DUMMY_UTXO_AMOUNT + 0,
   })
   // receive nft
   psbt.addOutput({
    script: bitcoin.address.toOutputScript(
      buyerAddress,
      bitcoin.networks.regtest
    ),
    value: 546,
  })
  // append seller input & output
  psbt.addInput({
    hash: "34f3d30981867dd331fa9189541a6bd79c7d493920b6fd6d814bab5503d19ac2",
    index: 0,
    sequence: sequence,
    sighashType: bitcoin.Transaction.SIGHASH_SINGLE | bitcoin.Transaction.SIGHASH_ANYONECANPAY,
    nonWitnessUtxo: Buffer.from("02000000000101900aa15c302425cd175fde300579fa715b3d1326294cfd4887da2fe206314df40200000000ffffffff0122020000000000001976a9142080c44ce0c1ad69e49b11e364b45848cf3da8cc88ac034017b5d4cf552470285c5abf3486e447c63c021bcaf0b6d2b88bef2655925cc6b325ca531e2df4eb42bb532002b98f4e0d1fe3d705e8032ecf2753e825614747692d0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38000968656c6c6f706f6e796821c1406cd7e5d61e4c27c7eddcb22af0019bd46c9250bd571bd750075678a84b337800000000", "hex"),
  });
  psbt.addOutput({
    script: bitcoin.address.toOutputScript(
      sellAddress,
      bitcoin.networks.regtest
    ),
    value: 5000000,
  });
  // -------------------------
  // add buyer payment utxo
  psbt.addInput({
    hash: "1ce61e21ce48af9cce57c359936942126076380f839c3edf06f50384e08ce8d8",
    index: 5,
    sequence: sequence,
    sighashType: bitcoin.Transaction.SIGHASH_ALL,
    nonWitnessUtxo: Buffer.from("02000000046620cebc2814f3439c18d1e16d40d5abc9c9fddf0ff9e01b2ea75bee84547bd4000000006a47304402207f12a5882214abbacd5037494204094d67cde6aade644f8b3214cb64fd6d009d02204235515647a86c0904393cece98a3ae36190115be36c0fda444115648ee268e2012103b48aee4a6dec95c0e98e5dc31737bdd46b220b79f804a96d93eb06c1b71270effdffffff6620cebc2814f3439c18d1e16d40d5abc9c9fddf0ff9e01b2ea75bee84547bd4010000006b483045022100e73aa21a28217a580838c4ceb98ef73b6f51e85085eaf1d1fbd97cb51a97176b02203514668538ca5da8d8ef5b6938dfdf39719903b35bddca47691143d0f92cf255012103b48aee4a6dec95c0e98e5dc31737bdd46b220b79f804a96d93eb06c1b71270effdffffff02f55730a3dd6666b5d0035a3acbd9de4c6491086159aa1ab0793dfce9915b28000000006a47304402207835124ad8f85730fa8e11dcb80db616cef7a6efe508d85425cffcef0e92a4b9022060201b3eee52c47f482d09f502ad598928c69be643e20f5ba607db9f13501b08832102406cd7e5d61e4c27c7eddcb22af0019bd46c9250bd571bd750075678a84b3378fdffffff6620cebc2814f3439c18d1e16d40d5abc9c9fddf0ff9e01b2ea75bee84547bd4020000006b4830450221008d4dd48aa41d6c12f7927de691ca1e6d94756c7ed8ff1359d5a2ca17a24ace0702200d2855d7180a2a1ceb0625cd872290ea364b71b970d2a5f34a9705552be2801b012103b48aee4a6dec95c0e98e5dc31737bdd46b220b79f804a96d93eb06c1b71270effdffffff06b0040000000000001976a914d85290e927b8e20b1e48dbfeac9d8ea4c9a9955088ac22020000000000001976a914d85290e927b8e20b1e48dbfeac9d8ea4c9a9955088ac404b4c00000000001976a9142080c44ce0c1ad69e49b11e364b45848cf3da8cc88ac58020000000000001976a914d85290e927b8e20b1e48dbfeac9d8ea4c9a9955088ac58020000000000001976a914d85290e927b8e20b1e48dbfeac9d8ea4c9a9955088ac4c329a05000000001976a914d85290e927b8e20b1e48dbfeac9d8ea4c9a9955088ac00000000", "hex")
  });
  // todo:// skip platform fee

  // new duumy utxo
  psbt.addOutput({
    script: bitcoin.address.toOutputScript(
      buyerAddress,
      bitcoin.networks.regtest
    ),
    value: DUMMY_UTXO_AMOUNT,
  })
  psbt.addOutput({
    script: bitcoin.address.toOutputScript(
      buyerAddress,
      bitcoin.networks.regtest
    ),
    value: DUMMY_UTXO_AMOUNT,
  })
  // change amount
  psbt.addOutput({
    script: bitcoin.address.toOutputScript(
      buyerAddress,
      bitcoin.networks.regtest
    ),
    value: 88000000,
  })

  const toSignInputs = [];
  psbt.data.inputs.forEach((v,index) => {
    let script = null;
    let value = 0;
    if(v.nonWitnessUtxo) {
      const tx = bitcoin.Transaction.fromBuffer(v.nonWitnessUtxo);
      const output = tx.outs[psbt.txInputs[index].index];
      script = output.script;
      value = output.value;
    }
    const isSigned = v.finalScriptSig || v.finalScriptWitness;
      if (script && !isSigned) {
        const address = bitcoin.address.fromOutputScript(script, bitcoin.networks.regtest);
        if (bitcoin.payments.p2pkh({ pubkey: buyer.publicKey, network: bitcoin.networks.regtest }).address === address) {
          toSignInputs.push({
            index,
            publicKey: buyer.publicKey,
            sighashTypes: [bitcoin.Transaction.SIGHASH_ALL]
          });
        }
      }
  })
  psbt.signInput(2, seller, [bitcoin.Transaction.SIGHASH_SINGLE | bitcoin.Transaction.SIGHASH_ANYONECANPAY])

  toSignInputs.forEach((input)=> {
    psbt.signInput(input.index, buyer,[bitcoin.Transaction.SIGHASH_ALL]);
    // psbt.finalizeInput(input.index)
  })

  console.log("MAP",JSON.stringify(psbt.data.globalMap))

  // create seller 
  const sellerPsbt = new bitcoin.Psbt({ network: bitcoin.networks.regtest });

  sellerPsbt.addInput({
    hash: "34f3d30981867dd331fa9189541a6bd79c7d493920b6fd6d814bab5503d19ac2",
    index: 0,
    sequence: sequence,
    sighashType: bitcoin.Transaction.SIGHASH_SINGLE | bitcoin.Transaction.SIGHASH_ANYONECANPAY,
    nonWitnessUtxo: Buffer.from("02000000000101900aa15c302425cd175fde300579fa715b3d1326294cfd4887da2fe206314df40200000000ffffffff0122020000000000001976a9142080c44ce0c1ad69e49b11e364b45848cf3da8cc88ac034017b5d4cf552470285c5abf3486e447c63c021bcaf0b6d2b88bef2655925cc6b325ca531e2df4eb42bb532002b98f4e0d1fe3d705e8032ecf2753e825614747692d0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38000968656c6c6f706f6e796821c1406cd7e5d61e4c27c7eddcb22af0019bd46c9250bd571bd750075678a84b337800000000", "hex"),
  });
  sellerPsbt.addOutput({
    script: bitcoin.address.toOutputScript(
      sellAddress,
      bitcoin.networks.regtest
    ),
    value: 5000000,
  });

  sellerPsbt.signInput(0, seller, [bitcoin.Transaction.SIGHASH_SINGLE | bitcoin.Transaction.SIGHASH_ANYONECANPAY])
  console.log("sell psbt:", sellerPsbt.toBase64())
  console.log("buyer psbt:", psbt.toBase64())
  psbt.data.inputs[2] = sellerPsbt.data.inputs[0]

  psbt.finalizeAllInputs()
  var res = psbt.extractTransaction().toHex()
  console.log("Res: ", res);
}

buy if i sign direct by psbt with seller on index 2. it is success. can you help me?