init.go 6.33 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"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
14
	ci "github.com/jbenet/go-ipfs/p2p/crypto"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
15
	peer "github.com/jbenet/go-ipfs/p2p/peer"
16
	repo "github.com/jbenet/go-ipfs/repo"
17
	config "github.com/jbenet/go-ipfs/repo/config"
18
	fsrepo "github.com/jbenet/go-ipfs/repo/fsrepo"
Matt Bell's avatar
Matt Bell committed
19
	u "github.com/jbenet/go-ipfs/util"
20
	debugerror "github.com/jbenet/go-ipfs/util/debugerror"
Matt Bell's avatar
Matt Bell committed
21 22
)

23 24
const nBitsForKeypairDefault = 4096

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

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

		// 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
40
	},
41
	Run: func(req cmds.Request) (interface{}, error) {
42

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

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

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

60
var errRepoExists = debugerror.New(`ipfs configuration file already exists!
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
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'
`

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

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

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

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

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

	return nil, nil
}

// addTheWelcomeFile adds a file containing the welcome message to the newly
117
// minted node.
118
func addTheWelcomeFile(repoRoot string) error {
119
	ctx, cancel := context.WithCancel(context.Background())
120 121 122 123 124 125
	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))
126
	if err != nil {
127
		return err
128 129 130 131
	}
	defer nd.Close()

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

141
func datastoreConfig() (*config.Datastore, error) {
142 143
	dspath, err := config.DataStorePath("")
	if err != nil {
144
		return nil, err
Brian Tiger Chow's avatar
Brian Tiger Chow committed
145
	}
146 147 148 149
	return &config.Datastore{
		Path: dspath,
		Type: "leveldb",
	}, nil
Brian Tiger Chow's avatar
Brian Tiger Chow committed
150
}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
151

152 153
func initConfig(nBitsForKeypair int) (*config.Config, error) {
	ds, err := datastoreConfig()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
154 155 156 157
	if err != nil {
		return nil, err
	}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
158
	identity, err := identityConfig(nBitsForKeypair)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
159 160 161 162
	if err != nil {
		return nil, err
	}

163
	logConfig, err := initLogs()
164 165 166 167
	if err != nil {
		return nil, err
	}

168 169 170 171 172
	bootstrapPeers, err := corecmds.DefaultBootstrapPeers()
	if err != nil {
		return nil, err
	}

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

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
175 176
		// setup the node's default addresses.
		// Note: two swarm listen addrs, one tcp, one utp.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
177
		Addresses: config.Addresses{
178 179
			Swarm: []string{
				"/ip4/0.0.0.0/tcp/4001",
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
180
				// "/ip4/0.0.0.0/udp/4002/utp", // disabled for now.
181 182
			},
			API: "/ip4/127.0.0.1/tcp/5001",
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
183 184
		},

185
		Bootstrap: bootstrapPeers,
186
		Datastore: *ds,
187
		Logs:      *logConfig,
188
		Identity:  identity,
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203

		// 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
204 205
// identityConfig initializes a new identity.
func identityConfig(nbits int) (config.Identity, error) {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
206 207 208
	// TODO guard higher up
	ident := config.Identity{}
	if nbits < 1024 {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
209
		return ident, debugerror.New("Bitsize less than 1024 is considered unsafe.")
Brian Tiger Chow's avatar
Brian Tiger Chow committed
210 211
	}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
212
	fmt.Printf("generating key pair...")
Brian Tiger Chow's avatar
Brian Tiger Chow committed
213 214 215 216
	sk, pk, err := ci.GenerateKeyPair(ci.RSA, nbits)
	if err != nil {
		return ident, err
	}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
217
	fmt.Printf("done\n")
Brian Tiger Chow's avatar
Brian Tiger Chow committed
218 219 220 221 222 223 224 225 226

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

227
	id, err := peer.IDFromPublicKey(pk)
Brian Tiger Chow's avatar
Brian Tiger Chow committed
228 229 230 231
	if err != nil {
		return ident, err
	}
	ident.PeerID = id.Pretty()
Brian Tiger Chow's avatar
Brian Tiger Chow committed
232
	fmt.Printf("peer identity: %s\n", ident.PeerID)
Brian Tiger Chow's avatar
Brian Tiger Chow committed
233 234
	return ident, nil
}
235

236 237 238
// initLogs initializes the event logger.
func initLogs() (*config.Logs, error) {
	logpath, err := config.LogsPath("")
239
	if err != nil {
240
		return nil, err
241
	}
242
	conf := config.Logs{
243
		Filename: path.Join(logpath, "events.log"),
244
	}
245
	return &conf, nil
246
}