From edc4152faf7a9f30df1730da913fd0ea11b8001b Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Wed, 31 Aug 2022 14:09:17 +0200 Subject: [PATCH] works --- README.md | 190 +++++++++++++++++++++++++++++++++- api/1ml.go | 3 +- api/amboss.go | 28 +---- api/api.go | 45 +++++--- channelAcceptor.go | 27 ++--- config.yaml.example | 10 ++ config.go => config/config.go | 15 ++- helpers.go | 8 +- htlcInterceptor.go | 22 ++-- main.go | 11 +- main_test.go | 92 ++++++++-------- rules/HtlcForward.js | 1 + rules/rules.go | 7 ++ 13 files changed, 335 insertions(+), 124 deletions(-) rename config.go => config/config.go (87%) diff --git a/README.md b/README.md index 0a82b38..e779715 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ # ⚡️🛡 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. ![electronwall0 3 2](https://user-images.githubusercontent.com/93376500/178162791-e6ba90c1-2798-471d-b7aa-0b12eae8bf2e.png) @@ -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). ## Config -Edit `config.yaml.example` and rename to `config.yaml`. +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` + ```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* \ No newline at end of file diff --git a/api/1ml.go b/api/1ml.go index 895c64b..91f619f 100644 --- a/api/1ml.go +++ b/api/1ml.go @@ -7,6 +7,7 @@ import ( "net/http" "time" + "github.com/callebtc/electronwall/config" 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) 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) diff --git a/api/amboss.go b/api/amboss.go index 94e806d..2a900df 100644 --- a/api/amboss.go +++ b/api/amboss.go @@ -4,32 +4,11 @@ import ( "context" "time" + "github.com/callebtc/electronwall/config" "github.com/machinebox/graphql" 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 { Socials struct { Info struct { @@ -178,7 +157,10 @@ func (c *AmbossClient) GetNodeInfo(pubkey string) (Amboss_NodeInfoResponse, erro graphqlRequest.Header.Set("Content-Type", "application/json") 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) } diff --git a/api/api.go b/api/api.go index 007f5cd..bb0a179 100644 --- a/api/api.go +++ b/api/api.go @@ -1,6 +1,9 @@ package api -import log "github.com/sirupsen/logrus" +import ( + "github.com/callebtc/electronwall/config" + log "github.com/sirupsen/logrus" +) type ApiClient interface { GetNodeInfo(pubkey string) OneML_NodeInfoResponse @@ -12,23 +15,31 @@ type ApiNodeInfo struct { } func GetApiNodeinfo(pubkey string) (ApiNodeInfo, error) { - // get info from 1ml - OnemlClient := GetOneMlClient() - onemlNodeInfo, err := OnemlClient.GetNodeInfo(pubkey) - if err != nil { - log.Errorf(err.Error()) - onemlNodeInfo = OneML_NodeInfoResponse{} + response := ApiNodeInfo{ + OneMl: OneML_NodeInfoResponse{}, + Amboss: Amboss_NodeInfoResponse{}, } - // get info from amboss - ambossClient := GetAmbossClient() - ambossNodeInfo, err := ambossClient.GetNodeInfo(pubkey) - if err != nil { - log.Errorf(err.Error()) - ambossNodeInfo = Amboss_NodeInfoResponse{} + if config.Configuration.ApiRules.OneMl.Active { + // get info from 1ml + OnemlClient := GetOneMlClient() + onemlNodeInfo, err := OnemlClient.GetNodeInfo(pubkey) + if err != nil { + log.Errorf(err.Error()) + onemlNodeInfo = OneML_NodeInfoResponse{} + } + response.OneMl = onemlNodeInfo } - return ApiNodeInfo{ - OneMl: onemlNodeInfo, - Amboss: ambossNodeInfo, - }, err + + if config.Configuration.ApiRules.Amboss.Active { + // get info from amboss + ambossClient := GetAmbossClient() + ambossNodeInfo, err := ambossClient.GetNodeInfo(pubkey) + if err != nil { + log.Errorf(err.Error()) + ambossNodeInfo = Amboss_NodeInfoResponse{} + } + response.Amboss = ambossNodeInfo + } + return response, nil } diff --git a/channelAcceptor.go b/channelAcceptor.go index 6ac1755..1365213 100644 --- a/channelAcceptor.go +++ b/channelAcceptor.go @@ -7,6 +7,7 @@ import ( "sync" "github.com/callebtc/electronwall/api" + "github.com/callebtc/electronwall/config" "github.com/callebtc/electronwall/rules" "github.com/callebtc/electronwall/types" "github.com/lightningnetwork/lnd/lnrpc" @@ -25,7 +26,7 @@ func (app *App) GetChannelAcceptEvent(ctx context.Context, req lnrpc.ChannelAcce log.Errorf(err.Error()) } - noeInfo, err := api.GetApiNodeinfo(string(req.NodePubkey)) + noeInfo, err := api.GetApiNodeinfo(hex.EncodeToString(req.NodePubkey)) if err != nil { log.Errorf(err.Error()) } @@ -125,7 +126,7 @@ func (app *App) interceptChannelEvents(ctx context.Context) error { return err } // parse list - list_decision, err := app.channelAcceptDecision(req) + list_decision, err := app.channelAcceptListDecision(req) if err != nil { return err } @@ -137,7 +138,7 @@ func (app *App) interceptChannelEvents(ctx context.Context) error { res := lnrpc.ChannelAcceptResponse{} if accept { - if Configuration.LogJson { + if config.Configuration.LogJson { contextLogger.Infof("allow") } else { log.Infof("[channel] ✅ Allow channel %s", channel_info_string) @@ -152,13 +153,13 @@ func (app *App) interceptChannelEvents(ctx context.Context) error { } } else { - if Configuration.LogJson { + if config.Configuration.LogJson { contextLogger.Infof("deny") } else { log.Infof("[channel] ❌ Deny channel %s", channel_info_string) } res = lnrpc.ChannelAcceptResponse{Accept: false, - Error: Configuration.ChannelRejectMessage} + Error: config.Configuration.ChannelRejectMessage} } err = acceptClient.Send(&res) 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 var accept bool var listToParse []string - if Configuration.ChannelMode == "allowlist" { + if config.Configuration.ChannelMode == "allowlist" { accept = false - listToParse = Configuration.ChannelAllowlist - } else if Configuration.ChannelMode == "denylist" { + listToParse = config.Configuration.ChannelAllowlist + } else if config.Configuration.ChannelMode == "denylist" { accept = true - listToParse = Configuration.ChannelDenylist + listToParse = config.Configuration.ChannelDenylist } // parse and make decision - log.Infof("TRYING %s", string(req.NodePubkey)) for _, pubkey := range listToParse { - if string(req.NodePubkey) == pubkey || pubkey == "*" { + if hex.EncodeToString(req.NodePubkey) == pubkey || pubkey == "*" { accept = !accept break } } + log.Infof("[list] decision: %t", accept) return accept, nil } @@ -214,7 +215,7 @@ func (app *App) logChannelEvents(ctx context.Context) error { alias, ) - if Configuration.LogJson { + if config.Configuration.LogJson { contextLogger := log.WithFields(log.Fields{ "event": "channel", "capacity": event.GetOpenChannel().Capacity, diff --git a/config.yaml.example b/config.yaml.example index fe6b9c9..cd6b196 100644 --- a/config.yaml.example +++ b/config.yaml.example @@ -41,3 +41,13 @@ forward-allowlist: - "*->25328x256x0" # all forwards to this channel forward-denylist: - "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 diff --git a/config.go b/config/config.go similarity index 87% rename from config.go rename to config/config.go index a76445f..e26c51c 100644 --- a/config.go +++ b/config/config.go @@ -1,4 +1,4 @@ -package main +package config import ( "fmt" @@ -20,6 +20,17 @@ var Configuration = struct { ForwardMode string `yaml:"forward-mode"` ForwardAllowlist []string `yaml:"forward-allowlist"` 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() { @@ -31,8 +42,6 @@ func init() { } func checkConfig() { - setLogger(Configuration.Debug, Configuration.LogJson) - welcome() if Configuration.Host == "" { panic(fmt.Errorf("no host specified in config.yaml")) diff --git a/helpers.go b/helpers.go index 3689375..f774239 100644 --- a/helpers.go +++ b/helpers.go @@ -20,12 +20,12 @@ func trimPubKey(pubkey []byte) string { } } -func welcome() { - log.Info("---- ⚡️ electronwall 0.3.3 ⚡️ ----") +func Welcome() { + log.Info("---- ⚡️ electronwall 0.4 ⚡️ ----") } -// setLogger will initialize the log format -func setLogger(debug bool, json bool) { +// SetLogger will initialize the log format +func SetLogger(debug bool, json bool) { if json { customFormatter := new(log.JSONFormatter) customFormatter.TimestampFormat = "2006-01-02 15:04:05" diff --git a/htlcInterceptor.go b/htlcInterceptor.go index 6f805f3..b9915aa 100644 --- a/htlcInterceptor.go +++ b/htlcInterceptor.go @@ -8,6 +8,7 @@ import ( "strings" "sync" + "github.com/callebtc/electronwall/config" "github.com/callebtc/electronwall/rules" "github.com/callebtc/electronwall/types" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" @@ -144,14 +145,14 @@ func (app *App) interceptHtlcEvents(ctx context.Context) error { } switch accept { case true: - if Configuration.LogJson { + if config.Configuration.LogJson { contextLogger.Infof("allow") } else { log.Infof("[forward] ✅ Allow HTLC %s", forward_info_string) } response.Action = routerrpc.ResolveHoldForwardAction_RESUME case false: - if Configuration.LogJson { + if config.Configuration.LogJson { contextLogger.Infof("deny") } else { 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) // determine filtering mode and list to parse - switch Configuration.ForwardMode { + switch config.Configuration.ForwardMode { case "allowlist": accept = false - listToParse = Configuration.ForwardAllowlist + listToParse = config.Configuration.ForwardAllowlist case "denylist": accept = true - listToParse = Configuration.ForwardDenylist + listToParse = config.Configuration.ForwardDenylist 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 @@ -217,6 +218,7 @@ func (app *App) htlcInterceptDecision(ctx context.Context, event *routerrpc.Forw } } // decision_chan <- accept + log.Infof("[list] decision: %t", accept) return accept, nil } @@ -251,7 +253,7 @@ func (app *App) logHtlcEvents(ctx context.Context) error { switch event.Event.(type) { case *routerrpc.HtlcEvent_SettleEvent: - if Configuration.LogJson { + if config.Configuration.LogJson { contextLogger(event).Infof("SettleEvent") // contextLogger.Debugf("[forward] Preimage: %s", hex.EncodeToString(event.GetSettleEvent().Preimage)) } else { @@ -262,7 +264,7 @@ func (app *App) logHtlcEvents(ctx context.Context) error { } case *routerrpc.HtlcEvent_ForwardFailEvent: - if Configuration.LogJson { + if config.Configuration.LogJson { contextLogger(event).Infof("ForwardFailEvent") // contextLogger.Debugf("[forward] Reason: %s", event.GetForwardFailEvent()) } else { @@ -271,7 +273,7 @@ func (app *App) logHtlcEvents(ctx context.Context) error { } case *routerrpc.HtlcEvent_ForwardEvent: - if Configuration.LogJson { + if config.Configuration.LogJson { contextLogger(event).Infof("ForwardEvent") } else { 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()) case *routerrpc.HtlcEvent_LinkFailEvent: - if Configuration.LogJson { + if config.Configuration.LogJson { contextLogger(event).Infof("LinkFailEvent") // contextLogger(event).Debugf("[forward] Reason: %s", event.GetLinkFailEvent().FailureString) } else { diff --git a/main.go b/main.go index 2c04bf8..c00f9eb 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "sync" + "github.com/callebtc/electronwall/config" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/macaroons" @@ -39,11 +40,11 @@ func NewApp(ctx context.Context, lnd lndclient) *App { // gets the lnd grpc connection func getClientConnection(ctx context.Context) (*grpc.ClientConn, error) { - creds, err := credentials.NewClientTLSFromFile(Configuration.TLSPath, "") + creds, err := credentials.NewClientTLSFromFile(config.Configuration.TLSPath, "") if err != nil { return nil, err } - macBytes, err := ioutil.ReadFile(Configuration.MacaroonPath) + macBytes, err := ioutil.ReadFile(config.Configuration.MacaroonPath) if err != nil { return nil, err } @@ -60,8 +61,8 @@ func getClientConnection(ctx context.Context) (*grpc.ClientConn, error) { grpc.WithBlock(), grpc.WithPerRPCCredentials(cred), } - log.Infof("Connecting to %s", Configuration.Host) - conn, err := grpc.DialContext(ctx, Configuration.Host, opts...) + log.Infof("Connecting to %s", config.Configuration.Host) + conn, err := grpc.DialContext(ctx, config.Configuration.Host, opts...) if err != nil { return nil, err } @@ -84,6 +85,8 @@ func newLndClient(ctx context.Context) (*LndClient, error) { } func main() { + SetLogger(config.Configuration.Debug, config.Configuration.LogJson) + Welcome() ctx := context.Background() for { lnd, err := newLndClient(ctx) diff --git a/main_test.go b/main_test.go index 10f8438..a635f50 100644 --- a/main_test.go +++ b/main_test.go @@ -2,12 +2,12 @@ package main import ( "context" - "testing" - + "github.com/callebtc/electronwall/config" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" + "testing" ) func TestApp(t *testing.T) { @@ -33,8 +33,8 @@ func TestHTLCDenylist_BothMatch(t *testing.T) { app := NewApp(ctx, client) - Configuration.ForwardMode = "denylist" - Configuration.ForwardDenylist = []string{"700762x1327x1->690757x1005x1"} + config.Configuration.ForwardMode = "denylist" + config.Configuration.ForwardDenylist = []string{"700762x1327x1->690757x1005x1"} app.DispatchHTLCAcceptor(ctx) @@ -67,8 +67,8 @@ func TestHTLCDenylist_BothNoMatch(t *testing.T) { app := NewApp(ctx, client) - Configuration.ForwardMode = "denylist" - Configuration.ForwardDenylist = []string{"700762x1327x1->690757x1005x1"} + config.Configuration.ForwardMode = "denylist" + config.Configuration.ForwardDenylist = []string{"700762x1327x1->690757x1005x1"} app.DispatchHTLCAcceptor(ctx) @@ -101,12 +101,12 @@ func TestHTLCDenylist_WildCardOutBothMatch(t *testing.T) { app := NewApp(ctx, client) - Configuration.ForwardMode = "denylist" - Configuration.ForwardDenylist = []string{"700762x1327x1->690757x1005x1"} + config.Configuration.ForwardMode = "denylist" + config.Configuration.ForwardDenylist = []string{"700762x1327x1->690757x1005x1"} app.DispatchHTLCAcceptor(ctx) - Configuration.ForwardDenylist = []string{"700762x1327x1->*"} + config.Configuration.ForwardDenylist = []string{"700762x1327x1->*"} key := &routerrpc.CircuitKey{ ChanId: 770495967390531585, @@ -137,14 +137,14 @@ func TestHTLCDenylist_WildCardOutNoMatch(t *testing.T) { app := NewApp(ctx, client) - Configuration.ForwardMode = "denylist" - Configuration.ForwardDenylist = []string{"700762x1327x1->690757x1005x1"} + config.Configuration.ForwardMode = "denylist" + config.Configuration.ForwardDenylist = []string{"700762x1327x1->690757x1005x1"} app.DispatchHTLCAcceptor(ctx) // wildcard out, first key doesn't match: should be allowed - Configuration.ForwardDenylist = []string{"700762x1327x1->*"} + config.Configuration.ForwardDenylist = []string{"700762x1327x1->*"} key := &routerrpc.CircuitKey{ ChanId: 759495353533530113, @@ -176,11 +176,11 @@ func TestHTLCAllowlist_BothMatch(t *testing.T) { app.DispatchHTLCAcceptor(ctx) - Configuration.ForwardMode = "allowlist" + config.Configuration.ForwardMode = "allowlist" // both keys correct: should be allowed - Configuration.ForwardAllowlist = []string{"700762x1327x1->690757x1005x1"} - log.Tracef("[test] Mode: %s, Rules: %v", Configuration.ForwardMode, Configuration.ForwardAllowlist) + config.Configuration.ForwardAllowlist = []string{"700762x1327x1->690757x1005x1"} + log.Tracef("[test] Mode: %s, Rules: %v", config.Configuration.ForwardMode, config.Configuration.ForwardAllowlist) key := &routerrpc.CircuitKey{ ChanId: 770495967390531585, @@ -212,10 +212,10 @@ func TestHTLCAllowlist_BothNonMatch(t *testing.T) { app.DispatchHTLCAcceptor(ctx) - Configuration.ForwardMode = "allowlist" + config.Configuration.ForwardMode = "allowlist" // both keys wrong: should be denied - Configuration.ForwardAllowlist = []string{"700762x1327x1->690757x1005x1"} - log.Tracef("[test] Mode: %s, Rules: %v", Configuration.ForwardMode, Configuration.ForwardAllowlist) + config.Configuration.ForwardAllowlist = []string{"700762x1327x1->690757x1005x1"} + log.Tracef("[test] Mode: %s, Rules: %v", config.Configuration.ForwardMode, config.Configuration.ForwardAllowlist) key := &routerrpc.CircuitKey{ ChanId: 123456789876543210, @@ -246,10 +246,10 @@ func TestHTLCAllowlist_Wildcard(t *testing.T) { app.DispatchHTLCAcceptor(ctx) - Configuration.ForwardMode = "allowlist" + config.Configuration.ForwardMode = "allowlist" // wildcard: should be allowed - Configuration.ForwardAllowlist = []string{"*"} - log.Tracef("[test] Mode: %s, Rules: %v", Configuration.ForwardMode, Configuration.ForwardAllowlist) + config.Configuration.ForwardAllowlist = []string{"*"} + log.Tracef("[test] Mode: %s, Rules: %v", config.Configuration.ForwardMode, config.Configuration.ForwardAllowlist) key := &routerrpc.CircuitKey{ ChanId: 123456789876543210, HtlcId: 1337000, @@ -279,11 +279,11 @@ func TestHTLCAllowlist_WildcardIn(t *testing.T) { app.DispatchHTLCAcceptor(ctx) - Configuration.ForwardMode = "allowlist" + config.Configuration.ForwardMode = "allowlist" // wildcard in: should be allowed - Configuration.ForwardAllowlist = []string{"*->690757x1005x1"} - log.Tracef("[test] Mode: %s, Rules: %v", Configuration.ForwardMode, Configuration.ForwardAllowlist) + config.Configuration.ForwardAllowlist = []string{"*->690757x1005x1"} + log.Tracef("[test] Mode: %s, Rules: %v", config.Configuration.ForwardMode, config.Configuration.ForwardAllowlist) key := &routerrpc.CircuitKey{ ChanId: 123456789876543210, @@ -314,10 +314,10 @@ func TestHTLCAllowlist_WildcardOut(t *testing.T) { app.DispatchHTLCAcceptor(ctx) - Configuration.ForwardMode = "allowlist" + config.Configuration.ForwardMode = "allowlist" // wildcard out: should be allowed - Configuration.ForwardAllowlist = []string{"700762x1327x1->*"} - log.Tracef("[test] Mode: %s, Rules: %v", Configuration.ForwardMode, Configuration.ForwardAllowlist) + config.Configuration.ForwardAllowlist = []string{"700762x1327x1->*"} + log.Tracef("[test] Mode: %s, Rules: %v", config.Configuration.ForwardMode, config.Configuration.ForwardAllowlist) key := &routerrpc.CircuitKey{ ChanId: 770495967390531585, @@ -348,10 +348,10 @@ func TestHTLCAllowlist_WildcardOutWrongKeyIn(t *testing.T) { app.DispatchHTLCAcceptor(ctx) - Configuration.ForwardMode = "allowlist" + config.Configuration.ForwardMode = "allowlist" // wildcard out but wrong in key: should be denied - Configuration.ForwardAllowlist = []string{"700762x1327x1->*"} - log.Tracef("[test] Mode: %s, Rules: %v", Configuration.ForwardMode, Configuration.ForwardAllowlist) + config.Configuration.ForwardAllowlist = []string{"700762x1327x1->*"} + log.Tracef("[test] Mode: %s, Rules: %v", config.Configuration.ForwardMode, config.Configuration.ForwardAllowlist) key := &routerrpc.CircuitKey{ ChanId: 123456789876543210, @@ -382,10 +382,10 @@ func TestHTLCAllowlist_WildcardIn_WrongKeyOut(t *testing.T) { app.DispatchHTLCAcceptor(ctx) - Configuration.ForwardMode = "allowlist" + config.Configuration.ForwardMode = "allowlist" // wildcard in but wrong out key: should be denied - Configuration.ForwardAllowlist = []string{"*->700762x1327x1"} - log.Tracef("[test] Mode: %s, Rules: %v", Configuration.ForwardMode, Configuration.ForwardAllowlist) + config.Configuration.ForwardAllowlist = []string{"*->700762x1327x1"} + log.Tracef("[test] Mode: %s, Rules: %v", config.Configuration.ForwardMode, config.Configuration.ForwardAllowlist) key := &routerrpc.CircuitKey{ ChanId: 123456789876543210, @@ -416,10 +416,10 @@ func TestHTLCAllowlist_WildcardBoth(t *testing.T) { app.DispatchHTLCAcceptor(ctx) - Configuration.ForwardMode = "allowlist" + config.Configuration.ForwardMode = "allowlist" // wildcard both: should be allowed - Configuration.ForwardAllowlist = []string{"*->*"} - log.Tracef("[test] Mode: %s, Rules: %v", Configuration.ForwardMode, Configuration.ForwardAllowlist) + config.Configuration.ForwardAllowlist = []string{"*->*"} + log.Tracef("[test] Mode: %s, Rules: %v", config.Configuration.ForwardMode, config.Configuration.ForwardAllowlist) key := &routerrpc.CircuitKey{ ChanId: 123456789876543210, @@ -452,8 +452,8 @@ func TestChannelAllowlist_CorrectKey(t *testing.T) { app := NewApp(ctx, client) - Configuration.ChannelMode = "allowlist" - Configuration.ChannelAllowlist = []string{"03006fcf3312dae8d068ea297f58e2bd00ec1ffe214b793eda46966b6294a53ce6"} + config.Configuration.ChannelMode = "allowlist" + config.Configuration.ChannelAllowlist = []string{"03006fcf3312dae8d068ea297f58e2bd00ec1ffe214b793eda46966b6294a53ce6"} app.DispatchChannelAcceptor(ctx) @@ -476,8 +476,8 @@ func TestChannelAllowlist_WrongKey(t *testing.T) { app := NewApp(ctx, client) - Configuration.ChannelMode = "allowlist" - Configuration.ChannelAllowlist = []string{"03006fcf3312dae8d068ea297f58e2bd00ec1ffe214b793eda46966b6294a53ce6"} + config.Configuration.ChannelMode = "allowlist" + config.Configuration.ChannelAllowlist = []string{"03006fcf3312dae8d068ea297f58e2bd00ec1ffe214b793eda46966b6294a53ce6"} app.DispatchChannelAcceptor(ctx) // wrong key: should be denied @@ -501,8 +501,8 @@ func TestChannelAllowlist_Wildcard(t *testing.T) { app.DispatchChannelAcceptor(ctx) // wildcard: should be allowed - Configuration.ChannelMode = "allowlist" - Configuration.ChannelAllowlist = []string{"*"} + config.Configuration.ChannelMode = "allowlist" + config.Configuration.ChannelAllowlist = []string{"*"} client.channelAcceptorRequests <- &lnrpc.ChannelAcceptRequest{ NodePubkey: []byte("03006fcf3312dae8d068ea297f58e2bd00ec1ffe214b793eda46966b6294a53ce6"), @@ -521,8 +521,8 @@ func TestChannelDenylist_Match(t *testing.T) { app := NewApp(ctx, client) - Configuration.ChannelMode = "denylist" - Configuration.ChannelDenylist = []string{"03006fcf3312dae8d068ea297f58e2bd00ec1ffe214b793eda46966b6294a53ce6"} + config.Configuration.ChannelMode = "denylist" + config.Configuration.ChannelDenylist = []string{"03006fcf3312dae8d068ea297f58e2bd00ec1ffe214b793eda46966b6294a53ce6"} app.DispatchChannelAcceptor(ctx) @@ -545,8 +545,8 @@ func TestChannelAllowlist_Match(t *testing.T) { app := NewApp(ctx, client) - Configuration.ChannelMode = "allowlist" - Configuration.ChannelAllowlist = []string{"03006fcf3312dae8d068ea297f58e2bd00ec1ffe214b793eda46966b6294a53ce6"} + config.Configuration.ChannelMode = "allowlist" + config.Configuration.ChannelAllowlist = []string{"03006fcf3312dae8d068ea297f58e2bd00ec1ffe214b793eda46966b6294a53ce6"} app.DispatchChannelAcceptor(ctx) diff --git a/rules/HtlcForward.js b/rules/HtlcForward.js index 40e9a6d..fdc66bd 100644 --- a/rules/HtlcForward.js +++ b/rules/HtlcForward.js @@ -1,4 +1,5 @@ if ( + // only forward amounts larger than 100 sat HtlcForward.Event.OutgoingAmountMsat >= 100000 ) { true } else { false } diff --git a/rules/rules.go b/rules/rules.go index da4aaa9..d549f4c 100644 --- a/rules/rules.go +++ b/rules/rules.go @@ -4,12 +4,18 @@ import ( "fmt" "os" + "github.com/callebtc/electronwall/config" "github.com/callebtc/electronwall/types" "github.com/dop251/goja" log "github.com/sirupsen/logrus" ) func Apply(s interface{}, decision_chan chan bool) (accept bool, err error) { + + if !config.Configuration.ApiRules.Apply { + return true, nil + } + vm := goja.New() var js_script []byte @@ -41,5 +47,6 @@ func Apply(s interface{}, decision_chan chan bool) (accept bool, err error) { accept = v.Export().(bool) decision_chan <- accept + log.Infof("[rules] decision: %t", accept) return accept, nil }