7

Is it possible to manually create a HTLC using the bitcoin-qt command line?

I know how to manually creating createrawtransaction/sendrawtransaction using the bitcoin-qt clients command line, and wanted to know how to manually create a HTLC.

When I say "manually", I mean, really getting your hands dirty, like using notepad to construct the contract, putting the values into bitcoin-cli, to convert it into a hash, ready for it to be sent using sendrawtransaction.

2 Answers 2

4
+300

I haven’t played the whole HTLC thing through, but was starting easy: try to create a redeem script, and the condition shall be, that a hash is checked with „equal verify“. Only play on testnet/regtest! My example below is on unixoide systems, at the command line with bitcoin in regtest mode. I have done this with "bitcoin_cli -version": v0.15.1 Attention, there has been changes in v0.16.

To begin, for the ScriptSig I simply ignore signatures, and just put the secret on the stack, followed by an OP_TRUE to fulfill the IF STATEMENT later on.

For the RedeemScript the following logic is used:

#  OP_IF
#   a9 14 <hash_lock> 87
#  OP_ELSE
#   76 a9 14 <pubkey_hash> 88 ac
#  OP_ENDIF

Which ends up in a pseudo serialized way a bit like this:

# 63 a914<hash_lock>88 67 76a914<pubkey_hash>88ac 68

And in my case into this redeem script: 63a9148103b0df9ad75e2b774f43d6e7e71eeaa2c73efb876776a9146a81e587585e58b07dce293a089894a0f8a61b8488ac68

The secret shall be:

#   426974636f696e5f72756c657321 
#     = ascii hex(Bitcoin_rules!)
#   SHA256(4269...7321) = bc6cee57412cfe8c26cbef4676d3af3182e2897ba482a8ce05ee407aebe76699
#   RIPEMD160(SHA256(4269...7321) = 8103b0df9ad75e2b774f43d6e7e71eeaa2c73efb

And here are all the variables I used to have:

b_cli="bitcoin-cli -regtest"
RedeemScriptHash=361801286b4be137f6a468ff8a34cfceb071763c
RedeemScriptAddress=2MxBFEWKRPBy96BCxmuZuXkz5CfivDg8e1a
Spend_TX_Value=9.99995000
Src_Address=mqE7Xm8vXYMx2T7qkCk5ggYuxwo8PHGT7k
Src_PrivKey=cT2KDnyWU7UoFdwVBgv682HAAPAVzpiHZ9YY54YRgFvUmXFr6L4n
Src_PubKey=02dae48354460f84dde299dbf35504056ae6cc500e2ef32b805cda1fdb1924a420
Src_PubKeyScript=76a9146a81e587585e58b07dce293a089894a0f8a61b8488ac
Tgt_Address=mtad6HurkyVUtZUWvWHuF2DQ6nQ7bodz19
Tgt_PubKeyScript=76a9148f4b44f4975751d7cf6a797e0818c353afbd3bb388ac
UTXO_ScriptPK=a914361801286b4be137f6a468ff8a34cfceb071763c87
UTXO_TXID=6a8764163bc493073c3f05f9ad37cb9fdfb8136d19263c8e4ae38d8e00cdf16b
UTXO_VOUT=0

I then created a funding transaction of 10 BTC to the redeem script address with this command, and generate a block:

$b_cli sendtoaddress $RedeemScriptAddress 10
$b_cli generate 1

CREATING TX

$b_cli createrawtransaction
'''[{\"txid\":\"'$UTXO_TXID'\",\"vout\":'$UTXO_VOUT'}]'''
'''{\"'$Tgt_Address'\":'$Spend_TX_Value'}''' 

02000000016bf1cd008e8de34a8e3c26196d13b8df9fcb37adf9053f3c0793c43b1664876a0000000000ffffffff0178b69a3b000000001976a9148f4b44f4975751d7cf6a797e0818c353afbd3bb388ac00000000

(gosh! these escape sequences drove me mad in the shell script ...)

SIGNING TX

$b_cli signrawtransaction $RAW_TX '''[{"txid": "'$UTXO_TXID'","vout": '$UTXO_VOUT',"scriptPubKey": "'$UTXO_ScriptPK'","redeemScript": "'$RedeemScript'"}]''' '''["'$Src_PrivKey'"]'''

{
  "hex": "02000000016bf1cd008e8de34a8e3c26196d13b8df9fcb37adf9053f3c0793c43b1664876a00000000343363a9148103b0df9ad75e2b774f43d6e7e71eeaa2c73efb876776a9146a81e587585e58b07dce293a089894a0f8a61b8488ac68ffffffff0178b69a3b000000001976a9148f4b44f4975751d7cf6a797e0818c353afbd3bb388ac00000000",
  "complete": false,
  "errors": [
    {
      "txid": "6a8764163bc493073c3f05f9ad37cb9fdfb8136d19263c8e4ae38d8e00cdf16b",
      "vout": 0,
      "witness": [
      ],
      "scriptSig": "3363a9148103b0df9ad75e2b774f43d6e7e71eeaa2c73efb876776a9146a81e587585e58b07dce293a089894a0f8a61b8488ac68",
      "sequence": 4294967295,
      "error": "Invalid OP_IF construction"
    }
  ]
}
BITCOIND Signed_TX:

02000000016bf1cd008e8de34a8e3c26196d13b8df9fcb37adf9053f3c0793c43b1664876a00000000343363a9148103b0df9ad75e2b774f43d6e7e71eeaa2c73efb876776a9146a81e587585e58b07dce293a089894a0f8a61b8488ac68ffffffff0178b69a3b000000001976a9148f4b44f4975751d7cf6a797e0818c353afbd3bb388ac00000000

Note that bitcoin_cli returned an error with an invalid OP_IF statement

The returned BITCOIND Signed_TX can be decomposed this way:

Version:       02000000
V_INs:         01
TX_ID:         6bf1cd008e8de34a8e3c26196d13b8df9fcb37adf9053f3c0793c43b1664876a
TX_Outpoint:   00000000
Len subscript: 34 
Len script:    33
  OP_IF:       63
               a914 8103b0df9ad75e2b774f43d6e7e71eeaa2c73efb87
  OP_ELSE:     67
               76a914 6a81e587585e58b07dce293a089894a0f8a61b84 88ac
  OP_ENDIF:    68
Sequence:      ffffffff
TX_OUT …:      0178b69a3b000000001976a9148f4b44f4975751d7cf6a797e0818c353afbd3bb388ac
LockTime:      00000000

Now fiddling with the length values, and bringing in the secret, I end up with:

PREPARING SigScript

02000000016bf1cd008e8de34a8e3c26196d13b8df9fcb37adf9053f3c0793c43b1664876a00000000440E426974636f696e5f72756c657321513363a9148103b0df9ad75e2b774f43d6e7e71eeaa2c73efb876776a9146a81e587585e58b07dce293a089894a0f8a61b8488ac68ffffffff0178b69a3b000000001976a9148f4b44f4975751d7cf6a797e0818c353afbd3bb388ac00000000

Version:       02000000
V_INs:         01
TX_ID:         6bf1cd008e8de34a8e3c26196d13b8df9fcb37adf9053f3c0793c43b1664876a
TX_Outpoint:   00000000
Len subscript: 44
The secret:    0E426974636f696e5f72756c657321     = ascii hex(Bitcoin_rules!)
  OP_TRUE:     51 
Len script:    33
  OP_IF:       63
               a914 8103b0df9ad75e2b774f43d6e7e71eeaa2c73efb87
  OP_ELSE:     67
               76a914 6a81e587585e58b07dce293a089894a0f8a61b84 88ac
  OP_ENDIF:    68
Sequence:      ffffffff
TX_OUT …:      0178b69a3b000000001976a9148f4b44f4975751d7cf6a797e0818c353afbd3bb388ac
LockTime:      00000000

SENDING TX

$b_cli sendrawtransaction $to_send_TX

afa97d910badda64e8e2498d822387f8aa75c1efad7eb78caa06e67a0103bb0f

bitcoin-cli -regtest getrawmempool

[ "afa97d910badda64e8e2498d822387f8aa75c1efad7eb78caa06e67a0103bb0f" ]

bitcoin-cli -regtest generate 1

[ "5a5ecf0f67074eff7ce10d633fee99256f1094657354ab5e832d5412157782b0" ]

And then verify results with:

$b_cli listunspent | grep $RedeemScriptAddress
$b_cli getreceivedbyaddress $Tgt_Address

Hint: The biggest „issue“ is to create the final tx before sending it. There you need to play with the length of the parts of the scriptsigs. Therefor I created some universally usable procedures to convert between hex and decimal in my .profile:

d2h() 
{ 
  echo "obase=16;$1" | bc
}

h2d() 
{ 
  echo "ibase=16;$1" | bc
}

len() 
{ 
  printf "$1" | wc -c
}

lenx() 
{ 
  my_len=$( len $1)
  d2h "$my_len / 2"
}

I hope this helps to see, how a manually constructed smart contract can be realized at the command line step by step. I used two important references, especially the answer sections:

How to redeem a basic Tx?

How does the ECDSA verification algorithm work during transaction?

6

The HTLC isn't really a specific type of bitcoin transaction, it is more a specific output script with the required spend conditions. Thus to make an HTLC, you really need to create a custom script which the bitcoin core command line tools don't really accomplish. You could construct the script by hand or perhaps find another tool which allows more easy script editing, perhaps btcdeb by kallewoof.

A HTLC script format can be found in BIP 199. There was also a PR opened to bitcoin core to add HTLC support to the wallet, but the author became too busy to continue maintaining that PR and it was never merged. It may interest you, you can find it here.

Let's show an example. Take your value "secret", and hash it with sha256 (you could use hash160 instead also). Then create the following script:

OP_IF
    OP_SHA256 hash_of_secret OP_EQUALVERIFY OP_DUP OP_HASH160 your_pubkey            
OP_ELSE
    timeout OP_CSV OP_DROP OP_DUP OP_HASH160 their_pubkey
OP_ENDIF
OP_EQUALVERIFY
OP_CHECKSIG

To spend, the first value on the stack is true or false to pick which branch of the if statement to evaluate. If you are the seller who has the preimage of the secret hash, you use the top branch and reveal the secret and sign the transaction to spend the output instantly. Otherwise, if the deal falls through or whatever, they can redeem their funds again without revealing the secret after a certain amount of time. If the CSV with the timeout (you could use CLT instead) is successful, meaning the time has passed, they redeem it with just their signature on the transaction, and don't require the preimage of the hash.

It is relatively trivial to convert this script to an actual hex encoded script, simply by looking up the correct hex representation of each op_code and using the correct push opcodes for the length of each piece of data you are pushing (pubkeys, secret hash, etc)

4
  • 1
    I guess that is what I am after, how to create a HTLC script by hand. May 7, 2018 at 10:23
  • You don't have to create it; it already exists and is defined in BIP 199. May 7, 2018 at 19:43
  • @MeshCollider, Regarding your last paragraph. I have been trying to convert that script to an actual bex encoded script by looking up the correct hex representations of each op_code, but so far I have been unsuccessful. Could you please give me an example (testnet) hex so I can work backwards from it? May 8, 2018 at 9:04
  • @oshirowanen what do you mean unsuccessful, what issue have you encountered? May 8, 2018 at 13:20

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.