github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/consensus/istanbul/backend/announce.go (about)

     1  // Copyright 2017 The Celo Authors
     2  // This file is part of the celo library.
     3  //
     4  // The celo 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 celo 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 celo library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package backend
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/hex"
    22  	"fmt"
    23  	"io"
    24  	mrand "math/rand"
    25  	"sort"
    26  	"time"
    27  
    28  	"github.com/syndtr/goleveldb/leveldb"
    29  
    30  	"github.com/ethereum/go-ethereum/common"
    31  	"github.com/ethereum/go-ethereum/consensus/istanbul"
    32  	vet "github.com/ethereum/go-ethereum/consensus/istanbul/backend/internal/enodes"
    33  	contract_errors "github.com/ethereum/go-ethereum/contract_comm/errors"
    34  	"github.com/ethereum/go-ethereum/contract_comm/validators"
    35  	"github.com/ethereum/go-ethereum/p2p"
    36  	"github.com/ethereum/go-ethereum/p2p/enode"
    37  	"github.com/ethereum/go-ethereum/rlp"
    38  )
    39  
    40  // ===============================================================
    41  //
    42  // define the istanbul announce data and announce record structure
    43  
    44  type announceRecord struct {
    45  	DestAddress       common.Address
    46  	EncryptedEnodeURL []byte
    47  }
    48  
    49  func (ar *announceRecord) String() string {
    50  	return fmt.Sprintf("{DestAddress: %s, EncryptedEnodeURL length: %d}", ar.DestAddress.String(), len(ar.EncryptedEnodeURL))
    51  }
    52  
    53  type announceData struct {
    54  	AnnounceRecords []*announceRecord
    55  	EnodeURLHash    common.Hash
    56  	Timestamp       uint
    57  }
    58  
    59  func (ad *announceData) String() string {
    60  	return fmt.Sprintf("{Timestamp: %v, EnodeURLHash: %v, AnnounceRecords: %v}", ad.Timestamp, ad.EnodeURLHash.Hex(), ad.AnnounceRecords)
    61  }
    62  
    63  // ==============================================
    64  //
    65  // define the functions that needs to be provided for rlp Encoder/Decoder.
    66  
    67  // EncodeRLP serializes ar into the Ethereum RLP format.
    68  func (ar *announceRecord) EncodeRLP(w io.Writer) error {
    69  	return rlp.Encode(w, []interface{}{ar.DestAddress, ar.EncryptedEnodeURL})
    70  }
    71  
    72  // DecodeRLP implements rlp.Decoder, and load the ar fields from a RLP stream.
    73  func (ar *announceRecord) DecodeRLP(s *rlp.Stream) error {
    74  	var msg struct {
    75  		DestAddress       common.Address
    76  		EncryptedEnodeURL []byte
    77  	}
    78  
    79  	if err := s.Decode(&msg); err != nil {
    80  		return err
    81  	}
    82  	ar.DestAddress, ar.EncryptedEnodeURL = msg.DestAddress, msg.EncryptedEnodeURL
    83  	return nil
    84  }
    85  
    86  // EncodeRLP serializes ad into the Ethereum RLP format.
    87  func (ad *announceData) EncodeRLP(w io.Writer) error {
    88  	return rlp.Encode(w, []interface{}{ad.AnnounceRecords, ad.EnodeURLHash, ad.Timestamp})
    89  }
    90  
    91  // DecodeRLP implements rlp.Decoder, and load the ad fields from a RLP stream.
    92  func (ad *announceData) DecodeRLP(s *rlp.Stream) error {
    93  	var msg struct {
    94  		AnnounceRecords []*announceRecord
    95  		EnodeURLHash    common.Hash
    96  		Timestamp       uint
    97  	}
    98  
    99  	if err := s.Decode(&msg); err != nil {
   100  		return err
   101  	}
   102  	ad.AnnounceRecords, ad.EnodeURLHash, ad.Timestamp = msg.AnnounceRecords, msg.EnodeURLHash, msg.Timestamp
   103  	return nil
   104  }
   105  
   106  // ==============================================
   107  //
   108  // define the constant, types, and function for the sendAnnounce thread
   109  
   110  type AnnounceFrequencyState int
   111  
   112  const (
   113  	// In this state, send out an announce message every 1 minute until the first peer is established
   114  	HighFreqBeforeFirstPeerState AnnounceFrequencyState = iota
   115  
   116  	// In this state, send out an announce message every 1 minute for the first 10 announce messages after the first peer is established.
   117  	// This is on the assumption that when this node first establishes a peer, the p2p network that this node is in may
   118  	// be partitioned with the broader p2p network. We want to give that p2p network some time to connect to the broader p2p network.
   119  	HighFreqAfterFirstPeerState
   120  
   121  	// In this state, send out an announce message every 10 minutes
   122  	LowFreqState
   123  )
   124  
   125  const (
   126  	HighFreqTickerDuration = 1 * time.Minute
   127  	LowFreqTickerDuration  = 10 * time.Minute
   128  )
   129  
   130  // The sendAnnounce thread function
   131  func (sb *Backend) sendAnnounceMsgs() {
   132  	sb.announceWg.Add(1)
   133  	defer sb.announceWg.Done()
   134  
   135  	// Send out an announce message when this thread starts
   136  	go sb.sendIstAnnounce()
   137  
   138  	// Set the initial states
   139  	announceThreadState := HighFreqBeforeFirstPeerState
   140  	currentTickerDuration := HighFreqTickerDuration
   141  	ticker := time.NewTicker(currentTickerDuration)
   142  	numSentMsgsInHighFreqAfterFirstPeerState := 0
   143  
   144  	for {
   145  		select {
   146  		case <-sb.newEpochCh:
   147  			go sb.sendIstAnnounce()
   148  
   149  		case <-ticker.C:
   150  			switch announceThreadState {
   151  			case HighFreqBeforeFirstPeerState:
   152  				{
   153  					if len(sb.broadcaster.FindPeers(nil, p2p.AnyPurpose)) > 0 {
   154  						announceThreadState = HighFreqAfterFirstPeerState
   155  					}
   156  				}
   157  
   158  			case HighFreqAfterFirstPeerState:
   159  				{
   160  					if numSentMsgsInHighFreqAfterFirstPeerState >= 10 {
   161  						announceThreadState = LowFreqState
   162  					}
   163  					numSentMsgsInHighFreqAfterFirstPeerState += 1
   164  				}
   165  
   166  			case LowFreqState:
   167  				{
   168  					if currentTickerDuration != LowFreqTickerDuration {
   169  						// Reset the ticker
   170  						currentTickerDuration = LowFreqTickerDuration
   171  						ticker.Stop()
   172  						ticker = time.NewTicker(currentTickerDuration)
   173  					}
   174  				}
   175  			}
   176  
   177  			go sb.sendIstAnnounce()
   178  
   179  		case <-sb.announceQuit:
   180  			ticker.Stop()
   181  			return
   182  		}
   183  	}
   184  }
   185  
   186  func (sb *Backend) generateIstAnnounce() (*istanbul.Message, error) {
   187  	var enodeUrl string
   188  	if sb.config.Proxied {
   189  		if sb.proxyNode != nil {
   190  			enodeUrl = sb.proxyNode.externalNode.String()
   191  		} else {
   192  			sb.logger.Error("Proxied node is not connected to a proxy")
   193  			return nil, errNoProxyConnection
   194  		}
   195  	} else {
   196  		enodeUrl = sb.p2pserver.Self().String()
   197  	}
   198  
   199  	// If the message is not within the registered validator set, then ignore it
   200  	regAndActiveVals, err := sb.retrieveActiveAndRegisteredValidators()
   201  	if err != nil {
   202  		return nil, err
   203  	}
   204  
   205  	announceRecords := make([]*announceRecord, 0, len(regAndActiveVals))
   206  	for addr := range regAndActiveVals {
   207  		// TODO - Need to encrypt using the remote validator's validator key
   208  		announceRecords = append(announceRecords, &announceRecord{DestAddress: addr, EncryptedEnodeURL: []byte(enodeUrl)})
   209  	}
   210  
   211  	announceData := &announceData{
   212  		AnnounceRecords: announceRecords,
   213  		EnodeURLHash:    istanbul.RLPHash(enodeUrl),
   214  		// Unix() returns a int64, but we need a uint for the golang rlp encoding implmentation. Warning: This timestamp value will be truncated in 2106.
   215  		Timestamp: uint(time.Now().Unix()),
   216  	}
   217  
   218  	announceBytes, err := rlp.EncodeToBytes(announceData)
   219  	if err != nil {
   220  		sb.logger.Error("Error encoding announce content in an Istanbul Validator Enode Share message", "AnnounceData", announceData.String(), "err", err)
   221  		return nil, err
   222  	}
   223  
   224  	msg := &istanbul.Message{
   225  		Code:      istanbulAnnounceMsg,
   226  		Msg:       announceBytes,
   227  		Address:   sb.Address(),
   228  		Signature: []byte{},
   229  	}
   230  
   231  	sb.logger.Debug("Generated an announce message", "IstanbulMsg", msg.String(), "AnnounceMsg", announceData.String())
   232  
   233  	return msg, nil
   234  }
   235  
   236  func (sb *Backend) sendIstAnnounce() error {
   237  	istMsg, err := sb.generateIstAnnounce()
   238  	if err != nil {
   239  		return err
   240  	}
   241  
   242  	if istMsg == nil {
   243  		return nil
   244  	}
   245  
   246  	// Sign the announce message
   247  	if err := istMsg.Sign(sb.Sign); err != nil {
   248  		sb.logger.Error("Error in signing an Istanbul Announce Message", "AnnounceMsg", istMsg.String(), "err", err)
   249  		return err
   250  	}
   251  
   252  	// Convert to payload
   253  	payload, err := istMsg.Payload()
   254  	if err != nil {
   255  		sb.logger.Error("Error in converting Istanbul Announce Message to payload", "AnnounceMsg", istMsg.String(), "err", err)
   256  		return err
   257  	}
   258  
   259  	sb.Gossip(nil, payload, istanbulAnnounceMsg, true)
   260  
   261  	return nil
   262  }
   263  
   264  func (sb *Backend) retrieveActiveAndRegisteredValidators() (map[common.Address]bool, error) {
   265  	validatorsSet := make(map[common.Address]bool)
   266  
   267  	registeredValidators, err := validators.RetrieveRegisteredValidatorSigners(nil, nil)
   268  
   269  	// The validator contract may not be deployed yet.
   270  	// Even if it is deployed, it may not have any registered validators yet.
   271  	if err == contract_errors.ErrSmartContractNotDeployed || len(registeredValidators) == 0 {
   272  		sb.logger.Trace("Can't retrieve the registered validators.  Only allowing the initial validator set to send announce messages", "err", err, "registeredValidators", registeredValidators)
   273  	} else if err != nil {
   274  		sb.logger.Error("Error in retrieving the registered validators", "err", err)
   275  		return validatorsSet, err
   276  	}
   277  
   278  	for _, address := range registeredValidators {
   279  		validatorsSet[address] = true
   280  	}
   281  
   282  	// Add active validators regardless
   283  	block := sb.currentBlock()
   284  	valSet := sb.getValidators(block.Number().Uint64(), block.Hash())
   285  	for _, val := range valSet.List() {
   286  		validatorsSet[val.Address()] = true
   287  	}
   288  
   289  	return validatorsSet, nil
   290  }
   291  
   292  func (sb *Backend) handleIstAnnounce(payload []byte) error {
   293  	logger := sb.logger.New("func", "handleIstAnnounce")
   294  
   295  	msg := new(istanbul.Message)
   296  
   297  	// Decode message
   298  	err := msg.FromPayload(payload, istanbul.GetSignatureAddress)
   299  	if err != nil {
   300  		logger.Error("Error in decoding received Istanbul Announce message", "err", err, "payload", hex.EncodeToString(payload))
   301  		return err
   302  	}
   303  	logger.Trace("Handling an IstanbulAnnounce message", "from", msg.Address)
   304  
   305  	// If the message is originally from this node, then ignore it
   306  	if msg.Address == sb.Address() {
   307  		logger.Trace("Received an IstanbulAnnounce message originating from this node. Ignoring it.")
   308  		return nil
   309  	}
   310  
   311  	var announceData announceData
   312  	err = rlp.DecodeBytes(msg.Msg, &announceData)
   313  	if err != nil {
   314  		logger.Warn("Error in decoding received Istanbul Announce message content", "err", err, "IstanbulMsg", msg.String())
   315  		return err
   316  	}
   317  
   318  	logger = logger.New("msgAddress", msg.Address, "msg_timestamp", announceData.Timestamp)
   319  
   320  	if currentEntryTimestamp, err := sb.valEnodeTable.GetTimestampFromAddress(msg.Address); err == nil {
   321  		if announceData.Timestamp < currentEntryTimestamp {
   322  			logger.Trace("Received an old announce message", "currentEntryTimestamp", currentEntryTimestamp)
   323  			return errOldAnnounceMessage
   324  		}
   325  	} else if err != leveldb.ErrNotFound {
   326  		logger.Warn("Error when retrieving timestamp for entry in the ValEnodeTable", "err", err)
   327  	}
   328  
   329  	// If the message is not within the registered validator set, then ignore it
   330  	regAndActiveVals, err := sb.retrieveActiveAndRegisteredValidators()
   331  	if err != nil {
   332  		return err
   333  	}
   334  
   335  	if !regAndActiveVals[msg.Address] {
   336  		logger.Debug("Received an IstanbulAnnounce message from a non registered validator. Ignoring it.", "AnnounceMsg", msg.String(), "err", err)
   337  		return errUnauthorizedAnnounceMessage
   338  	}
   339  
   340  	var node *enode.Node
   341  	var destAddresses = make([]string, 0, len(announceData.AnnounceRecords))
   342  	var processedAddresses = make(map[common.Address]bool)
   343  	var msgHasDupsOrIrrelevantEntries bool = false
   344  	for _, announceRecord := range announceData.AnnounceRecords {
   345  		// Don't process duplicate entries or entries that are not in the regAndActive valset
   346  		if !regAndActiveVals[announceRecord.DestAddress] || processedAddresses[announceRecord.DestAddress] {
   347  			msgHasDupsOrIrrelevantEntries = true
   348  			continue
   349  		}
   350  
   351  		if announceRecord.DestAddress == sb.Address() {
   352  			// TODO: Decrypt the enodeURL using this validator's validator key after making changes to encrypt it
   353  			enodeUrl := string(announceRecord.EncryptedEnodeURL)
   354  			node, err = enode.ParseV4(enodeUrl)
   355  			if err != nil {
   356  				logger.Error("Error in parsing enodeURL", "enodeUrl", enodeUrl)
   357  				return err
   358  			}
   359  		}
   360  		destAddresses = append(destAddresses, announceRecord.DestAddress.String())
   361  		processedAddresses[announceRecord.DestAddress] = true
   362  	}
   363  	// Save in the valEnodeTable if mining
   364  	if sb.coreStarted && node != nil {
   365  		if err := sb.valEnodeTable.Upsert(map[common.Address]*vet.AddressEntry{msg.Address: {Node: node, Timestamp: announceData.Timestamp}}); err != nil {
   366  			logger.Warn("Error in upserting a valenode entry", "AnnounceData", announceData.String(), "error", err)
   367  			return err
   368  		}
   369  	}
   370  
   371  	if !msgHasDupsOrIrrelevantEntries {
   372  		if err = sb.regossipIstAnnounce(msg, payload, announceData, regAndActiveVals, destAddresses); err != nil {
   373  			return err
   374  		}
   375  	}
   376  
   377  	return nil
   378  }
   379  
   380  func (sb *Backend) regossipIstAnnounce(msg *istanbul.Message, payload []byte, announceData announceData, regAndActiveVals map[common.Address]bool, destAddresses []string) error {
   381  	logger := sb.logger.New("func", "regossipIstAnnounce", "msgAddress", msg.Address, "msg_timestamp", announceData.Timestamp)
   382  	// If we gossiped this address/enodeURL within the last 60 seconds and the enodeURLHash and destAddressHash didn't change, then don't regossip
   383  
   384  	// Generate the destAddresses hash
   385  	sort.Strings(destAddresses)
   386  	destAddressesHash := istanbul.RLPHash(destAddresses)
   387  
   388  	sb.lastAnnounceGossipedMu.RLock()
   389  	if lastGossipTs, ok := sb.lastAnnounceGossiped[msg.Address]; ok {
   390  
   391  		if lastGossipTs.enodeURLHash == announceData.EnodeURLHash && bytes.Equal(lastGossipTs.destAddressesHash.Bytes(), destAddressesHash.Bytes()) && time.Since(lastGossipTs.timestamp) < time.Minute {
   392  			logger.Trace("Already regossiped the msg within the last minute, so not regossiping.", "IstanbulMsg", msg.String(), "AnnounceData", announceData.String())
   393  			sb.lastAnnounceGossipedMu.RUnlock()
   394  			return nil
   395  		}
   396  	}
   397  	sb.lastAnnounceGossipedMu.RUnlock()
   398  
   399  	logger.Trace("Regossiping the istanbul announce message", "IstanbulMsg", msg.String(), "AnnounceMsg", announceData.String())
   400  	sb.Gossip(nil, payload, istanbulAnnounceMsg, true)
   401  
   402  	sb.lastAnnounceGossipedMu.Lock()
   403  	defer sb.lastAnnounceGossipedMu.Unlock()
   404  	sb.lastAnnounceGossiped[msg.Address] = &AnnounceGossipTimestamp{enodeURLHash: announceData.EnodeURLHash, timestamp: time.Now(), destAddressesHash: destAddressesHash}
   405  
   406  	// prune non registered validator entries in the valEnodeTable, reverseValEnodeTable, and lastAnnounceGossiped tables about 5% of the times that an announce msg is handled
   407  	if (mrand.Int() % 100) <= 5 {
   408  		for remoteAddress := range sb.lastAnnounceGossiped {
   409  			if !regAndActiveVals[remoteAddress] {
   410  				logger.Trace("Deleting entry from the lastAnnounceGossiped table", "address", remoteAddress, "gossip timestamp", sb.lastAnnounceGossiped[remoteAddress])
   411  				delete(sb.lastAnnounceGossiped, remoteAddress)
   412  			}
   413  		}
   414  
   415  		if err := sb.valEnodeTable.PruneEntries(regAndActiveVals); err != nil {
   416  			return err
   417  		}
   418  	}
   419  
   420  	return nil
   421  }