From 49eabb5773eb6c252f863108c989957a3bbfa129 Mon Sep 17 00:00:00 2001 From: Edwin Date: Mon, 21 Aug 2017 17:15:14 +0800 Subject: [PATCH 1/4] container: add Address function for interface --- container/ethereum.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/container/ethereum.go b/container/ethereum.go index 172f4375..46cd12fc 100644 --- a/container/ethereum.go +++ b/container/ethereum.go @@ -36,7 +36,9 @@ import ( "github.com/docker/docker/client" "github.com/docker/go-connections/nat" "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/p2p/discover" @@ -59,6 +61,7 @@ type Ethereum interface { Stop() error NodeAddress() string + Address() common.Address ContainerID() string Host() string @@ -363,6 +366,10 @@ func (eth *ethereum) NodeAddress() string { return "" } +func (eth *ethereum) Address() common.Address { + return crypto.PubkeyToAddress(eth.key.PublicKey) +} + func (eth *ethereum) ConsensusMonitor(errCh chan<- error, quit chan struct{}) { cli := eth.NewClient() From 5b7c69ece09be1a65a4ba5984a4dc57106ac515f Mon Sep 17 00:00:00 2001 From: Edwin Date: Mon, 21 Aug 2017 17:17:52 +0800 Subject: [PATCH 2/4] container: add addValidators function for blockchain --- container/blockchain.go | 109 +++++++++++++++++++++++++++++----------- 1 file changed, 79 insertions(+), 30 deletions(-) diff --git a/container/blockchain.go b/container/blockchain.go index eb280c18..55a4313c 100644 --- a/container/blockchain.go +++ b/container/blockchain.go @@ -32,6 +32,7 @@ import ( ) type Blockchain interface { + AddValidators(numOfValidators int) ([]Ethereum, error) EnsureConsensusWorking(geths []Ethereum, t time.Duration) error Start() error Stop(bool) error @@ -40,7 +41,7 @@ type Blockchain interface { } func NewBlockchain(numOfValidators int, options ...Option) (bc *blockchain) { - bc = &blockchain{} + bc = &blockchain{opts: options} var err error bc.dockerClient, err = client.NewEnvClient() @@ -48,10 +49,7 @@ func NewBlockchain(numOfValidators int, options ...Option) (bc *blockchain) { log.Fatalf("Cannot connect to Docker daemon, err: %v", err) } - keys, addrs := generateKeys(numOfValidators) - bc.setupGenesis(addrs) - bc.setupValidators(keys, options...) - + bc.addValidators(numOfValidators) return bc } @@ -61,6 +59,33 @@ type blockchain struct { dockerClient *client.Client genesisFile string validators []Ethereum + opts []Option +} + +func (bc *blockchain) AddValidators(numOfValidators int) ([]Ethereum, error) { + // TODO: need a lock + lastLen := len(bc.validators) + bc.addValidators(numOfValidators) + + newValidators := bc.validators[lastLen:] + if err := bc.start(newValidators); err != nil { + return nil, err + } + + // propose new validators as validator in consensus + for _, v := range bc.validators[:lastLen] { + istClient := v.NewIstanbulClient() + for _, newV := range newValidators { + if err := istClient.ProposeValidator(context.Background(), newV.Address(), true); err != nil { + return nil, err + } + } + } + + if err := bc.connectAll(); err != nil { + return nil, err + } + return newValidators, nil } func (bc *blockchain) EnsureConsensusWorking(geths []Ethereum, t time.Duration) error { @@ -85,12 +110,9 @@ func (bc *blockchain) EnsureConsensusWorking(geths []Ethereum, t time.Duration) } func (bc *blockchain) Start() error { - for _, v := range bc.validators { - if err := v.Start(); err != nil { - return err - } + if err := bc.start(bc.validators); err != nil { + return err } - return bc.connectAll() } @@ -100,7 +122,6 @@ func (bc *blockchain) Stop(force bool) error { return err } } - return nil } @@ -114,16 +135,42 @@ func (bc *blockchain) Validators() []Ethereum { // ---------------------------------------------------------------------------- +func (bc *blockchain) addValidators(numOfValidators int) error { + keys, addrs := generateKeys(numOfValidators) + bc.setupGenesis(addrs) + bc.setupValidators(keys, bc.opts...) + + return nil +} + +func (bc *blockchain) connectAll() error { + for i, v := range bc.validators { + istClient := v.NewIstanbulClient() + for j, v := range bc.validators { + if i == j { + continue + } + err := istClient.AddPeer(context.Background(), v.NodeAddress()) + if err != nil { + return err + } + } + } + return nil +} + func (bc *blockchain) setupGenesis(addrs []common.Address) { - setupDir, err := generateRandomDir() - if err != nil { - log.Fatal("Failed to create setup dir", err) + if bc.genesisFile == "" { + setupDir, err := generateRandomDir() + if err != nil { + log.Fatal("Failed to create setup dir", err) + } + err = genesis.Save(setupDir, genesis.New(addrs)) + if err != nil { + log.Fatal("Failed to save genesis", err) + } + bc.genesisFile = filepath.Join(setupDir, genesis.FileName) } - err = genesis.Save(setupDir, genesis.New(addrs)) - if err != nil { - log.Fatal("Failed to save genesis", err) - } - bc.genesisFile = filepath.Join(setupDir, genesis.FileName) } func (bc *blockchain) setupValidators(keys []*ecdsa.PrivateKey, options ...Option) { @@ -155,17 +202,19 @@ func (bc *blockchain) setupValidators(keys []*ecdsa.PrivateKey, options ...Optio } } -func (bc *blockchain) connectAll() error { - for i, v := range bc.validators { - istClient := v.NewIstanbulClient() - for j, v := range bc.validators { - if i == j { - continue - } - err := istClient.AddPeer(context.Background(), v.NodeAddress()) - if err != nil { - return err - } +func (bc *blockchain) start(validators []Ethereum) error { + for _, v := range validators { + if err := v.Start(); err != nil { + return err + } + } + return nil +} + +func (bc *blockchain) stop(validators []Ethereum) error { + for _, v := range validators { + if err := v.Stop(); err != nil { + return err } } return nil From e9de7516c8045f8e8d725f3d54bbf5077b64cc81 Mon Sep 17 00:00:00 2001 From: Edwin Date: Mon, 21 Aug 2017 17:18:37 +0800 Subject: [PATCH 3/4] tests: implement TFU-02-01 adding validators --- tests/dynamic_test.go | 87 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 tests/dynamic_test.go diff --git a/tests/dynamic_test.go b/tests/dynamic_test.go new file mode 100644 index 00000000..14de8314 --- /dev/null +++ b/tests/dynamic_test.go @@ -0,0 +1,87 @@ +// 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 tests + +import ( + "context" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "github.com/getamis/istanbul-tools/container" +) + +var _ = Describe("Dynamic validators addition/removal testing", func() { + const ( + numberOfValidators = 4 + ) + var ( + blockchain container.Blockchain + ) + + BeforeEach(func() { + blockchain = container.NewBlockchain( + numberOfValidators, + container.ImageRepository("quay.io/amis/geth"), + container.ImageTag("istanbul_develop"), + container.DataDir("/data"), + container.WebSocket(), + container.WebSocketAddress("0.0.0.0"), + container.WebSocketAPI("admin,eth,net,web3,personal,miner,istanbul"), + container.WebSocketOrigin("*"), + container.NAT("any"), + container.NoDiscover(), + container.Etherbase("1a9afb711302c5f83b5902843d1c007a1a137632"), + container.Mine(), + container.Logging(true), + ) + + Expect(blockchain.Start()).To(BeNil()) + }) + + AfterEach(func() { + Expect(blockchain.Stop(false)).To(BeNil()) + blockchain.Finalize() + }) + + It("TFU-02-01 Add validators", func() { + testValidators := 3 + + By("Ensure that numbers of validator is equal than $numberOfValidators", func() { + for _, v := range blockchain.Validators() { + client := v.NewIstanbulClient() + validators, err := client.GetValidators(context.Background(), nil) + Expect(err).Should(BeNil()) + Expect(len(validators)).Should(BeNumerically("==", numberOfValidators)) + } + }) + + _, err := blockchain.AddValidators(testValidators) + Expect(err).Should(BeNil()) + + By("Ensure that consensus is working in 50 seconds", func() { + Expect(blockchain.EnsureConsensusWorking(blockchain.Validators(), 50*time.Second)).Should(BeNil()) + }) + for _, v := range blockchain.Validators() { + client := v.NewIstanbulClient() + validators, err := client.GetValidators(context.Background(), nil) + Expect(err).Should(BeNil()) + Expect(len(validators)).Should(BeNumerically("==", numberOfValidators+testValidators)) + } + }) +}) From e4fbae8320856eba59c51bf73a001bf884c44e26 Mon Sep 17 00:00:00 2001 From: Edwin Date: Tue, 22 Aug 2017 15:48:10 +0800 Subject: [PATCH 4/4] fix pr comments --- tests/dynamic_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/dynamic_test.go b/tests/dynamic_test.go index 14de8314..27bebe8a 100644 --- a/tests/dynamic_test.go +++ b/tests/dynamic_test.go @@ -59,7 +59,7 @@ var _ = Describe("Dynamic validators addition/removal testing", func() { blockchain.Finalize() }) - It("TFU-02-01 Add validators", func() { + It("TFS-02-01 Add validators", func() { testValidators := 3 By("Ensure that numbers of validator is equal than $numberOfValidators", func() {