package main
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"flag"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
|
|
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/credentials"
|
|
|
|
"github.com/tendermint/tendermint/libs/log"
|
|
tmnet "github.com/tendermint/tendermint/libs/net"
|
|
tmos "github.com/tendermint/tendermint/libs/os"
|
|
"github.com/tendermint/tendermint/privval"
|
|
grpcprivval "github.com/tendermint/tendermint/privval/grpc"
|
|
privvalproto "github.com/tendermint/tendermint/proto/tendermint/privval"
|
|
)
|
|
|
|
var (
|
|
// Create a metrics registry.
|
|
reg = prometheus.NewRegistry()
|
|
|
|
// Create some standard server metrics.
|
|
grpcMetrics = grpc_prometheus.NewServerMetrics()
|
|
)
|
|
|
|
func main() {
|
|
var (
|
|
addr = flag.String("addr", "127.0.0.1:26659", "Address to listen on (host:port)")
|
|
chainID = flag.String("chain-id", "mychain", "chain id")
|
|
privValKeyPath = flag.String("priv-key", "", "priv val key file path")
|
|
privValStatePath = flag.String("priv-state", "", "priv val state file path")
|
|
insecure = flag.Bool("insecure", false, "allow server to run insecurely (no TLS)")
|
|
certFile = flag.String("certfile", "", "absolute path to server certificate")
|
|
keyFile = flag.String("keyfile", "", "absolute path to server key")
|
|
rootCA = flag.String("rootcafile", "", "absolute path to root CA")
|
|
prometheusAddr = flag.String("prometheus-addr", "", "address for prometheus endpoint (host:port)")
|
|
|
|
logger = log.NewTMLogger(
|
|
log.NewSyncWriter(os.Stdout),
|
|
).With("module", "priv_val")
|
|
)
|
|
flag.Parse()
|
|
|
|
logger.Info(
|
|
"Starting private validator",
|
|
"addr", *addr,
|
|
"chainID", *chainID,
|
|
"privKeyPath", *privValKeyPath,
|
|
"privStatePath", *privValStatePath,
|
|
"insecure", *insecure,
|
|
"certFile", *certFile,
|
|
"keyFile", *keyFile,
|
|
"rootCA", *rootCA,
|
|
)
|
|
|
|
pv, err := privval.LoadFilePV(*privValKeyPath, *privValStatePath)
|
|
if err != nil {
|
|
fmt.Fprint(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
opts := []grpc.ServerOption{}
|
|
if !*insecure {
|
|
certificate, err := tls.LoadX509KeyPair(*certFile, *keyFile)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "failed to load X509 key pair: %v", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
certPool := x509.NewCertPool()
|
|
bs, err := ioutil.ReadFile(*rootCA)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "failed to read client ca cert: %s", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
if ok := certPool.AppendCertsFromPEM(bs); !ok {
|
|
fmt.Fprintf(os.Stderr, "failed to append client certs")
|
|
os.Exit(1)
|
|
}
|
|
|
|
tlsConfig := &tls.Config{
|
|
ClientAuth: tls.RequireAndVerifyClientCert,
|
|
Certificates: []tls.Certificate{certificate},
|
|
ClientCAs: certPool,
|
|
MinVersion: tls.VersionTLS13,
|
|
}
|
|
|
|
creds := grpc.Creds(credentials.NewTLS(tlsConfig))
|
|
opts = append(opts, creds)
|
|
logger.Info("SignerServer: Creating security credentials")
|
|
} else {
|
|
logger.Info("SignerServer: You are using an insecure gRPC connection!")
|
|
}
|
|
|
|
// add prometheus metrics for unary RPC calls
|
|
opts = append(opts, grpc.UnaryInterceptor(grpc_prometheus.UnaryServerInterceptor))
|
|
|
|
ss := grpcprivval.NewSignerServer(*chainID, pv, logger)
|
|
|
|
protocol, address := tmnet.ProtocolAndAddress(*addr)
|
|
|
|
lis, err := net.Listen(protocol, address)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "SignerServer: Failed to listen %v", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
s := grpc.NewServer(opts...)
|
|
|
|
privvalproto.RegisterPrivValidatorAPIServer(s, ss)
|
|
|
|
var httpSrv *http.Server
|
|
if *prometheusAddr != "" {
|
|
httpSrv = registerPrometheus(*prometheusAddr, s)
|
|
}
|
|
|
|
logger.Info("SignerServer: Starting grpc server")
|
|
if err := s.Serve(lis); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Unable to listen on port %s: %v", *addr, err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Stop upon receiving SIGTERM or CTRL-C.
|
|
tmos.TrapSignal(logger, func() {
|
|
logger.Debug("SignerServer: calling Close")
|
|
if *prometheusAddr != "" {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
|
defer cancel()
|
|
if err := httpSrv.Shutdown(ctx); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Unable to stop http server: %v", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
s.GracefulStop()
|
|
})
|
|
|
|
// Run forever.
|
|
select {}
|
|
}
|
|
|
|
func registerPrometheus(addr string, s *grpc.Server) *http.Server {
|
|
// Initialize all metrics.
|
|
grpcMetrics.InitializeMetrics(s)
|
|
// create http server to serve prometheus
|
|
httpServer := &http.Server{Handler: promhttp.HandlerFor(reg, promhttp.HandlerOpts{}), Addr: addr}
|
|
|
|
go func() {
|
|
if err := httpServer.ListenAndServe(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Unable to start a http server: %v", err)
|
|
os.Exit(1)
|
|
}
|
|
}()
|
|
|
|
return httpServer
|
|
}
|