diff --git a/README.md b/README.md
index 4e82deef..5704de5e 100644
--- a/README.md
+++ b/README.md
@@ -99,7 +99,7 @@ seal: 0x000000000000000000000000000000000000000000000000000000000000000000000000
Click here to expand
-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.
@@ -201,10 +201,12 @@ DESCRIPTION:
OPTIONS:
- --num value Number of validators (default: 0)
- --nodes Print static nodes template
- --verbose Print validator details
- --save Save to files
+ --num value Number of validators (default: 0)
+ --nodes Print static nodes template
+ --verbose Print validator details
+ --quorum Use quorum
+ --docker-compose Print docker compose file
+ --save Save to files
```
diff --git a/cmd/istanbul/setup/cmd.go b/cmd/istanbul/setup/cmd.go
index 1618c2ef..5c04879d 100644
--- a/cmd/istanbul/setup/cmd.go
+++ b/cmd/istanbul/setup/cmd.go
@@ -25,13 +25,14 @@ import (
"os"
"path"
"strconv"
+ "strings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/p2p/discover"
- "github.com/urfave/cli"
-
istcommon "github.com/getamis/istanbul-tools/common"
+ "github.com/getamis/istanbul-tools/docker/compose"
"github.com/getamis/istanbul-tools/genesis"
+ "github.com/urfave/cli"
)
type validatorInfo struct {
@@ -57,6 +58,8 @@ var (
numOfValidatorsFlag,
staticNodesFlag,
verboseFlag,
+ quorumFlag,
+ dockerComposeFlag,
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.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(string(jsonBytes))
@@ -126,5 +139,31 @@ func gen(ctx *cli.Context) error {
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
}
+
+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
+}
diff --git a/cmd/istanbul/setup/flags.go b/cmd/istanbul/setup/flags.go
index 24f4cf49..bbcbbefa 100644
--- a/cmd/istanbul/setup/flags.go
+++ b/cmd/istanbul/setup/flags.go
@@ -34,6 +34,16 @@ var (
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{
Name: "save",
Usage: "Save to files",
diff --git a/docker/compose/istanbul.go b/docker/compose/istanbul.go
new file mode 100644
index 00000000..6fdd335e
--- /dev/null
+++ b/docker/compose/istanbul.go
@@ -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 .
+
+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`
diff --git a/docker/compose/quorum.go b/docker/compose/quorum.go
new file mode 100644
index 00000000..b04a3ecf
--- /dev/null
+++ b/docker/compose/quorum.go
@@ -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 .
+
+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 }}
+`
diff --git a/docker/service/constellation.go b/docker/service/constellation.go
new file mode 100644
index 00000000..8f4fb951
--- /dev/null
+++ b/docker/service/constellation.go
@@ -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 .
+
+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`
diff --git a/docker/service/eth_stats.go b/docker/service/eth_stats.go
new file mode 100644
index 00000000..fbe31939
--- /dev/null
+++ b/docker/service/eth_stats.go
@@ -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 .
+
+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 }}`
diff --git a/docker/service/quorum.go b/docker/service/quorum.go
new file mode 100644
index 00000000..f30ea01f
--- /dev/null
+++ b/docker/service/quorum.go
@@ -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 .
+
+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 }}`
diff --git a/docker/service/validator.go b/docker/service/validator.go
new file mode 100644
index 00000000..812430a2
--- /dev/null
+++ b/docker/service/validator.go
@@ -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 .
+
+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`
diff --git a/genesis/gen_quorum_genesis.go b/genesis/gen_quorum_genesis.go
new file mode 100644
index 00000000..4e75f078
--- /dev/null
+++ b/genesis/gen_quorum_genesis.go
@@ -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
+}
diff --git a/genesis/genesis.go b/genesis/genesis.go
index 0a2800ab..05c3205b 100644
--- a/genesis/genesis.go
+++ b/genesis/genesis.go
@@ -18,11 +18,9 @@ package genesis
import (
"encoding/json"
- "fmt"
"io/ioutil"
"math/big"
"path/filepath"
- "strings"
"time"
"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 {
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 {
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)
}
diff --git a/genesis/quorum.go b/genesis/quorum.go
new file mode 100644
index 00000000..3fcda096
--- /dev/null
+++ b/genesis/quorum.go
@@ -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 .
+
+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,
+ }
+}