cmdruid / tapscript

A humble library for working with Tapscript and Bitcoin Transactions.
https://www.npmjs.com/package/@cmdcode/tapscript
Creative Commons Zero v1.0 Universal
196 stars 50 forks source link

Tap script inquiry #25

Closed 0xfinetuned closed 8 months ago

0xfinetuned commented 10 months ago

I want to create a specific transaction using tapscript similar to the ones shown in this example and I can't figure out how to put a 7 before the sig inside the witness. Can you please help ?

`const test = async () => { // Switch this to true to enable console output. const VERBOSE = true

    // Create a keypair to use for testing.
    const secret = '0a7d01d1c2e1592a02ea7671bb79ecd31d8d5e660b008f4b10e67787f4f24712'
    const seckey = utils.getSecretKey(secret)
    const pubkey = utils.getPublicKey(seckey, true)

    // Specify an array of scripts to use for testing.
    const scripts = [
        [ 7, 'OP_EQUALVERIFY', pubkey, 'OP_CHECKSIG' ],
        [ 8, 'OP_EQUALVERIFY', pubkey, 'OP_CHECKSIG' ],
    ]

    // Convert our array of scripts into tapleaves.
    const tree = scripts.map(s => Tap.encodeScript(s))

    if (VERBOSE) console.log('tree:', tree)

    // Pick one of our scripts as a target for spending.
    const index  = 0
    const script = scripts[index]
    const target = Tap.encodeScript(script)

    if (VERBOSE) console.log('target:', target)

    // Generate a tapkey that includes our tree. Also, create a merlke proof 
    // (cblock) that targets our leaf and proves its inclusion in the tapkey.
    const [ tpubkey, cblock ] = Tap.getPubKey(pubkey, { tree, target })

    // A taproot address is simply the tweaked public key, encoded in bech32 format.
    const address = Address.p2tr.fromPubKey(tpubkey, 'testnet')
    if (VERBOSE) console.log('Your address:', address)

    /* NOTE: To continue with this example, send 100_000 sats to the above address.
    You will also need to make a note of the txid and vout of that transaction,
    so that you can include that information below in the redeem tx.
    */ 

    const txdata = Tx.create({
        vin  : [{
            // Use the txid of the funding transaction used to send the sats.
            txid: '3a9e52dc01bdb22c4b36a0490d73b06392bccf635e06ba3913339e733e7ab9a2',
            // Specify the index value of the output that you are going to spend from.
            vout: 0,
            // Also include the value and script of that ouput.
            prevout: {
            // Feel free to change this if you sent a different amount.
            value: 10_000,
            // This is what our address looks like in script form.
            scriptPubKey: [ 'OP_1', tpubkey ]
            },
        }],
        vout : [{
            // We are leaving behind 1000 sats as a fee to the miners.
            value: 9_000,
            // This is the new script that we are locking our funds to.
            scriptPubKey: Address.toScriptPubKey('2MzcAVUFfHb1ni46AXCn5z7ADQKrKvz52nn')
        }]
    })

    // For this example, we are signing for input 0 of our transaction,
    // using the untweaked secret key. We are also extending the signature 
    // to include a commitment to the tapleaf script that we wish to use.
    const sig = Signer.taproot.sign(seckey, txdata, 0, { extension: target })

    // Add the signature to our witness data for input 0, along with the script
    // and merkle proof (cblock) for the script.
    txdata.vin[0].witness = [ sig, script, cblock ]

    // Check if the signature is valid for the provided public key, and that the
    // transaction is also valid (the merkle proof will be validated as well).
    const isValid = await Signer.taproot.verify(txdata, 0, { pubkey })

    if (VERBOSE) {
        console.log('Your txhex:', Tx.encode(txdata).hex)
        console.dir(txdata, { depth: null })
    }
}`
cmdruid commented 10 months ago

If you would like to add additional arguments to the witness, you can do so like this:

txdata.vin[0].witness = [ 7, sig, script, cblock ]

The script interpreter should convert the 7 into the proper opcode (it should convert to OP_7).

The Signer.taproot.verify method is unfortunately not capable of verifying arbitrary scripts, but you can verify the tx/signature and script/cblock separately.

The Tap.getPubKey method will verify the cblock before returning it, but you can also verify it independently using Tap.checkPath.

For verifying the signature, you can create the transaction hash using Signer.taproot.hash, and then check if the signature (and pubkey) is valid for that hash.

I hope this helps!

0xfinetuned commented 10 months ago

Yuup that helped thank you soo much.

Although this didn't work: txdata.vin[0].witness = [ 7, sig, script, cblock ] Had to do this: txdata.vin[0].witness = [ sig.hex, '07', script, cblock ]

There might be a bug in the script interpreter

cmdruid commented 10 months ago

I should have mentioned that the order of the arguments in the witness will depend on the order that the script consumes them.