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  }