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 }