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

import (
4
	"bytes"
Matt Bell's avatar
Matt Bell committed
5
	"encoding/base64"
Brian Tiger Chow's avatar
Brian Tiger Chow committed
6
	"fmt"
7
	"path"
Matt Bell's avatar
Matt Bell committed
8

9
	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
Matt Bell's avatar
Matt Bell committed
10
	cmds "github.com/jbenet/go-ipfs/commands"
11
	core "github.com/jbenet/go-ipfs/core"
12
	corecmds "github.com/jbenet/go-ipfs/core/commands"
Brian Tiger Chow's avatar
Brian Tiger Chow committed
13
	coreunix "github.com/jbenet/go-ipfs/core/coreunix"
Jeromy's avatar
Jeromy committed
14
	ipns "github.com/jbenet/go-ipfs/fuse/ipns"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
15
	ci "github.com/jbenet/go-ipfs/p2p/crypto"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
16
	peer "github.com/jbenet/go-ipfs/p2p/peer"
17
	repo "github.com/jbenet/go-ipfs/repo"
18
	config "github.com/jbenet/go-ipfs/repo/config"
19
	fsrepo "github.com/jbenet/go-ipfs/repo/fsrepo"
Matt Bell's avatar
Matt Bell committed
20
	u "github.com/jbenet/go-ipfs/util"
21
	debugerror "github.com/jbenet/go-ipfs/util/debugerror"
Matt Bell's avatar
Matt Bell committed
22 23
)

24 25
const nBitsForKeypairDefault = 4096

Matt Bell's avatar
Matt Bell committed
26
var initCmd = &cmds.Command{
27 28 29 30
	Helptext: cmds.HelpText{
		Tagline:          "Initializes IPFS config file",
		ShortDescription: "Initializes IPFS configuration files and generates a new keypair.",
	},
31

Matt Bell's avatar
Matt Bell committed
32
	Options: []cmds.Option{
33
		cmds.IntOption("bits", "b", "Number of bits to use in the generated RSA private key (defaults to 4096)"),
34 35
		cmds.StringOption("passphrase", "p", "Passphrase for encrypting the private key"),
		cmds.BoolOption("force", "f", "Overwrite existing config (if it exists)"),
36 37 38 39 40

		// TODO need to decide whether to expose the override as a file or a
		// directory. That is: should we allow the user to also specify the
		// name of the file?
		// TODO cmds.StringOption("event-logs", "l", "Location for machine-readable event logs"),
Matt Bell's avatar
Matt Bell committed
41
	},
42
	Run: func(req cmds.Request) (interface{}, error) {
43

44
		force, _, err := req.Option("f").Bool() // if !found, it's okay force == false
45 46
		if err != nil {
			return nil, err
47 48
		}

49
		nBitsForKeypair, bitsOptFound, err := req.Option("b").Int()
50 51 52
		if err != nil {
			return nil, err
		}
53
		if !bitsOptFound {
54
			nBitsForKeypair = nBitsForKeypairDefault
55 56
		}

57
		return doInit(req.Context().ConfigRoot, force, nBitsForKeypair)
58 59
	},
}
Matt Bell's avatar
Matt Bell committed
60

61
var errRepoExists = debugerror.New(`ipfs configuration file already exists!
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
Reinitializing would overwrite your keys.
(use -f to force overwrite)
`)

var welcomeMsg = `Hello and Welcome to IPFS!

██╗██████╗ ███████╗███████╗
██║██╔══██╗██╔════╝██╔════╝
██║██████╔╝█████╗  ███████╗
██║██╔═══╝ ██╔══╝  ╚════██║
██║██║     ██║     ███████║
╚═╝╚═╝     ╚═╝     ╚══════╝

If you're seeing this, you have successfully installed
IPFS and are now interfacing with the ipfs merkledag!

For a short demo of what you can do, enter 'ipfs tour'
`

81 82
func initWithDefaults(repoRoot string) error {
	_, err := doInit(repoRoot, false, nBitsForKeypairDefault)
Brian Tiger Chow's avatar
Brian Tiger Chow committed
83
	return debugerror.Wrap(err)
84 85
}

86
func doInit(repoRoot string, force bool, nBitsForKeypair int) (interface{}, error) {
87

88
	u.POut("initializing ipfs node at %s\n", repoRoot)
89

90
	if fsrepo.IsInitialized(repoRoot) && !force {
91
		return nil, errRepoExists
92
	}
Matt Bell's avatar
Matt Bell committed
93

94
	conf, err := initConfig(nBitsForKeypair)
95
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
96
		return nil, err
97
	}
98
	if fsrepo.IsInitialized(repoRoot) {
99 100 101
		if err := fsrepo.Remove(repoRoot); err != nil {
			return nil, err
		}
102
	}
103 104 105
	if err := fsrepo.Init(repoRoot, conf); err != nil {
		return nil, err
	}
106
	if err := repo.ConfigureEventLogger(conf.Logs); err != nil {
107 108
		return nil, err
	}
109
	err = addTheWelcomeFile(repoRoot)
110 111 112 113
	if err != nil {
		return nil, err
	}

Jeromy's avatar
Jeromy committed
114 115 116 117 118
	err = initializeIpnsKeyspace(repoRoot)
	if err != nil {
		return nil, err
	}

119 120 121 122
	return nil, nil
}

// addTheWelcomeFile adds a file containing the welcome message to the newly
123
// minted node.
124
func addTheWelcomeFile(repoRoot string) error {
125
	ctx, cancel := context.WithCancel(context.Background())
126 127 128 129 130 131
	defer cancel()
	r := fsrepo.At(repoRoot)
	if err := r.Open(); err != nil { // NB: repo is owned by the node
		return err
	}
	nd, err := core.NewIPFSNode(ctx, core.Offline(r))
132
	if err != nil {
133
		return err
134 135 136 137
	}
	defer nd.Close()

	// Set up default file
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
138
	reader := bytes.NewBufferString(welcomeMsg)
Brian Tiger Chow's avatar
Brian Tiger Chow committed
139
	k, err := coreunix.Add(nd, reader)
140
	if err != nil {
141
		return fmt.Errorf("failed to write test file: %s", err)
142
	}
Brian Tiger Chow's avatar
fix  
Brian Tiger Chow committed
143
	fmt.Printf("\nto get started, enter: ipfs cat %s\n", k)
144
	return nil
Matt Bell's avatar
Matt Bell committed
145
}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
146

Jeromy's avatar
Jeromy committed
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
func initializeIpnsKeyspace(repoRoot string) error {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	r := fsrepo.At(repoRoot)
	if err := r.Open(); err != nil { // NB: repo is owned by the node
		return err
	}

	nd, err := core.NewIPFSNode(ctx, core.Offline(r))
	if err != nil {
		return err
	}
	defer nd.Close()

	err = nd.SetupOfflineRouting()
	if err != nil {
		return err
	}

	return ipns.InitializeKeyspace(nd, nd.PrivateKey)
}

170
func datastoreConfig() (*config.Datastore, error) {
171 172
	dspath, err := config.DataStorePath("")
	if err != nil {
173
		return nil, err
Brian Tiger Chow's avatar
Brian Tiger Chow committed
174
	}
175 176 177 178
	return &config.Datastore{
		Path: dspath,
		Type: "leveldb",
	}, nil
Brian Tiger Chow's avatar
Brian Tiger Chow committed
179
}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
180

181 182
func initConfig(nBitsForKeypair int) (*config.Config, error) {
	ds, err := datastoreConfig()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
183 184 185 186
	if err != nil {
		return nil, err
	}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
187
	identity, err := identityConfig(nBitsForKeypair)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
188 189 190 191
	if err != nil {
		return nil, err
	}

192
	logConfig, err := initLogs()
193 194 195 196
	if err != nil {
		return nil, err
	}

197 198 199 200 201
	bootstrapPeers, err := corecmds.DefaultBootstrapPeers()
	if err != nil {
		return nil, err
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
202 203
	conf := &config.Config{

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
204 205
		// setup the node's default addresses.
		// Note: two swarm listen addrs, one tcp, one utp.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
206
		Addresses: config.Addresses{
207 208
			Swarm: []string{
				"/ip4/0.0.0.0/tcp/4001",
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
209
				// "/ip4/0.0.0.0/udp/4002/utp", // disabled for now.
210 211
			},
			API: "/ip4/127.0.0.1/tcp/5001",
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
212 213
		},

214
		Bootstrap: bootstrapPeers,
215
		Datastore: *ds,
216
		Logs:      *logConfig,
217
		Identity:  identity,
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232

		// setup the node mount points.
		Mounts: config.Mounts{
			IPFS: "/ipfs",
			IPNS: "/ipns",
		},

		// tracking ipfs version used to generate the init folder and adding
		// update checker default setting.
		Version: config.VersionDefaultValue(),
	}

	return conf, nil
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
233 234
// identityConfig initializes a new identity.
func identityConfig(nbits int) (config.Identity, error) {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
235 236 237
	// TODO guard higher up
	ident := config.Identity{}
	if nbits < 1024 {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
238
		return ident, debugerror.New("Bitsize less than 1024 is considered unsafe.")
Brian Tiger Chow's avatar
Brian Tiger Chow committed
239 240
	}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
241
	fmt.Printf("generating key pair...")
Brian Tiger Chow's avatar
Brian Tiger Chow committed
242 243 244 245
	sk, pk, err := ci.GenerateKeyPair(ci.RSA, nbits)
	if err != nil {
		return ident, err
	}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
246
	fmt.Printf("done\n")
Brian Tiger Chow's avatar
Brian Tiger Chow committed
247 248 249 250 251 252 253 254 255

	// 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)

256
	id, err := peer.IDFromPublicKey(pk)
Brian Tiger Chow's avatar
Brian Tiger Chow committed
257 258 259 260
	if err != nil {
		return ident, err
	}
	ident.PeerID = id.Pretty()
Brian Tiger Chow's avatar
Brian Tiger Chow committed
261
	fmt.Printf("peer identity: %s\n", ident.PeerID)
Brian Tiger Chow's avatar
Brian Tiger Chow committed
262 263
	return ident, nil
}
264

265 266 267
// initLogs initializes the event logger.
func initLogs() (*config.Logs, error) {
	logpath, err := config.LogsPath("")
268
	if err != nil {
269
		return nil, err
270
	}
271
	conf := config.Logs{
272
		Filename: path.Join(logpath, "events.log"),
273
	}
274
	return &conf, nil
275
}