Skip to content

Circuitbreaker4 #687

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion resources/networks/hello/network.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ plugins: # Each plugin section has a number of hooks available (preDeploy, post
simln: # You can have multiple plugins per hook
entrypoint: "../../plugins/simln"
activity: '[{"source": "tank-0003-ln", "destination": "tank-0005-ln", "interval_secs": 1, "amount_msat": 2000}]'
circuitbreaker:
entrypoint: "../../plugins/circuitbreaker"
podName: "circuitbreaker-pod"
rpcserver: "172.29.34.166:10009"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rpcserver should be determined at runtime, right?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I was having trouble connecting to the ln node on Warnet so I decided to connect to the lnd in my local for the meantime at least to determine I am on the right track.

Copy link
Author

@Camillarhi Camillarhi Mar 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@macgyver13 I am closing this now and using this PR link instead and all changes has been updated there

#688

httplisten: "0.0.0.0:9235"
preNode: # preNode plugins run before each node is deployed
hello:
entrypoint: "../../plugins/hello"
Expand All @@ -84,4 +89,4 @@ plugins: # Each plugin section has a number of hooks available (preDeploy, post
hello:
entrypoint: "../../plugins/hello"
helloTo: "postNetwork!"
podName: "hello-post-network"
podName: "hello-post-network"
151 changes: 151 additions & 0 deletions resources/plugins/circuitbreaker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# Circuit Breaker Plugin

## Overview
The Circuit Breaker plugin integrates the [circuitbreaker](https://github.com/lightningequipment/circuitbreaker) tool with Warnet to protect Lightning Network nodes from being flooded with HTLCs. Circuit Breaker functions like a firewall for Lightning, allowing node operators to set limits on in-flight HTLCs and implement rate limiting on a per-peer basis.

## What is Circuit Breaker?
Circuit Breaker is to Lightning what firewalls are to the internet. It provides protection against:
- HTLC flooding attacks
- Channel slot exhaustion (max 483 slots per channel)
- DoS/spam attacks using large numbers of fast-resolving HTLCs
- Channel balance probing attacks

Circuit Breaker offers insights into HTLC traffic and provides configurable operating modes to handle excess traffic.

## Usage
In your Python virtual environment with Warnet installed and set up, create a new Warnet user folder:

```
$ warnet new user_folder
$ cd user_folder
```

Deploy a network with Circuit Breaker enabled:

```
$ warnet deploy networks/circuitbreaker
```

## Configuration in `network.yaml`
You can incorporate the Circuit Breaker plugin into your `network.yaml` file as shown below:

```yaml
nodes:
- name: tank-0000
addnode:
- tank-0001
ln:
lnd: true

- name: tank-0001
addnode:
- tank-0002
ln:
lnd: true

- name: tank-0002
addnode:
- tank-0000
ln:
lnd: true

- name: tank-0003
addnode:
- tank-0000
ln:
lnd: true
lnd:
channels:
- id:
block: 300
index: 1
target: tank-0004-ln
capacity: 100000
push_amt: 50000

plugins:
postDeploy:
circuitbreaker:
entrypoint: "../../plugins/circuitbreaker"
nodes: ["tank-0000-ln", "tank-0003-ln"] # Nodes to apply Circuit Breaker to
mode: "fail" # Operating mode: fail, queue, or queue_peer_initiated
maxPendingHtlcs: 10 # Default maximum pending HTLCs per peer
rateLimit: 1 # Minimum seconds between HTLCs (token bucket rate limit)
```

## Plugin Parameters

| Parameter | Description | Default |
|-----------|-------------|---------|
| `nodes` | List of LN node names to apply Circuit Breaker to | Required |
| `mode` | Operating mode (`fail`, `queue`, or `queue_peer_initiated`) | `fail` |
| `maxPendingHtlcs` | Default maximum number of pending HTLCs per peer | `30` |
| `rateLimit` | Minimum interval in seconds between HTLCs | `0` (disabled) |
| `port` | Port to expose the Circuit Breaker UI on | `9235` |
| `trusted_peers` | Map of node pubkeys to their individual HTLC limits | `{}` |

## Operating Modes

- **fail**: Fail HTLCs when limits are exceeded. Minimizes liquidity lock-up but affects routing reputation.
- **queue**: Queue HTLCs when limits are exceeded, forwarding them when space becomes available. Penalizes upstream nodes for bad traffic.
- **queue_peer_initiated**: Queue only HTLCs from channels that the remote node initiated. Uses fail mode for channels we initiated.

**WARNING**: Queue modes require LND 0.16+ with auto-fail support to prevent force-closes.

## Accessing the UI

After deploying, you can port-forward to access the Circuit Breaker UI:

```
$ kubectl port-forward pod/circuitbreaker-tank-0000 9235:9235
```

Then open http://127.0.0.1:9235 in a browser to view and configure Circuit Breaker settings.

## Advanced Configuration Example

```yaml
plugins:
postDeploy:
circuitbreaker:
entrypoint: "../../plugins/circuitbreaker"
nodes: ["tank-0000-ln", "tank-0003-ln"]
mode: "fail"
maxPendingHtlcs: 15
rateLimit: 0.5
trusted_peers: {
"03abcdef...": 50,
"02123456...": 100
}
```

<!-- ## Combining with SimLN

The Circuit Breaker plugin can be used alongside the SimLN plugin to test how Circuit Breaker behaves under various payment patterns:

```yaml
plugins:
postDeploy:
circuitbreaker:
entrypoint: "../../plugins/circuitbreaker"
nodes: ["tank-0000-ln"]
mode: "fail"
maxPendingHtlcs: 10
simln:
entrypoint: "../../plugins/simln"
activity: '[{"source": "tank-0003-ln", "destination": "tank-0005-ln", "interval_secs": 0.1, "amount_msat": 2000}]'
``` -->

## Limitations

- Circuit Breaker is alpha quality software. Use with caution, especially on mainnet.
- LND interfaces are not optimized for this purpose, which may lead to edge cases.
- Queue modes require LND 0.16+ to prevent channel force-closes.

## Development

To build your own version of the Circuit Breaker plugin:

1. Clone the Circuit Breaker repository: `git clone https://github.com/lightningequipment/circuitbreaker.git`
2. Follow the build instructions in the repository
3. Update the plugin's `values.yaml` to point to your custom image
23 changes: 23 additions & 0 deletions resources/plugins/circuitbreaker/charts/circuitbreaker/.helmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
apiVersion: v2
name: circuitbreaker
description: A Helm chart to deploy Circuit Breaker
version: 0.1.0
appVersion: "0.1.0"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{{- define "mychart.name" -}}
{{- .Chart.Name | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{- define "mychart.fullname" -}}
{{- printf "%s-%s" (include "mychart.name" .) .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "mychart.fullname" . }}-data
data:
tls.cert: |
-----BEGIN CERTIFICATE-----
MIICRTCCAeygAwIBAgIRAMe5IfFsBM9nqG1hwA1tswAwCgYIKoZIzj0EAwIwOzEf
MB0GA1UEChMWbG5kIGF1dG9nZW5lcmF0ZWQgY2VydDEYMBYGA1UEAxMPREVTS1RP
UC00OEJVR0xTMB4XDTI1MDIwMTE2NDAyNFoXDTI2MDMyOTE2NDAyNFowOzEfMB0G
A1UEChMWbG5kIGF1dG9nZW5lcmF0ZWQgY2VydDEYMBYGA1UEAxMPREVTS1RPUC00
OEJVR0xTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIl4bWvtGVb1T4iUyjLfj
U2IVnF1yJBwbTa2diRJh+a0UbwjUSdn/hIVkNALr9f3NKYWmotyq8IGOmjwhAFis
HKOB0DCBzTAOBgNVHQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYD
VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU38wWLmz1lsVv7vtZZamSgkcoQUcwdgYD
VR0RBG8wbYIPREVTS1RPUC00OEJVR0xTgglsb2NhbGhvc3SCBHVuaXiCCnVuaXhw
YWNrZXSCB2J1ZmNvbm6HBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAGHBAr///6HBKwd
IqaHEP6AAAAAAAAAAhVd//6GM+MwCgYIKoZIzj0EAwIDRwAwRAIgNe9zoH9iz7Tw
1j8+Jk05DU6nJ48a5mbP0viZ50UGu7sCIEK0AoPBrqxnicdhEEInONWyIm5VUR/l
YURZZyNuJ8lJ
-----END CERTIFICATE-----
admin.macaroon.hex: |
0201036C6E6402F801030A107EC4D3E96DE93FA58F70968A1729AE6C1201301A160A0761646472657373120472656164120577726974651A130A04696E666F120472656164120577726974651A170A08696E766F69636573120472656164120577726974651A210A086D616361726F6F6E120867656E6572617465120472656164120577726974651A160A076D657373616765120472656164120577726974651A170A086F6666636861696E120472656164120577726974651A160A076F6E636861696E120472656164120577726974651A140A057065657273120472656164120577726974651A180A067369676E6572120867656E65726174651204726561640000062023AFC3BF7DB1D186342905D79461793FFCB59F583858F495C253F0A1EB4D33C2
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
apiVersion: v1
kind: Pod
metadata:
name: {{ include "mychart.fullname" . }}
labels:
app: {{ include "mychart.name" . }}
mission: {{ .Values.name }}
spec:
initContainers:
- name: "init"
image: "busybox"
command:
- "sh"
- "-c"
args:
- >
mkdir -p /shared/.lnd/data/chain/bitcoin/mainnet &&
cp /configmap/tls.cert /shared/.lnd/tls.cert &&
cat /configmap/admin.macaroon.hex | xxd -r -p > /shared/.lnd/data/chain/bitcoin/mainnet/admin.macaroon
volumeMounts:
- name: shared-volume
mountPath: /shared
- name: configmap-volume
mountPath: /configmap
containers:
- name: {{ .Values.name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
command:
- "sh"
- "-c"
args:
- >
mkdir -p /root/.lnd/data/chain/bitcoin/mainnet &&
ln -s /shared/.lnd/tls.cert /root/.lnd/tls.cert &&
ln -s /shared/.lnd/data/chain/bitcoin/mainnet/admin.macaroon /root/.lnd/data/chain/bitcoin/mainnet/admin.macaroon &&
circuitbreaker --rpcserver={{ .Values.lnd.rpcserver }} --httplisten={{ .Values.lnd.httplisten }}
volumeMounts:
- name: shared-volume
mountPath: /shared
volumes:
- name: configmap-volume
configMap:
name: {{ include "mychart.fullname" . }}-data
- name: shared-volume
emptyDir: {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "mychart.fullname" . }}-service
labels:
app: {{ include "mychart.name" . }}
spec:
type: NodePort
ports:
- port: 9235
targetPort: 9235
nodePort: 30000 # Choose a port between 30000-32767
selector:
app: {{ include "mychart.name" . }}
14 changes: 14 additions & 0 deletions resources/plugins/circuitbreaker/charts/circuitbreaker/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: "circuitbreaker"
image:
repository: "camillarhi/circuitbreaker"
tag: "latest"
pullPolicy: IfNotPresent
workingVolume:
name: working-volume
mountPath: /working
configmapVolume:
name: configmap-volume
mountPath: /configmap
lnd:
rpcserver: "172.29.34.166:10009" # Default LND RPC server address
httplisten: "0.0.0.0:9235" # Default HTTP listen address
Loading
Loading