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 }