github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/consensus/istanbul/backend/val_enodes_share.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  	"encoding/hex"
    21  	"fmt"
    22  	"io"
    23  	"time"
    24  
    25  	"github.com/ethereum/go-ethereum/common"
    26  	"github.com/ethereum/go-ethereum/consensus/istanbul"
    27  	vet "github.com/ethereum/go-ethereum/consensus/istanbul/backend/internal/enodes"
    28  	"github.com/ethereum/go-ethereum/log"
    29  	"github.com/ethereum/go-ethereum/p2p/enode"
    30  	"github.com/ethereum/go-ethereum/rlp"
    31  )
    32  
    33  // ==============================================
    34  //
    35  // define the validator enode share message
    36  
    37  type sharedValidatorEnode struct {
    38  	Address   common.Address
    39  	EnodeURL  string
    40  	Timestamp uint
    41  }
    42  
    43  type valEnodesShareData struct {
    44  	ValEnodes []sharedValidatorEnode
    45  }
    46  
    47  func (sve *sharedValidatorEnode) String() string {
    48  	return fmt.Sprintf("{Address: %s, EnodeURL: %v, Timestamp: %v}", sve.Address.Hex(), sve.EnodeURL, sve.Timestamp)
    49  }
    50  
    51  func (sd *valEnodesShareData) String() string {
    52  	outputStr := "{ValEnodes:"
    53  	for _, valEnode := range sd.ValEnodes {
    54  		outputStr = fmt.Sprintf("%s %s", outputStr, valEnode.String())
    55  	}
    56  	return fmt.Sprintf("%s}", outputStr)
    57  }
    58  
    59  // ==============================================
    60  //
    61  // define the functions that needs to be provided for rlp Encoder/Decoder.
    62  
    63  // EncodeRLP serializes sd into the Ethereum RLP format.
    64  func (sd *valEnodesShareData) EncodeRLP(w io.Writer) error {
    65  	return rlp.Encode(w, []interface{}{sd.ValEnodes})
    66  }
    67  
    68  // DecodeRLP implements rlp.Decoder, and load the sd fields from a RLP stream.
    69  func (sd *valEnodesShareData) DecodeRLP(s *rlp.Stream) error {
    70  	var msg struct {
    71  		ValEnodes []sharedValidatorEnode
    72  	}
    73  
    74  	if err := s.Decode(&msg); err != nil {
    75  		return err
    76  	}
    77  	sd.ValEnodes = msg.ValEnodes
    78  	return nil
    79  }
    80  
    81  // This function is meant to be run as a goroutine.  It will periodically send validator enode share messages
    82  // to this node's proxies so that proxies know the enodes of validators
    83  func (sb *Backend) sendValEnodesShareMsgs() {
    84  	sb.valEnodesShareWg.Add(1)
    85  	defer sb.valEnodesShareWg.Done()
    86  
    87  	ticker := time.NewTicker(time.Minute)
    88  
    89  	for {
    90  		select {
    91  		case <-ticker.C:
    92  			// output the valEnodeTable for debugging purposes
    93  			log.Trace("ValidatorEnodeTable dump", "ValidatorEnodeTable", sb.valEnodeTable.String())
    94  			go sb.sendValEnodesShareMsg()
    95  
    96  		case <-sb.valEnodesShareQuit:
    97  			ticker.Stop()
    98  			return
    99  		}
   100  	}
   101  }
   102  
   103  func (sb *Backend) generateValEnodesShareMsg() (*istanbul.Message, error) {
   104  	vetEntries, err := sb.valEnodeTable.GetAllValEnodes()
   105  
   106  	if err != nil {
   107  		sb.logger.Error("Error in retrieving all the entries from the ValEnodeTable", "err", err)
   108  		return nil, err
   109  	}
   110  
   111  	sharedValidatorEnodes := make([]sharedValidatorEnode, 0, len(vetEntries))
   112  	for address, vetEntry := range vetEntries {
   113  		sharedValidatorEnodes = append(sharedValidatorEnodes, sharedValidatorEnode{
   114  			Address:   address,
   115  			EnodeURL:  vetEntry.Node.String(),
   116  			Timestamp: vetEntry.Timestamp,
   117  		})
   118  	}
   119  
   120  	valEnodesShareData := &valEnodesShareData{
   121  		ValEnodes: sharedValidatorEnodes,
   122  	}
   123  
   124  	valEnodesShareBytes, err := rlp.EncodeToBytes(valEnodesShareData)
   125  	if err != nil {
   126  		sb.logger.Error("Error encoding Istanbul Validator Enodes Share message content", "ValEnodesShareData", valEnodesShareData.String(), "err", err)
   127  		return nil, err
   128  	}
   129  
   130  	msg := &istanbul.Message{
   131  		Code:      istanbulValEnodesShareMsg,
   132  		Msg:       valEnodesShareBytes,
   133  		Address:   sb.Address(),
   134  		Signature: []byte{},
   135  	}
   136  
   137  	sb.logger.Trace("Generated a Istanbul Validator Enodes Share message", "IstanbulMsg", msg.String(), "ValEnodesShareData", valEnodesShareData.String())
   138  
   139  	return msg, nil
   140  }
   141  
   142  func (sb *Backend) sendValEnodesShareMsg() error {
   143  	if sb.proxyNode == nil || sb.proxyNode.peer == nil {
   144  		sb.logger.Error("No proxy peers, cannot send Istanbul Validator Enodes Share message")
   145  		return nil
   146  	}
   147  
   148  	msg, err := sb.generateValEnodesShareMsg()
   149  	if err != nil {
   150  		return err
   151  	}
   152  
   153  	// Sign the validator enode share message
   154  	if err := msg.Sign(sb.Sign); err != nil {
   155  		sb.logger.Error("Error in signing an Istanbul ValEnodesShare Message", "ValEnodesShareMsg", msg.String(), "err", err)
   156  		return err
   157  	}
   158  
   159  	// Convert to payload
   160  	payload, err := msg.Payload()
   161  	if err != nil {
   162  		sb.logger.Error("Error in converting Istanbul ValEnodesShare Message to payload", "ValEnodesShareMsg", msg.String(), "err", err)
   163  		return err
   164  	}
   165  
   166  	sb.logger.Debug("Sending Istanbul Validator Enodes Share payload to proxy peer")
   167  	go sb.proxyNode.peer.Send(istanbulValEnodesShareMsg, payload)
   168  
   169  	return nil
   170  }
   171  
   172  func (sb *Backend) handleValEnodesShareMsg(payload []byte) error {
   173  	sb.logger.Debug("Handling an Istanbul Validator Enodes Share message")
   174  
   175  	msg := new(istanbul.Message)
   176  	// Decode message
   177  	err := msg.FromPayload(payload, istanbul.GetSignatureAddress)
   178  	if err != nil {
   179  		sb.logger.Error("Error in decoding received Istanbul Validator Enode Share message", "err", err, "payload", hex.EncodeToString(payload))
   180  		return err
   181  	}
   182  
   183  	// Verify that the sender is from the proxied validator
   184  	if msg.Address != sb.config.ProxiedValidatorAddress {
   185  		sb.logger.Error("Unauthorized valEnodesShare message", "sender address", msg.Address, "authorized sender address", sb.config.ProxiedValidatorAddress)
   186  		return errUnauthorizedValEnodesShareMessage
   187  	}
   188  
   189  	var valEnodesShareData valEnodesShareData
   190  	err = rlp.DecodeBytes(msg.Msg, &valEnodesShareData)
   191  	if err != nil {
   192  		sb.logger.Error("Error in decoding received Istanbul Validator Enodes Share message content", "err", err, "IstanbulMsg", msg.String())
   193  		return err
   194  	}
   195  
   196  	sb.logger.Trace("Received an Istanbul Validator Enodes Share message", "IstanbulMsg", msg.String(), "ValEnodesShareData", valEnodesShareData.String())
   197  
   198  	upsertBatch := make(map[common.Address]*vet.AddressEntry)
   199  	for _, sharedValidatorEnode := range valEnodesShareData.ValEnodes {
   200  		if node, err := enode.ParseV4(sharedValidatorEnode.EnodeURL); err != nil {
   201  			sb.logger.Warn("Error in parsing enodeURL", "enodeURL", sharedValidatorEnode.EnodeURL)
   202  			continue
   203  		} else {
   204  			upsertBatch[sharedValidatorEnode.Address] = &vet.AddressEntry{Node: node, Timestamp: sharedValidatorEnode.Timestamp}
   205  		}
   206  	}
   207  
   208  	if len(upsertBatch) > 0 {
   209  		if err := sb.valEnodeTable.Upsert(upsertBatch); err != nil {
   210  			sb.logger.Warn("Error in upserting a batch to the valEnodeTable", "IstanbulMsg", msg.String(), "UpsertBatch", upsertBatch, "error", err)
   211  		}
   212  	}
   213  
   214  	sb.logger.Trace("ValidatorEnodeTable dump", "ValidatorEnodeTable", sb.valEnodeTable.String())
   215  
   216  	return nil
   217  }