github.com/metacurrency/holochain@v0.1.0-alpha-26.0.20200915073418-5c83169c9b5b/world_test.go (about) 1 package holochain 2 3 import ( 4 "fmt" 5 . "github.com/holochain/holochain-proto/hash" 6 peer "github.com/libp2p/go-libp2p-peer" 7 pstore "github.com/libp2p/go-libp2p-peerstore" 8 ma "github.com/multiformats/go-multiaddr" 9 . "github.com/smartystreets/goconvey/convey" 10 "testing" 11 "time" 12 ) 13 14 func testAddNodeToWorld(world *World, ID peer.ID, addr ma.Multiaddr) { 15 pi := pstore.PeerInfo{ID: ID, Addrs: []ma.Multiaddr{addr}} 16 err := world.AddNode(pi, nil) 17 if err != nil { 18 panic(err) 19 } 20 } 21 22 func testAddNodesToWorld(world *World, start, count int) (nodes []*Node) { 23 for i := start; i < start+count; i++ { 24 node, err := makeNode(1234, fmt.Sprintf("node%d", i)) 25 if err != nil { 26 panic(err) 27 } 28 nodes = append(nodes, node) 29 testAddNodeToWorld(world, node.HashAddr, node.NetAddr) 30 } 31 return 32 } 33 34 func TestWorldNodes(t *testing.T) { 35 b58 := "QmY8Mzg9F69e5P9AoQPYat655HEhc1TVGs11tmfNSzkqh2" 36 peer, _ := peer.IDB58Decode(b58) 37 38 ht := BuntHT{} 39 world := NewWorld(peer, &ht, nil) 40 41 Convey("to start with I should know about nobody", t, func() { 42 nodes, err := world.AllNodes() 43 So(err, ShouldBeNil) 44 So(nodes, ShouldBeEmpty) 45 }) 46 47 n := testAddNodesToWorld(world, 0, 1) 48 Convey("nodes can be added to the world model", t, func() { 49 nodes, err := world.AllNodes() 50 So(err, ShouldBeNil) 51 So(nodes[0], ShouldEqual, n[0].HashAddr) 52 }) 53 54 Convey("GetRecord should return the nodes data", t, func() { 55 record := world.GetNodeRecord(n[0].HashAddr) 56 So(record, ShouldNotBeNil) 57 So(record.PeerInfo.ID.Pretty(), ShouldEqual, n[0].HashAddr.Pretty()) 58 So(len(record.IsHolding), ShouldEqual, 0) 59 }) 60 61 hash, _ := NewHash("QmVGtdTZdTFaLsaj2RwdVG8jcjNNcp1DE914DKZ2kHmXHw") 62 63 Convey("SetNodeHolding should set a node as holding a given hash", t, func() { 64 err := world.SetNodeHolding(peer, hash) 65 So(err, ShouldEqual, ErrNodeNotFound) 66 67 theNode := n[0].HashAddr 68 holding, err := world.IsHolding(theNode, hash) 69 So(err, ShouldBeNil) 70 So(holding, ShouldBeFalse) 71 72 err = world.SetNodeHolding(theNode, hash) 73 74 holding, err = world.IsHolding(theNode, hash) 75 So(err, ShouldBeNil) 76 So(holding, ShouldBeTrue) 77 }) 78 79 Convey("nodes can be sorted by closeness to a hash", t, func() { 80 testAddNodesToWorld(world, 1, 5) 81 nodes, err := world.nodesByHash(hash) 82 So(err, ShouldBeNil) 83 So(len(nodes), ShouldEqual, 7) // 7 because NodesByHash should add in "me" too 84 So(distance(nodes[0], hash).Cmp(distance(nodes[1], hash)), ShouldBeLessThanOrEqualTo, 0) 85 So(distance(nodes[1], hash).Cmp(distance(nodes[2], hash)), ShouldBeLessThanOrEqualTo, 0) 86 So(distance(nodes[2], hash).Cmp(distance(nodes[3], hash)), ShouldBeLessThanOrEqualTo, 0) 87 So(distance(nodes[3], hash).Cmp(distance(nodes[4], hash)), ShouldBeLessThanOrEqualTo, 0) 88 So(distance(nodes[4], hash).Cmp(distance(nodes[5], hash)), ShouldBeLessThanOrEqualTo, 0) 89 So(distance(nodes[6], hash).Cmp(distance(nodes[6], hash)), ShouldBeLessThanOrEqualTo, 0) 90 }) 91 92 } 93 94 func TestWorldUpdateResponsible(t *testing.T) { 95 b58 := "QmY8Mzg9F69e5P9AoQPYat655HEhc1TVGs11tmfNSzkqh2" 96 var p1, p2, p3, p4, p5 peer.ID 97 var hash1, hash2, hash4 Hash 98 p1, _ = peer.IDB58Decode(b58) 99 ht := BuntHT{} 100 world := NewWorld(p1, &ht, nil) 101 var addr ma.Multiaddr 102 var err error 103 var responsible bool 104 testAddNodeToWorld(world, p1, addr) 105 Convey("you should always be responsible for yourself!", t, func() { 106 hash1 = HashFromPeerID(p1) 107 responsible, err := world.UpdateResponsible(hash1, 0) 108 So(err, ShouldBeNil) 109 So(responsible, ShouldBeTrue) 110 responsible, err = world.UpdateResponsible(hash1, 2) 111 So(err, ShouldBeNil) 112 So(responsible, ShouldBeTrue) 113 }) 114 Convey("you shouldn't be responsible for far away hashes", t, func() { 115 p2, _ = peer.IDB58Decode("QmY9Mzg9F69e5P9AoQPYat655HEhc1TVGs11tmfNSzkqh2") 116 testAddNodeToWorld(world, p2, addr) 117 p3, _ = peer.IDB58Decode("QmY9Mzg9F69e5P9AoQPYbt655HEhc1TVGs11tmfNSzkqh2") 118 testAddNodeToWorld(world, p3, addr) 119 p4, _ = peer.IDB58Decode("QmY8Mzg9F69e5P9AoQPYbt655HEhc1TVGs11tmfNSykqh2") 120 testAddNodeToWorld(world, p4, addr) 121 122 hash2 = HashFromPeerID(p2) 123 responsible, err := world.UpdateResponsible(hash2, 2) 124 So(err, ShouldBeNil) 125 So(responsible, ShouldBeFalse) 126 127 hash4 = HashFromPeerID(p4) 128 responsible, err = world.UpdateResponsible(hash4, 2) 129 So(err, ShouldBeNil) 130 So(responsible, ShouldBeTrue) 131 }) 132 Convey("when new closer nodes are added you should stop being responsible", t, func() { 133 p5, _ = peer.IDB58Decode("QmY8Mzg9F69e5P9AoQPYbt655HEhc1TVGs11tmfNSykqh1") 134 testAddNodeToWorld(world, p5, addr) 135 136 hash4 = HashFromPeerID(p4) 137 responsible, err = world.UpdateResponsible(hash4, 2) 138 So(err, ShouldBeNil) 139 So(responsible, ShouldBeFalse) 140 }) 141 } 142 143 func TestWorldOverlap(t *testing.T) { 144 nodesCount := 20 145 mt := setupMultiNodeTesting(nodesCount) 146 defer mt.cleanupMultiNodeTesting() 147 nodes := mt.nodes 148 h := nodes[0] 149 if !h.Config.EnableWorldModel { 150 return 151 } 152 starConnectMutual(t, mt.ctx, nodes, nodesCount) 153 154 Convey("it should add all nodes to the world model", t, func() { 155 nodes, err := h.world.AllNodes() 156 So(err, ShouldBeNil) 157 So(len(nodes), ShouldEqual, nodesCount-1) 158 }) 159 160 Convey("when redundancy is 0 overlap is 100%", t, func() { 161 for i := 0; i < nodesCount; i++ { 162 chain := nodes[i].Chain() 163 for _, hd := range chain.Headers { 164 responsible, err := h.world.UpdateResponsible(hd.EntryLink, 0) 165 So(err, ShouldBeNil) 166 So(responsible, ShouldBeTrue) 167 } 168 } 169 170 entries, err := h.world.Responsible() 171 So(err, ShouldBeNil) 172 So(len(entries), ShouldEqual, nodesCount+1) // all hashes are agent entries plus the DHA hash 173 174 for i := 0; i < nodesCount; i++ { 175 overlap, err := h.Overlap(nodes[i].AgentHash()) 176 So(err, ShouldBeNil) 177 So(len(overlap), ShouldEqual, nodesCount-1) //all the nodes except me 178 } 179 }) 180 181 Convey("when redundancy is 5, and assuming no uptime adjustment, overlap should be 4 nodes", t, func() { 182 r := 5 183 for i := 0; i < nodesCount; i++ { 184 nodes[i].nucleus.dna.DHTConfig.RedundancyFactor = r 185 chain := nodes[i].Chain() 186 for _, hd := range chain.Headers { 187 h.world.UpdateResponsible(hd.EntryLink, r) 188 } 189 } 190 191 entries, err := h.world.Responsible() 192 So(err, ShouldBeNil) 193 So(len(entries), ShouldEqual, 2) // I'm only responsible for some of the entries 194 195 // for all entries there should be 4 other nodes that I hold responsible for it. 196 for i := 0; i < len(entries); i++ { 197 overlap, err := h.Overlap(entries[i]) 198 So(err, ShouldBeNil) 199 So(len(overlap), ShouldEqual, 4) 200 } 201 }) 202 } 203 204 func SkipTestWorldHoldingTask(t *testing.T) { 205 nodesCount := 10 206 mt := setupMultiNodeTesting(nodesCount) 207 defer mt.cleanupMultiNodeTesting() 208 nodes := mt.nodes 209 if !nodes[0].Config.EnableWorldModel { 210 return 211 } 212 213 fullConnect(t, mt.ctx, nodes, nodesCount) 214 //randConnect(t, mt.ctx, nodes, nodesCount, 7, 4) 215 //starConnect(t, mt.ctx, nodes, nodesCount) 216 Convey("each node should have one other node from the ring connect", t, func() { 217 for i := 0; i < nodesCount; i++ { 218 others, err := nodes[i].world.AllNodes() 219 So(err, ShouldBeNil) 220 So(len(others), ShouldEqual, 1) 221 } 222 }) 223 224 Convey("each node should only have it's own puts", t, func() { 225 for i := 0; i < nodesCount; i++ { 226 hashes := myHashes(nodes[i]) 227 So(len(hashes), ShouldEqual, 3) 228 } 229 }) 230 231 /* Convey("HoldingTask should have all sent change requests to all responsible nodes", t, func() { 232 233 for i := 0; i < nodesCount; i++ { 234 nodes[i].nucleus.dna.DHTConfig.RedundancyFactor = 10 235 236 nodes[i].Config.gossipInterval = 0 237 nodes[i].Config.holdingCheckInterval = 0 238 nodes[i].StartBackgroundTasks() 239 } 240 241 HoldingTask(nodes[0]) 242 // processChangeRequestsInTesting(nodes[0]) 243 for i := 1; i < nodesCount; i++ { 244 // processChangeRequestsInTesting(nodes[i]) 245 246 puts, err := nodes[i].dht.GetPuts(0) 247 So(err, ShouldBeNil) 248 fmt.Printf("Puts for %v: %d\n", nodes[i].nodeID.Pretty(), len(puts)) 249 // So(len(puts), ShouldEqual, 5) 250 } 251 })*/ 252 253 Convey("each node should have everybody's puts after enough propigation time", t, func() { 254 r := 2 255 for i := 0; i < nodesCount; i++ { 256 nodes[i].nucleus.dna.DHTConfig.RedundancyFactor = r 257 nodes[i].Config.gossipInterval = 0 258 nodes[i].Config.holdingCheckInterval = 200 * time.Millisecond 259 nodes[i].StartBackgroundTasks() 260 } 261 262 checkPropigated(nodes, r) 263 264 start := time.Now() 265 propigated := false 266 ticker := time.NewTicker(210 * time.Millisecond) 267 stop := make(chan bool, 1) 268 269 go func() { 270 for tick := range ticker.C { 271 // abort just in case in 4 seconds (only if propgation fails) 272 if tick.Sub(start) > (10 * time.Second) { 273 //fmt.Printf("Aborting!") 274 stop <- true 275 return 276 } 277 278 propigated = checkPropigated(nodes, r) 279 if propigated { 280 stop <- true 281 return 282 } 283 } 284 }() 285 <-stop 286 ticker.Stop() 287 So(propigated, ShouldBeTrue) 288 }) 289 } 290 291 func checkPropigated(nodes []*Holochain, redundancy int) bool { 292 nodesCount := len(nodes) 293 propigated := true 294 allHashes := make(map[Hash]int) 295 var required int 296 if redundancy == 0 { 297 required = nodesCount 298 } else { 299 required = redundancy 300 } 301 // check to see if the nodes have all gotten the puts yet. 302 for i := 0; i < nodesCount; i++ { 303 hashes := myHashes(nodes[i]) 304 if len(hashes) < nodesCount*2+1 { 305 propigated = false 306 } 307 puts, err := nodes[i].dht.GetPuts(0) 308 if err != nil { 309 panic(err) 310 } 311 fmt.Printf("NODE%d(%s):%d- %d:", i, nodes[i].nodeID.Pretty()[2:4], len(puts), len(hashes)) 312 for j := 0; j < len(hashes); j++ { 313 hash := hashes[j] 314 //nodes[i].world.UpdateResponsible(hash, r) 315 316 count, ok := allHashes[hash] 317 if !ok { 318 allHashes[hash] = 1 319 } else { 320 allHashes[hash] = count + 1 321 } 322 fmt.Printf("%s,", hash.String()[2:4]) 323 } 324 fmt.Printf("\n") 325 326 fmt.Printf(" knows about:") 327 others, err := nodes[i].world.AllNodes() 328 for j := 0; j < len(others); j++ { 329 fmt.Printf("%s,", others[j].Pretty()[2:4]) 330 } 331 fmt.Printf("\n") 332 333 /* for j := 0; j < len(hashes); j++ { 334 hash := hashes[j] 335 fmt.Printf(" RESPONSIBLE for %v:\n %v\n", hash.String()[2:4], nodes[i].world.responsible[hash]) 336 }*/ 337 338 } 339 fmt.Printf("HashCounts(%d): ", len(allHashes)) 340 propigated = true 341 for hash, count := range allHashes { 342 if count < required { 343 propigated = false 344 } 345 fmt.Printf("%s:%d, ", hash.String()[2:4], count) 346 } 347 fmt.Printf("\n\n") 348 349 return propigated 350 }