init.go 6.73 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"
13 14
	imp "github.com/jbenet/go-ipfs/importer"
	chunk "github.com/jbenet/go-ipfs/importer/chunk"
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

Brian Tiger Chow's avatar
Brian Tiger Chow committed
61
var errCannotInitConfigExists = 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, errCannotInitConfigExists
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

99 100 101 102 103
	if !fsrepo.IsInitialized(repoRoot) {
		if err := fsrepo.Init(repoRoot, conf); err != nil {
			return nil, err
		}
	} else {
104 105 106
		if err := fsrepo.Remove(repoRoot); err != nil {
			return nil, err
		}
107 108 109 110 111 112 113 114 115 116
		r := fsrepo.At(repoRoot)
		if err := r.Open(); err != nil {
			return nil, err
		}
		if err := r.SetConfig(conf); err != nil {
			return nil, err
		}
		if err := r.Close(); err != nil {
			return nil, err
		}
117
	}
118

119
	if err := repo.ConfigureEventLogger(conf.Logs); err != nil {
120 121
		return nil, err
	}
Brian Tiger Chow's avatar
fix  
Brian Tiger Chow committed
122
	err = addTheWelcomeFile(conf)
123 124 125 126 127 128 129 130 131
	if err != nil {
		return nil, err
	}

	return nil, nil
}

// addTheWelcomeFile adds a file containing the welcome message to the newly
// minted node. On success, it calls onSuccess
Brian Tiger Chow's avatar
fix  
Brian Tiger Chow committed
132
func addTheWelcomeFile(conf *config.Config) error {
133
	// TODO extract this file creation operation into a function
134
	ctx, cancel := context.WithCancel(context.Background())
135
	nd, err := core.NewIPFSNode(ctx, core.Offline(conf))
136
	if err != nil {
137
		return err
138 139
	}
	defer nd.Close()
140
	defer cancel()
141 142

	// Set up default file
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
143
	reader := bytes.NewBufferString(welcomeMsg)
144 145 146

	defnd, err := imp.BuildDagFromReader(reader, nd.DAG, nd.Pinning.GetManual(), chunk.DefaultSplitter)
	if err != nil {
147
		return err
148 149
	}

150 151
	k, err := defnd.Key()
	if err != nil {
152
		return fmt.Errorf("failed to write test file: %s", err)
153
	}
Brian Tiger Chow's avatar
fix  
Brian Tiger Chow committed
154
	fmt.Printf("\nto get started, enter: ipfs cat %s\n", k)
155
	return nil
Matt Bell's avatar
Matt Bell committed
156
}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
157

158
func datastoreConfig() (*config.Datastore, error) {
159 160
	dspath, err := config.DataStorePath("")
	if err != nil {
161
		return nil, err
Brian Tiger Chow's avatar
Brian Tiger Chow committed
162
	}
163 164 165 166
	return &config.Datastore{
		Path: dspath,
		Type: "leveldb",
	}, nil
Brian Tiger Chow's avatar
Brian Tiger Chow committed
167
}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
168

169 170
func initConfig(nBitsForKeypair int) (*config.Config, error) {
	ds, err := datastoreConfig()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
171 172 173 174
	if err != nil {
		return nil, err
	}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
175
	identity, err := identityConfig(nBitsForKeypair)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
176 177 178 179
	if err != nil {
		return nil, err
	}

180
	logConfig, err := initLogs()
181 182 183 184
	if err != nil {
		return nil, err
	}

185 186 187 188 189
	bootstrapPeers, err := corecmds.DefaultBootstrapPeers()
	if err != nil {
		return nil, err
	}

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

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
192 193
		// setup the node's default addresses.
		// Note: two swarm listen addrs, one tcp, one utp.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
194
		Addresses: config.Addresses{
195 196
			Swarm: []string{
				"/ip4/0.0.0.0/tcp/4001",
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
197
				// "/ip4/0.0.0.0/udp/4002/utp", // disabled for now.
198 199
			},
			API: "/ip4/127.0.0.1/tcp/5001",
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
200 201
		},

202
		Bootstrap: bootstrapPeers,
203
		Datastore: *ds,
204
		Logs:      *logConfig,
205
		Identity:  identity,
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220

		// 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
221 222
// identityConfig initializes a new identity.
func identityConfig(nbits int) (config.Identity, error) {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
223 224 225
	// TODO guard higher up
	ident := config.Identity{}
	if nbits < 1024 {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
226
		return ident, debugerror.New("Bitsize less than 1024 is considered unsafe.")
Brian Tiger Chow's avatar
Brian Tiger Chow committed
227 228
	}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
229
	fmt.Printf("generating key pair...")
Brian Tiger Chow's avatar
Brian Tiger Chow committed
230 231 232 233
	sk, pk, err := ci.GenerateKeyPair(ci.RSA, nbits)
	if err != nil {
		return ident, err
	}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
234
	fmt.Printf("done\n")
Brian Tiger Chow's avatar
Brian Tiger Chow committed
235 236 237 238 239 240 241 242 243

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

244
	id, err := peer.IDFromPublicKey(pk)
Brian Tiger Chow's avatar
Brian Tiger Chow committed
245 246 247 248
	if err != nil {
		return ident, err
	}
	ident.PeerID = id.Pretty()
Brian Tiger Chow's avatar
Brian Tiger Chow committed
249
	fmt.Printf("peer identity: %s\n", ident.PeerID)
Brian Tiger Chow's avatar
Brian Tiger Chow committed
250 251
	return ident, nil
}
252

253 254 255
// initLogs initializes the event logger.
func initLogs() (*config.Logs, error) {
	logpath, err := config.LogsPath("")
256
	if err != nil {
257
		return nil, err
258
	}
259
	conf := config.Logs{
260
		Filename: path.Join(logpath, "events.log"),
261
	}
262
	return &conf, nil
263
}