From 6f9c297f0876d883f102e4fe09c60c61d5712d36 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 20 Jan 2015 12:29:02 -0800 Subject: [PATCH 01/13] update multiaddr for ipfs addrs --- Godeps/Godeps.json | 4 +- .../jbenet/go-multiaddr/.travis.yml | 3 +- .../github.com/jbenet/go-multiaddr/codec.go | 100 ++++++++++++++---- .../jbenet/go-multiaddr/multiaddr.go | 6 +- .../jbenet/go-multiaddr/multiaddr_test.go | 12 ++- .../jbenet/go-multiaddr/protocols.csv | 1 + .../jbenet/go-multiaddr/protocols.go | 31 ++++-- 7 files changed, 116 insertions(+), 41 deletions(-) diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 63fb64d66..5d78f52df 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -167,8 +167,8 @@ }, { "ImportPath": "github.com/jbenet/go-multiaddr", - "Comment": "0.1.2-34-g0d7b54b", - "Rev": "0d7b54ba432fda14bac37cdad717bd6270eacc85" + "Comment": "0.1.2-38-gc13f11b", + "Rev": "c13f11bbfe6439771f4df7bfb330f686826144e8" }, { "ImportPath": "github.com/jbenet/go-multiaddr-net", diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/.travis.yml b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/.travis.yml index 32ce5a786..7b571f400 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/.travis.yml +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/.travis.yml @@ -1,10 +1,9 @@ language: go go: - - 1.2 - 1.3 - release - tip script: - - go test -v ./... + - go test -race -cpu=5 -v ./... diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/codec.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/codec.go index 03502e9dd..a8a9b31c5 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/codec.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/codec.go @@ -2,10 +2,13 @@ package multiaddr import ( "encoding/binary" + "errors" "fmt" "net" "strconv" "strings" + + mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" ) func stringToBytes(s string) ([]byte, error) { @@ -31,17 +34,19 @@ func stringToBytes(s string) ([]byte, error) { b = append(b, CodeToVarint(p.Code)...) sp = sp[1:] - if p.Size > 0 { - if len(sp) < 1 { - return nil, fmt.Errorf("protocol requires address, none given: %s", p.Name) - } - a, err := addressStringToBytes(p, sp[0]) - if err != nil { - return nil, fmt.Errorf("failed to parse %s: %s %s", p.Name, sp[0], err) - } - b = append(b, a...) - sp = sp[1:] + if p.Size == 0 { // no length. + continue } + + if len(sp) < 1 { + return nil, fmt.Errorf("protocol requires address, none given: %s", p.Name) + } + a, err := addressStringToBytes(p, sp[0]) + if err != nil { + return nil, fmt.Errorf("failed to parse %s: %s %s", p.Name, sp[0], err) + } + b = append(b, a...) + sp = sp[1:] } return b, nil } @@ -51,7 +56,14 @@ func bytesToString(b []byte) (ret string, err error) { defer func() { if e := recover(); e != nil { ret = "" - err = e.(error) + switch e := e.(type) { + case error: + err = e + case string: + err = errors.New(e) + default: + err = fmt.Errorf("%v", e) + } } }() @@ -65,20 +77,38 @@ func bytesToString(b []byte) (ret string, err error) { if p.Code == 0 { return "", fmt.Errorf("no protocol with code %d", code) } - s = strings.Join([]string{s, "/", p.Name}, "") + s += "/" + p.Name - if p.Size > 0 { - a := addressBytesToString(p, b[:(p.Size/8)]) - if len(a) > 0 { - s = strings.Join([]string{s, "/", a}, "") - } - b = b[(p.Size / 8):] + if p.Size == 0 { + continue } + + size := sizeForAddr(p, b) + a, err := addressBytesToString(p, b[:size]) + if err != nil { + return "", err + } + if len(a) > 0 { + s += "/" + a + } + b = b[size:] } return s, nil } +func sizeForAddr(p Protocol, b []byte) int { + switch { + case p.Size > 0: + return (p.Size / 8) + case p.Size == 0: + return 0 + default: + size, n := ReadVarintCode(b) + return size + n + } +} + func bytesSplit(b []byte) (ret [][]byte, err error) { // panic handler, in case we try accessing bytes incorrectly. defer func() { @@ -96,7 +126,8 @@ func bytesSplit(b []byte) (ret [][]byte, err error) { return [][]byte{}, fmt.Errorf("no protocol with code %d", b[0]) } - length := n + (p.Size / 8) + size := sizeForAddr(p, b[n:]) + length := n + size ret = append(ret, b[:length]) b = b[length:] } @@ -133,23 +164,46 @@ func addressStringToBytes(p Protocol, s string) ([]byte, error) { b := make([]byte, 2) binary.BigEndian.PutUint16(b, uint16(i)) return b, nil + + case P_IPFS: // ipfs + // the address is a varint prefixed multihash string representation + m, err := mh.FromB58String(s) + if err != nil { + return nil, fmt.Errorf("failed to parse ipfs addr: %s %s", s, err) + } + size := CodeToVarint(len(m)) + b := append(size, m...) + return b, nil } return []byte{}, fmt.Errorf("failed to parse %s addr: unknown", p.Name) } -func addressBytesToString(p Protocol, b []byte) string { +func addressBytesToString(p Protocol, b []byte) (string, error) { switch p.Code { // ipv4,6 case P_IP4, P_IP6: - return net.IP(b).String() + return net.IP(b).String(), nil // tcp udp dccp sctp case P_TCP, P_UDP, P_DCCP, P_SCTP: i := binary.BigEndian.Uint16(b) - return strconv.Itoa(int(i)) + return strconv.Itoa(int(i)), nil + + case P_IPFS: // ipfs + // the address is a varint-prefixed multihash string representation + size, n := ReadVarintCode(b) + b = b[n:] + if len(b) != size { + panic("inconsistent lengths") + } + m, err := mh.Cast(b) + if err != nil { + return "", err + } + return m.B58String(), nil } - return "" + return "", fmt.Errorf("unknown protocol") } diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr.go index 373c2c193..1cb7ad4e9 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr.go @@ -64,6 +64,7 @@ func (m *multiaddr) Protocols() []Protocol { } }() + size := 0 ps := []Protocol{} b := m.bytes[:] for len(b) > 0 { @@ -75,7 +76,10 @@ func (m *multiaddr) Protocols() []Protocol { panic(fmt.Errorf("no protocol with code %d", b[0])) } ps = append(ps, p) - b = b[n+(p.Size/8):] + b = b[n:] + + size = sizeForAddr(p, b) + b = b[size:] } return ps } diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr_test.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr_test.go index 654589764..f0e9c36cf 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr_test.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr_test.go @@ -32,11 +32,13 @@ func TestConstructFails(t *testing.T) { "/ip4/127.0.0.1/udp", "/ip4/127.0.0.1/tcp/jfodsajfidosajfoidsa", "/ip4/127.0.0.1/tcp", + "/ip4/127.0.0.1/ipfs", + "/ip4/127.0.0.1/ipfs/tcp", } for _, a := range cases { if _, err := NewMultiaddr(a); err == nil { - t.Errorf("should have failed: %s", a) + t.Errorf("should have failed: %s - %s", a, err) } } } @@ -55,18 +57,24 @@ func TestConstructSucceeds(t *testing.T) { "/sctp/1234", "/udp/65535", "/tcp/65535", + "/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", "/udp/1234/sctp/1234", "/udp/1234/udt", "/udp/1234/utp", + "/tcp/1234/http", + "/tcp/1234/https", + "/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234", "/ip4/127.0.0.1/udp/1234", "/ip4/127.0.0.1/udp/0", "/ip4/127.0.0.1/tcp/1234", "/ip4/127.0.0.1/tcp/1234/", + "/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", + "/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234", } for _, a := range cases { if _, err := NewMultiaddr(a); err != nil { - t.Errorf("should have succeeded: %s", a) + t.Errorf("should have succeeded: %s -- %s", a, err) } } } diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.csv b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.csv index a8b1e3a47..213e9b52b 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.csv +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.csv @@ -7,5 +7,6 @@ code size name 132 16 sctp 301 0 udt 302 0 utp +421 V ipfs 480 0 http 443 0 https diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.go index eaddc615e..c4ee5df7a 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.go @@ -9,7 +9,7 @@ import ( // Protocol is a Multiaddr protocol description structure. type Protocol struct { Code int - Size int + Size int // a size of -1 indicates a length-prefixed variable size Name string VCode []byte } @@ -19,14 +19,22 @@ type Protocol struct { // 2. ensuring errors in the csv don't screw up code. // 3. changing a number has to happen in two places. const ( - P_IP4 = 4 - P_TCP = 6 - P_UDP = 17 - P_DCCP = 33 - P_IP6 = 41 - P_SCTP = 132 - P_UTP = 301 - P_UDT = 302 + P_IP4 = 4 + P_TCP = 6 + P_UDP = 17 + P_DCCP = 33 + P_IP6 = 41 + P_SCTP = 132 + P_UTP = 301 + P_UDT = 302 + P_IPFS = 421 + P_HTTP = 480 + P_HTTPS = 443 +) + +// These are special sizes +const ( + LengthPrefixedVarSize = -1 ) // Protocols is the list of multiaddr protocols supported by this module. @@ -40,8 +48,9 @@ var Protocols = []Protocol{ Protocol{P_SCTP, 16, "sctp", CodeToVarint(P_SCTP)}, Protocol{P_UTP, 0, "utp", CodeToVarint(P_UTP)}, Protocol{P_UDT, 0, "udt", CodeToVarint(P_UDT)}, - // {480, 0, "http"}, - // {443, 0, "https"}, + Protocol{P_HTTP, 0, "http", CodeToVarint(P_HTTP)}, + Protocol{P_HTTPS, 0, "https", CodeToVarint(P_HTTPS)}, + Protocol{P_IPFS, LengthPrefixedVarSize, "ipfs", CodeToVarint(P_IPFS)}, } // ProtocolWithName returns the Protocol description with given string name. From 2e6be0b1997820a6255de59173ce669f31a2de9f Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 20 Jan 2015 14:22:17 -0800 Subject: [PATCH 02/13] util/ipfsaddr: Multiaddr() + ID() --- util/ipfsaddr/ipfsaddr.go | 80 ++++++++++++++++++++++ util/ipfsaddr/ipfsaddr_test.go | 117 +++++++++++++++++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 util/ipfsaddr/ipfsaddr.go create mode 100644 util/ipfsaddr/ipfsaddr_test.go diff --git a/util/ipfsaddr/ipfsaddr.go b/util/ipfsaddr/ipfsaddr.go new file mode 100644 index 000000000..4c1f54a1a --- /dev/null +++ b/util/ipfsaddr/ipfsaddr.go @@ -0,0 +1,80 @@ +package ipfsaddr + +import ( + "errors" + "strings" + + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + + peer "github.com/jbenet/go-ipfs/p2p/peer" +) + +// ErrInvalidAddr signals an address is not a valid ipfs address. +var ErrInvalidAddr = errors.New("invalid ipfs address") + +type IPFSAddr interface { + ID() peer.ID + Multiaddr() ma.Multiaddr +} + +type ipfsAddr struct { + ma ma.Multiaddr + id peer.ID +} + +func (a ipfsAddr) ID() peer.ID { + return a.id +} + +func (a ipfsAddr) Multiaddr() ma.Multiaddr { + return a.ma +} + +// ParseString parses a string representation of an address into an IPFSAddr +func ParseString(str string) (a IPFSAddr, err error) { + if str == "" { + return nil, ErrInvalidAddr + } + + m, err := ma.NewMultiaddr(str) + if err != nil { + return nil, err + } + + return ParseMultiaddr(m) +} + +// ParseMultiaddr parses a multiaddr into an IPFSAddr +func ParseMultiaddr(m ma.Multiaddr) (a IPFSAddr, err error) { + // // never panic. + // defer func() { + // if r := recover(); r != nil { + // a = nil + // err = ErrInvalidAddr + // } + // }() + + if m == nil { + return nil, ErrInvalidAddr + } + + // make sure it's an ipfs addr + parts := ma.Split(m) + if len(parts) < 1 { + return nil, ErrInvalidAddr + } + ipfspart := parts[len(parts)-1] // last part + if ipfspart.Protocols()[0].Code != ma.P_IPFS { + return nil, ErrInvalidAddr + } + + // make sure ipfs id parses as a peer.ID + peerIdParts := strings.Split(ipfspart.String(), "/") + peerIdStr := peerIdParts[len(peerIdParts)-1] + id, err := peer.IDB58Decode(peerIdStr) + if err != nil { + return nil, err + } + + return ipfsAddr{ma: m, id: id}, nil +} diff --git a/util/ipfsaddr/ipfsaddr_test.go b/util/ipfsaddr/ipfsaddr_test.go new file mode 100644 index 000000000..d5e655e9d --- /dev/null +++ b/util/ipfsaddr/ipfsaddr_test.go @@ -0,0 +1,117 @@ +package ipfsaddr + +import ( + "strings" + "testing" + + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + peer "github.com/jbenet/go-ipfs/p2p/peer" +) + +var good = []string{ + "/ipfs/5dru6bJPUM1B7N69528u49DJiWZnok", + "/ipfs/kTRX47RthhwNzWdi6ggwqjuX", + "/ipfs/QmUCseQWXCSrhf9edzVKTvoj8o8Ts5aXFGNPameZRPJ6uR", + "/ip4/1.2.3.4/tcp/1234/ipfs/5dru6bJPUM1B7N69528u49DJiWZnok", + "/ip4/1.2.3.4/tcp/1234/ipfs/kTRX47RthhwNzWdi6ggwqjuX", + "/ip4/1.2.3.4/tcp/1234/ipfs/QmUCseQWXCSrhf9edzVKTvoj8o8Ts5aXFGNPameZRPJ6uR", +} + +var bad = []string{ + "5dru6bJPUM1B7N69528u49DJiWZnok", // bad ma + "kTRX47RthhwNzWdi6ggwqjuX", // bad ma + "QmUCseQWXCSrhf9edzVKTvoj8o8Ts5aXFGNPameZRPJ6uR", // bad ma + "ipfs/5dru6bJPUM1B7N69528u49DJiWZnok", // bad ma + "ipfs/kTRX47RthhwNzWdi6ggwqjuX", // bad ma + "ipfs/QmUCseQWXCSrhf9edzVKTvoj8o8Ts5aXFGNPameZRPJ6uR", // bad ma + "/ipfs/5dru6bJPUM1B7N69528u49DJiWZno", // bad mh + "/ipfs/kTRX47RthhwNzWdi6ggwqju", // bad mh + "/ipfs/QmUCseQWXCSrhf9edzVKTvj8o8Ts5aXFGNPameZRPJ6uR", // bad mh + "/ipfs/QmUCseQWXCSrhf9edzVKTvoj8o8Ts5aXFGNPameZRPJ6uR/tcp/1234", // ipfs not last + "/ip4/1.2.3.4/tcp/ipfs/5dru6bJPUM1B7N69528u49DJiWZnok", // bad tcp part + "/ip4/tcp/1234/ipfs/kTRX47RthhwNzWdi6ggwqjuX", // bad ip part + "/ip4/1.2.3.4/tcp/1234/ipfs", // no id + "/ip4/1.2.3.4/tcp/1234/ipfs/", // no id +} + +func newMultiaddr(t *testing.T, s string) ma.Multiaddr { + maddr, err := ma.NewMultiaddr(s) + if err != nil { + t.Fatal(err) + } + return maddr +} + +func TestParseStringGood(t *testing.T) { + for _, g := range good { + if _, err := ParseString(g); err != nil { + t.Error("failed to parse", g, err) + } + } +} + +func TestParseStringBad(t *testing.T) { + for _, b := range bad { + if _, err := ParseString(b); err == nil { + t.Error("succeeded in parsing", b) + } + } +} + +func TestParseMultiaddrGood(t *testing.T) { + for _, g := range good { + if _, err := ParseMultiaddr(newMultiaddr(t, g)); err != nil { + t.Error("failed to parse", g, err) + } + } +} + +func TestParseMultiaddrBad(t *testing.T) { + for _, b := range bad { + m, err := ma.NewMultiaddr(b) + if err != nil { + continue // skip these. + } + + if _, err := ParseMultiaddr(m); err == nil { + t.Error("succeeded in parsing", m) + } + } +} + +func TestIDMatches(t *testing.T) { + for _, g := range good { + a, err := ParseString(g) + if err != nil { + t.Error("failed to parse", g, err) + continue + } + + sp := strings.Split(g, "/") + sid := sp[len(sp)-1] + id, err := peer.IDB58Decode(sid) + if err != nil { + t.Error("failed to parse", sid, err) + continue + } + + if a.ID() != id { + t.Error("not equal", a.ID(), id) + } + } +} + +func TestMultiaddrMatches(t *testing.T) { + for _, g := range good { + a, err := ParseString(g) + if err != nil { + t.Error("failed to parse", g, err) + continue + } + + m := newMultiaddr(t, g) + if !a.Multiaddr().Equal(m) { + t.Error("not equal", a.Multiaddr(), m) + } + } +} From 29bf59dded42bf661cff9832b6ada8c53ec4faaa Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 20 Jan 2015 16:05:48 -0800 Subject: [PATCH 03/13] bootstrap: use ipfsaddr for boostrap peers :warning: :warning: this commit makes your current configs unusable, as the default bootstrap peers. You may need to edit your config. Go from: ```js Bootstrap: [ { "Address": "/ip4/104.131.131.82/tcp/4001", "PeerID": "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ" } ] ``` To: ```js Bootstrap: [ "/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ" ] ``` --- core/bootstrap.go | 33 ++++++--------- core/commands/bootstrap.go | 36 +++++++++++----- core/core.go | 13 ++++-- repo/config/bootstrap_peers.go | 76 ++++++++++++++-------------------- repo/config/config.go | 16 +++---- repo/config/init.go | 2 +- test/3nodetest/client/run.sh | 2 +- test/3nodetest/server/run.sh | 4 +- util/ipfsaddr/ipfsaddr.go | 16 +++++++ 9 files changed, 107 insertions(+), 91 deletions(-) diff --git a/core/bootstrap.go b/core/bootstrap.go index 16e5be40d..b1d1f2587 100644 --- a/core/bootstrap.go +++ b/core/bootstrap.go @@ -220,32 +220,25 @@ func bootstrapConnect(ctx context.Context, return nil } -func toPeerInfos(bpeers []config.BootstrapPeer) ([]peer.PeerInfo, error) { +func toPeerInfos(bpeers []config.BootstrapPeer) []peer.PeerInfo { var peers []peer.PeerInfo for _, bootstrap := range bpeers { - p, err := toPeerInfo(bootstrap) - if err != nil { - return nil, err - } - peers = append(peers, p) + peers = append(peers, toPeerInfo(bootstrap)) } - return peers, nil + return peers } -func toPeerInfo(bootstrap config.BootstrapPeer) (p peer.PeerInfo, err error) { - id, err := peer.IDB58Decode(bootstrap.PeerID) - if err != nil { - return +func toPeerInfo(bp config.BootstrapPeer) peer.PeerInfo { + // for now, we drop the "ipfs addr" part of the multiaddr. the rest + // of the codebase currently uses addresses without the peerid part. + m := bp.Multiaddr() + s := ma.Split(m) + m = ma.Join(s[:len(s)-1]...) + + return peer.PeerInfo{ + ID: bp.ID(), + Addrs: []ma.Multiaddr{m}, } - maddr, err := ma.NewMultiaddr(bootstrap.Address) - if err != nil { - return - } - p = peer.PeerInfo{ - ID: id, - Addrs: []ma.Multiaddr{maddr}, - } - return } func randomSubsetOfPeers(in []peer.PeerInfo, max int) []peer.PeerInfo { diff --git a/core/commands/bootstrap.go b/core/commands/bootstrap.go index 6b4e3f3e0..2b047f2e6 100644 --- a/core/commands/bootstrap.go +++ b/core/commands/bootstrap.go @@ -3,6 +3,7 @@ package commands import ( "bytes" "io" + "sort" cmds "github.com/jbenet/go-ipfs/commands" repo "github.com/jbenet/go-ipfs/repo" @@ -197,8 +198,13 @@ var bootstrapListCmd = &cmds.Command{ return } - peers := cfg.Bootstrap + peers, err := cfg.BootstrapPeers() + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } res.SetOutput(&BootstrapOutput{peers}) + return }, Type: BootstrapOutput{}, Marshalers: cmds.MarshalerMap{ @@ -219,9 +225,10 @@ func bootstrapMarshaler(res cmds.Response) (io.Reader, error) { func bootstrapWritePeers(w io.Writer, prefix string, peers []config.BootstrapPeer) error { - for _, peer := range peers { - s := prefix + peer.Address + "/" + peer.PeerID + "\n" - _, err := w.Write([]byte(s)) + pstrs := config.BootstrapPeerStrings(peers) + sort.Stable(sort.StringSlice(pstrs)) + for _, peer := range pstrs { + _, err := w.Write([]byte(peer + "\n")) if err != nil { return err } @@ -235,14 +242,14 @@ func bootstrapAdd(r repo.Repo, cfg *config.Config, peers []config.BootstrapPeer) for _, peer := range peers { duplicate := false for _, peer2 := range cfg.Bootstrap { - if peer.Address == peer2.Address && peer.PeerID == peer2.PeerID { + if peer.Equal(peer2) { duplicate = true break } } if !duplicate { - cfg.Bootstrap = append(cfg.Bootstrap, peer) + cfg.Bootstrap = append(cfg.Bootstrap, peer.String()) added = append(added, peer) } } @@ -258,10 +265,15 @@ func bootstrapRemove(r repo.Repo, cfg *config.Config, toRemove []config.Bootstra removed := make([]config.BootstrapPeer, 0, len(toRemove)) keep := make([]config.BootstrapPeer, 0, len(cfg.Bootstrap)) - for _, peer := range cfg.Bootstrap { + peers, err := cfg.BootstrapPeers() + if err != nil { + return nil, err + } + + for _, peer := range peers { found := false for _, peer2 := range toRemove { - if peer.Address == peer2.Address && peer.PeerID == peer2.PeerID { + if peer.Equal(peer2) { found = true removed = append(removed, peer) break @@ -272,7 +284,7 @@ func bootstrapRemove(r repo.Repo, cfg *config.Config, toRemove []config.Bootstra keep = append(keep, peer) } } - cfg.Bootstrap = keep + cfg.SetBootstrapPeers(keep) if err := r.SetConfig(cfg); err != nil { return nil, err @@ -282,8 +294,10 @@ func bootstrapRemove(r repo.Repo, cfg *config.Config, toRemove []config.Bootstra } func bootstrapRemoveAll(r repo.Repo, cfg *config.Config) ([]config.BootstrapPeer, error) { - removed := make([]config.BootstrapPeer, len(cfg.Bootstrap)) - copy(removed, cfg.Bootstrap) + removed, err := cfg.BootstrapPeers() + if err != nil { + return nil, err + } cfg.Bootstrap = nil if err := r.SetConfig(cfg); err != nil { diff --git a/core/core.go b/core/core.go index 93411c4a4..3fdcfd259 100644 --- a/core/core.go +++ b/core/core.go @@ -318,10 +318,9 @@ func (n *IpfsNode) Bootstrap(cfg BootstrapConfig) error { // freshest bootstrap peers from config. this responds to live changes. if cfg.BootstrapPeers == nil { cfg.BootstrapPeers = func() []peer.PeerInfo { - bpeers := n.Repo.Config().Bootstrap - ps, err := toPeerInfos(bpeers) + ps, err := n.loadBootstrapPeers() if err != nil { - log.Warningf("failed to parse bootstrap peers from config: %s", bpeers) + log.Warningf("failed to parse bootstrap peers from config: %s", n.Repo.Config().Bootstrap) return nil } return ps @@ -370,6 +369,14 @@ func (n *IpfsNode) loadPrivateKey() error { return nil } +func (n *IpfsNode) loadBootstrapPeers() ([]peer.PeerInfo, error) { + parsed, err := n.Repo.Config().BootstrapPeers() + if err != nil { + return nil, err + } + return toPeerInfos(parsed), nil +} + // SetupOfflineRouting loads the local nodes private key and // uses it to instantiate a routing system in offline mode. // This is primarily used for offline ipns modifications. diff --git a/repo/config/bootstrap_peers.go b/repo/config/bootstrap_peers.go index 2b437d185..de4935d78 100644 --- a/repo/config/bootstrap_peers.go +++ b/repo/config/bootstrap_peers.go @@ -1,12 +1,9 @@ package config import ( - "strings" - - ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - errors "github.com/jbenet/go-ipfs/util/debugerror" + + iaddr "github.com/jbenet/go-ipfs/util/ipfsaddr" ) // DefaultBootstrapAddresses are the hardcoded bootstrap addresses @@ -16,21 +13,25 @@ import ( // Note: this is here -- and not inside cmd/ipfs/init.go -- because of an // import dependency issue. TODO: move this into a config/default/ package. var DefaultBootstrapAddresses = []string{ - "/ip4/104.131.131.82/tcp/4001/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", // mars.i.ipfs.io - "/ip4/104.236.176.52/tcp/4001/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z", // neptune (to be neptune.i.ipfs.io) - "/ip4/104.236.179.241/tcp/4001/QmSoLpPVmHKQ4XTPdz8tjDFgdeRFkpV8JgYq8JVJ69RrZm", // pluto (to be pluto.i.ipfs.io) - "/ip4/162.243.248.213/tcp/4001/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm", // uranus (to be uranus.i.ipfs.io) - "/ip4/128.199.219.111/tcp/4001/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu", // saturn (to be saturn.i.ipfs.io) - "/ip4/104.236.76.40/tcp/4001/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64", // venus (to be venus.i.ipfs.io) - "/ip4/178.62.158.247/tcp/4001/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd", // earth (to be earth.i.ipfs.io) - "/ip4/178.62.61.185/tcp/4001/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3", // mercury (to be mercury.i.ipfs.io) - "/ip4/104.236.151.122/tcp/4001/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx", // jupiter (to be jupiter.i.ipfs.io) + "/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", // mars.i.ipfs.io + "/ip4/104.236.176.52/tcp/4001/ipfs/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z", // neptune (to be neptune.i.ipfs.io) + "/ip4/104.236.179.241/tcp/4001/ipfs/QmSoLpPVmHKQ4XTPdz8tjDFgdeRFkpV8JgYq8JVJ69RrZm", // pluto (to be pluto.i.ipfs.io) + "/ip4/162.243.248.213/tcp/4001/ipfs/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm", // uranus (to be uranus.i.ipfs.io) + "/ip4/128.199.219.111/tcp/4001/ipfs/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu", // saturn (to be saturn.i.ipfs.io) + "/ip4/104.236.76.40/tcp/4001/ipfs/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64", // venus (to be venus.i.ipfs.io) + "/ip4/178.62.158.247/tcp/4001/ipfs/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd", // earth (to be earth.i.ipfs.io) + "/ip4/178.62.61.185/tcp/4001/ipfs/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3", // mercury (to be mercury.i.ipfs.io) + "/ip4/104.236.151.122/tcp/4001/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx", // jupiter (to be jupiter.i.ipfs.io) } // BootstrapPeer is a peer used to bootstrap the network. -type BootstrapPeer struct { - Address string - PeerID string // until multiaddr supports ipfs, use another field. +type BootstrapPeer iaddr.IPFSAddr + +// ErrInvalidPeerAddr signals an address is not a valid peer address. +var ErrInvalidPeerAddr = errors.New("invalid peer address") + +func (c *Config) BootstrapPeers() ([]BootstrapPeer, error) { + return ParseBootstrapPeers(c.Bootstrap) } // DefaultBootstrapPeers returns the (parsed) set of default bootstrap peers. @@ -45,39 +46,16 @@ This is a problem with the ipfs codebase. Please report it to the dev team.`, er return ps, nil } -func (bp *BootstrapPeer) String() string { - return bp.Address + "/" + bp.PeerID +func (c *Config) SetBootstrapPeers(bps []BootstrapPeer) { + c.Bootstrap = BootstrapPeerStrings(bps) } func ParseBootstrapPeer(addr string) (BootstrapPeer, error) { - // to be replaced with just multiaddr parsing, once ptp is a multiaddr protocol - idx := strings.LastIndex(addr, "/") - if idx == -1 { - return BootstrapPeer{}, errors.New("invalid address") - } - addrS := addr[:idx] - peeridS := addr[idx+1:] - - // make sure addrS parses as a multiaddr. - if len(addrS) > 0 { - maddr, err := ma.NewMultiaddr(addrS) - if err != nil { - return BootstrapPeer{}, err - } - - addrS = maddr.String() - } - - // make sure idS parses as a peer.ID - _, err := mh.FromB58String(peeridS) + ia, err := iaddr.ParseString(addr) if err != nil { - return BootstrapPeer{}, err + return nil, err } - - return BootstrapPeer{ - Address: addrS, - PeerID: peeridS, - }, nil + return BootstrapPeer(ia), err } func ParseBootstrapPeers(addrs []string) ([]BootstrapPeer, error) { @@ -91,3 +69,11 @@ func ParseBootstrapPeers(addrs []string) ([]BootstrapPeer, error) { } return peers, nil } + +func BootstrapPeerStrings(bps []BootstrapPeer) []string { + bpss := make([]string, len(bps)) + for i, p := range bps { + bpss[i] = p.String() + } + return bpss +} diff --git a/repo/config/config.go b/repo/config/config.go index 9159223f7..e912429cd 100644 --- a/repo/config/config.go +++ b/repo/config/config.go @@ -16,14 +16,14 @@ var log = u.Logger("config") // Config is used to load IPFS config files. type Config struct { - Identity Identity // local node's peer identity - Datastore Datastore // local node's storage - Addresses Addresses // local node's addresses - Mounts Mounts // local node's mount points - Version Version // local node's version management - Bootstrap []BootstrapPeer // local nodes's bootstrap peers - Tour Tour // local node's tour position - Gateway Gateway // local node's gateway server options + Identity Identity // local node's peer identity + Datastore Datastore // local node's storage + Addresses Addresses // local node's addresses + Mounts Mounts // local node's mount points + Version Version // local node's version management + Bootstrap []string // local nodes's bootstrap peer addresses + Tour Tour // local node's tour position + Gateway Gateway // local node's gateway server options } const ( diff --git a/repo/config/init.go b/repo/config/init.go index 976d26688..6d1c529e7 100644 --- a/repo/config/init.go +++ b/repo/config/init.go @@ -38,7 +38,7 @@ func Init(out io.Writer, nBitsForKeypair int) (*Config, error) { API: "/ip4/127.0.0.1/tcp/5001", }, - Bootstrap: bootstrapPeers, + Bootstrap: BootstrapPeerStrings(bootstrapPeers), Datastore: *ds, Identity: identity, diff --git a/test/3nodetest/client/run.sh b/test/3nodetest/client/run.sh index 5c7c59a6e..5143905a2 100644 --- a/test/3nodetest/client/run.sh +++ b/test/3nodetest/client/run.sh @@ -1,4 +1,4 @@ -ipfs bootstrap add /ip4/$BOOTSTRAP_PORT_4011_TCP_ADDR/tcp/$BOOTSTRAP_PORT_4011_TCP_PORT/QmNXuBh8HFsWq68Fid8dMbGNQTh7eG6hV9rr1fQyfmfomE +ipfs bootstrap add /ip4/$BOOTSTRAP_PORT_4011_TCP_ADDR/tcp/$BOOTSTRAP_PORT_4011_TCP_PORT/ipfs/QmNXuBh8HFsWq68Fid8dMbGNQTh7eG6hV9rr1fQyfmfomE echo "3nodetest> starting client daemon" diff --git a/test/3nodetest/server/run.sh b/test/3nodetest/server/run.sh index d357faa11..e37d86153 100644 --- a/test/3nodetest/server/run.sh +++ b/test/3nodetest/server/run.sh @@ -1,12 +1,12 @@ # must be connected to bootstrap node -ipfs bootstrap add /ip4/$BOOTSTRAP_PORT_4011_TCP_ADDR/tcp/$BOOTSTRAP_PORT_4011_TCP_PORT/QmNXuBh8HFsWq68Fid8dMbGNQTh7eG6hV9rr1fQyfmfomE +ipfs bootstrap add /ip4/$BOOTSTRAP_PORT_4011_TCP_ADDR/tcp/$BOOTSTRAP_PORT_4011_TCP_PORT/ipfs/QmNXuBh8HFsWq68Fid8dMbGNQTh7eG6hV9rr1fQyfmfomE # wait for daemon to start/bootstrap # alternatively use ipfs swarm connect echo "3nodetest> starting server daemon" ipfs daemon & sleep 3 -# TODO instead of bootrapping: ipfs swarm connect /ip4/$BOOTSTRAP_PORT_4011_TCP_ADDR/tcp/$BOOTSTRAP_PORT_4011_TCP_PORT/QmNXuBh8HFsWq68Fid8dMbGNQTh7eG6hV9rr1fQyfmfomE +# TODO instead of bootrapping: ipfs swarm connect /ip4/$BOOTSTRAP_PORT_4011_TCP_ADDR/tcp/$BOOTSTRAP_PORT_4011_TCP_PORT/ipfs/QmNXuBh8HFsWq68Fid8dMbGNQTh7eG6hV9rr1fQyfmfomE # must mount this volume from data container ipfs add -q /data/filetiny > tmptiny diff --git a/util/ipfsaddr/ipfsaddr.go b/util/ipfsaddr/ipfsaddr.go index 4c1f54a1a..42fb659f0 100644 --- a/util/ipfsaddr/ipfsaddr.go +++ b/util/ipfsaddr/ipfsaddr.go @@ -15,6 +15,8 @@ var ErrInvalidAddr = errors.New("invalid ipfs address") type IPFSAddr interface { ID() peer.ID Multiaddr() ma.Multiaddr + String() string + Equal(b interface{}) bool } type ipfsAddr struct { @@ -30,6 +32,20 @@ func (a ipfsAddr) Multiaddr() ma.Multiaddr { return a.ma } +func (a ipfsAddr) String() string { + return a.ma.String() +} + +func (a ipfsAddr) Equal(b interface{}) bool { + if ib, ok := b.(IPFSAddr); ok { + return a.Multiaddr().Equal(ib.Multiaddr()) + } + if mb, ok := b.(ma.Multiaddr); ok { + return a.Multiaddr().Equal(mb) + } + return false +} + // ParseString parses a string representation of an address into an IPFSAddr func ParseString(str string) (a IPFSAddr, err error) { if str == "" { From c99dd848dc465d72d8aaeea0a601f5a58d4da20d Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 31 Jan 2015 08:27:27 -0800 Subject: [PATCH 04/13] changelog: added to account for breaking changes --- CHANGELOG.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..6af526397 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,17 @@ +# go-ipfs changelog + +Until we near full stability, this changelog must only +list breakages and backwards incompatible changes. + + +### 2015-01-31: + +* bootstrap addresses now have .../ipfs/... in format + config file Bootstrap field changed accordingly. users + can upgrade cleanly with: + + ipfs bootstrap >boostrap_peers + ipfs bootstrap rm --all + + + ipfs bootstrap add Date: Sat, 31 Jan 2015 18:13:35 -0800 Subject: [PATCH 05/13] util/ipfsaddr: recover from panics parsing --- util/ipfsaddr/ipfsaddr.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/util/ipfsaddr/ipfsaddr.go b/util/ipfsaddr/ipfsaddr.go index 42fb659f0..af4302ed6 100644 --- a/util/ipfsaddr/ipfsaddr.go +++ b/util/ipfsaddr/ipfsaddr.go @@ -7,8 +7,11 @@ import ( ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" peer "github.com/jbenet/go-ipfs/p2p/peer" + eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog" ) +var log = eventlog.Logger("ipfsaddr") + // ErrInvalidAddr signals an address is not a valid ipfs address. var ErrInvalidAddr = errors.New("invalid ipfs address") @@ -62,13 +65,14 @@ func ParseString(str string) (a IPFSAddr, err error) { // ParseMultiaddr parses a multiaddr into an IPFSAddr func ParseMultiaddr(m ma.Multiaddr) (a IPFSAddr, err error) { - // // never panic. - // defer func() { - // if r := recover(); r != nil { - // a = nil - // err = ErrInvalidAddr - // } - // }() + // never panic. + defer func() { + if r := recover(); r != nil { + log.Debug("recovered from panic: ", r) + a = nil + err = ErrInvalidAddr + } + }() if m == nil { return nil, ErrInvalidAddr From de464b7e400f96827c04b66b6710f974b363473e Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 31 Jan 2015 18:17:13 -0800 Subject: [PATCH 06/13] test/3nodetest: list bootstrap nodes to debug --- test/3nodetest/client/run.sh | 1 + test/3nodetest/server/run.sh | 1 + 2 files changed, 2 insertions(+) diff --git a/test/3nodetest/client/run.sh b/test/3nodetest/client/run.sh index 5143905a2..9bb1fb6fa 100644 --- a/test/3nodetest/client/run.sh +++ b/test/3nodetest/client/run.sh @@ -1,4 +1,5 @@ ipfs bootstrap add /ip4/$BOOTSTRAP_PORT_4011_TCP_ADDR/tcp/$BOOTSTRAP_PORT_4011_TCP_PORT/ipfs/QmNXuBh8HFsWq68Fid8dMbGNQTh7eG6hV9rr1fQyfmfomE +ipfs bootstrap # list bootstrap nodes for debugging echo "3nodetest> starting client daemon" diff --git a/test/3nodetest/server/run.sh b/test/3nodetest/server/run.sh index e37d86153..68a45d4a3 100644 --- a/test/3nodetest/server/run.sh +++ b/test/3nodetest/server/run.sh @@ -1,5 +1,6 @@ # must be connected to bootstrap node ipfs bootstrap add /ip4/$BOOTSTRAP_PORT_4011_TCP_ADDR/tcp/$BOOTSTRAP_PORT_4011_TCP_PORT/ipfs/QmNXuBh8HFsWq68Fid8dMbGNQTh7eG6hV9rr1fQyfmfomE +ipfs bootstrap # list bootstrap nodes for debugging # wait for daemon to start/bootstrap # alternatively use ipfs swarm connect From 188d336204c8e241096f1e9ab8e05b5954f84220 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 1 Feb 2015 05:36:07 -0800 Subject: [PATCH 07/13] cmds/bootstrap: fix marshalling + listing errors --- core/commands/bootstrap.go | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/core/commands/bootstrap.go b/core/commands/bootstrap.go index 2b047f2e6..06f0ccb55 100644 --- a/core/commands/bootstrap.go +++ b/core/commands/bootstrap.go @@ -14,7 +14,7 @@ import ( ) type BootstrapOutput struct { - Peers []config.BootstrapPeer + Peers []string } var peerOptionDesc = "A peer to add to the bootstrap list (in the format '/')" @@ -91,18 +91,18 @@ in the bootstrap list). inputPeers = append(inputPeers, defltPeers...) } + if len(inputPeers) == 0 { + res.SetError(errors.New("no bootstrap peers to add"), cmds.ErrClient) + return + } + added, err := bootstrapAdd(r, cfg, inputPeers) if err != nil { res.SetError(err, cmds.ErrNormal) return } - if len(inputPeers) == 0 { - res.SetError(errors.New("no bootstrap peers to add"), cmds.ErrClient) - return - } - - res.SetOutput(&BootstrapOutput{added}) + res.SetOutput(&BootstrapOutput{config.BootstrapPeerStrings(added)}) }, Type: BootstrapOutput{}, Marshalers: cmds.MarshalerMap{ @@ -168,7 +168,7 @@ var bootstrapRemoveCmd = &cmds.Command{ return } - res.SetOutput(&BootstrapOutput{removed}) + res.SetOutput(&BootstrapOutput{config.BootstrapPeerStrings(removed)}) }, Type: BootstrapOutput{}, Marshalers: cmds.MarshalerMap{ @@ -192,18 +192,20 @@ var bootstrapListCmd = &cmds.Command{ }, Run: func(req cmds.Request, res cmds.Response) { - cfg, err := req.Context().GetConfig() - if err != nil { + r := fsrepo.At(req.Context().ConfigRoot) + if err := r.Open(); err != nil { res.SetError(err, cmds.ErrNormal) return } + defer r.Close() + cfg := r.Config() peers, err := cfg.BootstrapPeers() if err != nil { res.SetError(err, cmds.ErrNormal) return } - res.SetOutput(&BootstrapOutput{peers}) + res.SetOutput(&BootstrapOutput{config.BootstrapPeerStrings(peers)}) return }, Type: BootstrapOutput{}, @@ -223,11 +225,10 @@ func bootstrapMarshaler(res cmds.Response) (io.Reader, error) { return &buf, err } -func bootstrapWritePeers(w io.Writer, prefix string, peers []config.BootstrapPeer) error { +func bootstrapWritePeers(w io.Writer, prefix string, peers []string) error { - pstrs := config.BootstrapPeerStrings(peers) - sort.Stable(sort.StringSlice(pstrs)) - for _, peer := range pstrs { + sort.Stable(sort.StringSlice(peers)) + for _, peer := range peers { _, err := w.Write([]byte(peer + "\n")) if err != nil { return err From bebc3ca0fd8d96dc29e23edb5f1bc04799618aed Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 1 Feb 2015 06:07:21 -0800 Subject: [PATCH 08/13] cmd/bootstrap: bugfix on add (dedup) --- core/commands/bootstrap.go | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/core/commands/bootstrap.go b/core/commands/bootstrap.go index 06f0ccb55..4c1d0d42d 100644 --- a/core/commands/bootstrap.go +++ b/core/commands/bootstrap.go @@ -238,28 +238,40 @@ func bootstrapWritePeers(w io.Writer, prefix string, peers []string) error { } func bootstrapAdd(r repo.Repo, cfg *config.Config, peers []config.BootstrapPeer) ([]config.BootstrapPeer, error) { - added := make([]config.BootstrapPeer, 0, len(peers)) + addedMap := map[string]struct{}{} + addedList := make([]config.BootstrapPeer, 0, len(peers)) + // re-add cfg bootstrap peers to rm dupes + bpeers := cfg.Bootstrap + cfg.Bootstrap = nil + + // add new peers for _, peer := range peers { - duplicate := false - for _, peer2 := range cfg.Bootstrap { - if peer.Equal(peer2) { - duplicate = true - break - } + s := peer.String() + if _, found := addedMap[s]; found { + continue } - if !duplicate { - cfg.Bootstrap = append(cfg.Bootstrap, peer.String()) - added = append(added, peer) + cfg.Bootstrap = append(cfg.Bootstrap, s) + addedList = append(addedList, peer) + addedMap[s] = struct{}{} + } + + // add back original peers. in this order so that we output them. + for _, s := range bpeers { + if _, found := addedMap[s]; found { + continue } + + cfg.Bootstrap = append(cfg.Bootstrap, s) + addedMap[s] = struct{}{} } if err := r.SetConfig(cfg); err != nil { return nil, err } - return added, nil + return addedList, nil } func bootstrapRemove(r repo.Repo, cfg *config.Config, toRemove []config.BootstrapPeer) ([]config.BootstrapPeer, error) { From d0dd22b7d435416b84c70c3c563344266f6d39f1 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 1 Feb 2015 06:07:28 -0800 Subject: [PATCH 09/13] sharness/t0120-bootstrap: test the bootstrap cmd and do it both online + offline --- test/sharness/t0120-bootstrap.sh | 131 +++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100755 test/sharness/t0120-bootstrap.sh diff --git a/test/sharness/t0120-bootstrap.sh b/test/sharness/t0120-bootstrap.sh new file mode 100755 index 000000000..592c51379 --- /dev/null +++ b/test/sharness/t0120-bootstrap.sh @@ -0,0 +1,131 @@ +#!/bin/sh +# +# Copyright (c) 2014 Jeromy Johnson +# MIT Licensed; see the LICENSE file in this repository. +# + +# changing the bootstrap peers will require changing it in two places :) +BP1="/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ" +BP2="/ip4/104.236.151.122/tcp/4001/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx" +BP3="/ip4/104.236.176.52/tcp/4001/ipfs/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z" +BP4="/ip4/104.236.179.241/tcp/4001/ipfs/QmSoLpPVmHKQ4XTPdz8tjDFgdeRFkpV8JgYq8JVJ69RrZm" +BP5="/ip4/104.236.76.40/tcp/4001/ipfs/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64" +BP6="/ip4/128.199.219.111/tcp/4001/ipfs/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu" +BP7="/ip4/162.243.248.213/tcp/4001/ipfs/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm" +BP8="/ip4/178.62.158.247/tcp/4001/ipfs/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd" +BP9="/ip4/178.62.61.185/tcp/4001/ipfs/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3" + +test_description="Test ipfs repo operations" + +. lib/test-lib.sh + +test_init_ipfs + +# we use a function so that we can run it both offline + online +test_bootstrap_list_cmd() { + printf "" >list_expected + for BP in "$@" + do + echo "$BP" >>list_expected + done + + test_expect_success "'ipfs bootstrap' succeeds" ' + ipfs bootstrap >list_actual + ' + + test_expect_success "'ipfs bootstrap' output looks good" ' + test_cmp list_actual list_expected + ' + + test_expect_success "'ipfs bootstrap list' succeeds" ' + ipfs bootstrap list >list2_actual + ' + + test_expect_success "'ipfs bootstrap list' output looks good" ' + test_cmp list2_actual list_expected + ' +} + +# we use a function so that we can run it both offline + online +test_bootstrap_cmd() { + + # remove all peers just in case. + # if this fails, the first listing may not be empty + ipfs bootstrap rm --all + + test_bootstrap_list_cmd + + test_expect_success "'ipfs bootstrap add' succeeds" ' + ipfs bootstrap add "$BP1" "$BP2" "$BP3" >add_actual + ' + + test_expect_success "'ipfs bootstrap add' output looks good" ' + echo $BP1 >add_expected + echo $BP2 >>add_expected + echo $BP3 >>add_expected + test_cmp add_actual add_expected + ' + + test_bootstrap_list_cmd $BP1 $BP2 $BP3 + + test_expect_success "'ipfs bootstrap rm' succeeds" ' + ipfs bootstrap rm "$BP1" "$BP3" >rm_actual + ' + + test_expect_success "'ipfs bootstrap rm' output looks good" ' + echo $BP1 >rm_expected + echo $BP3 >>rm_expected + test_cmp rm_actual rm_expected + ' + + test_bootstrap_list_cmd $BP2 + + test_expect_success "'ipfs bootstrap add --default' succeeds" ' + ipfs bootstrap add --default >add2_actual + ' + + test_expect_success "'ipfs bootstrap add --default' output has default BP" ' + echo $BP1 >add2_expected + echo $BP2 >>add2_expected + echo $BP3 >>add2_expected + echo $BP4 >>add2_expected + echo $BP5 >>add2_expected + echo $BP6 >>add2_expected + echo $BP7 >>add2_expected + echo $BP8 >>add2_expected + echo $BP9 >>add2_expected + test_cmp add2_actual add2_expected + ' + + test_bootstrap_list_cmd $BP1 $BP2 $BP3 $BP4 $BP5 $BP6 $BP7 $BP8 $BP9 + + test_expect_success "'ipfs bootstrap rm --all' succeeds" ' + ipfs bootstrap rm --all >rm2_actual + ' + + test_expect_success "'ipfs bootstrap rm' output looks good" ' + echo $BP1 >rm2_expected + echo $BP2 >>rm2_expected + echo $BP3 >>rm2_expected + echo $BP4 >>rm2_expected + echo $BP5 >>rm2_expected + echo $BP6 >>rm2_expected + echo $BP7 >>rm2_expected + echo $BP8 >>rm2_expected + echo $BP9 >>rm2_expected + test_cmp rm2_actual rm2_expected + ' + + test_bootstrap_list_cmd +} + +# should work offline +test_bootstrap_cmd + +# should work online +test_launch_ipfs_daemon +test_bootstrap_cmd +test_kill_ipfs_daemon + + +test_done From 5a8aa1afcff221457d4f98b202341715aec5ea67 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 1 Feb 2015 06:32:59 -0800 Subject: [PATCH 10/13] ipfsaddr: Transport part. --- util/ipfsaddr/ipfsaddr.go | 12 ++++++++++++ util/ipfsaddr/ipfsaddr_test.go | 20 ++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/util/ipfsaddr/ipfsaddr.go b/util/ipfsaddr/ipfsaddr.go index af4302ed6..036f8e2e6 100644 --- a/util/ipfsaddr/ipfsaddr.go +++ b/util/ipfsaddr/ipfsaddr.go @@ -18,6 +18,7 @@ var ErrInvalidAddr = errors.New("invalid ipfs address") type IPFSAddr interface { ID() peer.ID Multiaddr() ma.Multiaddr + Transport() ma.Multiaddr String() string Equal(b interface{}) bool } @@ -35,6 +36,10 @@ func (a ipfsAddr) Multiaddr() ma.Multiaddr { return a.ma } +func (a ipfsAddr) Transport() ma.Multiaddr { + return Transport(a) +} + func (a ipfsAddr) String() string { return a.ma.String() } @@ -98,3 +103,10 @@ func ParseMultiaddr(m ma.Multiaddr) (a IPFSAddr, err error) { return ipfsAddr{ma: m, id: id}, nil } + +func Transport(iaddr IPFSAddr) (maddr ma.Multiaddr) { + maddr = iaddr.Multiaddr() + split := ma.Split(maddr) + maddr = ma.Join(split[:len(split)-1]...) + return +} diff --git a/util/ipfsaddr/ipfsaddr_test.go b/util/ipfsaddr/ipfsaddr_test.go index d5e655e9d..b421da470 100644 --- a/util/ipfsaddr/ipfsaddr_test.go +++ b/util/ipfsaddr/ipfsaddr_test.go @@ -115,3 +115,23 @@ func TestMultiaddrMatches(t *testing.T) { } } } + +func TestTransport(t *testing.T) { + for _, g := range good { + a, err := ParseString(g) + if err != nil { + t.Error("failed to parse", g, err) + continue + } + + m := newMultiaddr(t, g) + split := ma.Split(m) + m = ma.Join(split[:len(split)-1]...) + if a.Multiaddr().Equal(m) { + t.Error("should not be equal", a.Multiaddr(), m) + } + if !Transport(a).Equal(m) { + t.Error("should be equal", Transport(a), m) + } + } +} From fc78f20093643dcb6feafbee9281210bce5bdc8e Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 1 Feb 2015 06:35:48 -0800 Subject: [PATCH 11/13] p2p/conn: expose io.Closer interface --- p2p/net/conn/interface.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/p2p/net/conn/interface.go b/p2p/net/conn/interface.go index 558402fdd..749d7d38b 100644 --- a/p2p/net/conn/interface.go +++ b/p2p/net/conn/interface.go @@ -18,6 +18,8 @@ import ( type Map map[u.Key]Conn type PeerConn interface { + io.Closer + // LocalPeer (this side) ID, PrivateKey, and Address LocalPeer() peer.ID LocalPrivateKey() ic.PrivKey @@ -45,7 +47,6 @@ type Conn interface { msgio.Reader msgio.Writer - io.Closer } // Dialer is an object that can open connections. We could have a "convenience" From 3cd3424900397d9d0a4ff0833696b67519b0088d Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 1 Feb 2015 06:36:51 -0800 Subject: [PATCH 12/13] cmds/swarm: now speaks ipfsaddr (.../ipfs...) --- core/commands/swarm.go | 39 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/core/commands/swarm.go b/core/commands/swarm.go index 1e97a30f9..3c6c46d3e 100644 --- a/core/commands/swarm.go +++ b/core/commands/swarm.go @@ -4,15 +4,14 @@ import ( "bytes" "fmt" "io" - "path" "sort" cmds "github.com/jbenet/go-ipfs/commands" peer "github.com/jbenet/go-ipfs/p2p/peer" errors "github.com/jbenet/go-ipfs/util/debugerror" + iaddr "github.com/jbenet/go-ipfs/util/ipfsaddr" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" ) type stringList struct { @@ -64,7 +63,7 @@ ipfs swarm peers lists the set of peers this node is connected to. for i, c := range conns { pid := c.RemotePeer() addr := c.RemoteMultiaddr() - addrs[i] = fmt.Sprintf("%s/%s", addr, pid.Pretty()) + addrs[i] = fmt.Sprintf("%s/ipfs/%s", addr, pid.Pretty()) } sort.Sort(sort.StringSlice(addrs)) @@ -78,12 +77,12 @@ ipfs swarm peers lists the set of peers this node is connected to. var swarmConnectCmd = &cmds.Command{ Helptext: cmds.HelpText{ - Tagline: "Open connection to a given peer", + Tagline: "Open connection to a given address", ShortDescription: ` 'ipfs swarm connect' opens a connection to a peer address. The address format is an ipfs multiaddr: -ipfs swarm connect /ip4/104.131.131.82/tcp/4001/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ +ipfs swarm connect /ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ `, }, Arguments: []cmds.Argument{ @@ -92,7 +91,6 @@ ipfs swarm connect /ip4/104.131.131.82/tcp/4001/QmaCpDMGvV2BGHeYERUEnRQAwe3N8Szb Run: func(req cmds.Request, res cmds.Response) { ctx := context.TODO() - log.Debug("ipfs swarm connect") n, err := req.Context().GetNode() if err != nil { res.SetError(err, cmds.ErrNormal) @@ -146,37 +144,30 @@ func stringListMarshaler(res cmds.Response) (io.Reader, error) { return &buf, nil } -// splitAddresses is a function that takes in a slice of string peer addresses +// parseAddresses is a function that takes in a slice of string peer addresses // (multiaddr + peerid) and returns slices of multiaddrs and peerids. -func splitAddresses(addrs []string) (maddrs []ma.Multiaddr, pids []peer.ID, err error) { - - maddrs = make([]ma.Multiaddr, len(addrs)) - pids = make([]peer.ID, len(addrs)) - for i, addr := range addrs { - a, err := ma.NewMultiaddr(path.Dir(addr)) +func parseAddresses(addrs []string) (iaddrs []iaddr.IPFSAddr, err error) { + iaddrs = make([]iaddr.IPFSAddr, len(addrs)) + for i, saddr := range addrs { + iaddrs[i], err = iaddr.ParseString(saddr) if err != nil { - return nil, nil, cmds.ClientError("invalid peer address: " + err.Error()) + return nil, cmds.ClientError("invalid peer address: " + err.Error()) } - id, err := peer.IDB58Decode(path.Base(addr)) - if err != nil { - return nil, nil, err - } - pids[i] = id - maddrs[i] = a } return } // peersWithAddresses is a function that takes in a slice of string peer addresses // (multiaddr + peerid) and returns a slice of properly constructed peers -func peersWithAddresses(ps peer.Peerstore, addrs []string) ([]peer.ID, error) { - maddrs, pids, err := splitAddresses(addrs) +func peersWithAddresses(ps peer.Peerstore, addrs []string) (pids []peer.ID, err error) { + iaddrs, err := parseAddresses(addrs) if err != nil { return nil, err } - for i, p := range pids { - ps.AddAddress(p, maddrs[i]) + for _, iaddr := range iaddrs { + pids = append(pids, iaddr.ID()) + ps.AddAddress(iaddr.ID(), iaddr.Multiaddr()) } return pids, nil } From aff1d8bdcfbf96fecde5f784d146dcb268112292 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 1 Feb 2015 06:37:14 -0800 Subject: [PATCH 13/13] ipfs swarm disconnect (opposite of connect) --- core/commands/swarm.go | 77 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/core/commands/swarm.go b/core/commands/swarm.go index 3c6c46d3e..1db08aa02 100644 --- a/core/commands/swarm.go +++ b/core/commands/swarm.go @@ -22,8 +22,9 @@ var SwarmCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "swarm inspection tool", Synopsis: ` -ipfs swarm peers - List peers with open connections -ipfs swarm connect
- Open connection to a given peer +ipfs swarm peers - List peers with open connections +ipfs swarm connect
- Open connection to a given address +ipfs swarm disconnect
- Close connection to a given address `, ShortDescription: ` ipfs swarm is a tool to manipulate the network swarm. The swarm is the @@ -32,8 +33,9 @@ ipfs peers in the internet. `, }, Subcommands: map[string]*cmds.Command{ - "peers": swarmPeersCmd, - "connect": swarmConnectCmd, + "peers": swarmPeersCmd, + "connect": swarmConnectCmd, + "disconnect": swarmDisconnectCmd, }, } @@ -130,6 +132,73 @@ ipfs swarm connect /ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3 Type: stringList{}, } +var swarmDisconnectCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Close connection to a given address", + ShortDescription: ` +'ipfs swarm disconnect' closes a connection to a peer address. The address format +is an ipfs multiaddr: + +ipfs swarm disconnect /ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ +`, + }, + Arguments: []cmds.Argument{ + cmds.StringArg("address", true, true, "address of peer to connect to").EnableStdin(), + }, + Run: func(req cmds.Request, res cmds.Response) { + n, err := req.Context().GetNode() + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + addrs := req.Arguments() + + if n.PeerHost == nil { + res.SetError(errNotOnline, cmds.ErrClient) + return + } + + iaddrs, err := parseAddresses(addrs) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + output := make([]string, len(iaddrs)) + for i, addr := range iaddrs { + taddr := addr.Transport() + output[i] = "disconnect " + addr.ID().Pretty() + + found := false + conns := n.PeerHost.Network().ConnsToPeer(addr.ID()) + for _, conn := range conns { + if !conn.RemoteMultiaddr().Equal(taddr) { + log.Error("it's not", conn.RemoteMultiaddr(), taddr) + continue + } + + if err := conn.Close(); err != nil { + output[i] += " failure: " + err.Error() + } else { + output[i] += " success" + } + found = true + break + } + + if !found { + output[i] += " failure: conn not found" + } + } + res.SetOutput(&stringList{output}) + }, + Marshalers: cmds.MarshalerMap{ + cmds.Text: stringListMarshaler, + }, + Type: stringList{}, +} + func stringListMarshaler(res cmds.Response) (io.Reader, error) { list, ok := res.Output().(*stringList) if !ok {