0

I am trying to spend a P2SH UTXO based on a redeem script that requires no signatures and simply needs the correct input to equal the OP_HASH160 digest in the corresponding P2SH locking script.

My unlocking script pushes 17 bytes onto the stack (0x11) which are the following 17 bytes that constitute the "secret" number to get the correct hash.

The decoded raw tx is the following (with the 17 byte secret number removed):

"txid": "0ba564fc00cbeada33ccded7f4e45f17bfff69c8f8a38132dc715de0d905b857",
"hash": "0ba564fc00cbeada33ccded7f4e45f17bfff69c8f8a38132dc715de0d905b857",
"version": 1,
"size": 113,
"vsize": 113,
"weight": 452,
"locktime": 0,
"vin": [
  {
    "txid": "f8e5dfe32a57872d58cee6f3eb32ec0277f3563306ae623109ea651fd9dc8087",
    "vout": 1,
    "scriptSig": {
      "asm": "<17 byte redeem script>",
      "hex": "11<17 byte redeem script>"
    },
    "sequence": 4294967295
  }
],
"vout": [
  {
    "value": 0.00002000,
    "n": 0,
    "scriptPubKey": {
      "asm": "02de153317307164e7c9918791c7787d9833a3a8201bdff880e631e490cf9a087c      OP_CHECKSIG",
      "desc": "pk(02de153317307164e7c9918791c7787d9833a3a8201bdff880e631e490cf9a087c)#epafp6sh",
      "hex": "2102de153317307164e7c9918791c7787d9833a3a8201bdff880e631e490cf9a087cac",
      "type": "pubkey"
    }
  }
]
}

I can't see any mistakes however "sendrawtransaction" gives me the error: error code: -26 error message: mandatory-script-verify-flag-failed (Opcode missing or not understood)

I had thought maybe this was a non-standard tx and therefore wouldn't be valid unless submitted directly to miner*, however it was my understanding that P2SH allowed the freedom to submit arbitrary redeem scripts, provided the unlocking script is valid and the redeem script equals the redeem script hash in the locking script.

*bitcoin-cli decodescript for my unlocking script hex does give me Type:nonstandard

Peter
  • 115
  • 5

1 Answers1

1

A p2sh unlocking script first pushes the actual script, then any stack element needed to fulfill this script. The interpreter will pop one element from the stack and verify if it's hash160 is equal to the one committed in the spk. If yes, then the interpreter destroys the runtime, and build a new one, but using the popped element as script instead.
In your case, bitcoind is interpreting your secret as a script, and it isn't a valid script. Hence, yielding an error about opcodes. The correct way of doing this with p2sh is by taking the hash160 of the hex-encoded script:

OP_HASH160 OP_PUSH20 <your_hash> OP_EQUALVERIFY

Your program hash is the resulting digest.
When spending, you first push this script, and your secret as a second stack element. Your initial stack looks like this: <script> <secret>

Davidson Souza
  • 1,056
  • 3
  • 11
  • I don't follow. Are you saying that my redeem script should have the 0x11 included? So the entire unlocking script would be <11secret key>? Also it's not clear to me why 11 wouldn't just push onto the stack, and then the HASH_160 on the unlocking script would pop this off and compare result to script hash. In my case there is no "script" on the unlocking side, I just want to push the redeem script/number on the stack, and be evaluated by the locking script. – Peter Feb 06 '23 at 04:23
  • Appears I have some confusion with regards to the push byte opcodes. For example this P2SH UTXO https://mempool.space/tx/6f90c7fdcd70a30606ce477d6ebe30280475f48bd668a7db8fef61ef532c7d3a , the redeem script is 00140e90dbfd847487b28ce26055b11826116d040090 and the full unlocking script is 1600140e90dbfd847487b28ce26055b11826116d040090. So 0x16 push 22 bytes onto stack, but then the redeem script also has push 0x14 bytes onto stack (when it is already on stack?) – Peter Feb 06 '23 at 04:30
  • Following that logic, should my unlocking script be: 12<11secret key>? – Peter Feb 06 '23 at 04:33
  • What you are missing is that p2sh isn't just the explicit script. If you see the sh template, you make an extra step that is not in the script where the first element is popped out from the stack and used as scrip. Otherwise, you wouldn't run the actual script, just the p2sh template. Take a look at bip 16 to more details. And yes OP_PUSH18 <script> where script is <OP_PUSH17 <secret>. But I think this violates the clean stack rule. – Davidson Souza Feb 06 '23 at 11:32
  • Alright, I didn't realize P2SH has a different script execution logic, although BIP16 doesn't lay out how this works precisely. Is it correct to say with a P2SH locking script, this somehow triggers a different execution logic, where in the locking script, one must push n bytes of redeem script onto stack, the stack is the copied, the redeem script executed (where 1 must be left on top of stack at end). If 1 on top then stack is replaced by redeem script, where the locking script is executed to compare the hash? – Peter Feb 07 '23 at 02:35
  • So in my case the unlocking script was never going to work because script was trying to execute my redeem script, which didn't have valid opcodes. However it would work if I had some unlocking script where the redeem script had some logic including the magic number, where it evaluated to true. But ofc changing the redeem script changes the hash, so that locking script can't be unlocked. – Peter Feb 07 '23 at 02:39
  • About your first comment, the first pass (the one checking if script hash is ok) ends with a clean stack. OP_EQUALVERIFY doesn't push OP_TRUE into the stack, it fails in case of not equal. If the script hash doesn't match, it fails right away. You only run the actual script if this initial check pass. And yes, these funds are lost forever because you need a valid script with the same hash as this one, but this is a collision against hash160. – Davidson Souza Feb 07 '23 at 14:50
  • So the P2SH execution flow in my example is: 1. Run unlocking script, push 0x11 bytes to stack (ends with redeem script on stack) and stack copied 2. Run locking script, redeem script popped from stack and locking script verifies hash (ending with 1 on stack as OP_EQUAL not OP_EQUALVERIFY) 3. Prior copied stack (contained redeem script) popped off and executed as script. Script fails as magic number contains invalid opcodes. Thanks for your help. – Peter Feb 07 '23 at 20:34