Merge pull request #608 from jbenet/ipfs-multiaddr

multiaddrs with .../ipfs/...
This commit is contained in:
Juan Batiz-Benet 2015-02-01 06:45:14 -08:00
commit fbf1a50a54
21 changed files with 729 additions and 181 deletions

17
CHANGELOG.md Normal file
View File

@ -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
<install new ipfs>
<manually add .../ipfs/... to addrs in bootstrap_peers>
ipfs bootstrap add <bootstrap_peers

4
Godeps/Godeps.json generated
View File

@ -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",

View File

@ -1,10 +1,9 @@
language: go
go:
- 1.2
- 1.3
- release
- tip
script:
- go test -v ./...
- go test -race -cpu=5 -v ./...

View File

@ -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")
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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

1 code size name
7 132 16 sctp
8 301 0 udt
9 302 0 utp
10 421 V ipfs
11 480 0 http
12 443 0 https

View File

@ -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.

View File

@ -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 {

View File

@ -3,6 +3,7 @@ package commands
import (
"bytes"
"io"
"sort"
cmds "github.com/jbenet/go-ipfs/commands"
repo "github.com/jbenet/go-ipfs/repo"
@ -13,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 '<multiaddr>/<peerID>')"
@ -90,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{
@ -167,7 +168,7 @@ var bootstrapRemoveCmd = &cmds.Command{
return
}
res.SetOutput(&BootstrapOutput{removed})
res.SetOutput(&BootstrapOutput{config.BootstrapPeerStrings(removed)})
},
Type: BootstrapOutput{},
Marshalers: cmds.MarshalerMap{
@ -191,14 +192,21 @@ var bootstrapListCmd = &cmds.Command{
},
Run: func(req cmds.Request, res cmds.Response) {
cfg, err := req.Context().GetConfig()
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
}
peers := cfg.Bootstrap
res.SetOutput(&BootstrapOutput{peers})
res.SetOutput(&BootstrapOutput{config.BootstrapPeerStrings(peers)})
return
},
Type: BootstrapOutput{},
Marshalers: cmds.MarshalerMap{
@ -217,11 +225,11 @@ 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 {
sort.Stable(sort.StringSlice(peers))
for _, peer := range peers {
s := prefix + peer.Address + "/" + peer.PeerID + "\n"
_, err := w.Write([]byte(s))
_, err := w.Write([]byte(peer + "\n"))
if err != nil {
return err
}
@ -230,38 +238,55 @@ func bootstrapWritePeers(w io.Writer, prefix string, peers []config.BootstrapPee
}
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.Address == peer2.Address && peer.PeerID == peer2.PeerID {
duplicate = true
break
}
s := peer.String()
if _, found := addedMap[s]; found {
continue
}
if !duplicate {
cfg.Bootstrap = append(cfg.Bootstrap, peer)
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) {
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 +297,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 +307,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 {

View File

@ -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 {
@ -23,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 <address> - Open connection to a given peer
ipfs swarm peers - List peers with open connections
ipfs swarm connect <address> - Open connection to a given address
ipfs swarm disconnect <address> - Close connection to a given address
`,
ShortDescription: `
ipfs swarm is a tool to manipulate the network swarm. The swarm is the
@ -33,8 +33,9 @@ ipfs peers in the internet.
`,
},
Subcommands: map[string]*cmds.Command{
"peers": swarmPeersCmd,
"connect": swarmConnectCmd,
"peers": swarmPeersCmd,
"connect": swarmConnectCmd,
"disconnect": swarmDisconnectCmd,
},
}
@ -64,7 +65,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 +79,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 +93,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)
@ -132,6 +132,73 @@ ipfs swarm connect /ip4/104.131.131.82/tcp/4001/QmaCpDMGvV2BGHeYERUEnRQAwe3N8Szb
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 {
@ -146,37 +213,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
}

View File

@ -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.

View File

@ -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"

View File

@ -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
}

View File

@ -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 (

View File

@ -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,

View File

@ -1,4 +1,5 @@
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
ipfs bootstrap # list bootstrap nodes for debugging
echo "3nodetest> starting client daemon"

View File

@ -1,12 +1,13 @@
# 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
ipfs bootstrap # list bootstrap nodes for debugging
# 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

131
test/sharness/t0120-bootstrap.sh Executable file
View File

@ -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

112
util/ipfsaddr/ipfsaddr.go Normal file
View File

@ -0,0 +1,112 @@
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"
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")
type IPFSAddr interface {
ID() peer.ID
Multiaddr() ma.Multiaddr
Transport() ma.Multiaddr
String() string
Equal(b interface{}) bool
}
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
}
func (a ipfsAddr) Transport() ma.Multiaddr {
return Transport(a)
}
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 == "" {
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 {
log.Debug("recovered from panic: ", r)
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
}
func Transport(iaddr IPFSAddr) (maddr ma.Multiaddr) {
maddr = iaddr.Multiaddr()
split := ma.Split(maddr)
maddr = ma.Join(split[:len(split)-1]...)
return
}

View File

@ -0,0 +1,137 @@
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)
}
}
}
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)
}
}
}