init.go 6.58 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

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 114 115 116 117 118
	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
119
func addTheWelcomeFile(repoRoot string) error {
120
	// TODO extract this file creation operation into a function
121
	ctx, cancel := context.WithCancel(context.Background())
122 123 124 125 126 127
	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))
128
	if err != nil {
129
		return err
130 131 132 133
	}
	defer nd.Close()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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