// TODO: Maybe put these in some sort of "ipfs_testutil" package func _randPeer() *peer.Peer { p := new(peer.Peer) p.ID = make(peer.ID, 16) p.Addresses = []*ma.Multiaddr{nil} crand.Read(p.ID) return p }
// Ping a peer, log the time it took func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { // Thoughts: maybe this should accept an ID and do a peer lookup? u.DOut("Enter Ping.\n") pmes := Message{ID: swarm.GenerateMessageID(), Type: PBDHTMessage_PING} mes := swarm.NewMessage(p, pmes.ToProtobuf()) before := time.Now() responseChan := dht.listener.Listen(pmes.ID, 1, time.Minute) dht.netChan.Outgoing <- mes tout := time.After(timeout) select { case <-responseChan: roundtrip := time.Since(before) p.SetLatency(roundtrip) u.DOut("Ping took %s.\n", roundtrip.String()) return nil case <-tout: // Timed out, think about removing peer from network u.DOut("[%s] Ping peer [%s] timed out.", dht.self.ID.Pretty(), p.ID.Pretty()) dht.listener.Unlisten(pmes.ID) return u.ErrTimeout } }
// Handle getting ID from this peer and adding it into the map func (s *Swarm) handleNewConn(nconn net.Conn) { p := new(peer.Peer) conn := &Conn{ Peer: p, Addr: nil, Conn: nconn, } newConnChans(conn) sin, sout, err := ident.Handshake(s.local, p, conn.Incoming.MsgChan, conn.Outgoing.MsgChan) if err != nil { u.PErr("%v\n", err.Error()) conn.Close() return } // Get address to contact remote peer from addr := <-sin maddr, err := ma.NewMultiaddr(string(addr)) if err != nil { u.PErr("Got invalid address from peer.") s.Error(err) return } p.AddAddress(maddr) conn.secIn = sin conn.secOut = sout err = s.StartConn(conn) if err != nil { s.Error(err) } }
// If less than K nodes are in the entire network, it should fail when we make // a GET rpc and nobody has the value func TestLessThanKResponses(t *testing.T) { u.Debug = false fn := newFauxNet() fn.Listen() local := new(peer.Peer) local.ID = peer.ID("test_peer") d := NewDHT(local, fn, ds.NewMapDatastore()) d.Start() var ps []*peer.Peer for i := 0; i < 5; i++ { ps = append(ps, _randPeer()) d.Update(ps[i]) } other := _randPeer() // Reply with random peers to every message fn.AddHandler(func(mes *swarm.Message) *swarm.Message { pmes := new(PBDHTMessage) err := proto.Unmarshal(mes.Data, pmes) if err != nil { t.Fatal(err) } switch pmes.GetType() { case PBDHTMessage_GET_VALUE: resp := Message{ Type: pmes.GetType(), ID: pmes.GetId(), Response: true, Success: false, Peers: []*peer.Peer{other}, } return swarm.NewMessage(mes.Peer, resp.ToProtobuf()) default: panic("Shouldnt recieve this.") } }) _, err := d.GetValue(u.Key("hello"), time.Second*30) if err != nil { switch err { case u.ErrNotFound: //Success! return case u.ErrTimeout: t.Fatal("Should not have gotten timeout!") default: t.Fatalf("Got unexpected error: %s", err) } } t.Fatal("Expected to recieve an error.") }
func (bs *BitSwap) getLedger(p *peer.Peer) *Ledger { l, ok := bs.partners[p.Key()] if ok { return l } l = new(Ledger) l.Strategy = bs.strategy l.Partner = p bs.partners[p.Key()] = l return l }
// ConnectNew is for connecting to a peer when you dont know their ID, // Should only be used when you are sure that you arent already connected to peer in question func (s *Swarm) ConnectNew(addr *ma.Multiaddr) (*peer.Peer, error) { if addr == nil { return nil, errors.New("nil Multiaddr passed to swarm.Connect()") } npeer := new(peer.Peer) npeer.AddAddress(addr) conn, err := Dial("tcp", npeer) if err != nil { return nil, err } err = s.handleDialedCon(conn) return npeer, err }
// Update adds or moves the given peer to the front of its respective bucket // If a peer gets removed from a bucket, it is returned func (rt *RoutingTable) Update(p *peer.Peer) *peer.Peer { rt.tabLock.Lock() defer rt.tabLock.Unlock() peerID := ConvertPeerID(p.ID) cpl := xor(peerID, rt.local).commonPrefixLen() bucketID := cpl if bucketID >= len(rt.Buckets) { bucketID = len(rt.Buckets) - 1 } bucket := rt.Buckets[bucketID] e := bucket.find(p.ID) if e == nil { // New peer, add to bucket if p.GetLatency() > rt.maxLatency { // Connection doesnt meet requirements, skip! return nil } bucket.pushFront(p) // Are we past the max bucket size? if bucket.len() > rt.bucketsize { if bucketID == len(rt.Buckets)-1 { newBucket := bucket.Split(bucketID, rt.local) rt.Buckets = append(rt.Buckets, newBucket) if newBucket.len() > rt.bucketsize { // TODO: This is a very rare and annoying case panic("Case not handled.") } // If all elements were on left side of split... if bucket.len() > rt.bucketsize { return bucket.popBack() } } else { // If the bucket cant split kick out least active node return bucket.popBack() } } return nil } // If the peer is already in the table, move it to the front. // This signifies that it it "more active" and the less active nodes // Will as a result tend towards the back of the list bucket.moveToFront(e) return nil }
// Dial connects to a particular peer, over a given network // Example: Dial("udp", peer) func Dial(network string, peer *peer.Peer) (*Conn, error) { addr := peer.NetAddress(network) if addr == nil { return nil, fmt.Errorf("No address for network %s", network) } network, host, err := addr.DialArgs() if err != nil { return nil, err } nconn, err := net.Dial(network, host) if err != nil { return nil, err } conn := &Conn{ Peer: peer, Addr: addr, Conn: nconn, } newConnChans(conn) return conn, nil }
// Dial connects to a peer. // // The idea is that the client of Swarm does not need to know what network // the connection will happen over. Swarm can use whichever it choses. // This allows us to use various transport protocols, do NAT traversal/relay, // etc. to achive connection. // // For now, Dial uses only TCP. This will be extended. func (s *Swarm) Dial(peer *peer.Peer) (*Conn, error) { k := peer.Key() // check if we already have an open connection first s.connsLock.RLock() conn, found := s.conns[k] s.connsLock.RUnlock() if found { return conn, nil } // open connection to peer conn, err := Dial("tcp", peer) if err != nil { return nil, err } // add to conns s.connsLock.Lock() s.conns[k] = conn s.connsLock.Unlock() // kick off reader goroutine go s.fanIn(conn) return conn, nil }
// Dial connects to a peer. // // The idea is that the client of Swarm does not need to know what network // the connection will happen over. Swarm can use whichever it choses. // This allows us to use various transport protocols, do NAT traversal/relay, // etc. to achive connection. // // For now, Dial uses only TCP. This will be extended. func (s *Swarm) Dial(peer *peer.Peer) (*Conn, error, bool) { k := peer.Key() // check if we already have an open connection first s.connsLock.RLock() conn, found := s.conns[k] s.connsLock.RUnlock() if found { return conn, nil, true } // open connection to peer conn, err := Dial("tcp", peer) if err != nil { return nil, err, false } return conn, nil, false }
func makePeer(addr *ma.Multiaddr) *peer.Peer { p := new(peer.Peer) p.AddAddress(addr) sk, pk, err := ci.GenerateKeyPair(ci.RSA, 512) if err != nil { panic(err) } p.PrivKey = sk p.PubKey = pk id, err := identify.IDFromPubKey(pk) if err != nil { panic(err) } p.ID = id return p }
func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) { var addrs []*ma.Multiaddr for i := 0; i < 4; i++ { a, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 5000+i)) if err != nil { t.Fatal(err) } addrs = append(addrs, a) } var peers []*peer.Peer for i := 0; i < 4; i++ { p := new(peer.Peer) p.AddAddress(addrs[i]) sk, pk, err := ci.GenerateKeyPair(ci.RSA, 512) if err != nil { panic(err) } p.PubKey = pk p.PrivKey = sk id, err := identify.IDFromPubKey(pk) if err != nil { panic(err) } p.ID = id peers = append(peers, p) } var dhts []*IpfsDHT for i := 0; i < 4; i++ { net := swarm.NewSwarm(peers[i]) err := net.Listen() if err != nil { t.Fatal(err) } d := NewDHT(peers[i], net, ds.NewMapDatastore()) dhts = append(dhts, d) d.Start() } return addrs, peers, dhts }
func _randPeer() *peer.Peer { p := new(peer.Peer) p.ID = make(peer.ID, 16) crand.Read(p.ID) return p }
func TestGetFailures(t *testing.T) { fn := newFauxNet() fn.Listen() local := new(peer.Peer) local.ID = peer.ID("test_peer") d := NewDHT(local, fn, ds.NewMapDatastore()) other := &peer.Peer{ID: peer.ID("other_peer")} d.Start() d.Update(other) // This one should time out _, err := d.GetValue(u.Key("test"), time.Millisecond*10) if err != nil { if err != u.ErrTimeout { t.Fatal("Got different error than we expected.") } } else { t.Fatal("Did not get expected error!") } // Reply with failures to every message fn.AddHandler(func(mes *swarm.Message) *swarm.Message { pmes := new(PBDHTMessage) err := proto.Unmarshal(mes.Data, pmes) if err != nil { t.Fatal(err) } resp := Message{ Type: pmes.GetType(), ID: pmes.GetId(), Response: true, Success: false, } return swarm.NewMessage(mes.Peer, resp.ToProtobuf()) }) // This one should fail with NotFound _, err = d.GetValue(u.Key("test"), time.Millisecond*1000) if err != nil { if err != u.ErrNotFound { t.Fatalf("Expected ErrNotFound, got: %s", err) } } else { t.Fatal("expected error, got none.") } success := make(chan struct{}) fn.handlers = nil fn.AddHandler(func(mes *swarm.Message) *swarm.Message { resp := new(PBDHTMessage) err := proto.Unmarshal(mes.Data, resp) if err != nil { t.Fatal(err) } if resp.GetSuccess() { t.Fatal("Get returned success when it shouldnt have.") } success <- struct{}{} return nil }) // Now we test this DHT's handleGetValue failure req := Message{ Type: PBDHTMessage_GET_VALUE, Key: "hello", ID: swarm.GenerateMessageID(), Value: []byte{0}, } fn.Chan.Incoming <- swarm.NewMessage(other, req.ToProtobuf()) <-success }