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  }