github.com/insight-chain/inb-go@v1.1.3-0.20191221022159-da049980ae38/consensus/vdpos/signers.go (about)

     1  // Copyright 2019 The inb-go Authors
     2  // This file is part of the inb-go library.
     3  //
     4  // The inb-go library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The inb-go library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the inb-go library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // Package vdpos implements the delegated-proof-of-stake consensus engine.
    18  package vdpos
    19  
    20  import (
    21  	"bytes"
    22  	"encoding/binary"
    23  	"fmt"
    24  	"github.com/insight-chain/inb-go/common"
    25  	"github.com/insight-chain/inb-go/core/types"
    26  	"github.com/insight-chain/inb-go/crypto"
    27  	"github.com/insight-chain/inb-go/log"
    28  	"github.com/insight-chain/inb-go/params"
    29  	"github.com/insight-chain/inb-go/rlp"
    30  	"github.com/insight-chain/inb-go/trie"
    31  	"math/big"
    32  	"math/rand"
    33  	"sort"
    34  )
    35  
    36  const (
    37  	candidateMaxLen   = 50 //max lenth of candidate
    38  	defaultFullCredit = 1  //default rate of stake
    39  )
    40  
    41  type TallyItem struct {
    42  	addr       common.Address
    43  	votesValue *big.Int
    44  }
    45  type TallySlice []TallyItem
    46  
    47  func (s TallySlice) Len() int      { return len(s) }
    48  func (s TallySlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
    49  func (s TallySlice) Less(i, j int) bool {
    50  	isLess := s[i].votesValue.Cmp(s[j].votesValue)
    51  	if isLess > 0 {
    52  		return true
    53  
    54  	} else if isLess < 0 {
    55  		return false
    56  	}
    57  	return bytes.Compare(s[i].addr.Bytes(), s[j].addr.Bytes()) > 0
    58  }
    59  
    60  // verify the SignersPool base on block hash
    61  func (s *SnapContext) verifySignersPool(signersPool []common.Address) error {
    62  
    63  	if len(signersPool) > int(s.config.MaxSignerCount) {
    64  		return errInvalidSignersPool
    65  	}
    66  	sq, err := s.createSignersPool()
    67  	if err != nil {
    68  		return err
    69  	}
    70  	if len(sq) == 0 || len(sq) != len(signersPool) {
    71  		return errInvalidSignersPool
    72  	}
    73  	for i, signer := range signersPool {
    74  		if signer != sq[i] {
    75  			return errInvalidSignersPool
    76  		}
    77  	}
    78  	return nil
    79  }
    80  
    81  // build TallySlice from TallyTrie
    82  func (s *SnapContext) buildTallySlice() TallySlice {
    83  	var tallySlice TallySlice
    84  	tallTrie := s.VdposContext.TallyTrie()
    85  	// use trie iterator
    86  	tallyIterator := trie.NewIterator(tallTrie.PrefixIterator(nil))
    87  	existTally := tallyIterator.Next()
    88  	if !existTally {
    89  		return nil
    90  	}
    91  	for existTally {
    92  		tallyRLP := tallyIterator.Value
    93  		tally := new(types.Tally)
    94  		if err := rlp.DecodeBytes(tallyRLP, tally); err != nil {
    95  			log.Error("Failed to decode tally")
    96  			return nil
    97  		}
    98  		address := tally.Address
    99  		tlsv := tally.TimeLimitedStakingValue
   100  		if tlsv.Cmp(new(big.Int).Mul(big.NewInt(1000000), big.NewInt(params.Inber))) >= 0 {
   101  			votesValue := tally.VotesValue
   102  			tallySlice = append(tallySlice, TallyItem{address, new(big.Int).Mul(votesValue, big.NewInt(defaultFullCredit))})
   103  		}
   104  		existTally = tallyIterator.Next()
   105  	}
   106  
   107  	return tallySlice
   108  }
   109  
   110  func (s *SnapContext) createSignersPool() ([]common.Address, error) {
   111  	// check up if we really need to create signersPool
   112  	if (s.Number+1)%(s.config.MaxSignerCount*s.config.SignerBlocks) != 0 {
   113  		return nil, errCreateSignersPoolNotAllowed
   114  	}
   115  
   116  	var topStakeAddress []common.Address
   117  	var tallySliceOrder TallySlice
   118  
   119  	// use parent block hash as seed so that every signers can use the same one
   120  	seed := int64(binary.LittleEndian.Uint32(crypto.Keccak512(s.ParentHash.Bytes())))
   121  
   122  	// only recalculate signers from to tally per defaultLoopCntRecalculateSigners loop,
   123  	// other loop end just random the order of signers base on parent block hash
   124  	if (s.Number+1)%(s.config.MaxSignerCount*s.config.SignerBlocks*s.config.LoopCntRecalculate) == 0 {
   125  		tallySlice := s.buildTallySlice()
   126  		if tallySlice != nil {
   127  			sort.Sort(TallySlice(tallySlice))
   128  			// remove minimum tickets tally beyond candidateMaxLen
   129  			s.removeExtraCandidate(&tallySlice)
   130  			for _, item := range tallySlice {
   131  				log.Debug(item.addr.Hex())
   132  			}
   133  			poolLength := int(s.config.MaxSignerCount)
   134  			if poolLength > len(tallySlice) {
   135  				poolLength = len(tallySlice)
   136  			}
   137  			tallySliceOrder = tallySlice[:poolLength]
   138  			s.random(tallySliceOrder, seed)
   139  			for _, itemx := range tallySliceOrder {
   140  				log.Debug(itemx.addr.Hex())
   141  			}
   142  		}
   143  	} else {
   144  		if s.SignersPool == nil {
   145  			return nil, errCreateSignersPoolNotAllowed
   146  		}
   147  		tallTrie := s.VdposContext.TallyTrie()
   148  		for _, signer := range s.SignersPool {
   149  			tallyRLP := tallTrie.Get(signer[:])
   150  			if tallyRLP != nil {
   151  				tally := new(types.Tally)
   152  				if err := rlp.DecodeBytes(tallyRLP, tally); err != nil {
   153  					return nil, fmt.Errorf("failed to decode tally: %s", err)
   154  				}
   155  				tallyItem := TallyItem{
   156  					addr:       tally.Address,
   157  					votesValue: tally.VotesValue,
   158  				}
   159  				tallySliceOrder = append(tallySliceOrder, tallyItem)
   160  			}
   161  		}
   162  		s.random(tallySliceOrder, seed)
   163  		for _, itemx := range tallySliceOrder {
   164  			log.Debug(itemx.addr.Hex())
   165  		}
   166  	}
   167  
   168  	// Set the top signers in random order base on parent block hash
   169  	if len(tallySliceOrder) == 0 {
   170  		//return nil, errSignersPoolEmpty
   171  		log.Error("signers pool is empty when createSignersPool")
   172  		return s.SignersPool, nil
   173  	}
   174  	for i := 0; i < int(s.config.MaxSignerCount); i++ {
   175  		topStakeAddress = append(topStakeAddress, tallySliceOrder[i%len(tallySliceOrder)].addr)
   176  	}
   177  
   178  	return topStakeAddress, nil
   179  
   180  }
   181  
   182  func (s *SnapContext) random(arr TallySlice, seed int64) {
   183  	if len(arr) <= 0 {
   184  		return
   185  	}
   186  	rand.Seed(seed)
   187  	for i := len(arr) - 1; i >= 0; i-- {
   188  		num := rand.Intn(len(arr))
   189  		arr[i], arr[num] = arr[num], arr[i]
   190  	}
   191  	return
   192  }
   193  
   194  // inturn returns if a signer at a given block height is in-turn or not.
   195  func (s *SnapContext) inturn(signer common.Address, header *types.Header, parent *types.Header) bool {
   196  	if header.Number.Uint64() == 1 {
   197  		parent = header
   198  	}
   199  	parentExtra := HeaderExtra{}
   200  	err := decodeHeaderExtra(parent.Extra[extraVanity:len(parent.Extra)-extraSeal], &parentExtra)
   201  	if err != nil {
   202  		log.Error("Fail to decode header", "err", err)
   203  		return false
   204  	}
   205  	//signers, err := s.VdposContext.GetSignersFromTrie()
   206  	//if err != nil {
   207  	//	return false
   208  	//}
   209  
   210  	headerTime := header.Time.Uint64()
   211  	loopStartTime := parentExtra.LoopStartTime
   212  	//loopStartTime := parent.LoopStartTime
   213  	signers := parentExtra.SignersPool
   214  	if signersCount := len(signers); signersCount > 0 {
   215  		// handle config.Period != config.SignerPeriod
   216  		if loopIndex := ((headerTime - loopStartTime) / (s.config.Period*(s.config.SignerBlocks-1) + s.config.SignerPeriod)) % uint64(signersCount); signers[loopIndex] == signer {
   217  			return true
   218  		}
   219  	}
   220  	return false
   221  }
   222  
   223  func (s *SnapContext) removeExtraCandidate(tally *TallySlice) {
   224  	tallySlice := *tally
   225  	if len(tallySlice) > candidateMaxLen {
   226  		needRemoveTally := tallySlice[candidateMaxLen:]
   227  		for _, tallySlice := range needRemoveTally {
   228  			s.VdposContext.TallyTrie().Delete(tallySlice.addr[:])
   229  		}
   230  	}
   231  }