github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/cmd/puppeth/module_node.go (about)

     1  
     2  //此源码被清华学神尹成大魔王专业翻译分析并修改
     3  //尹成QQ77025077
     4  //尹成微信18510341407
     5  //尹成所在QQ群721929980
     6  //尹成邮箱 yinc13@mails.tsinghua.edu.cn
     7  //尹成毕业于清华大学,微软区块链领域全球最有价值专家
     8  //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620
     9  //版权所有2017 Go Ethereum作者
    10  //此文件是Go以太坊的一部分。
    11  //
    12  //Go以太坊是免费软件:您可以重新发布和/或修改它
    13  //根据GNU通用公共许可证的条款
    14  //自由软件基金会,或者许可证的第3版,或者
    15  //(由您选择)任何更高版本。
    16  //
    17  //Go以太坊的分布希望它会有用,
    18  //但没有任何保证;甚至没有
    19  //适销性或特定用途的适用性。见
    20  //GNU通用公共许可证了解更多详细信息。
    21  //
    22  //你应该已经收到一份GNU通用公共许可证的副本
    23  //一起去以太坊吧。如果没有,请参见<http://www.gnu.org/licenses/>。
    24  
    25  package main
    26  
    27  import (
    28  	"bytes"
    29  	"encoding/json"
    30  	"fmt"
    31  	"math/rand"
    32  	"path/filepath"
    33  	"strconv"
    34  	"strings"
    35  	"text/template"
    36  
    37  	"github.com/ethereum/go-ethereum/common"
    38  	"github.com/ethereum/go-ethereum/log"
    39  )
    40  
    41  //
    42  var nodeDockerfile = `
    43  FROM ethereum/client-go:latest
    44  
    45  ADD genesis.json /genesis.json
    46  {{if .Unlock}}
    47  	ADD signer.json /signer.json
    48  	ADD signer.pass /signer.pass
    49  {{end}}
    50  RUN \
    51    echo 'geth --cache 512 init /genesis.json' > geth.sh && \{{if .Unlock}}
    52  	echo 'mkdir -p /root/.ethereum/keystore/ && cp /signer.json /root/.ethereum/keystore/' >> geth.sh && \{{end}}
    53  	echo $'exec geth --networkid {{.NetworkID}} --cache 512 --port {{.Port}} --maxpeers {{.Peers}} {{.LightFlag}} --ethstats \'{{.Ethstats}}\' {{if .Bootnodes}}--bootnodes {{.Bootnodes}}{{end}} {{if .Etherbase}}--miner.etherbase {{.Etherbase}} --mine --miner.threads 1{{end}} {{if .Unlock}}--unlock 0 --password /signer.pass --mine{{end}} --miner.gastarget {{.GasTarget}} --miner.gasprice {{.GasPrice}}' >> geth.sh
    54  
    55  ENTRYPOINT ["/bin/sh", "geth.sh"]
    56  `
    57  
    58  //
    59  //
    60  var nodeComposefile = `
    61  version: '2'
    62  services:
    63    {{.Type}}:
    64      build: .
    65      image: {{.Network}}/{{.Type}}
    66      ports:
    67        - "{{.Port}}:{{.Port}}"
    68        - "{{.Port}}:{{.Port}}/udp"
    69      volumes:
    70        - {{.Datadir}}:/root/.ethereum{{if .Ethashdir}}
    71        - {{.Ethashdir}}:/root/.ethash{{end}}
    72      environment:
    73        - PORT={{.Port}}/tcp
    74        - TOTAL_PEERS={{.TotalPeers}}
    75        - LIGHT_PEERS={{.LightPeers}}
    76        - STATS_NAME={{.Ethstats}}
    77        - MINER_NAME={{.Etherbase}}
    78        - GAS_TARGET={{.GasTarget}}
    79        - GAS_PRICE={{.GasPrice}}
    80      logging:
    81        driver: "json-file"
    82        options:
    83          max-size: "1m"
    84          max-file: "10"
    85      restart: always
    86  `
    87  
    88  //
    89  //Docker和Docker组合。如果具有指定网络名称的实例
    90  //已经存在,将被覆盖!
    91  func deployNode(client *sshClient, network string, bootnodes []string, config *nodeInfos, nocache bool) ([]byte, error) {
    92  	kind := "sealnode"
    93  	if config.keyJSON == "" && config.etherbase == "" {
    94  		kind = "bootnode"
    95  		bootnodes = make([]string, 0)
    96  	}
    97  //生成要上载到服务器的内容
    98  	workdir := fmt.Sprintf("%d", rand.Int63())
    99  	files := make(map[string][]byte)
   100  
   101  	lightFlag := ""
   102  	if config.peersLight > 0 {
   103  		lightFlag = fmt.Sprintf("--lightpeers=%d --lightserv=50", config.peersLight)
   104  	}
   105  	dockerfile := new(bytes.Buffer)
   106  	template.Must(template.New("").Parse(nodeDockerfile)).Execute(dockerfile, map[string]interface{}{
   107  		"NetworkID": config.network,
   108  		"Port":      config.port,
   109  		"Peers":     config.peersTotal,
   110  		"LightFlag": lightFlag,
   111  		"Bootnodes": strings.Join(bootnodes, ","),
   112  		"Ethstats":  config.ethstats,
   113  		"Etherbase": config.etherbase,
   114  		"GasTarget": uint64(1000000 * config.gasTarget),
   115  		"GasPrice":  uint64(1000000000 * config.gasPrice),
   116  		"Unlock":    config.keyJSON != "",
   117  	})
   118  	files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes()
   119  
   120  	composefile := new(bytes.Buffer)
   121  	template.Must(template.New("").Parse(nodeComposefile)).Execute(composefile, map[string]interface{}{
   122  		"Type":       kind,
   123  		"Datadir":    config.datadir,
   124  		"Ethashdir":  config.ethashdir,
   125  		"Network":    network,
   126  		"Port":       config.port,
   127  		"TotalPeers": config.peersTotal,
   128  		"Light":      config.peersLight > 0,
   129  		"LightPeers": config.peersLight,
   130  		"Ethstats":   config.ethstats[:strings.Index(config.ethstats, ":")],
   131  		"Etherbase":  config.etherbase,
   132  		"GasTarget":  config.gasTarget,
   133  		"GasPrice":   config.gasPrice,
   134  	})
   135  	files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes()
   136  
   137  	files[filepath.Join(workdir, "genesis.json")] = config.genesis
   138  	if config.keyJSON != "" {
   139  		files[filepath.Join(workdir, "signer.json")] = []byte(config.keyJSON)
   140  		files[filepath.Join(workdir, "signer.pass")] = []byte(config.keyPass)
   141  	}
   142  //将部署文件上载到远程服务器(然后清理)
   143  	if out, err := client.Upload(files); err != nil {
   144  		return out, err
   145  	}
   146  	defer client.Run("rm -rf " + workdir)
   147  
   148  //
   149  	if nocache {
   150  		return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s build --pull --no-cache && docker-compose -p %s up -d --force-recreate --timeout 60", workdir, network, network))
   151  	}
   152  	return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s up -d --build --force-recreate --timeout 60", workdir, network))
   153  }
   154  
   155  //
   156  //各种配置参数。
   157  type nodeInfos struct {
   158  	genesis    []byte
   159  	network    int64
   160  	datadir    string
   161  	ethashdir  string
   162  	ethstats   string
   163  	port       int
   164  	enode      string
   165  	peersTotal int
   166  	peersLight int
   167  	etherbase  string
   168  	keyJSON    string
   169  	keyPass    string
   170  	gasTarget  float64
   171  	gasPrice   float64
   172  }
   173  
   174  //报表将类型化结构转换为纯字符串->字符串映射,其中包含
   175  //大多数(但不是全部)字段用于向用户报告。
   176  func (info *nodeInfos) Report() map[string]string {
   177  	report := map[string]string{
   178  		"Data directory":           info.datadir,
   179  		"Listener port":            strconv.Itoa(info.port),
   180  		"Peer count (all total)":   strconv.Itoa(info.peersTotal),
   181  		"Peer count (light nodes)": strconv.Itoa(info.peersLight),
   182  		"Ethstats username":        info.ethstats,
   183  	}
   184  	if info.gasTarget > 0 {
   185  //
   186  		report["Gas limit (baseline target)"] = fmt.Sprintf("%0.3f MGas", info.gasTarget)
   187  		report["Gas price (minimum accepted)"] = fmt.Sprintf("%0.3f GWei", info.gasPrice)
   188  
   189  		if info.etherbase != "" {
   190  //
   191  			report["Ethash directory"] = info.ethashdir
   192  			report["Miner account"] = info.etherbase
   193  		}
   194  		if info.keyJSON != "" {
   195  //
   196  			var key struct {
   197  				Address string `json:"address"`
   198  			}
   199  			if err := json.Unmarshal([]byte(info.keyJSON), &key); err == nil {
   200  				report["Signer account"] = common.HexToAddress(key.Address).Hex()
   201  			} else {
   202  				log.Error("Failed to retrieve signer address", "err", err)
   203  			}
   204  		}
   205  	}
   206  	return report
   207  }
   208  
   209  //
   210  //
   211  func checkNode(client *sshClient, network string, boot bool) (*nodeInfos, error) {
   212  	kind := "bootnode"
   213  	if !boot {
   214  		kind = "sealnode"
   215  	}
   216  //
   217  	infos, err := inspectContainer(client, fmt.Sprintf("%s_%s_1", network, kind))
   218  	if err != nil {
   219  		return nil, err
   220  	}
   221  	if !infos.running {
   222  		return nil, ErrServiceOffline
   223  	}
   224  //
   225  	totalPeers, _ := strconv.Atoi(infos.envvars["TOTAL_PEERS"])
   226  	lightPeers, _ := strconv.Atoi(infos.envvars["LIGHT_PEERS"])
   227  	gasTarget, _ := strconv.ParseFloat(infos.envvars["GAS_TARGET"], 64)
   228  	gasPrice, _ := strconv.ParseFloat(infos.envvars["GAS_PRICE"], 64)
   229  
   230  //
   231  	var out []byte
   232  	if out, err = client.Run(fmt.Sprintf("docker exec %s_%s_1 geth --exec admin.nodeInfo.id --cache=16 attach", network, kind)); err != nil {
   233  		return nil, ErrServiceUnreachable
   234  	}
   235  	id := bytes.Trim(bytes.TrimSpace(out), "\"")
   236  
   237  	if out, err = client.Run(fmt.Sprintf("docker exec %s_%s_1 cat /genesis.json", network, kind)); err != nil {
   238  		return nil, ErrServiceUnreachable
   239  	}
   240  	genesis := bytes.TrimSpace(out)
   241  
   242  	keyJSON, keyPass := "", ""
   243  	if out, err = client.Run(fmt.Sprintf("docker exec %s_%s_1 cat /signer.json", network, kind)); err == nil {
   244  		keyJSON = string(bytes.TrimSpace(out))
   245  	}
   246  	if out, err = client.Run(fmt.Sprintf("docker exec %s_%s_1 cat /signer.pass", network, kind)); err == nil {
   247  		keyPass = string(bytes.TrimSpace(out))
   248  	}
   249  //运行健全性检查以查看是否可以访问devp2p
   250  	port := infos.portmap[infos.envvars["PORT"]]
   251  	if err = checkPort(client.server, port); err != nil {
   252  		log.Warn(fmt.Sprintf("%s devp2p port seems unreachable", strings.Title(kind)), "server", client.server, "port", port, "err", err)
   253  	}
   254  //收集并返回有用的信息
   255  	stats := &nodeInfos{
   256  		genesis:    genesis,
   257  		datadir:    infos.volumes["/root/.ethereum"],
   258  		ethashdir:  infos.volumes["/root/.ethash"],
   259  		port:       port,
   260  		peersTotal: totalPeers,
   261  		peersLight: lightPeers,
   262  		ethstats:   infos.envvars["STATS_NAME"],
   263  		etherbase:  infos.envvars["MINER_NAME"],
   264  		keyJSON:    keyJSON,
   265  		keyPass:    keyPass,
   266  		gasTarget:  gasTarget,
   267  		gasPrice:   gasPrice,
   268  	}
   269  stats.enode = fmt.Sprintf("enode://
   270  
   271  	return stats, nil
   272  }