mirror of
https://github.com/callebtc/electronwall.git
synced 2025-12-17 07:04:21 +01:00
works
This commit is contained in:
190
README.md
190
README.md
@@ -1,7 +1,9 @@
|
|||||||
# ⚡️🛡 electronwall
|
# ⚡️🛡 electronwall
|
||||||
A tiny firewall for LND that can filter Lightning **channel opening requests** and **HTLC forwards** on your node based on set rules.
|
A tiny firewall for LND that can filter Lightning **channel opening requests** and **HTLC forwards** based on your custom rules.
|
||||||
|
|
||||||
Currently, 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.
|
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.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -21,10 +23,192 @@ go build .
|
|||||||
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).
|
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
|
## Config
|
||||||
Edit `config.yaml.example` and rename to `config.yaml`.
|
Edit `config.yaml.example` and rename to `config.yaml`.
|
||||||
|
|
||||||
## Run
|
## Run
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./electronwall
|
./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`
|
||||||
|
```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*
|
||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/callebtc/electronwall/config"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -44,7 +45,7 @@ func (c *OneMlClient) GetNodeInfo(pubkey string) (OneML_NodeInfoResponse, error)
|
|||||||
log.Infof("Getting info from 1ml.com for %s", pubkey)
|
log.Infof("Getting info from 1ml.com for %s", pubkey)
|
||||||
|
|
||||||
client := http.Client{
|
client := http.Client{
|
||||||
Timeout: time.Second * 2, // Timeout after 2 seconds
|
Timeout: time.Second * time.Duration(config.Configuration.ApiRules.OneMl.Timeout),
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||||
|
|||||||
@@ -4,32 +4,11 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/callebtc/electronwall/config"
|
||||||
"github.com/machinebox/graphql"
|
"github.com/machinebox/graphql"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AutoGenerated struct {
|
|
||||||
Data struct {
|
|
||||||
GetNode struct {
|
|
||||||
GraphInfo struct {
|
|
||||||
Node struct {
|
|
||||||
Alias string `json:"alias"`
|
|
||||||
} `json:"node"`
|
|
||||||
} `json:"graph_info"`
|
|
||||||
} `json:"getNode"`
|
|
||||||
} `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var simple_query = `query Node($pubkey: String!) {
|
|
||||||
getNode(pubkey: $pubkey) {
|
|
||||||
graph_info {
|
|
||||||
node {
|
|
||||||
alias
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`
|
|
||||||
|
|
||||||
type Amboss_NodeInfoResponse struct {
|
type Amboss_NodeInfoResponse struct {
|
||||||
Socials struct {
|
Socials struct {
|
||||||
Info struct {
|
Info struct {
|
||||||
@@ -178,7 +157,10 @@ func (c *AmbossClient) GetNodeInfo(pubkey string) (Amboss_NodeInfoResponse, erro
|
|||||||
graphqlRequest.Header.Set("Content-Type", "application/json")
|
graphqlRequest.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
var r_nested Amboss_NodeInfoResponse_Nested
|
var r_nested Amboss_NodeInfoResponse_Nested
|
||||||
if err := graphqlClient.Run(context.Background(), graphqlRequest, &r_nested.Data); err != nil {
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(config.Configuration.ApiRules.Amboss.Timeout))
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := graphqlClient.Run(ctx, graphqlRequest, &r_nested.Data); err != nil {
|
||||||
log.Errorf("[amboss] api error: %v", err)
|
log.Errorf("[amboss] api error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
45
api/api.go
45
api/api.go
@@ -1,6 +1,9 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import log "github.com/sirupsen/logrus"
|
import (
|
||||||
|
"github.com/callebtc/electronwall/config"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
type ApiClient interface {
|
type ApiClient interface {
|
||||||
GetNodeInfo(pubkey string) OneML_NodeInfoResponse
|
GetNodeInfo(pubkey string) OneML_NodeInfoResponse
|
||||||
@@ -12,23 +15,31 @@ type ApiNodeInfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetApiNodeinfo(pubkey string) (ApiNodeInfo, error) {
|
func GetApiNodeinfo(pubkey string) (ApiNodeInfo, error) {
|
||||||
// get info from 1ml
|
response := ApiNodeInfo{
|
||||||
OnemlClient := GetOneMlClient()
|
OneMl: OneML_NodeInfoResponse{},
|
||||||
onemlNodeInfo, err := OnemlClient.GetNodeInfo(pubkey)
|
Amboss: Amboss_NodeInfoResponse{},
|
||||||
if err != nil {
|
|
||||||
log.Errorf(err.Error())
|
|
||||||
onemlNodeInfo = OneML_NodeInfoResponse{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// get info from amboss
|
if config.Configuration.ApiRules.OneMl.Active {
|
||||||
ambossClient := GetAmbossClient()
|
// get info from 1ml
|
||||||
ambossNodeInfo, err := ambossClient.GetNodeInfo(pubkey)
|
OnemlClient := GetOneMlClient()
|
||||||
if err != nil {
|
onemlNodeInfo, err := OnemlClient.GetNodeInfo(pubkey)
|
||||||
log.Errorf(err.Error())
|
if err != nil {
|
||||||
ambossNodeInfo = Amboss_NodeInfoResponse{}
|
log.Errorf(err.Error())
|
||||||
|
onemlNodeInfo = OneML_NodeInfoResponse{}
|
||||||
|
}
|
||||||
|
response.OneMl = onemlNodeInfo
|
||||||
}
|
}
|
||||||
return ApiNodeInfo{
|
|
||||||
OneMl: onemlNodeInfo,
|
if config.Configuration.ApiRules.Amboss.Active {
|
||||||
Amboss: ambossNodeInfo,
|
// get info from amboss
|
||||||
}, err
|
ambossClient := GetAmbossClient()
|
||||||
|
ambossNodeInfo, err := ambossClient.GetNodeInfo(pubkey)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(err.Error())
|
||||||
|
ambossNodeInfo = Amboss_NodeInfoResponse{}
|
||||||
|
}
|
||||||
|
response.Amboss = ambossNodeInfo
|
||||||
|
}
|
||||||
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/callebtc/electronwall/api"
|
"github.com/callebtc/electronwall/api"
|
||||||
|
"github.com/callebtc/electronwall/config"
|
||||||
"github.com/callebtc/electronwall/rules"
|
"github.com/callebtc/electronwall/rules"
|
||||||
"github.com/callebtc/electronwall/types"
|
"github.com/callebtc/electronwall/types"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
@@ -25,7 +26,7 @@ func (app *App) GetChannelAcceptEvent(ctx context.Context, req lnrpc.ChannelAcce
|
|||||||
log.Errorf(err.Error())
|
log.Errorf(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
noeInfo, err := api.GetApiNodeinfo(string(req.NodePubkey))
|
noeInfo, err := api.GetApiNodeinfo(hex.EncodeToString(req.NodePubkey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(err.Error())
|
log.Errorf(err.Error())
|
||||||
}
|
}
|
||||||
@@ -125,7 +126,7 @@ func (app *App) interceptChannelEvents(ctx context.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// parse list
|
// parse list
|
||||||
list_decision, err := app.channelAcceptDecision(req)
|
list_decision, err := app.channelAcceptListDecision(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -137,7 +138,7 @@ func (app *App) interceptChannelEvents(ctx context.Context) error {
|
|||||||
|
|
||||||
res := lnrpc.ChannelAcceptResponse{}
|
res := lnrpc.ChannelAcceptResponse{}
|
||||||
if accept {
|
if accept {
|
||||||
if Configuration.LogJson {
|
if config.Configuration.LogJson {
|
||||||
contextLogger.Infof("allow")
|
contextLogger.Infof("allow")
|
||||||
} else {
|
} else {
|
||||||
log.Infof("[channel] ✅ Allow channel %s", channel_info_string)
|
log.Infof("[channel] ✅ Allow channel %s", channel_info_string)
|
||||||
@@ -152,13 +153,13 @@ func (app *App) interceptChannelEvents(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if Configuration.LogJson {
|
if config.Configuration.LogJson {
|
||||||
contextLogger.Infof("deny")
|
contextLogger.Infof("deny")
|
||||||
} else {
|
} else {
|
||||||
log.Infof("[channel] ❌ Deny channel %s", channel_info_string)
|
log.Infof("[channel] ❌ Deny channel %s", channel_info_string)
|
||||||
}
|
}
|
||||||
res = lnrpc.ChannelAcceptResponse{Accept: false,
|
res = lnrpc.ChannelAcceptResponse{Accept: false,
|
||||||
Error: Configuration.ChannelRejectMessage}
|
Error: config.Configuration.ChannelRejectMessage}
|
||||||
}
|
}
|
||||||
err = acceptClient.Send(&res)
|
err = acceptClient.Send(&res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -168,26 +169,26 @@ func (app *App) interceptChannelEvents(ctx context.Context) error {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *App) channelAcceptDecision(req lnrpc.ChannelAcceptRequest) (bool, error) {
|
func (app *App) channelAcceptListDecision(req lnrpc.ChannelAcceptRequest) (bool, error) {
|
||||||
// determine mode and list of channels to parse
|
// determine mode and list of channels to parse
|
||||||
var accept bool
|
var accept bool
|
||||||
var listToParse []string
|
var listToParse []string
|
||||||
if Configuration.ChannelMode == "allowlist" {
|
if config.Configuration.ChannelMode == "allowlist" {
|
||||||
accept = false
|
accept = false
|
||||||
listToParse = Configuration.ChannelAllowlist
|
listToParse = config.Configuration.ChannelAllowlist
|
||||||
} else if Configuration.ChannelMode == "denylist" {
|
} else if config.Configuration.ChannelMode == "denylist" {
|
||||||
accept = true
|
accept = true
|
||||||
listToParse = Configuration.ChannelDenylist
|
listToParse = config.Configuration.ChannelDenylist
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse and make decision
|
// parse and make decision
|
||||||
log.Infof("TRYING %s", string(req.NodePubkey))
|
|
||||||
for _, pubkey := range listToParse {
|
for _, pubkey := range listToParse {
|
||||||
if string(req.NodePubkey) == pubkey || pubkey == "*" {
|
if hex.EncodeToString(req.NodePubkey) == pubkey || pubkey == "*" {
|
||||||
accept = !accept
|
accept = !accept
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
log.Infof("[list] decision: %t", accept)
|
||||||
return accept, nil
|
return accept, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -214,7 +215,7 @@ func (app *App) logChannelEvents(ctx context.Context) error {
|
|||||||
alias,
|
alias,
|
||||||
)
|
)
|
||||||
|
|
||||||
if Configuration.LogJson {
|
if config.Configuration.LogJson {
|
||||||
contextLogger := log.WithFields(log.Fields{
|
contextLogger := log.WithFields(log.Fields{
|
||||||
"event": "channel",
|
"event": "channel",
|
||||||
"capacity": event.GetOpenChannel().Capacity,
|
"capacity": event.GetOpenChannel().Capacity,
|
||||||
|
|||||||
@@ -41,3 +41,13 @@ forward-allowlist:
|
|||||||
- "*->25328x256x0" # all forwards to this channel
|
- "*->25328x256x0" # all forwards to this channel
|
||||||
forward-denylist:
|
forward-denylist:
|
||||||
- "9961472x65537x1"
|
- "9961472x65537x1"
|
||||||
|
|
||||||
|
# ---- Javascript rules ----
|
||||||
|
rules:
|
||||||
|
apply: true # whether to respect the rule decision
|
||||||
|
oneml: # 1ML.com API
|
||||||
|
active: true
|
||||||
|
timeout: 5 # API timeout in seconds
|
||||||
|
amboss: # Amboss.space API
|
||||||
|
active: true
|
||||||
|
timeout: 5 # API timeout in seconds
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -20,6 +20,17 @@ var Configuration = struct {
|
|||||||
ForwardMode string `yaml:"forward-mode"`
|
ForwardMode string `yaml:"forward-mode"`
|
||||||
ForwardAllowlist []string `yaml:"forward-allowlist"`
|
ForwardAllowlist []string `yaml:"forward-allowlist"`
|
||||||
ForwardDenylist []string `yaml:"forward-denylist"`
|
ForwardDenylist []string `yaml:"forward-denylist"`
|
||||||
|
ApiRules struct {
|
||||||
|
Apply bool `yaml:"apply"`
|
||||||
|
OneMl struct {
|
||||||
|
Active bool `yaml:"active"`
|
||||||
|
Timeout int `yaml:"timeout"`
|
||||||
|
} `yaml:"oneml"`
|
||||||
|
Amboss struct {
|
||||||
|
Active bool `yaml:"active"`
|
||||||
|
Timeout int `yaml:"timeout"`
|
||||||
|
} `yaml:"amboss"`
|
||||||
|
} `yaml:"rules"`
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -31,8 +42,6 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func checkConfig() {
|
func checkConfig() {
|
||||||
setLogger(Configuration.Debug, Configuration.LogJson)
|
|
||||||
welcome()
|
|
||||||
|
|
||||||
if Configuration.Host == "" {
|
if Configuration.Host == "" {
|
||||||
panic(fmt.Errorf("no host specified in config.yaml"))
|
panic(fmt.Errorf("no host specified in config.yaml"))
|
||||||
@@ -20,12 +20,12 @@ func trimPubKey(pubkey []byte) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func welcome() {
|
func Welcome() {
|
||||||
log.Info("---- ⚡️ electronwall 0.3.3 ⚡️ ----")
|
log.Info("---- ⚡️ electronwall 0.4 ⚡️ ----")
|
||||||
}
|
}
|
||||||
|
|
||||||
// setLogger will initialize the log format
|
// SetLogger will initialize the log format
|
||||||
func setLogger(debug bool, json bool) {
|
func SetLogger(debug bool, json bool) {
|
||||||
if json {
|
if json {
|
||||||
customFormatter := new(log.JSONFormatter)
|
customFormatter := new(log.JSONFormatter)
|
||||||
customFormatter.TimestampFormat = "2006-01-02 15:04:05"
|
customFormatter.TimestampFormat = "2006-01-02 15:04:05"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/callebtc/electronwall/config"
|
||||||
"github.com/callebtc/electronwall/rules"
|
"github.com/callebtc/electronwall/rules"
|
||||||
"github.com/callebtc/electronwall/types"
|
"github.com/callebtc/electronwall/types"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||||
@@ -144,14 +145,14 @@ func (app *App) interceptHtlcEvents(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
switch accept {
|
switch accept {
|
||||||
case true:
|
case true:
|
||||||
if Configuration.LogJson {
|
if config.Configuration.LogJson {
|
||||||
contextLogger.Infof("allow")
|
contextLogger.Infof("allow")
|
||||||
} else {
|
} else {
|
||||||
log.Infof("[forward] ✅ Allow HTLC %s", forward_info_string)
|
log.Infof("[forward] ✅ Allow HTLC %s", forward_info_string)
|
||||||
}
|
}
|
||||||
response.Action = routerrpc.ResolveHoldForwardAction_RESUME
|
response.Action = routerrpc.ResolveHoldForwardAction_RESUME
|
||||||
case false:
|
case false:
|
||||||
if Configuration.LogJson {
|
if config.Configuration.LogJson {
|
||||||
contextLogger.Infof("deny")
|
contextLogger.Infof("deny")
|
||||||
} else {
|
} else {
|
||||||
log.Infof("[forward] ❌ Deny HTLC %s", forward_info_string)
|
log.Infof("[forward] ❌ Deny HTLC %s", forward_info_string)
|
||||||
@@ -181,15 +182,15 @@ func (app *App) htlcInterceptDecision(ctx context.Context, event *routerrpc.Forw
|
|||||||
// time.Sleep(60 * time.Second)
|
// time.Sleep(60 * time.Second)
|
||||||
|
|
||||||
// determine filtering mode and list to parse
|
// determine filtering mode and list to parse
|
||||||
switch Configuration.ForwardMode {
|
switch config.Configuration.ForwardMode {
|
||||||
case "allowlist":
|
case "allowlist":
|
||||||
accept = false
|
accept = false
|
||||||
listToParse = Configuration.ForwardAllowlist
|
listToParse = config.Configuration.ForwardAllowlist
|
||||||
case "denylist":
|
case "denylist":
|
||||||
accept = true
|
accept = true
|
||||||
listToParse = Configuration.ForwardDenylist
|
listToParse = config.Configuration.ForwardDenylist
|
||||||
default:
|
default:
|
||||||
return false, fmt.Errorf("unknown forward mode: %s", Configuration.ForwardMode)
|
return false, fmt.Errorf("unknown forward mode: %s", config.Configuration.ForwardMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse list and decide
|
// parse list and decide
|
||||||
@@ -217,6 +218,7 @@ func (app *App) htlcInterceptDecision(ctx context.Context, event *routerrpc.Forw
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// decision_chan <- accept
|
// decision_chan <- accept
|
||||||
|
log.Infof("[list] decision: %t", accept)
|
||||||
return accept, nil
|
return accept, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,7 +253,7 @@ func (app *App) logHtlcEvents(ctx context.Context) error {
|
|||||||
|
|
||||||
switch event.Event.(type) {
|
switch event.Event.(type) {
|
||||||
case *routerrpc.HtlcEvent_SettleEvent:
|
case *routerrpc.HtlcEvent_SettleEvent:
|
||||||
if Configuration.LogJson {
|
if config.Configuration.LogJson {
|
||||||
contextLogger(event).Infof("SettleEvent")
|
contextLogger(event).Infof("SettleEvent")
|
||||||
// contextLogger.Debugf("[forward] Preimage: %s", hex.EncodeToString(event.GetSettleEvent().Preimage))
|
// contextLogger.Debugf("[forward] Preimage: %s", hex.EncodeToString(event.GetSettleEvent().Preimage))
|
||||||
} else {
|
} else {
|
||||||
@@ -262,7 +264,7 @@ func (app *App) logHtlcEvents(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case *routerrpc.HtlcEvent_ForwardFailEvent:
|
case *routerrpc.HtlcEvent_ForwardFailEvent:
|
||||||
if Configuration.LogJson {
|
if config.Configuration.LogJson {
|
||||||
contextLogger(event).Infof("ForwardFailEvent")
|
contextLogger(event).Infof("ForwardFailEvent")
|
||||||
// contextLogger.Debugf("[forward] Reason: %s", event.GetForwardFailEvent())
|
// contextLogger.Debugf("[forward] Reason: %s", event.GetForwardFailEvent())
|
||||||
} else {
|
} else {
|
||||||
@@ -271,7 +273,7 @@ func (app *App) logHtlcEvents(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case *routerrpc.HtlcEvent_ForwardEvent:
|
case *routerrpc.HtlcEvent_ForwardEvent:
|
||||||
if Configuration.LogJson {
|
if config.Configuration.LogJson {
|
||||||
contextLogger(event).Infof("ForwardEvent")
|
contextLogger(event).Infof("ForwardEvent")
|
||||||
} else {
|
} else {
|
||||||
log.Infof("[forward] HTLC ForwardEvent (chan_id:%s, htlc_id:%d)", ParseChannelID(event.IncomingChannelId), event.IncomingHtlcId)
|
log.Infof("[forward] HTLC ForwardEvent (chan_id:%s, htlc_id:%d)", ParseChannelID(event.IncomingChannelId), event.IncomingHtlcId)
|
||||||
@@ -280,7 +282,7 @@ func (app *App) logHtlcEvents(ctx context.Context) error {
|
|||||||
// log.Debugf("[forward] Details: %s", event.GetForwardEvent().String())
|
// log.Debugf("[forward] Details: %s", event.GetForwardEvent().String())
|
||||||
|
|
||||||
case *routerrpc.HtlcEvent_LinkFailEvent:
|
case *routerrpc.HtlcEvent_LinkFailEvent:
|
||||||
if Configuration.LogJson {
|
if config.Configuration.LogJson {
|
||||||
contextLogger(event).Infof("LinkFailEvent")
|
contextLogger(event).Infof("LinkFailEvent")
|
||||||
// contextLogger(event).Debugf("[forward] Reason: %s", event.GetLinkFailEvent().FailureString)
|
// contextLogger(event).Debugf("[forward] Reason: %s", event.GetLinkFailEvent().FailureString)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
11
main.go
11
main.go
@@ -5,6 +5,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/callebtc/electronwall/config"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||||
"github.com/lightningnetwork/lnd/macaroons"
|
"github.com/lightningnetwork/lnd/macaroons"
|
||||||
@@ -39,11 +40,11 @@ func NewApp(ctx context.Context, lnd lndclient) *App {
|
|||||||
|
|
||||||
// gets the lnd grpc connection
|
// gets the lnd grpc connection
|
||||||
func getClientConnection(ctx context.Context) (*grpc.ClientConn, error) {
|
func getClientConnection(ctx context.Context) (*grpc.ClientConn, error) {
|
||||||
creds, err := credentials.NewClientTLSFromFile(Configuration.TLSPath, "")
|
creds, err := credentials.NewClientTLSFromFile(config.Configuration.TLSPath, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
macBytes, err := ioutil.ReadFile(Configuration.MacaroonPath)
|
macBytes, err := ioutil.ReadFile(config.Configuration.MacaroonPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -60,8 +61,8 @@ func getClientConnection(ctx context.Context) (*grpc.ClientConn, error) {
|
|||||||
grpc.WithBlock(),
|
grpc.WithBlock(),
|
||||||
grpc.WithPerRPCCredentials(cred),
|
grpc.WithPerRPCCredentials(cred),
|
||||||
}
|
}
|
||||||
log.Infof("Connecting to %s", Configuration.Host)
|
log.Infof("Connecting to %s", config.Configuration.Host)
|
||||||
conn, err := grpc.DialContext(ctx, Configuration.Host, opts...)
|
conn, err := grpc.DialContext(ctx, config.Configuration.Host, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -84,6 +85,8 @@ func newLndClient(ctx context.Context) (*LndClient, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
SetLogger(config.Configuration.Debug, config.Configuration.LogJson)
|
||||||
|
Welcome()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
for {
|
for {
|
||||||
lnd, err := newLndClient(ctx)
|
lnd, err := newLndClient(ctx)
|
||||||
|
|||||||
92
main_test.go
92
main_test.go
@@ -2,12 +2,12 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"testing"
|
"github.com/callebtc/electronwall/config"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestApp(t *testing.T) {
|
func TestApp(t *testing.T) {
|
||||||
@@ -33,8 +33,8 @@ func TestHTLCDenylist_BothMatch(t *testing.T) {
|
|||||||
|
|
||||||
app := NewApp(ctx, client)
|
app := NewApp(ctx, client)
|
||||||
|
|
||||||
Configuration.ForwardMode = "denylist"
|
config.Configuration.ForwardMode = "denylist"
|
||||||
Configuration.ForwardDenylist = []string{"700762x1327x1->690757x1005x1"}
|
config.Configuration.ForwardDenylist = []string{"700762x1327x1->690757x1005x1"}
|
||||||
|
|
||||||
app.DispatchHTLCAcceptor(ctx)
|
app.DispatchHTLCAcceptor(ctx)
|
||||||
|
|
||||||
@@ -67,8 +67,8 @@ func TestHTLCDenylist_BothNoMatch(t *testing.T) {
|
|||||||
|
|
||||||
app := NewApp(ctx, client)
|
app := NewApp(ctx, client)
|
||||||
|
|
||||||
Configuration.ForwardMode = "denylist"
|
config.Configuration.ForwardMode = "denylist"
|
||||||
Configuration.ForwardDenylist = []string{"700762x1327x1->690757x1005x1"}
|
config.Configuration.ForwardDenylist = []string{"700762x1327x1->690757x1005x1"}
|
||||||
|
|
||||||
app.DispatchHTLCAcceptor(ctx)
|
app.DispatchHTLCAcceptor(ctx)
|
||||||
|
|
||||||
@@ -101,12 +101,12 @@ func TestHTLCDenylist_WildCardOutBothMatch(t *testing.T) {
|
|||||||
|
|
||||||
app := NewApp(ctx, client)
|
app := NewApp(ctx, client)
|
||||||
|
|
||||||
Configuration.ForwardMode = "denylist"
|
config.Configuration.ForwardMode = "denylist"
|
||||||
Configuration.ForwardDenylist = []string{"700762x1327x1->690757x1005x1"}
|
config.Configuration.ForwardDenylist = []string{"700762x1327x1->690757x1005x1"}
|
||||||
|
|
||||||
app.DispatchHTLCAcceptor(ctx)
|
app.DispatchHTLCAcceptor(ctx)
|
||||||
|
|
||||||
Configuration.ForwardDenylist = []string{"700762x1327x1->*"}
|
config.Configuration.ForwardDenylist = []string{"700762x1327x1->*"}
|
||||||
|
|
||||||
key := &routerrpc.CircuitKey{
|
key := &routerrpc.CircuitKey{
|
||||||
ChanId: 770495967390531585,
|
ChanId: 770495967390531585,
|
||||||
@@ -137,14 +137,14 @@ func TestHTLCDenylist_WildCardOutNoMatch(t *testing.T) {
|
|||||||
|
|
||||||
app := NewApp(ctx, client)
|
app := NewApp(ctx, client)
|
||||||
|
|
||||||
Configuration.ForwardMode = "denylist"
|
config.Configuration.ForwardMode = "denylist"
|
||||||
Configuration.ForwardDenylist = []string{"700762x1327x1->690757x1005x1"}
|
config.Configuration.ForwardDenylist = []string{"700762x1327x1->690757x1005x1"}
|
||||||
|
|
||||||
app.DispatchHTLCAcceptor(ctx)
|
app.DispatchHTLCAcceptor(ctx)
|
||||||
|
|
||||||
// wildcard out, first key doesn't match: should be allowed
|
// wildcard out, first key doesn't match: should be allowed
|
||||||
|
|
||||||
Configuration.ForwardDenylist = []string{"700762x1327x1->*"}
|
config.Configuration.ForwardDenylist = []string{"700762x1327x1->*"}
|
||||||
|
|
||||||
key := &routerrpc.CircuitKey{
|
key := &routerrpc.CircuitKey{
|
||||||
ChanId: 759495353533530113,
|
ChanId: 759495353533530113,
|
||||||
@@ -176,11 +176,11 @@ func TestHTLCAllowlist_BothMatch(t *testing.T) {
|
|||||||
|
|
||||||
app.DispatchHTLCAcceptor(ctx)
|
app.DispatchHTLCAcceptor(ctx)
|
||||||
|
|
||||||
Configuration.ForwardMode = "allowlist"
|
config.Configuration.ForwardMode = "allowlist"
|
||||||
|
|
||||||
// both keys correct: should be allowed
|
// both keys correct: should be allowed
|
||||||
Configuration.ForwardAllowlist = []string{"700762x1327x1->690757x1005x1"}
|
config.Configuration.ForwardAllowlist = []string{"700762x1327x1->690757x1005x1"}
|
||||||
log.Tracef("[test] Mode: %s, Rules: %v", Configuration.ForwardMode, Configuration.ForwardAllowlist)
|
log.Tracef("[test] Mode: %s, Rules: %v", config.Configuration.ForwardMode, config.Configuration.ForwardAllowlist)
|
||||||
|
|
||||||
key := &routerrpc.CircuitKey{
|
key := &routerrpc.CircuitKey{
|
||||||
ChanId: 770495967390531585,
|
ChanId: 770495967390531585,
|
||||||
@@ -212,10 +212,10 @@ func TestHTLCAllowlist_BothNonMatch(t *testing.T) {
|
|||||||
|
|
||||||
app.DispatchHTLCAcceptor(ctx)
|
app.DispatchHTLCAcceptor(ctx)
|
||||||
|
|
||||||
Configuration.ForwardMode = "allowlist"
|
config.Configuration.ForwardMode = "allowlist"
|
||||||
// both keys wrong: should be denied
|
// both keys wrong: should be denied
|
||||||
Configuration.ForwardAllowlist = []string{"700762x1327x1->690757x1005x1"}
|
config.Configuration.ForwardAllowlist = []string{"700762x1327x1->690757x1005x1"}
|
||||||
log.Tracef("[test] Mode: %s, Rules: %v", Configuration.ForwardMode, Configuration.ForwardAllowlist)
|
log.Tracef("[test] Mode: %s, Rules: %v", config.Configuration.ForwardMode, config.Configuration.ForwardAllowlist)
|
||||||
|
|
||||||
key := &routerrpc.CircuitKey{
|
key := &routerrpc.CircuitKey{
|
||||||
ChanId: 123456789876543210,
|
ChanId: 123456789876543210,
|
||||||
@@ -246,10 +246,10 @@ func TestHTLCAllowlist_Wildcard(t *testing.T) {
|
|||||||
|
|
||||||
app.DispatchHTLCAcceptor(ctx)
|
app.DispatchHTLCAcceptor(ctx)
|
||||||
|
|
||||||
Configuration.ForwardMode = "allowlist"
|
config.Configuration.ForwardMode = "allowlist"
|
||||||
// wildcard: should be allowed
|
// wildcard: should be allowed
|
||||||
Configuration.ForwardAllowlist = []string{"*"}
|
config.Configuration.ForwardAllowlist = []string{"*"}
|
||||||
log.Tracef("[test] Mode: %s, Rules: %v", Configuration.ForwardMode, Configuration.ForwardAllowlist)
|
log.Tracef("[test] Mode: %s, Rules: %v", config.Configuration.ForwardMode, config.Configuration.ForwardAllowlist)
|
||||||
key := &routerrpc.CircuitKey{
|
key := &routerrpc.CircuitKey{
|
||||||
ChanId: 123456789876543210,
|
ChanId: 123456789876543210,
|
||||||
HtlcId: 1337000,
|
HtlcId: 1337000,
|
||||||
@@ -279,11 +279,11 @@ func TestHTLCAllowlist_WildcardIn(t *testing.T) {
|
|||||||
|
|
||||||
app.DispatchHTLCAcceptor(ctx)
|
app.DispatchHTLCAcceptor(ctx)
|
||||||
|
|
||||||
Configuration.ForwardMode = "allowlist"
|
config.Configuration.ForwardMode = "allowlist"
|
||||||
// wildcard in: should be allowed
|
// wildcard in: should be allowed
|
||||||
|
|
||||||
Configuration.ForwardAllowlist = []string{"*->690757x1005x1"}
|
config.Configuration.ForwardAllowlist = []string{"*->690757x1005x1"}
|
||||||
log.Tracef("[test] Mode: %s, Rules: %v", Configuration.ForwardMode, Configuration.ForwardAllowlist)
|
log.Tracef("[test] Mode: %s, Rules: %v", config.Configuration.ForwardMode, config.Configuration.ForwardAllowlist)
|
||||||
|
|
||||||
key := &routerrpc.CircuitKey{
|
key := &routerrpc.CircuitKey{
|
||||||
ChanId: 123456789876543210,
|
ChanId: 123456789876543210,
|
||||||
@@ -314,10 +314,10 @@ func TestHTLCAllowlist_WildcardOut(t *testing.T) {
|
|||||||
|
|
||||||
app.DispatchHTLCAcceptor(ctx)
|
app.DispatchHTLCAcceptor(ctx)
|
||||||
|
|
||||||
Configuration.ForwardMode = "allowlist"
|
config.Configuration.ForwardMode = "allowlist"
|
||||||
// wildcard out: should be allowed
|
// wildcard out: should be allowed
|
||||||
Configuration.ForwardAllowlist = []string{"700762x1327x1->*"}
|
config.Configuration.ForwardAllowlist = []string{"700762x1327x1->*"}
|
||||||
log.Tracef("[test] Mode: %s, Rules: %v", Configuration.ForwardMode, Configuration.ForwardAllowlist)
|
log.Tracef("[test] Mode: %s, Rules: %v", config.Configuration.ForwardMode, config.Configuration.ForwardAllowlist)
|
||||||
|
|
||||||
key := &routerrpc.CircuitKey{
|
key := &routerrpc.CircuitKey{
|
||||||
ChanId: 770495967390531585,
|
ChanId: 770495967390531585,
|
||||||
@@ -348,10 +348,10 @@ func TestHTLCAllowlist_WildcardOutWrongKeyIn(t *testing.T) {
|
|||||||
|
|
||||||
app.DispatchHTLCAcceptor(ctx)
|
app.DispatchHTLCAcceptor(ctx)
|
||||||
|
|
||||||
Configuration.ForwardMode = "allowlist"
|
config.Configuration.ForwardMode = "allowlist"
|
||||||
// wildcard out but wrong in key: should be denied
|
// wildcard out but wrong in key: should be denied
|
||||||
Configuration.ForwardAllowlist = []string{"700762x1327x1->*"}
|
config.Configuration.ForwardAllowlist = []string{"700762x1327x1->*"}
|
||||||
log.Tracef("[test] Mode: %s, Rules: %v", Configuration.ForwardMode, Configuration.ForwardAllowlist)
|
log.Tracef("[test] Mode: %s, Rules: %v", config.Configuration.ForwardMode, config.Configuration.ForwardAllowlist)
|
||||||
|
|
||||||
key := &routerrpc.CircuitKey{
|
key := &routerrpc.CircuitKey{
|
||||||
ChanId: 123456789876543210,
|
ChanId: 123456789876543210,
|
||||||
@@ -382,10 +382,10 @@ func TestHTLCAllowlist_WildcardIn_WrongKeyOut(t *testing.T) {
|
|||||||
|
|
||||||
app.DispatchHTLCAcceptor(ctx)
|
app.DispatchHTLCAcceptor(ctx)
|
||||||
|
|
||||||
Configuration.ForwardMode = "allowlist"
|
config.Configuration.ForwardMode = "allowlist"
|
||||||
// wildcard in but wrong out key: should be denied
|
// wildcard in but wrong out key: should be denied
|
||||||
Configuration.ForwardAllowlist = []string{"*->700762x1327x1"}
|
config.Configuration.ForwardAllowlist = []string{"*->700762x1327x1"}
|
||||||
log.Tracef("[test] Mode: %s, Rules: %v", Configuration.ForwardMode, Configuration.ForwardAllowlist)
|
log.Tracef("[test] Mode: %s, Rules: %v", config.Configuration.ForwardMode, config.Configuration.ForwardAllowlist)
|
||||||
|
|
||||||
key := &routerrpc.CircuitKey{
|
key := &routerrpc.CircuitKey{
|
||||||
ChanId: 123456789876543210,
|
ChanId: 123456789876543210,
|
||||||
@@ -416,10 +416,10 @@ func TestHTLCAllowlist_WildcardBoth(t *testing.T) {
|
|||||||
|
|
||||||
app.DispatchHTLCAcceptor(ctx)
|
app.DispatchHTLCAcceptor(ctx)
|
||||||
|
|
||||||
Configuration.ForwardMode = "allowlist"
|
config.Configuration.ForwardMode = "allowlist"
|
||||||
// wildcard both: should be allowed
|
// wildcard both: should be allowed
|
||||||
Configuration.ForwardAllowlist = []string{"*->*"}
|
config.Configuration.ForwardAllowlist = []string{"*->*"}
|
||||||
log.Tracef("[test] Mode: %s, Rules: %v", Configuration.ForwardMode, Configuration.ForwardAllowlist)
|
log.Tracef("[test] Mode: %s, Rules: %v", config.Configuration.ForwardMode, config.Configuration.ForwardAllowlist)
|
||||||
|
|
||||||
key := &routerrpc.CircuitKey{
|
key := &routerrpc.CircuitKey{
|
||||||
ChanId: 123456789876543210,
|
ChanId: 123456789876543210,
|
||||||
@@ -452,8 +452,8 @@ func TestChannelAllowlist_CorrectKey(t *testing.T) {
|
|||||||
|
|
||||||
app := NewApp(ctx, client)
|
app := NewApp(ctx, client)
|
||||||
|
|
||||||
Configuration.ChannelMode = "allowlist"
|
config.Configuration.ChannelMode = "allowlist"
|
||||||
Configuration.ChannelAllowlist = []string{"03006fcf3312dae8d068ea297f58e2bd00ec1ffe214b793eda46966b6294a53ce6"}
|
config.Configuration.ChannelAllowlist = []string{"03006fcf3312dae8d068ea297f58e2bd00ec1ffe214b793eda46966b6294a53ce6"}
|
||||||
|
|
||||||
app.DispatchChannelAcceptor(ctx)
|
app.DispatchChannelAcceptor(ctx)
|
||||||
|
|
||||||
@@ -476,8 +476,8 @@ func TestChannelAllowlist_WrongKey(t *testing.T) {
|
|||||||
|
|
||||||
app := NewApp(ctx, client)
|
app := NewApp(ctx, client)
|
||||||
|
|
||||||
Configuration.ChannelMode = "allowlist"
|
config.Configuration.ChannelMode = "allowlist"
|
||||||
Configuration.ChannelAllowlist = []string{"03006fcf3312dae8d068ea297f58e2bd00ec1ffe214b793eda46966b6294a53ce6"}
|
config.Configuration.ChannelAllowlist = []string{"03006fcf3312dae8d068ea297f58e2bd00ec1ffe214b793eda46966b6294a53ce6"}
|
||||||
|
|
||||||
app.DispatchChannelAcceptor(ctx)
|
app.DispatchChannelAcceptor(ctx)
|
||||||
// wrong key: should be denied
|
// wrong key: should be denied
|
||||||
@@ -501,8 +501,8 @@ func TestChannelAllowlist_Wildcard(t *testing.T) {
|
|||||||
app.DispatchChannelAcceptor(ctx)
|
app.DispatchChannelAcceptor(ctx)
|
||||||
|
|
||||||
// wildcard: should be allowed
|
// wildcard: should be allowed
|
||||||
Configuration.ChannelMode = "allowlist"
|
config.Configuration.ChannelMode = "allowlist"
|
||||||
Configuration.ChannelAllowlist = []string{"*"}
|
config.Configuration.ChannelAllowlist = []string{"*"}
|
||||||
|
|
||||||
client.channelAcceptorRequests <- &lnrpc.ChannelAcceptRequest{
|
client.channelAcceptorRequests <- &lnrpc.ChannelAcceptRequest{
|
||||||
NodePubkey: []byte("03006fcf3312dae8d068ea297f58e2bd00ec1ffe214b793eda46966b6294a53ce6"),
|
NodePubkey: []byte("03006fcf3312dae8d068ea297f58e2bd00ec1ffe214b793eda46966b6294a53ce6"),
|
||||||
@@ -521,8 +521,8 @@ func TestChannelDenylist_Match(t *testing.T) {
|
|||||||
|
|
||||||
app := NewApp(ctx, client)
|
app := NewApp(ctx, client)
|
||||||
|
|
||||||
Configuration.ChannelMode = "denylist"
|
config.Configuration.ChannelMode = "denylist"
|
||||||
Configuration.ChannelDenylist = []string{"03006fcf3312dae8d068ea297f58e2bd00ec1ffe214b793eda46966b6294a53ce6"}
|
config.Configuration.ChannelDenylist = []string{"03006fcf3312dae8d068ea297f58e2bd00ec1ffe214b793eda46966b6294a53ce6"}
|
||||||
|
|
||||||
app.DispatchChannelAcceptor(ctx)
|
app.DispatchChannelAcceptor(ctx)
|
||||||
|
|
||||||
@@ -545,8 +545,8 @@ func TestChannelAllowlist_Match(t *testing.T) {
|
|||||||
|
|
||||||
app := NewApp(ctx, client)
|
app := NewApp(ctx, client)
|
||||||
|
|
||||||
Configuration.ChannelMode = "allowlist"
|
config.Configuration.ChannelMode = "allowlist"
|
||||||
Configuration.ChannelAllowlist = []string{"03006fcf3312dae8d068ea297f58e2bd00ec1ffe214b793eda46966b6294a53ce6"}
|
config.Configuration.ChannelAllowlist = []string{"03006fcf3312dae8d068ea297f58e2bd00ec1ffe214b793eda46966b6294a53ce6"}
|
||||||
|
|
||||||
app.DispatchChannelAcceptor(ctx)
|
app.DispatchChannelAcceptor(ctx)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
if (
|
if (
|
||||||
|
// only forward amounts larger than 100 sat
|
||||||
HtlcForward.Event.OutgoingAmountMsat >= 100000
|
HtlcForward.Event.OutgoingAmountMsat >= 100000
|
||||||
) { true } else { false }
|
) { true } else { false }
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,18 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/callebtc/electronwall/config"
|
||||||
"github.com/callebtc/electronwall/types"
|
"github.com/callebtc/electronwall/types"
|
||||||
"github.com/dop251/goja"
|
"github.com/dop251/goja"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Apply(s interface{}, decision_chan chan bool) (accept bool, err error) {
|
func Apply(s interface{}, decision_chan chan bool) (accept bool, err error) {
|
||||||
|
|
||||||
|
if !config.Configuration.ApiRules.Apply {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
vm := goja.New()
|
vm := goja.New()
|
||||||
|
|
||||||
var js_script []byte
|
var js_script []byte
|
||||||
@@ -41,5 +47,6 @@ func Apply(s interface{}, decision_chan chan bool) (accept bool, err error) {
|
|||||||
|
|
||||||
accept = v.Export().(bool)
|
accept = v.Export().(bool)
|
||||||
decision_chan <- accept
|
decision_chan <- accept
|
||||||
|
log.Infof("[rules] decision: %t", accept)
|
||||||
return accept, nil
|
return accept, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user