github.com/holochain/holochain-proto@v0.1.0-alpha-26.0.20200915073418-5c83169c9b5b/gossip.go (about) 1 // Copyright (C) 2013-2018, The MetaCurrency Project (Eric Harris-Braun, Arthur Brock, et. al.) 2 // Use of this source code is governed by GPLv3 found in the LICENSE file 3 //---------------------------------------------------------------------------------------- 4 5 // gossip implements the gossip protocol for the distributed hash table 6 7 package holochain 8 9 import ( 10 "errors" 11 "fmt" 12 . "github.com/holochain/holochain-proto/hash" 13 peer "github.com/libp2p/go-libp2p-peer" 14 "github.com/tidwall/buntdb" 15 "math/rand" 16 "sort" 17 "strconv" 18 "strings" 19 "time" 20 ) 21 22 // Put holds a put or link for gossiping 23 type Put struct { 24 Idx int 25 M Message 26 } 27 28 // Gossip holds a gossip message 29 type Gossip struct { 30 Puts []Put 31 } 32 33 // GossipReq holds a gossip request 34 type GossipReq struct { 35 MyIdx int 36 YourIdx int 37 } 38 39 // we also gossip about peers too, keeping lists of different peers e.g. blockedlist etc 40 type PeerListType string 41 42 const ( 43 BlockedList = "blockedlist" 44 ) 45 46 type PeerRecord struct { 47 ID peer.ID 48 Warrant string // evidence, reasons, documentation of why peer is in this list 49 } 50 51 type PeerList struct { 52 Type PeerListType 53 Records []PeerRecord 54 } 55 56 var ErrDHTErrNoGossipersAvailable error = errors.New("no gossipers available") 57 var ErrDHTExpectedGossipReqInBody error = errors.New("expected gossip request") 58 var ErrNoSuchIdx error = errors.New("no such change index") 59 60 //HaveFingerprint returns true if we have seen the given fingerprint 61 func (dht *DHT) HaveFingerprint(f Hash) (result bool, err error) { 62 index, err := dht.GetFingerprint(f) 63 if err == nil { 64 result = index >= 0 65 } 66 return 67 } 68 69 // GetFingerprint returns the index that of the message that made a change or -1 if we don't have it 70 func (dht *DHT) GetFingerprint(f Hash) (index int, err error) { 71 index = -1 72 db := dht.ht.(*BuntHT).db 73 err = db.View(func(tx *buntdb.Tx) error { 74 idxStr, e := tx.Get("f:" + f.String()) 75 if e == buntdb.ErrNotFound { 76 return nil 77 } 78 if e != nil { 79 return e 80 } 81 index, e = strconv.Atoi(idxStr) 82 if e != nil { 83 return e 84 } 85 return nil 86 }) 87 return 88 } 89 90 // GetPuts returns a list of puts after the given index 91 func (dht *DHT) GetPuts(since int) (puts []Put, err error) { 92 puts = make([]Put, 0) 93 db := dht.ht.(*BuntHT).db 94 err = db.View(func(tx *buntdb.Tx) error { 95 err = tx.AscendGreaterOrEqual("idx", string(since), func(key, value string) bool { 96 x := strings.Split(key, ":") 97 idx, _ := strconv.Atoi(x[1]) 98 if idx >= since { 99 p := Put{Idx: idx} 100 if value != "" { 101 err := ByteDecoder([]byte(value), &p.M) 102 if err != nil { 103 return false 104 } 105 } 106 puts = append(puts, p) 107 } 108 return true 109 }) 110 sort.Slice(puts, func(i, j int) bool { return puts[i].Idx < puts[j].Idx }) 111 return err 112 }) 113 return 114 } 115 116 // GetGossiper loads returns last known index of the gossiper, and adds them if not didn't exist before 117 func (dht *DHT) GetGossiper(id peer.ID) (idx int, err error) { 118 key := "peer:" + peer.IDB58Encode(id) 119 db := dht.ht.(*BuntHT).db 120 err = db.View(func(tx *buntdb.Tx) error { 121 var e error 122 idx, e = getIntVal(key, tx) 123 if e != nil { 124 return e 125 } 126 return nil 127 }) 128 return 129 } 130 131 type GossiperData struct { 132 ID peer.ID 133 PutIdx int 134 } 135 136 func (dht *DHT) GetGossipers() (gossipers []GossiperData, err error) { 137 var glist []peer.ID 138 glist, err = dht._getGossipers() 139 if err != nil { 140 return 141 } 142 for _, id := range glist { 143 var idx int 144 idx, err = dht.GetGossiper(id) 145 if err != nil { 146 return 147 } 148 gossipers = append(gossipers, GossiperData{ID: id, PutIdx: idx}) 149 } 150 return 151 } 152 153 func (dht *DHT) getGossipers() (glist []peer.ID, err error) { 154 glist, err = dht._getGossipers() 155 if err != nil { 156 return 157 } 158 ns := dht.config.RedundancyFactor 159 glist = dht.h.node.filterInactviePeers(glist, ns) 160 return 161 } 162 163 func (dht *DHT) _getGossipers() (glist []peer.ID, err error) { 164 glist = make([]peer.ID, 0) 165 db := dht.ht.(*BuntHT).db 166 err = db.View(func(tx *buntdb.Tx) error { 167 err = tx.Ascend("peer", func(key, value string) bool { 168 x := strings.Split(key, ":") 169 id, e := peer.IDB58Decode(x[1]) 170 if e != nil { 171 return false 172 } 173 // idx, _ := strconv.Atoi(value) 174 glist = append(glist, id) 175 return true 176 }) 177 return nil 178 }) 179 ns := dht.config.RedundancyFactor 180 if ns > 1 { 181 size := len(glist) 182 hlist := make([]Hash, size) 183 for i := 0; i < size; i++ { 184 h := HashFromPeerID(glist[i]) 185 hlist[i] = h 186 } 187 me := HashFromPeerID(dht.h.nodeID) 188 189 hlist = SortByDistance(me, hlist) 190 glist = make([]peer.ID, len(hlist)) 191 for i := 0; i < len(hlist); i++ { 192 glist[i] = PeerIDFromHash(hlist[i]) 193 } 194 } 195 return 196 } 197 198 // FindGossiper picks a random DHT node to gossip with 199 func (dht *DHT) FindGossiper() (g peer.ID, err error) { 200 var glist []peer.ID 201 glist, err = dht.getGossipers() 202 if err != nil { 203 return 204 } 205 if len(glist) == 0 { 206 err = ErrDHTErrNoGossipersAvailable 207 } else { 208 g = glist[rand.Intn(len(glist))] 209 } 210 return 211 } 212 213 // AddGossiper adds a new gossiper to the gossiper store 214 func (dht *DHT) AddGossiper(id peer.ID) (err error) { 215 // never add ourselves as a gossiper 216 if id == dht.h.node.HashAddr { 217 return 218 } 219 err = dht.updateGossiper(id, 0) 220 return 221 } 222 223 // internal update gossiper function, assumes all checks have been made 224 func (dht *DHT) updateGossiper(id peer.ID, newIdx int) (err error) { 225 db := dht.ht.(*BuntHT).db 226 err = db.Update(func(tx *buntdb.Tx) error { 227 key := "peer:" + peer.IDB58Encode(id) 228 idx, e := getIntVal(key, tx) 229 if e != nil { 230 return e 231 } 232 if newIdx < idx { 233 return nil 234 } 235 sidx := fmt.Sprintf("%d", newIdx) 236 _, _, err = tx.Set(key, sidx, nil) 237 if err != nil { 238 return err 239 } 240 return nil 241 }) 242 return 243 } 244 245 // UpdateGossiper updates a gossiper 246 func (dht *DHT) UpdateGossiper(id peer.ID, newIdx int) (err error) { 247 if dht.h.node.IsBlocked(id) { 248 dht.glog.Logf("gossiper %v on blocklist, deleting", id) 249 dht.DeleteGossiper(id) // ignore error 250 return 251 } 252 dht.glog.Logf("updating %v to %d", id, newIdx) 253 err = dht.updateGossiper(id, newIdx) 254 return 255 } 256 257 // DeleteGossiper removes a gossiper from the database 258 func (dht *DHT) DeleteGossiper(id peer.ID) (err error) { 259 dht.glog.Logf("deleting %v", id) 260 db := dht.ht.(*BuntHT).db 261 err = db.Update(func(tx *buntdb.Tx) error { 262 key := "peer:" + peer.IDB58Encode(id) 263 _, e := tx.Delete(key) 264 return e 265 }) 266 return 267 } 268 269 const ( 270 GossipBackPutDelay = 100 * time.Millisecond 271 ) 272 273 // GossipReceiver implements the handler for the gossip protocol 274 func GossipReceiver(h *Holochain, m *Message) (response interface{}, err error) { 275 dht := h.dht 276 switch m.Type { 277 case GOSSIP_REQUEST: 278 dht.glog.Logf("GossipReceiver got: %v", m) 279 switch t := m.Body.(type) { 280 case GossipReq: 281 dht.glog.Logf("%v wants my puts since %d and is at %d", m.From, t.YourIdx, t.MyIdx) 282 283 // give the gossiper what they want 284 var puts []Put 285 puts, err = h.dht.GetPuts(t.YourIdx) 286 g := Gossip{Puts: puts} 287 response = g 288 289 // check to see what we know they said, and if our record is less 290 // that where they are currently at, gossip back 291 idx, e := h.dht.GetGossiper(m.From) 292 if e == nil && idx < t.MyIdx { 293 dht.glog.Logf("we only have %d of %d from %v so gossiping back", idx, t.MyIdx, m.From) 294 295 pi := h.node.host.Peerstore().PeerInfo(m.From) 296 if len(pi.Addrs) == 0 { 297 dht.glog.Logf("NO ADDRESSES FOR PEER:%v", pi) 298 } 299 300 // queue up a request to gossip back 301 go func() { 302 defer func() { 303 if r := recover(); r != nil { 304 // ignore writes past close 305 } 306 }() 307 // but give them a chance to finish handling the response 308 // from this request first so sleep a bit per put 309 time.Sleep(GossipBackPutDelay * time.Duration(len(puts))) 310 dht.gchan <- gossipWithReq{m.From} 311 }() 312 } 313 314 default: 315 err = ErrDHTExpectedGossipReqInBody 316 } 317 default: 318 err = fmt.Errorf("message type %d not in holochain-gossip protocol", int(m.Type)) 319 } 320 return 321 } 322 323 // gossipWith gossips with a peer asking for everything after since 324 func (dht *DHT) gossipWith(id peer.ID) (err error) { 325 // prevent rentrance 326 dht.glk.Lock() 327 defer dht.glk.Unlock() 328 329 dht.glog.Logf("starting gossipWith %v", id) 330 defer func() { 331 dht.glog.Logf("finish gossipWith %v, err=%v", id, err) 332 }() 333 334 var myIdx, yourIdx int 335 myIdx, err = dht.GetIdx() 336 if err != nil { 337 return 338 } 339 340 yourIdx, err = dht.GetGossiper(id) 341 if err != nil { 342 return 343 } 344 345 var r interface{} 346 msg := dht.h.node.NewMessage(GOSSIP_REQUEST, GossipReq{MyIdx: myIdx, YourIdx: yourIdx + 1}) 347 r, err = dht.h.Send(dht.h.node.ctx, GossipProtocol, id, msg, 0) 348 if err != nil { 349 return 350 } 351 352 gossip := r.(Gossip) 353 puts := gossip.Puts 354 355 // gossiper has more stuff that we new about before so update the gossipers status 356 // and also run their puts 357 count := len(puts) 358 if count > 0 { 359 dht.glog.Logf("queuing %d puts:\n%v", count, puts) 360 var idx int 361 for i, p := range puts { 362 idx = i + yourIdx + 1 363 // put the message into the gossip put handling queue so we can return quickly 364 dht.gossipPuts <- p 365 } 366 err = dht.UpdateGossiper(id, idx) 367 } else { 368 dht.glog.Log("no new puts received") 369 } 370 return 371 } 372 373 // gossipPut handles a given put 374 func (dht *DHT) gossipPut(p Put) (err error) { 375 f, e := p.M.Fingerprint() 376 if e == nil { 377 // dht.sources[p.M.From] = true 378 // dht.fingerprints[f.String()[2:4]] = true 379 dht.glog.Logf("PUT--%d (fingerprint: %v)", p.Idx, f) 380 exists, e := dht.HaveFingerprint(f) 381 if !exists && e == nil { 382 dht.glog.Logf("PUT--%d calling ActionReceiver", p.Idx) 383 r, e := ActionReceiver(dht.h, &p.M) 384 dht.glog.Logf("PUT--%d ActionReceiver returned %v with err %v", p.Idx, r, e) 385 if e != nil { 386 // put receiver error so do what? probably nothing because 387 // put will get retried 388 } 389 } else { 390 if e == nil { 391 dht.glog.Logf("already have fingerprint %v", f) 392 } else { 393 dht.glog.Logf("error in HaveFingerprint %v", e) 394 } 395 } 396 397 } else { 398 dht.glog.Logf("error calculating fingerprint for %v", p) 399 } 400 return 401 } 402 403 func handleGossipPut(dht *DHT, x interface{}) (err error) { 404 p := x.(Put) 405 err = dht.gossipPut(p) 406 return 407 } 408 409 // gossip picks a random node in my neighborhood and sends gossips with it 410 func (dht *DHT) gossip() (err error) { 411 412 var g peer.ID 413 g, err = dht.FindGossiper() 414 if err != nil { 415 return 416 } 417 dht.gchan <- gossipWithReq{g} 418 return 419 } 420 421 // GossipTask runs a gossip and logs any errors 422 func GossipTask(h *Holochain) { 423 if h.dht != nil && h.dht.gchan != nil { 424 err := h.dht.gossip() 425 if err != nil { 426 h.dht.glog.Logf("error: %v", err) 427 } 428 } 429 430 } 431 432 func handleGossipWith(dht *DHT, x interface{}) (err error) { 433 g := x.(gossipWithReq) 434 err = dht.gossipWith(g.id) 435 return 436 } 437 438 func (dht *DHT) handleTillDone(errtext string, channel Channel, handlerFn func(*DHT, interface{}) error) (err error) { 439 var done bool 440 for !done { 441 dht.glog.Logf("%s: waiting for request", errtext) 442 x, ok := <-channel 443 if !ok { 444 done = true 445 break 446 } else { 447 err = handlerFn(dht, x) 448 if err != nil { 449 dht.glog.Logf("%s: got err: %v", errtext, err) 450 } 451 } 452 } 453 dht.glog.Logf("%s: channel closed, stopping", errtext) 454 return nil 455 } 456 457 // HandleGossipWiths waits on a channel for gossipWith requests 458 func (dht *DHT) HandleGossipWiths() (err error) { 459 err = dht.handleTillDone("HandleGossipWiths", dht.gchan, handleGossipWith) 460 return 461 } 462 463 // HandleGossipPuts waits on a channel for gossip changes 464 func (dht *DHT) HandleGossipPuts() (err error) { 465 err = dht.handleTillDone("HandleGossipPuts", dht.gossipPuts, handleGossipPut) 466 return nil 467 } 468 469 // getList returns the peer list of the given type 470 func (dht *DHT) getList(listType PeerListType) (result PeerList, err error) { 471 result.Type = listType 472 result.Records = make([]PeerRecord, 0) 473 db := dht.ht.(*BuntHT).db 474 err = db.View(func(tx *buntdb.Tx) error { 475 err = tx.Ascend("list", func(key, value string) bool { 476 x := strings.Split(key, ":") 477 478 if x[1] == string(listType) { 479 pid, e := peer.IDB58Decode(x[2]) 480 if e != nil { 481 return false 482 } 483 r := PeerRecord{ID: pid, Warrant: value} 484 result.Records = append(result.Records, r) 485 } 486 return true 487 }) 488 return nil 489 }) 490 return 491 } 492 493 // addToList adds the peers to a list 494 func (dht *DHT) addToList(m *Message, list PeerList) (err error) { 495 dht.dlog.Logf("addToList %s=>%v", list.Type, list.Records) 496 db := dht.ht.(*BuntHT).db 497 err = db.Update(func(tx *buntdb.Tx) error { 498 _, err = incIdx(tx, m) 499 if err != nil { 500 return err 501 } 502 for _, r := range list.Records { 503 k := peer.IDB58Encode(r.ID) 504 _, _, err = tx.Set("list:"+string(list.Type)+":"+k, r.Warrant, nil) 505 if err != nil { 506 return err 507 } 508 } 509 return err 510 }) 511 return 512 }