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 }