Ring of Fire

A ring of fire is a circle of equal balanced channels. By forming a circle, your node gets better connected within the network and make a profit by routing transactions.

Ring of fire theory

To route transactions, you'll need both incoming and outgoing liquidity. Always try to open channels with nodes that carry a proper reputation. The BOS score on indexes such as 1ML gives you an idea of reputation but it is still difficult to check at this stage.

Best practices

It is important to realise that a ring of fire only works when all nodes remain connected to the network. If a node goes offline or a participant closes a channel, the route will be interrupted. With Satoshi Radio, the largest bitcoin community in the Netherlands, we came to a sweet spot of around 8-10 people.

Roles

Each ring of fire has its own ringleader. A ringleader guides the process and carries out the steps which need to be done using terminal. All participants ensure they pass on the information requested by our ringleader and make sure they are available during the proces in case any actions need to be performed.

Before we start

The ringleader will determine who will connect with whom and makes up the channel direction. A diagram like the one below will really help your group to understand their position within the ring.

Step 1: collect information

The ringleader is responsible to collect all node information from each ring member and place them in the pinned overview group message.

All members claim their node with @cheeserobot in a Telegram DM using the following steps:

1. Send a DM with the text /claim space your node ID to @cheeserobot

2. @cheeserobot will ask to sign a message. Open the Ride the Lightning app with Umbrel and sign the message.

Copy the message text and sign with TR

3. Copy your signature in RTL and send @cheeserobot the following command /claim space your node ID space signature

4. Your node is now claimed!

In case your run into some issues, reach out to the @cheeserobot support channel

5. Open your ring of fire telegram group and share your node details by entering the following command: /node your ringleader needs this information to get started.

6. The ringleader pins a message with all node information using the same order we made in our diagram and monitors if all nodes are tor enabled + funded before we go to step 2.

🧅🙌🏻@I2Sappig 02b6b8e6b683811f358e9602f8271694951b92dd66ff0841501f2ce9ddcf7bc2fc Alias: 2Sappig

🧅🙌🏻@stocktoflow 02e425b0102cwef0b8c1bce9d42001a41f615ad5we17247d57d1949bwefff3a4 Alias: BuidlBuidl

🧅🙌🏻@rebtorr 03bd5aef25b963cc256bdd009bc8084891e53cd067b781c68786f333e63a186e0f Alias: RebtoriaNode

🧅🙌🏻@udo327 03a97a8f6f2bd2293285474a4be48e8d971f333aa3183c95e51d0f704fd42fd92e Alias: udo327

🧅🙌🏻@PeteDoo 02258a9aa9463749bf729511af78b1ea339ecdb36b37a6e18a8c8caa895228bfcb Alias: on_fire

🧅🙌🏻Jupil 026b6cc74185ddc03cf5c57641b1c853dbab5fb2b738883c3891c5b4858f4e68b9 Alias: Jupil

🧅🙌🏻@stacktracey 025df45996b60d6f5a834f0963d2c3da6a0f187b7b434dc0cc3af46cf7956c3603 Alias: none

🧅🙌🏻@JJ 03581cae4671ebfc1d23fe971411216ee004102ab440069409c4748167ba96118e Alias: none

🧅🙌🏻@maartemmobiel 0334c4b3e5bc2c4677974561cc484f50181a5983cc956ceac473fb3f86c11f6299 Alias: none

🧅 Tor enabled 🙌🏻 Node topped up and ready to open a channel ⏳ Channel opening pending ✅ Channel established

Step 2: open channels

  1. Look at our diagram to who you are going to open a channel. We go clockwise so in this case @l2sappig opens a channel of 50000 sats to @stocktoflow, @stocktoflow to @rebtorr and so on.

To open a channel you need the Node ID + tor address + port. To find this information, head over to 1ML and paste the node ID of the person you want to open a channel with. Copy the channel info on 1ML, click open a new channel in Umbrel and hit paste.

To avoid paying higher fees than necessary, check the mempool for the suggested fee.

2. After establishing all channels, every ring member will open RTL and go to: Lightning -> Peers/Channels -> Your channel -> Actions -> Update fee policy Set base and fee to 0 and hit save.

3. Notify your ringleader in Telegram when your fee policy is changed.

@ringleader: monitors if all channels are opened and update the status in the pinned message (step 1) with ✅ behind each established channel

Step 3: monitor node connections

Installing Ringtools

  1. Login using SSH

2. Upgrade pip

pip3 install --upgrade pip

3. Run command:

git clone https://github.com/StijnBTC/Ringtools

4. After downloading go to the Ringtools directory

cd Ringtools

5. Run command:

pip3 install -r requirements.txt 

This can take a while, be patient.

6. Run command:

sudo nano channels.txt

7. Replace everything in the file for the channel IDs you want to check. Look up the channel IDs of all channels, for example on 1ml.com.

You can filter on newest channel to make your life a little bit easier in case someone has lots of open channels.

In our case:

766994022803570689
766999520399654913
766992923372093441
767133660771254273
766994022803505153
767068789744009217
767000619939528705
766994022802128897
766995122387681281

8. Save this file by closing it with Ctrl+X and then Enter and Y to confirm.

9. Finally, run Ringtools with the following command:

python3 ringtools.py -f -l status

10. You will now see the overview of all nodes + their status. Make sure no nodes are red before continuing to step 4.

On_fire - Udo327 is in red. Our nodes have to gossip, this can take up to +/-48 hours
All nodes are connected, you can continue with step 4

To close Ringtools, hit Ctrl+C

Resources

Step 4: rebalance

Now all nodes are connected we can start rebalancing our channels to make sure we have liquidity in both directions.

  1. Login using ssh

  2. Download igniter

sudo wget https://raw.githubusercontent.com/RooSoft/igniter/main/igniter.sh

3. Enter the following command:

sudo chmod u+x igniter.sh

4. Enter the following command:

sudo nano igniter.sh

5. Edit line 7-15 of the script using the node ID's in order of our diagram. Change AMOUNT (line 18) to half the channel size (500000 sats in our case) and change OUTGOING CHAIN ID to your own (ringleader) outgoing channel ID.

#!/bin/bash

# before running this script, the array below must be populated with
# all nodes pub keys that will be part of the route

declare pub_keys=(
    03bd5aef25b963cc256bdd009bc8084891e53cd067b781c68786f333e63a186e0f # first hop pub key
    03a97a8f6f2bd2293285474a4be48e8d971f333aa3183c95e51d0f704fd42fd92e # next hop's pub key
    02258a9aa9463749bf729511af78b1ea339ecdb36b37a6e18a8c8caa895228bfcb # next hop's pub key
    026b6cc74185ddc03cf5c57641b1c853dbab5fb2b738883c3891c5b4858f4e68b9 # next hop's pub key
    025df45996b60d6f5a834f0963d2c3da6a0f187b7b434dc0cc3af46cf7956c3603 # next hop's pub key
    03581cae4671ebfc1d23fe971411216ee004102ab440069409c4748167ba96118e # next hop's pub key
    0334c4b3e5bc2c4677974561cc484f50181a5983cc956ceac473fb3f86c11f6299 # next hop's pub key
    02b6b8e6b683811f358e9602f8271694951b92dd66ff0841501f2ce9ddcf7bc2fc # next hop's pub key
    02e425b0102cf690b8c1bce9d42771a41f615ad5c417247d57d1949b7e8e7ff3a4 # your node's pub key
    )

AMOUNT=250000                            # value in satoshis to transmit
OUTGOING_CHAN_ID=766999520399654913  # initial channel to transmit from
MAX_FEE=10000                          # Max fee, in sats that you're prepared to pay.


####################################################
## the remaining of this script can remain untouched

# Join pub keys into single string at $HOPS
IFS=, eval 'HOPS="${pub_keys[*]}"'

# If an umbrel, use docker, else call lncli directly
LNCLI="lncli"
if uname -a | grep umbrel > /dev/null; then
    LNCLI="docker exec -i lnd lncli"
fi

# Arg option: 'build'
build () {
    $LNCLI buildroute --amt ${AMOUNT} --hops ${HOPS} --outgoing_chan_id ${OUTGOING_CHAN_ID}
}

# Arg option: 'send'
send () {
  INVOICE=$($LNCLI addinvoice --amt=${AMOUNT} --memo="Rebalancing...")

  PAYMENT_HASH=$(echo -n $INVOICE | jq -r .r_hash)
  PAYMENT_ADDRESS=$(echo -n $INVOICE | jq -r .payment_addr)
  
  ROUTE=$(build)
  FEE=$(echo -n $ROUTE | jq .route.total_fees_msat)
  # The fee is expressed as a quoted string in msat. A string length of less than 6 indicates a 0 sat fee.
  FEE=$([ ${#FEE} -lt 6 ] && echo "0" || echo ${FEE:1:-4})
  
  echo "Route fee is $FEE sats."

  if (( FEE  > MAX_FEE )); then
    echo "Error: $FEE exceeded max fee of $MAX_FEE"
    exit 1
  fi

  echo $ROUTE \
    | jq -c "(.route.hops[-1] | .mpp_record) |= {payment_addr:\"${PAYMENT_ADDRESS}\", total_amt_msat: \"${AMOUNT}000\"}" \
    | $LNCLI sendtoroute --payment_hash=${PAYMENT_HASH} -
}

# test for availability of tools before use, don't rely on users
# not used to cli dealing with errors down the line
assert_tools () {
  err=0
    while test $# -gt 0; do
      command -v "$1" >/dev/null 2>/dev/null || {
        >&2 printf "tool missing: $1\n"
        err=$(( $err + 1 ))
      }
      shift
    done
    test $err -eq 0 || exit $err
}

# Arg option: '--help'
help () {
    cat << EOF
usage: ./igniter.sh [--help] [build] [send]
       <command> [<args>]

Open the script and configure values first. Then run
the script with one of the following flags:

   build             Build the routes for the configured nodes
   send              Build route and send payment along route

EOF
}

# Run the script
dependecies="cat jq lncli"
#assert_tools ${dependecies}
all_args=("$@")
rest_args_array=("${all_args[@]:1}")
rest_args="${rest_args_array[@]}"

case $1 in
    "build" )
        build $rest_args
        ;;
    "send" )
        send $rest_args
        ;;
    "--help" )
        help
        ;;
    * )
        help
        ;;
esac

6. Test our route

./igniter.sh build

7. Send a transaction to yourself by entering the following command:

./igniter.sh send

Your node (and all others) will start rebalancing.

8. All channels are now rebalanced and your ring of fire is ready to route 😎

Resources

Last updated

Was this helpful?