Files
python-teos/watchtower-plugin

Watchtower client

This is a Watchtower client plugin to interact with an Eye of Satoshi tower, and eventually to any BOLT13 compliant Watchtower.

The plugin manages all the client-side logic to send appointment to a number of registered towers every time a new commitment transaction is generated. It also keeps track of a summary of what has been sent and what the tower has responded.

The plugin has the following methods:

  • registertower tower_id : registers the user id (compressed public key) with a given tower.
  • list_towers: lists all the registered towers.
  • gettowerinfo tower_id: gets all the locally stored data about a given tower.
  • retrytower tower_id: tries to send pending appointment to a (previously) unreachable tower.
  • getappointment tower_id locator: queries an appointment to a given tower.

The plugin also has an implicit add_appointment method triggered via the commitment_revocation hook.

Config file, data folder and first bootstrap

The plugin will create a data folder under the user's home directory (~/.watchtower). All the plugin's data will be stored in the data folder. A config file (watchtower.conf) can be placed in the data folder to modify some of the configuration defaults (check template.conf).

On first bootstrap, the plugin will generate a key pair that will be used as the user identifier. All request from the user will be signed using that the secret key, so the tower can authenticate the user after the registration process (registertower).

All the appointments generated by the tower, as well as all the registered towers data, are stored on a leveldb under ~/.watchtower/towers.

Getting started

Registering with a tower

Once the plugin is loaded in your node, the first step will be to register your node with a active tower. You can do so by running:

lightning-cli registertower tower_id [host, port]

Where tower_id represents the target tower public key. As a convenience, tower_id may be of the form tower_id@host or id@host:port. In this case, the host and port parameters must be omitted. Port defaults to 9814 and can be changed in the config file.

Example

lightning-cli registertower 0230053e39c53b8bcb43354a4ed886b8082af1d1e8fc14956e60ad0592bfdfab51@localhost

If the tower is online, you should get back a response similar to this:

{
   "available_slots": 10000,
   "public_key": "02d5001fb4204fd9ab26f06c7869e3820906be565c5b449ecfb0251af4eff1c752",
   "subscription_expiry": 4753
}

Where available_slots is the amount of free slots the user has available in the tower, public_key is the user's public key (user_id for now on) and subscription_expiry is the block height when the subscription will expire. In this example, the user has 10000 slots to use in, roughly, the next month.

Notice that, ideally, the client and the tower will agree on the subscription details (available_slots and subscription_expiry). Currently, those depend only on the tower, since it is offering the service for free. However, in the current state, hitting registertower again will add another 10000 slots and reset the time to current_height + roughtly_one_mont_in_blocks.

Sending data to the tower

Once your node is registered with at least one tower it will start sending appointments to the tower for every commitment transaction update on any of your channels. In the current version of the plugin, everything is sent to every register tower (full replication). There's nothing to be done here, under normal conditions, the plugin takes care of it.

Checking the state of the towers

There are two commands you can run to check what is going on with the towers you are sending data to list_towers and gettowerinfo.

listtowers will simply give you an overview of what towers you are registered with and what the current state of them:

lightning-cli listtowers
{
   "towers": [
      {
         "id": "0230053e39c53b8bcb43354a4ed886b8082af1d1e8fc14956e60ad0592bfdfab51",
         "netaddr": "http://localhost:1234",
         "status": "unreachable",
         "available_slots": 9998,
         "pending_appointments": [
            "fff5ceaff045b0e4ddf11936d66c02a0",
            "0698bd6c0e1f74c11857b99ef6009156"
         ],
         "invalid_appointments": []
      }
   ]
}

The overview contains the id and network address of the tower (netaddr), as well as the current status and two list of appointments: pending and invalid.

The tower can be in 5 status:

  • reachable: the tower is reachable at the given network address.
  • temporarily unreachable: the tower is temporarily unreachable, meaning that one of the last requests sent to it has failed.
  • unreachable: the tower has been unreachable for a while.
  • misbehaving: the tower has send us incorrect data.
  • subscription_error: the subscription with the tower has expired or run out of slots.

The main difference between temporarily unreachable and unreachable is has much time has passed. If a tower is temporarily unreachable, a backoff strategy will be triggered and all the appointments that cannot be delivered will be stored under pending_appointments. If the tower comes back online within the retry strategy, every pending appointment will be sent trough and the tower will be flagged back as reachable. However, if the backoff strategy ends up giving up, the tower will be flagged as unreachable.

If the client receives data from a tower that it is not properly signed, the tower is flagged as misbehaving and it is abandoned, meaning that no more appointments will be sent to it. This state should never be reached by honest towers.

A subscription error means that the subscription needs to be renewed (hit registertower again).

Regarding pending_appointments and invalid_appointments they store the data that is pending to be sent to the tower (for unreachable towers) and the appointments that have been rejected by the tower for being invalid, respectively. The later should never get populated for honest clients.

gettowerinfo will give us more detailed information about the tower:

Usage

lightning-cli gettowerinfo tower_id

Call

lightning-cli gettowerinfo 0230053e39c53b8bcb43354a4ed886b8082af1d1e8fc14956e60ad0592bfdfab51

Return

{
   "id": "03623859b406e83ec7fab195cd67f1ba9e6d46bc07fac3e900351d657dd8ad05aa",
   "netaddr": "http://localhost:1234",
   "available_slots": 9998,
   "status": "unreachable",
   "appointments": {
      "3866ee02e6249454bba39fd52fbf673b": "rytpauxwoyic3ui47kojj7cjg415t8cws1nu3r9gj9h6dp5mpgkky9o9i8qfud5tguo85gyhfr7pjja368isq6di1c9ghic9pckz3838",
      "1a03e9239a459d8166cd6b7dba781574": "d7b9ucq8rihduk8w6c3fmeq86hcmpk5kfoqoerpjpiob4fsc9kbiqi8uz484tk9bcxnehdn8prt9s8wrejh78pan995f6314sf6hht89"
   },
   "pending_appointments": [
      {
         "appointment": {
            "locator": "fff5ceaff045b0e4ddf11936d66c02a0",
            "to_self_delay": 20,
            "encrypted_blob": "2cb558721e22c34d62aab6395aff3b0611f7ce29fbb8d699c998e4874649be01097ed910ffd6253738b4d565eb6b9f1df1dcb9f4fbe2c0b71fb56d497fe1e0a2a41647bfb1d88c7270d502f19472ef39de74e05e7651cd31c705e7daa5f3bd91151c25ef8def635e172f720baef1141f6291a660bdc2b702396f89b69254ccae9f92eb12ed3d6c782a4309eba0ab78cb1d7d8c39e7524c476777e5022e43b8f201aae0c69aaa6c8d9bd864ed1b16459967efbbb63b16ced56b585279c381fb9a03012d578bd90b9de2e718259ddeb4d3acb91eabe5328ac6089642a4efc248e7b940a2f68c8d106868514dfd29a5f3dd8d07ece518c604311425d4"
         },
         "signature": "rns7z4orgjninrfguygzuij5ndek5544kdoncudbx9kqcxk3gpjjyd3f7qmixygzobko3n1nqwqycmi89uo6d8e56k3iyd3trtihec7s"
      },
      {
         "appointment": {
            "locator": "0698bd6c0e1f74c11857b99ef6009156",
            "to_self_delay": 20,
            "encrypted_blob": "b77b5eb73b4901abe24427c7786233fce6aefb4655757802f7ff342a1ab90d8b20a65941038fbe4dba9c775f25650f6bb4c3f3caf6906c4b2c6b6bc09c943a61c57d14204e36a4094b92dc45492d0eaef7165ec5a7af4e8d33c38bfbeeb4e21e9fd699f1046ad0e0a442ff113b3d2f1e956cf912b075c4e0845d009c5913c06c6336c6dd1682e0a0ec696026970388c9a68a7e84d6c25b8346cb39bc0d0d3610beccee02fe999981be1c209d4efd0b2e966068e31c5f5e34507c739a849e1527c7102afbc610e344d6757db57fe3994119880856c6ab997a257ed477a66d5e95505052558b856e4b1df23e7d0ed49164c5063bc731c3ad6bc08a35"
         },
         "signature": "dhcr7jmokewgxnrdh4o6zaq3wxrfjdmkhj5y81rcff1utr7riut7kn7yzzoaxhsxezgu7xmq93fwsb1cc6ghbbrwxgwt5tyasr3aswf1"
      }
   ],
   "invalid_appointments": [],
   "misbehaving_proof": null
}

Notice that there are two new fields in this report: appointments and misbehaving_proof.

appointments contains a collection of locator:tower_signature pairs of all the appointments sent and accepted by the tower.

misbehaving_proof contains a proof of misbehaviour of the tower in the form of an appointment:wrong_signature pair. In this case the tower has not misbehaved, so it is set to null.

Finally, notice how pending_appointments now contains all the data about the pending appointments (the full appointment plus the signature). The same applies to invalid_appointments.

Manually retrying a tower

If a tower has been flagged as unreachable (after the default backoff has failed) or there has been a subscription error, the tower won't be tried again until the user manually requests so. This can be managed with the retrytower command:

Usage

lightning-cli retrytower tower_id

Call

lightning-cli retrytower 03623859b406e83ec7fab195cd67f1ba9e6d46bc07fac3e900351d657dd8ad05aa

Return

"Retrying tower 
03623859b406e83ec7fab195cd67f1ba9e6d46bc07fac3e900351d657dd8ad05aa"

Notice that this will only work provided the tower is in unreachable or subscription error state. A tower cannot be retried if it is already being retried (temporarily unreachable), or if it is misbehaving.

Query data from a tower

Data can be queried from a tower to check, for instance, that the tower is keeping it or that it is correct. This can be done using the getappointment command:

Usage

lightning-cli getappointment 03623859b406e83ec7fab195cd67f1ba9e6d46bc07fac3e900351d657dd8ad05aa  3866ee02e6249454bba39fd52fbf673b

Call

lightning-cli getappointment 03623859b406e83ec7fab195cd67f1ba9e6d46bc07fac3e900351d657dd8ad05aa  3866ee02e6249454bba39fd52fbf673b

Return

{
   "encrypted_blob": "e0622ceac74be1def70de5fdcaf0f498f9f9abae54cf2418358f8843eb22b683607ce298ea19f990fd74d9bf673c3d3c5a171fa6a2f635f6a2bbf06167504400000545ec192007811ef08027ff3dcec42058a3f309c0bb3b0f42990c0e46802b86cd7dba4053800d90766d3c050d1a38ed3a949f9bc02b30a641a43dd4566463607d4b5db1853ef6013e2df0b08cf5355a2f8f0aef23025a59f53e30f1aaad9c079d2f468b118f3450c33d015ba0d03d812328f5f16ef8ea4cefc75531f0f6e828b224c190980f1e7d8704853349a75e5d3e114c2cdf275b952e9c52457194dbf8991912ced217f20cc3f6a694a20cc5c274b984c48004777deba3",
   "locator": "3866ee02e6249454bba39fd52fbf673b",
   "status": "being_watched",
   "to_self_delay": 20
}