diff --git a/.gitignore b/.gitignore
index 1ee8b83022efe585f890e9104913de9cf40bfacf..15ccd00ed0f77a816351c0e2d6b11d174a6855ac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,3 +47,4 @@ profile.cov
 /dashboard/assets/package-lock.json
 
 **/yarn-error.log
+./test
diff --git a/command/flagset/flagset.go b/command/flagset/flagset.go
index c272bfbcad4e4174b8b012dbfcfcbb9620e9c757..c3c8704999dfeb120e1a93e3e88a027bb74acc37 100644
--- a/command/flagset/flagset.go
+++ b/command/flagset/flagset.go
@@ -78,10 +78,9 @@ func (f *Flagset) StringFlag(b *StringFlag) {
 }
 
 type IntFlag struct {
-	Name    string
-	Usage   string
-	Default int
-	Value   *int
+	Name  string
+	Usage string
+	Value *int
 }
 
 func (f *Flagset) IntFlag(i *IntFlag) {
@@ -89,14 +88,13 @@ func (f *Flagset) IntFlag(i *IntFlag) {
 		Name:  i.Name,
 		Usage: i.Usage,
 	})
-	f.set.IntVar(i.Value, i.Name, i.Default, i.Usage)
+	f.set.IntVar(i.Value, i.Name, *i.Value, i.Usage)
 }
 
 type Uint64Flag struct {
-	Name    string
-	Usage   string
-	Default uint64
-	Value   *uint64
+	Name  string
+	Usage string
+	Value *uint64
 }
 
 func (f *Flagset) Uint64Flag(i *Uint64Flag) {
@@ -104,17 +102,19 @@ func (f *Flagset) Uint64Flag(i *Uint64Flag) {
 		Name:  i.Name,
 		Usage: i.Usage,
 	})
-	f.set.Uint64Var(i.Value, i.Name, i.Default, i.Usage)
+	f.set.Uint64Var(i.Value, i.Name, *i.Value, i.Usage)
 }
 
 type BigIntFlag struct {
-	Name    string
-	Usage   string
-	Default *big.Int
-	Value   *big.Int
+	Name  string
+	Usage string
+	Value *big.Int
 }
 
 func (b *BigIntFlag) String() string {
+	if b.Value == nil {
+		return ""
+	}
 	return b.Value.String()
 }
 
@@ -145,15 +145,18 @@ func (f *Flagset) BigIntFlag(b *BigIntFlag) {
 type SliceStringFlag struct {
 	Name  string
 	Usage string
-	Value []string
+	Value *[]string
 }
 
 func (i *SliceStringFlag) String() string {
-	return strings.Join(i.Value, ",")
+	if i.Value == nil {
+		return ""
+	}
+	return strings.Join(*i.Value, ",")
 }
 
 func (i *SliceStringFlag) Set(value string) error {
-	i.Value = append(i.Value, value)
+	*i.Value = append(*i.Value, strings.Split(value, ",")...)
 	return nil
 }
 
@@ -171,25 +174,12 @@ type DurationFlag struct {
 	Value *time.Duration
 }
 
-func (d *DurationFlag) String() string {
-	return d.Value.String()
-}
-
-func (d *DurationFlag) Set(value string) error {
-	v, err := time.ParseDuration(value)
-	if err != nil {
-		return err
-	}
-	d.Value = &v
-	return nil
-}
-
 func (f *Flagset) DurationFlag(d *DurationFlag) {
 	f.addFlag(&FlagVar{
 		Name:  d.Name,
 		Usage: d.Usage,
 	})
-	f.set.Var(d, d.Name, d.Usage)
+	f.set.DurationVar(d.Value, d.Name, *d.Value, "")
 }
 
 type MapStringFlag struct {
@@ -199,6 +189,9 @@ type MapStringFlag struct {
 }
 
 func (m *MapStringFlag) String() string {
+	if m.Value == nil {
+		return ""
+	}
 	ls := []string{}
 	for k, v := range *m.Value {
 		ls = append(ls, k+"="+v)
diff --git a/command/flagset/flagset_test.go b/command/flagset/flagset_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..2f046c32480458cd11f90459acf333816663b6b3
--- /dev/null
+++ b/command/flagset/flagset_test.go
@@ -0,0 +1,60 @@
+package flagset
+
+import (
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestFlagsetBool(t *testing.T) {
+	f := NewFlagSet("")
+
+	value := false
+	f.BoolFlag(&BoolFlag{
+		Name:  "flag",
+		Value: &value,
+	})
+
+	assert.NoError(t, f.Parse([]string{"--flag", "true"}))
+	assert.Equal(t, value, true)
+}
+
+func TestFlagsetSliceString(t *testing.T) {
+	f := NewFlagSet("")
+
+	value := []string{}
+	f.SliceStringFlag(&SliceStringFlag{
+		Name:  "flag",
+		Value: &value,
+	})
+
+	assert.NoError(t, f.Parse([]string{"--flag", "a,b", "--flag", "c"}))
+	assert.Equal(t, value, []string{"a", "b", "c"})
+}
+
+func TestFlagsetDuration(t *testing.T) {
+	f := NewFlagSet("")
+
+	value := time.Duration(0)
+	f.DurationFlag(&DurationFlag{
+		Name:  "flag",
+		Value: &value,
+	})
+
+	assert.NoError(t, f.Parse([]string{"--flag", "1m"}))
+	assert.Equal(t, value, 1*time.Minute)
+}
+
+func TestFlagsetMapString(t *testing.T) {
+	f := NewFlagSet("")
+
+	value := map[string]string{}
+	f.MapStringFlag(&MapStringFlag{
+		Name:  "flag",
+		Value: &value,
+	})
+
+	assert.NoError(t, f.Parse([]string{"--flag", "a=b,c=d"}))
+	assert.Equal(t, value, map[string]string{"a": "b", "c": "d"})
+}
diff --git a/command/server/command.go b/command/server/command.go
index 56b1e1f1053b607f7993fb3eb7336ab0cdb8df0b..9e3df6a5fc9e86f4d20e712d1ee83d8e252e0745 100644
--- a/command/server/command.go
+++ b/command/server/command.go
@@ -3,13 +3,16 @@ 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"
@@ -30,6 +33,9 @@ type Command struct {
 	// cli configuration
 	cliConfig *Config
 
+	// final configuration
+	config *Config
+
 	configFile string
 
 	// bor node
@@ -74,6 +80,7 @@ func (c *Command) Run(args []string) int {
 		c.UI.Error(err.Error())
 		return 1
 	}
+	c.config = config
 
 	// start the logger
 	setupLogger(*config.LogLevel)
@@ -121,7 +128,7 @@ func (c *Command) Run(args []string) int {
 	}
 
 	// register ethash service
-	if config.EthStats != nil {
+	if *config.EthStats != "" {
 		if err := ethstats.New(stack, backend.APIBackend, backend.Engine(), *config.EthStats); err != nil {
 			c.UI.Error(err.Error())
 			return 1
@@ -129,13 +136,23 @@ func (c *Command) Run(args []string) int {
 	}
 
 	// setup account manager (only keystore)
+	var borKeystore *keystore.KeyStore
 	{
 		keydir := stack.KeyStoreDir()
 		n, p := keystore.StandardScryptN, keystore.StandardScryptP
-		if *config.UseLightweightKDF {
+		if *config.Accounts.UseLightweightKDF {
 			n, p = keystore.LightScryptN, keystore.LightScryptP
 		}
-		stack.AccountManager().AddBackend(keystore.NewKeyStore(keydir, n, p))
+		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
+		}
 	}
 
 	// sealing (if enabled)
@@ -159,6 +176,49 @@ func (c *Command) Run(args []string) int {
 	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) setupMetrics(config *MetricsConfig) error {
 	metrics.Enabled = *config.Enabled
 	metrics.EnabledExpensive = *config.Expensive
@@ -208,17 +268,19 @@ func (c *Command) handleSignals() int {
 	gracefulCh := make(chan struct{})
 	go func() {
 		c.node.Close()
+		c.node.Wait()
 		close(gracefulCh)
 	}()
 
-	select {
-	case <-signalCh:
-		return 1
-	case <-time.After(5 * time.Second):
-		return 1
-	case <-gracefulCh:
-		return 0
+	for i := 10; i > 0; i-- {
+		select {
+		case <-signalCh:
+			log.Warn("Already shutting down, interrupt more force stop.", "times", i-1)
+		case <-gracefulCh:
+			return 0
+		}
 	}
+	return 1
 }
 
 func setupLogger(logLevel string) {
@@ -239,3 +301,16 @@ func setupLogger(logLevel string) {
 	}
 	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 5236d20c7240e66da196694ae990a4a3b63bc04b..20cfaa5cfb2ea2e2da1a04b2aaf420a55b472e57 100644
--- a/command/server/config.go
+++ b/command/server/config.go
@@ -1,13 +1,13 @@
 package server
 
 import (
-	"crypto/ecdsa"
 	"fmt"
 	"io/ioutil"
 	"math"
 	"math/big"
 	"os"
 	"path/filepath"
+	"runtime"
 	"strconv"
 	"time"
 
@@ -16,7 +16,6 @@ import (
 	"github.com/ethereum/go-ethereum/command/server/chains"
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/common/fdlimit"
-	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/eth/downloader"
 	"github.com/ethereum/go-ethereum/eth/ethconfig"
 	"github.com/ethereum/go-ethereum/eth/gasprice"
@@ -27,6 +26,7 @@ import (
 	"github.com/ethereum/go-ethereum/p2p/nat"
 	"github.com/ethereum/go-ethereum/params"
 	"github.com/imdario/mergo"
+	"github.com/mitchellh/go-homedir"
 	gopsutil "github.com/shirou/gopsutil/mem"
 )
 
@@ -57,25 +57,25 @@ func durPtr(d time.Duration) *time.Duration {
 type Config struct {
 	chain *chains.Chain
 
-	Chain             *string
-	Debug             *bool
-	Whitelist         *map[string]string
-	UseLightweightKDF *bool
-	LogLevel          *string
-	DataDir           *string
-	P2P               *P2PConfig
-	SyncMode          *string
-	GcMode            *string
-	Snapshot          *bool
-	EthStats          *string
-	Heimdall          *HeimdallConfig
-	TxPool            *TxPoolConfig
-	Sealer            *SealerConfig
-	JsonRPC           *JsonRPCConfig
-	Gpo               *GpoConfig
-	Ethstats          *string
-	Metrics           *MetricsConfig
-	Cache             *CacheConfig
+	Chain     *string
+	Debug     *bool
+	Whitelist *map[string]string
+	LogLevel  *string
+	DataDir   *string
+	P2P       *P2PConfig
+	SyncMode  *string
+	GcMode    *string
+	Snapshot  *bool
+	EthStats  *string
+	Heimdall  *HeimdallConfig
+	TxPool    *TxPoolConfig
+	Sealer    *SealerConfig
+	JsonRPC   *JsonRPCConfig
+	Gpo       *GpoConfig
+	Ethstats  *string
+	Metrics   *MetricsConfig
+	Cache     *CacheConfig
+	Accounts  *AccountsConfig
 }
 
 type P2PConfig struct {
@@ -132,7 +132,6 @@ type JsonRPCConfig struct {
 	Modules []string
 	VHost   []string
 	Cors    []string
-	Bind    *string
 
 	GasCap   *uint64
 	TxFeeCap *float64
@@ -146,6 +145,7 @@ type APIConfig struct {
 	Enabled *bool
 	Port    *uint64
 	Prefix  *string
+	Host    *string
 }
 
 type GpoConfig struct {
@@ -187,14 +187,20 @@ type CacheConfig struct {
 	TxLookupLimit *uint64
 }
 
+type AccountsConfig struct {
+	Unlock              []string
+	PasswordFile        *string
+	AllowInsecureUnlock *bool
+	UseLightweightKDF   *bool
+}
+
 func DefaultConfig() *Config {
 	return &Config{
-		Chain:             stringPtr("mainnet"),
-		Debug:             boolPtr(false),
-		UseLightweightKDF: boolPtr(false),
-		Whitelist:         mapStrPtr(map[string]string{}),
-		LogLevel:          stringPtr("INFO"),
-		DataDir:           stringPtr(node.DefaultDataDir()),
+		Chain:     stringPtr("mainnet"),
+		Debug:     boolPtr(false),
+		Whitelist: mapStrPtr(map[string]string{}),
+		LogLevel:  stringPtr("INFO"),
+		DataDir:   stringPtr(defaultDataDir()),
 		P2P: &P2PConfig{
 			MaxPeers:     uint64Ptr(30),
 			MaxPendPeers: uint64Ptr(50),
@@ -235,6 +241,7 @@ func DefaultConfig() *Config {
 		},
 		Sealer: &SealerConfig{
 			Enabled:   boolPtr(false),
+			Etherbase: stringPtr(""),
 			GasCeil:   uint64Ptr(8000000),
 			GasPrice:  big.NewInt(params.GWei),
 			ExtraData: stringPtr(""),
@@ -247,6 +254,7 @@ func DefaultConfig() *Config {
 		},
 		JsonRPC: &JsonRPCConfig{
 			IPCDisable: boolPtr(false),
+			IPCPath:    stringPtr(""),
 			Modules:    []string{"web3", "net"},
 			Cors:       []string{"*"},
 			VHost:      []string{"*"},
@@ -256,11 +264,13 @@ func DefaultConfig() *Config {
 				Enabled: boolPtr(false),
 				Port:    uint64Ptr(8545),
 				Prefix:  stringPtr(""),
+				Host:    stringPtr("localhost"),
 			},
 			Ws: &APIConfig{
 				Enabled: boolPtr(false),
 				Port:    uint64Ptr(8546),
 				Prefix:  stringPtr(""),
+				Host:    stringPtr("localhost"),
 			},
 			Graphql: &APIConfig{
 				Enabled: boolPtr(false),
@@ -295,6 +305,12 @@ func DefaultConfig() *Config {
 			Preimages:     boolPtr(false),
 			TxLookupLimit: uint64Ptr(2350000),
 		},
+		Accounts: &AccountsConfig{
+			Unlock:              []string{},
+			PasswordFile:        stringPtr(""),
+			AllowInsecureUnlock: boolPtr(false),
+			UseLightweightKDF:   boolPtr(false),
+		},
 	}
 }
 
@@ -321,11 +337,6 @@ func (c *Config) loadChain() error {
 	c.chain = chain
 
 	// preload some default values that depend on the chain file
-
-	// the genesis files defines default bootnodes
-	if c.P2P.Discovery.Bootnodes == nil {
-		c.P2P.Discovery.Bootnodes = c.chain.Bootnodes
-	}
 	if c.P2P.Discovery.DNS == nil {
 		c.P2P.Discovery.DNS = c.chain.DNS
 	}
@@ -368,11 +379,11 @@ func (c *Config) buildEth() (*ethconfig.Config, error) {
 		n.Miner.GasCeil = *c.Sealer.GasCeil
 		n.Miner.ExtraData = []byte(*c.Sealer.ExtraData)
 
-		if etherbase := c.Sealer.Etherbase; etherbase != nil {
-			if !common.IsHexAddress(*etherbase) {
-				return nil, fmt.Errorf("etherbase is not an address: %s", *etherbase)
+		if etherbase := *c.Sealer.Etherbase; etherbase != "" {
+			if !common.IsHexAddress(etherbase) {
+				return nil, fmt.Errorf("etherbase is not an address: %s", etherbase)
 			}
-			n.Miner.Etherbase = common.HexToAddress(*etherbase)
+			n.Miner.Etherbase = common.HexToAddress(etherbase)
 		}
 	}
 
@@ -495,17 +506,18 @@ func (c *Config) buildNode() (*node.Config, error) {
 	ipcPath := ""
 	if !*c.JsonRPC.IPCDisable {
 		ipcPath = clientIdentifier + ".ipc"
-		if c.JsonRPC.IPCPath != nil {
+		if *c.JsonRPC.IPCPath != "" {
 			ipcPath = *c.JsonRPC.IPCPath
 		}
 	}
 
 	cfg := &node.Config{
-		Name:              clientIdentifier,
-		DataDir:           *c.DataDir,
-		UseLightweightKDF: *c.UseLightweightKDF,
-		Version:           params.VersionWithCommit(gitCommit, gitDate),
-		IPCPath:           ipcPath,
+		Name:                  clientIdentifier,
+		DataDir:               *c.DataDir,
+		UseLightweightKDF:     *c.Accounts.UseLightweightKDF,
+		InsecureUnlockAllowed: *c.Accounts.AllowInsecureUnlock,
+		Version:               params.VersionWithCommit(gitCommit, gitDate),
+		IPCPath:               ipcPath,
 		P2P: p2p.Config{
 			MaxPeers:        int(*c.P2P.MaxPeers),
 			MaxPendingPeers: int(*c.P2P.MaxPendPeers),
@@ -526,11 +538,11 @@ func (c *Config) buildNode() (*node.Config, error) {
 	// enable jsonrpc endpoints
 	{
 		if *c.JsonRPC.Http.Enabled {
-			cfg.HTTPHost = *c.JsonRPC.Bind
+			cfg.HTTPHost = *c.JsonRPC.Http.Host
 			cfg.HTTPPort = int(*c.JsonRPC.Http.Port)
 		}
 		if *c.JsonRPC.Ws.Enabled {
-			cfg.WSHost = *c.JsonRPC.Bind
+			cfg.WSHost = *c.JsonRPC.Ws.Host
 			cfg.WSPort = int(*c.JsonRPC.Ws.Port)
 		}
 	}
@@ -541,15 +553,13 @@ func (c *Config) buildNode() (*node.Config, error) {
 	}
 	cfg.P2P.NAT = natif
 
-	// setup private key for DevP2P if not found
-	devP2PPrivKey, err := readDevP2PKey(*c.DataDir)
-	if err != nil {
-		return nil, err
-	}
-	cfg.P2P.PrivateKey = devP2PPrivKey
-
 	// Discovery
-	if cfg.P2P.BootstrapNodes, err = parseBootnodes(c.P2P.Discovery.Bootnodes); err != nil {
+	// if no bootnodes are defined, use the ones from the chain file.
+	bootnodes := c.P2P.Discovery.Bootnodes
+	if len(bootnodes) == 0 {
+		bootnodes = c.chain.Bootnodes
+	}
+	if cfg.P2P.BootstrapNodes, err = parseBootnodes(bootnodes); err != nil {
 		return nil, err
 	}
 	if cfg.P2P.BootstrapNodesV5, err = parseBootnodes(c.P2P.Discovery.BootnodesV5); err != nil {
@@ -572,8 +582,8 @@ func (c *Config) buildNode() (*node.Config, error) {
 
 func (c *Config) Merge(cc ...*Config) error {
 	for _, elem := range cc {
-		if err := mergo.Merge(&c, elem); err != nil {
-			return err
+		if err := mergo.Merge(c, elem, mergo.WithOverride, mergo.WithAppendSlice); err != nil {
+			return fmt.Errorf("failed to merge configurations: %v", err)
 		}
 	}
 	return nil
@@ -605,30 +615,24 @@ func parseBootnodes(urls []string) ([]*enode.Node, error) {
 	return dst, nil
 }
 
-const devP2PKeyPath = "devp2p.key"
-
-func readDevP2PKey(dataDir string) (*ecdsa.PrivateKey, error) {
-	path := filepath.Join(dataDir, devP2PKeyPath)
-	_, err := os.Stat(path)
-	if err != nil && !os.IsNotExist(err) {
-		return nil, fmt.Errorf("failed to stat (%s): %v", path, err)
-	}
-
-	if os.IsNotExist(err) {
-		priv, err := crypto.GenerateKey()
-		if err != nil {
-			return nil, err
+func defaultDataDir() string {
+	// Try to place the data folder in the user's home dir
+	home, _ := homedir.Dir()
+	if home == "" {
+		// we cannot guess a stable location
+		return ""
+	}
+	switch runtime.GOOS {
+	case "darwin":
+		return filepath.Join(home, "Library", "Bor")
+	case "windows":
+		appdata := os.Getenv("LOCALAPPDATA")
+		if appdata == "" {
+			// Windows XP and below don't have LocalAppData.
+			panic("environment variable LocalAppData is undefined")
 		}
-		if err := crypto.SaveECDSA(path, priv); err != nil {
-			return nil, err
-		}
-		return priv, nil
-	}
-
-	// exists
-	priv, err := crypto.LoadECDSA(path)
-	if err != nil {
-		return nil, err
+		return filepath.Join(appdata, "Bor")
+	default:
+		return filepath.Join(home, ".bor")
 	}
-	return priv, nil
 }
diff --git a/command/server/config_test.go b/command/server/config_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..386757a602cca58f908e119492ca2af6753c440d
--- /dev/null
+++ b/command/server/config_test.go
@@ -0,0 +1,90 @@
+package server
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestConfigDefault(t *testing.T) {
+	// the default config should work out of the box
+	config := DefaultConfig()
+	assert.NoError(t, config.loadChain())
+
+	_, err := config.buildNode()
+	assert.NoError(t, err)
+
+	_, err = config.buildEth()
+	assert.NoError(t, err)
+}
+
+func TestConfigMerge(t *testing.T) {
+	c0 := &Config{
+		Chain: stringPtr("0"),
+		Debug: boolPtr(false),
+		Whitelist: mapStrPtr(map[string]string{
+			"a": "b",
+		}),
+		P2P: &P2PConfig{
+			Discovery: &P2PDiscovery{
+				StaticNodes: []string{
+					"a",
+				},
+			},
+		},
+	}
+	c1 := &Config{
+		Chain: stringPtr("1"),
+		Whitelist: mapStrPtr(map[string]string{
+			"b": "c",
+		}),
+		P2P: &P2PConfig{
+			Discovery: &P2PDiscovery{
+				StaticNodes: []string{
+					"b",
+				},
+			},
+		},
+	}
+	expected := &Config{
+		Chain: stringPtr("1"),
+		Debug: boolPtr(false),
+		Whitelist: mapStrPtr(map[string]string{
+			"a": "b",
+			"b": "c",
+		}),
+		P2P: &P2PConfig{
+			Discovery: &P2PDiscovery{
+				StaticNodes: []string{
+					"a",
+					"b",
+				},
+			},
+		},
+	}
+	assert.NoError(t, c0.Merge(c1))
+	assert.Equal(t, c0, expected)
+}
+
+var dummyEnodeAddr = "enode://0cb82b395094ee4a2915e9714894627de9ed8498fb881cec6db7c65e8b9a5bd7f2f25cc84e71e89d0947e51c76e85d0847de848c7782b13c0255247a6758178c@44.232.55.71:30303"
+
+func TestConfigBootnodesDefault(t *testing.T) {
+	t.Run("EmptyBootnodes", func(t *testing.T) {
+		// if no bootnodes are specific, we use the ones from the genesis chain
+		config := DefaultConfig()
+		assert.NoError(t, config.loadChain())
+
+		cfg, err := config.buildNode()
+		assert.NoError(t, err)
+		assert.NotEmpty(t, cfg.P2P.BootstrapNodes)
+	})
+	t.Run("NotEmptyBootnodes", func(t *testing.T) {
+		// if bootnodes specific, DO NOT load the genesis bootnodes
+		config := DefaultConfig()
+		config.P2P.Discovery.Bootnodes = []string{dummyEnodeAddr}
+
+		cfg, err := config.buildNode()
+		assert.NoError(t, err)
+		assert.Len(t, cfg.P2P.BootstrapNodes, 1)
+	})
+}
diff --git a/command/server/flags.go b/command/server/flags.go
index b0359d525f74fa2e9438ea85a65ee3a5d8466efc..8513e190611d4ff1067e40b8db6762a0b743b462 100644
--- a/command/server/flags.go
+++ b/command/server/flags.go
@@ -44,11 +44,6 @@ func (c *Command) Flags() *flagset.Flagset {
 		Usage: `Blockchain garbage collection mode ("full", "archive")`,
 		Value: c.cliConfig.GcMode,
 	})
-	f.BoolFlag(&flagset.BoolFlag{
-		Name:  "lightkdf",
-		Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength",
-		Value: c.cliConfig.UseLightweightKDF,
-	})
 	f.MapStringFlag(&flagset.MapStringFlag{
 		Name:  "whitelist",
 		Usage: "Comma separated block number-to-hash mappings to enforce (<number>=<hash>)",
@@ -76,7 +71,7 @@ func (c *Command) Flags() *flagset.Flagset {
 	f.SliceStringFlag(&flagset.SliceStringFlag{
 		Name:  "txpool.locals",
 		Usage: "Comma separated accounts to treat as locals (no flush, priority inclusion)",
-		Value: c.cliConfig.TxPool.Locals,
+		Value: &c.cliConfig.TxPool.Locals,
 	})
 	f.BoolFlag(&flagset.BoolFlag{
 		Name:  "txpool.nolocals",
@@ -259,21 +254,32 @@ func (c *Command) Flags() *flagset.Flagset {
 		Value: c.cliConfig.JsonRPC.IPCPath,
 	})
 	f.SliceStringFlag(&flagset.SliceStringFlag{
-		Name:  "rpc.corsdomain",
+		Name:  "jsonrpc.corsdomain",
 		Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
-		Value: c.cliConfig.JsonRPC.Cors,
+		Value: &c.cliConfig.JsonRPC.Cors,
 	})
 	f.SliceStringFlag(&flagset.SliceStringFlag{
-		Name:  "rpc.vhosts",
+		Name:  "jsonrpc.vhosts",
 		Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.",
-		Value: c.cliConfig.JsonRPC.VHost,
+		Value: &c.cliConfig.JsonRPC.VHost,
+	})
+	f.SliceStringFlag(&flagset.SliceStringFlag{
+		Name: "jsonrpc.modules",
+		Usage: "API's offered over the HTTP-RPC interface",
+		Value: &c.cliConfig.JsonRPC.Modules,
 	})
+
 	// http options
 	f.BoolFlag(&flagset.BoolFlag{
 		Name:  "http",
 		Usage: "Enable the HTTP-RPC server",
 		Value: c.cliConfig.JsonRPC.Http.Enabled,
 	})
+	f.StringFlag(&flagset.StringFlag{
+		Name:  "http.addr",
+		Usage: "HTTP-RPC server listening interface",
+		Value: c.cliConfig.JsonRPC.Http.Host,
+	})
 	f.Uint64Flag(&flagset.Uint64Flag{
 		Name:  "http.port",
 		Usage: "HTTP-RPC server listening port",
@@ -290,6 +296,11 @@ func (c *Command) Flags() *flagset.Flagset {
 		Usage: "Enable the WS-RPC server",
 		Value: c.cliConfig.JsonRPC.Ws.Enabled,
 	})
+	f.StringFlag(&flagset.StringFlag{
+		Name:  "ws.addr",
+		Usage: "WS-RPC server listening interface",
+		Value: c.cliConfig.JsonRPC.Ws.Host,
+	})
 	f.Uint64Flag(&flagset.Uint64Flag{
 		Name:  "ws.port",
 		Usage: "WS-RPC server listening port",
@@ -321,7 +332,7 @@ func (c *Command) Flags() *flagset.Flagset {
 	f.SliceStringFlag(&flagset.SliceStringFlag{
 		Name:  "bootnodes",
 		Usage: "Comma separated enode URLs for P2P discovery bootstrap",
-		Value: c.cliConfig.P2P.Discovery.Bootnodes,
+		Value: &c.cliConfig.P2P.Discovery.Bootnodes,
 	})
 	f.Uint64Flag(&flagset.Uint64Flag{
 		Name:  "maxpeers",
@@ -412,5 +423,27 @@ func (c *Command) Flags() *flagset.Flagset {
 		Value: c.cliConfig.Metrics.InfluxDB.Organization,
 	})
 
+	// account
+	f.SliceStringFlag(&flagset.SliceStringFlag{
+		Name:  "unlock",
+		Usage: "Comma separated list of accounts to unlock",
+		Value: &c.cliConfig.Accounts.Unlock,
+	})
+	f.StringFlag(&flagset.StringFlag{
+		Name:  "password",
+		Usage: "Password file to use for non-interactive password input",
+		Value: c.cliConfig.Accounts.PasswordFile,
+	})
+	f.BoolFlag(&flagset.BoolFlag{
+		Name:  "allow-insecure-unlock",
+		Usage: "Allow insecure account unlocking when account-related RPCs are exposed by http",
+		Value: c.cliConfig.Accounts.AllowInsecureUnlock,
+	})
+	f.BoolFlag(&flagset.BoolFlag{
+		Name:  "lightkdf",
+		Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength",
+		Value: c.cliConfig.Accounts.UseLightweightKDF,
+	})
+
 	return f
 }
diff --git a/go.mod b/go.mod
index dfc33fc8c752b5260165438cb00603213bd7b7f3..b5f8b0032da6ce64c2ff44d8bf4f45e8f0152015 100644
--- a/go.mod
+++ b/go.mod
@@ -51,6 +51,7 @@ require (
 	github.com/mattn/go-colorable v0.1.8
 	github.com/mattn/go-isatty v0.0.12
 	github.com/mitchellh/cli v1.1.2
+	github.com/mitchellh/go-homedir v1.1.0
 	github.com/naoina/go-stringutil v0.1.0 // indirect
 	github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416
 	github.com/olekukonko/tablewriter v0.0.5
diff --git a/go.sum b/go.sum
index 79bb6c6cf6a418db1ab0b7a3590511198765086e..a4b737eacd840f306e7d22ce824d7c00305ff9a0 100644
--- a/go.sum
+++ b/go.sum
@@ -33,7 +33,6 @@ github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSW
 github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
 github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
 github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
-github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
 github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
 github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
 github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
@@ -331,6 +330,8 @@ github.com/mitchellh/cli v1.1.2 h1:PvH+lL2B7IQ101xQL63Of8yFS2y+aDlsFcsqNc+u/Kw=
 github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4=
 github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
 github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
+github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
 github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=