github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/cmd/puppeth/module_faucet.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 12:09:31</date> 10 //</624342603621470208> 11 12 13 package main 14 15 import ( 16 "bytes" 17 "encoding/json" 18 "fmt" 19 "html/template" 20 "math/rand" 21 "path/filepath" 22 "strconv" 23 "strings" 24 25 "github.com/ethereum/go-ethereum/common" 26 "github.com/ethereum/go-ethereum/log" 27 ) 28 29 // 30 // 31 var faucetDockerfile = ` 32 FROM ethereum/client-go:alltools-latest 33 34 ADD genesis.json /genesis.json 35 ADD account.json /account.json 36 ADD account.pass /account.pass 37 38 EXPOSE 8080 30303 30303/udp 39 40 ENTRYPOINT [ \ 41 "faucet", "--genesis", "/genesis.json", "--network", "{{.NetworkID}}", "--bootnodes", "{{.Bootnodes}}", "--ethstats", "{{.Ethstats}}", "--ethport", "{{.EthPort}}", \ 42 "--faucet.name", "{{.FaucetName}}", "--faucet.amount", "{{.FaucetAmount}}", "--faucet.minutes", "{{.FaucetMinutes}}", "--faucet.tiers", "{{.FaucetTiers}}", \ 43 "--account.json", "/account.json", "--account.pass", "/account.pass" \ 44 {{if .CaptchaToken}}, "--captcha.token", "{{.CaptchaToken}}", "--captcha.secret", "{{.CaptchaSecret}}"{{end}}{{if .NoAuth}}, "--noauth"{{end}} \ 45 ]` 46 47 //水龙头组合文件是部署和维护所需的docker-compose.yml文件。 48 //加密水龙头。 49 var faucetComposefile = ` 50 version: '2' 51 services: 52 faucet: 53 build: . 54 image: {{.Network}}/faucet 55 ports: 56 - "{{.EthPort}}:{{.EthPort}}"{{if not .VHost}} 57 - "{{.ApiPort}}:8080"{{end}} 58 volumes: 59 - {{.Datadir}}:/root/.faucet 60 environment: 61 - ETH_PORT={{.EthPort}} 62 - ETH_NAME={{.EthName}} 63 - FAUCET_AMOUNT={{.FaucetAmount}} 64 - FAUCET_MINUTES={{.FaucetMinutes}} 65 - FAUCET_TIERS={{.FaucetTiers}} 66 - CAPTCHA_TOKEN={{.CaptchaToken}} 67 - CAPTCHA_SECRET={{.CaptchaSecret}} 68 - NO_AUTH={{.NoAuth}}{{if .VHost}} 69 - VIRTUAL_HOST={{.VHost}} 70 - VIRTUAL_PORT=8080{{end}} 71 logging: 72 driver: "json-file" 73 options: 74 max-size: "1m" 75 max-file: "10" 76 restart: always 77 ` 78 79 // 80 //Docker和Docker组合。如果具有指定网络名称的实例 81 //已经存在,将被覆盖! 82 func deployFaucet(client *sshClient, network string, bootnodes []string, config *faucetInfos, nocache bool) ([]byte, error) { 83 //生成要上载到服务器的内容 84 workdir := fmt.Sprintf("%d", rand.Int63()) 85 files := make(map[string][]byte) 86 87 dockerfile := new(bytes.Buffer) 88 template.Must(template.New("").Parse(faucetDockerfile)).Execute(dockerfile, map[string]interface{}{ 89 "NetworkID": config.node.network, 90 "Bootnodes": strings.Join(bootnodes, ","), 91 "Ethstats": config.node.ethstats, 92 "EthPort": config.node.port, 93 "CaptchaToken": config.captchaToken, 94 "CaptchaSecret": config.captchaSecret, 95 "FaucetName": strings.Title(network), 96 "FaucetAmount": config.amount, 97 "FaucetMinutes": config.minutes, 98 "FaucetTiers": config.tiers, 99 "NoAuth": config.noauth, 100 }) 101 files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes() 102 103 composefile := new(bytes.Buffer) 104 template.Must(template.New("").Parse(faucetComposefile)).Execute(composefile, map[string]interface{}{ 105 "Network": network, 106 "Datadir": config.node.datadir, 107 "VHost": config.host, 108 "ApiPort": config.port, 109 "EthPort": config.node.port, 110 "EthName": config.node.ethstats[:strings.Index(config.node.ethstats, ":")], 111 "CaptchaToken": config.captchaToken, 112 "CaptchaSecret": config.captchaSecret, 113 "FaucetAmount": config.amount, 114 "FaucetMinutes": config.minutes, 115 "FaucetTiers": config.tiers, 116 "NoAuth": config.noauth, 117 }) 118 files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes() 119 120 files[filepath.Join(workdir, "genesis.json")] = config.node.genesis 121 files[filepath.Join(workdir, "account.json")] = []byte(config.node.keyJSON) 122 files[filepath.Join(workdir, "account.pass")] = []byte(config.node.keyPass) 123 124 //将部署文件上载到远程服务器(然后清理) 125 if out, err := client.Upload(files); err != nil { 126 return out, err 127 } 128 defer client.Run("rm -rf " + workdir) 129 130 // 131 if nocache { 132 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)) 133 } 134 return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s up -d --build --force-recreate --timeout 60", workdir, network)) 135 } 136 137 //水龙头信息从水龙头状态检查返回,以允许报告各种 138 //配置参数。 139 type faucetInfos struct { 140 node *nodeInfos 141 host string 142 port int 143 amount int 144 minutes int 145 tiers int 146 noauth bool 147 captchaToken string 148 captchaSecret string 149 } 150 151 //报表将类型化结构转换为纯字符串->字符串映射,其中包含 152 //大多数(但不是全部)字段用于向用户报告。 153 func (info *faucetInfos) Report() map[string]string { 154 report := map[string]string{ 155 "Website address": info.host, 156 "Website listener port": strconv.Itoa(info.port), 157 "Ethereum listener port": strconv.Itoa(info.node.port), 158 "Funding amount (base tier)": fmt.Sprintf("%d Ethers", info.amount), 159 "Funding cooldown (base tier)": fmt.Sprintf("%d mins", info.minutes), 160 "Funding tiers": strconv.Itoa(info.tiers), 161 "Captha protection": fmt.Sprintf("%v", info.captchaToken != ""), 162 "Ethstats username": info.node.ethstats, 163 } 164 if info.noauth { 165 report["Debug mode (no auth)"] = "enabled" 166 } 167 if info.node.keyJSON != "" { 168 var key struct { 169 Address string `json:"address"` 170 } 171 if err := json.Unmarshal([]byte(info.node.keyJSON), &key); err == nil { 172 report["Funding account"] = common.HexToAddress(key.Address).Hex() 173 } else { 174 log.Error("Failed to retrieve signer address", "err", err) 175 } 176 } 177 return report 178 } 179 180 //检查水龙头是否对水龙头服务器进行健康检查以验证 181 //它正在运行,如果是,收集有关它的有用信息。 182 func checkFaucet(client *sshClient, network string) (*faucetInfos, error) { 183 // 184 infos, err := inspectContainer(client, fmt.Sprintf("%s_faucet_1", network)) 185 if err != nil { 186 return nil, err 187 } 188 if !infos.running { 189 return nil, ErrServiceOffline 190 } 191 //从主机或反向代理解析端口 192 port := infos.portmap["8080/tcp"] 193 if port == 0 { 194 if proxy, _ := checkNginx(client, network); proxy != nil { 195 port = proxy.port 196 } 197 } 198 if port == 0 { 199 return nil, ErrNotExposed 200 } 201 // 202 host := infos.envvars["VIRTUAL_HOST"] 203 if host == "" { 204 host = client.server 205 } 206 amount, _ := strconv.Atoi(infos.envvars["FAUCET_AMOUNT"]) 207 minutes, _ := strconv.Atoi(infos.envvars["FAUCET_MINUTES"]) 208 tiers, _ := strconv.Atoi(infos.envvars["FAUCET_TIERS"]) 209 210 // 211 var out []byte 212 keyJSON, keyPass := "", "" 213 if out, err = client.Run(fmt.Sprintf("docker exec %s_faucet_1 cat /account.json", network)); err == nil { 214 keyJSON = string(bytes.TrimSpace(out)) 215 } 216 if out, err = client.Run(fmt.Sprintf("docker exec %s_faucet_1 cat /account.pass", network)); err == nil { 217 keyPass = string(bytes.TrimSpace(out)) 218 } 219 //运行健全检查以查看端口是否可访问 220 if err = checkPort(host, port); err != nil { 221 log.Warn("Faucet service seems unreachable", "server", host, "port", port, "err", err) 222 } 223 //容器可用,组装并返回有用的信息 224 return &faucetInfos{ 225 node: &nodeInfos{ 226 datadir: infos.volumes["/root/.faucet"], 227 port: infos.portmap[infos.envvars["ETH_PORT"]+"/tcp"], 228 ethstats: infos.envvars["ETH_NAME"], 229 keyJSON: keyJSON, 230 keyPass: keyPass, 231 }, 232 host: host, 233 port: port, 234 amount: amount, 235 minutes: minutes, 236 tiers: tiers, 237 captchaToken: infos.envvars["CAPTCHA_TOKEN"], 238 captchaSecret: infos.envvars["CAPTCHA_SECRET"], 239 noauth: infos.envvars["NO_AUTH"] == "true", 240 }, nil 241 } 242