Tutorial: Hashed Timelocked Contract (HTLC)

Some time ago George published his work on BTC Atomic Swaps via HTLC: Cope Edition. I’ve been wanting to look at the code and create the necessary tools to interact with the contract.

For this I’ve recently published the C# version of the Zenon CLI and created a htlc branch for interacting with George’s code changes.

In order to make it more accessible for everyone I also created a tutorial I believe even the most non-tech savvy among us can follow.

The tutorial can be found here Tutorial: Hashed Timelocked Contact (HTLC)

Have fun and let me know what you think.


what do the following variable do?

“all your znn belong to us”

also, how does Alice reclaim her ZNN after an HTLC expires and Bob does not claim the funds? I want to try that scenario.

1 Like

wtf lol

Hi 0x3639,

first off, you can get a list of all commands by typing in znn-cli.exe or a detailed help of the command by typing in znn-cli.exe [command].

For example, to get the detailed help info for the htlc.create command, just type: znn-cli.exe htlc.create.

This will give you the following help info:

-p, --passphrase                        Use this passphrase for the keyStore or enter it manually in a secure way
-k, --keyStore                          Select the local keyStore (defaults to "available keyStore if only one is present")
-i, --index                             (Default: 0) Address index
-n, --netId                             (Default: 1) Specify the network id to use
-u, --url                               (Default: ws:// Provide a websocket znnd connection URL with a port
--help                                  Display this help screen.
--version                               Display version information.
hashLockedAddress (pos. 1)              Required.
tokenStandard (pos. 2) [ZNN/QSR/ZTS]    Required.
amount (pos. 3)                         Required.
expirationTime (pos. 4)                 Required. Total seconds from now
hashType (pos. 5)                       Required. 0 = SHA3-256
keyMaxSize (pos. 6)                     Required.
preimage (pos. 7)

So, the last three arguments mean:

0 = hashType (currently only hash type 0 is supported)
32 = keyMaxSize (determines the maximum pre-image size).
“all your znn belong to us” = pre-image (currently only tekst is supported. Looking to add option to add base64)

If the lock is expired Alice can reclaim her funds by calling.

./znn-cli htlc.reclaim [hash id]

and receive all unreceived transactions, just like Bob did in the toturial.

Hope this answers your question.

Yes, that is helpful. wanted to confirm the command would be

./znn-cli htlc.reclaim [hash id] -k Alice -n 321

I tried this after the contract expired and got the following error:

Error data bib existent

Then I ran the following command

./znn-cli htlc.timeLocked -k Alice -n 321

And the expired contract was not there. I can see the ZNN is not in Bob or Alice’s wallets. Any thoughts?

Do you have any unreceived transactions?

./znn-cli unreceived -k Alice -n 321

yes. Looks like the hash has changed. Does that seem right?

Unreceived 100.00000000 tZNN from z1qxemdeddedxhtlcxxxxxxxxxxxxxxxxxygecvw. Use the hash 5da40a52b824efe63f87d2784e84569a0d20639fd7426c2aac72b32358241c7d to receive

Seems like you reclaimed the htlc. The contract send the funds back and creates an transaction.

But you have to receive it first to get the funds in your wallet.

./znn-cli receiveAll -k Alice -n 321

I see. that received it. thanks. I’m going to write up a few additional test scenarios. thx!

1 Like

I’ve made some changes to the CLI and added scenario 2 for creating an atomic swap.

The changes include:

  • added createHash command
  • added htlc.inspect command
  • added htlc.monitor command
  • added htlc.monitorAll command
  • removed preimage argument from htlc.create command
  • added hashLock argument to htlc.create command
  • improved formatting

Thank you for all this invaluable work

1 Like

Updated the tutorial and integrated the Linux version made by 0x3639.


George implemented support for the sha-256 hashing algoritme. I’ve updated the tools and integrated the functionality.

To try it out, you can either remove the existing builds and follow the tutorial from scratch or follow the instructions below to update and rebuild both repositories.


BICH go-zenon

cd ~/repos/go-zenon
git config pull.rebase false
git pull
git merge origin/htlc
make znnd

Zenon CLI for .NET

cd ~/repos/znn_cli_csharp
git config pull.rebase false
git pull
dotnet build --runtime linux-x64 src/ZenonCli/ZenonCli.csproj


BICH go-zenon

cd ~/repos/go-zenon
git config pull.rebase false
git pull
git merge origin/htlc
go build -o build/libznn.dll -buildmode=c-shared -tags libznn main_libznn.go
go build -o build/znnd.exe main.go

Zenon CLI for .NET

cd ~/repos/znn_cli_csharp
git config pull.rebase false
git pull
dotnet build ./src/ZenonCli/ZenonCli.csproj


Use the createHash command to create a sha-256 hash.

./znn-cli createHash "all your znn belong to us" 1

Don’t forget to specify the hash type when creating a htlc, by default it uses the sha3-256 hashing algoritme. Use the following command to display the help information for command.

./znn-cli htlc.create --help

1 Like

When you go git pull you get the following error.

x3639@x3639-virtual-machine:~/repos/go-zenon$ git pull 
hint: You have divergent branches and need to specify how to reconcile them.
hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint:   git config pull.rebase false  # merge (the default strategy)
hint:   git config pull.rebase true   # rebase
hint:   git config pull.ff only       # fast-forward only
hint: You can replace "git config" with "git config --global" to set a default
hint: preference for all repositories. You can also pass --rebase, --no-rebase,
hint: or --ff-only on the command line to override the configured default per
hint: invocation.
fatal: Need to specify how to reconcile divergent branches.

We merged htlc into origin previously. And now we are trying to pull an update to go-zenon. I’m not a git expert, but looks like we should merge the code?

git config pull.rebase false

here is what I tried:

x3639@x3639-virtual-machine:~/repos/go-zenon$ git config pull.rebase false

x3639@x3639-virtual-machine:~/repos/go-zenon$ git pull
Merge made by the 'ort' strategy.
 app/action_devnet.go | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 118 insertions(+), 3 deletions(-)

Also note in this command, you need to insert a “1” in the hashType to use the sha-256

./znn-cli htlc.create z1qpsjv3wzzuuzdudg7tf6uhvr6sk4ag8me42ua4 ZNN 100 3600 de543a6cab8db5bdc086d1720b97b0f097458841cd0264d789350e3b07587f5b [hashType] -k Alice -n 321

HTLC Scenario 1 & 2 work with the new code using sha-256


You’re right, my settings were already set to git config pull.rebase false.


Zenon Sdk and Cli for .NET have been updated to reflect htlc api changes.


The tutorial has been updated to reflect @georgezgeorgez latest BTC Atomic Swaps via HTLC: Cope Edition changes.