├── btcd └── btcd.conf ├── cmd ├── start-attacker.sh ├── start-victim.sh ├── start-watchtower.sh ├── btcctl.sh ├── lncli-victim.sh ├── lncli-attacker.sh ├── lncli-watchtower.sh ├── start-btcd.sh └── restart.sh ├── .gitignore ├── nodes ├── attacker │ └── lnd.conf ├── watchtower │ └── lnd.conf └── victim │ └── lnd.conf └── README.md /btcd/btcd.conf: -------------------------------------------------------------------------------- 1 | simnet=1 2 | rpcuser=kek 3 | rpcpass=kek 4 | -------------------------------------------------------------------------------- /cmd/start-attacker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | lnd --lnddir=./nodes/attacker -------------------------------------------------------------------------------- /cmd/start-victim.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | lnd --lnddir=./nodes/victim 3 | -------------------------------------------------------------------------------- /cmd/start-watchtower.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | lnd --lnddir=./nodes/watchtower -------------------------------------------------------------------------------- /cmd/btcctl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | btcctl --configfile ./btcd/btcd.conf --rpcuser kek --rpcpass kek --simnet $@ -------------------------------------------------------------------------------- /cmd/lncli-victim.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | lncli --network simnet --rpcserver localhost:30001 --lnddir ./nodes/victim $@ -------------------------------------------------------------------------------- /cmd/lncli-attacker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | lncli --network simnet --rpcserver localhost:31001 --lnddir ./nodes/attacker $@ -------------------------------------------------------------------------------- /cmd/lncli-watchtower.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | lncli --network simnet --rpcserver localhost:32001 --lnddir ./nodes/watchtower $@ -------------------------------------------------------------------------------- /cmd/start-btcd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | btcd --configfile=./btcd/btcd.conf --datadir=./btcd/data --logdir=./btcd/logs --txindex $@ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | btcd/data 2 | btcd/logs 3 | nodes/*/data 4 | nodes/*/logs 5 | nodes/*/tls.* 6 | nodes/attacker-evil 7 | nodes/attacker-honest -------------------------------------------------------------------------------- /cmd/restart.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -rf nodes/*/data 3 | rm -rf nodes/*/logs 4 | rm -rf nodes/*/tls.* 5 | rm -rf nodes/attacker-honest 6 | rm -rf nodes/attacker-evil 7 | -------------------------------------------------------------------------------- /nodes/attacker/lnd.conf: -------------------------------------------------------------------------------- 1 | [Application Options] 2 | alias="Attacker" 3 | color="#FF0000" 4 | rpclisten=localhost:31001 5 | restlisten=localhost:31002 6 | listen=localhost:31011 7 | 8 | [Bitcoin] 9 | bitcoin.simnet=1 10 | bitcoin.active=1 11 | bitcoin.node=btcd 12 | 13 | [btcd] 14 | btcd.rpcuser=kek 15 | btcd.rpcpass=kek 16 | -------------------------------------------------------------------------------- /nodes/watchtower/lnd.conf: -------------------------------------------------------------------------------- 1 | [Application Options] 2 | alias="Watchtower" 3 | color="#000000" 4 | rpclisten=localhost:32001 5 | restlisten=localhost:32002 6 | listen=localhost:32011 7 | 8 | [Bitcoin] 9 | bitcoin.simnet=1 10 | bitcoin.active=1 11 | bitcoin.node=btcd 12 | 13 | [btcd] 14 | btcd.rpcuser=kek 15 | btcd.rpcpass=kek 16 | 17 | [Watchtower] 18 | watchtower.active=1 19 | watchtower.listen=localhost:32021 -------------------------------------------------------------------------------- /nodes/victim/lnd.conf: -------------------------------------------------------------------------------- 1 | [Application Options] 2 | alias="Victim" 3 | color="#0000FF" 4 | rpclisten=localhost:30001 5 | restlisten=localhost:30002 6 | listen=localhost:30011 7 | 8 | [Bitcoin] 9 | bitcoin.simnet=1 10 | bitcoin.active=1 11 | bitcoin.node=btcd 12 | 13 | [btcd] 14 | btcd.rpcuser=kek 15 | btcd.rpcpass=kek 16 | 17 | [Wtclient] 18 | # wtclient.private-tower-uris=@localhost:32021 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Watchtower Example Demo 2 | 3 | Give LND's watchtowers a shot with your own simulated channel breach! 4 | 5 | ## Requirements 6 | 7 | * `lnd` 8 | * `btcd` 9 | * 5 different terminal windows open 10 | 11 | ## Setup 12 | 13 | To run watchtowers at the time of writing, we'll need to be on the [latest release `v0.7.0-beta-rc1`](https://github.com/lightningnetwork/lnd/releases/tag/v0.7.0-beta-rc1) as it's the only release that includes the code from [this pull request](https://github.com/lightningnetwork/lnd/pull/3133). With that version of LND's code, we'll recompile and run through the exercise. 14 | 15 | These commands assume you've already installed LND before. Make sure you've turned off any running nodes before doing this. 16 | 17 | ```bash 18 | cd $GOPATH/src/github.com/lightningnetwork/lnd 19 | git pull 20 | git checkout v0.7.0-beta-rc1 21 | make clean && make && make install 22 | ``` 23 | 24 | ## Walthrough 25 | 26 | ### 1. Initial Setup 27 | 28 | We need to setup our three nodes, the `attacker`, the `victim`, and the `watchtower`. Config files have already been created for them, you just need to get them started. 29 | 30 | ```sh 31 | # Window 1 32 | cmd/start-btcd.sh 33 | 34 | # Window 2 35 | cmd/start-victim.sh 36 | 37 | # Window 3 38 | cmd/start-attacker.sh 39 | 40 | # Window 4 41 | cmd/start-watchtower.sh 42 | 43 | # Window 5 44 | cmd/lncli-victim.sh create # Enter 'password', 'password', 'n', and hit enter for the last one 45 | cmd/lncli-attacker.sh create # Enter 'password', 'password', 'n', and hit enter for the last one 46 | cmd/lncli-watchtower.sh create # Enter 'password', 'password', 'n', and hit enter for the last one 47 | ``` 48 | 49 | ### 2. Configure the Watchtower 50 | 51 | In order to configure our victim to use our watchtower, we need to know the watchtower node's pubkey. Let's grab it: 52 | 53 | ```sh 54 | # Window 5 55 | cmd/lncli-watchtower.sh getinfo # Copy the "identity_pubkey" from this 56 | ``` 57 | 58 | Then open up `nodes/victim/lnd.conf`, and uncomment the bottom and add the watchtower's pubkey to the indicated spot. It should look something like this: 59 | 60 | ```conf 61 | [Wtclient] 62 | wtclient.private-tower-uris=037ab06ef760b98c88bb28e4a4959e37ea370a71f2d7a74c7bd8274961b23ed3b5@localhost:32021 63 | ``` 64 | 65 | You'll then need to restart the victim node with a `CTRL+C` and running the following: 66 | 67 | ```sh 68 | # Window 2 69 | cmd/start-victim.sh 70 | 71 | # Window 5 72 | cmd/lncli-victim.sh unlock # Enter 'password' from the create command 73 | ``` 74 | 75 | You'll know it worked if it starts successfully and you see the following logs in Window 2: 76 | 77 | ```log 78 | [INF] WTCL: Acquired new session with id= 79 | ``` 80 | 81 | ### 3. Getting Funds 82 | 83 | In order to try out an attack, we need to fund the attacker with some simnet bitcoin. Due to the way btcd works, we'll need to restart it to get some money going to the attacker. Run the following in order, but in their respective windows: 84 | 85 | ```sh 86 | # Window 5 87 | cmd/lncli-attacker.sh newaddress p2wkh # Copy the address 88 | 89 | # Window 1, CTRL+C then run 90 | cmd/start-btcd.sh --miningaddr=[address] # Use the address from Window 5 91 | 92 | # Window 5 93 | cmd/btcctl.sh generate 500 # This may take a few seconds 94 | ``` 95 | 96 | You can confirm it worked by checking the attacker's node wallet balance: 97 | 98 | ```sh 99 | # Window 5 100 | cmd/lncli-attacker.sh walletbalance 101 | ``` 102 | 103 | ### 4. Running the Attack 104 | 105 | Now that everything's in place, we'll begin our attack. The first thing we need to do is connect the two nodes as peers, and open a channel from the attacker to the victim. 106 | 107 | ```sh 108 | # Window 5 109 | cmd/lncli-victim.sh getinfo # Copy the "identity_pubkey" from this 110 | cmd/lncli-attacker.sh connect @localhost:30011 # Use the pubkey from the previous command 111 | 112 | cmd/lncli-attacker.sh openchannel 2000000 113 | cmd/btcctl.sh generate 10 # To confirm blocks for channel open 114 | cmd/lncli-attacker.sh listchannels # To view the open channel 115 | ``` 116 | 117 | You should see the channel in the results of `listchannels` after this. Now we need to make a quick backup of the attacker's current state, where all of the channel capacity is on their side. Run the following: 118 | 119 | ```sh 120 | # Window 5 121 | cp -r nodes/attacker nodes/attacker-evil 122 | ``` 123 | 124 | We'll then make a large payment to the victim, that we'll try to steal back: 125 | 126 | ```sh 127 | # Window 5 128 | cmd/lncli-victim.sh addinvoice 1000000 # Copy "pay_req" from this 129 | cmd/lncli-attacker.sh sendpayment --pay_req [pay_req] # Use the pay_req from before 130 | cmd/lncli-attacker.sh listchannels # Confirm the "remote_balance" is now 1000000 131 | ``` 132 | 133 | With that payment made, you'll want to turn off your attacker node in window 3 with a `ctrl+c`, and copy the old directory back in: 134 | 135 | ```sh 136 | # Window 3 137 | mv nodes/attacker nodes/attacker-honest 138 | mv nodes/attacker-evil nodes/attacker 139 | ``` 140 | 141 | Now let's assume some time has passed, and our attacker has been waiting for the victim's node to go offline. Go ahead and `ctrl+c` the node to stop it in window 2, and then let's start up the attacker in window 3 again: 142 | 143 | ```sh 144 | # Window 3 145 | cmd/start-attacker.sh 146 | 147 | # Window 5 148 | cmd/lncli-attacker.sh unlock # Enter 'password' from create 149 | cmd/lncli-attacker.sh listchannels 150 | ``` 151 | 152 | You should see in the output of `listchannels` that the entire balance is back in `local_balance`, ready for the attack. Now all we need to do is broadcast the old state by doing a force close. Grab the `"channel_point"` from `listchannels` for the arguments to the next command, split where the colon is: 153 | 154 | ```sh 155 | # Window 5 156 | cmd/lncli-attacker.sh closechannel --force 157 | ``` 158 | 159 | For instance, `"channel_point": "4c53040:0"` would be `closechannel --force 4c53040 0`. 160 | 161 | Now if we mine just one more block so that the force close transaction is mined, the watchtower will spring into action and rectify the situation. Go ahead and run: 162 | 163 | ```sh 164 | # Window 5 165 | cmd/btcctl.sh generate 1 166 | ``` 167 | 168 | You'll know it all worked if you see the following in the watchtower's logs in Window 4: 169 | 170 | ``` 171 | [INF] WTWR: Found 1 breach in (height=1366, hash=) 172 | [INF] WTWR: Dispatching punisher for client , breach-txid= 173 | [INF] WTWR: Publishing justice transaction for client= with txid= 174 | [INF] LNWL: Inserting unconfirmed transaction 175 | [INF] WTWR: Punishment for client with breach-txid= dispatched 176 | ``` 177 | 178 | Mine a few more blocks, start the victim's node back up, and you'll find the reward (minus fees) sitting in the victim's wallet: 179 | 180 | ```sh 181 | # Window 5 182 | cmd/btcctl.sh generate 10 183 | 184 | # Window 2 185 | cmd/start-victim.sh 186 | 187 | # Window 5 188 | cmd/lncli-victim.sh unlock # Enter 'password' from create 189 | cmd/lncli-victim.sh walletbalance 190 | ``` 191 | 192 | --- 193 | 194 | If you want to run through this all again, or you made a mistake along the way, just shut down your lightning nodes and run `cmd/restart.sh` to revert back to the original state. 195 | 196 | ## Troubleshooting 197 | 198 | ### `lnd / lncli / btcd / btcctl: command not found` 199 | 200 | Make sure you installed all of the requirements, and they're accessible in your `$PATH`. 201 | 202 | ### `unable to parse private watchtower address` 203 | 204 | Make sure you copied the victim's pubkey into `nodes/watchtower/lnd.conf` correctly. 205 | 206 | ### `Peer is not online` 207 | 208 | Just rerun the `connect` command to have the node check if the other is online. When you restart one of your nodes, it can take a little time for them to see each other as online again. 209 | 210 | ### `Block number out of range` 211 | 212 | Just generate a bunch of new blocks on btcd using `cmd/btcctl.sh generate 100`. It should fix itself. 213 | 214 | ### `cannot force close channel with state: ChanStatusDefault|ChanStatusBorked|ChanStatusLocalDataLoss` 215 | 216 | Your attacker node somehow got informed that their state was out of date, so it won't let you try to do a malicious force close. This is probably because you left your victim node online when swapping in the malicious state. 217 | --------------------------------------------------------------------------------