mirror of
				https://github.com/libp2p/go-libp2p.git
				synced 2025-10-26 01:30:32 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			342 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			342 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package mocknet
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"sync"
 | |
| 	"time"
 | |
| 
 | |
| 	ic "github.com/jbenet/go-ipfs/p2p/crypto"
 | |
| 	host "github.com/jbenet/go-ipfs/p2p/host"
 | |
| 	bhost "github.com/jbenet/go-ipfs/p2p/host/basic"
 | |
| 	inet "github.com/jbenet/go-ipfs/p2p/net"
 | |
| 	peer "github.com/jbenet/go-ipfs/p2p/peer"
 | |
| 	testutil "github.com/jbenet/go-ipfs/util/testutil"
 | |
| 
 | |
| 	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
 | |
| 	ctxgroup "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup"
 | |
| 	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
 | |
| )
 | |
| 
 | |
| // mocknet implements mocknet.Mocknet
 | |
| type mocknet struct {
 | |
| 	nets  map[peer.ID]*peernet
 | |
| 	hosts map[peer.ID]*bhost.BasicHost
 | |
| 
 | |
| 	// links make it possible to connect two peers.
 | |
| 	// think of links as the physical medium.
 | |
| 	// usually only one, but there could be multiple
 | |
| 	// **links are shared between peers**
 | |
| 	links map[peer.ID]map[peer.ID]map[*link]struct{}
 | |
| 
 | |
| 	linkDefaults LinkOptions
 | |
| 
 | |
| 	cg ctxgroup.ContextGroup // for Context closing
 | |
| 	sync.RWMutex
 | |
| }
 | |
| 
 | |
| func New(ctx context.Context) Mocknet {
 | |
| 	return &mocknet{
 | |
| 		nets:  map[peer.ID]*peernet{},
 | |
| 		hosts: map[peer.ID]*bhost.BasicHost{},
 | |
| 		links: map[peer.ID]map[peer.ID]map[*link]struct{}{},
 | |
| 		cg:    ctxgroup.WithContext(ctx),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) GenPeer() (host.Host, error) {
 | |
| 	sk, _, err := testutil.SeededKeyPair(time.Now().UnixNano())
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	a := testutil.RandLocalTCPAddress()
 | |
| 
 | |
| 	h, err := mn.AddPeer(sk, a)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return h, nil
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) AddPeer(k ic.PrivKey, a ma.Multiaddr) (host.Host, error) {
 | |
| 	n, err := newPeernet(mn.cg.Context(), mn, k, a)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	h := bhost.New(n)
 | |
| 
 | |
| 	// make sure to add listening address!
 | |
| 	// this makes debugging things simpler as remembering to register
 | |
| 	// an address may cause unexpected failure.
 | |
| 	n.Peerstore().AddAddress(n.LocalPeer(), a)
 | |
| 	log.Debugf("mocknet added listen addr for peer: %s -- %s", n.LocalPeer(), a)
 | |
| 
 | |
| 	mn.cg.AddChildGroup(n.cg)
 | |
| 
 | |
| 	mn.Lock()
 | |
| 	mn.nets[n.peer] = n
 | |
| 	mn.hosts[n.peer] = h
 | |
| 	mn.Unlock()
 | |
| 	return h, nil
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) Peers() []peer.ID {
 | |
| 	mn.RLock()
 | |
| 	defer mn.RUnlock()
 | |
| 
 | |
| 	cp := make([]peer.ID, 0, len(mn.nets))
 | |
| 	for _, n := range mn.nets {
 | |
| 		cp = append(cp, n.peer)
 | |
| 	}
 | |
| 	return cp
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) Host(pid peer.ID) host.Host {
 | |
| 	mn.RLock()
 | |
| 	host := mn.hosts[pid]
 | |
| 	mn.RUnlock()
 | |
| 	return host
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) Net(pid peer.ID) inet.Network {
 | |
| 	mn.RLock()
 | |
| 	n := mn.nets[pid]
 | |
| 	mn.RUnlock()
 | |
| 	return n
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) Hosts() []host.Host {
 | |
| 	mn.RLock()
 | |
| 	defer mn.RUnlock()
 | |
| 
 | |
| 	cp := make([]host.Host, 0, len(mn.hosts))
 | |
| 	for _, h := range mn.hosts {
 | |
| 		cp = append(cp, h)
 | |
| 	}
 | |
| 	return cp
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) Nets() []inet.Network {
 | |
| 	mn.RLock()
 | |
| 	defer mn.RUnlock()
 | |
| 
 | |
| 	cp := make([]inet.Network, 0, len(mn.nets))
 | |
| 	for _, n := range mn.nets {
 | |
| 		cp = append(cp, n)
 | |
| 	}
 | |
| 	return cp
 | |
| }
 | |
| 
 | |
| // Links returns a copy of the internal link state map.
 | |
| // (wow, much map. so data structure. how compose. ahhh pointer)
 | |
| func (mn *mocknet) Links() LinkMap {
 | |
| 	mn.RLock()
 | |
| 	defer mn.RUnlock()
 | |
| 
 | |
| 	links := map[string]map[string]map[Link]struct{}{}
 | |
| 	for p1, lm := range mn.links {
 | |
| 		sp1 := string(p1)
 | |
| 		links[sp1] = map[string]map[Link]struct{}{}
 | |
| 		for p2, ls := range lm {
 | |
| 			sp2 := string(p2)
 | |
| 			links[sp1][sp2] = map[Link]struct{}{}
 | |
| 			for l := range ls {
 | |
| 				links[sp1][sp2][l] = struct{}{}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return links
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) LinkAll() error {
 | |
| 	nets := mn.Nets()
 | |
| 	for _, n1 := range nets {
 | |
| 		for _, n2 := range nets {
 | |
| 			if _, err := mn.LinkNets(n1, n2); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) LinkPeers(p1, p2 peer.ID) (Link, error) {
 | |
| 	mn.RLock()
 | |
| 	n1 := mn.nets[p1]
 | |
| 	n2 := mn.nets[p2]
 | |
| 	mn.RUnlock()
 | |
| 
 | |
| 	if n1 == nil {
 | |
| 		return nil, fmt.Errorf("network for p1 not in mocknet")
 | |
| 	}
 | |
| 
 | |
| 	if n2 == nil {
 | |
| 		return nil, fmt.Errorf("network for p2 not in mocknet")
 | |
| 	}
 | |
| 
 | |
| 	return mn.LinkNets(n1, n2)
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) validate(n inet.Network) (*peernet, error) {
 | |
| 	// WARNING: assumes locks acquired
 | |
| 
 | |
| 	nr, ok := n.(*peernet)
 | |
| 	if !ok {
 | |
| 		return nil, fmt.Errorf("Network not supported (use mock package nets only)")
 | |
| 	}
 | |
| 
 | |
| 	if _, found := mn.nets[nr.peer]; !found {
 | |
| 		return nil, fmt.Errorf("Network not on mocknet. is it from another mocknet?")
 | |
| 	}
 | |
| 
 | |
| 	return nr, nil
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) LinkNets(n1, n2 inet.Network) (Link, error) {
 | |
| 	mn.RLock()
 | |
| 	n1r, err1 := mn.validate(n1)
 | |
| 	n2r, err2 := mn.validate(n2)
 | |
| 	ld := mn.linkDefaults
 | |
| 	mn.RUnlock()
 | |
| 
 | |
| 	if err1 != nil {
 | |
| 		return nil, err1
 | |
| 	}
 | |
| 	if err2 != nil {
 | |
| 		return nil, err2
 | |
| 	}
 | |
| 
 | |
| 	l := newLink(mn, ld)
 | |
| 	l.nets = append(l.nets, n1r, n2r)
 | |
| 	mn.addLink(l)
 | |
| 	return l, nil
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) Unlink(l2 Link) error {
 | |
| 
 | |
| 	l, ok := l2.(*link)
 | |
| 	if !ok {
 | |
| 		return fmt.Errorf("only links from mocknet are supported")
 | |
| 	}
 | |
| 
 | |
| 	mn.removeLink(l)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) UnlinkPeers(p1, p2 peer.ID) error {
 | |
| 	ls := mn.LinksBetweenPeers(p1, p2)
 | |
| 	if ls == nil {
 | |
| 		return fmt.Errorf("no link between p1 and p2")
 | |
| 	}
 | |
| 
 | |
| 	for _, l := range ls {
 | |
| 		if err := mn.Unlink(l); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) UnlinkNets(n1, n2 inet.Network) error {
 | |
| 	return mn.UnlinkPeers(n1.LocalPeer(), n2.LocalPeer())
 | |
| }
 | |
| 
 | |
| // get from the links map. and lazily contruct.
 | |
| func (mn *mocknet) linksMapGet(p1, p2 peer.ID) *map[*link]struct{} {
 | |
| 
 | |
| 	l1, found := mn.links[p1]
 | |
| 	if !found {
 | |
| 		mn.links[p1] = map[peer.ID]map[*link]struct{}{}
 | |
| 		l1 = mn.links[p1] // so we make sure it's there.
 | |
| 	}
 | |
| 
 | |
| 	l2, found := l1[p2]
 | |
| 	if !found {
 | |
| 		m := map[*link]struct{}{}
 | |
| 		l1[p2] = m
 | |
| 		l2 = l1[p2]
 | |
| 	}
 | |
| 
 | |
| 	return &l2
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) addLink(l *link) {
 | |
| 	mn.Lock()
 | |
| 	defer mn.Unlock()
 | |
| 
 | |
| 	n1, n2 := l.nets[0], l.nets[1]
 | |
| 	(*mn.linksMapGet(n1.peer, n2.peer))[l] = struct{}{}
 | |
| 	(*mn.linksMapGet(n2.peer, n1.peer))[l] = struct{}{}
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) removeLink(l *link) {
 | |
| 	mn.Lock()
 | |
| 	defer mn.Unlock()
 | |
| 
 | |
| 	n1, n2 := l.nets[0], l.nets[1]
 | |
| 	delete(*mn.linksMapGet(n1.peer, n2.peer), l)
 | |
| 	delete(*mn.linksMapGet(n2.peer, n1.peer), l)
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) ConnectAll() error {
 | |
| 	nets := mn.Nets()
 | |
| 	for _, n1 := range nets {
 | |
| 		for _, n2 := range nets {
 | |
| 			if n1 == n2 {
 | |
| 				continue
 | |
| 			}
 | |
| 
 | |
| 			if _, err := mn.ConnectNets(n1, n2); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) ConnectPeers(a, b peer.ID) (inet.Conn, error) {
 | |
| 	return mn.Net(a).DialPeer(mn.cg.Context(), b)
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) ConnectNets(a, b inet.Network) (inet.Conn, error) {
 | |
| 	return a.DialPeer(mn.cg.Context(), b.LocalPeer())
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) DisconnectPeers(p1, p2 peer.ID) error {
 | |
| 	return mn.Net(p1).ClosePeer(p2)
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) DisconnectNets(n1, n2 inet.Network) error {
 | |
| 	return n1.ClosePeer(n2.LocalPeer())
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) LinksBetweenPeers(p1, p2 peer.ID) []Link {
 | |
| 	mn.RLock()
 | |
| 	defer mn.RUnlock()
 | |
| 
 | |
| 	ls2 := *mn.linksMapGet(p1, p2)
 | |
| 	cp := make([]Link, 0, len(ls2))
 | |
| 	for l := range ls2 {
 | |
| 		cp = append(cp, l)
 | |
| 	}
 | |
| 	return cp
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) LinksBetweenNets(n1, n2 inet.Network) []Link {
 | |
| 	return mn.LinksBetweenPeers(n1.LocalPeer(), n2.LocalPeer())
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) SetLinkDefaults(o LinkOptions) {
 | |
| 	mn.Lock()
 | |
| 	mn.linkDefaults = o
 | |
| 	mn.Unlock()
 | |
| }
 | |
| 
 | |
| func (mn *mocknet) LinkDefaults() LinkOptions {
 | |
| 	mn.RLock()
 | |
| 	defer mn.RUnlock()
 | |
| 	return mn.linkDefaults
 | |
| }
 | 
