github.com/annchain/OG@v0.0.9/node/config.go (about)

     1  // Copyright © 2019 Annchain Authors <EMAIL ADDRESS>
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  package node
    15  
    16  import (
    17  	"bytes"
    18  	"crypto/ecdsa"
    19  	"encoding/hex"
    20  	"encoding/json"
    21  	"fmt"
    22  	"github.com/annchain/OG/arefactor/common/io"
    23  	"github.com/annchain/OG/common/crypto"
    24  	ogcrypto2 "github.com/annchain/OG/deprecated/ogcrypto"
    25  	"github.com/annchain/OG/p2p"
    26  	"github.com/annchain/OG/p2p/discv5"
    27  	"github.com/annchain/OG/p2p/nat"
    28  	"github.com/annchain/OG/p2p/onode"
    29  	log "github.com/sirupsen/logrus"
    30  	"github.com/spf13/viper"
    31  	"io/ioutil"
    32  	"net"
    33  	"net/http"
    34  	"os"
    35  	"strconv"
    36  	"strings"
    37  	"time"
    38  )
    39  
    40  const (
    41  	datadirPrivateKey = "nodekey" // Path within the datadir to the node's private key
    42  	defaultMaxPeers   = 50
    43  	defaultNetworkId  = 1
    44  )
    45  
    46  type BootstrapInfoRequest struct {
    47  	NetworkId int64  `json:"networkid"`
    48  	PublicKey string `json:"publickey"`
    49  	ONode     string `json:"onode"`
    50  }
    51  
    52  type BootstrapInfoResponse struct {
    53  	Status         string `json:"status"`
    54  	BootstrapNode  bool   `json:"bootstrap_node"`
    55  	BootstrapNodes string `json:"bootstrap_nodes"`
    56  	GenesisPk      string `json:"genesis_pk"`
    57  	Message        string `json:"message"`
    58  	Partners       int    `json:"partners"`
    59  }
    60  
    61  func getNodePrivKey() *ecdsa.PrivateKey {
    62  	nodeKey := viper.GetString("p2p.node_key")
    63  	if nodeKey != "" {
    64  		keyByte, err := hex.DecodeString(nodeKey)
    65  		if err != nil {
    66  			panic(fmt.Sprintf("get nodekey error %v ", err))
    67  		}
    68  		key, err := ogcrypto2.ToECDSA(keyByte)
    69  		if err != nil {
    70  			panic(fmt.Sprintf("get nodekey error %v ", err))
    71  		}
    72  		return key
    73  	}
    74  	dataDir := viper.GetString("datadir")
    75  	// Use any specifically configured key.
    76  
    77  	keyFile := io.FixPrefixPath(dataDir, datadirPrivateKey)
    78  	if key, err := ogcrypto2.LoadECDSA(keyFile); err == nil {
    79  		return key
    80  	}
    81  	// No persistent key found, generate and store a new one.
    82  	key, err := ogcrypto2.GenerateKey()
    83  	if err != nil {
    84  		panic(fmt.Sprintf("failed to generate node key: %v", err))
    85  	}
    86  	if err := os.MkdirAll(dataDir, 0700); err != nil {
    87  		log.Error(fmt.Sprintf("failed to persist node key: %v", err))
    88  		return key
    89  	}
    90  	if err := ogcrypto2.SaveECDSA(keyFile, key); err != nil {
    91  		log.Error(fmt.Sprintf("failed to persist node key: %v", err))
    92  	}
    93  	data := ogcrypto2.FromECDSA(key)
    94  	viper.SetDefault("p2p.node_key", hex.EncodeToString(data))
    95  	return key
    96  }
    97  
    98  func getOnodeURL(privKey *ecdsa.PrivateKey) string {
    99  	port := viper.GetString("p2p.port")
   100  	tcpPort, _ := strconv.Atoi(port)
   101  	ogNode := onode.NewV4(&privKey.PublicKey, net.ParseIP("127.0.0.1"), tcpPort, tcpPort)
   102  	// if I got the hostname from env (given by kubernetes), use it as my URL
   103  	// Make sure the hostname is mapped to this pod by kubernetes's Service
   104  	// Or the IP will be refreshed after restarting the pod
   105  	s := ogNode.String()
   106  	if v, ok := os.LookupEnv("HOSTNAME"); ok {
   107  		s = strings.Replace(s, "127.0.0.1", v, 1)
   108  	}
   109  	return s
   110  }
   111  
   112  func NewP2PServer(privKey *ecdsa.PrivateKey, isBootNode bool) *p2p.Server {
   113  	var p2pConfig p2p.Config
   114  	p2pConfig.PrivateKey = privKey
   115  	port := viper.GetString("p2p.port")
   116  	p2pConfig.ListenAddr = ":" + port
   117  	maxPeers := viper.GetInt("p2p.max_peers")
   118  	if maxPeers <= 0 {
   119  		maxPeers = defaultMaxPeers
   120  	}
   121  	p2pConfig.MaxPeers = maxPeers
   122  	staticNodes := viper.GetString("p2p.static_nodes")
   123  	p2pConfig.StaticNodes = parserNodes(staticNodes)
   124  	trustNode := viper.GetString("p2p.trust_nodes")
   125  	p2pConfig.TrustedNodes = parserNodes(trustNode)
   126  	nodeName := viper.GetString("p2p.node_name")
   127  	if nodeName == "" {
   128  		nodeName = "og"
   129  	}
   130  	p2pConfig.NodeName = nodeName
   131  	p2pConfig.NodeDatabase = viper.GetString("p2p.node_db")
   132  	bootNodes := viper.GetString("p2p.bootstrap_nodes")
   133  	bootNodesV5 := viper.GetString("p2p.bootstrap_nodes_v5")
   134  	p2pConfig.BootstrapNodes = parserNodes(bootNodes)
   135  	p2pConfig.BootstrapNodesV5 = parserV5Nodes(bootNodesV5)
   136  	//p2pConfig.NoDiscovery = true
   137  	//p2pConfig.DiscoveryV5 = true
   138  	//p2pConfig.BootstrapNodesV5: config.BootstrapNodes.nodes,
   139  	p2pConfig.NAT = nat.Any()
   140  	p2pConfig.NoEncryption = viper.GetBool("p2p.no_encryption")
   141  
   142  	if isBootNode {
   143  		tcpPort, err := strconv.Atoi(port)
   144  		if err != nil {
   145  			panic(err)
   146  		}
   147  		ogNode := onode.NewV4(&privKey.PublicKey, net.ParseIP("127.0.0.1"), tcpPort, tcpPort)
   148  		viper.SetDefault("p2p.bootstrap_nodes", ogNode.String())
   149  	}
   150  
   151  	return &p2p.Server{Config: p2pConfig}
   152  }
   153  
   154  func parserNodes(nodeString string) []*onode.Node {
   155  	nodeList := strings.Split(nodeString, ";")
   156  	var nodes []*onode.Node
   157  	for _, url := range nodeList {
   158  		if url == "" {
   159  			continue
   160  		}
   161  		node, err := onode.ParseV4(url)
   162  		if err != nil {
   163  			log.Error(fmt.Sprintf("Node URL %s: %v\n", url, err))
   164  			continue
   165  		}
   166  		nodes = append(nodes, node)
   167  	}
   168  	return nodes
   169  }
   170  
   171  func parserV5Nodes(nodeString string) []*discv5.Node {
   172  	nodeList := strings.Split(nodeString, ";")
   173  	var nodes []*discv5.Node
   174  	for _, url := range nodeList {
   175  		if url == "" {
   176  			continue
   177  		}
   178  		node, err := discv5.ParseNode(url)
   179  		if err != nil {
   180  			log.Error(fmt.Sprintf("node URL %s: %v\n", url, err))
   181  			continue
   182  		}
   183  		nodes = append(nodes, node)
   184  	}
   185  	return nodes
   186  }
   187  
   188  func parserGenesisAccounts(pubkeys string) []crypto.PublicKey {
   189  	pubkeyList := strings.Split(pubkeys, ";")
   190  	var account []crypto.PublicKey
   191  	for _, pubKeyStr := range pubkeyList {
   192  		pubKey, err := crypto.PublicKeyFromString(pubKeyStr)
   193  		if err != nil {
   194  			panic(err)
   195  		}
   196  		account = append(account, pubKey)
   197  	}
   198  	return account
   199  }
   200  
   201  // buildBootstrap keeps sending info of itself to quering server and wait for other bootstrap server to be ready
   202  func buildBootstrap(networkId int64, nodeURL string, key *crypto.PublicKey) {
   203  
   204  	pubkey := key.String()
   205  
   206  	breq := BootstrapInfoRequest{
   207  		NetworkId: networkId,
   208  		ONode:     nodeURL,
   209  		PublicKey: pubkey,
   210  	}
   211  	for {
   212  		bresp, err := doRequest(breq)
   213  		if err != nil {
   214  			log.Warn("failed to get bootstrap config. wait for another 5 seconds")
   215  			time.Sleep(time.Second * 5)
   216  			continue
   217  		}
   218  		if bresp.Status != "ok" {
   219  			log.WithField("message", bresp.Message).Info("Consensus group is not ready. waiting for more nodes.")
   220  			time.Sleep(time.Second * 5)
   221  			continue
   222  		}
   223  		// ready.
   224  		injectedPath := io.FixPrefixPath(viper.GetString("datadir"), "injected.toml")
   225  		injectedViper := viper.New()
   226  		injectedViper.SetConfigType("toml")
   227  
   228  		injectedViper.Set("p2p.bootstrap_node", bresp.BootstrapNode)
   229  		injectedViper.Set("p2p.bootstrap_nodes", bresp.BootstrapNodes)
   230  		injectedViper.Set("annsensus.genesis_pk", bresp.GenesisPk)
   231  		injectedViper.Set("annsensus.partner_number", bresp.Partners)
   232  		injectedViper.Set("annsensus.threshold", 2*bresp.Partners/3+1)
   233  
   234  		err = injectedViper.WriteConfigAs(injectedPath)
   235  		if err != nil {
   236  			log.WithError(err).Fatal("cannot dump injected config")
   237  		}
   238  		err = viper.MergeConfigMap(injectedViper.AllSettings())
   239  		if err != nil {
   240  			log.WithError(err).Fatal("cannot merge injected config")
   241  		}
   242  
   243  		log.WithField("resp", bresp).Info("bootstrap info is updated.")
   244  		break
   245  	}
   246  
   247  }
   248  
   249  func doRequest(breq BootstrapInfoRequest) (bresp BootstrapInfoResponse, err error) {
   250  	url := viper.GetString("p2p.bootstrap_config_server")
   251  	if url == "" {
   252  		panic("You must either provide a bootstrap server or provide a bootstrap config server to start building network.")
   253  	}
   254  
   255  	jsonStr, err := json.Marshal(breq)
   256  	if err != nil {
   257  		panic("failed to marshal bootstrap request data")
   258  	}
   259  	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
   260  	req.Header.Set("Content-Type", "application/json")
   261  
   262  	client := &http.Client{
   263  		Timeout: time.Second * 10,
   264  	}
   265  	defer client.CloseIdleConnections()
   266  
   267  	resp, err := client.Do(req)
   268  	if err != nil {
   269  		log.WithError(err).Warn("failed to request bootstrap centralized server")
   270  		return
   271  	}
   272  	body, err := ioutil.ReadAll(resp.Body)
   273  	if err != nil {
   274  		log.WithError(err).Warn("failed to read response")
   275  		return
   276  	}
   277  	err = json.Unmarshal(body, &bresp)
   278  	if err != nil {
   279  		log.WithError(err).Warn("response cannot be unmarshalled")
   280  		return
   281  	}
   282  	return
   283  }