Register a node
In this section we'll use a developer certificate to register a node.
We'll start with creating a seed that is used to derive node-secrets from. Each node on the lightning network is identified by a public key and the corresponding private key is one of these secrets. In the next step, we'll connect to the Scheduler using a developer identity and register the node. This requires you to prove that you own the private key mentioned previously.
At the end of this section your node will be registered on Greenlight and you will have a device-identity that can be used to connect the node.
Creating a seed
Let's start with the seed secret: the seed secret is a 32 byte secret that all other secrets and private keys are derived from, as such it is paramount that this secret never leaves your user's device and is only handled by the Signer.
We suggest to derive the seed secret from a BIP 39 seed phrase, so the user can back it up on a physical piece of paper, steel plate, or whatever creative way of storing it they can think of.
Note
The following code-snippets build on each other. By copying each snippet after the other you should get a working example.
Install the bip39
and rand
crates required for secure randomness and conversion of mnemonics. Add the following lines to your Cargo.toml
[dependencies]
rand = "*"
bip39 = { version = "*", features=["rand_core"] }
Install the bip39
package which we'll use to encode the
seed secret as a seed phrase:
pip install bip39
Now we can securely generate some randomness, encode it as BIP 39 phrase and then convert it into a seed secret we can use:
use bip39::{Mnemonic, Language};
let mut rng = rand::thread_rng();
let m = Mnemonic::generate_in_with(&mut rng, Language::English, 24).unwrap();
let phrase = m.word_iter().fold("".to_string(), |c, n| c + " " + n);
// Prompt user to safely store the phrase
let seed = &m.to_seed("")[0..32]; // Only need the first 32 bytes
let secret = seed[0..32].to_vec();
// Store the seed on the filesystem, or secure configuration system
import bip39
import secrets # Make sure to use cryptographically sound randomness
rand = secrets.randbits(256).to_bytes(32, 'big') # 32 bytes of randomness
phrase = bip39.encode_bytes(rand)
# Prompt user to safely store the phrase
seed = bip39.phrase_to_seed(phrase)[:32] # Only need 32 bytes
# Store the seed on the filesystem, or secure configuration system
Important
Remember to store the seed somewhere (file on disk, registry, etc) because without it, you will not have access to the node, and any funds on the node will be lost forever! We mean it when we say you're the only one with access to the seed!
Registering the node
We'll configure mTLS using our developer identity. Any connection using the
TlsConfig
-object specified below will allow you to register new Greenlight
nodes.
use gl_client::tls::TlsConfig;
// Creating a new `TlsConfig` object using your developer certificate
// cert: contains the content of `client.crt`
// key: contains the content of `client-key.pem`
let tls = TlsConfig::new().unwrap().identity(cert, key);
from glclient import TlsConfig
# Creating a new `TlsConfig` object using your developer certificate
# - cert: contains the content of `client.crt`
# - key: contains the content of `client-key.pem`
tls = TlsConfig().identity(cert, key)
The next step is to create the Signer
which processes incoming signature
requests, and is used when registering a node to prove ownership of
the private key. The last thing to decide is which network we want the
node to run on. You can chose between the following networks:
testnet
bitcoin
We'll pick bitcoin
, because ... reckless 😉
use gl_client::signer::Signer;
use gl_client::bitcoin::Network;
let signer = Signer::new(secret, Network::Bitcoin, tls).unwrap();
from glclient import Signer
signer = Signer(seed, network="bitcoin", tls=tls)
Registering a new node
Registering a node with the Scheduler
creates the node on the
Greenlight service and ensures everything is setup to start the node.
In order to register a node, the client needs to prove it has access to the
node's private key. Since the private key is managed exclusively by the
Signer
we need to pass the Signer
to the
Scheduler
:
use gl_client::scheduler::Scheduler;
use gl_client::bitcoin::Network;
let scheduler = Scheduler::new(signer.node_id(), Network::Bitcoin).await.unwrap();
// Passing in the signer is required because the client needs to prove
// ownership of the `node_id`
scheduler.register(&signer, None).await.unwrap();
from glclient import Scheduler
scheduler = Scheduler(
node_id=signer.node_id(),
network="bitcoin",
tls=tls,
)
# Passing in the signer is required because the client needs to prove
# ownership of the `node_id`
res = scheduler.register(signer)
The result of register
contains the credentials that can be used
going forward to talk to the scheduler and the node itself.
Important
Please make sure to store them somewhere safe, since anyone with these credentials can access your node.
let tls = TlsConfig::new().unwrap().identity(res.device_cert, res.device_key);
// Use the configured `tls` instance when creating `Scheduler` and `Signer`
// instance going forward
let signer = Signer::new(seed.to_vec(), Network::Bitcoin, tls.clone()).unwrap();
let scheduler = Scheduler::with(signer.node_id(), Network::Bitcoin, "uri".to_string(), &tls).await.unwrap();
let mut node: ClnClient = scheduler.schedule(tls.clone()).await.unwrap();
tls = TlsConfig().identity(res.device_cert, res.device_key)
# Use the configured `tls` instance when creating `Scheduler` and `Signer`
# instance going forward
signer = Signer(seed, network="bitcoin", tls=tls)
scheduler = Scheduler(node_id=signer.node_id(), network="bitcoin", tls=tls)
node = scheduler.node()
If you get an error about a certificate verification failure when
talking to the node, you most likely are using an unconfigured
TlsConfig
that doesn't have access to the node. See
Security for details on how authentication and
authorization work under the hood.