Watchtowers in LND – do they work?

Read on for my experience. Want to test yourself? Follow my detailed guide! On testnet, losing coins is free, go ahead and play! I’m writing this fairly beginner friendly, and not assume too much familiarity with LND. I encourage anyone not experienced with LND to read the logs, which can be found in ~/.lnd-*/logs/bitcoin/testnet/ – lnd.log is the last one, and there’s automatic log rotation.

Setup

Everything was done with the same user on a Linux machine, to keep it simple. This particularly simplifies the bitcoind configuration.

Bitcoind

Bitcoind compiled with ZMQ was configured to run on testnet with the following configuration in ~/.bitcoin/bitcoin.conf:

testnet=1
txindex=1
server=1
daemon=1
zmqpubrawblock=tcp://127.0.0.1:18501
zmqpubrawtx=tcp://127.0.0.1:18502

LND  node 1

I compiled a fresh, 0.8.0-beta, LND.

I configured a node VegardNodeA by creating a .lnd-a directory in my home directory, copying sample-lnd.conf from the LND source directory to lnd.conf in this directory, with the following changes:

[Application Options]

listen=0.0.0.0:9735
rpclisten=localhost:10009
restlisten=localhost:8080
alias=VegardNodeA

[Bitcoin]
bitcoin.active=1
bitcoin.testnet=1
bitcoin.node=bitcoind

[wtclient]
wtclient.active=1

LND node 2

Then, I configured a second node, VegardNodeB, in a .lnd-b directory, with lnd.conf:

[Application Options]
listen=0.0.0.0:9745
rpclisten=localhost:10019
restlisten=localhost:8085
alias=VegardNodeB

[Bitcoin]

bitcoin.active=1
bitcoin.testnet=1
bitcoin.node=bitcoind

Watchtower LND

[Application Options]
listen=0.0.0.0:9755
rpclisten=localhost:10029
restlisten=localhost:8090
alias=VegardWatchtower

[Bitcoin]
bitcoin.active=1
bitcoin.testnet=1

[watchtower]
watchtower.active=1

Running the instances

Finally, you need to start and initalize all the nodes. Fire up three different terminals to run lnd’s in, and one to control  them all

lnd --lnddir ~/.lnd-a
lnd --lnddir ~/.lnd-b
lnd --lnddir ~/.lnd-wt

lncli --network=testnet --lnddir ~/.lnd-a --rpcserver localhost:10009 create
lncli --network=testnet --lnddir ~/.lnd-b --rpcserver localhost:10019 create
lncli --network=testnet --lnddir ~/.lnd-wt --rpcserver localhost:10029 create


It might be a good idea for minimizing typing (and I will use this in rest of the tutorial)

alias lnclia="lncli --network=testnet --lnddir ~/.lnd-a --rpcserver localhost:10009"
alias lnclib="lncli --network=testnet --lnddir ~/.lnd-b --rpcserver localhost:10019"
alias lncliwt="lncli --network=testnet --lnddir ~/.lnd-wt --rpcserver localhost:10029"

and then use the aliases like this:

lnclia create
lnclib create
lncliwt create

For all lncli create’s, create passwords and write down seeds as you would for any wallet creation.

Now, familiarize yourself with your setup by running lncli getinfo and other queries against nodes A, B and the watchtower, and finally

lncliwt tower info

This should reveal the URI for the tower, which is different than the regular URI for the tower node. Now, you need to add this watchtower to node A (replace URI with your own URI, of course)

lnclia wtclient add 03471c0a89d51f189c899dcbc0c623cdb8c632686a4b7d6bc9b4f2cba2a84a6528@127.0.0.1:9911

This should give you only the following output:

{

}

Funding a wallet

Having set up it all, get some testnet coins. Go to https://coinfaucet.eu/en/btc-testnet/ and paste the resulting address from

lncli newaddress np2wkh

Now, you need to wait for a confirmation. You can check status with

lnclia walletbalance

When the transactions confirmed, the wallet balance moves to confirmed balance.

Creating a channel

lnclia connect 03523083824e7c9a3c01ac804ebabedf36d4cc216d62e1b365dc4d0aea1344de95@88.95.208.194:9745
lnclia openchannel 03523083824e7c9a3c01ac804ebabedf36d4cc216d62e1b365dc4d0aea1344de95 300000 --sat_per_byte 10

You will of course need to use the URI of node B instead of my example URI. Now, you again need to wait a few confirmations. Check back after a while if your channel appears when you do

lnclia listchannels

This would probably be a good place to go grab a coffee, or in my case – walk the dog. Or, read the logfiles, ~/.lnd-<node>/logs/bitcoin/testnet/lnd.log to see what things happens.

Do some payments

For our tests to work, there has to happen some transactions. So, we’ll go on send some bitcoin back and forth between node A and B. You can of course not cut and paste all this, but need


 lnclib addinvoice 100000 
{ 
"r_hash": "85a0e3033bcbcc6067e095fc952947739e514466ed8671ef5c6ce8c76a4e4563", 
"pay_req": "lntb1m1pw6j3jlpp5skswxqeme0xxqelqjh7f2228ww09z3rxakr8rm6udn5vw6jwg43sdqqcqzpgvl7ja8n88rp6sgt7ydrggfzsw78mdc4qzcqfh2urpwrf8aa769ks2k4rd6n8mx2sz37l5ezpzqhd27hhmjx6evn2d3ma0hfr2gqadhcpntcj5d", 
"add_index": 2 
} 
lnclia payinvoice lntb1m1pw6j3jlpp5skswxqeme0xxqelqjh7f2228ww09z3rxakr8rm6udn5vw6jwg43sdqqcqzpgvl7ja8n88rp6sgt7ydrggfzsw78mdc4qzcqfh2urpwrf8aa769ks2k4rd6n8mx2sz37l5ezpzqhd27hhmjx6evn2d3ma0hfr2gqadhcpntcj5d
Description: 
Amount (in satoshis): 100000 
Destination: 03523083824e7c9a3c01ac804ebabedf36d4cc216d62e1b365dc4d0aea1344de95
Confirm payment (yes/no): yes 
{ 
"payment_error": "", 
"payment_preimage": "1e7162123cc48125f4bbf770f060383906e0a8a899999d457d7cd17c6c99e72c", 
"payment_route": { "total_time_lock": 1583287, 
"total_amt": 100000, "hops": [ { "chan_id": 1740763301772722176, 
"chan_capacity": 300000, "amt_to_forward": 100000, 
"expiry": 1583287, "amt_to_forward_msat": 100000000, 
"pub_key": "03523083824e7c9a3c01ac804ebabedf36d4cc216d62e1b365dc4d0aea1344de95", 
"tlv_payload": true } ], 
"total_amt_msat": 100000000 
} 
} 

lnclia addinvoice 50000 
{ 
"r_hash": "91c58b341de687d12a58a97e8f374c6c9a61cc4a4056db13bd1c041fe54dde51", 
"pay_req": "lntb500u1pw6j34fpp5j8zckdqau6raz2jc49lg7d6vdjdxrnz2gptdkyaarszple2dmegsdqqcqzpgzcp3x4w883jpdan6uzqvmgcw5ef763hknpcq9ch4xs460wklslqn5pch6sv967k44jfdmk2qr22htxyscgduu97ehty7mu6sl24ex3cqse5t0x", 
"add_index": 1 
} 
lnclib payinvoice lntb500u1pw6j34fpp5j8zckdqau6raz2jc49lg7d6vdjdxrnz2gptdkyaarszple2dmegsdqqcqzpgzcp3x4w883jpdan6uzqvmgcw5ef763hknpcq9ch4xs460wklslqn5pch6sv967k44jfdmk2qr22htxyscgduu97ehty7mu6sl24ex3cqse5t0x 
Description: 
Amount (in satoshis): 50000 
Destination: 0292f889e38b479731676c4fd307e0126672b06d76d9bb4815602cc4bbd19a1d11 
Confirm payment (yes/no): yes 
{ "payment_error": "", 
"payment_preimage": "65b8e81e7928a0b6c70197a83a20b5d75350a299d8466476a96ab237c8e1075f", 
"payment_route": { "total_time_lock": 1583287, 
"total_amt": 50000, 
"hops": [ { 
"chan_id": 1740763301772722176, 
"chan_capacity": 300000, 
"amt_to_forward": 50000, 
"expiry": 1583287, 
"amt_to_forward_msat": 50000000, 
"pub_key": "0292f889e38b479731676c4fd307e0126672b06d76d9bb4815602cc4bbd19a1d11", 
"tlv_payload": true } ], 
"total_amt_msat": 50000000 
} 
}

Setting up for a fraud….

You now need to copy channel.db of node B to a safe place. Ideally while node B is down, to make sure it’s consistent and usable.

lnclib stop
cp ~/.lnd-b/data/graph/testnet/channel.db ~/.lnd-b/data/graph/testnet/channel.db.copy

Now, restart lnd B in another terminal

lnd --lnddir=~/.lnd-b

And unlock it in your terminal with your aliases.

lnclib unlock
Input wallet password: 

lnd successfully unlocked!

Now, we need to give node B something to steal…

lnclia addinvoice 10000
 {
 "r_hash": "924cad77f3dbcc4e7cc19d0c1f1554843214f17da6bdf0ddbe99be883419c583",
 "pay_req": "lntb100u1pw6jjmgpp5jfx26alnm0xyulxpn5xp7925ssepfuta567lphd7nxlgsdqeckpsdqqcqzpgfegtwge95z5x7ppnrkdcdmw6yr5f5kls94xc09u7gms9g6fvvrpz9xs0pfuszqh9zpg7sl2xj67fvlyexg5kmjg2mkv80gkvvrkhyjgpf89rw6",
 "add_index": 2
 }
lnclib payinvoice lntb100u1pw6jjmgpp5jfx26alnm0xyulxpn5xp7925ssepfuta567lphd7nxlgsdqeckpsdqqcqzpgfegtwge95z5x7ppnrkdcdmw6yr5f5kls94xc09u7gms9g6fvvrpz9xs0pfuszqh9zpg7sl2xj67fvlyexg5kmjg2mkv80gkvvrkhyjgpf89rw6
 Description:
 Amount (in satoshis): 10000
 Destination: 0292f889e38b479731676c4fd307e0126672b06d76d9bb4815602cc4bbd19a1d11
 Confirm payment (yes/no): yes
 {
 "payment_error": "",
 "payment_preimage": "0b710f14ebaa6245672e4bdd04ba7309ca1d0159009da57f4427a3d13b9e63f5",
 "payment_route": {
 "total_time_lock": 1583289,
 "total_amt": 10000,
 "hops": [
 {
 "chan_id": 1740763301772722176,
 "chan_capacity": 300000,
 "amt_to_forward": 10000,
 "expiry": 1583289,
 "amt_to_forward_msat": 10000000,
 "pub_key": "0292f889e38b479731676c4fd307e0126672b06d76d9bb4815602cc4bbd19a1d11",
 "tlv_payload": true
 }
 ],
 "total_amt_msat": 10000000
 }

}


The fraud

For this to work, node A need to be down, so stop it.

lnclia stop

Now, upon seeing that node A is gone, node B is guessing that he will not be back before the timelocks built into the channel is over. Let’s do some fraud! Stop node B, put back in place the backup, and restart it

lnclib stop
cp ~/.lnd-b/data/graph/testnet/channel.db.copy ~/.lnd-b/data/graph/testnet/channel.db
lnd --lnddir=~/.lnd-b (in another window)
lnclib unlock

Verify that it indeed thinks it has 50000 (the result after the first payment of 50000), and not 40000 (after paying 10000 more), and find the channel point.

lnclib listchannels
{
 "channels": [
 {
 "active": false,
 "remote_pubkey": "0292f889e38b479731676c4fd307e0126672b06d76d9bb4815602cc4bbd19a1d11",
 "channel_point": "108b9d9b1202d23307d7272530ea74c7473d5c3d8e250384cb672b602e7239f7:0",
 "chan_id": "1740763301772722176",
 "capacity": "300000",
 "local_balance": "50000",
 "remote_balance": "249817",
 "commit_fee": "183",
 "commit_weight": "724",
 "fee_per_kw": "253",
 "unsettled_balance": "0",
 "total_satoshis_sent": "50000",
 "total_satoshis_received": "100000",
 "num_updates": "4",
 "pending_htlcs": [
 ],
 "csv_delay": 144,
 "private": false,
 "initiator": false,
 "chan_status_flags": "ChanStatusDefault",
 "local_chan_reserve_sat": "3000",
 "remote_chan_reserve_sat": "3000",
 "static_remote_key": true
 }
 ]
}

Now, force close it!

lnclib closechannel 108b9d9b1202d23307d7272530ea74c7473d5c3d8e250384cb672b602e7239f7 0 --force
{
 "closing_txid": "827732758fb9973f3b99b677ccb460460fb54712e3ec1639ce019e33d87d06f0"
}

Now, a watchtower acts on confirmed transactions, so we’ll need to wait a block. Follow ~/.lnd-wt/logs/bitcoin/testnet/lnd.log, until you see it gets a new block and magic starts happen!

019-10-18 07:43:44.651 [INF] NTFN: New block: height=1583249, sha=0000000000088208f755f57ec65814fcba2a4b2385826cf9e2103484002601c8
2019-10-18 07:43:44.651 [INF] UTXN: Attempting to graduate height=1583249: num_kids=0, num_babies=0
2019-10-18 07:43:44.692 [INF] WTWR: Found 1 breach in (height=1583249, hash=0000000000088208f755f57ec65814fcba2a4b2385826cf9e2103484002601c8)
2019-10-18 07:43:44.692 [INF] WTWR: Dispatching punisher for client 0395b404e198d07a2ae5542cd7c691c030587ea9df12647cb3032c54edee52bc59, breach-txid=827732758fb9973f3b99b677ccb460460fb54712e3ec1639ce019e33d87d06f0
2019-10-18 07:43:44.694 [INF] WTWR: Publishing justice transaction for client=0395b404e198d07a2ae5542cd7c691c030587ea9df12647cb3032c54edee52bc59 with txid=079082649b11986377a1717ba04bb90097a774977a0f44d3b6424d90e81418cf
2019-10-18 07:43:44.694 [INF] LNWL: Inserting unconfirmed transaction 079082649b11986377a1717ba04bb90097a774977a0f44d3b6424d90e81418cf
2019-10-18 07:43:44.700 [INF] WTWR: Punishment for client 0395b404e198d07a2ae5542cd7c691c030587ea9df12647cb3032c54edee52bc59 with breach-txid=827732758fb9973f3b99b677ccb460460fb54712e3ec1639ce019e33d87d06f0 dispatched

Now, let’s restart node A and see what happens

lnd --lnddir ~/.lnd-a/ (in another window)
lnclia unlock

And check the balance

lnclia walletbalance
{
 "total_balance": "2682098",
 "confirmed_balance": "2682098",
 "unconfirmed_balance": "0"
}

Read ~/.lnd-a/logs/bitcoin/testnet/lnd.log and you should see something like this

2019-10-18 07:48:50.463 [ERR] BRAR: Unable to broadcast justice tx: Transaction rejected: output already spent
019-10-18 07:48:50.464 [INF] BRAR: Waiting for a spend event before attempting to craft new justice tx.
2019-10-18 07:48:50.464 [INF] BRAR: Checking spend from CommitmentNoDelayTweakless(827732758fb9973f3b99b677ccb460460fb54712e3ec1639ce019e33d87d06f0:1) for 
ChannelPoint(108b9d9b1202d23307d7272530ea74c7473d5c3d8e250384cb672b602e7239f7:0)
2019-10-18 07:48:50.464 [INF] NTFN: New spend subscription: spend_id=2, outpoint=827732758fb9973f3b99b677ccb460460fb54712e3ec1639ce019e33d87d06f0:1, height
_hint=1583249
2019-10-18 07:48:50.466 [INF] BRAR: Checking spend from CommitmentRevoke(827732758fb9973f3b99b677ccb460460fb54712e3ec1639ce019e33d87d06f0:0) for ChannelPoi
nt(108b9d9b1202d23307d7272530ea74c7473d5c3d8e250384cb672b602e7239f7:0)
2019-10-18 07:48:50.466 [INF] NTFN: New spend subscription: spend_id=3, outpoint=827732758fb9973f3b99b677ccb460460fb54712e3ec1639ce019e33d87d06f0:0, height
_hint=1583249

This is node A trying to serve justice to node B itself, but noticing that the transaction is already spent – by the watchtower! Now, let’s wait for the next block, and we see…

2019-10-18 08:03:53.362 [INF] LNWL: Inserting unconfirmed transaction 079082649b11986377a1717ba04bb90097a774977a0f44d3b6424d90e81418cf
2019-10-18 08:03:53.382 [INF] LNWL: Marking unconfirmed transaction 079082649b11986377a1717ba04bb90097a774977a0f44d3b6424d90e81418cf mined in block 1583250
2019-10-18 08:03:53.489 [INF] CRTR: Pruning channel graph using block 0000000044cfde0667c22eec017b059f7ab47a9626221a7d47cb5a78d3fa0eb9 (height=1583250)
2019-10-18 08:03:53.507 [INF] CRTR: Block 0000000044cfde0667c22eec017b059f7ab47a9626221a7d47cb5a78d3fa0eb9 (height=1583250) closed 0 channels
2019-10-18 08:03:53.591 [INF] NTFN: New block: height=1583250, sha=0000000044cfde0667c22eec017b059f7ab47a9626221a7d47cb5a78d3fa0eb9
2019-10-18 08:03:53.591 [INF] NTFN: Dispatching confirmed spend notification for outpoint=827732758fb9973f3b99b677ccb460460fb54712e3ec1639ce019e33d87d06f0:0 at height=1583250
2019-10-18 08:03:53.591 [INF] NTFN: Dispatching confirmed spend notification for outpoint=827732758fb9973f3b99b677ccb460460fb54712e3ec1639ce019e33d87d06f0:1 at height=1583250
2019-10-18 08:03:53.591 [INF] BRAR: Detected spend on CommitmentNoDelayTweakless(827732758fb9973f3b99b677ccb460460fb54712e3ec1639ce019e33d87d06f0:1) by txid(079082649b11986377a1717ba04bb90097a774977a0f44d3b6424d90e81418cf) for ChannelPoint(108b9d9b1202d23307d7272530ea74c7473d5c3d8e250384cb672b602e7239f7:0)
2019-10-18 08:03:53.591 [INF] UTXN: Attempting to graduate height=1583250: num_kids=0, num_babies=0
2019-10-18 08:03:53.591 [INF] BRAR: Detected spend on CommitmentRevoke(827732758fb9973f3b99b677ccb460460fb54712e3ec1639ce019e33d87d06f0:0) by txid(079082649b11986377a1717ba04bb90097a774977a0f44d3b6424d90e81418cf) for ChannelPoint(108b9d9b1202d23307d7272530ea74c7473d5c3d8e250384cb672b602e7239f7:0)
2019-10-18 08:03:53.591 [INF] BRAR: Spend on CommitmentNoDelayTweakless(827732758fb9973f3b99b677ccb460460fb54712e3ec1639ce019e33d87d06f0:1) for ChannelPoint(108b9d9b1202d23307d7272530ea74c7473d5c3d8e250384cb672b602e7239f7:0) transitions output to terminal state, removing input from justice transaction
2019-10-18 08:03:53.591 [INF] BRAR: Spend on CommitmentRevoke(827732758fb9973f3b99b677ccb460460fb54712e3ec1639ce019e33d87d06f0:0) for ChannelPoint(108b9d9b1202d23307d7272530ea74c7473d5c3d8e250384cb672b602e7239f7:0) transitions output to terminal state, removing input from justice transaction

And

vegard@vegardlaptop:~$ lnclia walletbalance
{
“total_balance”: “2730201”,
“confirmed_balance”: “2730201”,
“unconfirmed_balance”: “0”
}

We have got back the 50k. Now, let’s check our whole onchain history.

lnclia listchaintxns
{
 "transactions": [
 {
 "tx_hash": "22a9fca598c63f98f9192699c91d37774fdf97fc5f800a80d96412ee6e54e254",
 "amount": "2734038",
 "num_confirmations": 37,
 "block_hash": "000000002afed444dfcc5990d1ecee289f81353934e25354e1c11f8a87cf1f01",
 "block_height": 1583214,
 "time_stamp": "1571343204",
 "total_fees": "0",
 "dest_addresses": [
 "2MszP1ucze7pVQvT4t2puSG6uocFMSqK5QT",
 "2N3dmvz4yZ6Deax3uPMueaSMGBoWy8vzTcX"
 ],
 "raw_tx_hex": "02000000000101dad2d5004a6dc6d1c2406e2d17fe026b2da61f42e370fd67de108f7ce3d49a310000000017160014d923e6eff6605fc9c779fdc0c706e7307a3bae38feffffff02228d29060000000017a91408296c2c771644f2a2ac66d9932fa092927d224787d6b729000000000017a91471f512e28098d4f9f7783fa3e6409697d5270c3d8702473044022059a73c2f586a8a20653694a87b56f41a8fa843f0656ad46e51d324cef2a7a4b60220356447f366fe0828df78d8bdf2ea96c108e4ab26f285e272857dda7d17878f530121033337a82f3823ccf16d6a8cce7bb3923438bde4001303bbddfeeb1e7547f8f4f26d281800"
 },
 {
 "tx_hash": "108b9d9b1202d23307d7272530ea74c7473d5c3d8e250384cb672b602e7239f7",
 "amount": "-301757",
 "num_confirmations": 36,
 "block_hash": "00000000756a9068b87636f6e2da22d17230037743262b1b9385c699a369b18e",
 "block_height": 1583215,
 "time_stamp": "1571344408",
 "total_fees": "1757",
 "dest_addresses": [
 "tb1qres4wzattp4cpn4c7egpg57v8m77x3q0z2hkma43fga9pus655ys0uhkx7",
 "tb1qkf380ljsjefv83q6748jd2t2srnqfea72tukku"
 ],
 "raw_tx_hex": "0100000000010154e2546eee1264d9800a805ffc97df4f77371dc9992619f9983fc698a5fca92201000000171600147654d9bf658e6cf5db44d0d75382beb8ba496608ffffffff02e0930400000000002200201e61570bab586b80ceb8f6501453cc3efde3440f12af6df6b14a3a50f21aa509191d250000000000160014b26277fe509652c3c41af54f26a96a80e604e7be0247304402207309e076a8e3e1db9a8f0514b1905ea427314edd855abf03b5f0fff93e40bfbe02205f04e50d0e4e52240610733230e3f9ab9738c00df4b592ba62ddd726e6e725af0121023413d20fe826a6587f3e6dc46d7a6337f65641aba2b672acdd129e196acefa4d00000000"
 },
 {
 "tx_hash": "827732758fb9973f3b99b677ccb460460fb54712e3ec1639ce019e33d87d06f0",
 "amount": "249817",
 "num_confirmations": 2,
 "block_hash": "0000000000088208f755f57ec65814fcba2a4b2385826cf9e2103484002601c8",
 "block_height": 1583249,
 "time_stamp": "1571377425",
 "total_fees": "0",
 "dest_addresses": [
 "tb1qq0tprf2rtc7yleq6zl5qrcsuk65u655ceyskpq8ngl9vmmejkzzsjc2k64",
 "tb1qfdfqsm04epnnnv4k2qwzywggwgequ4rmtk2qcl"
 ],
 "raw_tx_hex": "02000000000101f739722e602b67cb8403258e3d5c3d47c774ea302527d70733d202129b9d8b100000000000d3692c800250c300000000000022002003d611a5435e3c4fe41a17e801e21cb6a9cd5298c9216080f347cacdef32b085d9cf0300000000001600144b52086df5c86739b2b6501c22390872320e547b040047304402205b100144524d510dd9df6a5245fda54985c8f40ed379fe96cbba2fd163c3966c022073d7ad1e5afccee53134a5f65284ecc3ffe480e8862760037dcbdb65736617fc01483045022100b7ae8870c7e9efb5d88a7fe0b012c14f4eb1c2ff30319b2fc924240d4351e2ce0220057cf5514996283d5b2a0129cc52f03e721de95995eb2ae70b5d77f3224f797401475221030b495346e543601fef83cfd8af1072e406bc7b04105ee2a070ea4def6d36216e2103ca54f236bf4218c80f3435d373a7e57ce0efed86bb6d02c6465f3917c508fb6c52ae1a45b820"
 },
 {
 "tx_hash": "079082649b11986377a1717ba04bb90097a774977a0f44d3b6424d90e81418cf",
 "amount": "48103",
 "num_confirmations": 1,
 "block_hash": "0000000044cfde0667c22eec017b059f7ab47a9626221a7d47cb5a78d3fa0eb9",
 "block_height": 1583250,
 "time_stamp": "1571378631",
 "total_fees": "0",
 "dest_addresses": [
 "tb1qage5aacsvql758v6z8ufulzn083pst6ukzwzw8"
 ],
 "raw_tx_hex": "02000000000102f0067dd8339e01ce3916ece31247b50f4660b4cc77b6993b3f97b98f75327782000000000000000000f0067dd8339e01ce3916ece31247b50f4660b4cc77b6993b3f97b98f7532778201000000000000000001c08b040000000000160014ea334ef710603fea1d9a11f89e7c5379e2182f5c03473044022011b42b74cbed2ff7a4303547e3f4c34dbc8b32c6684dbc9982119c851c3604a302206e5345806ce5a463a22e858d703b94eb469c239e7bde3bfc21b81737a650c5950101014d632103ecf2f02402d7477f06b4581e7fe09d556c902b8a18c8f02a1b7f371f4bd49ed367029000b2752103ef235e503884117ab07d79ae38017ed343a37dd5de4977e0ad9966f4762c90c568ac024730440220094166b2fd72c98dd69f78a2572e8bd392df705daf6c2821f94d65a3cee85d0702202201e6667d5d3a07d820c8860565f3fb56937fa7ee82644eacf34dd7421d7d9201210376f06e006287a7ffdea318d008e997535a144f4bef6b44138ec127f372e6a1c300000000"
 }
 ]
}

We can see 4 transactions. The first transaction, node A gets  2734038 satoshi from the faucet. In the second transaction, node A spends 301757 satoshi to set up a channel with 300k capacity. Then, node A got the (approximately, fee subtracted) 250k that  node B had to give him in the fraudulent transaction. Note that he can not even attempt to steal everything, he can attempt to steal only what node A at some time has allowed him to spend. The fourth, and last, transaction, is the 50k (minus some fees) from the watchtower that caught the fraud, and sent back not only the 10k he tried to take, but the full balance he had. Justice is served! Note also that in this version of watchtowers, the watchtower gets nothing at all, so it’s  not as trustless as it will eventually be – there’s no self-interest for a watchtower to publish a penalty transaction. This means we’re not likely to see a bunch of public watchtowers at this stage. However, you could still set up a watchtower for yourself – perhaps in the cloud (remember, it doesn’t actually hold any funds), or you could partner with some friends or business partners to run watchtower services for each others.

Another thing to note is that in LND version 0.8.0-beta, the address that a force close transaction sent to now is controlled by the on-chain wallet, while earlier it would be a temporary address. This means that if you totally lose the node, and have no backup at all, everything should end up in addressess recoverable by your seed if your partner force close the channel. If you want to signal to your channel partners to do that, you will still need the static channel backup that LND generates.

This new commitment format, “safu commitment format”, also leads to less on-chain transactions. Earlier, force-closed transactions ended up in a temporary address that your node would then have to move to the on-chain wallet, in a new transaction. This should now slowly be gone, for new channels.

So, does watchtowers work? I’ll have to say yes! But they will improve in the future.

9 thoughts on “Watchtowers in LND – do they work?

  1. I’ve test it but my case. Tower not do their job. After node A down. Seem like tower no longer receive and action anything until node A back online. Watch tower seem to get some info from node A. Also once node A online then it send punishment as you say. But another test, if I let node A down past the time of justice or 1 day. Then cheat success. Meaning watch tower don’t do it’s job. I don’t sure why? Is it because it local networking?

    1. Hard to say, been a while since I played with this. There might also have been changes in how you set it up.

      But all the nodes of course need to be getting new blocks from the blockchain. First thing would be to check if the nodes are healthy and get new blocks.

      1. I’ve try again, open channel, close channel. For more than 5th time. Result still similar. I also send you email. Can you help me some? Maybe I can send what I’ve done trough email more easy. And I sure I get new block, it testnet and I got Bitcoin tow test around 2million Satoshi. I also can send and receive Satoshi trough node.b follow by your article. Also node wt seem to get info quite well. Node a log seem to connect to watch tower as well. That log output from node a. But node wt seem to quite shy, little activity I mean.

  2. And 2 scenario I test:
    Cheating success if I let node.a offline for more than 1 day. Node.b will get what it cheating for. Verify by wallet balance.

    Cheating protected if node.a online before finish 1 day then it node.a publish justice transaction then punished node.b those also verify by wallet balance after force close channels.

    That said but I still not sure why watchtower not publish the cheating transaction thing . Only node.a done when it online within 1 day after force close channel.

      1. My bad! It work already. Look carefully again in log file in watch tower node with grep. I can see it log breach detect around 5-10 minute. later or my 2-times test case. Thank for help. I like your article. It simple to follow and explanation.

        1. The watchtower would need to wait until the cheat is confirmed onchain, of course. I don’t know either if it will wait a couple of confirmation, which might be sensible because then it allows you to catch the cheater yourself, witch might save you some fees.

          Been a while since I looked at this, so my info here would probably even be outdated if I knew it at that point.

Leave a Reply

Your email address will not be published. Required fields are marked *