init.go 6.54 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
func initWithDefaults(configRoot string) error {
82
	_, err := doInit(configRoot, false, nBitsForKeypairDefault)
Brian Tiger Chow's avatar
Brian Tiger Chow committed
83
	return debugerror.Wrap(err)
84 85
}

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

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

90
	if fsrepo.ConfigIsInitialized(configRoot) && !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
	r := fsrepo.At(configRoot)
	if err := r.Open(); err != nil {
101 102
		return nil, err
	}
103
	if err := r.SetConfig(conf); err != nil {
104 105
		return nil, err
	}
106 107 108 109
	if err := r.Close(); err != nil {
		return nil, err
	}
	if err := repo.ConfigureEventLogger(conf.Logs); err != nil {
110 111
		return nil, err
	}
Brian Tiger Chow's avatar
fix  
Brian Tiger Chow committed
112
	err = addTheWelcomeFile(conf)
113 114 115 116 117 118 119 120 121
	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
122
func addTheWelcomeFile(conf *config.Config) error {
123
	// TODO extract this file creation operation into a function
124
	ctx, cancel := context.WithCancel(context.Background())
125
	nd, err := core.NewIPFSNode(ctx, core.Offline(conf))
126
	if err != nil {
127
		return err
128 129
	}
	defer nd.Close()
130
	defer cancel()
131 132

	// Set up default file
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
133
	reader := bytes.NewBufferString(welcomeMsg)
134 135 136

	defnd, err := imp.BuildDagFromReader(reader, nd.DAG, nd.Pinning.GetManual(), chunk.DefaultSplitter)
	if err != nil {
137
		return err
138 139
	}

140 141
	k, err := defnd.Key()
	if err != nil {
142
		return fmt.Errorf("failed to write test file: %s", err)
143
	}
Brian Tiger Chow's avatar
fix  
Brian Tiger Chow committed
144
	fmt.Printf("\nto get started, enter: ipfs cat %s\n", k)
145
	return nil
Matt Bell's avatar
Matt Bell committed
146
}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
147

148
func datastoreConfig() (*config.Datastore, error) {
149 150
	dspath, err := config.DataStorePath("")
	if err != nil {
151
		return nil, err
Brian Tiger Chow's avatar
Brian Tiger Chow committed
152
	}
153 154 155 156
	return &config.Datastore{
		Path: dspath,
		Type: "leveldb",
	}, nil
Brian Tiger Chow's avatar
Brian Tiger Chow committed
157
}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
158

159 160
func initConfig(nBitsForKeypair int) (*config.Config, error) {
	ds, err := datastoreConfig()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
161 162 163 164
	if err != nil {
		return nil, err
	}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
165
	identity, err := identityConfig(nBitsForKeypair)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
166 167 168 169
	if err != nil {
		return nil, err
	}

170
	logConfig, err := initLogs()
171 172 173 174
	if err != nil {
		return nil, err
	}

175 176 177 178 179
	bootstrapPeers, err := corecmds.DefaultBootstrapPeers()
	if err != nil {
		return nil, err
	}

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

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
182 183
		// setup the node's default addresses.
		// Note: two swarm listen addrs, one tcp, one utp.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
184
		Addresses: config.Addresses{
185 186
			Swarm: []string{
				"/ip4/0.0.0.0/tcp/4001",
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
187
				// "/ip4/0.0.0.0/udp/4002/utp", // disabled for now.
188 189
			},
			API: "/ip4/127.0.0.1/tcp/5001",
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
190 191
		},

192
		Bootstrap: bootstrapPeers,
193
		Datastore: *ds,
194
		Logs:      *logConfig,
195
		Identity:  identity,
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210

		// 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
211 212
// identityConfig initializes a new identity.
func identityConfig(nbits int) (config.Identity, error) {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
213 214 215
	// TODO guard higher up
	ident := config.Identity{}
	if nbits < 1024 {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
216
		return ident, debugerror.New("Bitsize less than 1024 is considered unsafe.")
Brian Tiger Chow's avatar
Brian Tiger Chow committed
217 218
	}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
219
	fmt.Printf("generating key pair...")
Brian Tiger Chow's avatar
Brian Tiger Chow committed
220 221 222 223
	sk, pk, err := ci.GenerateKeyPair(ci.RSA, nbits)
	if err != nil {
		return ident, err
	}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
224
	fmt.Printf("done\n")
Brian Tiger Chow's avatar
Brian Tiger Chow committed
225 226 227 228 229 230 231 232 233

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

234
	id, err := peer.IDFromPublicKey(pk)
Brian Tiger Chow's avatar
Brian Tiger Chow committed
235 236 237 238
	if err != nil {
		return ident, err
	}
	ident.PeerID = id.Pretty()
Brian Tiger Chow's avatar
Brian Tiger Chow committed
239
	fmt.Printf("peer identity: %s\n", ident.PeerID)
Brian Tiger Chow's avatar
Brian Tiger Chow committed
240 241
	return ident, nil
}
242

243 244 245
// initLogs initializes the event logger.
func initLogs() (*config.Logs, error) {
	logpath, err := config.LogsPath("")
246
	if err != nil {
247
		return nil, err
248
	}
249
	conf := config.Logs{
250
		Filename: path.Join(logpath, "events.log"),
251
	}
252
	return &conf, nil
253
}