diff --git a/Makefile b/Makefile
index 8a9fbaa0a2e84666ce7155691ee41ae7423b0137..b9bc9db602e795019d00ec08467cca8942206086 100644
--- a/Makefile
+++ b/Makefile
@@ -13,6 +13,9 @@ GO ?= latest
 GORUN = env GO111MODULE=on go run
 GOPATH = $(shell go env GOPATH)
 
+protoc:
+	protoc --go_out=. --go-grpc_out=. ./command/server/proto/*.proto
+
 bor:
 	$(GORUN) build/ci.go install ./cmd/geth
 	mkdir -p $(GOPATH)/bin/
diff --git a/command/debug.go b/command/debug.go
new file mode 100644
index 0000000000000000000000000000000000000000..c62f862a99013396d2370cf9c454cabc30d2c6aa
--- /dev/null
+++ b/command/debug.go
@@ -0,0 +1,44 @@
+package main
+
+import (
+	"fmt"
+
+	"github.com/ethereum/go-ethereum/command/flagset"
+)
+
+type DebugCommand struct {
+	*Meta2
+}
+
+// Help implements the cli.Command interface
+func (d *DebugCommand) Help() string {
+	return `Usage: bor debug
+
+  Debug`
+}
+
+func (d *DebugCommand) Flags() *flagset.Flagset {
+	return d.NewFlagSet("debug")
+}
+
+// Synopsis implements the cli.Command interface
+func (d *DebugCommand) Synopsis() string {
+	return "Debug"
+}
+
+// Run implements the cli.Command interface
+func (d *DebugCommand) Run(args []string) int {
+	flags := d.Flags()
+	if err := flags.Parse(args); err != nil {
+		d.UI.Error(err.Error())
+		return 1
+	}
+
+	clt, err := d.BorConn()
+	if err != nil {
+		d.UI.Error(err.Error())
+		return 1
+	}
+	fmt.Println(clt)
+	return 0
+}
diff --git a/command/main.go b/command/main.go
index 67b5a004af3223452b0812e0aeb3bc37cb0b9d11..702fce9942bc25a3dd23fda543ff10a8429d9513 100644
--- a/command/main.go
+++ b/command/main.go
@@ -7,9 +7,11 @@ import (
 	"github.com/ethereum/go-ethereum/accounts/keystore"
 	"github.com/ethereum/go-ethereum/command/flagset"
 	"github.com/ethereum/go-ethereum/command/server"
+	"github.com/ethereum/go-ethereum/command/server/proto"
 	"github.com/ethereum/go-ethereum/node"
 	"github.com/mitchellh/cli"
 	"github.com/ryanuber/columnize"
+	"google.golang.org/grpc"
 )
 
 func main() {
@@ -40,6 +42,9 @@ func commands() map[string]cli.CommandFactory {
 		ErrorWriter: os.Stderr,
 	}
 
+	meta2 := &Meta2{
+		UI: ui,
+	}
 	meta := &Meta{
 		UI: ui,
 	}
@@ -54,6 +59,11 @@ func commands() map[string]cli.CommandFactory {
 				UI: ui,
 			}, nil
 		},
+		"debug": func() (cli.Command, error) {
+			return &DebugCommand{
+				Meta2: meta2,
+			}, nil
+		},
 		"account": func() (cli.Command, error) {
 			return &Account{
 				UI: ui,
@@ -77,6 +87,42 @@ func commands() map[string]cli.CommandFactory {
 	}
 }
 
+type Meta2 struct {
+	UI cli.Ui
+
+	addr string
+}
+
+func (m *Meta2) NewFlagSet(n string) *flagset.Flagset {
+	f := flagset.NewFlagSet(n)
+
+	f.StringFlag(&flagset.StringFlag{
+		Name:  "address",
+		Value: &m.addr,
+		Usage: "Address of the grpc endpoint",
+	})
+	return f
+}
+
+func (m *Meta2) Conn() (*grpc.ClientConn, error) {
+	if m.addr == "" {
+		m.addr = "http://localhost:3131"
+	}
+	conn, err := grpc.Dial(m.addr, grpc.WithInsecure())
+	if err != nil {
+		return nil, fmt.Errorf("failed to connect to server: %v", err)
+	}
+	return conn, nil
+}
+
+func (m *Meta2) BorConn() (proto.BorClient, error) {
+	conn, err := m.Conn()
+	if err != nil {
+		return nil, err
+	}
+	return proto.NewBorClient(conn), nil
+}
+
 // Meta is a helper utility for the commands
 type Meta struct {
 	UI cli.Ui
diff --git a/command/server/command.go b/command/server/command.go
index 361be1ba097b96f6d7d85cf6e64445abdc65ab51..4b2374dc427480ba2e2b8efaa9319c41ce53c109 100644
--- a/command/server/command.go
+++ b/command/server/command.go
@@ -2,27 +2,11 @@ package server
 
 import (
 	"fmt"
-	"io"
-	"io/ioutil"
 	"os"
 	"os/signal"
-	"strings"
 	"syscall"
-	"time"
 
-	"github.com/ethereum/go-ethereum/accounts"
-	"github.com/ethereum/go-ethereum/accounts/keystore"
-	"github.com/ethereum/go-ethereum/common"
-	"github.com/ethereum/go-ethereum/eth"
-	"github.com/ethereum/go-ethereum/eth/tracers"
-	"github.com/ethereum/go-ethereum/ethstats"
-	"github.com/ethereum/go-ethereum/graphql"
 	"github.com/ethereum/go-ethereum/log"
-	"github.com/ethereum/go-ethereum/metrics"
-	"github.com/ethereum/go-ethereum/metrics/influxdb"
-	"github.com/ethereum/go-ethereum/node"
-	"github.com/mattn/go-colorable"
-	"github.com/mattn/go-isatty"
 	"github.com/mitchellh/cli"
 )
 
@@ -38,8 +22,7 @@ type Command struct {
 
 	configFile []string
 
-	// bor node
-	node *node.Node
+	srv *Server
 }
 
 // Help implements the cli.Command interface
@@ -82,182 +65,16 @@ func (c *Command) Run(args []string) int {
 	}
 	c.config = config
 
-	// start the logger
-	setupLogger(config.LogLevel)
-
-	// load the chain genesis
-	if err := config.loadChain(); err != nil {
-		c.UI.Error(err.Error())
-		return 1
-	}
-
-	// create the node/stack
-	nodeCfg, err := config.buildNode()
-	if err != nil {
-		c.UI.Error(err.Error())
-		return 1
-	}
-	stack, err := node.New(nodeCfg)
-	if err != nil {
-		c.UI.Error(err.Error())
-		return 1
-	}
-	c.node = stack
-
-	// register the ethereum backend
-	ethCfg, err := config.buildEth()
+	srv, err := NewServer(config)
 	if err != nil {
 		c.UI.Error(err.Error())
 		return 1
 	}
-	backend, err := eth.New(stack, ethCfg)
-	if err != nil {
-		c.UI.Error(err.Error())
-		return 1
-	}
-
-	log.Info("Heimdall setup", "url", ethCfg.HeimdallURL)
-
-	// debug tracing is enabled by default
-	stack.RegisterAPIs(tracers.APIs(backend.APIBackend))
-
-	// graphql is started from another place
-	if config.JsonRPC.Graphql.Enabled {
-		if err := graphql.New(stack, backend.APIBackend, config.JsonRPC.Cors, config.JsonRPC.Modules); err != nil {
-			c.UI.Error(fmt.Sprintf("Failed to register the GraphQL service: %v", err))
-			return 1
-		}
-	}
-
-	// register ethash service
-	if config.Ethstats != "" {
-		if err := ethstats.New(stack, backend.APIBackend, backend.Engine(), config.Ethstats); err != nil {
-			c.UI.Error(err.Error())
-			return 1
-		}
-	}
-
-	// setup account manager (only keystore)
-	var borKeystore *keystore.KeyStore
-	{
-		keydir := stack.KeyStoreDir()
-		n, p := keystore.StandardScryptN, keystore.StandardScryptP
-		if config.Accounts.UseLightweightKDF {
-			n, p = keystore.LightScryptN, keystore.LightScryptP
-		}
-		borKeystore = keystore.NewKeyStore(keydir, n, p)
-		stack.AccountManager().AddBackend(borKeystore)
-	}
-
-	// unlock accounts if necessary
-	if len(config.Accounts.Unlock) != 0 {
-		if err := c.unlockAccounts(borKeystore); err != nil {
-			c.UI.Error(fmt.Sprintf("failed to unlock: %v", err))
-			return 1
-		}
-	}
+	c.srv = srv
 
-	// sealing (if enabled)
-	if config.Sealer.Enabled {
-		if err := backend.StartMining(1); err != nil {
-			c.UI.Error(err.Error())
-			return 1
-		}
-	}
-
-	if err := c.setupTelemetry(config.Telemetry); err != nil {
-		c.UI.Error(err.Error())
-		return 1
-	}
-
-	// start the node
-	if err := c.node.Start(); err != nil {
-		c.UI.Error(err.Error())
-		return 1
-	}
 	return c.handleSignals()
 }
 
-func (c *Command) unlockAccounts(borKeystore *keystore.KeyStore) error {
-	// If insecure account unlocking is not allowed if node's APIs are exposed to external.
-	if !c.node.Config().InsecureUnlockAllowed && c.node.Config().ExtRPCEnabled() {
-		return fmt.Errorf("account unlock with HTTP access is forbidden")
-	}
-
-	// read passwords from file if possible
-	passwords := []string{}
-	if c.config.Accounts.PasswordFile != "" {
-		var err error
-		if passwords, err = readMultilineFile(c.config.Accounts.PasswordFile); err != nil {
-			return err
-		}
-	}
-	decodePassword := func(addr common.Address, index int) (string, error) {
-		if len(passwords) > 0 {
-			if index < len(passwords) {
-				return passwords[index], nil
-			}
-			return passwords[len(passwords)-1], nil
-		}
-		// ask for the password
-		return c.UI.AskSecret(fmt.Sprintf("Please give a password to unlock '%s'", addr.String()))
-	}
-
-	for index, addrStr := range c.config.Accounts.Unlock {
-		if !common.IsHexAddress(addrStr) {
-			return fmt.Errorf("unlock value '%s' is not an address", addrStr)
-		}
-		acct := accounts.Account{Address: common.HexToAddress(addrStr)}
-
-		password, err := decodePassword(acct.Address, index)
-		if err != nil {
-			return err
-		}
-		if err := borKeystore.Unlock(acct, password); err != nil {
-			return err
-		}
-		log.Info("Unlocked account", "address", acct.Address.Hex())
-	}
-	return nil
-}
-
-func (c *Command) setupTelemetry(config *TelemetryConfig) error {
-	metrics.Enabled = config.Enabled
-	metrics.EnabledExpensive = config.Expensive
-
-	if !metrics.Enabled {
-		// metrics are disabled, do not set up any sink
-		return nil
-	}
-
-	log.Info("Enabling metrics collection")
-
-	// influxdb
-	if v1Enabled, v2Enabled := (config.InfluxDB.V1Enabled), (config.InfluxDB.V2Enabled); v1Enabled || v2Enabled {
-		if v1Enabled && v2Enabled {
-			return fmt.Errorf("both influx v1 and influx v2 cannot be enabled")
-		}
-
-		cfg := config.InfluxDB
-		tags := cfg.Tags
-		endpoint := cfg.Endpoint
-
-		if v1Enabled {
-			log.Info("Enabling metrics export to InfluxDB (v1)")
-			go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, cfg.Database, cfg.Username, cfg.Password, "geth.", tags)
-		}
-		if v2Enabled {
-			log.Info("Enabling metrics export to InfluxDB (v2)")
-			go influxdb.InfluxDBV2WithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, cfg.Token, cfg.Bucket, cfg.Organization, "geth.", tags)
-		}
-	}
-
-	// Start system runtime metrics collection
-	go metrics.CollectProcessMetrics(3 * time.Second)
-
-	return nil
-}
-
 func (c *Command) handleSignals() int {
 	signalCh := make(chan os.Signal, 4)
 	signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP)
@@ -269,8 +86,7 @@ func (c *Command) handleSignals() int {
 
 	gracefulCh := make(chan struct{})
 	go func() {
-		c.node.Close()
-		c.node.Wait()
+		c.srv.Stop()
 		close(gracefulCh)
 	}()
 
@@ -284,35 +100,3 @@ func (c *Command) handleSignals() int {
 	}
 	return 1
 }
-
-func setupLogger(logLevel string) {
-	output := io.Writer(os.Stderr)
-	usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
-	if usecolor {
-		output = colorable.NewColorableStderr()
-	}
-	ostream := log.StreamHandler(output, log.TerminalFormat(usecolor))
-	glogger := log.NewGlogHandler(ostream)
-
-	// logging
-	lvl, err := log.LvlFromString(strings.ToLower(logLevel))
-	if err == nil {
-		glogger.Verbosity(lvl)
-	} else {
-		glogger.Verbosity(log.LvlInfo)
-	}
-	log.Root().SetHandler(glogger)
-}
-
-func readMultilineFile(path string) ([]string, error) {
-	text, err := ioutil.ReadFile(path)
-	if err != nil {
-		return nil, err
-	}
-	lines := strings.Split(string(text), "\n")
-	// Sanitise DOS line endings.
-	for i := range lines {
-		lines[i] = strings.TrimRight(lines[i], "\r")
-	}
-	return lines, nil
-}
diff --git a/command/server/config.go b/command/server/config.go
index 177983a70237f63371baed046f075e7446060548..07118dadc0a5cd604c7cfa39ade5bb259e7aa018 100644
--- a/command/server/config.go
+++ b/command/server/config.go
@@ -84,6 +84,8 @@ type Config struct {
 
 	// Account has the validator account related settings
 	Accounts *AccountsConfig `hcl:"accounts,block"`
+
+	GRPC *GRPCConfig
 }
 
 type P2PConfig struct {
@@ -226,6 +228,10 @@ type JsonRPCConfig struct {
 	Graphql *APIConfig `hcl:"graphql,block"`
 }
 
+type GRPCConfig struct {
+	Addr string
+}
+
 type APIConfig struct {
 	// Enabled selects whether the api is enabled
 	Enabled bool `hcl:"enabled,optional"`
@@ -459,6 +465,9 @@ func DefaultConfig() *Config {
 			AllowInsecureUnlock: false,
 			UseLightweightKDF:   false,
 		},
+		GRPC: &GRPCConfig{
+			Addr: ":3131",
+		},
 	}
 }
 
diff --git a/command/server/flags.go b/command/server/flags.go
index 98ab599f2fcfca1e5cf9a27445b3c1806ae80ec1..bd7ac80de347dca8d490b7f376cbf627144d65c1 100644
--- a/command/server/flags.go
+++ b/command/server/flags.go
@@ -440,5 +440,12 @@ func (c *Command) Flags() *flagset.Flagset {
 		Value: &c.cliConfig.Accounts.UseLightweightKDF,
 	})
 
+	// grpc
+	f.StringFlag(&flagset.StringFlag{
+		Name:  "grpc.addr",
+		Usage: "Address and port to bind the GRPC server",
+		Value: &c.cliConfig.GRPC.Addr,
+	})
+
 	return f
 }
diff --git a/command/server/pprof/pprof.go b/command/server/pprof/pprof.go
new file mode 100644
index 0000000000000000000000000000000000000000..7b25f4374343065d1878f2e2f9601c86ed797fea
--- /dev/null
+++ b/command/server/pprof/pprof.go
@@ -0,0 +1,36 @@
+package pprof
+
+import (
+	"bytes"
+	"fmt"
+	"runtime"
+	"runtime/pprof"
+)
+
+// Profile generates a pprof.Profile report for the given profile name.
+func Profile(profile string, debug, gc int) ([]byte, map[string]string, error) {
+	p := pprof.Lookup(profile)
+	if p == nil {
+		return nil, nil, fmt.Errorf("profile '%s' not found", profile)
+	}
+
+	if profile == "heap" && gc > 0 {
+		runtime.GC()
+	}
+
+	var buf bytes.Buffer
+	if err := p.WriteTo(&buf, debug); err != nil {
+		return nil, nil, err
+	}
+
+	headers := map[string]string{
+		"X-Content-Type-Options": "nosniff",
+	}
+	if debug != 0 {
+		headers["Content-Type"] = "text/plain; charset=utf-8"
+	} else {
+		headers["Content-Type"] = "application/octet-stream"
+		headers["Content-Disposition"] = fmt.Sprintf(`attachment; filename="%s"`, profile)
+	}
+	return buf.Bytes(), headers, nil
+}
diff --git a/command/server/proto/server.pb.go b/command/server/proto/server.pb.go
new file mode 100644
index 0000000000000000000000000000000000000000..c2f6c316a3eb39e96802f3e2e6c1958f6f1f1717
--- /dev/null
+++ b/command/server/proto/server.pb.go
@@ -0,0 +1,143 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.25.0
+// 	protoc        v3.12.0
+// source: command/server/proto/server.proto
+
+package proto
+
+import (
+	proto "github.com/golang/protobuf/proto"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// This is a compile-time assertion that a sufficiently up-to-date version
+// of the legacy proto package is being used.
+const _ = proto.ProtoPackageIsVersion4
+
+type DebugInput struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+}
+
+func (x *DebugInput) Reset() {
+	*x = DebugInput{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_command_server_proto_server_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DebugInput) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DebugInput) ProtoMessage() {}
+
+func (x *DebugInput) ProtoReflect() protoreflect.Message {
+	mi := &file_command_server_proto_server_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use DebugInput.ProtoReflect.Descriptor instead.
+func (*DebugInput) Descriptor() ([]byte, []int) {
+	return file_command_server_proto_server_proto_rawDescGZIP(), []int{0}
+}
+
+var File_command_server_proto_server_proto protoreflect.FileDescriptor
+
+var file_command_server_proto_server_proto_rawDesc = []byte{
+	0x0a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
+	0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x0c, 0x0a, 0x0a, 0x44, 0x65,
+	0x62, 0x75, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x32, 0x34, 0x0a, 0x03, 0x42, 0x6f, 0x72, 0x12,
+	0x2d, 0x0a, 0x05, 0x44, 0x65, 0x62, 0x75, 0x67, 0x12, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x1a, 0x11, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x42, 0x17,
+	0x5a, 0x15, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65,
+	0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_command_server_proto_server_proto_rawDescOnce sync.Once
+	file_command_server_proto_server_proto_rawDescData = file_command_server_proto_server_proto_rawDesc
+)
+
+func file_command_server_proto_server_proto_rawDescGZIP() []byte {
+	file_command_server_proto_server_proto_rawDescOnce.Do(func() {
+		file_command_server_proto_server_proto_rawDescData = protoimpl.X.CompressGZIP(file_command_server_proto_server_proto_rawDescData)
+	})
+	return file_command_server_proto_server_proto_rawDescData
+}
+
+var file_command_server_proto_server_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_command_server_proto_server_proto_goTypes = []interface{}{
+	(*DebugInput)(nil), // 0: proto.DebugInput
+}
+var file_command_server_proto_server_proto_depIdxs = []int32{
+	0, // 0: proto.Bor.Debug:input_type -> proto.DebugInput
+	0, // 1: proto.Bor.Debug:output_type -> proto.DebugInput
+	1, // [1:2] is the sub-list for method output_type
+	0, // [0:1] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_command_server_proto_server_proto_init() }
+func file_command_server_proto_server_proto_init() {
+	if File_command_server_proto_server_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_command_server_proto_server_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DebugInput); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_command_server_proto_server_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   1,
+			NumExtensions: 0,
+			NumServices:   1,
+		},
+		GoTypes:           file_command_server_proto_server_proto_goTypes,
+		DependencyIndexes: file_command_server_proto_server_proto_depIdxs,
+		MessageInfos:      file_command_server_proto_server_proto_msgTypes,
+	}.Build()
+	File_command_server_proto_server_proto = out.File
+	file_command_server_proto_server_proto_rawDesc = nil
+	file_command_server_proto_server_proto_goTypes = nil
+	file_command_server_proto_server_proto_depIdxs = nil
+}
diff --git a/command/server/proto/server.proto b/command/server/proto/server.proto
new file mode 100644
index 0000000000000000000000000000000000000000..64972fa1551f564eaacf376127a6b8eb02aa4de0
--- /dev/null
+++ b/command/server/proto/server.proto
@@ -0,0 +1,13 @@
+syntax = "proto3";
+
+package proto;
+
+option go_package = "/command/server/proto";
+
+service Bor {
+    rpc Debug(DebugInput) returns (DebugInput);
+}
+
+message DebugInput {
+
+}
diff --git a/command/server/proto/server_grpc.pb.go b/command/server/proto/server_grpc.pb.go
new file mode 100644
index 0000000000000000000000000000000000000000..e23aa7fda5932160d5cc2ad1dc09cf6c3e6ca1b7
--- /dev/null
+++ b/command/server/proto/server_grpc.pb.go
@@ -0,0 +1,101 @@
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+
+package proto
+
+import (
+	context "context"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+// Requires gRPC-Go v1.32.0 or later.
+const _ = grpc.SupportPackageIsVersion7
+
+// BorClient is the client API for Bor service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type BorClient interface {
+	Debug(ctx context.Context, in *DebugInput, opts ...grpc.CallOption) (*DebugInput, error)
+}
+
+type borClient struct {
+	cc grpc.ClientConnInterface
+}
+
+func NewBorClient(cc grpc.ClientConnInterface) BorClient {
+	return &borClient{cc}
+}
+
+func (c *borClient) Debug(ctx context.Context, in *DebugInput, opts ...grpc.CallOption) (*DebugInput, error) {
+	out := new(DebugInput)
+	err := c.cc.Invoke(ctx, "/proto.Bor/Debug", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// BorServer is the server API for Bor service.
+// All implementations must embed UnimplementedBorServer
+// for forward compatibility
+type BorServer interface {
+	Debug(context.Context, *DebugInput) (*DebugInput, error)
+	mustEmbedUnimplementedBorServer()
+}
+
+// UnimplementedBorServer must be embedded to have forward compatible implementations.
+type UnimplementedBorServer struct {
+}
+
+func (UnimplementedBorServer) Debug(context.Context, *DebugInput) (*DebugInput, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Debug not implemented")
+}
+func (UnimplementedBorServer) mustEmbedUnimplementedBorServer() {}
+
+// UnsafeBorServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to BorServer will
+// result in compilation errors.
+type UnsafeBorServer interface {
+	mustEmbedUnimplementedBorServer()
+}
+
+func RegisterBorServer(s grpc.ServiceRegistrar, srv BorServer) {
+	s.RegisterService(&Bor_ServiceDesc, srv)
+}
+
+func _Bor_Debug_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(DebugInput)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(BorServer).Debug(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/proto.Bor/Debug",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(BorServer).Debug(ctx, req.(*DebugInput))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+// Bor_ServiceDesc is the grpc.ServiceDesc for Bor service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var Bor_ServiceDesc = grpc.ServiceDesc{
+	ServiceName: "proto.Bor",
+	HandlerType: (*BorServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "Debug",
+			Handler:    _Bor_Debug_Handler,
+		},
+	},
+	Streams:  []grpc.StreamDesc{},
+	Metadata: "command/server/proto/server.proto",
+}
diff --git a/command/server/server.go b/command/server/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..437a39b90631d10404531c763a4f5cc96be16598
--- /dev/null
+++ b/command/server/server.go
@@ -0,0 +1,202 @@
+package server
+
+import (
+	"context"
+	"fmt"
+	"io"
+	"net"
+	"os"
+	"strings"
+	"time"
+
+	"github.com/ethereum/go-ethereum/accounts/keystore"
+	"github.com/ethereum/go-ethereum/command/server/proto"
+	"github.com/ethereum/go-ethereum/eth"
+	"github.com/ethereum/go-ethereum/eth/tracers"
+	"github.com/ethereum/go-ethereum/ethstats"
+	"github.com/ethereum/go-ethereum/graphql"
+	"github.com/ethereum/go-ethereum/log"
+	"github.com/ethereum/go-ethereum/metrics"
+	"github.com/ethereum/go-ethereum/metrics/influxdb"
+	"github.com/ethereum/go-ethereum/node"
+	"github.com/mattn/go-colorable"
+	"github.com/mattn/go-isatty"
+	"google.golang.org/grpc"
+)
+
+type Server struct {
+	proto.UnimplementedBorServer
+	node       *node.Node
+	grpcServer *grpc.Server
+}
+
+func NewServer(config *Config) (*Server, error) {
+	srv := &Server{}
+
+	// start the logger
+	setupLogger(config.LogLevel)
+
+	if err := srv.setupGRPCServer(config.GRPC.Addr); err != nil {
+		return nil, err
+	}
+
+	// load the chain genesis
+	if err := config.loadChain(); err != nil {
+		return nil, err
+	}
+
+	// create the node/stack
+	nodeCfg, err := config.buildNode()
+	if err != nil {
+		return nil, err
+	}
+	stack, err := node.New(nodeCfg)
+	if err != nil {
+		return nil, err
+	}
+	srv.node = stack
+
+	// register the ethereum backend
+	ethCfg, err := config.buildEth()
+	if err != nil {
+		return nil, err
+	}
+	backend, err := eth.New(stack, ethCfg)
+	if err != nil {
+		return nil, err
+	}
+
+	// debug tracing is enabled by default
+	stack.RegisterAPIs(tracers.APIs(backend.APIBackend))
+
+	// graphql is started from another place
+	if config.JsonRPC.Graphql.Enabled {
+		if err := graphql.New(stack, backend.APIBackend, config.JsonRPC.Cors, config.JsonRPC.Modules); err != nil {
+			return nil, fmt.Errorf("failed to register the GraphQL service: %v", err)
+		}
+	}
+
+	// register ethash service
+	if config.Ethstats != "" {
+		if err := ethstats.New(stack, backend.APIBackend, backend.Engine(), config.Ethstats); err != nil {
+			return nil, err
+		}
+	}
+
+	// setup account manager (only keystore)
+	{
+		keydir := stack.KeyStoreDir()
+		n, p := keystore.StandardScryptN, keystore.StandardScryptP
+		if config.Accounts.UseLightweightKDF {
+			n, p = keystore.LightScryptN, keystore.LightScryptP
+		}
+		stack.AccountManager().AddBackend(keystore.NewKeyStore(keydir, n, p))
+	}
+
+	// sealing (if enabled)
+	if config.Sealer.Enabled {
+		if err := backend.StartMining(1); err != nil {
+			return nil, err
+		}
+	}
+
+	if err := srv.setupMetrics(config.Telemetry); err != nil {
+		return nil, err
+	}
+
+	// start the node
+	if err := srv.node.Start(); err != nil {
+		return nil, err
+	}
+	return srv, nil
+}
+
+func (s *Server) Stop() {
+	s.node.Close()
+}
+
+func (s *Server) setupMetrics(config *TelemetryConfig) error {
+	metrics.Enabled = config.Enabled
+	metrics.EnabledExpensive = config.Expensive
+
+	if !metrics.Enabled {
+		// metrics are disabled, do not set up any sink
+		return nil
+	}
+
+	log.Info("Enabling metrics collection")
+
+	// influxdb
+	if v1Enabled, v2Enabled := config.InfluxDB.V1Enabled, config.InfluxDB.V2Enabled; v1Enabled || v2Enabled {
+		if v1Enabled && v2Enabled {
+			return fmt.Errorf("both influx v1 and influx v2 cannot be enabled")
+		}
+
+		cfg := config.InfluxDB
+		tags := cfg.Tags
+		endpoint := cfg.Endpoint
+
+		if v1Enabled {
+			log.Info("Enabling metrics export to InfluxDB (v1)")
+			go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, cfg.Database, cfg.Username, cfg.Password, "geth.", tags)
+		}
+		if v2Enabled {
+			log.Info("Enabling metrics export to InfluxDB (v2)")
+			go influxdb.InfluxDBV2WithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, cfg.Token, cfg.Bucket, cfg.Organization, "geth.", tags)
+		}
+	}
+
+	// Start system runtime metrics collection
+	go metrics.CollectProcessMetrics(3 * time.Second)
+
+	return nil
+}
+
+func (s *Server) setupGRPCServer(addr string) error {
+	s.grpcServer = grpc.NewServer(s.withLoggingUnaryInterceptor())
+	proto.RegisterBorServer(s.grpcServer, s)
+
+	lis, err := net.Listen("tcp", addr)
+	if err != nil {
+		return err
+	}
+
+	go func() {
+		if err := s.grpcServer.Serve(lis); err != nil {
+			log.Error("failed to serve grpc server", "err", err)
+		}
+	}()
+
+	log.Info("GRPC Server started", "addr", addr)
+	return nil
+}
+
+func (s *Server) withLoggingUnaryInterceptor() grpc.ServerOption {
+	return grpc.UnaryInterceptor(s.loggingServerInterceptor)
+}
+
+func (s *Server) loggingServerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
+	start := time.Now()
+	h, err := handler(ctx, req)
+	log.Trace("Request", "method", info.FullMethod, "duration", time.Since(start), "error", err)
+	return h, err
+}
+
+func setupLogger(logLevel string) {
+	output := io.Writer(os.Stderr)
+	usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
+	if usecolor {
+		output = colorable.NewColorableStderr()
+	}
+	ostream := log.StreamHandler(output, log.TerminalFormat(usecolor))
+	glogger := log.NewGlogHandler(ostream)
+
+	// logging
+	lvl, err := log.LvlFromString(strings.ToLower(logLevel))
+	if err == nil {
+		glogger.Verbosity(lvl)
+	} else {
+		glogger.Verbosity(log.LvlInfo)
+	}
+	log.Root().SetHandler(glogger)
+}
diff --git a/command/server/service.go b/command/server/service.go
new file mode 100644
index 0000000000000000000000000000000000000000..f6a41281b0f0c9f7fc549f9604a9442eb0484d8c
--- /dev/null
+++ b/command/server/service.go
@@ -0,0 +1,11 @@
+package server
+
+import (
+	"context"
+
+	"github.com/ethereum/go-ethereum/command/server/proto"
+)
+
+func (s *Server) Debug(ctx context.Context, req *proto.DebugInput) (*proto.DebugInput, error) {
+	return &proto.DebugInput{}, nil
+}
diff --git a/go.mod b/go.mod
index 7a2ed2478371402c9c6527fcee712f10a5d5d3ed..ee79f9b22223c6189eb78e577c830c3c42445542 100644
--- a/go.mod
+++ b/go.mod
@@ -74,6 +74,8 @@ require (
 	golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912
 	golang.org/x/text v0.3.6
 	golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba
+	google.golang.org/grpc v1.37.0
+	google.golang.org/protobuf v1.25.0
 	gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce
 	gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6
 	gopkg.in/urfave/cli.v1 v1.20.0
diff --git a/go.sum b/go.sum
index cdb8050f378ad40d826cb339511325f9cdf9234a..a8b1df8e13a526d6f7c987f70d043b94fc70baab 100644
--- a/go.sum
+++ b/go.sum
@@ -116,6 +116,7 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cloudflare/cloudflare-go v0.14.0 h1:gFqGlGl/5f9UGXAaKapCGUfaTCgRKKnzu2VvzMZlOFA=
 github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ=
 github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f h1:C43yEtQ6NIf4ftFXD/V55gnGFgPbMQobd//YlnLjUJ8=
 github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q=
@@ -144,7 +145,9 @@ github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbT
 github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts=
 github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=
 github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
@@ -201,6 +204,7 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x
 github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
 github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
 github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
 github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
@@ -217,6 +221,7 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
 github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
 github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa h1:Q75Upo5UN4JbPFURXZ8nLKYUvF85dyFRop/vQ0Rv+64=
@@ -672,18 +677,27 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx
 google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
 google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.37.0 h1:uSZWeQJX5j11bIQ4AJoj+McDBo29cY1MCoC1wO3ts+c=
+google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
 google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
 google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=