0

I got inspired by this thread:

Single signature P2SH redeem script

Assume I want to "hide" my address as long as can be. So I want a P2SH tx, which only shows a redeemscript (hash of my pubkey) on the blockchain until spent. I try to go analog to a multisig, but use just the pubkey as redeemscript. The funding tx would hash160 the public key, and use it as redeem script. This would be a payment to a "type 3 address", with this code in the "tx_out pkscript".

A9: OP_HASH160
14: OP_Data14 (= decimal 20 and the redeem hash)
87: OP_EQUAL

Would the spending tx have this in it's scriptsig?

<sig><pubkey><OP_CHECKSIG>

(instead of the way it is done in multisigs: <sig><pubkey 1-n><OP_CHECKMULTISIG>)

Looking what happens on the stack:

sig
pubkey
<OP_CHECKSIG>

<OP_HASH160>
redeem hash
<OP_Equal>

For this to work, the <OP_HASH160> opcode would have to know, that the pubkey and the <OP_CHECKSIG> would need to be hashed together (to be compared to the redeemscript hash which follows for <OP_Equal>) ...

I think the same question applies to multisig. There is just two or more pubkeys followed by <OP_CHECKMULTISIG> (instead of <OP_CHECKSIG>).

1 Answer 1

1

Now I understand, why there was no answer - it gets really long :-)

So I answer it by myself. I am with bitcoin core (getinfo) version 150100, on OpenBSD (ksh) and OSX (bash v3):

I solved it on testnet, and got the tx to go through. Some information from @nick here:
Does OP_HASH160 consume the top stack element?

I was wondering, if this holds true:

For this to work, the <OP_HASH160> opcode would have to know, that the pubkey and the <OP_CHECKSIG> would need to be hashed together

Obviously it does. Here is the whole cycle... First I try to have a look at the stack with input and output scripts (as per link above):

sigscript:     <sig> <redeem script> 
pubkey script: OP_HASH160 <RedeemScriptHash> OP_EQUAL

Based on the link above and looking at the stack, it would run through these steps:

  1. Input SigScript (push data on stack) STACK:

  2. Execute pubkey script a. this would first push first OPCODE of pubkey script on stack and execute STACK: OP_HASH160

    b. this command consumes top stack element, and replaces with hash STACK:

    c. the next elements of the pubkey script is pushed to stack STACK: OP_EQUAL

  3. OP_Equal verifies top two stack elements, if untrue --> invalid

  4. If true, script ends with "true" on top of stack STACK:

  5. verify if script is P2SH (... ok, it is P2SH ...)

  6. clean the stack, and run sigscript again STACK:

  7. pop top stack element, and run it as script a. the top element of the redeem script is OP_CHECKSIG STACK: OP_CHECKSIG

    b. CHECKSIG would verify pubkey and sig, and return true ...

To see if this is correct, assemble a funding tx on testnet/regtest. When using coinbase tx (mined block), need >100 confirmations! Start with creation of a redeemscript and address for this tx:

NEWADDRESS=$( bitcoin-cli -regtest getnewaddress )
echo $NEWADDRESS
PUBKEY=$( bitcoin-cli -regtest validateaddress $NEWADDRESS | awk -F '\"' '/pubkey/ { print $4 }' )
REDEEMSCRIPT=$( printf "21%sac" $PUBKEY )
# creating the hash of the REDEEMSCRIPT
printf $( echo $REDEEMSCRIPT | sed 's/[[:xdigit:]]\{2\}/\\x&/g' ) > tmp_fn.hex
openssl dgst -sha256 -binary <tmp_fn.hex >tmp_sha256.hex
REDEEMSCRIPTHASH=$( openssl dgst -ripemd160 <tmp_sha256.hex | cut -d " " -f2 )
# convert redeemscripthash to bitcoin address (testnet!), with "C4" Prefix
PRFX_REDEEMSCRIPT=$( printf "c4%s" $REDEEMSCRIPTHASH )
# double sha256 the hex value
printf $( echo $PRFX_REDEEMSCRIPT | sed 's/[[:xdigit:]]\{2\}/\\x&/g' ) > tmp_fn.hex
openssl dgst -sha256 -binary <tmp_fn.hex >tmp_sha256.hex
openssl dgst -sha256 <tmp_sha256.hex | cut -d " " -f2 >tmp_dsha256.txt
# checksum and append
result=$( cat tmp_dsha256.txt | cut -b 1-8 )
result=$( printf "%s%s" $PRFX_REDEEMSCRIPT $result | tr "[:lower:]" "[:upper:]" )
# and base58
base58str="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
REDEEMSCRIPTADDR=$( dc -e "16i $result [3A ~r d0<x]dsxx +f" | while read -r n; do 
    j=$(( n + 1 ))
    echo $base58str | cut -b $j 
  done | tr -d '\n' ) 
bitcoin-cli -regtest importaddress "$REDEEMSCRIPTADDR" p2sh 
bitcoin-cli -regtest importaddress "$REDEEMSCRIPTADDR" rescan
bitcoin-cli -regtest validateaddress "$REDEEMSCRIPTADDR"
bitcoin-cli -regtest listunspent

The redeemscript's address would start with a "2" on testnet. Now create the funding tx. Search for an UTXO, that we can use, and note down tx and vout.

UTXO_TXID=d260e120647360dcfc8606e242c6389acece3ee585d5e0d76a26b87b08b322e0
UTXO_VOUT=0
VALUE=49.9997
RAW_TX=$( bitcoin-cli -regtest createrawtransaction '''[{"txid":"'$UTXO_TXID'","vout":'$UTXO_VOUT'}]''' '''{"'$REDEEMSCRIPTADDR'":'$VALUE'}''' )
bitcoin-cli -regtest decoderawtransaction $RAW_TX
SIGNED_TX=$( bitcoin-cli -regtest signrawtransaction $RAW_TX | awk -F '\"' '{ print $4 }' )
bitcoin-cli -regtest decoderawtransaction $SIGNED_TX
UTXO_TXID=$( bitcoin-cli -regtest sendrawtransaction $SIGNED_TX )
bitcoin-cli -regtest getrawmempool
bitcoin-cli -regtest generate 1

# verify new address has funds:
bitcoin-cli -regtest validateaddress "$REDEEMSCRIPTADDR"
bitcoin-cli -regtest getreceivedbyaddress "$REDEEMSCRIPTADDR"

At this point, the funding transaction is sent. Now the spending tx, we need a new address to receive the funds from the P2SH address:

RCVADDRESS=$( bitcoin-cli -regtest getnewaddress )
echo $RCVADDRESS

# verify that tx from $REDEEMSCRIPTADDR has funds
RAW_TX=$( bitcoin-cli -regtest getrawtransaction $UTXO_TXID )
bitcoin-cli -regtest decoderawtransaction $RAW_TX
# to send funds from $REDEEMSCRIPTADDR to a new address, need the privkey:
NEWADDRESS_PRIVKEY=$( bitcoin-cli -regtest dumpprivkey $NEWADDRESS )

create the raw tx, sign it, and send it (maybe the amount needs to be updated):

UTXO_VOUT=0
UTXO_OUTPUT_SCRIPT=$( bitcoin-cli -regtest validateaddress "$REDEEMSCRIPTADDR" | awk -F '\"' '/scriptPubKey/ { print $4 }' )
VALUE=7.7699
RAW_TX=$( bitcoin-cli -regtest createrawtransaction '''[{"txid":"'$UTXO_TXID'","vout":'$UTXO_VOUT'}]''' '''{"'$RCVADDRESS'":'$VALUE'}''' )
bitcoin-cli -regtest decoderawtransaction $RAW_TX
SIGNED_TX=$( bitcoin-cli -regtest signrawtransaction $RAW_TX '''[{"txid": "'$UTXO_TXID'","vout": '$UTXO_VOUT',"scriptPubKey": "'$UTXO_OUTPUT_SCRIPT'","redeemScript": "'$REDEEMSCRIPT'"}]''' '''["'$NEWADDRESS_PRIVKEY'"]''' | awk -F '\"' '{ print $4 }' )
UTXO_TXID=$( bitcoin-cli -regtest sendrawtransaction $SIGNED_TX )
bitcoin-cli -regtest getrawmempool
bitcoin-cli -regtest generate 1

At this point I could see, that my newly generated RCVADDRESS contained the value (bitcoin-cli -regtest listunspent | grep -A5 -B5 $RCVADDRESS):

... "amount": 7.76990000

I had one smaller observation, which is not yet fully clear to me: the scriptsig was changed in the final tx. It had the signature, and behind should follow the redeemscript. This redeemscript was "extended" in this way:

23210355ef94c6e097752f303d685e4011b9dba8c1cd1382f8fa68bf2de0d523f18fffac

The 23 at the beginning would be the length indicator for the following pubkey, but this would include "ac"... Not sure, why bitcoin core extends it this way.

Now that I got it to work with simple P2SH, I can try SegWit-P2SH :-)

1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.