cmd/istanbul, docker, genesis: add docker-compose and quorum flags
This commit is contained in:
parent
a72a4b5633
commit
8ff6abd0e7
12
README.md
12
README.md
|
@ -99,7 +99,7 @@ seal: 0x000000000000000000000000000000000000000000000000000000000000000000000000
|
||||||
<details>
|
<details>
|
||||||
<summary>Click here to expand</summary>
|
<summary>Click here to expand</summary>
|
||||||
|
|
||||||
When `--nodes --verbose` flags are given, a `static-nodes.json` template as well as the validators' node keys, public keys, and addresses are generated. When `--save` flag is given, the generated configs will be saved.
|
When `--nodes --verbose` flags are given, a `static-nodes.json` template as well as the validators' node keys, public keys, and addresses are generated. When `--docker-compose` is given, a `docker-compose.yml` for the validators is generated. When `--save` flag is given, all generated configs will be saved. Use Quorum when `--quorum` flag is given.
|
||||||
|
|
||||||
**Note**: the generated `static-nodes.json` template are set with IP `0.0.0.0`, please make according change to match your environment.
|
**Note**: the generated `static-nodes.json` template are set with IP `0.0.0.0`, please make according change to match your environment.
|
||||||
|
|
||||||
|
@ -201,10 +201,12 @@ DESCRIPTION:
|
||||||
|
|
||||||
|
|
||||||
OPTIONS:
|
OPTIONS:
|
||||||
--num value Number of validators (default: 0)
|
--num value Number of validators (default: 0)
|
||||||
--nodes Print static nodes template
|
--nodes Print static nodes template
|
||||||
--verbose Print validator details
|
--verbose Print validator details
|
||||||
--save Save to files
|
--quorum Use quorum
|
||||||
|
--docker-compose Print docker compose file
|
||||||
|
--save Save to files
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
|
@ -25,13 +25,14 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
"github.com/urfave/cli"
|
|
||||||
|
|
||||||
istcommon "github.com/getamis/istanbul-tools/common"
|
istcommon "github.com/getamis/istanbul-tools/common"
|
||||||
|
"github.com/getamis/istanbul-tools/docker/compose"
|
||||||
"github.com/getamis/istanbul-tools/genesis"
|
"github.com/getamis/istanbul-tools/genesis"
|
||||||
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
type validatorInfo struct {
|
type validatorInfo struct {
|
||||||
|
@ -57,6 +58,8 @@ var (
|
||||||
numOfValidatorsFlag,
|
numOfValidatorsFlag,
|
||||||
staticNodesFlag,
|
staticNodesFlag,
|
||||||
verboseFlag,
|
verboseFlag,
|
||||||
|
quorumFlag,
|
||||||
|
dockerComposeFlag,
|
||||||
saveFlag,
|
saveFlag,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -113,12 +116,22 @@ func gen(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
genesis := genesis.New(
|
var jsonBytes []byte
|
||||||
|
isQuorum := ctx.Bool(quorumFlag.Name)
|
||||||
|
g := genesis.New(
|
||||||
genesis.Validators(addrs...),
|
genesis.Validators(addrs...),
|
||||||
genesis.Alloc(addrs, new(big.Int).Exp(big.NewInt(10), big.NewInt(50), nil)),
|
genesis.Alloc(addrs, new(big.Int).Exp(big.NewInt(10), big.NewInt(50), nil)),
|
||||||
)
|
)
|
||||||
|
|
||||||
jsonBytes, _ := json.MarshalIndent(genesis, "", " ")
|
jsonBytes, _ = json.MarshalIndent(g, "", " ")
|
||||||
|
fmt.Println("--Old genesis.json")
|
||||||
|
fmt.Println(string(jsonBytes))
|
||||||
|
|
||||||
|
if isQuorum {
|
||||||
|
jsonBytes, _ = json.MarshalIndent(genesis.ToQuorum(g, true), "", " ")
|
||||||
|
} else {
|
||||||
|
jsonBytes, _ = json.MarshalIndent(g, "", " ")
|
||||||
|
}
|
||||||
fmt.Println("genesis.json")
|
fmt.Println("genesis.json")
|
||||||
fmt.Println(string(jsonBytes))
|
fmt.Println(string(jsonBytes))
|
||||||
|
|
||||||
|
@ -126,5 +139,31 @@ func gen(ctx *cli.Context) error {
|
||||||
ioutil.WriteFile("genesis.json", jsonBytes, os.ModePerm)
|
ioutil.WriteFile("genesis.json", jsonBytes, os.ModePerm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctx.Bool(dockerComposeFlag.Name) {
|
||||||
|
fmt.Print("\n\n\n")
|
||||||
|
compose := compose.New(
|
||||||
|
"172.16.239",
|
||||||
|
num,
|
||||||
|
"bb98a0b6442386d0cdf8a31b267892c1",
|
||||||
|
nodekeys,
|
||||||
|
removeSpacesAndLines(jsonBytes),
|
||||||
|
removeSpacesAndLines(staticNodes),
|
||||||
|
isQuorum)
|
||||||
|
fmt.Println("docker-compose.yml")
|
||||||
|
fmt.Println(compose.String())
|
||||||
|
|
||||||
|
if ctx.Bool(saveFlag.Name) {
|
||||||
|
ioutil.WriteFile("docker-compose.yml", []byte(compose.String()), os.ModePerm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func removeSpacesAndLines(b []byte) string {
|
||||||
|
out := string(b)
|
||||||
|
out = strings.Replace(out, " ", "", -1)
|
||||||
|
out = strings.Replace(out, "\t", "", -1)
|
||||||
|
out = strings.Replace(out, "\n", "", -1)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
|
@ -34,6 +34,16 @@ var (
|
||||||
Usage: "Print static nodes template",
|
Usage: "Print static nodes template",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dockerComposeFlag = cli.BoolFlag{
|
||||||
|
Name: "docker-compose",
|
||||||
|
Usage: "Print docker compose file",
|
||||||
|
}
|
||||||
|
|
||||||
|
quorumFlag = cli.BoolFlag{
|
||||||
|
Name: "quorum",
|
||||||
|
Usage: "Use Quorum",
|
||||||
|
}
|
||||||
|
|
||||||
saveFlag = cli.BoolFlag{
|
saveFlag = cli.BoolFlag{
|
||||||
Name: "save",
|
Name: "save",
|
||||||
Usage: "Save to files",
|
Usage: "Save to files",
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
// Copyright 2017 AMIS Technologies
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package compose
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/getamis/istanbul-tools/docker/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Compose interface {
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type istanbul struct {
|
||||||
|
IPPrefix string
|
||||||
|
EthStats *service.EthStats
|
||||||
|
Services []*service.Validator
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(ipPrefix string, number int, secret string, nodeKeys []string,
|
||||||
|
genesis string, staticNodes string, quorum bool) Compose {
|
||||||
|
ist := &istanbul{
|
||||||
|
IPPrefix: ipPrefix,
|
||||||
|
EthStats: service.NewEthStats(fmt.Sprintf("%v.9", ipPrefix), secret),
|
||||||
|
}
|
||||||
|
ist.init(number, nodeKeys, genesis, staticNodes)
|
||||||
|
if quorum {
|
||||||
|
return newQuorum(ist, number)
|
||||||
|
}
|
||||||
|
return ist
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ist *istanbul) init(number int, nodeKeys []string, genesis string, staticNodes string) {
|
||||||
|
for i := 0; i < number; i++ {
|
||||||
|
s := service.NewValidator(i,
|
||||||
|
genesis,
|
||||||
|
nodeKeys[i],
|
||||||
|
"",
|
||||||
|
30303+i,
|
||||||
|
8545+i,
|
||||||
|
ist.EthStats.Host(),
|
||||||
|
// from subnet ip 10
|
||||||
|
fmt.Sprintf("%v.%v", ist.IPPrefix, i+10),
|
||||||
|
)
|
||||||
|
|
||||||
|
staticNodes = strings.Replace(staticNodes, "0.0.0.0", s.IP, 1)
|
||||||
|
ist.Services = append(ist.Services, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// update static nodes
|
||||||
|
for i := range ist.Services {
|
||||||
|
ist.Services[i].StaticNodes = staticNodes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ist istanbul) String() string {
|
||||||
|
tmpl, err := template.New("istanbul").Parse(istanbulTemplate)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to parse template, %v", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
result := new(bytes.Buffer)
|
||||||
|
err = tmpl.Execute(result, ist)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to render template, %v", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
var istanbulTemplate = `version: '3'
|
||||||
|
services:
|
||||||
|
{{ .EthStats }}
|
||||||
|
{{- range .Services }}
|
||||||
|
{{ . }}
|
||||||
|
{{- end }}
|
||||||
|
networks:
|
||||||
|
app_net:
|
||||||
|
driver: bridge
|
||||||
|
ipam:
|
||||||
|
driver: default
|
||||||
|
config:
|
||||||
|
- subnet: {{ .IPPrefix }}.0/24`
|
|
@ -0,0 +1,110 @@
|
||||||
|
// Copyright 2017 AMIS Technologies
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package compose
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/getamis/istanbul-tools/docker/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
type quorum struct {
|
||||||
|
*istanbul
|
||||||
|
Number int
|
||||||
|
QuorumServices []*service.Quorum
|
||||||
|
}
|
||||||
|
|
||||||
|
func newQuorum(ist *istanbul, number int) Compose {
|
||||||
|
q := &quorum{
|
||||||
|
istanbul: ist,
|
||||||
|
Number: number,
|
||||||
|
}
|
||||||
|
q.init()
|
||||||
|
return q
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *quorum) init() {
|
||||||
|
// set constellations
|
||||||
|
var constellations []*service.Constellation
|
||||||
|
for i := 0; i < q.Number; i++ {
|
||||||
|
constellations = append(constellations,
|
||||||
|
service.NewConstellation(q.Services[i].Identity,
|
||||||
|
// from subnet ip 100
|
||||||
|
fmt.Sprintf("%v.%v", q.IPPrefix, i+100),
|
||||||
|
10000+i,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for i := 0; i < q.Number; i++ {
|
||||||
|
// set othernodes
|
||||||
|
var nodes []string
|
||||||
|
for j := 0; j < q.Number; j++ {
|
||||||
|
if i != j {
|
||||||
|
nodes = append(nodes, constellations[j].Host())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
constellations[i].SetOtherNodes(nodes)
|
||||||
|
|
||||||
|
// update quorum service
|
||||||
|
q.QuorumServices = append(q.QuorumServices,
|
||||||
|
service.NewQuorum(q.Services[i], constellations[i]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *quorum) String() string {
|
||||||
|
tmpl, err := template.New("quorum").Funcs(template.FuncMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"PrintVolumes": func() (result string) {
|
||||||
|
for i := 0; i < q.Number; i++ {
|
||||||
|
result += fmt.Sprintf(" \"%v\":\n", i)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
},
|
||||||
|
})).Parse(quorumTemplate)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to parse template, %v", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
result := new(bytes.Buffer)
|
||||||
|
err = tmpl.Execute(result, q)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to render template, %v", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
var quorumTemplate = `version: '3'
|
||||||
|
services:
|
||||||
|
{{ .EthStats }}
|
||||||
|
{{- range .QuorumServices }}
|
||||||
|
{{ . }}
|
||||||
|
{{- end }}
|
||||||
|
networks:
|
||||||
|
app_net:
|
||||||
|
driver: bridge
|
||||||
|
ipam:
|
||||||
|
driver: default
|
||||||
|
config:
|
||||||
|
- subnet: {{ .IPPrefix }}.0/24
|
||||||
|
volumes:
|
||||||
|
{{ PrintVolumes }}
|
||||||
|
`
|
|
@ -0,0 +1,110 @@
|
||||||
|
// Copyright 2017 AMIS Technologies
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Constellation struct {
|
||||||
|
Identity int
|
||||||
|
Name string
|
||||||
|
IP string
|
||||||
|
Port int
|
||||||
|
OtherNodes string
|
||||||
|
PublicKey string
|
||||||
|
PrivateKey string
|
||||||
|
SocketPath string
|
||||||
|
ConfigPath string
|
||||||
|
Folder string
|
||||||
|
KeyPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConstellation(identity int, ip string, port int) *Constellation {
|
||||||
|
folder := "/constellation"
|
||||||
|
keyPath := fmt.Sprintf("%v/tm", folder)
|
||||||
|
return &Constellation{
|
||||||
|
Identity: identity,
|
||||||
|
Name: fmt.Sprintf("constellation-%v", identity),
|
||||||
|
IP: ip,
|
||||||
|
Port: port,
|
||||||
|
PublicKey: fmt.Sprintf("%v.pub", keyPath),
|
||||||
|
PrivateKey: fmt.Sprintf("%v.key", keyPath),
|
||||||
|
SocketPath: fmt.Sprintf("%v.ipc", keyPath),
|
||||||
|
ConfigPath: fmt.Sprintf("%v.conf", keyPath),
|
||||||
|
Folder: folder,
|
||||||
|
KeyPath: keyPath,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Constellation) SetOtherNodes(nodes []string) {
|
||||||
|
c.OtherNodes = strings.Join(nodes, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Constellation) Host() string {
|
||||||
|
return fmt.Sprintf("http://%v:%v/", c.IP, c.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Constellation) String() string {
|
||||||
|
tmpl, err := template.New("constellation").Parse(constellationTemplate)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to parse template, %v", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
result := new(bytes.Buffer)
|
||||||
|
err = tmpl.Execute(result, c)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to render template, %v", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
var constellationTemplate = `{{ .Name }}:
|
||||||
|
hostname: {{ .Name }}
|
||||||
|
image: quay.io/amis/constellation:latest
|
||||||
|
ports:
|
||||||
|
- '{{ .Port }}:{{ .Port }}'
|
||||||
|
volumes:
|
||||||
|
- {{ .Identity }}:{{ .Folder }}:z
|
||||||
|
- .:/tmp/
|
||||||
|
entrypoint:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
mkdir -p {{ .Folder }}
|
||||||
|
echo "socket=\"{{ .SocketPath }}\"\npublickeys=[\"{{ .PublicKey }}\"]\n" > {{ .ConfigPath }}
|
||||||
|
constellation-node --generatekeys={{ .KeyPath }}
|
||||||
|
cp {{ .KeyPath }}.pub /tmp/tm{{ .Identity }}.pub
|
||||||
|
constellation-node \
|
||||||
|
--url={{ .Host }} \
|
||||||
|
--port={{ .Port }} \
|
||||||
|
--socket={{ .SocketPath }} \
|
||||||
|
--othernodes={{ .OtherNodes }} \
|
||||||
|
--publickeys={{ .PublicKey }} \
|
||||||
|
--privatekeys={{ .PrivateKey }} \
|
||||||
|
--storage={{ .Folder }} \
|
||||||
|
--verbosity=4
|
||||||
|
networks:
|
||||||
|
app_net:
|
||||||
|
ipv4_address: {{ .IP }}
|
||||||
|
restart: always`
|
|
@ -0,0 +1,67 @@
|
||||||
|
// Copyright 2017 AMIS Technologies
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EthStats struct {
|
||||||
|
Secret string
|
||||||
|
IP string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEthStats(ip string, secret string) *EthStats {
|
||||||
|
return &EthStats{
|
||||||
|
IP: ip,
|
||||||
|
Secret: secret,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c EthStats) Host() string {
|
||||||
|
return fmt.Sprintf("%v@%v:3000", c.Secret, c.IP)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c EthStats) String() string {
|
||||||
|
tmpl, err := template.New("eth_stats").Parse(ethStatsTemplate)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to parse template, %v", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
result := new(bytes.Buffer)
|
||||||
|
err = tmpl.Execute(result, c)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to render template, %v", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
var ethStatsTemplate = `eth-stats:
|
||||||
|
image: quay.io/amis/ethstats:latest
|
||||||
|
ports:
|
||||||
|
- '3000:3000'
|
||||||
|
environment:
|
||||||
|
- WS_SECRET={{ .Secret }}
|
||||||
|
restart: always
|
||||||
|
networks:
|
||||||
|
app_net:
|
||||||
|
ipv4_address: {{ .IP }}`
|
|
@ -0,0 +1,95 @@
|
||||||
|
// Copyright 2017 AMIS Technologies
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Quorum struct {
|
||||||
|
*Validator
|
||||||
|
Constellation *Constellation
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewQuorum(v *Validator, c *Constellation) *Quorum {
|
||||||
|
return &Quorum{
|
||||||
|
Validator: v,
|
||||||
|
Constellation: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (q Quorum) String() string {
|
||||||
|
tmpl, err := template.New("quorum").Parse(quorumTemplate)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to parse template, %v", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
result := new(bytes.Buffer)
|
||||||
|
err = tmpl.Execute(result, q)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to render template, %v", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
var quorumTemplate = `{{ .Name }}:
|
||||||
|
hostname: {{ .Name }}
|
||||||
|
image: quay.io/amis/quorum:feature_istanbul
|
||||||
|
ports:
|
||||||
|
- '{{ .Port }}:30303'
|
||||||
|
- '{{ .RPCPort }}:8545'
|
||||||
|
volumes:
|
||||||
|
- {{ .Identity }}:{{ .Constellation.Folder }}:z
|
||||||
|
depends_on:
|
||||||
|
- {{ .Constellation.Name }}
|
||||||
|
environment:
|
||||||
|
- PRIVATE_CONFIG={{ .Constellation.ConfigPath }}
|
||||||
|
entrypoint:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
mkdir -p /eth/geth
|
||||||
|
echo '{{ .Genesis }}' > /eth/genesis.json
|
||||||
|
echo '{{ .StaticNodes }}' > /eth/geth/static-nodes.json
|
||||||
|
geth --datadir "/eth" init "/eth/genesis.json"
|
||||||
|
geth \
|
||||||
|
--identity "{{ .Name }}" \
|
||||||
|
--rpc \
|
||||||
|
--rpcaddr "0.0.0.0" \
|
||||||
|
--rpcport "8545" \
|
||||||
|
--rpccorsdomain "*" \
|
||||||
|
--datadir "/eth" \
|
||||||
|
--port "30303" \
|
||||||
|
--rpcapi "db,eth,net,web3,istanbul,personal" \
|
||||||
|
--networkid "2017" \
|
||||||
|
--nat "any" \
|
||||||
|
--nodekeyhex "{{ .NodeKey }}" \
|
||||||
|
--mine \
|
||||||
|
--debug \
|
||||||
|
--metrics \
|
||||||
|
--syncmode "full" \
|
||||||
|
--ethstats "{{ .Name }}:{{ .EthStats }}" \
|
||||||
|
--gasprice 0
|
||||||
|
networks:
|
||||||
|
app_net:
|
||||||
|
ipv4_address: {{ .IP }}
|
||||||
|
restart: always
|
||||||
|
{{ .Constellation }}`
|
|
@ -0,0 +1,102 @@
|
||||||
|
// Copyright 2017 AMIS Technologies
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Validator struct {
|
||||||
|
Identity int
|
||||||
|
Genesis string
|
||||||
|
NodeKey string
|
||||||
|
StaticNodes string
|
||||||
|
Port int
|
||||||
|
RPCPort int
|
||||||
|
IP string
|
||||||
|
EthStats string
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewValidator(identity int, genesis string, nodeKey string, staticNodes string, port int, rpcPort int, ethStats string, ip string) *Validator {
|
||||||
|
return &Validator{
|
||||||
|
Identity: identity,
|
||||||
|
Genesis: genesis,
|
||||||
|
NodeKey: nodeKey,
|
||||||
|
Port: port,
|
||||||
|
RPCPort: rpcPort,
|
||||||
|
EthStats: ethStats,
|
||||||
|
IP: ip,
|
||||||
|
Name: fmt.Sprintf("validator-%v", identity),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Validator) String() string {
|
||||||
|
tmpl, err := template.New("validator").Parse(validatorTemplate)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to parse template, %v", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
result := new(bytes.Buffer)
|
||||||
|
err = tmpl.Execute(result, v)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to render template, %v", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
var validatorTemplate = `{{ .Name }}:
|
||||||
|
hostname: {{ .Name }}
|
||||||
|
image: quay.io/amis/geth:latest
|
||||||
|
ports:
|
||||||
|
- '{{ .Port }}:30303'
|
||||||
|
- '{{ .RPCPort }}:8545'
|
||||||
|
entrypoint:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
mkdir -p /eth/geth
|
||||||
|
echo '{{ .Genesis }}' > /eth/genesis.json
|
||||||
|
echo '{{ .StaticNodes }}' > /eth/geth/static-nodes.json
|
||||||
|
geth --datadir "/eth" init "/eth/genesis.json"
|
||||||
|
geth \
|
||||||
|
--identity "{{ .Name }}" \
|
||||||
|
--rpc \
|
||||||
|
--rpcaddr "0.0.0.0" \
|
||||||
|
--rpcport "8545" \
|
||||||
|
--rpccorsdomain "*" \
|
||||||
|
--datadir "/eth" \
|
||||||
|
--port "30303" \
|
||||||
|
--rpcapi "db,eth,net,web3,istanbul,personal" \
|
||||||
|
--networkid "2017" \
|
||||||
|
--nat "any" \
|
||||||
|
--nodekeyhex "{{ .NodeKey }}" \
|
||||||
|
--mine \
|
||||||
|
--debug \
|
||||||
|
--metrics \
|
||||||
|
--syncmode "full" \
|
||||||
|
--ethstats "{{ .Name }}:{{ .EthStats }}" \
|
||||||
|
--gasprice 0
|
||||||
|
networks:
|
||||||
|
app_net:
|
||||||
|
ipv4_address: {{ .IP }}
|
||||||
|
restart: always`
|
|
@ -0,0 +1,116 @@
|
||||||
|
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||||
|
|
||||||
|
package genesis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/getamis/go-ethereum/common/math"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = (*genesisSpecMarshaling)(nil)
|
||||||
|
|
||||||
|
func (q QuorumGenesis) MarshalJSON() ([]byte, error) {
|
||||||
|
type QuorumGenesis struct {
|
||||||
|
Config *QuorumChainConfig `json:"config"`
|
||||||
|
Nonce math.HexOrDecimal64 `json:"nonce"`
|
||||||
|
Timestamp math.HexOrDecimal64 `json:"timestamp"`
|
||||||
|
ExtraData hexutil.Bytes `json:"extraData"`
|
||||||
|
GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
|
||||||
|
Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
|
||||||
|
Mixhash common.Hash `json:"mixHash"`
|
||||||
|
Coinbase common.Address `json:"coinbase"`
|
||||||
|
Alloc map[common.UnprefixedAddress]core.GenesisAccount `json:"alloc" gencodec:"required"`
|
||||||
|
Number math.HexOrDecimal64 `json:"number"`
|
||||||
|
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
||||||
|
ParentHash common.Hash `json:"parentHash"`
|
||||||
|
}
|
||||||
|
var enc QuorumGenesis
|
||||||
|
enc.Config = q.Config
|
||||||
|
enc.Nonce = math.HexOrDecimal64(q.Nonce)
|
||||||
|
enc.Timestamp = math.HexOrDecimal64(q.Timestamp)
|
||||||
|
enc.ExtraData = q.ExtraData
|
||||||
|
enc.GasLimit = math.HexOrDecimal64(q.GasLimit)
|
||||||
|
enc.Difficulty = (*math.HexOrDecimal256)(q.Difficulty)
|
||||||
|
enc.Mixhash = q.Mixhash
|
||||||
|
enc.Coinbase = q.Coinbase
|
||||||
|
if q.Alloc != nil {
|
||||||
|
enc.Alloc = make(map[common.UnprefixedAddress]core.GenesisAccount, len(q.Alloc))
|
||||||
|
for k, v := range q.Alloc {
|
||||||
|
enc.Alloc[common.UnprefixedAddress(k)] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enc.Number = math.HexOrDecimal64(q.Number)
|
||||||
|
enc.GasUsed = math.HexOrDecimal64(q.GasUsed)
|
||||||
|
enc.ParentHash = q.ParentHash
|
||||||
|
return json.Marshal(&enc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *QuorumGenesis) UnmarshalJSON(input []byte) error {
|
||||||
|
type QuorumGenesis struct {
|
||||||
|
Config *QuorumChainConfig `json:"config"`
|
||||||
|
Nonce *math.HexOrDecimal64 `json:"nonce"`
|
||||||
|
Timestamp *math.HexOrDecimal64 `json:"timestamp"`
|
||||||
|
ExtraData hexutil.Bytes `json:"extraData"`
|
||||||
|
GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
|
||||||
|
Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
|
||||||
|
Mixhash *common.Hash `json:"mixHash"`
|
||||||
|
Coinbase *common.Address `json:"coinbase"`
|
||||||
|
Alloc map[common.UnprefixedAddress]core.GenesisAccount `json:"alloc" gencodec:"required"`
|
||||||
|
Number *math.HexOrDecimal64 `json:"number"`
|
||||||
|
GasUsed *math.HexOrDecimal64 `json:"gasUsed"`
|
||||||
|
ParentHash *common.Hash `json:"parentHash"`
|
||||||
|
}
|
||||||
|
var dec QuorumGenesis
|
||||||
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if dec.Config != nil {
|
||||||
|
q.Config = dec.Config
|
||||||
|
}
|
||||||
|
if dec.Nonce != nil {
|
||||||
|
q.Nonce = uint64(*dec.Nonce)
|
||||||
|
}
|
||||||
|
if dec.Timestamp != nil {
|
||||||
|
q.Timestamp = uint64(*dec.Timestamp)
|
||||||
|
}
|
||||||
|
if dec.ExtraData != nil {
|
||||||
|
q.ExtraData = dec.ExtraData
|
||||||
|
}
|
||||||
|
if dec.GasLimit == nil {
|
||||||
|
return errors.New("missing required field 'gasLimit' for QuorumGenesis")
|
||||||
|
}
|
||||||
|
q.GasLimit = uint64(*dec.GasLimit)
|
||||||
|
if dec.Difficulty == nil {
|
||||||
|
return errors.New("missing required field 'difficulty' for QuorumGenesis")
|
||||||
|
}
|
||||||
|
q.Difficulty = (*big.Int)(dec.Difficulty)
|
||||||
|
if dec.Mixhash != nil {
|
||||||
|
q.Mixhash = *dec.Mixhash
|
||||||
|
}
|
||||||
|
if dec.Coinbase != nil {
|
||||||
|
q.Coinbase = *dec.Coinbase
|
||||||
|
}
|
||||||
|
if dec.Alloc == nil {
|
||||||
|
return errors.New("missing required field 'alloc' for QuorumGenesis")
|
||||||
|
}
|
||||||
|
q.Alloc = make(core.GenesisAlloc, len(dec.Alloc))
|
||||||
|
for k, v := range dec.Alloc {
|
||||||
|
q.Alloc[common.Address(k)] = v
|
||||||
|
}
|
||||||
|
if dec.Number != nil {
|
||||||
|
q.Number = uint64(*dec.Number)
|
||||||
|
}
|
||||||
|
if dec.GasUsed != nil {
|
||||||
|
q.GasUsed = uint64(*dec.GasUsed)
|
||||||
|
}
|
||||||
|
if dec.ParentHash != nil {
|
||||||
|
q.ParentHash = *dec.ParentHash
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -18,11 +18,9 @@ package genesis
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/consensus/istanbul"
|
"github.com/ethereum/go-ethereum/consensus/istanbul"
|
||||||
|
@ -84,17 +82,15 @@ func NewFile(isQuorum bool, options ...Option) string {
|
||||||
func Save(dataDir string, genesis *core.Genesis, isQuorum bool) error {
|
func Save(dataDir string, genesis *core.Genesis, isQuorum bool) error {
|
||||||
filePath := filepath.Join(dataDir, FileName)
|
filePath := filepath.Join(dataDir, FileName)
|
||||||
|
|
||||||
raw, err := json.Marshal(genesis)
|
var raw []byte
|
||||||
|
var err error
|
||||||
|
if isQuorum {
|
||||||
|
raw, err = json.Marshal(ToQuorum(genesis, true))
|
||||||
|
} else {
|
||||||
|
raw, err = json.Marshal(genesis)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
//Quorum hack: add isQuorum field
|
|
||||||
if isQuorum {
|
|
||||||
jsonStr := string(raw)
|
|
||||||
idx := strings.Index(jsonStr, ",\"istanbul\"")
|
|
||||||
jsonStr = fmt.Sprintf("%s,\"isQuorum\":true%s", jsonStr[:idx], jsonStr[idx:])
|
|
||||||
raw = []byte(jsonStr)
|
|
||||||
}
|
|
||||||
return ioutil.WriteFile(filePath, raw, 0600)
|
return ioutil.WriteFile(filePath, raw, 0600)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
// Copyright 2017 AMIS Technologies
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package genesis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/getamis/go-ethereum/common/math"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate gencodec -type QuorumGenesis -field-override genesisSpecMarshaling -out gen_quorum_genesis.go
|
||||||
|
|
||||||
|
// field type overrides for gencodec
|
||||||
|
type genesisSpecMarshaling struct {
|
||||||
|
Nonce math.HexOrDecimal64
|
||||||
|
Timestamp math.HexOrDecimal64
|
||||||
|
ExtraData hexutil.Bytes
|
||||||
|
GasLimit math.HexOrDecimal64
|
||||||
|
GasUsed math.HexOrDecimal64
|
||||||
|
Number math.HexOrDecimal64
|
||||||
|
Difficulty *math.HexOrDecimal256
|
||||||
|
Alloc map[common.UnprefixedAddress]core.GenesisAccount
|
||||||
|
}
|
||||||
|
|
||||||
|
type QuorumChainConfig struct {
|
||||||
|
*params.ChainConfig
|
||||||
|
IsQuorum bool `json:"isQuorum,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QuorumGenesis struct {
|
||||||
|
Config *QuorumChainConfig `json:"config"`
|
||||||
|
Nonce uint64 `json:"nonce"`
|
||||||
|
Timestamp uint64 `json:"timestamp"`
|
||||||
|
ExtraData []byte `json:"extraData"`
|
||||||
|
GasLimit uint64 `json:"gasLimit" gencodec:"required"`
|
||||||
|
Difficulty *big.Int `json:"difficulty" gencodec:"required"`
|
||||||
|
Mixhash common.Hash `json:"mixHash"`
|
||||||
|
Coinbase common.Address `json:"coinbase"`
|
||||||
|
Alloc core.GenesisAlloc `json:"alloc" gencodec:"required"`
|
||||||
|
|
||||||
|
// These fields are used for consensus tests. Please don't use them
|
||||||
|
// in actual genesis blocks.
|
||||||
|
Number uint64 `json:"number"`
|
||||||
|
GasUsed uint64 `json:"gasUsed"`
|
||||||
|
ParentHash common.Hash `json:"parentHash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToQuorum converts standard genesis to quorum genesis
|
||||||
|
func ToQuorum(g *core.Genesis, isQuorum bool) *QuorumGenesis {
|
||||||
|
return &QuorumGenesis{
|
||||||
|
Config: &QuorumChainConfig{
|
||||||
|
ChainConfig: g.Config,
|
||||||
|
IsQuorum: isQuorum,
|
||||||
|
},
|
||||||
|
Nonce: g.Nonce,
|
||||||
|
Timestamp: g.Timestamp,
|
||||||
|
ExtraData: g.ExtraData,
|
||||||
|
GasLimit: g.GasLimit,
|
||||||
|
Difficulty: g.Difficulty,
|
||||||
|
Mixhash: g.Mixhash,
|
||||||
|
Coinbase: g.Coinbase,
|
||||||
|
Alloc: g.Alloc,
|
||||||
|
Number: g.Number,
|
||||||
|
GasUsed: g.GasUsed,
|
||||||
|
ParentHash: g.ParentHash,
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue