init.go 4.68 KB
Newer Older
1
package main
Matt Bell's avatar
Matt Bell committed
2 3 4 5

import (
	"encoding/base64"
	"errors"
Brian Tiger Chow's avatar
Brian Tiger Chow committed
6
	"fmt"
Matt Bell's avatar
Matt Bell committed
7 8 9 10 11 12 13 14 15 16 17 18
	"os"
	"path/filepath"

	cmds "github.com/jbenet/go-ipfs/commands"
	config "github.com/jbenet/go-ipfs/config"
	ci "github.com/jbenet/go-ipfs/crypto"
	peer "github.com/jbenet/go-ipfs/peer"
	updates "github.com/jbenet/go-ipfs/updates"
	u "github.com/jbenet/go-ipfs/util"
)

var initCmd = &cmds.Command{
19 20 21 22
	Description: "Initializes IPFS config file",
	Help: `Initializes IPFS configuration files and generates a new keypair.
`,

Matt Bell's avatar
Matt Bell committed
23
	Options: []cmds.Option{
24 25 26 27 28 29 30 31
		cmds.Option{[]string{"bits", "b"}, cmds.Int,
			"Number of bits to use in the generated RSA private key (defaults to 4096)"},
		cmds.Option{[]string{"passphrase", "p"}, cmds.String,
			"Passphrase for encrypting the private key"},
		cmds.Option{[]string{"force", "f"}, cmds.Bool,
			"Overwrite existing config (if it exists)"},
		cmds.Option{[]string{"datastore", "d"}, cmds.String,
			"Location for the IPFS data store"},
Matt Bell's avatar
Matt Bell committed
32 33
	},
	Run: func(res cmds.Response, req cmds.Request) {
34 35

		arg, found := req.Option("d")
36
		dspath, ok := arg.(string)
37 38 39 40 41
		if found && !ok {
			res.SetError(errors.New("failed to parse datastore flag"), cmds.ErrNormal)
			return
		}

42 43 44 45 46 47 48
		arg, found = req.Option("f")
		force, ok := arg.(bool) // TODO param
		if found && !ok {
			res.SetError(errors.New("failed to parse force flag"), cmds.ErrNormal)
			return
		}

49 50 51 52 53 54 55 56 57
		arg, found = req.Option("b")
		nBitsForKeypair, ok := arg.(int) // TODO param
		if found && !ok {
			res.SetError(errors.New("failed to get bits flag"), cmds.ErrNormal)
			return
		} else if !found {
			nBitsForKeypair = 4096
		}

58
		err := doInit(req.Context().ConfigRoot, dspath, force, nBitsForKeypair)
59 60 61 62
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}
63 64
	},
}
Matt Bell's avatar
Matt Bell committed
65

66 67 68 69 70 71 72 73 74
// Use these hardcoded bootstrap peers for now.
var defaultPeers = []*config.BootstrapPeer{
	&config.BootstrapPeer{
		// mars.i.ipfs.io
		PeerID:  "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ",
		Address: "/ip4/104.131.131.82/tcp/4001",
	},
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
75
// TODO add default welcome hash: eaa68bedae247ed1e5bd0eb4385a3c0959b976e4
76
func doInit(configRoot string, dspath string, force bool, nBitsForKeypair int) error {
77

78
	u.POut("initializing ipfs node at %s\n", configRoot)
79 80

	configFilename, err := config.Filename(configRoot)
81
	if err != nil {
82
		return errors.New("Couldn't get home directory path")
83 84
	}

85
	fi, err := os.Lstat(configFilename)
86 87
	if fi != nil || (err != nil && !os.IsNotExist(err)) {
		if !force {
88 89
			// TODO multi-line string
			return errors.New("ipfs configuration file already exists!\nReinitializing would overwrite your keys.\n(use -f to force overwrite)")
Matt Bell's avatar
Matt Bell committed
90
		}
91
	}
Matt Bell's avatar
Matt Bell committed
92

Brian Tiger Chow's avatar
Brian Tiger Chow committed
93 94
	ds, err := datastoreConfig(dspath)
	if err != nil {
95
		return err
96 97
	}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
98 99 100 101
	identity, err := identityConfig(nBitsForKeypair)
	if err != nil {
		return err
	}
102

Brian Tiger Chow's avatar
Brian Tiger Chow committed
103
	conf := config.Config{
104

Brian Tiger Chow's avatar
Brian Tiger Chow committed
105 106 107 108 109 110 111 112 113 114 115
		// setup the node addresses.
		Addresses: config.Addresses{
			Swarm: "/ip4/0.0.0.0/tcp/4001",
			API:   "/ip4/127.0.0.1/tcp/5001",
		},

		Bootstrap: defaultPeers,

		Datastore: ds,

		Identity: identity,
116

Brian Tiger Chow's avatar
Brian Tiger Chow committed
117 118 119 120 121
		// setup the node mount points.
		Mounts: config.Mounts{
			IPFS: "/ipfs",
			IPNS: "/ipns",
		},
122

Brian Tiger Chow's avatar
Brian Tiger Chow committed
123 124
		// tracking ipfs version used to generate the init folder and adding
		// update checker default setting.
125 126 127

		// FIXME(brian): before merging into master, change this to...
		// Version: config.VersionDefaultValue()
Brian Tiger Chow's avatar
Brian Tiger Chow committed
128 129 130 131
		Version: config.Version{
			Check:   "error",
			Current: updates.Version,
		},
132 133
	}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
134
	err = config.WriteConfigFile(configFilename, conf)
135
	if err != nil {
136
		return err
137
	}
138
	return nil
Matt Bell's avatar
Matt Bell committed
139
}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166

func datastoreConfig(dspath string) (config.Datastore, error) {
	ds := config.Datastore{}
	if len(dspath) == 0 {
		var err error
		dspath, err = config.DataStorePath("")
		if err != nil {
			return ds, err
		}
	}
	ds.Path = dspath
	ds.Type = "leveldb"

	// Construct the data store if missing
	if err := os.MkdirAll(dspath, os.ModePerm); err != nil {
		return ds, err
	}

	// Check the directory is writeable
	if f, err := os.Create(filepath.Join(dspath, "._check_writeable")); err == nil {
		os.Remove(f.Name())
	} else {
		return ds, errors.New("Datastore '" + dspath + "' is not writeable")
	}

	return ds, nil
}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196

func identityConfig(nbits int) (config.Identity, error) {
	// TODO guard higher up
	ident := config.Identity{}
	if nbits < 1024 {
		return ident, errors.New("Bitsize less than 1024 is considered unsafe.")
	}

	fmt.Println("generating key pair...")
	sk, pk, err := ci.GenerateKeyPair(ci.RSA, nbits)
	if err != nil {
		return ident, err
	}

	// currently storing key unencrypted. in the future we need to encrypt it.
	// TODO(security)
	skbytes, err := sk.Bytes()
	if err != nil {
		return ident, err
	}
	ident.PrivKey = base64.StdEncoding.EncodeToString(skbytes)

	id, err := peer.IDFromPubKey(pk)
	if err != nil {
		return ident, err
	}
	ident.PeerID = id.Pretty()

	return ident, nil
}