d2193e980a
* Generate secondary hashXNotification(s) on every headerNotification. * Attempt LBCD connection with rpc.Client. * Optional --daemon-url. * Correct HashXStatusKey field. Should be HASHX_LEN. * Connect to lbcd using lbcd/rpcclient library. * Handle deprecation of node.js 12 actions. * Add --daemon-ca-path argument and establish HTTPS connection if specified. * Remove dead code. Tighten definition of TransactionBroadcastReq. * Correct default daemon URL.
415 lines
17 KiB
Go
415 lines
17 KiB
Go
package server
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"net/url"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/akamensky/argparse"
|
|
pb "github.com/lbryio/herald.go/protobuf/go"
|
|
"github.com/lbryio/lbcd/chaincfg"
|
|
)
|
|
|
|
const (
|
|
ServeCmd = iota
|
|
SearchCmd = iota
|
|
DBCmd = iota
|
|
)
|
|
|
|
// Args struct contains the arguments to the hub server.
|
|
type Args struct {
|
|
CmdType int
|
|
Host string
|
|
Port int
|
|
DBPath string
|
|
Chain *string
|
|
DaemonURL *url.URL
|
|
DaemonCAPath string
|
|
EsHost string
|
|
EsPort int
|
|
PrometheusPort int
|
|
NotifierPort int
|
|
JSONRPCPort int
|
|
JSONRPCHTTPPort int
|
|
MaxSessions int
|
|
SessionTimeout int
|
|
EsIndex string
|
|
RefreshDelta int
|
|
CacheTTL int
|
|
PeerFile string
|
|
Banner *string
|
|
Country string
|
|
BlockingChannelIds []string
|
|
FilteringChannelIds []string
|
|
|
|
GenesisHash string
|
|
ServerVersion string
|
|
ProtocolMin string
|
|
ProtocolMax string
|
|
ServerDescription string
|
|
PaymentAddress string
|
|
DonationAddress string
|
|
DailyFee string
|
|
|
|
Debug bool
|
|
DisableEs bool
|
|
DisableLoadPeers bool
|
|
DisableStartPrometheus bool
|
|
DisableStartUDP bool
|
|
DisableWritePeers bool
|
|
DisableFederation bool
|
|
DisableRocksDBRefresh bool
|
|
DisableResolve bool
|
|
DisableBlockingAndFiltering bool
|
|
DisableStartNotifier bool
|
|
DisableStartJSONRPC bool
|
|
}
|
|
|
|
const (
|
|
DefaultHost = "0.0.0.0"
|
|
DefaultPort = 50051
|
|
DefaultDBPath = "/mnt/d/data/snapshot_1072108/lbry-rocksdb/" // FIXME
|
|
DefaultEsHost = "http://localhost"
|
|
DefaultEsIndex = "claims"
|
|
DefaultEsPort = 9200
|
|
DefaultPrometheusPort = 2112
|
|
DefaultNotifierPort = 18080
|
|
DefaultJSONRPCPort = 50001
|
|
DefaultJSONRPCHTTPPort = 50002
|
|
DefaultMaxSessions = 10000
|
|
DefaultSessionTimeout = 300
|
|
DefaultRefreshDelta = 5
|
|
DefaultCacheTTL = 5
|
|
DefaultPeerFile = "peers.txt"
|
|
DefaultBannerFile = ""
|
|
DefaultCountry = "US"
|
|
|
|
HUB_PROTOCOL_VERSION = "0.107.0"
|
|
PROTOCOL_MIN = "0.54.0"
|
|
PROTOCOL_MAX = "0.199.0"
|
|
DefaultServerDescription = "Herald"
|
|
DefaultPaymentAddress = ""
|
|
DefaultDonationAddress = ""
|
|
DefaultDailyFee = "1.0"
|
|
|
|
DefaultDisableLoadPeers = false
|
|
DefaultDisableStartPrometheus = false
|
|
DefaultDisableStartUDP = false
|
|
DefaultDisableWritePeers = false
|
|
DefaultDisableFederation = false
|
|
DefaultDisableRockDBRefresh = false
|
|
DefaultDisableResolve = false
|
|
DefaultDisableBlockingAndFiltering = false
|
|
DisableStartNotifier = false
|
|
DisableStartJSONRPC = false
|
|
)
|
|
|
|
var (
|
|
DefaultBlockingChannelIds = []string{}
|
|
DefaultFilteringChannelIds = []string{}
|
|
)
|
|
|
|
func loadBanner(bannerFile *string, serverVersion string) *string {
|
|
var banner string
|
|
|
|
data, err := os.ReadFile(*bannerFile)
|
|
if err != nil {
|
|
banner = fmt.Sprintf("You are connected to an %s server.", serverVersion)
|
|
} else {
|
|
banner = string(data)
|
|
}
|
|
|
|
/*
|
|
banner := os.Getenv("BANNER")
|
|
if banner == "" {
|
|
return nil
|
|
}
|
|
*/
|
|
|
|
return &banner
|
|
}
|
|
|
|
// MakeDefaultArgs creates a default set of arguments for testing the server.
|
|
func MakeDefaultTestArgs() *Args {
|
|
args := &Args{
|
|
CmdType: ServeCmd,
|
|
Host: DefaultHost,
|
|
Port: DefaultPort,
|
|
DBPath: DefaultDBPath,
|
|
EsHost: DefaultEsHost,
|
|
EsPort: DefaultEsPort,
|
|
PrometheusPort: DefaultPrometheusPort,
|
|
NotifierPort: DefaultNotifierPort,
|
|
JSONRPCPort: DefaultJSONRPCPort,
|
|
JSONRPCHTTPPort: DefaultJSONRPCHTTPPort,
|
|
MaxSessions: DefaultMaxSessions,
|
|
SessionTimeout: DefaultSessionTimeout,
|
|
EsIndex: DefaultEsIndex,
|
|
RefreshDelta: DefaultRefreshDelta,
|
|
CacheTTL: DefaultCacheTTL,
|
|
PeerFile: DefaultPeerFile,
|
|
Banner: nil,
|
|
Country: DefaultCountry,
|
|
|
|
GenesisHash: chaincfg.TestNet3Params.GenesisHash.String(),
|
|
ServerVersion: HUB_PROTOCOL_VERSION,
|
|
ProtocolMin: PROTOCOL_MIN,
|
|
ProtocolMax: PROTOCOL_MAX,
|
|
ServerDescription: DefaultServerDescription,
|
|
PaymentAddress: DefaultPaymentAddress,
|
|
DonationAddress: DefaultDonationAddress,
|
|
DailyFee: DefaultDailyFee,
|
|
|
|
DisableEs: true,
|
|
Debug: true,
|
|
DisableLoadPeers: true,
|
|
DisableStartPrometheus: true,
|
|
DisableStartUDP: true,
|
|
DisableWritePeers: true,
|
|
DisableRocksDBRefresh: true,
|
|
DisableResolve: true,
|
|
DisableBlockingAndFiltering: true,
|
|
DisableStartNotifier: true,
|
|
DisableStartJSONRPC: true,
|
|
}
|
|
|
|
return args
|
|
}
|
|
|
|
// GetEnvironment takes the environment variables as an array of strings
|
|
// and a getkeyval function to turn it into a map.
|
|
func GetEnvironment(data []string, getkeyval func(item string) (key, val string)) map[string]string {
|
|
items := make(map[string]string)
|
|
for _, item := range data {
|
|
key, val := getkeyval(item)
|
|
items[key] = val
|
|
}
|
|
return items
|
|
}
|
|
|
|
// GetEnvironmentStandard gets the environment variables as a map.
|
|
func GetEnvironmentStandard() map[string]string {
|
|
return GetEnvironment(os.Environ(), func(item string) (key, val string) {
|
|
splits := strings.Split(item, "=")
|
|
key = splits[0]
|
|
val = splits[1]
|
|
return
|
|
})
|
|
}
|
|
|
|
// ParseArgs parses the command line arguments when started the hub server.
|
|
func ParseArgs(searchRequest *pb.SearchRequest) *Args {
|
|
|
|
environment := GetEnvironmentStandard()
|
|
parser := argparse.NewParser("herald", "herald server and client")
|
|
|
|
serveCmd := parser.NewCommand("serve", "start the herald server")
|
|
searchCmd := parser.NewCommand("search", "claim search")
|
|
dbCmd := parser.NewCommand("db", "db testing")
|
|
|
|
defaultDaemonURL := "http://localhost:9245"
|
|
if url, ok := environment["DAEMON_URL"]; ok {
|
|
defaultDaemonURL = url
|
|
}
|
|
|
|
validateURL := func(arg []string) error {
|
|
_, err := url.Parse(arg[0])
|
|
return err
|
|
}
|
|
validatePort := func(arg []string) error {
|
|
_, err := strconv.ParseUint(arg[0], 10, 16)
|
|
return err
|
|
}
|
|
|
|
// main server config arguments
|
|
host := parser.String("", "rpchost", &argparse.Options{Required: false, Help: "RPC host", Default: DefaultHost})
|
|
port := parser.Int("", "rpcport", &argparse.Options{Required: false, Help: "RPC port", Validate: validatePort, Default: DefaultPort})
|
|
dbPath := parser.String("", "db-path", &argparse.Options{Required: false, Help: "RocksDB path", Default: DefaultDBPath})
|
|
chain := parser.Selector("", "chain", []string{chaincfg.MainNetParams.Name, chaincfg.TestNet3Params.Name, chaincfg.RegressionNetParams.Name, "testnet"},
|
|
&argparse.Options{Required: false, Help: "Which chain to use, default is 'mainnet'. Values 'regtest' and 'testnet' are for testing", Default: chaincfg.MainNetParams.Name})
|
|
daemonURLStr := parser.String("", "daemon-url", &argparse.Options{Required: false, Help: "URL for rpc to lbrycrd or lbcd, <rpcuser>:<rpcpassword>@<lbcd rpc ip><lbrcd rpc port>.", Validate: validateURL, Default: defaultDaemonURL})
|
|
daemonCAPath := parser.String("", "daemon-ca-path", &argparse.Options{Required: false, Help: "Path to the lbcd CA file. Use SSL certificate to verify connection to lbcd."})
|
|
esHost := parser.String("", "eshost", &argparse.Options{Required: false, Help: "elasticsearch host", Default: DefaultEsHost})
|
|
esPort := parser.Int("", "esport", &argparse.Options{Required: false, Help: "elasticsearch port", Default: DefaultEsPort})
|
|
prometheusPort := parser.Int("", "prometheus-port", &argparse.Options{Required: false, Help: "prometheus port", Default: DefaultPrometheusPort})
|
|
notifierPort := parser.Int("", "notifier-port", &argparse.Options{Required: false, Help: "notifier port", Default: DefaultNotifierPort})
|
|
jsonRPCPort := parser.Int("", "json-rpc-port", &argparse.Options{Required: false, Help: "JSON RPC port", Validate: validatePort, Default: DefaultJSONRPCPort})
|
|
jsonRPCHTTPPort := parser.Int("", "json-rpc-http-port", &argparse.Options{Required: false, Help: "JSON RPC over HTTP port", Validate: validatePort, Default: DefaultJSONRPCHTTPPort})
|
|
maxSessions := parser.Int("", "max-sessions", &argparse.Options{Required: false, Help: "Maximum number of electrum clients that can be connected", Default: DefaultMaxSessions})
|
|
sessionTimeout := parser.Int("", "session-timeout", &argparse.Options{Required: false, Help: "Session inactivity timeout (seconds)", Default: DefaultSessionTimeout})
|
|
esIndex := parser.String("", "esindex", &argparse.Options{Required: false, Help: "elasticsearch index name", Default: DefaultEsIndex})
|
|
refreshDelta := parser.Int("", "refresh-delta", &argparse.Options{Required: false, Help: "elasticsearch index refresh delta in seconds", Default: DefaultRefreshDelta})
|
|
cacheTTL := parser.Int("", "cachettl", &argparse.Options{Required: false, Help: "Cache TTL in minutes", Default: DefaultCacheTTL})
|
|
peerFile := parser.String("", "peerfile", &argparse.Options{Required: false, Help: "Initial peer file for federation", Default: DefaultPeerFile})
|
|
bannerFile := parser.String("", "bannerfile", &argparse.Options{Required: false, Help: "Banner file server.banner", Default: DefaultBannerFile})
|
|
country := parser.String("", "country", &argparse.Options{Required: false, Help: "Country this node is running in. Default US.", Default: DefaultCountry})
|
|
blockingChannelIds := parser.StringList("", "blocking-channel-ids", &argparse.Options{Required: false, Help: "Blocking channel ids", Default: DefaultBlockingChannelIds})
|
|
filteringChannelIds := parser.StringList("", "filtering-channel-ids", &argparse.Options{Required: false, Help: "Filtering channel ids", Default: DefaultFilteringChannelIds})
|
|
|
|
// arguments for server features
|
|
serverDescription := parser.String("", "server-description", &argparse.Options{Required: false, Help: "Server description", Default: DefaultServerDescription})
|
|
paymentAddress := parser.String("", "payment-address", &argparse.Options{Required: false, Help: "Payment address", Default: DefaultPaymentAddress})
|
|
donationAddress := parser.String("", "donation-address", &argparse.Options{Required: false, Help: "Donation address", Default: DefaultDonationAddress})
|
|
dailyFee := parser.String("", "daily-fee", &argparse.Options{Required: false, Help: "Daily fee", Default: DefaultDailyFee})
|
|
|
|
// flags for disabling features
|
|
debug := parser.Flag("", "debug", &argparse.Options{Required: false, Help: "enable debug logging", Default: false})
|
|
disableEs := parser.Flag("", "disable-es", &argparse.Options{Required: false, Help: "Disable elastic search, for running/testing independently", Default: false})
|
|
disableLoadPeers := parser.Flag("", "disable-load-peers", &argparse.Options{Required: false, Help: "Disable load peers from disk at startup", Default: DefaultDisableLoadPeers})
|
|
disableStartPrometheus := parser.Flag("", "disable-start-prometheus", &argparse.Options{Required: false, Help: "Disable start prometheus server", Default: DefaultDisableStartPrometheus})
|
|
disableStartUdp := parser.Flag("", "disable-start-udp", &argparse.Options{Required: false, Help: "Disable start UDP ping server", Default: DefaultDisableStartUDP})
|
|
disableWritePeers := parser.Flag("", "disable-write-peers", &argparse.Options{Required: false, Help: "Disable write peer to disk as we learn about them", Default: DefaultDisableWritePeers})
|
|
disableFederation := parser.Flag("", "disable-federation", &argparse.Options{Required: false, Help: "Disable server federation", Default: DefaultDisableFederation})
|
|
disableRocksDBRefresh := parser.Flag("", "disable-rocksdb-refresh", &argparse.Options{Required: false, Help: "Disable rocksdb refreshing", Default: DefaultDisableRockDBRefresh})
|
|
disableResolve := parser.Flag("", "disable-resolve", &argparse.Options{Required: false, Help: "Disable resolve endpoint (and rocksdb loading)", Default: DefaultDisableRockDBRefresh})
|
|
disableBlockingAndFiltering := parser.Flag("", "disable-blocking-and-filtering", &argparse.Options{Required: false, Help: "Disable blocking and filtering of channels and streams", Default: DefaultDisableBlockingAndFiltering})
|
|
disableStartNotifier := parser.Flag("", "disable-start-notifier", &argparse.Options{Required: false, Help: "Disable start notifier", Default: DisableStartNotifier})
|
|
disableStartJSONRPC := parser.Flag("", "disable-start-jsonrpc", &argparse.Options{Required: false, Help: "Disable start jsonrpc endpoint", Default: DisableStartJSONRPC})
|
|
|
|
// search command arguments
|
|
text := parser.String("", "text", &argparse.Options{Required: false, Help: "text query"})
|
|
name := parser.String("", "name", &argparse.Options{Required: false, Help: "name"})
|
|
claimType := parser.String("", "claim_type", &argparse.Options{Required: false, Help: "claim_type"})
|
|
id := parser.String("", "id", &argparse.Options{Required: false, Help: "id"})
|
|
author := parser.String("", "author", &argparse.Options{Required: false, Help: "author"})
|
|
title := parser.String("", "title", &argparse.Options{Required: false, Help: "title"})
|
|
description := parser.String("", "description", &argparse.Options{Required: false, Help: "description"})
|
|
channelId := parser.String("", "channel_id", &argparse.Options{Required: false, Help: "channel id"})
|
|
channelIds := parser.StringList("", "channel_ids", &argparse.Options{Required: false, Help: "channel ids"})
|
|
|
|
// Now parse the arguments
|
|
err := parser.Parse(os.Args)
|
|
if err != nil {
|
|
log.Fatalln(parser.Usage(err))
|
|
}
|
|
|
|
// Use default JSON RPC port only if *neither* JSON RPC arg is specified.
|
|
if *jsonRPCPort == 0 && *jsonRPCHTTPPort == 0 {
|
|
*jsonRPCPort = DefaultJSONRPCPort
|
|
}
|
|
|
|
daemonURL, err := url.Parse(*daemonURLStr)
|
|
if err != nil {
|
|
log.Fatalf("URL parse failed: %v", err)
|
|
}
|
|
|
|
banner := loadBanner(bannerFile, HUB_PROTOCOL_VERSION)
|
|
|
|
args := &Args{
|
|
CmdType: SearchCmd,
|
|
Host: *host,
|
|
Port: *port,
|
|
DBPath: *dbPath,
|
|
Chain: chain,
|
|
DaemonURL: daemonURL,
|
|
DaemonCAPath: *daemonCAPath,
|
|
EsHost: *esHost,
|
|
EsPort: *esPort,
|
|
PrometheusPort: *prometheusPort,
|
|
NotifierPort: *notifierPort,
|
|
JSONRPCPort: *jsonRPCPort,
|
|
JSONRPCHTTPPort: *jsonRPCHTTPPort,
|
|
MaxSessions: *maxSessions,
|
|
SessionTimeout: *sessionTimeout,
|
|
EsIndex: *esIndex,
|
|
RefreshDelta: *refreshDelta,
|
|
CacheTTL: *cacheTTL,
|
|
PeerFile: *peerFile,
|
|
Banner: banner,
|
|
Country: *country,
|
|
BlockingChannelIds: *blockingChannelIds,
|
|
FilteringChannelIds: *filteringChannelIds,
|
|
|
|
GenesisHash: "",
|
|
ServerVersion: HUB_PROTOCOL_VERSION,
|
|
ProtocolMin: PROTOCOL_MIN,
|
|
ProtocolMax: PROTOCOL_MAX,
|
|
ServerDescription: *serverDescription,
|
|
PaymentAddress: *paymentAddress,
|
|
DonationAddress: *donationAddress,
|
|
DailyFee: *dailyFee,
|
|
|
|
Debug: *debug,
|
|
DisableEs: *disableEs,
|
|
DisableLoadPeers: *disableLoadPeers,
|
|
DisableStartPrometheus: *disableStartPrometheus,
|
|
DisableStartUDP: *disableStartUdp,
|
|
DisableWritePeers: *disableWritePeers,
|
|
DisableFederation: *disableFederation,
|
|
DisableRocksDBRefresh: *disableRocksDBRefresh,
|
|
DisableResolve: *disableResolve,
|
|
DisableBlockingAndFiltering: *disableBlockingAndFiltering,
|
|
DisableStartNotifier: *disableStartNotifier,
|
|
DisableStartJSONRPC: *disableStartJSONRPC,
|
|
}
|
|
|
|
if esHost, ok := environment["ELASTIC_HOST"]; ok {
|
|
args.EsHost = esHost
|
|
}
|
|
|
|
if !strings.HasPrefix(args.EsHost, "http") {
|
|
args.EsHost = "http://" + args.EsHost
|
|
}
|
|
|
|
if esPort, ok := environment["ELASTIC_PORT"]; ok {
|
|
args.EsPort, err = strconv.Atoi(esPort)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
if prometheusPort, ok := environment["GOHUB_PROMETHEUS_PORT"]; ok {
|
|
args.PrometheusPort, err = strconv.Atoi(prometheusPort)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
/*
|
|
Verify no invalid argument combinations
|
|
*/
|
|
if len(*channelIds) > 0 && *channelId != "" {
|
|
log.Fatal("Cannot specify both channel_id and channel_ids")
|
|
}
|
|
|
|
if serveCmd.Happened() {
|
|
args.CmdType = ServeCmd
|
|
} else if searchCmd.Happened() {
|
|
args.CmdType = SearchCmd
|
|
} else if dbCmd.Happened() {
|
|
args.CmdType = DBCmd
|
|
}
|
|
|
|
if *text != "" {
|
|
searchRequest.Text = *text
|
|
}
|
|
if *name != "" {
|
|
searchRequest.ClaimName = *name
|
|
}
|
|
if *claimType != "" {
|
|
searchRequest.ClaimType = []string{*claimType}
|
|
}
|
|
if *id != "" {
|
|
searchRequest.ClaimId = &pb.InvertibleField{Invert: false, Value: []string{*id}}
|
|
}
|
|
if *author != "" {
|
|
searchRequest.Author = *author
|
|
}
|
|
if *title != "" {
|
|
searchRequest.Title = *title
|
|
}
|
|
if *description != "" {
|
|
searchRequest.Description = *description
|
|
}
|
|
if *channelId != "" {
|
|
searchRequest.ChannelId = &pb.InvertibleField{Invert: false, Value: []string{*channelId}}
|
|
}
|
|
if len(*channelIds) > 0 {
|
|
searchRequest.ChannelId = &pb.InvertibleField{Invert: false, Value: *channelIds}
|
|
}
|
|
|
|
return args
|
|
}
|