github.com/metacurrency/holochain@v0.1.0-alpha-26.0.20200915073418-5c83169c9b5b/dht_test.go (about) 1 package holochain 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "regexp" 8 "strings" 9 "testing" 10 "time" 11 12 . "github.com/holochain/holochain-proto/hash" 13 b58 "github.com/jbenet/go-base58" 14 peer "github.com/libp2p/go-libp2p-peer" 15 . "github.com/smartystreets/goconvey/convey" 16 ) 17 18 func TestNewDHT(t *testing.T) { 19 d, _, h := PrepareTestChain("test") 20 defer CleanupTestChain(h, d) 21 os.Remove(filepath.Join(h.DBPath(), DHTStoreFileName)) 22 23 Convey("It should initialize the DHT struct and data store", t, func() { 24 So(FileExists(h.DBPath(), DHTStoreFileName), ShouldBeFalse) 25 dht := NewDHT(h) 26 So(FileExists(h.DBPath(), DHTStoreFileName), ShouldBeTrue) 27 So(dht.h, ShouldEqual, h) 28 So(dht.config, ShouldEqual, &h.nucleus.dna.DHTConfig) 29 }) 30 } 31 32 func TestSetupDHT(t *testing.T) { 33 d, _, h := PrepareTestChain("test") 34 defer CleanupTestChain(h, d) 35 36 err := h.dht.SetupDHT() 37 Convey("it should add the holochain ID to the DHT", t, func() { 38 So(err, ShouldBeNil) 39 ID := h.DNAHash() 40 So(h.dht.Exists(ID, StatusLive), ShouldBeNil) 41 _, et, _, status, err := h.dht.Get(h.dnaHash, StatusLive, GetMaskAll) 42 So(err, ShouldBeNil) 43 So(status, ShouldEqual, StatusLive) 44 So(et, ShouldEqual, DNAEntryType) 45 46 }) 47 48 Convey("it should push the agent entry to the DHT at genesis time", t, func() { 49 data, et, _, status, err := h.dht.Get(h.agentHash, StatusLive, GetMaskAll) 50 So(err, ShouldBeNil) 51 So(status, ShouldEqual, StatusLive) 52 So(et, ShouldEqual, AgentEntryType) 53 54 var e Entry 55 e, _, _ = h.chain.GetEntry(h.agentHash) 56 57 var b []byte 58 b, _ = e.Marshal() 59 60 So(string(data), ShouldEqual, string(b)) 61 }) 62 63 Convey("it should push the key to the DHT at genesis time", t, func() { 64 keyHash, _ := NewHash(h.nodeIDStr) 65 data, et, _, status, err := h.dht.Get(keyHash, StatusLive, GetMaskAll) 66 So(err, ShouldBeNil) 67 So(status, ShouldEqual, StatusLive) 68 So(et, ShouldEqual, KeyEntryType) 69 pubKey, err := h.agent.EncodePubKey() 70 So(string(data), ShouldEqual, pubKey) 71 72 data, et, _, status, err = h.dht.Get(keyHash, StatusDefault, GetMaskDefault) 73 So(err, ShouldBeNil) 74 So(status, ShouldEqual, StatusLive) 75 76 So(string(data), ShouldEqual, pubKey) 77 }) 78 } 79 80 func TestDHTSend(t *testing.T) { 81 d, _, h := PrepareTestChain("test") 82 defer CleanupTestChain(h, d) 83 84 hash, _ := NewHash("QmY8Mzg9F69e5P9AoQPYat655HEhc1TVGs11tmfNSzkqh2") 85 86 Convey("send GET_REQUEST message for non existent hash should get error", t, func() { 87 msg := h.node.NewMessage(GET_REQUEST, GetReq{H: hash, StatusMask: StatusLive}) 88 _, err := h.dht.send(nil, h.node.HashAddr, msg) 89 So(err, ShouldEqual, ErrHashNotFound) 90 }) 91 92 now := time.Unix(1, 1) // pick a constant time so the test will always work 93 e := GobEntry{C: "4"} 94 _, hd, err := h.NewEntry(now, "evenNumbers", &e) 95 if err != nil { 96 panic(err) 97 } 98 99 // publish the entry data to the dht 100 hash = hd.EntryLink 101 Convey("after a handled PUT_REQUEST data should be stored in DHT", t, func() { 102 msg := h.node.NewMessage(PUT_REQUEST, HoldReq{EntryHash: hash}) 103 r, err := h.dht.send(nil, h.node.HashAddr, msg) 104 So(err, ShouldBeNil) 105 So(r.(HoldResp).Code, ShouldEqual, ReceiptOK) 106 hd, _ := h.chain.GetEntryHeader(hash) 107 So(hd.EntryLink.Equal(hash), ShouldBeTrue) 108 }) 109 110 Convey("send GET_REQUEST message should return content", t, func() { 111 msg := h.node.NewMessage(GET_REQUEST, GetReq{H: hash, StatusMask: StatusLive}) 112 r, err := h.dht.send(nil, h.node.HashAddr, msg) 113 So(err, ShouldBeNil) 114 resp := r.(GetResp) 115 So(fmt.Sprintf("%v", resp.Entry), ShouldEqual, fmt.Sprintf("%v", e)) 116 }) 117 118 Convey("send GET_REQUEST message should return content of sys types", t, func() { 119 msg := h.node.NewMessage(GET_REQUEST, GetReq{H: h.agentHash, StatusMask: StatusLive}) 120 r, err := h.dht.send(nil, h.nodeID, msg) 121 So(err, ShouldBeNil) 122 resp := r.(GetResp) 123 ae, _ := h.agent.AgentEntry(nil) 124 a, _ := ae.ToJSON() 125 So(resp.Entry.Content().(string), ShouldEqual, a) 126 127 msg = h.node.NewMessage(GET_REQUEST, GetReq{H: HashFromPeerID(h.nodeID), StatusMask: StatusLive}) 128 r, err = h.dht.send(nil, h.nodeID, msg) 129 So(err, ShouldBeNil) 130 resp = r.(GetResp) 131 So(resp.Entry.Content().(string), ShouldEqual, ae.PublicKey) 132 133 // for now this is an error because we presume everyone has the DNA. 134 // once we implement dna changes, this needs to be changed 135 msg = h.node.NewMessage(GET_REQUEST, GetReq{H: h.dnaHash, StatusMask: StatusLive}) 136 r, err = h.dht.send(nil, h.nodeID, msg) 137 So(err, ShouldBeError) 138 139 }) 140 } 141 142 func TestDHTQueryGet(t *testing.T) { 143 nodesCount := 6 144 mt := setupMultiNodeTesting(nodesCount) 145 defer mt.cleanupMultiNodeTesting() 146 147 h := mt.nodes[0] 148 149 now := time.Unix(1, 1) // pick a constant time so the test will always work 150 e := GobEntry{C: "4"} 151 _, hd, err := h.NewEntry(now, "evenNumbers", &e) 152 if err != nil { 153 panic(err) 154 } 155 156 /*for i := 0; i < nodesCount; i++ { 157 fmt.Printf("node%d:%v\n", i, mt.nodes[i].node.HashAddr.Pretty()[2:6]) 158 }*/ 159 160 // publish the entry data to local DHT node (0) 161 hash := hd.EntryLink 162 msg := h.node.NewMessage(PUT_REQUEST, HoldReq{EntryHash: hash}) 163 _, err = h.dht.send(nil, h.node.HashAddr, msg) 164 if err != nil { 165 panic(err) 166 } 167 168 ringConnect(t, mt.ctx, mt.nodes, nodesCount) 169 170 // pick a distant node that has to do some of the recursive lookups to get back to node 0. 171 Convey("Kademlia GET_REQUEST should return content", t, func() { 172 h2 := mt.nodes[nodesCount-2] 173 r, err := h2.dht.Query(hash, GET_REQUEST, GetReq{H: hash, StatusMask: StatusLive}) 174 So(err, ShouldBeNil) 175 resp := r.(GetResp) 176 So(fmt.Sprintf("%v", resp.Entry), ShouldEqual, fmt.Sprintf("%v", e)) 177 }) 178 } 179 180 func TestDHTKadPut(t *testing.T) { 181 nodesCount := 6 182 mt := setupMultiNodeTesting(nodesCount) 183 defer mt.cleanupMultiNodeTesting() 184 185 h := mt.nodes[0] 186 187 now := time.Unix(1, 1) // pick a constant time so the test will always work 188 e := GobEntry{C: "4"} 189 _, hd, err := h.NewEntry(now, "evenNumbers", &e) 190 if err != nil { 191 panic(err) 192 } 193 hash := hd.EntryLink 194 195 /* 196 for i := 0; i < nodesCount; i++ { 197 fmt.Printf("node%d:%v\n", i, mt.nodes[i].node.HashAddr.Pretty()[2:6]) 198 } 199 //node0:NnRV 200 //node1:UfY4 201 //node2:YA62 202 //node3:S4BF 203 //node4:W4He 204 //node5:dxxu 205 206 starConnect(t, mt.ctx, mt.nodes, nodesCount) 207 // get closest peers in the routing table 208 rtp := h.node.routingTable.NearestPeers(hash, AlphaValue) 209 fmt.Printf("CLOSE:%v\n", rtp) 210 211 //[<peer.ID S4BFeT> <peer.ID W4HeEG> <peer.ID UfY4We>] 212 //i.e 3,4,1 213 */ 214 215 ringConnect(t, mt.ctx, mt.nodes, nodesCount) 216 217 Convey("Kademlia PUT_REQUEST should put the hash to its closet node even if we don't know about it yet", t, func() { 218 219 rtp := h.node.routingTable.NearestPeers(hash, AlphaValue) 220 // check that our routing table doesn't contain closest node yet 221 So(fmt.Sprintf("%v", rtp), ShouldEqual, "[<peer.ID UfY4We> <peer.ID dxxuES>]") 222 err := h.dht.Change(hash, PUT_REQUEST, HoldReq{EntryHash: hash}) 223 So(err, ShouldBeNil) 224 225 processChangeRequestsInTesting(h) 226 rtp = h.node.routingTable.NearestPeers(hash, AlphaValue) 227 // routing table should be updated 228 So(fmt.Sprintf("%v", rtp), ShouldEqual, "[<peer.ID S4BFeT> <peer.ID W4HeEG> <peer.ID UfY4We>]") 229 // and get from node should get the value 230 msg := h.node.NewMessage(GET_REQUEST, GetReq{H: hash, StatusMask: StatusLive}) 231 r, err := h.dht.send(nil, mt.nodes[3].nodeID, msg) 232 So(err, ShouldBeNil) 233 resp := r.(GetResp) 234 So(fmt.Sprintf("%v", resp.Entry), ShouldEqual, fmt.Sprintf("%v", e)) 235 236 if h.Config.EnableWorldModel { 237 // and the world model should show that it's being held 238 holding, err := h.world.IsHolding(mt.nodes[3].nodeID, hash) 239 So(err, ShouldBeNil) 240 So(holding, ShouldBeTrue) 241 } 242 }) 243 } 244 245 func TestActionReceiver(t *testing.T) { 246 d, _, h := PrepareTestChain("test") 247 defer CleanupTestChain(h, d) 248 249 Convey("PUT_REQUEST should fail if body isn't a hash", t, func() { 250 m := h.node.NewMessage(PUT_REQUEST, "foo") 251 _, err := ActionReceiver(h, m) 252 So(err.Error(), ShouldEqual, "Unexpected request body type 'string' in put request, expecting holochain.HoldReq") 253 }) 254 255 Convey("LINK_REQUEST should fail if body not a good linking request", t, func() { 256 m := h.node.NewMessage(LINK_REQUEST, "foo") 257 _, err := ActionReceiver(h, m) 258 So(err.Error(), ShouldEqual, "Unexpected request body type 'string' in link request, expecting holochain.HoldReq") 259 }) 260 261 now := time.Unix(1, 1) // pick a constant time so the test will always work 262 e := GobEntry{C: "124"} 263 _, hd, _ := h.NewEntry(now, "evenNumbers", &e) 264 hash := hd.EntryLink 265 266 Convey("PUT_REQUEST should queue a valid message", t, func() { 267 m := h.node.NewMessage(PUT_REQUEST, HoldReq{EntryHash: hash}) 268 r, err := ActionReceiver(h, m) 269 So(err, ShouldBeNil) 270 So(r.(HoldResp).Code, ShouldEqual, ReceiptOK) 271 data, _ := MakeReceiptData(m, ReceiptOK) 272 matches, err := h.VerifySignature(r.(HoldResp).Signature, string(data), h.agent.PubKey()) 273 So(err, ShouldBeNil) 274 So(matches, ShouldBeTrue) 275 }) 276 277 Convey("GET_REQUEST should return the requested values", t, func() { 278 m := h.node.NewMessage(GET_REQUEST, GetReq{H: hash, StatusMask: StatusLive}) 279 r, err := ActionReceiver(h, m) 280 So(err, ShouldBeNil) 281 resp := r.(GetResp) 282 So(fmt.Sprintf("%v", resp.Entry), ShouldEqual, fmt.Sprintf("%v", e)) 283 284 m = h.node.NewMessage(GET_REQUEST, GetReq{H: hash, GetMask: GetMaskEntryType}) 285 r, err = ActionReceiver(h, m) 286 So(err, ShouldBeNil) 287 resp = r.(GetResp) 288 So(resp.Entry.C, ShouldEqual, nil) 289 So(resp.EntryType, ShouldEqual, "evenNumbers") 290 291 m = h.node.NewMessage(GET_REQUEST, GetReq{H: hash, GetMask: GetMaskEntry + GetMaskEntryType}) 292 r, err = ActionReceiver(h, m) 293 So(err, ShouldBeNil) 294 resp = r.(GetResp) 295 So(fmt.Sprintf("%v", resp.Entry), ShouldEqual, fmt.Sprintf("%v", e)) 296 So(resp.EntryType, ShouldEqual, "evenNumbers") 297 298 m = h.node.NewMessage(GET_REQUEST, GetReq{H: hash, GetMask: GetMaskSources}) 299 r, err = ActionReceiver(h, m) 300 So(err, ShouldBeNil) 301 resp = r.(GetResp) 302 So(resp.Entry.C, ShouldEqual, nil) 303 So(fmt.Sprintf("%v", resp.Sources), ShouldEqual, fmt.Sprintf("[%v]", h.nodeIDStr)) 304 So(resp.EntryType, ShouldEqual, "evenNumbers") // you allways get the entry type even if not in getmask at this level (ActionReceiver) because we have to be able to look up the definition to interpret the contents 305 306 m = h.node.NewMessage(GET_REQUEST, GetReq{H: hash, GetMask: GetMaskEntry + GetMaskSources}) 307 r, err = ActionReceiver(h, m) 308 So(err, ShouldBeNil) 309 resp = r.(GetResp) 310 So(fmt.Sprintf("%v", resp.Entry), ShouldEqual, fmt.Sprintf("%v", e)) 311 So(fmt.Sprintf("%v", resp.Sources), ShouldEqual, fmt.Sprintf("[%v]", h.nodeIDStr)) 312 So(resp.EntryType, ShouldEqual, "evenNumbers") // you allways get the entry type even if not in getmask at this level (ActionReceiver) because we have to be able to look up the definition to interpret the contents 313 }) 314 315 someData := `{"firstName":"Zippy","lastName":"Pinhead"}` 316 e = GobEntry{C: someData} 317 _, hd, _ = h.NewEntry(now, "profile", &e) 318 profileHash := hd.EntryLink 319 320 le := GobEntry{C: fmt.Sprintf(`{"Links":[{"Base":"%s","Link":"%s","Tag":"4stars"},{"Base":"%s","Link":"%s","Tag":"3stars"}]}`, hash.String(), profileHash.String(), hash.String(), h.agentHash)} 321 _, lhd, _ := h.NewEntry(time.Now(), "rating", &le) 322 323 Convey("LINK_REQUEST should store links", t, func() { 324 lr := HoldReq{RelatedHash: hash, EntryHash: lhd.EntryLink} 325 m := h.node.NewMessage(LINK_REQUEST, lr) 326 r, err := ActionReceiver(h, m) 327 So(err, ShouldBeNil) 328 So(r.(HoldResp).Code, ShouldEqual, ReceiptOK) 329 data, _ := MakeReceiptData(m, ReceiptOK) 330 matches, err := h.VerifySignature(r.(HoldResp).Signature, string(data), h.agent.PubKey()) 331 So(err, ShouldBeNil) 332 So(matches, ShouldBeTrue) 333 334 // check that it got put 335 meta, err := h.dht.GetLinks(hash, "4stars", StatusLive) 336 So(err, ShouldBeNil) 337 So(meta[0].H, ShouldEqual, hd.EntryLink.String()) 338 }) 339 340 e2 := GobEntry{C: "322"} 341 hash2, _ := e2.Sum(h.hashSpec) 342 343 e3 := GobEntry{C: "324"} 344 hash3, _ := e3.Sum(h.hashSpec) 345 346 Convey("LINK_REQUEST of unknown hash should get queued for retry", t, func() { 347 lr := HoldReq{RelatedHash: hash2, EntryHash: hash3} 348 m := h.node.NewMessage(LINK_REQUEST, lr) 349 r, err := ActionReceiver(h, m) 350 So(err, ShouldBeNil) 351 So(r, ShouldEqual, DHTChangeUnknownHashQueuedForRetry) 352 So(len(h.dht.retryQueue), ShouldEqual, 1) 353 <-h.dht.retryQueue // unload the queue 354 }) 355 356 Convey("GETLINK_REQUEST should retrieve link values", t, func() { 357 mq := LinkQuery{Base: hash, T: "4stars"} 358 m := h.node.NewMessage(GETLINK_REQUEST, mq) 359 r, err := ActionReceiver(h, m) 360 So(err, ShouldBeNil) 361 results := r.(*LinkQueryResp) 362 So(results.Links[0].H, ShouldEqual, hd.EntryLink.String()) 363 So(results.Links[0].T, ShouldEqual, "") 364 }) 365 366 Convey("GETLINK_REQUEST with empty tag should retrieve all linked values", t, func() { 367 mq := LinkQuery{Base: hash, T: ""} 368 m := h.node.NewMessage(GETLINK_REQUEST, mq) 369 r, err := ActionReceiver(h, m) 370 So(err, ShouldBeNil) 371 results := r.(*LinkQueryResp) 372 var l4star, l3star TaggedHash 373 // could come back in any order... 374 if results.Links[0].T == "4stars" { 375 l4star = results.Links[0] 376 l3star = results.Links[1] 377 378 } else { 379 l4star = results.Links[1] 380 l3star = results.Links[0] 381 } 382 So(l3star.H, ShouldEqual, h.agentHash.String()) 383 So(l3star.T, ShouldEqual, "3stars") 384 So(l4star.H, ShouldEqual, hd.EntryLink.String()) 385 So(l4star.T, ShouldEqual, "4stars") 386 }) 387 388 Convey("GOSSIP_REQUEST should request and advertise data by idx", t, func() { 389 g := GossipReq{MyIdx: 1, YourIdx: 2} 390 m := h.node.NewMessage(GOSSIP_REQUEST, g) 391 r, err := GossipReceiver(h, m) 392 So(err, ShouldBeNil) 393 gr := r.(Gossip) 394 So(len(gr.Puts), ShouldEqual, 4) 395 }) 396 397 le2 := GobEntry{C: fmt.Sprintf(`{"Links":[{"Base":"%s","Link":"%s","Tag":"4stars","LinkAction":"%s"}]}`, hash.String(), profileHash.String(), DelLinkAction)} 398 _, lhd2, _ := h.NewEntry(time.Now(), "rating", &le2) 399 400 Convey("LINK_REQUEST with del type should mark a link as deleted", t, func() { 401 lr := HoldReq{RelatedHash: hash, EntryHash: lhd2.EntryLink} 402 m := h.node.NewMessage(LINK_REQUEST, lr) 403 r, err := ActionReceiver(h, m) 404 So(r.(HoldResp).Code, ShouldEqual, ReceiptOK) 405 data, _ := MakeReceiptData(m, ReceiptOK) 406 matches, err := h.VerifySignature(r.(HoldResp).Signature, string(data), h.agent.PubKey()) 407 So(err, ShouldBeNil) 408 So(matches, ShouldBeTrue) 409 410 results, err := h.dht.GetLinks(hash, "4stars", StatusLive) 411 So(err, ShouldBeNil) 412 So(len(results), ShouldEqual, 0) 413 414 results, err = h.dht.GetLinks(hash, "4stars", StatusDeleted) 415 So(err, ShouldBeNil) 416 So(len(results), ShouldEqual, 1) 417 }) 418 419 Convey("GETLINK_REQUEST with mask option should retrieve deleted link values", t, func() { 420 mq := LinkQuery{Base: hash, T: "4stars", StatusMask: StatusDeleted} 421 m := h.node.NewMessage(GETLINK_REQUEST, mq) 422 r, err := ActionReceiver(h, m) 423 So(err, ShouldBeNil) 424 results := r.(*LinkQueryResp) 425 So(results.Links[0].H, ShouldEqual, hd.EntryLink.String()) 426 }) 427 428 Convey("MOD_REQUEST of unknown hash should get queued for retry", t, func() { 429 req := HoldReq{RelatedHash: hash2, EntryHash: hash3} 430 m := h.node.NewMessage(MOD_REQUEST, req) 431 r, err := ActionReceiver(h, m) 432 So(err, ShouldBeNil) 433 So(r, ShouldEqual, DHTChangeUnknownHashQueuedForRetry) 434 So(len(h.dht.retryQueue), ShouldEqual, 1) 435 <-h.dht.retryQueue // unload the queue 436 }) 437 438 // put a second entry to DHT 439 h.NewEntry(now, "evenNumbers", &e2) 440 m2 := h.node.NewMessage(PUT_REQUEST, HoldReq{EntryHash: hash2}) 441 ActionReceiver(h, m2) 442 443 Convey("MOD_REQUEST should set hash to modified", t, func() { 444 req := HoldReq{RelatedHash: hash, EntryHash: hash2} 445 m := h.node.NewMessage(MOD_REQUEST, req) 446 r, err := ActionReceiver(h, m) 447 So(err, ShouldBeNil) 448 So(r.(HoldResp).Code, ShouldEqual, ReceiptOK) 449 data, _ := MakeReceiptData(m, ReceiptOK) 450 matches, err := h.VerifySignature(r.(HoldResp).Signature, string(data), h.agent.PubKey()) 451 So(err, ShouldBeNil) 452 So(matches, ShouldBeTrue) 453 }) 454 455 Convey("DELETE_REQUEST should set status of hash to deleted", t, func() { 456 entry := DelEntry{Hash: hash2, Message: "expired"} 457 a := NewDelAction(entry) 458 _, err := h.doCommit(a, NullHash()) 459 entryHash := a.header.EntryLink 460 m := h.node.NewMessage(DEL_REQUEST, HoldReq{RelatedHash: hash2, EntryHash: entryHash}) 461 r, err := ActionReceiver(h, m) 462 So(err, ShouldBeNil) 463 So(r.(HoldResp).Code, ShouldEqual, ReceiptOK) 464 data, _ := MakeReceiptData(m, ReceiptOK) 465 matches, err := h.VerifySignature(r.(HoldResp).Signature, string(data), h.agent.PubKey()) 466 So(err, ShouldBeNil) 467 So(matches, ShouldBeTrue) 468 469 data, entryType, _, status, _ := h.dht.Get(hash2, StatusAny, GetMaskAll) 470 var e GobEntry 471 e.Unmarshal(data) 472 So(e.C, ShouldEqual, "322") 473 So(entryType, ShouldEqual, "evenNumbers") 474 So(status, ShouldEqual, StatusDeleted) 475 }) 476 477 Convey("DELETE_REQUEST of unknown hash should get queued for retry", t, func() { 478 req := HoldReq{RelatedHash: hash3, EntryHash: hash3} 479 m := h.node.NewMessage(DEL_REQUEST, req) 480 r, err := ActionReceiver(h, m) 481 So(err, ShouldBeNil) 482 So(r, ShouldEqual, DHTChangeUnknownHashQueuedForRetry) 483 So(len(h.dht.retryQueue), ShouldEqual, 1) 484 }) 485 486 Convey("LISTADD_REQUEST with bad warrant should return error", t, func() { 487 pid, _ := makePeer("testPeer") 488 m := h.node.NewMessage(LISTADD_REQUEST, 489 ListAddReq{ 490 ListType: BlockedList, 491 Peers: []string{peer.IDB58Encode(pid)}, 492 WarrantType: SelfRevocationType, 493 Warrant: []byte("bad warrant!"), 494 }) 495 _, err := ActionReceiver(h, m) 496 So(err.Error(), ShouldEqual, "List add request rejected on warrant failure: unable to decode warrant (invalid character 'b' looking for beginning of value)") 497 }) 498 499 Convey("LISTADD_REQUEST with warrant out of context should return error", t, func() { 500 pid, oldPrivKey := makePeer("testPeer") 501 _, newPrivKey := makePeer("peer1") 502 revocation, _ := NewSelfRevocation(oldPrivKey, newPrivKey, []byte("extra data")) 503 w, _ := NewSelfRevocationWarrant(revocation) 504 data, _ := w.Encode() 505 m := h.node.NewMessage(LISTADD_REQUEST, 506 ListAddReq{ 507 ListType: BlockedList, 508 Peers: []string{peer.IDB58Encode(pid)}, 509 WarrantType: SelfRevocationType, 510 Warrant: data, 511 }) 512 _, err := ActionReceiver(h, m) 513 So(err.Error(), ShouldEqual, "List add request rejected on warrant failure: expected old key to be modified on DHT") 514 515 }) 516 517 /* 518 getting a good warrant without also having already had the addToList happen is hard, 519 so not quite sure how to test this 520 Convey("LISTADD_REQUEST with good warrant should add to list", t, func() { 521 pid, oldPrivKey := makePeer("testPeer") 522 _, newPrivKey := makePeer("peer1") 523 revocation, _ := NewSelfRevocation(oldPrivKey, newPrivKey, []byte("extra data")) 524 w, _ := NewSelfRevocationWarrant(revocation) 525 data, _ := w.Encode() 526 m := h.node.NewMessage(LISTADD_REQUEST, 527 ListAddReq{ 528 ListType: BlockedList, 529 Peers: []string{peer.IDB58Encode(pid)}, 530 WarrantType: SelfRevocationType, 531 Warrant: data, 532 }) 533 r, err := ActionReceiver(h, m) 534 So(err, ShouldBeNil) 535 So(r.(HoldResp).Code, ShouldEqual, ReceiptOK) 536 537 peerList, err := h.dht.getList(BlockedList) 538 So(err, ShouldBeNil) 539 So(len(peerList.Records), ShouldEqual, 1) 540 So(peerList.Records[0].ID, ShouldEqual, pid) 541 }) 542 */ 543 544 } 545 546 func TestDHTDump(t *testing.T) { 547 d, _, h := PrepareTestChain("test") 548 defer CleanupTestChain(h, d) 549 550 ht := h.dht.ht.(*BuntHT) 551 Convey("dht dump of index 1 should show the agent put", t, func() { 552 msg, _ := ht.GetIdxMessage(1) 553 f, _ := msg.Fingerprint() 554 msgStr := msg.String() 555 556 str, err := ht.dumpIdx(1) 557 So(err, ShouldBeNil) 558 559 So(strings.Index(str, fmt.Sprintf("MSG (fingerprint %v)", f)) >= 0, ShouldBeTrue) 560 So(strings.Index(str, msgStr) >= 0, ShouldBeTrue) 561 }) 562 563 Convey("dht dump of index 99 should return err", t, func() { 564 _, err := ht.dumpIdx(99) 565 So(err.Error(), ShouldEqual, "no such change index") 566 }) 567 568 reviewHash := commit(h, "review", "this is my bogus review of the user") 569 commit(h, "rating", fmt.Sprintf(`{"Links":[{"Base":"%s","Link":"%s","Tag":"4stars"}]}`, h.nodeIDStr, reviewHash.String())) 570 571 Convey("dht.String() should produce human readable DHT", t, func() { 572 dump := h.dht.String() 573 So(dump, ShouldContainSubstring, "DHT changes: 5") 574 d, _ := ht.dumpIdx(1) 575 So(dump, ShouldContainSubstring, d) 576 d, _ = ht.dumpIdx(2) 577 So(dump, ShouldContainSubstring, d) 578 579 So(dump, ShouldContainSubstring, "DHT entries:") 580 So(dump, ShouldContainSubstring, fmt.Sprintf("Hash--%s (status 1)", h.nodeIDStr)) 581 pk, _ := h.agent.PubKey().Bytes() 582 So(dump, ShouldContainSubstring, fmt.Sprintf("Value: %s", string(b58.Encode(pk)))) 583 So(dump, ShouldContainSubstring, fmt.Sprintf("Sources: %s", h.nodeIDStr)) 584 585 So(dump, ShouldContainSubstring, fmt.Sprintf("Linked to: %s with tag %s", reviewHash, "4stars")) 586 }) 587 588 Convey("dht.JSON() should output DHT formatted as JSON string", t, func() { 589 dump, err := h.dht.JSON() 590 So(err, ShouldBeNil) 591 d, _ := ht.dumpIdxJSON(1) 592 So(NormaliseJSON(dump), ShouldContainSubstring, NormaliseJSON(d)) 593 d, _ = ht.dumpIdxJSON(2) 594 So(NormaliseJSON(dump), ShouldContainSubstring, NormaliseJSON(d)) 595 596 json := NormaliseJSON(dump) 597 matched, err := regexp.MatchString(`{"dht_changes":\[.*\],"dht_entries":\[.*\]}`, json) 598 So(err, ShouldBeNil) 599 So(matched, ShouldBeTrue) 600 }) 601 } 602 603 func TestDHTRetry(t *testing.T) { 604 d, _, h := PrepareTestChain("test") 605 defer CleanupTestChain(h, d) 606 607 d1 := `{"firstName":"Zippy","lastName":"Pinhead"}` 608 e := GobEntry{C: d1} 609 hash, _ := e.Sum(h.hashSpec) 610 d2 := `{"firstName":"Zerbina","lastName":"Pinhead"}` 611 e2 := GobEntry{C: d2} 612 hash2, _ := e2.Sum(h.hashSpec) 613 614 Convey("it should make a change after some retries", t, func() { 615 req := HoldReq{RelatedHash: hash, EntryHash: hash2} 616 m := h.node.NewMessage(MOD_REQUEST, req) 617 r, err := ActionReceiver(h, m) 618 So(err, ShouldBeNil) 619 So(r, ShouldEqual, DHTChangeUnknownHashQueuedForRetry) 620 621 // pause for a few retires 622 h.node.stoppers[RetryingStopper] = h.TaskTicker(time.Millisecond*10, RetryTask) 623 time.Sleep(time.Millisecond * 25) 624 625 // add the entries and get them into the DHT 626 h.NewEntry(time.Now(), "profile", &e) 627 h.NewEntry(time.Now(), "profile", &e2) 628 m = h.node.NewMessage(PUT_REQUEST, HoldReq{EntryHash: hash}) 629 err = h.dht.Put(m, "profile", hash, h.nodeID, []byte(d1), StatusLive) 630 So(err, ShouldBeNil) 631 m = h.node.NewMessage(PUT_REQUEST, HoldReq{EntryHash: hash2}) 632 err = h.dht.Put(m, "profile", hash2, h.nodeID, []byte(d2), StatusLive) 633 So(err, ShouldBeNil) 634 635 _, _, _, status, _ := h.dht.Get(hash, StatusAny, GetMaskAll) 636 So(status, ShouldEqual, StatusLive) 637 638 // wait for next retry 639 time.Sleep(time.Millisecond * 40) 640 641 _, _, _, status, _ = h.dht.Get(hash, StatusAny, GetMaskAll) 642 So(status, ShouldEqual, StatusModified) 643 644 // stop retrying for next test 645 stop := h.node.stoppers[RetryingStopper] 646 h.node.stoppers[RetryingStopper] = nil 647 stop <- true 648 649 }) 650 651 Convey("retries should be limited", t, func() { 652 e3 := GobEntry{C: `{"firstName":"Zappy","lastName":"Pinhead"}`} 653 hash3, _ := e3.Sum(h.hashSpec) 654 e4 := GobEntry{C: `{"firstName":"Zuppy","lastName":"Pinhead"}`} 655 hash4, _ := e4.Sum(h.hashSpec) 656 req := HoldReq{RelatedHash: hash3, EntryHash: hash4} 657 m := h.node.NewMessage(MOD_REQUEST, req) 658 r, err := ActionReceiver(h, m) 659 So(err, ShouldBeNil) 660 So(r, ShouldEqual, DHTChangeUnknownHashQueuedForRetry) 661 So(len(h.dht.retryQueue), ShouldEqual, 1) 662 663 interval := time.Millisecond * 10 664 h.node.stoppers[RetryingStopper] = h.TaskTicker(interval, RetryTask) 665 time.Sleep(interval * (MaxRetries + 2)) 666 So(len(h.dht.retryQueue), ShouldEqual, 0) 667 }) 668 } 669 670 func TestDHTMultiNode(t *testing.T) { 671 nodesCount := 10 672 mt := setupMultiNodeTesting(nodesCount) 673 defer mt.cleanupMultiNodeTesting() 674 nodes := mt.nodes 675 676 ringConnectMutual(t, mt.ctx, mt.nodes, nodesCount) 677 var connections int 678 Convey("each node should be able to get the key of others", t, func() { 679 for i := 0; i < nodesCount; i++ { 680 h1 := nodes[i] 681 for j := 0; j < nodesCount; j++ { 682 h2 := nodes[j] 683 options := GetOptions{StatusMask: StatusDefault} 684 req := GetReq{H: HashFromPeerID(h1.nodeID), StatusMask: options.StatusMask, GetMask: options.GetMask} 685 response, err := callGet(h2, req, &options) 686 if err != nil { 687 fmt.Printf("FAIL : %v couldn't get from %v err: %err\n", h2.nodeID, h1.nodeID, err) 688 } else { 689 e := response.(GetResp).Entry 690 responseStr := fmt.Sprintf("%v", e.Content()) 691 pk, _ := h1.agent.EncodePubKey() 692 expectedResponseStr := pk 693 if responseStr == expectedResponseStr { 694 connections += 1 695 //fmt.Printf("SUCCESS: %v got from %v\n", h2.nodeID, h1.nodeID) 696 } else { 697 //fmt.Printf("Expected:%s\n", expectedResponseStr) 698 //fmt.Printf("Got :%s\n", responseStr) 699 } 700 } 701 } 702 } 703 So(connections, ShouldEqual, nodesCount*nodesCount) 704 }) 705 706 hashes := []Hash{} 707 // add a bunch of data and links to that data on the key 708 for i := 0; i < nodesCount; i++ { 709 h := nodes[i] 710 hash := commit(h, "review", fmt.Sprintf("this statement by node %d (%v)", i, h.nodeID)) 711 commit(h, "rating", fmt.Sprintf(`{"Links":[{"Base":"%s","Link":"%s","Tag":"statement"}]}`, h.nodeIDStr, hash.String())) 712 hashes = append(hashes, hash) 713 } 714 715 Convey("each node should be able to get statements form the other nodes including self", t, func() { 716 for i := 0; i < nodesCount; i++ { 717 h1 := nodes[i] 718 for j := 0; j < nodesCount; j++ { 719 h2 := nodes[j] 720 options := GetLinksOptions{Load: true, StatusMask: StatusLive} 721 fn := &APIFnGetLinks{action: *NewGetLinksAction( 722 &LinkQuery{ 723 Base: HashFromPeerID(h1.nodeID), 724 T: "statement", 725 StatusMask: options.StatusMask, 726 }, &options)} 727 response, err := fn.Call(h2) 728 So(err, ShouldBeNil) 729 So(fmt.Sprintf("%v", response), ShouldEqual, fmt.Sprintf("&{[{%v this statement by node %d (%v) review %s}]}", hashes[i], i, h1.nodeID, h1.nodeID.Pretty())) 730 } 731 } 732 }) 733 } 734 735 func TestDHTMakeReciept(t *testing.T) { 736 d, _, h := PrepareTestChain("test") 737 defer CleanupTestChain(h, d) 738 739 hash, _ := NewHash("QmY8Mzg9F69e5P9AoQPYat655HEhc1TVGs11tmfNSzkqh2") 740 741 Convey("it should make a receipt and signature", t, func() { 742 msg := h.node.NewMessage(PUT_REQUEST, HoldReq{EntryHash: hash}) 743 744 data, err := MakeReceiptData(msg, ReceiptOK) 745 So(err, ShouldBeNil) 746 sig, err := h.Sign(data) 747 if err != nil { 748 panic(err) 749 } 750 751 receiptSig, err := h.dht.MakeReceiptSignature(msg, ReceiptOK) 752 So(err, ShouldBeNil) 753 So(receiptSig.Equal(sig), ShouldBeTrue) 754 755 matches, err := h.VerifySignature(receiptSig, string(data), h.agent.PubKey()) 756 So(err, ShouldBeNil) 757 So(matches, ShouldBeTrue) 758 759 holdResp, err := h.dht.MakeHoldResp(msg, StatusRejected) 760 So(err, ShouldBeNil) 761 So(holdResp.Code, ShouldEqual, ReceiptRejected) 762 matches, err = h.VerifySignature(holdResp.Signature, string(data), h.agent.PubKey()) 763 So(err, ShouldBeNil) 764 So(matches, ShouldBeFalse) 765 766 holdResp, err = h.dht.MakeHoldResp(msg, StatusLive) 767 So(err, ShouldBeNil) 768 So(holdResp.Code, ShouldEqual, ReceiptOK) 769 matches, err = h.VerifySignature(holdResp.Signature, string(data), h.agent.PubKey()) 770 So(err, ShouldBeNil) 771 So(matches, ShouldBeTrue) 772 }) 773 } 774 775 func processChangeRequestsInTesting(h *Holochain) { 776 for len(h.dht.changeQueue) > 0 { 777 req := <-h.dht.changeQueue 778 err := handleChangeRequests(h.dht, req) 779 if err != nil { 780 panic(err) 781 } 782 } 783 }