Ideally, I would like clightning to have very simple watch tower capabilities.
The use case I have in mind has quite simple UX:
Merchant A knows Merchant B:
Merchant A share his watch tower endpoint (URI exposing a REST API made by BTCPAY) to Merchant B.
Merchant B can then configure his store to send watch information after every lightning payment received to the store. (Merchant B don't have to care to send watch information when he spends the money)
BTCPay would just relay this information to the underlying clightning instance.
Note that my case is quite simple: Merchant A knows Merchant B beforehand so no need of "discovery protocol". There is also no need of P2P level protocol at clightning level, as BTCPay is the one exposing the REST endpoints. Only things needed is a RPC method at clightning level to do this.
Hi @NicolasDorier, sorry but I cannot visualize exactly what you want as interface. Do you need some serialization of a specific channel state and RPC calls to get current channel state, and override that state? Or something else entirely?
Sorry I don't know a lot about the internal implementation of lightning.
The problem I want to solve is that right now, merchants are taking significant risks with lightning. If they get DDoS an attacker might be able to close the channel with an invalidated state.
I want to provide them a way to outsource channel monitoring.
In BTCPay, merchant knows each other, and can outsource such service to each other, I am not asking for any particular P2P protocol for watch towers. Just a RPC call where the merchant can tell to another merchant "If you see this old state on chain, broadcast this transaction to sweep the channel, so justice can be served".
It can be a simple monitor hash tx, where if the merchant see the preimage of hash on the blockchain, then he will broadcast tx. Though I think you may have a better way of monitoring channels in lightning.
Okay, that seems a little more designable.
Note however that currently our blockchain monitoring seems a little underoptimized: #1337.
Also ping @rustyrussell as I am unaware of status of plans for a "real" watchtower. I understand how merchants would want to have some assurance of channel justice even though a "real" watchtower system has not completed at LN yet.
Now, first some things:
A rough draft would be something like:
getshachain channelid [prev_shachain] - Get the current shachain representation for the current state of the given channel ID. Also provide funding_txid and funding_outnum. If prev_shachain is specified and the given prev_shachain matches the actual current shachain state, then block until a change in shachain state occurs.
monitorshachain funding_txid funding_outnum shachain address - Monitor the given funding outpoint. If its funding transaction gets spent, try a revocation key from the given shachain structure and send it to the given onchain address. If an existing monitor for that funding outpoint exists, overwrite the shachain and address.
Aside from getting the shachain, I think monitoring a funding transaction can be done in a separate project, not necessarily c-lightning. So the most minimal thing we need to provide is getshachain from lightningd.
I would be happy to implement the watch tower at NBXplorer level actually. Looking the chain in search for transaction matching features is already coded. If I can get the minimal information from clightning I need to be able to track, it should be pretty easy for my explorer.
It makes even more sense because BTCPay allow merchant to use their own lightning charge, so BTCPay can only create and monitor invoices but not steal any funds. The BTCPay host would be able to monitor channels on behalf of its merchants, this is pretty cool.
Okay, I will add the querying command for the current channel state.
@rustyrussell I would like to confirm my understanding of BOLT 3 and 5.
I am taking the point of view of the cheated node, as that is what a watchtower protects.
In order to make a justice transaction to handle my counterparty cheating transaction, I need:
per_commitment_secret that was revealed to me by the counterparty during revoke_and_ack message. This is stored in the shachain structure, which is a 64-bit minimum index, and up to 49 pairs of a 256-bit hash and 64-bit index. I identify the correct secret by getting the index in obscured form from the nLocktime and nSequence of the commitment transaction.payment_basepoint. Aside from using my own payment_basepoint to claim the THEIR_UNILATERAL/TO_US output, the two basepoints are also used to unmask the obscured commitment index in the shachain, so that I can determine the commitment secret there.revocation_basepoint_secret. This is used in computing the revocation private key together with the per_commitment_point.delayed_payment_basepoint. This is used in computing the script for the THEIR_UNILATERAL/TO_THEM output, otherwise it would be impossible for me to claim this as this is a P2WSH.htlc_basepoint. These are used in computing the scripts for HTLCs offered by us and by them, again these are P2WSH. I will use the revocation key to revoke these.So aside from the shachain I need to provide these to the watchtower:
our_payment_basepointtheir_payment_basepointour_revocation_basepoint_secrettheir_delayed_payment_basepointour_htlc_basepointtheir_htlc_basepointfunding_txid and funding_outnum of the channelSo on my explorer I will need to find out if (funding_txid, funding_outnum) is spent.
Then from your command output, do you have a small process on how I can spend the output of transaction? (telling me the math)
Ah actually, can you also give me the scriptPubKey in hex of the funding as a convenience? This facilitate the scanning as I will not need to do an outpoint lookup in database. (I am already doing a lookup based on infered scriptPubKey from the scriptSig/witScript)
Then from your command output, do you have a small process on how I can spend the output of transaction? (telling me the math)
See BOLT 3 and BOLT 5. My understanding of the math is shaky and that is why I tried summoning @rustyrussell here: my explanation might make things worse by misinforming you!
There is also, the code in handle_their_cheat inside onchaind/onchain.c. Be warned that there is a lot of very very similar (BUT SUBTLY DIFFERENT) code in the various handle_* functions in the same file, do not get confused reading them. You could copy the handle_their_cheat function in a separate file to help against this.
AS I UNDERSTAND IT (please make sure to run this by a real mathematician), you get:
per_commitment_point = per_commitment_secret * G where G is the standard generator point for Bitcoin secp256k1._basepoint is computed similarly from every _secret, e.g. payment_basepoint = payment_secret * G. Basepoints are shared between both sides as they are public, but secrets are kept secret (i.e. our_revocation_basepoint_secret is given to the WatchTower and allows the WatchTower to collude with the other side to steal the funds).per_commitment_point and the *_basepoint. For example, if you are claiming your output from their-side commitment transaction, you need the payment_pubkey for that commitment transaction, which is payment_pubkey = payment_basepoint + SHA256(per_commitment_point || payment_basepoint) * G, where SHA256 is the standard 256-bit SHA-2 hash function._secret you can get the corresponding _privkey for a specific _pubkey: The payment_privkey = payment_secret + SHA256(per_commitment_point || payment_basepoint).revocation_basepoint and the other side per_commitment_point as revocation_pubkey = SHA256(revocation_basepoint || per_commitment_point) * revocation_basepoint + SHA256(per_commitment_point || revocation_basepoint) * per_commitment_point.per_commitment_secret as part of revoke_and_ack, you can now compute the revocation_privkey as revocation_privkey = SHA256(revocation_basepoint || per_commitment_point) * revocation_secret + SHA256(per_commitment_point || revocation_basepoint) * per_commitment_secret.Roughly, when you see the funding transaction spent:
nLockTime and 24 bits of nSequence; the commitment number is obscured using the payment basepoints, see BOLT3 "Commitment Transaction".ccan/ccan/shachain directory). The shachain is effectively a function index -> Maybe per_commitment_secret. Note that the shachain index starts from the max value 2^48 - 1, but the commitment number starts at 0. So make sure to convert correctly!per_commitment_secret does not match the commitment transaction P2WPKH/P2WSH hashes, then it is either a valid commitment, our own commitment, or a bilateral close transaction. If so justice does not need to be served as it is valid. Either that or you have a bug in your code hahahaha.scriptSig and witness data). There are four output types, see BOLT 5 for how each is handled in detail:payment_pubkey for the given per_commitment_point of this commitment transaction and our own payment_basepoint.revocation_pubkey (derived from our own revocation_basepoint and the per_commitment_point of the commitment) and their delayed_payment_pubkey (derived from their delayed_payment_basepoint and the per_commitment_point). You can claim this using a signature using the revocation_secret. See BOLT 3.revocation_pubkey, and our and their htlc_pubkey, and payment_preimage. See BOLT 3. You can claim this with the revocation_secret, and if the other side could claim it first the other side claim can also be revoked with the revocation_secret somehow.It might be useful to separate the claims of the HTLCs from the claims of the "main" outputs, i.e. multiple justice transactions, not sure though, wait for @rustyrussell to comment.
Ah actually, can you also give me the
scriptPubKeyin hex of the funding as a convenience? This facilitate the scanning as I will not need to do an outpoint lookup in database. (I am already doing a lookup based on inferedscriptPubKeyfrom thescriptSig/witScript)
Lemme see how easy it is; in principle funding_txid and funding_outnum is enough.
Would it not be in principle easier using funding_txid and funding_outnum, however? When a new block comes in, scan each txin for the funding_txid and funding_outnum? Or are you using an SPV interface like Electrum?
funding_txid and funding_outnum is enough. But my explorer is wired to query on scriptPubKeys, never outpoint. Adding one query for each outpoint for each transaction might make things too slow, so it would make my life way easier.
I could workaround this by using Bitcoin RPC to fetch the output. (I think)
Actually Bitcoin RPC does not seem to allow me to fetch an utxo (and thus the scriptPubKey I need) given the outpoint... I hate Bitcoin RPC (-_-)
Edit: Actually the REST interface of Bitcoin seems to expose this, but activating it is yet another setting I prefer not to have my user to manually set... so if you can give me the scriptPubKey you save me lot's of time and support...
Okay, to derive the scriptPubKey of the funding outpoint I need to make a 2-of-2 2 <pubkey1> <pubkey2> 2 OP_CHECKMULTISIG P2WSH, where <pubkey1> is the numerically lesser of the DER-encoded pubkeys of both sides. The local funding pubkey is derivable using derive_basepoints from the channel->seed, while the remote funding pubkey is stored in the channel->channel_info.remote_fundingkey.
@ZmnSCPxj I can reconstruct the scriptPubKey on my side if you prefer.
It is easy to get the scriptpubkey once the pubkeys are known, it was digging through our structures to find both pubkeys that was hard.
@NicolasDorier let us continue discussion here.
In #1381 @NicolasDorier described the use case more precisely:
Having a trusted watch tower seems to defeat the point. My goal was that a web of cooperating merchant agree to watch for each other without trusting each other.
As #1381 provides enough information for the WatchTower to write and sign the justice transactions, an obvious attack by WatchTower would be to direct the justice transaction output to itself and the counterparty in the channel. The counterparty publishes a revoked transaction, the WatchTower revokes it --- in favor of itself and the counterparty rather than the protected node.
The trustless alternative would be for the protected node to create and sign (multiple!) justice transactions for each revoked other side commitment transaction of each channel it has. This has the advantage that the WatchTower is incapable of rewriting the justice transaction. This has a lot of engineering changes in c-lightning:
closingd/closing.c to something in common/.Because of the rather large engineering changes I want to wait for comments from @rustyrussell and @cdecker.
Discussion on the mailinglist has been quite active.
The general consensus so far seems to be that the protected node will provide a (txid[:16], blob) pair, where txid[:16] means "the first 16 bytes of the txid". The "txid" here is the txid of the revoked commitment transaction.
The WatchTower process basically is that when a new block is mined, we take the txid of every transaction in the block. If the first 16 bytes of a txid matches one of the (txid[:16], blob) pairs being watched, then we use the last 16 bytes of the txid to decrypt the blob. If the blob is decrypted correctly (maybe it has some embedded HMAC or checksum to determine if the decryption was correct), then the blob contains signatures for one or more justice transactions that properly reclaim the revoked transaction. We use one justice transaction for the remote side main output, and one justice transaction for every HTLC, using the fact that the HTLC outpoints have two stages, both of which are revokable, and that timely behavior is needed only to claim the second stage (so the WatchTower does not need to be given a signature for a justice transaction that can claim the first stage, only the second CSV-locked stage).
This probably requires more discussion and thought, so if you do not mind, I would like to let this topic stew a little while before we proceed to something to implement as a first cut.
I like the encrypted blob approach, it seems quite simple for the WatchTower.
Does c-lightning support watchtower now?
Not yet. It is likely we will imitate whatever lnd does.