├── .github └── workflows │ └── tests.yml ├── LICENSE ├── README.md ├── cln-amboss-ping.sh ├── cln-channelbalance.sh ├── cln-feereport.sh ├── cln-prune-protector.sh ├── cln-random-traffic-gen.sh ├── cln-routing-summary.sh ├── cln-walletbalance.sh ├── inc.common.sh ├── install.sh └── tests ├── scid.bats └── test_all.sh /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Test CLN scripts 2 | on: [push, pull_request] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - name: Run ShellCheck 9 | uses: ludeeus/action-shellcheck@master 10 | - name: Setup BATS 11 | uses: mig4/setup-bats@v1 12 | with: 13 | bats-version: 1.8.2 14 | - name: Run unit tests 15 | run: ./tests/test_all.sh 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Kristaps Kaupe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cln-scripts 2 | 3 | Various shell scripts for [CLN (Core Lightning / c-lightning)](https://github.com/ElementsProject/lightning). I also have [some scripts for Bitcoin Core](https://github.com/kristapsk/bitcoin-scripts). 4 | 5 | ## Installation 6 | 7 | Dependencies: `bash`, `lightning-cli` (comes with CLN), `bc`, [`jq`](https://github.com/stedolan/jq). 8 | 9 | Gentoo Linux users can use [my portage overlay](https://github.com/kristapsk/portage-overlay): 10 | ```sh 11 | # eselect repository add kristapsk git https://github.com/kristapsk/portage-overlay.git 12 | # emerge -av cln-scripts 13 | ``` 14 | 15 | Otherwise, use provided `install.sh` script, which will install everything in `/opt/cln-scripts` with symlinks in `/usr/local/bin` (so that they are on `PATH`). 16 | 17 | ## Usage 18 | 19 | All scripts support `-h`/`--help` for usage information. 20 | 21 | | Script | Description | 22 | | --- | --- | 23 | | `cln-amboss-ping` | Send node health check ping to Amboss, see [docs](https://docs.amboss.space/api/monitoring/health-checks). | 24 | | `cln-channelbalance` | Returns the sum of the total available channel balance across all open channels, like `lncli channelbalance` of LND. | 25 | | `cln-feereport` | Display the current fee policies of all active channels, like `lncli feereport` of LND. | 26 | | `cln-prune-protector` | Protect CLN against Bitcoin Core pruning too much. | 27 | | `cln-routing-summary` | Display routing summary for a given period. | 28 | | `cln-random-traffic-gen` | Generate random (decoy) LN traffic between you and your peers to fight traffic analysis. Has lock protection against running multiple instances at the same time so can safely be just added to crontab. | 29 | | `cln-walletbalance` | Compute and display the onchain wallet's current balance, like `lncli walletbalance` of LND. | 30 | 31 | ## Support 32 | 33 | If you want to support my work on this project and other free software (I am also maintainer of [JoinMarket](https://github.com/JoinMarket-Org/joinmarket-clientserver) and do other Bitcoin stuff), you can send some sats (Bitcoin) either [here](https://donate.kristapsk.lv/) (that's my self-hosted [SatSale](https://github.com/nickfarrow/SatSale) instance) or to my WoS LN address [tritejogging43@walletofsatoshi.com](lightning:tritejogging43@walletofsatoshi.com) (my old public CLN node is temporary down). 34 | -------------------------------------------------------------------------------- /cln-amboss-ping.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Based on https://gist.github.com/swissrouting/111d4a615d670ddf8d11eaa8a60eacca 3 | # Docs: https://docs.amboss.space/api/monitoring/health-checks 4 | 5 | if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then 6 | echo "Send node health check ping to Amboss." 7 | echo "Usage: $(basename "$0") [sleeptime]" 8 | echo "Where:" 9 | echo " sleeptime - sleep time between pings (default: 30)" 10 | exit 11 | fi 12 | 13 | API_URL="https://api.amboss.space/graphql" 14 | 15 | lockdir="/tmp/$(basename "$0").lock" 16 | if mkdir "$lockdir" 2> /dev/null; then 17 | trap 'rm -rf "$lockdir"' 0 18 | trap "exit 2" 1 2 3 15 19 | else 20 | #echo "Already running (pid $(cat "$lockdir"/pid))" 21 | exit 1 22 | fi 23 | echo "$$" > "$lockdir"/pid 2> /dev/null || exit 2 24 | 25 | if [[ -n "$1" ]]; then 26 | sleeptime="$1" 27 | else 28 | sleeptime="30" 29 | fi 30 | 31 | while :; do 32 | NOW="$(date -u +%Y-%m-%dT%H:%M:%S%z)" 33 | echo -n "[$NOW] " 34 | SIGNATURE="$(lightning-cli signmessage "$NOW" | jq -r .zbase)" 35 | if [[ -z "$SIGNATURE" ]]; then 36 | echo "Failed to sign message with CLN, aborting!" 37 | kill $$ 38 | fi 39 | PAYLOAD="$(jq -M <<< "{ 40 | \"query\": \"mutation HealthCheck(\$signature: String!, \$timestamp: String!) { healthCheck(signature: \$signature, timestamp: \$timestamp) }\", 41 | \"variables\": { 42 | \"signature\": \"$SIGNATURE\", 43 | \"timestamp\": \"$NOW\" 44 | } 45 | }")" 46 | echo "$API_URL $SIGNATURE" 47 | RESPONSE="$(echo "$PAYLOAD" | curl -sSf --data-binary @- \ 48 | -H "Content-Type: application/json" -X POST "$API_URL")" 49 | NOW="$(date -u +%Y-%m-%dT%H:%M:%S%z)" 50 | echo -n "[$NOW] " 51 | jq -Mc <<< "$RESPONSE" 52 | sleep "$sleeptime" 53 | done 54 | -------------------------------------------------------------------------------- /cln-channelbalance.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then 4 | echo "Returns the sum of the total available channel balance across all open channels." 5 | echo "Usage: $(basename "$0")" 6 | exit 7 | fi 8 | 9 | channels="$(lightning-cli listfunds | jq ".channels[]")" 10 | readarray -t channel_states < <(jq -r ".state" <<< "$channels") 11 | readarray -t channel_our_msat < <(jq -r ".our_amount_msat" <<< "$channels") 12 | readarray -t channel_total_msat < <(jq -r ".amount_msat" <<< "$channels") 13 | 14 | local_balance_msat="0" 15 | remote_balance_msat="0" 16 | unsettled_local_balance_msat="0" 17 | unsettled_remote_balance_msat="0" 18 | pending_open_local_balance_msat="0" 19 | pending_open_remote_balance_msat="0" 20 | for i in $(seq 0 $(( ${#channel_states[@]} - 1 )) ); do 21 | this_our_msat="${channel_our_msat[$i]}" 22 | this_our_msat=${this_our_msat%msat} 23 | this_total_msat="${channel_total_msat[$i]}" 24 | this_total_msat=${this_total_msat%msat} 25 | this_remote_msat=$(( this_total_msat - this_our_msat )) 26 | if [[ "${channel_states[$i]}" == "CHANNELD_NORMAL" ]]; then 27 | local_balance_msat=$(( local_balance_msat + this_our_msat )) 28 | remote_balance_msat=$(( remote_balance_msat + this_remote_msat )) 29 | elif 30 | [[ "${channel_states[$i]}" == "OPENINGD" ]] || 31 | [[ "${channel_states[$i]}" == "CHANNELD_AWAITING_LOCKIN" ]] || 32 | [[ "${channel_states[$i]}" == "DUALOPEND_OPEN_INIT" ]] || 33 | [[ "${channel_states[$i]}" == "DUALOPEND_AWAITING_LOCKIN" ]] 34 | then 35 | pending_open_local_balance_msat=$(( pending_open_local_balance_msat + this_our_msat )) 36 | pending_open_remote_balance_msat=$(( pending_open_remote_balance_msat + this_remote_msat )) 37 | else 38 | unsettled_local_balance_msat=$(( unsettled_local_balance_msat + this_our_msat )) 39 | unsettled_remote_balance_msat=$(( unsettled_remote_balance_msat + this_remote_msat )) 40 | fi 41 | done 42 | 43 | # LND has also "balance" and "pending_open_balance", but they are deprecated. 44 | # https://github.com/lightningnetwork/lnd/blob/8109c9ccf1e41b32234b2372d09aa83b20d65d63/lnrpc/lightning.proto#L2562 45 | jq -M <<< " 46 | { 47 | \"local_balance\": { 48 | \"sat\": \"$(( local_balance_msat / 1000 ))\", 49 | \"msat\": \"$local_balance_msat\" 50 | }, 51 | \"remote_balance\": { 52 | \"sat\": \"$(( remote_balance_msat / 1000 ))\", 53 | \"msat\": \"$remote_balance_msat\" 54 | }, 55 | \"unsettled_local_balance\": { 56 | \"sat\": \"$(( unsettled_local_balance_msat / 1000 ))\", 57 | \"msat\": \"$unsettled_local_balance_msat\" 58 | }, 59 | \"unsettled_remote_balance\": { 60 | \"sat\": \"$(( unsettled_remote_balance_msat / 1000 ))\", 61 | \"msat\": \"$unsettled_remote_balance_msat\" 62 | }, 63 | \"pending_open_local_balance\": { 64 | \"sat\": \"$(( pending_open_local_balance_msat / 1000 ))\", 65 | \"msat\": \"$pending_open_local_balance_msat\" 66 | }, 67 | \"pending_open_remote_balance\": { 68 | \"sat\": \"$(( pending_open_remote_balance_msat / 1000 ))\", 69 | \"msat\": \"$pending_open_remote_balance_msat\" 70 | } 71 | } 72 | " 73 | -------------------------------------------------------------------------------- /cln-feereport.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then 4 | echo "Display the current fee policies of all active channels." 5 | echo "Usage: $(basename "$0")" 6 | exit 7 | fi 8 | 9 | our_nodeid="$(lightning-cli getinfo | jq -r ".id")" 10 | 11 | # shellcheck disable=SC1091 12 | # shellcheck source=./inc.common.sh 13 | . "$(dirname "$(readlink -m "$0")")/inc.common.sh" 14 | 15 | channels="$(lightning-cli listfunds | \ 16 | jq '.channels[] | select(.state=="CHANNELD_NORMAL")')" 17 | readarray -t short_channel_ids < <(jq -r ".short_channel_id" <<< "$channels") 18 | readarray -t funding_txids < <(jq -r ".funding_txid" <<< "$channels") 19 | readarray -t funding_outputs < <(jq -r ".funding_output" <<< "$channels") 20 | 21 | channel_fees="" 22 | needs_comma="" 23 | for i in $(seq 0 $(( ${#short_channel_ids[@]} - 1 )) ); do 24 | channel_detail="$(lightning-cli listchannels "${short_channel_ids[$i]}" | \ 25 | jq ".channels")" 26 | readarray -t detail_sources < <(jq -r ".[].source" <<< "$channel_detail") 27 | readarray -t detail_base_fee_msat < <(jq -r ".[].base_fee_millisatoshi" \ 28 | <<< "$channel_detail") 29 | readarray -t detail_fee_per_mil < <(jq -r ".[].fee_per_millionth" \ 30 | <<< "$channel_detail") 31 | for j in $(seq 0 $(( ${#detail_sources[@]} - 1 )) ); do 32 | if [[ "${detail_sources[$j]}" == "$our_nodeid" ]]; then 33 | if [[ "$needs_comma" == "1" ]]; then 34 | channel_fees="$channel_fees," 35 | fi 36 | channel_fee="{ 37 | \"chan_id\": \"$(cl_to_lnd_scid "${short_channel_ids[$i]}")\", 38 | \"cln_chan_id\": \"${short_channel_ids[$i]}\", 39 | \"channel_point\": 40 | \"${funding_txids[$i]}:${funding_outputs[$i]}\", 41 | \"base_fee_msat\": \"${detail_base_fee_msat[$j]}\", 42 | \"fee_per_mil\": \"${detail_fee_per_mil[$j]}\", 43 | \"fee_rate\": 44 | $(bc <<< "scale=6; ${detail_fee_per_mil[$j]} / 1000000") 45 | }" 46 | channel_fees="$channel_fees$channel_fee" 47 | needs_comma="1" 48 | break 49 | fi 50 | done 51 | done 52 | 53 | day_ago="$(date -d "day ago" +"%s")" 54 | week_ago="$(date -d "week ago" +"%s")" 55 | month_ago="$(date -d "month ago" +"%s")" 56 | forwards="$(lightning-cli listforwards settled | \ 57 | jq ".forwards[] | select(.resolved_time > $month_ago)")" 58 | readarray -t fwd_fees < <(jq ".fee" <<< "$forwards") 59 | readarray -t fwd_times < <(jq -r ".resolved_time | floor" <<< "$forwards") 60 | day_fee_sum="0" 61 | week_fee_sum="0" 62 | month_fee_sum="0" 63 | for i in $(seq 0 $(( ${#fwd_fees[@]} - 1 )) ); do 64 | if (( ${fwd_times[$i]} > month_ago )); then 65 | month_fee_sum=$((month_fee_sum + ${fwd_fees[$i]})) 66 | if (( ${fwd_times[$i]} > week_ago )); then 67 | week_fee_sum=$((week_fee_sum + ${fwd_fees[$i]})) 68 | if (( ${fwd_times[$i]} > day_ago )); then 69 | day_fee_sum=$((day_fee_sum + ${fwd_fees[$i]})) 70 | fi 71 | fi 72 | fi 73 | done 74 | 75 | fees_collected_msat="$(lightning-cli getinfo | jq -r ".fees_collected_msat")" 76 | fees_collected_msat=${fees_collected_msat%msat} 77 | 78 | jq -M <<< "{ 79 | \"channel_fees\": [ $channel_fees ], 80 | \"day_fee_sum\": \"$(( day_fee_sum / 1000 ))\", 81 | \"week_fee_sum\": \"$(( week_fee_sum / 1000 ))\", 82 | \"month_fee_sum\": \"$(( month_fee_sum / 1000 ))\", 83 | \"total_fee_sum\": \"$(( fees_collected_msat / 1000 ))\" 84 | }" 85 | -------------------------------------------------------------------------------- /cln-prune-protector.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then 4 | echo "Protect CLN against Bitcoin Core pruning too much." 5 | echo "When CLN is not running or lags too many blocks behind, " 6 | echo " P2P network traffic of Bitcoin Core is temporary disabled." 7 | echo "Usage: $(basename "$0") [numblocks [checktime]]" 8 | echo "Where:" 9 | echo " numblocks - maximum number of blocks to allow lag behind (default: 500)" 10 | echo " checktime - interval at which to do the checks (default: 60s)" 11 | exit 12 | fi 13 | 14 | lockdir="/tmp/$(basename "$0").lock" 15 | if mkdir "$lockdir" 2> /dev/null; then 16 | trap 'rm -rf "$lockdir"' 0 17 | trap "exit 2" 1 2 3 15 18 | else 19 | #echo "Already running (pid $(cat "$lockdir"/pid))" 20 | exit 1 21 | fi 22 | echo "$$" > "$lockdir"/pid 2> /dev/null || exit 2 23 | 24 | if [[ -n "$1" ]]; then 25 | check_numblocks="$1" 26 | else 27 | check_numblocks="500" 28 | fi 29 | if [[ -n "$2" ]]; then 30 | check_interval="$2" 31 | else 32 | check_interval="10s" 33 | fi 34 | 35 | # shellcheck disable=SC1091 36 | # shellcheck source=./inc.common.sh 37 | . "$(dirname "$(readlink -m "$0")")/inc.common.sh" 38 | 39 | log_with_date "Starting" 40 | log_with_date "check_numblocks = $check_numblocks, check_interval = $check_interval" 41 | 42 | if ! bitcoin-cli echo > /dev/null 2>&1; then 43 | log_with_date "Bitcoin Core not running, exiting." 44 | exit 1 45 | fi 46 | 47 | if ! lightning-cli getinfo > /dev/null 2>&1; then 48 | log_with_date "CLN not running, exiting." 49 | exit 1 50 | fi 51 | 52 | bitcoind_networkactive="$(bitcoin-cli getnetworkinfo | \ 53 | jq -r ".networkactive")" 54 | 55 | bitcoind_setnetworkactive() 56 | { 57 | if [[ "$1" != "$bitcoind_networkactive" ]]; then 58 | echo -n "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] " 59 | bitcoind_networkactive="$(bitcoin-cli setnetworkactive "$1")" 60 | log_with_date "bitcoin-cli setnetworkactive $1 (because $2): $bitcoind_networkactive" 61 | fi 62 | } 63 | 64 | while :; do 65 | if ! btc_bh="$(bitcoin-cli getblockcount 2> /dev/null)"; then 66 | log_with_date "Bitcoin Core not running." 67 | elif wbh_res="$(lightning-cli waitblockheight 0 2> /dev/null)"; then 68 | cln_bh="$(jq -r ".blockheight" <<< "$wbh_res")" 69 | log_with_date "bitcoind blockheight $btc_bh, cln blockheigt $cln_bh (diff = $(( btc_bh - cln_bh )))" 70 | if (( $(( btc_bh - cln_bh )) > check_numblocks )); then 71 | bitcoind_setnetworkactive false \ 72 | "$btc_bh - $cln_bh > $check_numblocks" 73 | elif (( $(( btc_bh - cln_bh )) <= $(( check_numblocks / 2 )) )); then 74 | bitcoind_setnetworkactive true \ 75 | "$btc_bh - $cln_bh <= $(( check_numblocks / 2 ))" 76 | fi 77 | else 78 | bitcoind_setnetworkactive false "CLN not running" 79 | fi 80 | log_with_date "Sleeping for $check_interval" 81 | sleep "$check_interval" 82 | done 83 | -------------------------------------------------------------------------------- /cln-random-traffic-gen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then 4 | echo "Generate random (decoy) LN traffic between you and your peers to fight traffic analysis." 5 | echo "Usage: $(basename "$0") [sleeptime_min [sleeptime_max]]" 6 | echo "Where:" 7 | echo " sleeptime_min - minimum sleep time between ping loop iterations (default: 10)" 8 | echo " sleeptime_max - maximum sleep time between ping loop iterations (default: 600)" 9 | exit 10 | fi 11 | 12 | lockdir="/tmp/$(basename "$0").lock" 13 | if mkdir "$lockdir" 2> /dev/null; then 14 | trap 'rm -rf "$lockdir"' 0 15 | trap "exit 2" 1 2 3 15 16 | else 17 | #echo "Already running (pid $(cat "$lockdir"/pid))" 18 | exit 1 19 | fi 20 | echo "$$" > "$lockdir"/pid 2> /dev/null || exit 2 21 | 22 | if [[ -n "$1" ]]; then 23 | sleeptime_min="$1" 24 | else 25 | sleeptime_min="10" 26 | fi 27 | if [[ -n "$2" ]]; then 28 | sleeptime_max="$2" 29 | else 30 | sleeptime_max="600" 31 | fi 32 | sleeptime_diff="$(( sleeptime_max - sleeptime_min ))" 33 | 34 | # shellcheck disable=SC1091 35 | # shellcheck source=./inc.common.sh 36 | . "$(dirname "$(readlink -m "$0")")/inc.common.sh" 37 | 38 | log_with_date "Starting" 39 | 40 | while :; do 41 | lightning-cli listpeers | jq -r ".peers[].id" | while read -r node_id; do 42 | # Each peer has 50% probability of being pinged during each ping loop 43 | # iteration. 44 | if [[ $((RANDOM % 2)) != 0 ]]; then 45 | log_with_date "Pinging $node_id" 46 | timeout 60 lightning-cli ping "$node_id" \ 47 | $((RANDOM % 65530)) $((RANDOM % 65530)) 48 | fi 49 | done 50 | sleeptime="$(( (RANDOM % sleeptime_diff) + sleeptime_min ))" 51 | log_with_date "Sleeping for $sleeptime secs..." 52 | sleep "$sleeptime" 53 | done 54 | -------------------------------------------------------------------------------- /cln-routing-summary.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then 4 | echo "Display routing summary for a given period." 5 | echo "Usage: $(basename "$0") [start_date [end_date]]" 6 | exit 7 | fi 8 | 9 | if [[ -n "$1" ]]; then 10 | start_date="$(date -d "$1" +"%s")" 11 | else 12 | start_date="0" 13 | fi 14 | if [[ -n "$2" ]]; then 15 | end_date="$(date -d "$2" +"%s")" 16 | else 17 | end_date="$(date +"%s")" 18 | fi 19 | 20 | # Very old forwards will not have "resolved_time", but we want to include 21 | # them anyay if start_date is 0. 22 | if (( start_date == 0 )); then 23 | resolved_time_filter=".resolved_time <= $end_date" 24 | else 25 | resolved_time_filter=".resolved_time >= $start_date and \ 26 | .resolved_time <= $end_date" 27 | fi 28 | 29 | forwards="$(lightning-cli listforwards settled | \ 30 | jq "[.forwards[] | select($resolved_time_filter)]")" 31 | readarray -t fwd_out_msatoshis < <(jq ".[].out_msatoshi" <<< "$forwards") 32 | readarray -t fwd_fees < <(jq ".[].fee" <<< "$forwards") 33 | forwarded_sum="0" 34 | fee_sum="0" 35 | for i in $(seq 0 $(( ${#fwd_fees[@]} - 1 )) ); do 36 | forwarded_sum=$((forwarded_sum += ${fwd_out_msatoshis[$i]})) 37 | fee_sum=$((fee_sum += ${fwd_fees[$i]})) 38 | done 39 | 40 | jq -M <<< "{ 41 | \"period_start\": $start_date, 42 | \"period_end\": $end_date, 43 | \"num_forwards\": $(jq -r "length" <<< "$forwards"), 44 | \"forwarded_sum\": $(( forwarded_sum / 1000 )), 45 | \"fee_sum\": $(( fee_sum / 1000 )) 46 | }" 47 | -------------------------------------------------------------------------------- /cln-walletbalance.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then 4 | echo "Compute and display the wallet's current balance." 5 | echo "Usage: $(basename "$0")" 6 | exit 7 | fi 8 | 9 | # Note that unconfirmed_balance will always be 0 for now, even if there 10 | # is pending unconfirmed tx in mempool. 11 | # See . 12 | 13 | confirmed_balance="0" 14 | unconfirmed_balance="0" 15 | locked_balance="0" 16 | outputs="$(lightning-cli listfunds | jq ".outputs")" 17 | # lightning-cli listfunds | jq ".outputs" | jq -r ".[].status" 18 | readarray -t output_statuses < <(jq -r ".[].status" <<< "$outputs") 19 | readarray -t output_amount_msat < <(jq -r ".[].amount_msat" <<< "$outputs") 20 | readarray -t output_reserved < <(jq -r ".[].reserved" <<< "$outputs") 21 | for i in $(seq 0 $(( ${#output_statuses[@]} - 1 )) ); do 22 | if [[ "${output_reserved[$i]}" != "true" ]]; then 23 | if [[ "${output_statuses[$i]}" == "confirmed" ]]; then 24 | confirmed_balance=$((confirmed_balance + ${output_amount_msat[$i]})) 25 | elif [[ "${output_statuses[$i]}" == "unconfirmed" ]]; then 26 | unconfirmed_balance=$((unconfirmed_balance + ${output_amount_msat[$i]})) 27 | fi 28 | else 29 | locked_balance=$((locked_balance + ${output_amount_msat[$i]})) 30 | fi 31 | done 32 | confirmed_balance=$((confirmed_balance / 1000)) 33 | unconfirmed_balance=$((unconfirmed_balance / 1000)) 34 | total_balance=$((confirmed_balance + unconfirmed_balance)) 35 | 36 | echo "{ 37 | \"total_balance\": $total_balance, 38 | \"confirmed_balance\": $confirmed_balance, 39 | \"unconfirmed_balance\": $unconfirmed_balance, 40 | \"locked_balance\": $locked_balance 41 | }" 42 | -------------------------------------------------------------------------------- /inc.common.sh: -------------------------------------------------------------------------------- 1 | # shellcheck disable=SC2148 2 | 3 | # https://bitcoin.stackexchange.com/a/79427 4 | cl_to_lnd_scid() 5 | { 6 | IFS="x" read -r -a s <<< "$1" 7 | echo $(( (s[0] << 40) | (s[1] << 16) | s[2] )) 8 | } 9 | 10 | lnd_to_cl_scid() 11 | { 12 | echo "$(( "$1" >> 40 ))x$(( "$1" >> 16 & 0xFFFFFF ))x$(( "$1" & 0xFFFF ))" 13 | } 14 | 15 | log_with_date() 16 | { 17 | echo "[$(date -u +%Y-%m-%dT%H:%M:%S%z)] $*" 18 | } 19 | 20 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # shellcheck disable=SC2016 3 | 4 | if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then 5 | echo "Installs CLN scripts." 6 | echo "Usage: $(basename "$0")" 7 | exit 8 | fi 9 | 10 | PREFIX="/opt/cln-scripts" 11 | 12 | if [[ "$(whoami)" == "root" ]]; then 13 | sudo="" 14 | else 15 | sudo="sudo" 16 | fi 17 | 18 | cd "$(dirname "$0")" || exit 1 19 | 20 | $sudo mkdir -p "$PREFIX" 21 | 22 | echo -n "Installing inc.common.sh..." 23 | $sudo install -p -t "$PREFIX" -m 644 ./inc.common.sh 24 | echo "" 25 | 26 | SCRIPT_LIST=" 27 | amboss-ping 28 | channelbalance 29 | feereport 30 | prune-protector 31 | routing-summary 32 | random-traffic-gen 33 | walletbalance 34 | " 35 | 36 | for script in $SCRIPT_LIST; do 37 | echo -n "Installing cln-$script..." 38 | $sudo install -p -t "$PREFIX" "./cln-$script.sh" 39 | $sudo ln -f -s "$PREFIX/cln-$script.sh" "/usr/local/bin/cln-$script" 40 | echo "" 41 | done 42 | 43 | echo -n "Creating cln-uninstall..." 44 | $sudo bash -c 'cat < '"$PREFIX/cln-uninstall.sh"' 45 | #!/usr/bin/env bash 46 | if [[ "\$(whoami)" == "root" ]]; then 47 | sudo="" 48 | else 49 | sudo="sudo" 50 | fi 51 | PREFIX="'"$PREFIX"'" 52 | SCRIPT_LIST="'"$SCRIPT_LIST"'" 53 | read -n 1 -p "This will uninstall cln-scripts from \$PREFIX. Are you sure? (y/N) " 54 | echo "" 55 | if [[ \${REPLY} =~ y|Y ]]; then 56 | for script in \$SCRIPT_LIST; do 57 | echo -n "Uninstalling cln-\$script..." 58 | \$sudo unlink /usr/local/bin/cln-\$script 59 | \$sudo rm -f "\$PREFIX/cln-\$script.sh" 60 | echo "" 61 | done 62 | echo "Clean up rest..." 63 | \$sudo rm -f "\$PREFIX/inc.common.sh" 64 | \$sudo unlink /usr/local/sbin/cln-uninstall 65 | \$sudo rm -f "\$PREFIX/cln-uninstall.sh" 66 | \$sudo rmdir "\$PREFIX" 2> /dev/null 67 | echo "Done." 68 | fi 69 | EOF' 70 | $sudo chmod +x "$PREFIX/cln-uninstall.sh" 71 | $sudo ln -f -s "$PREFIX/cln-uninstall.sh" /usr/local/sbin/cln-uninstall 72 | 73 | echo "Done." 74 | -------------------------------------------------------------------------------- /tests/scid.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | . "../inc.common.sh" 4 | 5 | @test "CLN short channel id to LND short channel id" { 6 | [[ $(cl_to_lnd_scid "734778x1235x0") == "807896954914930688" ]] 7 | } 8 | 9 | @test "LND short channel id to CLN short channel id" { 10 | [[ $(lnd_to_cl_scid "807896954914930688") == "734778x1235x0" ]] 11 | } 12 | -------------------------------------------------------------------------------- /tests/test_all.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd "$(dirname "$0")" > /dev/null || exit 1 3 | for f in *.bats; do 4 | echo "Running $f" 5 | "./$f" 6 | echo "" 7 | done 8 | --------------------------------------------------------------------------------