ChrisCho-H / bitcoin-sdk-js

Bitcoin TypeScript/JavaScript Library for NodeJS, Browser and Mobile. Segwit & Taproot support.✨
MIT License
32 stars 4 forks source link

Transaction for TimeLock (or || and) HashLock: Script failed an OP_EQUALVERIFY operation #5

Closed omariosman closed 1 month ago

omariosman commented 1 month ago

The issue

I am trying to create Transaction for TimeLock (or || and) HashLock without Signer(P2SH, P2WSH) However when I try to broadcast the tx to https://blockstream.info/testnet/tx/push it gives me this error: sendrawtransaction RPC error: {"code":-26,"message":"mandatory-script-verify-flag-failed (Script failed an OP_EQUALVERIFY operation)"}

Can you inspect what is wrong with my script?

Tx raw:

010000000168f1479d7e6b2b67fde3c227324f18d93a5b0620a11fb872863891fe4e35c0ab0000000030015103abcdef2903a4bd2db175aa204533a01d26697df306b3380e08f4fae30f488d2985e6449e9bd9bd86849ddbc688fdffffff01d40200000000000017a9145b107712b126b2bedc82ddfd6673c1f946f741d387a4bd2d00

Tx decoded:

{
    "txid": "52458ff3d982120e8067ccf7214852b6b1e51772f4f16183ca9609699f169a93",
    "hash": "52458ff3d982120e8067ccf7214852b6b1e51772f4f16183ca9609699f169a93",
    "version": 1,
    "size": 131,
    "vsize": 131,
    "weight": 524,
    "locktime": 2997668,
    "vin": [
        {
            "txid": "abc0354efe91388672b81fa120065b3ad9184f3227c2e3fd672b6b7e9d47f168",
            "vout": 0,
            "scriptSig": {
                "asm": "81 -7327147 03a4bd2db175aa204533a01d26697df306b3380e08f4fae30f488d2985e6449e9bd9bd86849ddbc688",
                "hex": "015103abcdef2903a4bd2db175aa204533a01d26697df306b3380e08f4fae30f488d2985e6449e9bd9bd86849ddbc688"
            },
            "sequence": 4294967293
        }
    ],
    "vout": [
        {
            "value": 0.00000724,
            "n": 0,
            "scriptPubKey": {
                "asm": "OP_HASH160 5b107712b126b2bedc82ddfd6673c1f946f741d3 OP_EQUAL",
                "desc": "addr(39zX91yFegz6N5NfewGMua2ydWrsDHtKcb)#5p7pu9up",
                "hex": "a9145b107712b126b2bedc82ddfd6673c1f946f741d387",
                "address": "39zX91yFegz6N5NfewGMua2ydWrsDHtKcb",
                "type": "scripthash"
            }
        }
    ]
}

Script:

import * as bitcoin from 'bitcoin-sdk-js';

// initialize Bitcoin Transaction object
const tx = new bitcoin.Transaction();
const txId = "abc0354efe91388672b81fa120065b3ad9184f3227c2e3fd672b6b7e9d47f168";
const value = 1000;
const BLOCK_HEIGHT = 2997668;
const fee = 276;
// add UTXO to spend as an input
await tx.addInput({
  txHash: txId, // transaction id of utxo
  index: 0, // index of utxo in transaction
  value: value, // value of utxo(unit is satoshi)
} as bitcoin.UTXO);

// add Target output to send Bitcoin
// sends bitcoin to script with timelock + hashlock (no sig required)
await tx.addOutput({
    address: await bitcoin.address.generateScriptAddress(
        // if you only want hashlock, you can remove generateTimeLockScript
        (await bitcoin.script.generateTimeLockScript(BLOCK_HEIGHT)) +
        (await bitcoin.script.generateHashLockScript("abcdef")),   
        "legacy",
        "testnet"
    ),
    value: value - fee, // value of utxo - fee
} as bitcoin.Target);

// if transaction use timelock input, must set tx locktime bigger than input timelock
await tx.setLocktime(BLOCK_HEIGHT);

// if input utxo requires to unlock hash with smart contract(p2wsh or p2sh)
await tx.unlockHashInput(
    "abcdef",
    0,
    'legacy', // default is segwit, you might use legacy if necessary
    await bitcoin.script.generateTimeLockScript(BLOCK_HEIGHT),// is timelock input? provide script
);

// You can broadcast signed tx here: https://blockstream.info/testnet/tx/push
const txToBroadcast: string = await tx.getSignedHex();
console.log(`${txToBroadcast}`);
ChrisCho-H commented 1 month ago

You are trying to spend single-sig(p2pkh) utxo, but with unlockHashInput (https://blockstream.info/testnet/tx/abc0354efe91388672b81fa120065b3ad9184f3227c2e3fd672b6b7e9d47f168?expand). The code example assumes that your input to spend is utxo which is built in a same way of output to send(below one).

// add Target output to send Bitcoin
// sends bitcoin to script with timelock + hashlock (no sig required)
await tx.addOutput({
    address: await bitcoin.address.generateScriptAddress(
        // if you only want hashlock, you can remove generateTimeLockScript
        (await bitcoin.script.generateTimeLockScript(BLOCK_HEIGHT)) +
        (await bitcoin.script.generateHashLockScript("abcdef")),   
        "legacy",
        "testnet"
    ),
    value: value - fee, // value of utxo - fee
} as bitcoin.Target);

So after send to the address of above, then try unlockHashInput.

omariosman commented 1 month ago

Thank you so much for clearing up the confusion @ChrisCho-H It worked!

ChrisCho-H commented 1 month ago

Pleasure :)