From 10b2ec598f5afdd7a13f69aaf97d112c22333a25 Mon Sep 17 00:00:00 2001 From: Nguyen Ba Tam Date: Fri, 24 Aug 2018 15:00:51 +0700 Subject: [PATCH 1/7] split list blocks insert to chain by index (epoch - gas) --- cmd/tomo/main.go | 52 +-------------------- consensus/posv/posv.go | 19 ++++++++ core/blockchain.go | 101 ++++++++++++++++++++++++++++++++++++++--- eth/backend.go | 10 ---- 4 files changed, 115 insertions(+), 67 deletions(-) diff --git a/cmd/tomo/main.go b/cmd/tomo/main.go index 58d3bcb3cb..8b4bcb8f0d 100644 --- a/cmd/tomo/main.go +++ b/cmd/tomo/main.go @@ -25,13 +25,9 @@ import ( "time" "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/posv" "github.com/ethereum/go-ethereum/console" - validatorContract "github.com/ethereum/go-ethereum/contracts/validator/contract" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/ethclient" @@ -353,53 +349,7 @@ func startNode(ctx *cli.Context, stack *node.Node) { log.Info("Enabled staking node!!!") } case <-core.M1Ch: - log.Info("It's time to update new set of masternodes for the next epoch...") - // get masternodes information from smart contract - client, err := ethclient.Dial(stack.IPCEndpoint()) - if err != nil { - utils.Fatalf("Fail to connect IPC: %v", err) - } - addr := common.HexToAddress(common.MasternodeVotingSMC) - validator, err := validatorContract.NewTomoValidator(addr, client) - if err != nil { - utils.Fatalf("Fail to get validator smc: %v", err) - } - opts := new(bind.CallOpts) - candidates, err := validator.GetCandidates(opts) - if err != nil { - utils.Fatalf("Can't get list of masternode candidates: %v", err) - } - - var ms []posv.Masternode - for _, candidate := range candidates { - v, err := validator.GetCandidateCap(opts, candidate) - if err != nil { - log.Warn("Can't get cap of a masternode candidate. Will ignore him", "address", candidate, "error", err) - } - //TODO: smart contract shouldn't return "0x0000000000000000000000000000000000000000" - if candidate.String() != "0x0000000000000000000000000000000000000000" { - ms = append(ms, posv.Masternode{Address: candidate, Stake: v.String()}) - } - } - //// order by cap - //sort.Slice(ms, func(i, j int) bool { - // return ms[i].Stake > ms[j].Stake - //}) - log.Info("Ordered list of masternode candidates") - for _, m := range ms { - fmt.Printf("address: %s, stake: %s\n", m.Address.String(), m.Stake) - } - if len(ms) == 0 { - log.Info("No masternode candidates found. Keep the current masternodes set for the next epoch") - } else { - // update masternodes - log.Info("Updating new set of masternodes") - err = ethereum.UpdateMasternodes(ms) - if err != nil { - utils.Fatalf("Can't update masternodes: %v", err) - } - log.Info("Masternodes are ready for the next epoch") - } + ethereum.BlockChain().UpdateM1() } } }() diff --git a/consensus/posv/posv.go b/consensus/posv/posv.go index fb0d93e203..52cefb688e 100644 --- a/consensus/posv/posv.go +++ b/consensus/posv/posv.go @@ -306,6 +306,9 @@ func (c *Posv) verifyHeader(chain consensus.ChainReader, header *types.Header, p if !checkpoint && signersBytes != 0 { return errExtraSigners } + if checkpoint && signersBytes%common.AddressLength != 0 { + return errInvalidCheckpointSigners + } // Ensure that the mix digest is zero as we don't have fork protection currently if header.MixDigest != (common.Hash{}) { return errInvalidMixDigest @@ -351,6 +354,22 @@ func (c *Posv) verifyCascadingFields(chain consensus.ChainReader, header *types. if parent.Time.Uint64()+c.config.Period > header.Time.Uint64() { return ErrInvalidTimestamp } + // Retrieve the snapshot needed to verify this header and cache it + snap, err := c.snapshot(chain, number-1, header.ParentHash, parents) + if err != nil { + return err + } + // If the block is a checkpoint block, verify the signer list + if number%c.config.Epoch == 0 { + signers := make([]byte, len(snap.Signers)*common.AddressLength) + for i, signer := range snap.signers() { + copy(signers[i*common.AddressLength:], signer[:]) + } + extraSuffix := len(header.Extra) - extraSeal + if !bytes.Equal(header.Extra[extraVanity:extraSuffix], signers) { + return errInvalidCheckpointSigners + } + } // All basic checks passed, verify the seal and return return c.verifySeal(chain, header, parents) } diff --git a/core/blockchain.go b/core/blockchain.go index 87b40a18b6..c194ebc6bc 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -44,6 +44,9 @@ import ( "github.com/ethereum/go-ethereum/trie" "github.com/hashicorp/golang-lru" "gopkg.in/karalabe/cookiejar.v2/collections/prque" + "github.com/ethereum/go-ethereum/consensus/posv" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + contractValidator "github.com/ethereum/go-ethereum/contracts/validator/contract" ) var ( @@ -695,7 +698,7 @@ func (bc *BlockChain) procFutureBlocks() { type WriteStatus byte const ( - NonStatTy WriteStatus = iota + NonStatTy WriteStatus = iota CanonStatTy SideStatTy ) @@ -1007,9 +1010,41 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types. // // After insertion is done, all accumulated events will be fired. func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) { - n, events, logs, err := bc.insertChain(chain) - bc.PostChainEvents(events, logs) - return n, err + if bc.chainConfig != nil && bc.chainConfig.Posv != nil { + epoch := bc.chainConfig.Posv.Epoch + gap := bc.chainConfig.Posv.Gap + length := len(chain) + start := int(chain[0].NumberU64() % epoch) + end := int(epoch - gap - uint64(start)) + if (end < 0) { + end = end + int(epoch) + } + start = 0 + for { + if end >= length { + end = length - 1 + } + inserts := make([]*types.Block, end-start+1) + copy(inserts, chain[start:end+1]) + if len(inserts) > 0 { + n, events, logs, err := bc.insertChain(inserts) + bc.PostChainEvents(events, logs) + if err != nil { + return n, err + } + } + start = end + 1 + end = end + int(epoch) + if (start >= length) { + break + } + } + return 0, nil + } else { + n, events, logs, err := bc.insertChain(chain) + bc.PostChainEvents(events, logs) + return n, err + } } // insertChain will execute the actual chain insertion and event aggregation. The @@ -1189,14 +1224,14 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty stats.processed++ stats.usedGas += usedGas stats.report(chain, i, bc.stateCache.TrieDB().Size()) - if i == len(chain)-1 && bc.chainConfig.Posv != nil { + if bc.chainConfig.Posv != nil { // epoch block if (chain[i].NumberU64() % bc.chainConfig.Posv.Epoch) == 0 { CheckpointCh <- 1 } // prepare set of masternodes for the next epoch if (chain[i].NumberU64() % bc.chainConfig.Posv.Epoch) == (bc.chainConfig.Posv.Epoch - bc.chainConfig.Posv.Gap) { - M1Ch <- 1 + bc.UpdateM1() } } } @@ -1590,3 +1625,57 @@ func (bc *BlockChain) GetClient() (*ethclient.Client, error) { return bc.Client, nil } + +func (bc *BlockChain) UpdateM1() { + if bc.Config().Posv == nil { + return + } + engine := bc.Engine().(*posv.Posv) + log.Info("It's time to update new set of masternodes for the next epoch...") + // get masternodes information from smart contract + client, err := ethclient.Dial(bc.IPCEndpoint) + if err != nil { + log.Crit("Fail to connect IPC: %v", err) + } + addr := common.HexToAddress(common.MasternodeVotingSMC) + validator, err := contractValidator.NewTomoValidator(addr, client) + if err != nil { + log.Crit("Fail to get validator smc: %v", err) + } + opts := new(bind.CallOpts) + candidates, err := validator.GetCandidates(opts) + if err != nil { + log.Crit("Can't get list of masternode candidates: %v", err) + } + + var ms []posv.Masternode + for _, candidate := range candidates { + v, err := validator.GetCandidateCap(opts, candidate) + if err != nil { + log.Warn("Can't get cap of a masternode candidate. Will ignore him", "address", candidate, "error", err) + } + //TODO: smart contract shouldn't return "0x0000000000000000000000000000000000000000" + if candidate.String() != "0x0000000000000000000000000000000000000000" { + ms = append(ms, posv.Masternode{Address: candidate, Stake: v.String()}) + } + } + //// order by cap + //sort.Slice(ms, func(i, j int) bool { + // return ms[i].Stake > ms[j].Stake + //}) + log.Info("Ordered list of masternode candidates") + for _, m := range ms { + fmt.Printf("address: %s, stake: %s\n", m.Address.String(), m.Stake) + } + if len(ms) == 0 { + log.Info("No masternode candidates found. Keep the current masternodes set for the next epoch") + } else { + // update masternodes + log.Info("Updating new set of masternodes") + err = engine.UpdateMasternodes(bc, bc.CurrentHeader(), ms) + if err != nil { + log.Crit("Can't update masternodes: %v", err) + } + log.Info("Masternodes are ready for the next epoch") + } +} diff --git a/eth/backend.go b/eth/backend.go index dc4ac6acd7..67802e50f5 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -434,16 +434,6 @@ func (s *Ethereum) ValidateStaker() (bool, error) { return true, nil } -// Store new set of masternodes into local db -func (s *Ethereum) UpdateMasternodes(ms []posv.Masternode) error { - // get snapshot from local db - if s.chainConfig.Posv == nil { - return errors.New("not posv") - } - c := s.engine.(*posv.Posv) - return c.UpdateMasternodes(s.blockchain, s.blockchain.CurrentHeader(), ms) -} - func (s *Ethereum) StartStaking(local bool) error { eb, err := s.Etherbase() if err != nil { From e52c4bbcd547aec31592c5e47a871abaf876652f Mon Sep 17 00:00:00 2001 From: Tam Nguyen Date: Fri, 24 Aug 2018 16:50:34 +0700 Subject: [PATCH 2/7] Delete snapshot_test.go fix cycle import --- consensus/posv/snapshot_test.go | 404 -------------------------------- 1 file changed, 404 deletions(-) delete mode 100644 consensus/posv/snapshot_test.go diff --git a/consensus/posv/snapshot_test.go b/consensus/posv/snapshot_test.go deleted file mode 100644 index 8b4afe463b..0000000000 --- a/consensus/posv/snapshot_test.go +++ /dev/null @@ -1,404 +0,0 @@ -// Copyright (c) 2018 Tomochain -// -// This program 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. -// -// This program 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 this program. If not, see . - -package posv - -import ( - "bytes" - "crypto/ecdsa" - "math/big" - "testing" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/params" -) - -type testerVote struct { - signer string - voted string - auth bool -} - -// testerAccountPool is a pool to maintain currently active tester accounts, -// mapped from textual names used in the tests below to actual Ethereum private -// keys capable of signing transactions. -type testerAccountPool struct { - accounts map[string]*ecdsa.PrivateKey -} - -func newTesterAccountPool() *testerAccountPool { - return &testerAccountPool{ - accounts: make(map[string]*ecdsa.PrivateKey), - } -} - -func (ap *testerAccountPool) sign(header *types.Header, signer string) { - // Ensure we have a persistent key for the signer - if ap.accounts[signer] == nil { - ap.accounts[signer], _ = crypto.GenerateKey() - } - // Sign the header and embed the signature in extra data - sig, _ := crypto.Sign(sigHash(header).Bytes(), ap.accounts[signer]) - copy(header.Extra[len(header.Extra)-65:], sig) -} - -func (ap *testerAccountPool) address(account string) common.Address { - // Ensure we have a persistent key for the account - if ap.accounts[account] == nil { - ap.accounts[account], _ = crypto.GenerateKey() - } - // Resolve and return the Ethereum address - return crypto.PubkeyToAddress(ap.accounts[account].PublicKey) -} - -// testerChainReader implements consensus.ChainReader to access the genesis -// block. All other methods and requests will panic. -type testerChainReader struct { - db ethdb.Database -} - -func (r *testerChainReader) Config() *params.ChainConfig { return params.AllPosvProtocolChanges } -func (r *testerChainReader) CurrentHeader() *types.Header { panic("not supported") } -func (r *testerChainReader) GetHeader(common.Hash, uint64) *types.Header { panic("not supported") } -func (r *testerChainReader) GetBlock(common.Hash, uint64) *types.Block { panic("not supported") } -func (r *testerChainReader) GetHeaderByHash(common.Hash) *types.Header { panic("not supported") } -func (r *testerChainReader) GetHeaderByNumber(number uint64) *types.Header { - if number == 0 { - return core.GetHeader(r.db, core.GetCanonicalHash(r.db, 0), 0) - } - panic("not supported") -} - -// Tests that voting is evaluated correctly for various simple and complex scenarios. -func TestVoting(t *testing.T) { - // Define the various voting scenarios to test - tests := []struct { - epoch uint64 - signers []string - votes []testerVote - results []string - }{ - { - // Single signer, no votes cast - signers: []string{"A"}, - votes: []testerVote{{signer: "A"}}, - results: []string{"A"}, - }, { - // Single signer, voting to add two others (only accept first, second needs 2 votes) - signers: []string{"A"}, - votes: []testerVote{ - {signer: "A", voted: "B", auth: true}, - {signer: "B"}, - {signer: "A", voted: "C", auth: true}, - }, - results: []string{"A", "B"}, - }, { - // Two signers, voting to add three others (only accept first two, third needs 3 votes already) - signers: []string{"A", "B"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: true}, - {signer: "B", voted: "C", auth: true}, - {signer: "A", voted: "D", auth: true}, - {signer: "B", voted: "D", auth: true}, - {signer: "C"}, - {signer: "A", voted: "E", auth: true}, - {signer: "B", voted: "E", auth: true}, - }, - results: []string{"A", "B", "C", "D"}, - }, { - // Single signer, dropping itself (weird, but one less cornercase by explicitly allowing this) - signers: []string{"A"}, - votes: []testerVote{ - {signer: "A", voted: "A", auth: false}, - }, - results: []string{}, - }, { - // Two signers, actually needing mutual consent to drop either of them (not fulfilled) - signers: []string{"A", "B"}, - votes: []testerVote{ - {signer: "A", voted: "B", auth: false}, - }, - results: []string{"A", "B"}, - }, { - // Two signers, actually needing mutual consent to drop either of them (fulfilled) - signers: []string{"A", "B"}, - votes: []testerVote{ - {signer: "A", voted: "B", auth: false}, - {signer: "B", voted: "B", auth: false}, - }, - results: []string{"A"}, - }, { - // Three signers, two of them deciding to drop the third - signers: []string{"A", "B", "C"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: false}, - {signer: "B", voted: "C", auth: false}, - }, - results: []string{"A", "B"}, - }, { - // Four signers, consensus of two not being enough to drop anyone - signers: []string{"A", "B", "C", "D"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: false}, - {signer: "B", voted: "C", auth: false}, - }, - results: []string{"A", "B", "C", "D"}, - }, { - // Four signers, consensus of three already being enough to drop someone - signers: []string{"A", "B", "C", "D"}, - votes: []testerVote{ - {signer: "A", voted: "D", auth: false}, - {signer: "B", voted: "D", auth: false}, - {signer: "C", voted: "D", auth: false}, - }, - results: []string{"A", "B", "C"}, - }, { - // Authorizations are counted once per signer per target - signers: []string{"A", "B"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: true}, - {signer: "B"}, - {signer: "A", voted: "C", auth: true}, - {signer: "B"}, - {signer: "A", voted: "C", auth: true}, - }, - results: []string{"A", "B"}, - }, { - // Authorizing multiple accounts concurrently is permitted - signers: []string{"A", "B"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: true}, - {signer: "B"}, - {signer: "A", voted: "D", auth: true}, - {signer: "B"}, - {signer: "A"}, - {signer: "B", voted: "D", auth: true}, - {signer: "A"}, - {signer: "B", voted: "C", auth: true}, - }, - results: []string{"A", "B", "C", "D"}, - }, { - // Deauthorizations are counted once per signer per target - signers: []string{"A", "B"}, - votes: []testerVote{ - {signer: "A", voted: "B", auth: false}, - {signer: "B"}, - {signer: "A", voted: "B", auth: false}, - {signer: "B"}, - {signer: "A", voted: "B", auth: false}, - }, - results: []string{"A", "B"}, - }, { - // Deauthorizing multiple accounts concurrently is permitted - signers: []string{"A", "B", "C", "D"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: false}, - {signer: "B"}, - {signer: "C"}, - {signer: "A", voted: "D", auth: false}, - {signer: "B"}, - {signer: "C"}, - {signer: "A"}, - {signer: "B", voted: "D", auth: false}, - {signer: "C", voted: "D", auth: false}, - {signer: "A"}, - {signer: "B", voted: "C", auth: false}, - }, - results: []string{"A", "B"}, - }, { - // Votes from deauthorized signers are discarded immediately (deauth votes) - signers: []string{"A", "B", "C"}, - votes: []testerVote{ - {signer: "C", voted: "B", auth: false}, - {signer: "A", voted: "C", auth: false}, - {signer: "B", voted: "C", auth: false}, - {signer: "A", voted: "B", auth: false}, - }, - results: []string{"A", "B"}, - }, { - // Votes from deauthorized signers are discarded immediately (auth votes) - signers: []string{"A", "B", "C"}, - votes: []testerVote{ - {signer: "C", voted: "B", auth: false}, - {signer: "A", voted: "C", auth: false}, - {signer: "B", voted: "C", auth: false}, - {signer: "A", voted: "B", auth: false}, - }, - results: []string{"A", "B"}, - }, { - // Cascading changes are not allowed, only the account being voted on may change - signers: []string{"A", "B", "C", "D"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: false}, - {signer: "B"}, - {signer: "C"}, - {signer: "A", voted: "D", auth: false}, - {signer: "B", voted: "C", auth: false}, - {signer: "C"}, - {signer: "A"}, - {signer: "B", voted: "D", auth: false}, - {signer: "C", voted: "D", auth: false}, - }, - results: []string{"A", "B", "C"}, - }, { - // Changes reaching consensus out of bounds (via a deauth) execute on touch - signers: []string{"A", "B", "C", "D"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: false}, - {signer: "B"}, - {signer: "C"}, - {signer: "A", voted: "D", auth: false}, - {signer: "B", voted: "C", auth: false}, - {signer: "C"}, - {signer: "A"}, - {signer: "B", voted: "D", auth: false}, - {signer: "C", voted: "D", auth: false}, - {signer: "A"}, - {signer: "C", voted: "C", auth: true}, - }, - results: []string{"A", "B"}, - }, { - // Changes reaching consensus out of bounds (via a deauth) may go out of consensus on first touch - signers: []string{"A", "B", "C", "D"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: false}, - {signer: "B"}, - {signer: "C"}, - {signer: "A", voted: "D", auth: false}, - {signer: "B", voted: "C", auth: false}, - {signer: "C"}, - {signer: "A"}, - {signer: "B", voted: "D", auth: false}, - {signer: "C", voted: "D", auth: false}, - {signer: "A"}, - {signer: "B", voted: "C", auth: true}, - }, - results: []string{"A", "B", "C"}, - }, { - // Ensure that pending votes don't survive authorization status changes. This - // corner case can only appear if a signer is quickly added, removed and then - // readded (or the inverse), while one of the original voters dropped. If a - // past vote is left cached in the system somewhere, this will interfere with - // the final signer outcome. - signers: []string{"A", "B", "C", "D", "E"}, - votes: []testerVote{ - {signer: "A", voted: "F", auth: true}, // Authorize F, 3 votes needed - {signer: "B", voted: "F", auth: true}, - {signer: "C", voted: "F", auth: true}, - {signer: "D", voted: "F", auth: false}, // Deauthorize F, 4 votes needed (leave A's previous vote "unchanged") - {signer: "E", voted: "F", auth: false}, - {signer: "B", voted: "F", auth: false}, - {signer: "C", voted: "F", auth: false}, - {signer: "D", voted: "F", auth: true}, // Almost authorize F, 2/3 votes needed - {signer: "E", voted: "F", auth: true}, - {signer: "B", voted: "A", auth: false}, // Deauthorize A, 3 votes needed - {signer: "C", voted: "A", auth: false}, - {signer: "D", voted: "A", auth: false}, - {signer: "B", voted: "F", auth: true}, // Finish authorizing F, 3/3 votes needed - }, - results: []string{"B", "C", "D", "E", "F"}, - }, { - // Epoch transitions reset all votes to allow chain checkpointing - epoch: 3, - signers: []string{"A", "B"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: true}, - {signer: "B"}, - {signer: "A"}, // Checkpoint block, (don't vote here, it's validated outside of snapshots) - {signer: "B", voted: "C", auth: true}, - }, - results: []string{"A", "B"}, - }, - } - // Run through the scenarios and test them - for i, tt := range tests { - // Create the account pool and generate the initial set of signers - accounts := newTesterAccountPool() - - signers := make([]common.Address, len(tt.signers)) - for j, signer := range tt.signers { - signers[j] = accounts.address(signer) - } - for j := 0; j < len(signers); j++ { - for k := j + 1; k < len(signers); k++ { - if bytes.Compare(signers[j][:], signers[k][:]) > 0 { - signers[j], signers[k] = signers[k], signers[j] - } - } - } - // Create the genesis block with the initial set of signers - genesis := &core.Genesis{ - ExtraData: make([]byte, extraVanity+common.AddressLength*len(signers)+extraSeal), - } - for j, signer := range signers { - copy(genesis.ExtraData[extraVanity+j*common.AddressLength:], signer[:]) - } - // Create a pristine blockchain with the genesis injected - db, _ := ethdb.NewMemDatabase() - genesis.Commit(db) - - // Assemble a chain of headers from the cast votes - headers := make([]*types.Header, len(tt.votes)) - for j, vote := range tt.votes { - headers[j] = &types.Header{ - Number: big.NewInt(int64(j) + 1), - Time: big.NewInt(int64(j) * int64(blockPeriod)), - Coinbase: accounts.address(vote.voted), - Extra: make([]byte, extraVanity+extraSeal), - } - if j > 0 { - headers[j].ParentHash = headers[j-1].Hash() - } - if vote.auth { - copy(headers[j].Nonce[:], nonceAuthVote) - } - accounts.sign(headers[j], vote.signer) - } - // Pass all the headers through posv and ensure tallying succeeds - head := headers[len(headers)-1] - - snap, err := New(¶ms.PosvConfig{Epoch: tt.epoch}, db).snapshot(&testerChainReader{db: db}, head.Number.Uint64(), head.Hash(), headers) - if err != nil { - t.Errorf("test %d: failed to create voting snapshot: %v", i, err) - continue - } - // Verify the final list of signers against the expected ones - signers = make([]common.Address, len(tt.results)) - for j, signer := range tt.results { - signers[j] = accounts.address(signer) - } - for j := 0; j < len(signers); j++ { - for k := j + 1; k < len(signers); k++ { - if bytes.Compare(signers[j][:], signers[k][:]) > 0 { - signers[j], signers[k] = signers[k], signers[j] - } - } - } - result := snap.signers() - if len(result) != len(signers) { - t.Errorf("test %d: signers mismatch: have %x, want %x", i, result, signers) - continue - } - for j := 0; j < len(result); j++ { - if !bytes.Equal(result[j][:], signers[j][:]) { - t.Errorf("test %d, signer %d: signer mismatch: have %x, want %x", i, j, result[j], signers[j]) - } - } - } -} From 8818e652d2f44f19e61e8403e44185820d32c0e3 Mon Sep 17 00:00:00 2001 From: Tam Nguyen Date: Fri, 24 Aug 2018 16:50:53 +0700 Subject: [PATCH 3/7] Delete snapshot_test.go fix cycle import --- consensus/clique/snapshot_test.go | 405 ------------------------------ 1 file changed, 405 deletions(-) delete mode 100644 consensus/clique/snapshot_test.go diff --git a/consensus/clique/snapshot_test.go b/consensus/clique/snapshot_test.go deleted file mode 100644 index 8b51e6e094..0000000000 --- a/consensus/clique/snapshot_test.go +++ /dev/null @@ -1,405 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// 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 clique - -import ( - "bytes" - "crypto/ecdsa" - "math/big" - "testing" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/params" -) - -type testerVote struct { - signer string - voted string - auth bool -} - -// testerAccountPool is a pool to maintain currently active tester accounts, -// mapped from textual names used in the tests below to actual Ethereum private -// keys capable of signing transactions. -type testerAccountPool struct { - accounts map[string]*ecdsa.PrivateKey -} - -func newTesterAccountPool() *testerAccountPool { - return &testerAccountPool{ - accounts: make(map[string]*ecdsa.PrivateKey), - } -} - -func (ap *testerAccountPool) sign(header *types.Header, signer string) { - // Ensure we have a persistent key for the signer - if ap.accounts[signer] == nil { - ap.accounts[signer], _ = crypto.GenerateKey() - } - // Sign the header and embed the signature in extra data - sig, _ := crypto.Sign(sigHash(header).Bytes(), ap.accounts[signer]) - copy(header.Extra[len(header.Extra)-65:], sig) -} - -func (ap *testerAccountPool) address(account string) common.Address { - // Ensure we have a persistent key for the account - if ap.accounts[account] == nil { - ap.accounts[account], _ = crypto.GenerateKey() - } - // Resolve and return the Ethereum address - return crypto.PubkeyToAddress(ap.accounts[account].PublicKey) -} - -// testerChainReader implements consensus.ChainReader to access the genesis -// block. All other methods and requests will panic. -type testerChainReader struct { - db ethdb.Database -} - -func (r *testerChainReader) Config() *params.ChainConfig { return params.AllCliqueProtocolChanges } -func (r *testerChainReader) CurrentHeader() *types.Header { panic("not supported") } -func (r *testerChainReader) GetHeader(common.Hash, uint64) *types.Header { panic("not supported") } -func (r *testerChainReader) GetBlock(common.Hash, uint64) *types.Block { panic("not supported") } -func (r *testerChainReader) GetHeaderByHash(common.Hash) *types.Header { panic("not supported") } -func (r *testerChainReader) GetHeaderByNumber(number uint64) *types.Header { - if number == 0 { - return core.GetHeader(r.db, core.GetCanonicalHash(r.db, 0), 0) - } - panic("not supported") -} - -// Tests that voting is evaluated correctly for various simple and complex scenarios. -func TestVoting(t *testing.T) { - // Define the various voting scenarios to test - tests := []struct { - epoch uint64 - signers []string - votes []testerVote - results []string - }{ - { - // Single signer, no votes cast - signers: []string{"A"}, - votes: []testerVote{{signer: "A"}}, - results: []string{"A"}, - }, { - // Single signer, voting to add two others (only accept first, second needs 2 votes) - signers: []string{"A"}, - votes: []testerVote{ - {signer: "A", voted: "B", auth: true}, - {signer: "B"}, - {signer: "A", voted: "C", auth: true}, - }, - results: []string{"A", "B"}, - }, { - // Two signers, voting to add three others (only accept first two, third needs 3 votes already) - signers: []string{"A", "B"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: true}, - {signer: "B", voted: "C", auth: true}, - {signer: "A", voted: "D", auth: true}, - {signer: "B", voted: "D", auth: true}, - {signer: "C"}, - {signer: "A", voted: "E", auth: true}, - {signer: "B", voted: "E", auth: true}, - }, - results: []string{"A", "B", "C", "D"}, - }, { - // Single signer, dropping itself (weird, but one less cornercase by explicitly allowing this) - signers: []string{"A"}, - votes: []testerVote{ - {signer: "A", voted: "A", auth: false}, - }, - results: []string{}, - }, { - // Two signers, actually needing mutual consent to drop either of them (not fulfilled) - signers: []string{"A", "B"}, - votes: []testerVote{ - {signer: "A", voted: "B", auth: false}, - }, - results: []string{"A", "B"}, - }, { - // Two signers, actually needing mutual consent to drop either of them (fulfilled) - signers: []string{"A", "B"}, - votes: []testerVote{ - {signer: "A", voted: "B", auth: false}, - {signer: "B", voted: "B", auth: false}, - }, - results: []string{"A"}, - }, { - // Three signers, two of them deciding to drop the third - signers: []string{"A", "B", "C"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: false}, - {signer: "B", voted: "C", auth: false}, - }, - results: []string{"A", "B"}, - }, { - // Four signers, consensus of two not being enough to drop anyone - signers: []string{"A", "B", "C", "D"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: false}, - {signer: "B", voted: "C", auth: false}, - }, - results: []string{"A", "B", "C", "D"}, - }, { - // Four signers, consensus of three already being enough to drop someone - signers: []string{"A", "B", "C", "D"}, - votes: []testerVote{ - {signer: "A", voted: "D", auth: false}, - {signer: "B", voted: "D", auth: false}, - {signer: "C", voted: "D", auth: false}, - }, - results: []string{"A", "B", "C"}, - }, { - // Authorizations are counted once per signer per target - signers: []string{"A", "B"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: true}, - {signer: "B"}, - {signer: "A", voted: "C", auth: true}, - {signer: "B"}, - {signer: "A", voted: "C", auth: true}, - }, - results: []string{"A", "B"}, - }, { - // Authorizing multiple accounts concurrently is permitted - signers: []string{"A", "B"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: true}, - {signer: "B"}, - {signer: "A", voted: "D", auth: true}, - {signer: "B"}, - {signer: "A"}, - {signer: "B", voted: "D", auth: true}, - {signer: "A"}, - {signer: "B", voted: "C", auth: true}, - }, - results: []string{"A", "B", "C", "D"}, - }, { - // Deauthorizations are counted once per signer per target - signers: []string{"A", "B"}, - votes: []testerVote{ - {signer: "A", voted: "B", auth: false}, - {signer: "B"}, - {signer: "A", voted: "B", auth: false}, - {signer: "B"}, - {signer: "A", voted: "B", auth: false}, - }, - results: []string{"A", "B"}, - }, { - // Deauthorizing multiple accounts concurrently is permitted - signers: []string{"A", "B", "C", "D"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: false}, - {signer: "B"}, - {signer: "C"}, - {signer: "A", voted: "D", auth: false}, - {signer: "B"}, - {signer: "C"}, - {signer: "A"}, - {signer: "B", voted: "D", auth: false}, - {signer: "C", voted: "D", auth: false}, - {signer: "A"}, - {signer: "B", voted: "C", auth: false}, - }, - results: []string{"A", "B"}, - }, { - // Votes from deauthorized signers are discarded immediately (deauth votes) - signers: []string{"A", "B", "C"}, - votes: []testerVote{ - {signer: "C", voted: "B", auth: false}, - {signer: "A", voted: "C", auth: false}, - {signer: "B", voted: "C", auth: false}, - {signer: "A", voted: "B", auth: false}, - }, - results: []string{"A", "B"}, - }, { - // Votes from deauthorized signers are discarded immediately (auth votes) - signers: []string{"A", "B", "C"}, - votes: []testerVote{ - {signer: "C", voted: "B", auth: false}, - {signer: "A", voted: "C", auth: false}, - {signer: "B", voted: "C", auth: false}, - {signer: "A", voted: "B", auth: false}, - }, - results: []string{"A", "B"}, - }, { - // Cascading changes are not allowed, only the account being voted on may change - signers: []string{"A", "B", "C", "D"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: false}, - {signer: "B"}, - {signer: "C"}, - {signer: "A", voted: "D", auth: false}, - {signer: "B", voted: "C", auth: false}, - {signer: "C"}, - {signer: "A"}, - {signer: "B", voted: "D", auth: false}, - {signer: "C", voted: "D", auth: false}, - }, - results: []string{"A", "B", "C"}, - }, { - // Changes reaching consensus out of bounds (via a deauth) execute on touch - signers: []string{"A", "B", "C", "D"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: false}, - {signer: "B"}, - {signer: "C"}, - {signer: "A", voted: "D", auth: false}, - {signer: "B", voted: "C", auth: false}, - {signer: "C"}, - {signer: "A"}, - {signer: "B", voted: "D", auth: false}, - {signer: "C", voted: "D", auth: false}, - {signer: "A"}, - {signer: "C", voted: "C", auth: true}, - }, - results: []string{"A", "B"}, - }, { - // Changes reaching consensus out of bounds (via a deauth) may go out of consensus on first touch - signers: []string{"A", "B", "C", "D"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: false}, - {signer: "B"}, - {signer: "C"}, - {signer: "A", voted: "D", auth: false}, - {signer: "B", voted: "C", auth: false}, - {signer: "C"}, - {signer: "A"}, - {signer: "B", voted: "D", auth: false}, - {signer: "C", voted: "D", auth: false}, - {signer: "A"}, - {signer: "B", voted: "C", auth: true}, - }, - results: []string{"A", "B", "C"}, - }, { - // Ensure that pending votes don't survive authorization status changes. This - // corner case can only appear if a signer is quickly added, removed and then - // readded (or the inverse), while one of the original voters dropped. If a - // past vote is left cached in the system somewhere, this will interfere with - // the final signer outcome. - signers: []string{"A", "B", "C", "D", "E"}, - votes: []testerVote{ - {signer: "A", voted: "F", auth: true}, // Authorize F, 3 votes needed - {signer: "B", voted: "F", auth: true}, - {signer: "C", voted: "F", auth: true}, - {signer: "D", voted: "F", auth: false}, // Deauthorize F, 4 votes needed (leave A's previous vote "unchanged") - {signer: "E", voted: "F", auth: false}, - {signer: "B", voted: "F", auth: false}, - {signer: "C", voted: "F", auth: false}, - {signer: "D", voted: "F", auth: true}, // Almost authorize F, 2/3 votes needed - {signer: "E", voted: "F", auth: true}, - {signer: "B", voted: "A", auth: false}, // Deauthorize A, 3 votes needed - {signer: "C", voted: "A", auth: false}, - {signer: "D", voted: "A", auth: false}, - {signer: "B", voted: "F", auth: true}, // Finish authorizing F, 3/3 votes needed - }, - results: []string{"B", "C", "D", "E", "F"}, - }, { - // Epoch transitions reset all votes to allow chain checkpointing - epoch: 3, - signers: []string{"A", "B"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: true}, - {signer: "B"}, - {signer: "A"}, // Checkpoint block, (don't vote here, it's validated outside of snapshots) - {signer: "B", voted: "C", auth: true}, - }, - results: []string{"A", "B"}, - }, - } - // Run through the scenarios and test them - for i, tt := range tests { - // Create the account pool and generate the initial set of signers - accounts := newTesterAccountPool() - - signers := make([]common.Address, len(tt.signers)) - for j, signer := range tt.signers { - signers[j] = accounts.address(signer) - } - for j := 0; j < len(signers); j++ { - for k := j + 1; k < len(signers); k++ { - if bytes.Compare(signers[j][:], signers[k][:]) > 0 { - signers[j], signers[k] = signers[k], signers[j] - } - } - } - // Create the genesis block with the initial set of signers - genesis := &core.Genesis{ - ExtraData: make([]byte, extraVanity+common.AddressLength*len(signers)+extraSeal), - } - for j, signer := range signers { - copy(genesis.ExtraData[extraVanity+j*common.AddressLength:], signer[:]) - } - // Create a pristine blockchain with the genesis injected - db, _ := ethdb.NewMemDatabase() - genesis.Commit(db) - - // Assemble a chain of headers from the cast votes - headers := make([]*types.Header, len(tt.votes)) - for j, vote := range tt.votes { - headers[j] = &types.Header{ - Number: big.NewInt(int64(j) + 1), - Time: big.NewInt(int64(j) * int64(blockPeriod)), - Coinbase: accounts.address(vote.voted), - Extra: make([]byte, extraVanity+extraSeal), - } - if j > 0 { - headers[j].ParentHash = headers[j-1].Hash() - } - if vote.auth { - copy(headers[j].Nonce[:], nonceAuthVote) - } - accounts.sign(headers[j], vote.signer) - } - // Pass all the headers through clique and ensure tallying succeeds - head := headers[len(headers)-1] - - snap, err := New(¶ms.CliqueConfig{Epoch: tt.epoch}, db).snapshot(&testerChainReader{db: db}, head.Number.Uint64(), head.Hash(), headers) - if err != nil { - t.Errorf("test %d: failed to create voting snapshot: %v", i, err) - continue - } - // Verify the final list of signers against the expected ones - signers = make([]common.Address, len(tt.results)) - for j, signer := range tt.results { - signers[j] = accounts.address(signer) - } - for j := 0; j < len(signers); j++ { - for k := j + 1; k < len(signers); k++ { - if bytes.Compare(signers[j][:], signers[k][:]) > 0 { - signers[j], signers[k] = signers[k], signers[j] - } - } - } - result := snap.signers() - if len(result) != len(signers) { - t.Errorf("test %d: signers mismatch: have %x, want %x", i, result, signers) - continue - } - for j := 0; j < len(result); j++ { - if !bytes.Equal(result[j][:], signers[j][:]) { - t.Errorf("test %d, signer %d: signer mismatch: have %x, want %x", i, j, result[j], signers[j]) - } - } - } -} From e6ae9f13602188a49f58f8a310b3e87d7f9fd104 Mon Sep 17 00:00:00 2001 From: Nguyen Ba Tam Date: Fri, 24 Aug 2018 17:30:50 +0700 Subject: [PATCH 4/7] update process M1 synchronie only for downloader , not for fetcher --- core/blockchain.go | 42 ++++--------------------------- eth/downloader/downloader.go | 40 +++++++++++++++++++++++++---- eth/downloader/downloader_test.go | 4 +++ 3 files changed, 44 insertions(+), 42 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index c194ebc6bc..84652aa042 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1010,41 +1010,9 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types. // // After insertion is done, all accumulated events will be fired. func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) { - if bc.chainConfig != nil && bc.chainConfig.Posv != nil { - epoch := bc.chainConfig.Posv.Epoch - gap := bc.chainConfig.Posv.Gap - length := len(chain) - start := int(chain[0].NumberU64() % epoch) - end := int(epoch - gap - uint64(start)) - if (end < 0) { - end = end + int(epoch) - } - start = 0 - for { - if end >= length { - end = length - 1 - } - inserts := make([]*types.Block, end-start+1) - copy(inserts, chain[start:end+1]) - if len(inserts) > 0 { - n, events, logs, err := bc.insertChain(inserts) - bc.PostChainEvents(events, logs) - if err != nil { - return n, err - } - } - start = end + 1 - end = end + int(epoch) - if (start >= length) { - break - } - } - return 0, nil - } else { - n, events, logs, err := bc.insertChain(chain) - bc.PostChainEvents(events, logs) - return n, err - } + n, events, logs, err := bc.insertChain(chain) + bc.PostChainEvents(events, logs) + return n, err } // insertChain will execute the actual chain insertion and event aggregation. The @@ -1224,14 +1192,14 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty stats.processed++ stats.usedGas += usedGas stats.report(chain, i, bc.stateCache.TrieDB().Size()) - if bc.chainConfig.Posv != nil { + if i == len(chain)-1 && bc.chainConfig.Posv != nil { // epoch block if (chain[i].NumberU64() % bc.chainConfig.Posv.Epoch) == 0 { CheckpointCh <- 1 } // prepare set of masternodes for the next epoch if (chain[i].NumberU64() % bc.chainConfig.Posv.Epoch) == (bc.chainConfig.Posv.Epoch - bc.chainConfig.Posv.Gap) { - bc.UpdateM1() + M1Ch <- 1 } } } diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 90bbba65a5..89a274c0de 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -25,7 +25,7 @@ import ( "sync/atomic" "time" - ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" @@ -172,6 +172,8 @@ type LightChain interface { // BlockChain encapsulates functions required to sync a (full or fast) blockchain. type BlockChain interface { + Config() *params.ChainConfig + UpdateM1() LightChain // HasBlock verifies a block's presence in the local chain. @@ -1322,11 +1324,38 @@ func (d *Downloader) processFullSyncContent() error { if len(results) == 0 { return nil } - if d.chainInsertHook != nil { - d.chainInsertHook(results) + epoch := d.blockchain.Config().Posv.Epoch + gap := d.blockchain.Config().Posv.Gap + length := len(results) + start := int(results[0].Header.Number.Uint64() % epoch) + end := int(epoch - gap - uint64(start)) + if (end < 0) { + end = end + int(epoch) } - if err := d.importBlockResults(results); err != nil { - return err + start = 0 + for { + if end >= length { + end = length - 1 + } + inserts := make([]*fetchResult, end-start+1) + copy(inserts, results[start:end+1]) + if len(inserts) > 0 { + if d.chainInsertHook != nil { + d.chainInsertHook(inserts) + } + if err := d.importBlockResults(inserts); err != nil { + return err + } + // prepare set of masternodes for the next epoch + if (inserts[len(inserts)-1].Header.Number.Uint64() % epoch) == (epoch - gap) { + d.blockchain.UpdateM1() + } + } + start = end + 1 + end = end + int(epoch) + if (start >= length) { + break + } } } } @@ -1355,6 +1384,7 @@ func (d *Downloader) importBlockResults(results []*fetchResult) error { log.Debug("Downloaded item processing failed", "number", results[index].Header.Number, "hash", results[index].Header.Hash(), "err", err) return errInvalidChain } + return nil } diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index 56ce1aebd6..35dcdbc782 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -456,6 +456,10 @@ func (dl *downloadTester) dropPeer(id string) { dl.downloader.UnregisterPeer(id) } +// Config retrieves the blockchain's chain configuration. +func (dl *downloadTester) Config() *params.ChainConfig { return dl.downloader.blockchain.Config() } +func (dl *downloadTester) UpdateM1() { dl.downloader.blockchain.UpdateM1() } + type downloadTesterPeer struct { dl *downloadTester id string From f1645018855cf8f1205a3cede38ed432d759bcd9 Mon Sep 17 00:00:00 2001 From: Nguyen Ba Tam Date: Fri, 24 Aug 2018 17:36:29 +0700 Subject: [PATCH 5/7] format code again --- core/blockchain.go | 8 ++++---- eth/downloader/downloader.go | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 84652aa042..ad312ed3d7 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -27,9 +27,12 @@ import ( "sync/atomic" "time" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/posv" + contractValidator "github.com/ethereum/go-ethereum/contracts/validator/contract" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -44,9 +47,6 @@ import ( "github.com/ethereum/go-ethereum/trie" "github.com/hashicorp/golang-lru" "gopkg.in/karalabe/cookiejar.v2/collections/prque" - "github.com/ethereum/go-ethereum/consensus/posv" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - contractValidator "github.com/ethereum/go-ethereum/contracts/validator/contract" ) var ( @@ -698,7 +698,7 @@ func (bc *BlockChain) procFutureBlocks() { type WriteStatus byte const ( - NonStatTy WriteStatus = iota + NonStatTy WriteStatus = iota CanonStatTy SideStatTy ) diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 89a274c0de..77390495b0 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -1329,7 +1329,7 @@ func (d *Downloader) processFullSyncContent() error { length := len(results) start := int(results[0].Header.Number.Uint64() % epoch) end := int(epoch - gap - uint64(start)) - if (end < 0) { + if end < 0 { end = end + int(epoch) } start = 0 @@ -1353,7 +1353,7 @@ func (d *Downloader) processFullSyncContent() error { } start = end + 1 end = end + int(epoch) - if (start >= length) { + if start >= length { break } } From 9882b0803493b9a844e7d66af99348a345b481dd Mon Sep 17 00:00:00 2001 From: Nguyen Ba Tam Date: Mon, 27 Aug 2018 10:28:15 +0700 Subject: [PATCH 6/7] fix test download and recover some download config & test --- eth/downloader/downloader.go | 71 ++++--- eth/downloader/downloader_test.go | 318 +++++++++++++++--------------- 2 files changed, 199 insertions(+), 190 deletions(-) diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 77390495b0..d4ab38a740 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -38,8 +38,8 @@ import ( var ( MaxHashFetch = 512 // Amount of hashes to be fetched per retrieval request - MaxBlockFetch = 900 // Amount of blocks to be fetched per retrieval request - MaxHeaderFetch = 900 // Amount of block headers to be fetched per retrieval request + MaxBlockFetch = 128 // Amount of blocks to be fetched per retrieval request + MaxHeaderFetch = 192 // Amount of block headers to be fetched per retrieval request MaxSkeletonSize = 128 // Number of header fetches to need for a skeleton assembly MaxBodyFetch = 128 // Amount of block bodies to be fetched per retrieval request MaxReceiptFetch = 256 // Amount of transaction receipts to allow fetching per request @@ -56,9 +56,9 @@ var ( qosConfidenceCap = 10 // Number of peers above which not to modify RTT confidence qosTuningImpact = 0.25 // Impact that a new tuning target has on the previous value - maxQueuedHeaders = 900 // [eth/62] Maximum number of headers to queue for import (DOS protection) - maxHeadersProcess = 900 // Number of header download results to import at once into the chain - maxResultsProcess = 2048 // Number of content download results to import at once into the chain + maxQueuedHeaders = 32 * 1024 // [eth/62] Maximum number of headers to queue for import (DOS protection) + maxHeadersProcess = 2048 // Number of header download results to import at once into the chain + maxResultsProcess = 2048 // Number of content download results to import at once into the chain fsHeaderCheckFrequency = 100 // Verification frequency of the downloaded headers during fast sync fsHeaderSafetyNet = 2048 // Number of headers to discard in case a chain violation is detected @@ -1324,37 +1324,46 @@ func (d *Downloader) processFullSyncContent() error { if len(results) == 0 { return nil } - epoch := d.blockchain.Config().Posv.Epoch - gap := d.blockchain.Config().Posv.Gap - length := len(results) - start := int(results[0].Header.Number.Uint64() % epoch) - end := int(epoch - gap - uint64(start)) - if end < 0 { - end = end + int(epoch) - } - start = 0 - for { - if end >= length { - end = length - 1 + if d.blockchain.Config() != nil && d.blockchain.Config().Posv != nil { + epoch := d.blockchain.Config().Posv.Epoch + gap := d.blockchain.Config().Posv.Gap + length := len(results) + start := int(results[0].Header.Number.Uint64() % epoch) + end := int(epoch - gap - uint64(start)) + if end < 0 { + end = end + int(epoch) } - inserts := make([]*fetchResult, end-start+1) - copy(inserts, results[start:end+1]) - if len(inserts) > 0 { - if d.chainInsertHook != nil { - d.chainInsertHook(inserts) + start = 0 + for { + if end >= length { + end = length - 1 } - if err := d.importBlockResults(inserts); err != nil { - return err + inserts := make([]*fetchResult, end-start+1) + copy(inserts, results[start:end+1]) + if len(inserts) > 0 { + if d.chainInsertHook != nil { + d.chainInsertHook(inserts) + } + if err := d.importBlockResults(inserts); err != nil { + return err + } + // prepare set of masternodes for the next epoch + if (inserts[len(inserts)-1].Header.Number.Uint64() % epoch) == (epoch - gap) { + d.blockchain.UpdateM1() + } } - // prepare set of masternodes for the next epoch - if (inserts[len(inserts)-1].Header.Number.Uint64() % epoch) == (epoch - gap) { - d.blockchain.UpdateM1() + start = end + 1 + end = end + int(epoch) + if start >= length { + break } } - start = end + 1 - end = end + int(epoch) - if start >= length { - break + } else { + if d.chainInsertHook != nil { + d.chainInsertHook(results) + } + if err := d.importBlockResults(results); err != nil { + return err } } } diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index 35dcdbc782..1e1e64afdc 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -457,8 +457,8 @@ func (dl *downloadTester) dropPeer(id string) { } // Config retrieves the blockchain's chain configuration. -func (dl *downloadTester) Config() *params.ChainConfig { return dl.downloader.blockchain.Config() } -func (dl *downloadTester) UpdateM1() { dl.downloader.blockchain.UpdateM1() } +func (dl *downloadTester) Config() *params.ChainConfig { return params.TestChainConfig } +func (dl *downloadTester) UpdateM1() {} type downloadTesterPeer struct { dl *downloadTester @@ -1190,92 +1190,92 @@ func testShiftedHeaderAttack(t *testing.T, protocol int, mode SyncMode) { // Tests that upon detecting an invalid header, the recent ones are rolled back // for various failure scenarios. Afterwards a full sync is attempted to make // sure no state was corrupted. -//func TestInvalidHeaderRollback63Fast(t *testing.T) { testInvalidHeaderRollback(t, 63, FastSync) } -//func TestInvalidHeaderRollback64Fast(t *testing.T) { testInvalidHeaderRollback(t, 64, FastSync) } -//func TestInvalidHeaderRollback64Light(t *testing.T) { testInvalidHeaderRollback(t, 64, LightSync) } -// -//func testInvalidHeaderRollback(t *testing.T, protocol int, mode SyncMode) { -// t.Parallel() -// -// tester := newTester() -// defer tester.terminate() -// -// // Create a small enough block chain to download -// targetBlocks := 3*fsHeaderSafetyNet + 256 + fsMinFullBlocks -// hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false) -// -// // Attempt to sync with an attacker that feeds junk during the fast sync phase. -// // This should result in the last fsHeaderSafetyNet headers being rolled back. -// tester.newPeer("fast-attack", protocol, hashes, headers, blocks, receipts) -// missing := fsHeaderSafetyNet + MaxHeaderFetch + 1 -// delete(tester.peerHeaders["fast-attack"], hashes[len(hashes)-missing]) -// -// if err := tester.sync("fast-attack", nil, mode); err == nil { -// t.Fatalf("succeeded fast attacker synchronisation") -// } -// if head := tester.CurrentHeader().Number.Int64(); int(head) > MaxHeaderFetch { -// t.Errorf("rollback head mismatch: have %v, want at most %v", head, MaxHeaderFetch) -// } -// // Attempt to sync with an attacker that feeds junk during the block import phase. -// // This should result in both the last fsHeaderSafetyNet number of headers being -// // rolled back, and also the pivot point being reverted to a non-block status. -// tester.newPeer("block-attack", protocol, hashes, headers, blocks, receipts) -// missing = 3*fsHeaderSafetyNet + MaxHeaderFetch + 1 -// delete(tester.peerHeaders["fast-attack"], hashes[len(hashes)-missing]) // Make sure the fast-attacker doesn't fill in -// delete(tester.peerHeaders["block-attack"], hashes[len(hashes)-missing]) -// -// if err := tester.sync("block-attack", nil, mode); err == nil { -// t.Fatalf("succeeded block attacker synchronisation") -// } -// if head := tester.CurrentHeader().Number.Int64(); int(head) > 2*fsHeaderSafetyNet+MaxHeaderFetch { -// t.Errorf("rollback head mismatch: have %v, want at most %v", head, 2*fsHeaderSafetyNet+MaxHeaderFetch) -// } -// if mode == FastSync { -// if head := tester.CurrentBlock().NumberU64(); head != 0 { -// t.Errorf("fast sync pivot block #%d not rolled back", head) -// } -// } -// // Attempt to sync with an attacker that withholds promised blocks after the -// // fast sync pivot point. This could be a trial to leave the node with a bad -// // but already imported pivot block. -// tester.newPeer("withhold-attack", protocol, hashes, headers, blocks, receipts) -// missing = 3*fsHeaderSafetyNet + MaxHeaderFetch + 1 -// -// tester.downloader.syncInitHook = func(uint64, uint64) { -// for i := missing; i <= len(hashes); i++ { -// delete(tester.peerHeaders["withhold-attack"], hashes[len(hashes)-i]) -// } -// tester.downloader.syncInitHook = nil -// } -// -// if err := tester.sync("withhold-attack", nil, mode); err == nil { -// t.Fatalf("succeeded withholding attacker synchronisation") -// } -// if head := tester.CurrentHeader().Number.Int64(); int(head) > 2*fsHeaderSafetyNet+MaxHeaderFetch { -// t.Errorf("rollback head mismatch: have %v, want at most %v", head, 2*fsHeaderSafetyNet+MaxHeaderFetch) -// } -// if mode == FastSync { -// if head := tester.CurrentBlock().NumberU64(); head != 0 { -// t.Errorf("fast sync pivot block #%d not rolled back", head) -// } -// } -// // Synchronise with the valid peer and make sure sync succeeds. Since the last -// // rollback should also disable fast syncing for this process, verify that we -// // did a fresh full sync. Note, we can't assert anything about the receipts -// // since we won't purge the database of them, hence we can't use assertOwnChain. -// tester.newPeer("valid", protocol, hashes, headers, blocks, receipts) -// if err := tester.sync("valid", nil, mode); err != nil { -// t.Fatalf("failed to synchronise blocks: %v", err) -// } -// if hs := len(tester.ownHeaders); hs != len(headers) { -// t.Fatalf("synchronised headers mismatch: have %v, want %v", hs, len(headers)) -// } -// if mode != LightSync { -// if bs := len(tester.ownBlocks); bs != len(blocks) { -// t.Fatalf("synchronised blocks mismatch: have %v, want %v", bs, len(blocks)) -// } -// } -//} +func TestInvalidHeaderRollback63Fast(t *testing.T) { testInvalidHeaderRollback(t, 63, FastSync) } +func TestInvalidHeaderRollback64Fast(t *testing.T) { testInvalidHeaderRollback(t, 64, FastSync) } +func TestInvalidHeaderRollback64Light(t *testing.T) { testInvalidHeaderRollback(t, 64, LightSync) } + +func testInvalidHeaderRollback(t *testing.T, protocol int, mode SyncMode) { + t.Parallel() + + tester := newTester() + defer tester.terminate() + + // Create a small enough block chain to download + targetBlocks := 3*fsHeaderSafetyNet + 256 + fsMinFullBlocks + hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false) + + // Attempt to sync with an attacker that feeds junk during the fast sync phase. + // This should result in the last fsHeaderSafetyNet headers being rolled back. + tester.newPeer("fast-attack", protocol, hashes, headers, blocks, receipts) + missing := fsHeaderSafetyNet + MaxHeaderFetch + 1 + delete(tester.peerHeaders["fast-attack"], hashes[len(hashes)-missing]) + + if err := tester.sync("fast-attack", nil, mode); err == nil { + t.Fatalf("succeeded fast attacker synchronisation") + } + if head := tester.CurrentHeader().Number.Int64(); int(head) > MaxHeaderFetch { + t.Errorf("rollback head mismatch: have %v, want at most %v", head, MaxHeaderFetch) + } + // Attempt to sync with an attacker that feeds junk during the block import phase. + // This should result in both the last fsHeaderSafetyNet number of headers being + // rolled back, and also the pivot point being reverted to a non-block status. + tester.newPeer("block-attack", protocol, hashes, headers, blocks, receipts) + missing = 3*fsHeaderSafetyNet + MaxHeaderFetch + 1 + delete(tester.peerHeaders["fast-attack"], hashes[len(hashes)-missing]) // Make sure the fast-attacker doesn't fill in + delete(tester.peerHeaders["block-attack"], hashes[len(hashes)-missing]) + + if err := tester.sync("block-attack", nil, mode); err == nil { + t.Fatalf("succeeded block attacker synchronisation") + } + if head := tester.CurrentHeader().Number.Int64(); int(head) > 2*fsHeaderSafetyNet+MaxHeaderFetch { + t.Errorf("rollback head mismatch: have %v, want at most %v", head, 2*fsHeaderSafetyNet+MaxHeaderFetch) + } + if mode == FastSync { + if head := tester.CurrentBlock().NumberU64(); head != 0 { + t.Errorf("fast sync pivot block #%d not rolled back", head) + } + } + // Attempt to sync with an attacker that withholds promised blocks after the + // fast sync pivot point. This could be a trial to leave the node with a bad + // but already imported pivot block. + tester.newPeer("withhold-attack", protocol, hashes, headers, blocks, receipts) + missing = 3*fsHeaderSafetyNet + MaxHeaderFetch + 1 + + tester.downloader.syncInitHook = func(uint64, uint64) { + for i := missing; i <= len(hashes); i++ { + delete(tester.peerHeaders["withhold-attack"], hashes[len(hashes)-i]) + } + tester.downloader.syncInitHook = nil + } + + if err := tester.sync("withhold-attack", nil, mode); err == nil { + t.Fatalf("succeeded withholding attacker synchronisation") + } + if head := tester.CurrentHeader().Number.Int64(); int(head) > 2*fsHeaderSafetyNet+MaxHeaderFetch { + t.Errorf("rollback head mismatch: have %v, want at most %v", head, 2*fsHeaderSafetyNet+MaxHeaderFetch) + } + if mode == FastSync { + if head := tester.CurrentBlock().NumberU64(); head != 0 { + t.Errorf("fast sync pivot block #%d not rolled back", head) + } + } + // Synchronise with the valid peer and make sure sync succeeds. Since the last + // rollback should also disable fast syncing for this process, verify that we + // did a fresh full sync. Note, we can't assert anything about the receipts + // since we won't purge the database of them, hence we can't use assertOwnChain. + tester.newPeer("valid", protocol, hashes, headers, blocks, receipts) + if err := tester.sync("valid", nil, mode); err != nil { + t.Fatalf("failed to synchronise blocks: %v", err) + } + if hs := len(tester.ownHeaders); hs != len(headers) { + t.Fatalf("synchronised headers mismatch: have %v, want %v", hs, len(headers)) + } + if mode != LightSync { + if bs := len(tester.ownBlocks); bs != len(blocks) { + t.Fatalf("synchronised blocks mismatch: have %v, want %v", bs, len(blocks)) + } + } +} // Tests that a peer advertising an high TD doesn't get to stall the downloader // afterwards by not sending any useful hashes. @@ -1357,77 +1357,77 @@ func testBlockHeaderAttackerDropping(t *testing.T, protocol int) { } } -// Tests that synchronisation progress (origin block number, current block number -// and highest block number) is tracked and updated correctly. -//func TestSyncProgress62(t *testing.T) { testSyncProgress(t, 62, FullSync) } -//func TestSyncProgress63Full(t *testing.T) { testSyncProgress(t, 63, FullSync) } -//func TestSyncProgress63Fast(t *testing.T) { testSyncProgress(t, 63, FastSync) } -//func TestSyncProgress64Full(t *testing.T) { testSyncProgress(t, 64, FullSync) } -//func TestSyncProgress64Fast(t *testing.T) { testSyncProgress(t, 64, FastSync) } -//func TestSyncProgress64Light(t *testing.T) { testSyncProgress(t, 64, LightSync) } -// -//func testSyncProgress(t *testing.T, protocol int, mode SyncMode) { -// t.Parallel() -// -// tester := newTester() -// defer tester.terminate() -// -// // Create a small enough block chain to download -// targetBlocks := blockCacheItems - 15 -// hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false) -// -// // Set a sync init hook to catch progress changes -// starting := make(chan struct{}) -// progress := make(chan struct{}) -// -// tester.downloader.syncInitHook = func(origin, latest uint64) { -// starting <- struct{}{} -// <-progress -// } -// // Retrieve the sync progress and ensure they are zero (pristine sync) -// if progress := tester.downloader.Progress(); progress.StartingBlock != 0 || progress.CurrentBlock != 0 || progress.HighestBlock != 0 { -// t.Fatalf("Pristine progress mismatch: have %v/%v/%v, want %v/%v/%v", progress.StartingBlock, progress.CurrentBlock, progress.HighestBlock, 0, 0, 0) -// } -// // Synchronise half the blocks and check initial progress -// tester.newPeer("peer-half", protocol, hashes[targetBlocks/2:], headers, blocks, receipts) -// pending := new(sync.WaitGroup) -// pending.Add(1) -// -// go func() { -// defer pending.Done() -// if err := tester.sync("peer-half", nil, mode); err != nil { -// panic(fmt.Sprintf("failed to synchronise blocks: %v", err)) -// } -// }() -// <-starting -// if progress := tester.downloader.Progress(); progress.StartingBlock != 0 || progress.CurrentBlock != 0 || progress.HighestBlock != uint64(targetBlocks/2+1) { -// t.Fatalf("Initial progress mismatch: have %v/%v/%v, want %v/%v/%v", progress.StartingBlock, progress.CurrentBlock, progress.HighestBlock, 0, 0, targetBlocks/2+1) -// } -// progress <- struct{}{} -// pending.Wait() -// -// // Synchronise all the blocks and check continuation progress -// tester.newPeer("peer-full", protocol, hashes, headers, blocks, receipts) -// pending.Add(1) -// -// go func() { -// defer pending.Done() -// if err := tester.sync("peer-full", nil, mode); err != nil { -// panic(fmt.Sprintf("failed to synchronise blocks: %v", err)) -// } -// }() -// <-starting -// if progress := tester.downloader.Progress(); progress.StartingBlock != uint64(targetBlocks/2+1) || progress.CurrentBlock != uint64(targetBlocks/2+1) || progress.HighestBlock != uint64(targetBlocks) { -// t.Fatalf("Completing progress mismatch: have %v/%v/%v, want %v/%v/%v", progress.StartingBlock, progress.CurrentBlock, progress.HighestBlock, targetBlocks/2+1, targetBlocks/2+1, targetBlocks) -// } -// progress <- struct{}{} -// pending.Wait() -// -// // Check final progress after successful sync -// if progress := tester.downloader.Progress(); progress.StartingBlock != uint64(targetBlocks/2+1) || progress.CurrentBlock != uint64(targetBlocks) || progress.HighestBlock != uint64(targetBlocks) { -// t.Fatalf("Final progress mismatch: have %v/%v/%v, want %v/%v/%v", progress.StartingBlock, progress.CurrentBlock, progress.HighestBlock, targetBlocks/2+1, targetBlocks, targetBlocks) -// } -//} +//Tests that synchronisation progress (origin block number, current block number +//and highest block number) is tracked and updated correctly. +func TestSyncProgress62(t *testing.T) { testSyncProgress(t, 62, FullSync) } +func TestSyncProgress63Full(t *testing.T) { testSyncProgress(t, 63, FullSync) } +func TestSyncProgress63Fast(t *testing.T) { testSyncProgress(t, 63, FastSync) } +func TestSyncProgress64Full(t *testing.T) { testSyncProgress(t, 64, FullSync) } +func TestSyncProgress64Fast(t *testing.T) { testSyncProgress(t, 64, FastSync) } +func TestSyncProgress64Light(t *testing.T) { testSyncProgress(t, 64, LightSync) } + +func testSyncProgress(t *testing.T, protocol int, mode SyncMode) { + t.Parallel() + + tester := newTester() + defer tester.terminate() + + // Create a small enough block chain to download + targetBlocks := blockCacheItems - 15 + hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false) + + // Set a sync init hook to catch progress changes + starting := make(chan struct{}) + progress := make(chan struct{}) + + tester.downloader.syncInitHook = func(origin, latest uint64) { + starting <- struct{}{} + <-progress + } + // Retrieve the sync progress and ensure they are zero (pristine sync) + if progress := tester.downloader.Progress(); progress.StartingBlock != 0 || progress.CurrentBlock != 0 || progress.HighestBlock != 0 { + t.Fatalf("Pristine progress mismatch: have %v/%v/%v, want %v/%v/%v", progress.StartingBlock, progress.CurrentBlock, progress.HighestBlock, 0, 0, 0) + } + // Synchronise half the blocks and check initial progress + tester.newPeer("peer-half", protocol, hashes[targetBlocks/2:], headers, blocks, receipts) + pending := new(sync.WaitGroup) + pending.Add(1) + + go func() { + defer pending.Done() + if err := tester.sync("peer-half", nil, mode); err != nil { + panic(fmt.Sprintf("failed to synchronise blocks: %v", err)) + } + }() + <-starting + if progress := tester.downloader.Progress(); progress.StartingBlock != 0 || progress.CurrentBlock != 0 || progress.HighestBlock != uint64(targetBlocks/2+1) { + t.Fatalf("Initial progress mismatch: have %v/%v/%v, want %v/%v/%v", progress.StartingBlock, progress.CurrentBlock, progress.HighestBlock, 0, 0, targetBlocks/2+1) + } + progress <- struct{}{} + pending.Wait() + + // Synchronise all the blocks and check continuation progress + tester.newPeer("peer-full", protocol, hashes, headers, blocks, receipts) + pending.Add(1) + + go func() { + defer pending.Done() + if err := tester.sync("peer-full", nil, mode); err != nil { + panic(fmt.Sprintf("failed to synchronise blocks: %v", err)) + } + }() + <-starting + if progress := tester.downloader.Progress(); progress.StartingBlock != uint64(targetBlocks/2+1) || progress.CurrentBlock != uint64(targetBlocks/2+1) || progress.HighestBlock != uint64(targetBlocks) { + t.Fatalf("Completing progress mismatch: have %v/%v/%v, want %v/%v/%v", progress.StartingBlock, progress.CurrentBlock, progress.HighestBlock, targetBlocks/2+1, targetBlocks/2+1, targetBlocks) + } + progress <- struct{}{} + pending.Wait() + + // Check final progress after successful sync + if progress := tester.downloader.Progress(); progress.StartingBlock != uint64(targetBlocks/2+1) || progress.CurrentBlock != uint64(targetBlocks) || progress.HighestBlock != uint64(targetBlocks) { + t.Fatalf("Final progress mismatch: have %v/%v/%v, want %v/%v/%v", progress.StartingBlock, progress.CurrentBlock, progress.HighestBlock, targetBlocks/2+1, targetBlocks, targetBlocks) + } +} // Tests that synchronisation progress (origin block number and highest block // number) is tracked and updated correctly in case of a fork (or manual head From 9bac2dd5e374c9d779ee7be669d04caa45a41c95 Mon Sep 17 00:00:00 2001 From: Nguyen Ba Tam Date: Mon, 27 Aug 2018 14:45:26 +0700 Subject: [PATCH 7/7] update return error when update M1 --- cmd/tomo/main.go | 5 ++++- core/blockchain.go | 19 ++++++++----------- eth/downloader/downloader.go | 7 +++++-- eth/downloader/downloader_test.go | 2 +- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/cmd/tomo/main.go b/cmd/tomo/main.go index 8b4bcb8f0d..0cae7de10e 100644 --- a/cmd/tomo/main.go +++ b/cmd/tomo/main.go @@ -349,7 +349,10 @@ func startNode(ctx *cli.Context, stack *node.Node) { log.Info("Enabled staking node!!!") } case <-core.M1Ch: - ethereum.BlockChain().UpdateM1() + err := ethereum.BlockChain().UpdateM1() + if(err !=nil){ + log.Error("Error when update M1",err) + } } } }() diff --git a/core/blockchain.go b/core/blockchain.go index ad312ed3d7..1c8884d6af 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -698,7 +698,7 @@ func (bc *BlockChain) procFutureBlocks() { type WriteStatus byte const ( - NonStatTy WriteStatus = iota + NonStatTy WriteStatus = iota CanonStatTy SideStatTy ) @@ -1594,9 +1594,9 @@ func (bc *BlockChain) GetClient() (*ethclient.Client, error) { return bc.Client, nil } -func (bc *BlockChain) UpdateM1() { +func (bc *BlockChain) UpdateM1() error { if bc.Config().Posv == nil { - return + return errors.New("Posv not found in config") } engine := bc.Engine().(*posv.Posv) log.Info("It's time to update new set of masternodes for the next epoch...") @@ -1608,29 +1608,25 @@ func (bc *BlockChain) UpdateM1() { addr := common.HexToAddress(common.MasternodeVotingSMC) validator, err := contractValidator.NewTomoValidator(addr, client) if err != nil { - log.Crit("Fail to get validator smc: %v", err) + return err } opts := new(bind.CallOpts) candidates, err := validator.GetCandidates(opts) if err != nil { - log.Crit("Can't get list of masternode candidates: %v", err) + return err } var ms []posv.Masternode for _, candidate := range candidates { v, err := validator.GetCandidateCap(opts, candidate) if err != nil { - log.Warn("Can't get cap of a masternode candidate. Will ignore him", "address", candidate, "error", err) + return err } //TODO: smart contract shouldn't return "0x0000000000000000000000000000000000000000" if candidate.String() != "0x0000000000000000000000000000000000000000" { ms = append(ms, posv.Masternode{Address: candidate, Stake: v.String()}) } } - //// order by cap - //sort.Slice(ms, func(i, j int) bool { - // return ms[i].Stake > ms[j].Stake - //}) log.Info("Ordered list of masternode candidates") for _, m := range ms { fmt.Printf("address: %s, stake: %s\n", m.Address.String(), m.Stake) @@ -1642,8 +1638,9 @@ func (bc *BlockChain) UpdateM1() { log.Info("Updating new set of masternodes") err = engine.UpdateMasternodes(bc, bc.CurrentHeader(), ms) if err != nil { - log.Crit("Can't update masternodes: %v", err) + return err } log.Info("Masternodes are ready for the next epoch") } + return nil } diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index d4ab38a740..4180ae2eaa 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -173,7 +173,7 @@ type LightChain interface { // BlockChain encapsulates functions required to sync a (full or fast) blockchain. type BlockChain interface { Config() *params.ChainConfig - UpdateM1() + UpdateM1() error LightChain // HasBlock verifies a block's presence in the local chain. @@ -1349,7 +1349,10 @@ func (d *Downloader) processFullSyncContent() error { } // prepare set of masternodes for the next epoch if (inserts[len(inserts)-1].Header.Number.Uint64() % epoch) == (epoch - gap) { - d.blockchain.UpdateM1() + err := d.blockchain.UpdateM1() + if (err != nil) { + log.Error("Error when update M1", err) + } } } start = end + 1 diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index 1e1e64afdc..d3f422de64 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -458,7 +458,7 @@ func (dl *downloadTester) dropPeer(id string) { // Config retrieves the blockchain's chain configuration. func (dl *downloadTester) Config() *params.ChainConfig { return params.TestChainConfig } -func (dl *downloadTester) UpdateM1() {} +func (dl *downloadTester) UpdateM1() error { return nil } type downloadTesterPeer struct { dl *downloadTester