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, + } +}