init.go 6.48 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
	}
Brian Tiger Chow's avatar
fix  
Brian Tiger Chow committed
109
	err = addTheWelcomeFile(conf)
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
Brian Tiger Chow's avatar
fix  
Brian Tiger Chow committed
119
func addTheWelcomeFile(conf *config.Config) error {
120
	// TODO extract this file creation operation into a function
121
	ctx, cancel := context.WithCancel(context.Background())
122
	nd, err := core.NewIPFSNode(ctx, core.Offline(conf))
123
	if err != nil {
124
		return err
125 126
	}
	defer nd.Close()
127
	defer cancel()
128 129

	// Set up default file
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
130
	reader := bytes.NewBufferString(welcomeMsg)
131 132 133

	defnd, err := imp.BuildDagFromReader(reader, nd.DAG, nd.Pinning.GetManual(), chunk.DefaultSplitter)
	if err != nil {
134
		return err
135 136
	}

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

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

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

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

167
	logConfig, err := initLogs()
168 169 170 171
	if err != nil {
		return nil, err
	}

172 173 174 175 176
	bootstrapPeers, err := corecmds.DefaultBootstrapPeers()
	if err != nil {
		return nil, err
	}

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

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

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

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

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

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

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

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