cmd/puppeth: v4/v5 boot separation, signer gas configs (#14453)

This commit is contained in:
Péter Szilágyi 2017-05-13 03:03:56 +03:00 committed by Felix Lange
parent b0d0fafd68
commit ef7b9fb7d0
4 changed files with 73 additions and 8 deletions

@ -40,7 +40,7 @@ ADD genesis.json /genesis.json
RUN \ RUN \
echo '/geth init /genesis.json' > geth.sh && \{{if .Unlock}} echo '/geth init /genesis.json' > geth.sh && \{{if .Unlock}}
echo 'mkdir -p /root/.ethereum/keystore/ && cp /signer.json /root/.ethereum/keystore/' >> geth.sh && \{{end}} echo 'mkdir -p /root/.ethereum/keystore/ && cp /signer.json /root/.ethereum/keystore/' >> geth.sh && \{{end}}
echo $'/geth --networkid {{.NetworkID}} --cache 512 --port {{.Port}} --maxpeers {{.Peers}} {{.LightFlag}} --ethstats \'{{.Ethstats}}\' {{if .Bootnodes}}--bootnodes {{.Bootnodes}}{{end}} {{if .Etherbase}}--etherbase {{.Etherbase}} --mine{{end}}{{if .Unlock}}--unlock 0 --password /signer.pass --mine{{end}}' >> geth.sh echo $'/geth --networkid {{.NetworkID}} --cache 512 --port {{.Port}} --maxpeers {{.Peers}} {{.LightFlag}} --ethstats \'{{.Ethstats}}\' {{if .BootV4}}--bootnodesv4 {{.BootV4}}{{end}} {{if .BootV5}}--bootnodesv5 {{.BootV5}}{{end}} {{if .Etherbase}}--etherbase {{.Etherbase}} --mine{{end}}{{if .Unlock}}--unlock 0 --password /signer.pass --mine{{end}} --targetgaslimit {{.GasTarget}} --gasprice {{.GasPrice}}' >> geth.sh
ENTRYPOINT ["/bin/sh", "geth.sh"] ENTRYPOINT ["/bin/sh", "geth.sh"]
` `
@ -66,17 +66,20 @@ services:
- LIGHT_PEERS={{.LightPeers}} - LIGHT_PEERS={{.LightPeers}}
- STATS_NAME={{.Ethstats}} - STATS_NAME={{.Ethstats}}
- MINER_NAME={{.Etherbase}} - MINER_NAME={{.Etherbase}}
- GAS_TARGET={{.GasTarget}}
- GAS_PRICE={{.GasPrice}}
restart: always restart: always
` `
// deployNode deploys a new Ethereum node container to a remote machine via SSH, // deployNode deploys a new Ethereum node container to a remote machine via SSH,
// docker and docker-compose. If an instance with the specified network name // docker and docker-compose. If an instance with the specified network name
// already exists there, it will be overwritten! // already exists there, it will be overwritten!
func deployNode(client *sshClient, network string, bootnodes []string, config *nodeInfos) ([]byte, error) { func deployNode(client *sshClient, network string, bootv4, bootv5 []string, config *nodeInfos) ([]byte, error) {
kind := "sealnode" kind := "sealnode"
if config.keyJSON == "" && config.etherbase == "" { if config.keyJSON == "" && config.etherbase == "" {
kind = "bootnode" kind = "bootnode"
bootnodes = make([]string, 0) bootv4 = make([]string, 0)
bootv5 = make([]string, 0)
} }
// Generate the content to upload to the server // Generate the content to upload to the server
workdir := fmt.Sprintf("%d", rand.Int63()) workdir := fmt.Sprintf("%d", rand.Int63())
@ -92,9 +95,12 @@ func deployNode(client *sshClient, network string, bootnodes []string, config *n
"Port": config.portFull, "Port": config.portFull,
"Peers": config.peersTotal, "Peers": config.peersTotal,
"LightFlag": lightFlag, "LightFlag": lightFlag,
"Bootnodes": strings.Join(bootnodes, ","), "BootV4": strings.Join(bootv4, ","),
"BootV5": strings.Join(bootv5, ","),
"Ethstats": config.ethstats, "Ethstats": config.ethstats,
"Etherbase": config.etherbase, "Etherbase": config.etherbase,
"GasTarget": uint64(1000000 * config.gasTarget),
"GasPrice": uint64(1000000000 * config.gasPrice),
"Unlock": config.keyJSON != "", "Unlock": config.keyJSON != "",
}) })
files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes() files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes()
@ -111,6 +117,8 @@ func deployNode(client *sshClient, network string, bootnodes []string, config *n
"LightPeers": config.peersLight, "LightPeers": config.peersLight,
"Ethstats": config.ethstats[:strings.Index(config.ethstats, ":")], "Ethstats": config.ethstats[:strings.Index(config.ethstats, ":")],
"Etherbase": config.etherbase, "Etherbase": config.etherbase,
"GasTarget": config.gasTarget,
"GasPrice": config.gasPrice,
}) })
files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes() files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes()
@ -147,6 +155,8 @@ type nodeInfos struct {
etherbase string etherbase string
keyJSON string keyJSON string
keyPass string keyPass string
gasTarget float64
gasPrice float64
} }
// String implements the stringer interface. // String implements the stringer interface.
@ -155,7 +165,8 @@ func (info *nodeInfos) String() string {
if info.peersLight > 0 { if info.peersLight > 0 {
discv5 = fmt.Sprintf(", portv5=%d", info.portLight) discv5 = fmt.Sprintf(", portv5=%d", info.portLight)
} }
return fmt.Sprintf("port=%d%s, datadir=%s, peers=%d, lights=%d, ethstats=%s", info.portFull, discv5, info.datadir, info.peersTotal, info.peersLight, info.ethstats) return fmt.Sprintf("port=%d%s, datadir=%s, peers=%d, lights=%d, ethstats=%s, gastarget=%0.3f MGas, gasprice=%0.3f GWei",
info.portFull, discv5, info.datadir, info.peersTotal, info.peersLight, info.ethstats, info.gasTarget, info.gasPrice)
} }
// checkNode does a health-check against an boot or seal node server to verify // checkNode does a health-check against an boot or seal node server to verify
@ -176,6 +187,8 @@ func checkNode(client *sshClient, network string, boot bool) (*nodeInfos, error)
// Resolve a few types from the environmental variables // Resolve a few types from the environmental variables
totalPeers, _ := strconv.Atoi(infos.envvars["TOTAL_PEERS"]) totalPeers, _ := strconv.Atoi(infos.envvars["TOTAL_PEERS"])
lightPeers, _ := strconv.Atoi(infos.envvars["LIGHT_PEERS"]) lightPeers, _ := strconv.Atoi(infos.envvars["LIGHT_PEERS"])
gasTarget, _ := strconv.ParseFloat(infos.envvars["GAS_TARGET"], 64)
gasPrice, _ := strconv.ParseFloat(infos.envvars["GAS_PRICE"], 64)
// Container available, retrieve its node ID and its genesis json // Container available, retrieve its node ID and its genesis json
var out []byte var out []byte
@ -213,6 +226,8 @@ func checkNode(client *sshClient, network string, boot bool) (*nodeInfos, error)
etherbase: infos.envvars["MINER_NAME"], etherbase: infos.envvars["MINER_NAME"],
keyJSON: keyJSON, keyJSON: keyJSON,
keyPass: keyPass, keyPass: keyPass,
gasTarget: gasTarget,
gasPrice: gasPrice,
} }
stats.enodeFull = fmt.Sprintf("enode://%s@%s:%d", id, client.address, stats.portFull) stats.enodeFull = fmt.Sprintf("enode://%s@%s:%d", id, client.address, stats.portFull)
if stats.portLight != 0 { if stats.portLight != 0 {

@ -162,6 +162,48 @@ func (w *wizard) readDefaultInt(def int) int {
} }
} }
// readFloat reads a single line from stdin, trimming if from spaces, enforcing it
// to parse into a float.
func (w *wizard) readFloat() float64 {
for {
fmt.Printf("> ")
text, err := w.in.ReadString('\n')
if err != nil {
log.Crit("Failed to read user input", "err", err)
}
if text = strings.TrimSpace(text); text == "" {
continue
}
val, err := strconv.ParseFloat(strings.TrimSpace(text), 64)
if err != nil {
log.Error("Invalid input, expected float", "err", err)
continue
}
return val
}
}
// readDefaultFloat reads a single line from stdin, trimming if from spaces, enforcing
// it to parse into a float. If an empty line is entered, the default value is returned.
func (w *wizard) readDefaultFloat(def float64) float64 {
for {
fmt.Printf("> ")
text, err := w.in.ReadString('\n')
if err != nil {
log.Crit("Failed to read user input", "err", err)
}
if text = strings.TrimSpace(text); text == "" {
return def
}
val, err := strconv.ParseFloat(strings.TrimSpace(text), 64)
if err != nil {
log.Error("Invalid input, expected float", "err", err)
continue
}
return val
}
}
// readPassword reads a single line from stdin, trimming it from the trailing new // readPassword reads a single line from stdin, trimming it from the trailing new
// line and returns it. The input will not be echoed. // line and returns it. The input will not be echoed.
func (w *wizard) readPassword() string { func (w *wizard) readPassword() string {

@ -39,7 +39,7 @@ func (w *wizard) networkStats(tips bool) {
// Iterate over all the specified hosts and check their status // Iterate over all the specified hosts and check their status
stats := tablewriter.NewWriter(os.Stdout) stats := tablewriter.NewWriter(os.Stdout)
stats.SetHeader([]string{"Server", "IP", "Status", "Service", "Details"}) stats.SetHeader([]string{"Server", "IP", "Status", "Service", "Details"})
stats.SetColWidth(128) stats.SetColWidth(100)
for server, pubkey := range w.conf.Servers { for server, pubkey := range w.conf.Servers {
client := w.servers[server] client := w.servers[server]

@ -50,7 +50,7 @@ func (w *wizard) deployNode(boot bool) {
if boot { if boot {
infos = &nodeInfos{portFull: 30303, peersTotal: 512, peersLight: 256} infos = &nodeInfos{portFull: 30303, peersTotal: 512, peersLight: 256}
} else { } else {
infos = &nodeInfos{portFull: 30303, peersTotal: 50, peersLight: 0} infos = &nodeInfos{portFull: 30303, peersTotal: 50, peersLight: 0, gasTarget: 4.7, gasPrice: 18}
} }
} }
infos.genesis, _ = json.MarshalIndent(w.conf.genesis, "", " ") infos.genesis, _ = json.MarshalIndent(w.conf.genesis, "", " ")
@ -136,9 +136,17 @@ func (w *wizard) deployNode(boot bool) {
} }
} }
} }
// Establish the gas dynamics to be enforced by the signer
fmt.Println()
fmt.Printf("What gas limit should empty blocks target (MGas)? (default = %0.3f)\n", infos.gasTarget)
infos.gasTarget = w.readDefaultFloat(infos.gasTarget)
fmt.Println()
fmt.Printf("What gas price should the signer require (GWei)? (default = %0.3f)\n", infos.gasPrice)
infos.gasPrice = w.readDefaultFloat(infos.gasPrice)
} }
// Try to deploy the full node on the host // Try to deploy the full node on the host
if out, err := deployNode(client, w.network, w.conf.bootFull, infos); err != nil { if out, err := deployNode(client, w.network, w.conf.bootFull, w.conf.bootLight, infos); err != nil {
log.Error("Failed to deploy Ethereum node container", "err", err) log.Error("Failed to deploy Ethereum node container", "err", err)
if len(out) > 0 { if len(out) > 0 {
fmt.Printf("%s\n", out) fmt.Printf("%s\n", out)