init.go 6.66 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 104 105 106 107 108 109 110 111 112 113
	if !fsrepo.IsInitialized(repoRoot) {
		if err := fsrepo.Init(repoRoot, conf); err != nil {
			return nil, err
		}
	} else {
		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
		}
114
	}
115

116
	if err := repo.ConfigureEventLogger(conf.Logs); err != nil {
117 118
		return nil, err
	}
Brian Tiger Chow's avatar
fix  
Brian Tiger Chow committed
119
	err = addTheWelcomeFile(conf)
120 121 122 123 124 125 126 127 128
	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
129
func addTheWelcomeFile(conf *config.Config) error {
130
	// TODO extract this file creation operation into a function
131
	ctx, cancel := context.WithCancel(context.Background())
132
	nd, err := core.NewIPFSNode(ctx, core.Offline(conf))
133
	if err != nil {
134
		return err
135 136
	}
	defer nd.Close()
137
	defer cancel()
138 139

	// Set up default file
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
140
	reader := bytes.NewBufferString(welcomeMsg)
141 142 143

	defnd, err := imp.BuildDagFromReader(reader, nd.DAG, nd.Pinning.GetManual(), chunk.DefaultSplitter)
	if err != nil {
144
		return err
145 146
	}

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

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

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

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

177
	logConfig, err := initLogs()
178 179 180 181
	if err != nil {
		return nil, err
	}

182 183 184 185 186
	bootstrapPeers, err := corecmds.DefaultBootstrapPeers()
	if err != nil {
		return nil, err
	}

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

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

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

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

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

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

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

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