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