mirror of
https://github.com/callebtc/electronwall.git
synced 2026-02-10 09:44:37 +01:00
216 lines
8.8 KiB
Markdown
216 lines
8.8 KiB
Markdown
# ⚡️🛡 electronwall
|
|
A tiny firewall for LND that can filter Lightning **channel opening requests** and **HTLC forwards** based on your custom rules.
|
|
|
|
electronwall uses filter lists that either allow (allowlist) or reject (denylist) events from a list of node public keys for channel openings, or channel IDs and channel pairs for payment routings.
|
|
|
|
You can also write [custom rules](#programmable-rules) using a builtin Javascript engine.
|
|
|
|
*Note: electronwall is in early development and highly experimental software. Use at your own risk. Please report issues.*
|
|
|
|

|
|
|
|
## Install
|
|
|
|
### From source
|
|
Build from source (you may need to install go for this):
|
|
|
|
```bash
|
|
git clone https://github.com/callebtc/electronwall.git
|
|
cd electronwall
|
|
go build .
|
|
```
|
|
|
|
### Binaries
|
|
|
|
You can download a binary for your system [here](https://github.com/callebtc/electronwall/releases). You'll still need a [config file](https://github.com/callebtc/electronwall/blob/main/config.yaml.example).
|
|
|
|
## Config
|
|
Edit `config.yaml.example` and rename to `config.yaml`.
|
|
|
|
## Run
|
|
|
|
```bash
|
|
./electronwall
|
|
```
|
|
|
|
# Rules
|
|
|
|
## Allowlist and denylist
|
|
|
|
Allowlist and denylist rules are set in `config.yaml` under the appropriate keys. See the [example](config.yaml.example) config.
|
|
|
|
## Programmable rules
|
|
|
|
electronwall has a Javascript engine called [goja](https://github.com/dop251/goja) that allows you to set custom rules. Note that you can only use pure Javascript (ECMAScript), you can't import a ton of other dependcies like with web applications.
|
|
|
|
Rules are saved in the `rules/` directory. There are two files, one for channel open requests `ChannelAccept.js` and one for HTLC forwards `HtlcForward.js`.
|
|
|
|
electronwall passes [contextual information](#contextual-information) to the Javascript engine that you can use to create rich rules. See below for a list of objects that are currently supported.
|
|
|
|
Here is one rather complex rule for channel accept decisions in `ChannelAccept.js` for demonstration purposes:
|
|
|
|
```javascript
|
|
// only channels > 0.75 Msat
|
|
ChannelAccept.Event.FundingAmt >= 750000 &&
|
|
// nodes with high 1ML availability score
|
|
ChannelAccept.OneMl.Noderank.Availability > 100 &&
|
|
// nodes with a low enough 1ML age rank
|
|
ChannelAccept.OneMl.Noderank.Age < 10000 &&
|
|
(
|
|
// only nodes with Amboss contact data
|
|
ChannelAccept.Amboss.Socials.Info.Email ||
|
|
ChannelAccept.Amboss.Socials.Info.Twitter ||
|
|
ChannelAccept.Amboss.Socials.Info.Telegram
|
|
) &&
|
|
(
|
|
// elitist: either nodes with amboss prime
|
|
ChannelAccept.Amboss.Amboss.IsPrime ||
|
|
// or nodes with high-ranking capacity
|
|
ChannelAccept.Amboss.GraphInfo.Metrics.CapacityRank < 1000 ||
|
|
// or nodes with high-ranking channel count
|
|
ChannelAccept.Amboss.GraphInfo.Metrics.ChannelsRank < 1000
|
|
)
|
|
```
|
|
|
|
Here is an example `HtlcForward.js` for deciding on HTLC forwards:
|
|
```javascript
|
|
if (
|
|
// only forward amounts larger than 100 sat
|
|
HtlcForward.Event.OutgoingAmountMsat >= 100000
|
|
) { true } else { false }
|
|
```
|
|
|
|
### Contextual information
|
|
Here is a list of all objects that are passed to the Javascript engine. You need to look at the structure of these objects in order to use them in a custom rule like the example above.
|
|
|
|
#### LND: ChannelAcceptRequest `*.Event`
|
|
```go
|
|
type ChannelAcceptRequest struct {
|
|
state protoimpl.MessageState
|
|
sizeCache protoimpl.SizeCache
|
|
unknownFields protoimpl.UnknownFields
|
|
|
|
// The pubkey of the node that wishes to open an inbound channel.
|
|
NodePubkey []byte `protobuf:"bytes,1,opt,name=node_pubkey,json=nodePubkey,proto3" json:"node_pubkey,omitempty"`
|
|
// The hash of the genesis block that the proposed channel resides in.
|
|
ChainHash []byte `protobuf:"bytes,2,opt,name=chain_hash,json=chainHash,proto3" json:"chain_hash,omitempty"`
|
|
// The pending channel id.
|
|
PendingChanId []byte `protobuf:"bytes,3,opt,name=pending_chan_id,json=pendingChanId,proto3" json:"pending_chan_id,omitempty"`
|
|
// The funding amount in satoshis that initiator wishes to use in the
|
|
// channel.
|
|
FundingAmt uint64 `protobuf:"varint,4,opt,name=funding_amt,json=fundingAmt,proto3" json:"funding_amt,omitempty"`
|
|
// The push amount of the proposed channel in millisatoshis.
|
|
PushAmt uint64 `protobuf:"varint,5,opt,name=push_amt,json=pushAmt,proto3" json:"push_amt,omitempty"`
|
|
// The dust limit of the initiator's commitment tx.
|
|
DustLimit uint64 `protobuf:"varint,6,opt,name=dust_limit,json=dustLimit,proto3" json:"dust_limit,omitempty"`
|
|
// The maximum amount of coins in millisatoshis that can be pending in this
|
|
// channel.
|
|
MaxValueInFlight uint64 `protobuf:"varint,7,opt,name=max_value_in_flight,json=maxValueInFlight,proto3" json:"max_value_in_flight,omitempty"`
|
|
// The minimum amount of satoshis the initiator requires us to have at all
|
|
// times.
|
|
ChannelReserve uint64 `protobuf:"varint,8,opt,name=channel_reserve,json=channelReserve,proto3" json:"channel_reserve,omitempty"`
|
|
// The smallest HTLC in millisatoshis that the initiator will accept.
|
|
MinHtlc uint64 `protobuf:"varint,9,opt,name=min_htlc,json=minHtlc,proto3" json:"min_htlc,omitempty"`
|
|
// The initial fee rate that the initiator suggests for both commitment
|
|
// transactions.
|
|
FeePerKw uint64 `protobuf:"varint,10,opt,name=fee_per_kw,json=feePerKw,proto3" json:"fee_per_kw,omitempty"`
|
|
//
|
|
//The number of blocks to use for the relative time lock in the pay-to-self
|
|
//output of both commitment transactions.
|
|
CsvDelay uint32 `protobuf:"varint,11,opt,name=csv_delay,json=csvDelay,proto3" json:"csv_delay,omitempty"`
|
|
// The total number of incoming HTLC's that the initiator will accept.
|
|
MaxAcceptedHtlcs uint32 `protobuf:"varint,12,opt,name=max_accepted_htlcs,json=maxAcceptedHtlcs,proto3" json:"max_accepted_htlcs,omitempty"`
|
|
// A bit-field which the initiator uses to specify proposed channel
|
|
// behavior.
|
|
ChannelFlags uint32 `protobuf:"varint,13,opt,name=channel_flags,json=channelFlags,proto3" json:"channel_flags,omitempty"`
|
|
// The commitment type the initiator wishes to use for the proposed channel.
|
|
CommitmentType CommitmentType `protobuf:"varint,14,opt,name=commitment_type,json=commitmentType,proto3,enum=lnrpc.CommitmentType" json:"commitment_type,omitempty"`
|
|
}
|
|
|
|
```
|
|
|
|
#### 1ML node information `*.OneMl`
|
|
```go
|
|
type OneML_NodeInfoResponse struct {
|
|
LastUpdate int `json:"last_update"`
|
|
PubKey string `json:"pub_key"`
|
|
Alias string `json:"alias"`
|
|
Addresses []struct {
|
|
Network string `json:"network"`
|
|
Addr string `json:"addr"`
|
|
} `json:"addresses"`
|
|
Color string `json:"color"`
|
|
Capacity int `json:"capacity"`
|
|
Channelcount int `json:"channelcount"`
|
|
Noderank struct {
|
|
Capacity int `json:"capacity"`
|
|
Channelcount int `json:"channelcount"`
|
|
Age int `json:"age"`
|
|
Growth int `json:"growth"`
|
|
Availability int `json:"availability"`
|
|
} `json:"noderank"`
|
|
}
|
|
```
|
|
#### Amboss node information `*.Amboss`
|
|
```go
|
|
type Amboss_NodeInfoResponse struct {
|
|
Socials struct {
|
|
Info struct {
|
|
Email string `json:"email"`
|
|
Telegram string `json:"telegram"`
|
|
Twitter string `json:"twitter"`
|
|
LightningAddress string `json:"lightning_address"`
|
|
Website string `json:"website"`
|
|
Pubkey string `json:"pubkey"`
|
|
MinChannelSize interface{} `json:"minChannelSize"`
|
|
Message string `json:"message"`
|
|
TwitterVerified bool `json:"twitter_verified"`
|
|
Updated time.Time `json:"updated"`
|
|
} `json:"info"`
|
|
} `json:"socials"`
|
|
GraphInfo struct {
|
|
LastUpdate time.Time `json:"last_update"`
|
|
Metrics struct {
|
|
Capacity string `json:"capacity"`
|
|
CapacityRank int `json:"capacity_rank"`
|
|
Channels int `json:"channels"`
|
|
ChannelsRank int `json:"channels_rank"`
|
|
} `json:"metrics"`
|
|
Node struct {
|
|
Addresses []struct {
|
|
Addr string `json:"addr"`
|
|
IPInfo struct {
|
|
City string `json:"city"`
|
|
Country string `json:"country"`
|
|
CountryCode string `json:"country_code"`
|
|
} `json:"ip_info"`
|
|
Network string `json:"network"`
|
|
} `json:"addresses"`
|
|
LastUpdate int `json:"last_update"`
|
|
Color string `json:"color"`
|
|
Features []struct {
|
|
FeatureID string `json:"feature_id"`
|
|
IsKnown bool `json:"is_known"`
|
|
IsRequired bool `json:"is_required"`
|
|
Name string `json:"name"`
|
|
} `json:"features"`
|
|
} `json:"node"`
|
|
} `json:"graph_info"`
|
|
Amboss struct {
|
|
IsFavorite bool `json:"is_favorite"`
|
|
IsPrime bool `json:"is_prime"`
|
|
NumberFavorites int `json:"number_favorites"`
|
|
NewChannelGossipDelta struct {
|
|
Mean string `json:"mean"`
|
|
Sd string `json:"sd"`
|
|
} `json:"new_channel_gossip_delta"`
|
|
Notifications struct {
|
|
NumberSubscribers int `json:"number_subscribers"`
|
|
} `json:"notifications"`
|
|
} `json:"amboss"`
|
|
}
|
|
```
|
|
|
|
#### Network information `*.Network`
|
|
*TBD*
|