sensei
sensei copied to clipboard
Add support for remote signing
I think an important piece of Sensei will be adding the ability to run the keys outside of the node itself. This has been coming increasingly popular (for good reasons) and I think this makes perfect sense for the Sensei model. I think allowing users to run the keys to their node on a phone, server, pi, etc will be important. I've thought about this some and I'll lay out my ideas but happy to hear input.
Connecting:
It seems like most implementations have opted to go with the read-only node connecting out to the signer. However, I think for this use case the opposite is preferred. I think the signer should be the one to initiate the connection into the read-only node. If the signer is the one initiating the request and creating a persistent connection to the read-only node, then you wouldn't have to open up ports in a firewall and things like discoverability are solved. It could be as simple as the signer having a configuration that points it to a domain and it connects in on startup. This drastically reduces the complexity of connecting to two pieces together which is why I believe this is a good fit for Sensei and fits well when many users might have keys in different locations.
Identification:
In this model a very important piece is Identification and Authentication. If the signer is connecting in to the read-only node then we need a way to validate that it's a signer that we're expecting, and if so, is it allowed to talk to us and what node does it belong to. I think this could be solved with some simple sort of API Key (or macaroon) mechanism. When a signer connects it authenticates and gets linked up to the proper node based on who it identifies as.
Security:
Security is a big piece of this as well. The Lightning Signer project has been working on adding their ideas into LDK, however it's still in progress and I think there could be situations where you don't need all of the validation that Lightning Signer brings. I think an optimal solution is to let the user turn on or off certain security features or validations in the signer config. For example, a user could configure their signer to never sign a payment request above 1,000 sats or add a max limit to the wallet balance. This could enable tight security controls from the user while also letting them configure what they want.
Example:
1 Sensei node gets created in the standard form (keys on the node). Child node gets created in read-only mode. This child node is created, but essentially idle until Sensei detects a signer connections for this node. The user sets up the signer and points it at main Sensei node. Sensei detects the connection and the Child node operates just the same as any other node.
Architecture:
You've been reading my mind with all these ideas. Yes, this exact model has always been part of the plan :)
I'm curious why you say 'most implementations' do it the other way? I'm not familiar with LND's solution but I assume they do it the other way? The only other project I'm aware of that is doing this is Blockstream's Greenlight and what you describe is what they are doing (as far as I know)?
Regardless, this is the model I'm interested in pursuing. I've spoken with Lightning Signer guys a couple times about adopting there validated signer for Sensei. It's definitely do-able but is a significant amount of work.
Greenlight solves the identification problem by using mTLS and generates client tls certs when the read-only node is first created (presumably by the signer). When the signer issues commands they must provide the cert (which is issued by the Sensei node, so it can verify it's valid). That might be a good end-goal, starting with api keys is probably easier though.
I probably misspoke when saying 'most implementations'. I'm not familiar with Greenlight specifically and I had assumptions on clightning that might be incorrect. I know LND is the other way around. You must specify the signer's address to the read-only node on startup, so the discoverability is hard unless the signer is in a static place.
mTLS sounds like a good idea. I'll have to think on that a little bit. I like that being a method of verifying requests as well.
An additional thing to think about here is what happens when a signer is not connected. I think we should shoot for a very graceful event when that happens. Comparatively LND shuts down if it can't talk to the signer, so I think a better solution is to just fail that particular event but continue operating until the signer is reconnected.
Hello from the Validating Lightning Signer (VLS), renamed from Lightning Signer.
I wanted to note that the minimum set of validation required for security is actually pretty substantial. If the node fails to follow the Lightning protocol correctly, there are quite a few ways it can lose all funds. In particular, anything related to revocation, breach-remedy or the correct use of each derived key can result in complete loss.
I would say a majority of the VLS listed policies have to be enforced to achieve even basic security.
We are currently working on a gRPC client library, which should reduce the amount of code you would need to write for integration.
As to identification, the signer could identify itself by signing a challenge with its node secret key when it connects. For initial configuration, the hex node ID (which is just the serialized public key) could be copied from the signer to the configuration of the uninitialized node.
Thanks for the comments @devrandom. This makes sense. One question around VLS, if one were to implement all policies, does that require that the signer connect to a Bitcoin node as well as the remote, read-only node?
That's a good idea on the identification. I think there's a lot of possibilities there, but a signing device essentially has the right cryptography and signing built in that could be repurposed for our use case.
Thanks for the comments @devrandom. This makes sense. One question around VLS, if one were to implement all policies, does that require that the signer connect to a Bitcoin node as well as the remote, read-only node?
The signer does indeed needs to be aware of on-chain events, to make sure that the counterparty hasn't breached (broadcast an old state) and that the channel is actually active. This information could be provided via the same connection, proxied by the the read-only node. Since we can't trust the node, it should also be signed by a third party - an oracle for on-chain state.
We have a design doc with more details.
Noticing that the gRPC standard has the client make an HTTP/2 connection to the server, and then calls are initiated by the client. There's no way to make unary calls in the reverse direction, which is what we would have liked for this scenario.
Potential solutions:
- if only Rust needs to be supported, then perhaps a new tonic transport can be written that allows a connection in the other direction. This would be non-standard.
- A gRPC bi-di streaming call can be established, and then the actual calls would be a streamed object in the server -> client direction, followed by a streamed object in the other direction. That will work fine, but it requires creating a union type of all possible request objects and another one for all possible response objects. A bit cumbersome.
- A different protocol altogether. We have initial support for the C-Lightning hsmd wire protocol, which is not opinionated about the TCP initiator.
Thanks for the comments @devrandom. This makes sense. One question around VLS, if one were to implement all policies, does that require that the signer connect to a Bitcoin node as well as the remote, read-only node?
Ken Sedgwick here, also from the VLS project, greetings ...
I wanted to add that the VLS project includes a "front-end" process which performs all of the needed communication with the Bitcoin node (batteries included).
Thanks for the information. I need to take some time and think more deeply on this, but for the use case of doing something like running the keys on a phone, what's the VLS proposed solution to this? Use something like Neutrino or trust a third party with block data?
With neutrino, you still have to trust third parties to give you correct compact filters and not omit transactions.
So you do need to receive block information that is authenticated by third parties. The plan is to reduce reliance on a single party by having an ecosystem of compact-filter (or utreexo) oracles The signer would require a minimum number of signatures from these oracles, so a minority of the oracles can't collude.
With either CF or utreexo, the amount of data that has to be transferred would be pretty small.
That sounds interesting @devrandom .
Just a small addition, that blocks don't have to be downloaded from the same node who gave the client the filter.
Arguably it is better for privacy to download the blocks from a regular Bitcoin node (with fresh Tor identity).
This does not solve the filter withholding problem mentioned above, but at least the filter-provider will not learn which blocks the client is interested in.
Agreed, the oracles should be completely independent of the node block provider. Since they just sign the UTXO set, which is a global piece of data, they can just broadcast their signatures (e.g. blockstream satellite) and don't have to be queried by each client.