github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/cmd/puppeth/module_wallet.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:33</date> 10 //</624450069583630336> 11 12 13 package main 14 15 import ( 16 "bytes" 17 "fmt" 18 "html/template" 19 "math/rand" 20 "path/filepath" 21 "strconv" 22 "strings" 23 24 "github.com/ethereum/go-ethereum/log" 25 ) 26 27 // 28 var walletDockerfile = ` 29 FROM puppeth/wallet:latest 30 31 ADD genesis.json /genesis.json 32 33 RUN \ 34 echo 'node server.js &' > wallet.sh && \ 35 echo 'geth --cache 512 init /genesis.json' >> wallet.sh && \ 36 echo $'exec geth --networkid {{.NetworkID}} --port {{.NodePort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --rpc --rpcaddr=0.0.0.0 --rpccorsdomain "*" --rpcvhosts "*"' >> wallet.sh 37 38 RUN \ 39 sed -i 's/PuppethNetworkID/{{.NetworkID}}/g' dist/js/etherwallet-master.js && \ 40 sed -i 's/PuppethNetwork/{{.Network}}/g' dist/js/etherwallet-master.js && \ 41 sed -i 's/PuppethDenom/{{.Denom}}/g' dist/js/etherwallet-master.js && \ 42 sed -i 's/PuppethHost/{{.Host}}/g' dist/js/etherwallet-master.js && \ 43 sed -i 's/PuppethRPCPort/{{.RPCPort}}/g' dist/js/etherwallet-master.js 44 45 ENTRYPOINT ["/bin/sh", "wallet.sh"] 46 ` 47 48 //walletcomposefile是部署和 49 // 50 var walletComposefile = ` 51 version: '2' 52 services: 53 wallet: 54 build: . 55 image: {{.Network}}/wallet 56 container_name: {{.Network}}_wallet_1 57 ports: 58 - "{{.NodePort}}:{{.NodePort}}" 59 - "{{.NodePort}}:{{.NodePort}}/udp" 60 - "{{.RPCPort}}:8545"{{if not .VHost}} 61 - "{{.WebPort}}:80"{{end}} 62 volumes: 63 - {{.Datadir}}:/root/.ethereum 64 environment: 65 - NODE_PORT={{.NodePort}}/tcp 66 - STATS={{.Ethstats}}{{if .VHost}} 67 - VIRTUAL_HOST={{.VHost}} 68 - VIRTUAL_PORT=80{{end}} 69 logging: 70 driver: "json-file" 71 options: 72 max-size: "1m" 73 max-file: "10" 74 restart: always 75 ` 76 77 // 78 //Docker和Docker组合。如果具有指定网络名称的实例 79 //已经存在,将被覆盖! 80 func deployWallet(client *sshClient, network string, bootnodes []string, config *walletInfos, nocache bool) ([]byte, error) { 81 //生成要上载到服务器的内容 82 workdir := fmt.Sprintf("%d", rand.Int63()) 83 files := make(map[string][]byte) 84 85 dockerfile := new(bytes.Buffer) 86 template.Must(template.New("").Parse(walletDockerfile)).Execute(dockerfile, map[string]interface{}{ 87 "Network": strings.ToTitle(network), 88 "Denom": strings.ToUpper(network), 89 "NetworkID": config.network, 90 "NodePort": config.nodePort, 91 "RPCPort": config.rpcPort, 92 "Bootnodes": strings.Join(bootnodes, ","), 93 "Ethstats": config.ethstats, 94 "Host": client.address, 95 }) 96 files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes() 97 98 composefile := new(bytes.Buffer) 99 template.Must(template.New("").Parse(walletComposefile)).Execute(composefile, map[string]interface{}{ 100 "Datadir": config.datadir, 101 "Network": network, 102 "NodePort": config.nodePort, 103 "RPCPort": config.rpcPort, 104 "VHost": config.webHost, 105 "WebPort": config.webPort, 106 "Ethstats": config.ethstats[:strings.Index(config.ethstats, ":")], 107 }) 108 files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes() 109 110 files[filepath.Join(workdir, "genesis.json")] = config.genesis 111 112 //将部署文件上载到远程服务器(然后清理) 113 if out, err := client.Upload(files); err != nil { 114 return out, err 115 } 116 defer client.Run("rm -rf " + workdir) 117 118 //构建和部署引导或密封节点服务 119 if nocache { 120 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)) 121 } 122 return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s up -d --build --force-recreate --timeout 60", workdir, network)) 123 } 124 125 // 126 //各种配置参数。 127 type walletInfos struct { 128 genesis []byte 129 network int64 130 datadir string 131 ethstats string 132 nodePort int 133 rpcPort int 134 webHost string 135 webPort int 136 } 137 138 //报表将类型化结构转换为纯字符串->字符串映射,其中包含 139 //大多数(但不是全部)字段用于向用户报告。 140 func (info *walletInfos) Report() map[string]string { 141 report := map[string]string{ 142 "Data directory": info.datadir, 143 "Ethstats username": info.ethstats, 144 "Node listener port ": strconv.Itoa(info.nodePort), 145 "RPC listener port ": strconv.Itoa(info.rpcPort), 146 "Website address ": info.webHost, 147 "Website listener port ": strconv.Itoa(info.webPort), 148 } 149 return report 150 } 151 152 //check wallet对web wallet服务器进行健康检查,以验证 153 // 154 func checkWallet(client *sshClient, network string) (*walletInfos, error) { 155 // 156 infos, err := inspectContainer(client, fmt.Sprintf("%s_wallet_1", network)) 157 if err != nil { 158 return nil, err 159 } 160 if !infos.running { 161 return nil, ErrServiceOffline 162 } 163 //从主机或反向代理解析端口 164 webPort := infos.portmap["80/tcp"] 165 if webPort == 0 { 166 if proxy, _ := checkNginx(client, network); proxy != nil { 167 webPort = proxy.port 168 } 169 } 170 if webPort == 0 { 171 return nil, ErrNotExposed 172 } 173 //从反向代理和配置值解析主机 174 host := infos.envvars["VIRTUAL_HOST"] 175 if host == "" { 176 host = client.server 177 } 178 // 179 nodePort := infos.portmap[infos.envvars["NODE_PORT"]] 180 if err = checkPort(client.server, nodePort); err != nil { 181 log.Warn(fmt.Sprintf("Wallet devp2p port seems unreachable"), "server", client.server, "port", nodePort, "err", err) 182 } 183 rpcPort := infos.portmap["8545/tcp"] 184 if err = checkPort(client.server, rpcPort); err != nil { 185 log.Warn(fmt.Sprintf("Wallet RPC port seems unreachable"), "server", client.server, "port", rpcPort, "err", err) 186 } 187 //收集并返回有用的信息 188 stats := &walletInfos{ 189 datadir: infos.volumes["/root/.ethereum"], 190 nodePort: nodePort, 191 rpcPort: rpcPort, 192 webHost: host, 193 webPort: webPort, 194 ethstats: infos.envvars["STATS"], 195 } 196 return stats, nil 197 } 198