Learning Bitcoin Script/Miniscript and where to apply in a transaction

Partially Signed Bitcoin Transactions (PSBTs) are a standardized way of representing a Bitcoin transaction and its signatures, allowing different parties to collaborate in creating a transaction without sharing private keys. PSBTs are often used in multi-signature setups or with hardware wallets.

In this article we’ll discover more about how Bitcoin Script can be created with miniscript, where the hex of that script is placed in a transaction (psbt) and how it is reversed back to Bitcoin script for evaluation. If it evaluates to true, the spending condition is true, and funds can be spent. Also, check out Minsc

Both the locking script placed on an UTXO and the unlocking script are written in this scripting language. When a transaction is validated, the unlocking script in each input is executed alongside the corresponding locking script to see if it satisfies the spending condition.

Mastering Bitcoin 2nd Edition

For more on transactions check out “Mastering Bitcoin”

Transaction Serialization –inputs

Transaction serialization—outputs

You can also study Bitcoin free with the Saylor Academy

Encoding Bitcoin script in the PSBT inputs field

If you have a Bitcoin script that you want to include in a PSBT, you typically encode it within an input field of the PSBT. PSBTs have multiple fields for each input and output, and you would place your script in the appropriate input field.

Here’s a simplified example of how you might structure a PSBT:

{
  "psbt": "base64-encoded-partially-signed-transaction",
  "inputs": [
    {
      "non_witness_utxo": "base64-encoded-previous-transaction",
      "witness_utxo": {
        "amount": value_in_satoshis,
        "scriptPubKey": "hex-encoded-output-script"
      },
      "partial_sigs": {
        "public_key_1": "signature_1",
        "public_key_2": "signature_2"
      },
      "redeem_script": "hex-encoded-redeem-script",
      "witness_script": "hex-encoded-witness-script"
    },
    // More input fields...
  ],
  "outputs": [
    {
      "redeem_script": "hex-encoded-redeem-script",
      "witness_script": "hex-encoded-witness-script",
      "scriptPubKey": "hex-encoded-output-script",
      "partial_sigs": {
        "public_key_1": "signature_1",
        "public_key_2": "signature_2"
      }
    },
    // More output fields...
  ]
}

In this structure:

  • redeem_script: This is where your Bitcoin script would typically go for a P2SH (Pay-to-Script-Hash) input. If your script is a simple P2PKH (Pay-to-Public-Key-Hash), it might be in the scriptPubKey field directly.
  • witness_script: This is for the witness script, which is needed for certain script types like P2WSH (Pay-to-Witness-Script-Hash).
  • scriptPubKey: This is the locking script of the output.

In a PSBT, the redeem script for a multisig input is typically included in the redeem_script field within the corresponding input. Here’s an example:

In this example, the multisig redeem script is placed in the redeem_script field of the input. This is because for a multisig output, the redeem script is necessary to spend funds from that output. The partial_sigs field contains the partial signatures corresponding to the public keys involved in the multisig setup.

Multisig

Ensure that the scriptPubKey and witness_script fields are correctly set based on the type of multisig script you’re using. If it’s a P2WSH (Pay-to-Witness-Script-Hash) multisig, the witness_script field will be populated, and if it’s a P2SH multisig, the scriptPubKey field will have the corresponding script.

If you have a simple 2-of-2 multisig script where <K> represents a public key and <A> represents another public key, the corresponding redeem script might look something like this:

OP_2 <K> <A> OP_2 OP_CHECKMULTISIG

This script specifies that at least 2 out of the 2 provided public keys must produce valid signatures to spend the funds. The OP_CHECKMULTISIG opcode verifies the multiple signatures.

If you want to hex encode this script, you would convert the script into its hexadecimal representation. Assuming <K> and <A> are 33-byte compressed public keys, the hex-encoded script would look like this:

5221<K>21<A>52ae

Here:

  • 52 is the opcode for OP_2.
  • 21 is the length of the following data (33 bytes for a compressed public key).
  • <K> is the hex representation of the first public key.
  • 21 is the length of the following data (33 bytes for a compressed public key).
  • <A> is the hex representation of the second public key.
  • 52 is the opcode for OP_2.
  • ae is the opcode for OP_CHECKMULTISIG.

Make sure to replace <K> and <A> with the actual hex representations of your public keys. This is a basic example, and the exact structure may vary based on your specific needs and the tools or libraries you are using.

Here is an example with dummy data of a 2-of-2 multisig transaction

{
  "psbt": "cHNidP8BAFICAAAAAQAAAAqUA...base64-encoded-partially-signed-transaction...AgAAAAAB",
  "inputs": [
    {
      "non_witness_utxo": "0100000001...base64-encoded-previous-transaction...ffffffff",
      "witness_utxo": {
        "amount": 100000000,
        "scriptPubKey": "a9140123456789abcdef0123456789abcdef0123456787"
      },
      "partial_sigs": {
        "02374afd77a7fb123456789abcdef0123456789abcdef0123456789abcdef01": "30440220123456789abcdef0123456789abcdef0123456789abcdef0123456789012345678902204d123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01"
      },
      "redeem_script": "522103123456789abcdef0123456789abcdef0123456789210223456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0152ae",
      "witness_script": "",
      "hd_keypaths": {
        "02374afd77a7fb123456789abcdef0123456789abcdef0123456789abcdef01": "m/0/0",
        "03456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123": "m/0/1"
      }
    }
  ],
  "outputs": [
    {
      "redeem_script": "",
      "witness_script": "",
      "scriptPubKey": "a91456789abcdef0123456789abcdef0123456789abcdef87",
      "partial_sigs": {
        "02374afd77a7fb123456789abcdef0123456789abcdef0123456789abcdef01": "304502210123456789abcdef0123456789abcdef0123456789abcdef0123456789012345678902202c123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01"
      }
    }
  ]
}

Convert the ‘redeem script’ hex code back to Bitcoin script

Let’s break down the redeem script and convert it back into the corresponding Bitcoin script:

522103123456789abcdef0123456789abcdef0123456789210223456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0152ae

Breaking it down:

  • 52: OP_2 – This opcode pushes the number 2 onto the stack.
  • 21: 33-byte data follows.
  • 03123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01: This is the hex representation of the first public key.
  • 21: 33-byte data follows.
  • 0223456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01: This is the hex representation of the second public key.
  • 52: OP_2 – This opcode pushes the number 2 onto the stack.
  • ae: OP_CHECKMULTISIG – This opcode checks the signatures for the multisig script.

So, when converted to a Bitcoin script, this redeem script represents a 2-of-2 multisig setup where two public keys (03123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01 and 0223456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01) must produce valid signatures to spend the funds.

In simple terms, this script says that to spend the funds locked by this script, you need signatures from both public keys. It’s a basic form of a multisignature (multisig) script.

Put simply, scripts,however complex, just need to evaluate to TRUE

Miniscript allows a more human readable version on the Bitcoin script. Miniscript compiles to the same hex.

Links :

https://bitcoin.sipa.be/miniscript/

https://en.bitcoin.it/wiki/Script

https://github.com/bitcoin-core/btcdeb

jazz club