Skip to content

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.