github.com/holochain/holochain-proto@v0.1.0-alpha-26.0.20200915073418-5c83169c9b5b/world.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 // implements managing and storing the world model for holochain nodes 5 6 package holochain 7 8 import ( 9 "errors" 10 . "github.com/holochain/holochain-proto/hash" 11 ic "github.com/libp2p/go-libp2p-crypto" 12 peer "github.com/libp2p/go-libp2p-peer" 13 pstore "github.com/libp2p/go-libp2p-peerstore" 14 "sync" 15 ) 16 17 // NodeRecord stores the necessary information about other nodes in the world model 18 type NodeRecord struct { 19 PeerInfo pstore.PeerInfo 20 PubKey ic.PubKey 21 IsHolding map[Hash]bool 22 } 23 24 // World holds the data of a nodes' world model 25 type World struct { 26 me peer.ID 27 nodes map[peer.ID]*NodeRecord 28 responsible map[Hash][]peer.ID 29 ht HashTable 30 log *Logger 31 32 lk sync.RWMutex 33 } 34 35 var ErrNodeNotFound = errors.New("node not found") 36 37 // NewWorld creates and empty world model 38 func NewWorld(me peer.ID, ht HashTable, logger *Logger) *World { 39 world := World{me: me} 40 world.nodes = make(map[peer.ID]*NodeRecord) 41 world.responsible = make(map[Hash][]peer.ID) 42 world.ht = ht 43 world.log = logger 44 return &world 45 } 46 47 // GetNodeRecord returns the peer's node record 48 // NOTE: do not modify the contents of the returned record! not thread safe 49 func (world *World) GetNodeRecord(ID peer.ID) (record *NodeRecord) { 50 world.lk.RLock() 51 defer world.lk.RUnlock() 52 record = world.nodes[ID] 53 return 54 } 55 56 // SetNodeHolding marks a node as holding a particular hash 57 func (world *World) SetNodeHolding(ID peer.ID, hash Hash) (err error) { 58 world.log.Logf("Setting Holding for %v of holding %v nodes:%v\n", ID, hash, world.nodes) 59 world.lk.Lock() 60 defer world.lk.Unlock() 61 record := world.nodes[ID] 62 if record == nil { 63 err = ErrNodeNotFound 64 return 65 } 66 record.IsHolding[hash] = true 67 return 68 } 69 70 // IsHolding returns whether a node is holding a particular hash 71 func (world *World) IsHolding(ID peer.ID, hash Hash) (holding bool, err error) { 72 world.lk.RLock() 73 defer world.lk.RUnlock() 74 world.log.Logf("Looking to see if %v is holding %v\n", ID, hash) 75 world.log.Logf("NODES:%v\n", world.nodes) 76 record := world.nodes[ID] 77 if record == nil { 78 err = ErrNodeNotFound 79 return 80 } 81 holding = record.IsHolding[hash] 82 return 83 } 84 85 // AllNodes returns a list of all the nodes in the world model. 86 func (world *World) AllNodes() (nodes []peer.ID, err error) { 87 world.lk.RLock() 88 defer world.lk.RUnlock() 89 nodes, err = world.allNodes() 90 return 91 } 92 93 func (world *World) allNodes() (nodes []peer.ID, err error) { 94 nodes = make([]peer.ID, len(world.nodes)) 95 96 i := 0 97 for k := range world.nodes { 98 nodes[i] = k 99 i++ 100 } 101 return 102 } 103 104 // AddNode adds a node to the world model 105 func (world *World) AddNode(pi pstore.PeerInfo, pubKey ic.PubKey) (err error) { 106 world.lk.Lock() 107 defer world.lk.Unlock() 108 rec := NodeRecord{PeerInfo: pi, PubKey: pubKey, IsHolding: make(map[Hash]bool)} 109 world.nodes[pi.ID] = &rec 110 return 111 } 112 113 // NodesByHash returns a sorted list of peers, including "me" by distance from a hash 114 func (world *World) nodesByHash(hash Hash) (nodes []peer.ID, err error) { 115 nodes, err = world.allNodes() 116 if err != nil { 117 return 118 } 119 nodes = append(nodes, world.me) 120 nodes = SortClosestPeers(nodes, hash) 121 return 122 } 123 124 /* 125 func (world *World) NodeRecordsByHash(hash Hash) (records []*NodeRecord, err error) { 126 127 records = make([]*NodeRecord, len(nodes)) 128 i := 0 129 for _, id := range nodes { 130 records[i] = world.nodes[id] 131 i++ 132 } 133 return 134 }*/ 135 136 // UpdateResponsible calculates the list of nodes believed to be responsible for a given hash 137 // note that if redundancy is 0 the assumption is that all nodes are responsible 138 func (world *World) UpdateResponsible(hash Hash, redundancy int) (responsible bool, err error) { 139 world.lk.Lock() 140 defer world.lk.Unlock() 141 var nodes []peer.ID 142 if redundancy == 0 { 143 world.responsible[hash] = nil 144 responsible = true 145 } else if redundancy > 1 { 146 nodes, err = world.nodesByHash(hash) 147 if err != nil { 148 return 149 } 150 // TODO add in resilince calculations with uptime 151 // see https://waffle.io/Holochain/holochain-proto/cards/5af33c5b8daa2d001cd1d051 152 i := 0 153 for i = 0; i < redundancy; i++ { 154 if nodes[i] == world.me { 155 responsible = true 156 break 157 } 158 } 159 // if me is included in the range of nodes that are close to the has 160 // add this hash (and other nodes) to the responsible map 161 // otherwise delete the item from the responsible map 162 if responsible { 163 // remove myself from the nodes list so I can add set the 164 // responsible nodes 165 world.log.Logf("Number of nodes: %d, Nodes:%v\n", len(nodes), nodes) 166 max := len(nodes) 167 if max > redundancy { 168 max = redundancy 169 } 170 nodes = append(nodes[:i], nodes[i+1:max]...) 171 world.responsible[hash] = nodes 172 world.log.Logf("Responsible for %v: %v", hash, nodes) 173 174 } else { 175 delete(world.responsible, hash) 176 } 177 } else { 178 panic("resiliency=1 not implemented") 179 } 180 return 181 } 182 183 // Responsible returns a list of all the entries I'm responsible for holding 184 func (world *World) Responsible() (entries []Hash, err error) { 185 world.lk.RLock() 186 defer world.lk.RUnlock() 187 entries = make([]Hash, len(world.responsible)) 188 189 i := 0 190 for k := range world.responsible { 191 entries[i] = k 192 i++ 193 } 194 return 195 } 196 197 // Overlap returns a list of all the nodes that overlap for a given hash 198 func (h *Holochain) Overlap(hash Hash) (overlap []peer.ID, err error) { 199 h.world.lk.RLock() 200 defer h.world.lk.RUnlock() 201 if h.nucleus.dna.DHTConfig.RedundancyFactor == 0 { 202 overlap, err = h.world.allNodes() 203 } else { 204 overlap = h.world.responsible[hash] 205 } 206 return 207 } 208 209 func myHashes(h *Holochain) (hashes []Hash) { 210 h.dht.Iterate(func(hash Hash) bool { 211 hashes = append(hashes, hash) 212 return true 213 }) 214 return 215 } 216 217 func HoldingTask(h *Holochain) { 218 // coholders := make(map[*NodeRecord][]Hash) 219 220 // to protect against crashes from background routines after close 221 if h.dht == nil { 222 return 223 } 224 hashes := myHashes(h) 225 for _, hash := range hashes { 226 if hash.String() == h.dnaHash.String() { 227 continue 228 } 229 230 // TODO forget the hashes we are no longer responsible for 231 // https://waffle.io/Holochain/holochain-proto/cards/5af33e3b361c27001d5348c6 232 // TODO this really shouldn't be called in the holding task 233 // but instead should be called with the Node list or hash list changes. 234 h.world.UpdateResponsible(hash, h.RedundancyFactor()) 235 h.world.log.Logf("HoldingTask: updated %v\n", hash) 236 overlap, err := h.Overlap(hash) 237 if err == nil { 238 h.world.log.Logf("HoldingTask: sending put requests to %d nodes\n", len(overlap)) 239 240 for _, node := range overlap { 241 // to protect against crashes from background routines after close 242 if h.node == nil { 243 return 244 } 245 /*rec := h.world.GetNodeRecord(node) 246 /* hashes := coholders[rec] 247 coholders[rec] = append(hashes, hash) 248 */ 249 h.world.log.Logf("HoldingTask: PUT_REQUEST sent to %v\n", node) 250 msg := h.node.NewMessage(PUT_REQUEST, HoldReq{EntryHash: hash}) 251 h.dht.sendChange(node, msg) 252 } 253 } 254 } 255 256 /* for rec, hashes := range coholders { 257 258 } 259 */ 260 }