github.com/klaytn/klaytn@v1.10.2/cmd/kgen/main.go (about)

     1  // Copyright 2019 The klaytn Authors
     2  // This file is part of the klaytn library.
     3  //
     4  // The klaytn library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The klaytn library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the klaytn library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package main
    18  
    19  import (
    20  	"crypto/ecdsa"
    21  	"crypto/rand"
    22  	"encoding/json"
    23  	"fmt"
    24  	"io/ioutil"
    25  	"net"
    26  	"os"
    27  	"path"
    28  
    29  	"github.com/klaytn/klaytn/cmd/utils"
    30  	"github.com/klaytn/klaytn/cmd/utils/nodecmd"
    31  	"github.com/klaytn/klaytn/common"
    32  	"github.com/klaytn/klaytn/crypto"
    33  	"github.com/klaytn/klaytn/log"
    34  	"github.com/klaytn/klaytn/networks/p2p/discover"
    35  	"gopkg.in/urfave/cli.v1"
    36  )
    37  
    38  type validatorInfo struct {
    39  	Address  common.Address
    40  	Nodekey  string
    41  	NodeInfo string
    42  }
    43  
    44  const (
    45  	dirKeys = "keys" // directory name where the created files are stored.
    46  )
    47  
    48  var (
    49  	logger   = log.NewModuleLogger(log.CMDKGEN)
    50  	fileFlag = cli.BoolFlag{
    51  		Name:  "file",
    52  		Usage: `Generate a nodekey and a Klaytn node information as files`,
    53  	}
    54  	portFlag = cli.IntFlag{
    55  		Name:  "port",
    56  		Usage: `Specify a tcp port number`,
    57  		Value: 32323,
    58  	}
    59  	ipFlag = cli.StringFlag{
    60  		Name:  "ip",
    61  		Usage: `Specify an ip address`,
    62  		Value: "0.0.0.0",
    63  	}
    64  )
    65  
    66  func init() {
    67  	cli.AppHelpTemplate = utils.KgenHelpTemplate
    68  	cli.HelpPrinter = utils.NewHelpPrinter(nil)
    69  }
    70  
    71  func main() {
    72  	app := cli.NewApp()
    73  	app.Name = "kgen"
    74  	app.Usage = "The command line interface to generate nodekey information for Klaytn"
    75  	app.Copyright = "Copyright 2018-2019 The klaytn Authors"
    76  	app.Action = genNodeKey
    77  	app.Flags = []cli.Flag{
    78  		fileFlag,
    79  		ipFlag,
    80  		portFlag,
    81  	}
    82  	app.Commands = []cli.Command{
    83  		nodecmd.VersionCommand,
    84  	}
    85  	app.HideVersion = true
    86  	// app.CustomAppHelpTemplate = kgenHelper
    87  	if err := app.Run(os.Args); err != nil {
    88  		fmt.Fprintln(os.Stderr, err)
    89  		os.Exit(1)
    90  	}
    91  }
    92  
    93  // writeNodeKeyInfoToFile writes `nodekey` and `node_info.json` as files under the `parentDir` folder.
    94  // The validator is a json format file containing address, nodekey and nodeinfo.
    95  func writeNodeKeyInfoToFile(validator *validatorInfo, parentDir string, nodekey string) error {
    96  	parentPath := path.Join("", parentDir)
    97  	err := os.MkdirAll(parentPath, os.ModePerm)
    98  	if err != nil {
    99  		return err
   100  	}
   101  
   102  	nodeKeyFilePath := path.Join(parentPath, "nodekey")
   103  	if err = ioutil.WriteFile(nodeKeyFilePath, []byte(nodekey), os.ModePerm); err != nil {
   104  		return err
   105  	}
   106  	fmt.Println("Created : ", nodeKeyFilePath)
   107  
   108  	str, err := json.MarshalIndent(validator, "", "\t")
   109  	if err != nil {
   110  		return err
   111  	}
   112  	validatorInfoFilePath := path.Join(parentPath, "node_info.json")
   113  	if err = ioutil.WriteFile(validatorInfoFilePath, []byte(str), os.ModePerm); err != nil {
   114  		return err
   115  	}
   116  
   117  	fmt.Println("Created : ", validatorInfoFilePath)
   118  	return nil
   119  }
   120  
   121  // makeNodeInfo creates a validator with the given parameters.
   122  func makeNodeInfo(nodeAddr common.Address, nodeKey string, privKey *ecdsa.PrivateKey, ip string, port uint16) *validatorInfo {
   123  	return &validatorInfo{
   124  		Address: nodeAddr,
   125  		Nodekey: nodeKey,
   126  		NodeInfo: discover.NewNode(
   127  			discover.PubkeyID(&privKey.PublicKey),
   128  			net.ParseIP(ip),
   129  			0,
   130  			port,
   131  			nil,
   132  			discover.NodeTypeUnknown).String(),
   133  	}
   134  }
   135  
   136  // genNodeKey creates a validator which is printed as json format or is stored into files(nodekey, validator).
   137  func genNodeKey(ctx *cli.Context) error {
   138  	pk, nk, addr, err := generateNodeInfoContents()
   139  	if err != nil {
   140  		return err
   141  	}
   142  	ip := ctx.String(ipFlag.Name)
   143  	if net.ParseIP(ip).To4() == nil {
   144  		return fmt.Errorf("IP address is not valid")
   145  	}
   146  	port := ctx.Uint(portFlag.Name)
   147  	if port > 65535 {
   148  		return fmt.Errorf("invalid port number")
   149  	}
   150  	nodeinfo := makeNodeInfo(addr, nk, pk, ip, uint16(port))
   151  	if ctx.Bool(fileFlag.Name) {
   152  		if err := writeNodeKeyInfoToFile(nodeinfo, dirKeys, nk); err != nil {
   153  			return err
   154  		}
   155  	} else {
   156  		str, err := json.MarshalIndent(nodeinfo, "", "\t")
   157  		if err != nil {
   158  			return err
   159  		}
   160  		fmt.Println(string(str))
   161  	}
   162  	return nil
   163  }
   164  
   165  // randomBytes creates random bytes as long as `len`.
   166  func randomBytes(len int) ([]byte, error) {
   167  	b := make([]byte, len)
   168  	_, _ = rand.Read(b)
   169  
   170  	return b, nil
   171  }
   172  
   173  // randomHex creates a random 32-bytes hexadecimal string.
   174  func randomHex() string {
   175  	b, _ := randomBytes(32)
   176  	return common.BytesToHash(b).Hex()
   177  }
   178  
   179  // generateNodeInfoContents generates contents of a validator.
   180  func generateNodeInfoContents() (*ecdsa.PrivateKey, string, common.Address, error) {
   181  	nodekey := randomHex()[2:] // truncate `0x` prefix
   182  
   183  	key, err := crypto.HexToECDSA(nodekey)
   184  	if err != nil {
   185  		logger.Error("Failed to generate key", "err", err)
   186  		return nil, "", common.Address{}, err
   187  	}
   188  
   189  	addr := crypto.PubkeyToAddress(key.PublicKey)
   190  
   191  	return key, nodekey, addr, nil
   192  }