@ -1,88 +1,109 @@
package main
import (
"bufio"
"context"
"flag"
"fmt"
"io/ioutil"
"log"
"math/rand"
"strings"
"time"
golog "github.com/ipfs/go-log"
crypto "github.com/libp2p/go-libp2p-crypto"
host "github.com/libp2p/go-libp2p-host"
net "github.com/libp2p/go-libp2p-net"
peer "github.com/libp2p/go-libp2p-peer"
pstore "github.com/libp2p/go-libp2p-peerstore"
swarm "github.com/libp2p/go-libp2p-swarm"
bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
testutil "github.com/libp2p/go-testutil"
ma "github.com/multiformats/go-multiaddr"
gologging "github.com/whyrusleeping/go-logging"
bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
)
// create a 'Host' with a random peer to listen on the given address
func makeBasicHost ( listen string , secio bool ) ( host . Host , error ) {
addr , err := ma . NewMultiaddr ( listen )
// makeBasicHost creates a LibP2P host with a random peer ID listening on the
// given multiaddress. It will use secio if secio is true.
func makeBasicHost ( listenPort int , secio bool ) ( host . Host , error ) {
// Generate a key pair for this host. We will use it at least
// to obtain a valid host ID.
priv , pub , err := crypto . GenerateKeyPair ( crypto . RSA , 2048 )
if err != nil {
return nil , err
}
// Obtain Peer ID from public key
pid , err := peer . IDFromPublicKey ( pub )
if err != nil {
return nil , err
}
// Create a multiaddress
addr , err := ma . NewMultiaddr ( fmt . Sprintf ( "/ip4/127.0.0.1/tcp/%d" , listenPort ) )
if err != nil {
return nil , err
}
// Create a peerstore
ps := pstore . NewPeerstore ( )
var pid peer . ID
// If using secio, we add the keys to the peerstore
// for this peer ID.
if secio {
ident , err := testutil . RandIdentity ( )
if err != nil {
return nil , err
}
ident . PrivateKey ( )
ps . AddPrivKey ( ident . ID ( ) , ident . PrivateKey ( ) )
ps . AddPubKey ( ident . ID ( ) , ident . PublicKey ( ) )
pid = ident . ID ( )
} else {
fakepid , err := testutil . RandPeerID ( )
if err != nil {
return nil , err
}
pid = fakepid
ps . AddPrivKey ( pid , priv )
ps . AddPubKey ( pid , pub )
}
ctx := context . Background ( )
// Create swarm (implements libP2P Network)
netwrk , err := swarm . NewNetwork (
context . Background ( ) ,
[ ] ma . Multiaddr { addr } ,
pid ,
ps ,
nil )
// create a new swarm to be used by the service host
netw , err := swarm . NewNetwork ( ctx , [ ] ma . Multiaddr { addr } , pid , ps , nil )
if err != nil {
return nil , err
basicHost := bhost . New ( netwrk )
// Build host multiaddress
hostAddr , _ := ma . NewMultiaddr ( fmt . Sprintf ( "/ipfs/%s" , basicHost . ID ( ) . Pretty ( ) ) )
// Now we can build a full multiaddress to reach this host
// by encapsulating both addresses:
fullAddr := addr . Encapsulate ( hostAddr )
log . Printf ( "I am %s\n" , fullAddr )
if secio {
log . Printf ( "Now run \"./echo -l %d -d %s -secio\" on a different terminal\n" , listenPort + 1 , fullAddr )
} else {
log . Printf ( "Now run \"./echo -l %d -d %s\" on a different terminal\n" , listenPort + 1 , fullAddr )
}
log . Printf ( "I am %s/ipfs/%s\n" , addr , pid . Pretty ( ) )
return bhost . New ( netw ) , nil
return basicHost , nil
}
func main ( ) {
rand . Seed ( time . Now ( ) . UnixNano ( ) )
// LibP2P code uses golog to log messages. They log with different
// string IDs (i.e. "swarm"). We can control the verbosity level for
// all loggers with:
golog . SetAllLoggers ( gologging . INFO ) // Change to DEBUG for extra info
// Parse options from the command line
listenF := flag . Int ( "l" , 0 , "wait for incoming connections" )
target := flag . String ( "d" , "" , "target peer to dial" )
secio := flag . Bool ( "secio" , false , "enable secio" )
flag . Parse ( )
if * listenF == 0 {
log . Fatal ( "Please provide a port to bind on with -l" )
}
listenaddr := fmt . Sprintf ( "/ip4/127.0.0.1/tcp/%d" , * listenF )
ha , err := makeBasicHost ( listenaddr , * secio )
// Make a host that listens on the given multiaddress
ha , err := makeBasicHost ( * listenF , * secio )
if err != nil {
log . Fatal ( err )
}
// Set a stream handler on host A
// Set a stream handler on host A. /echo/1.0.0 is
// a user-defined protocol name.
ha . SetStreamHandler ( "/echo/1.0.0" , func ( s net . Stream ) {
log . Println ( "Got a new stream!" )
defer s . Close ( )
@ -93,8 +114,10 @@ func main() {
log . Println ( "listening for connections" )
select { } // hang forever
}
// This is where the listener code ends
/**** This is where the listener code ends ****/
// The following code extracts target's the peer ID from the
// given multiaddress
ipfsaddr , err := ma . NewMultiaddr ( * target )
if err != nil {
log . Fatalln ( err )
@ -110,26 +133,26 @@ func main() {
log . Fatalln ( err )
}
tptaddr := strings . Split ( ipfsaddr . String ( ) , "/ipfs/" ) [ 0 ]
// This creates a MA with the "/ip4/ipaddr/tcp/port" part of the target
tptmaddr , err := ma . NewMultiaddr ( tptaddr )
if err != nil {
log . Fatalln ( err )
}
// Decapsulate the /ipfs/<peerID> part from the target
// /ip4/<a.b.c.d>/ipfs/<peer> becomes /ip4/<a.b.c.d>
targetPeerAddr , _ := ma . NewMultiaddr (
fmt . Sprintf ( "/ipfs/%s" , peer . IDB58Encode ( peerid ) ) )
targetAddr := ipfsaddr . Decapsulate ( targetPeerAddr )
// We need to add the target to our peerstore, so we know how we can
// contact it
ha . Peerstore ( ) . AddAddr ( peerid , tptm addr , pstore . PermanentAddrTTL )
// We have a peer ID and a targetAddr so we add it to the peerstore
// so LibP2P knows how to contact it
ha . Peerstore ( ) . AddAddr ( peerid , targetA ddr , peer store . PermanentAddrTTL )
log . Println ( "opening stream" )
// make a new stream from host B to host A
// it should be handled on host A by the handler we set above
// it should be handled on host A by the handler we set above because
// we use the same /echo/1.0.0 protocol
s , err := ha . NewStream ( context . Background ( ) , peerid , "/echo/1.0.0" )
if err != nil {
log . Fatalln ( err )
}
_ , err = s . Write ( [ ] byte ( "Hello, world!" ) )
_ , err = s . Write ( [ ] byte ( "Hello, world!\n " ) )
if err != nil {
log . Fatalln ( err )
}
@ -142,18 +165,17 @@ func main() {
log . Printf ( "read reply: %q\n" , out )
}
// doEcho reads some data from a stream, writes it back and closes the
// stream.
// doEcho reads a line of data a stream and writes it back
func doEcho ( s net . Stream ) {
buf := make ( [ ] byte , 1024 )
n , err := s . Read ( buf )
buf := bufio . NewReader ( s )
str , err := buf . ReadString ( '\n' )
if err != nil {
log . Println ( err )
return
}
log . Printf ( "read request: %q\n" , buf [ : n ] )
_ , err = s . Write ( buf [ : n ] )
log . Printf ( "read: %s\n" , str )
_ , err = s . Write ( [ ] byte ( str ) )
if err != nil {
log . Println ( err )
return