init.go 6.56 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"
Matt Bell's avatar
Matt Bell committed
7

8
	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
Matt Bell's avatar
Matt Bell committed
9
	cmds "github.com/jbenet/go-ipfs/commands"
10
	core "github.com/jbenet/go-ipfs/core"
11
	corecmds "github.com/jbenet/go-ipfs/core/commands"
Brian Tiger Chow's avatar
Brian Tiger Chow committed
12
	coreunix "github.com/jbenet/go-ipfs/core/coreunix"
Jeromy's avatar
Jeromy committed
13
	ipns "github.com/jbenet/go-ipfs/fuse/ipns"
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
	config "github.com/jbenet/go-ipfs/repo/config"
17
	fsrepo "github.com/jbenet/go-ipfs/repo/fsrepo"
Matt Bell's avatar
Matt Bell committed
18
	u "github.com/jbenet/go-ipfs/util"
19
	debugerror "github.com/jbenet/go-ipfs/util/debugerror"
Matt Bell's avatar
Matt Bell committed
20 21
)

22 23
const nBitsForKeypairDefault = 4096

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

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

		// 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
39
	},
40
	Run: func(req cmds.Request, res cmds.Response) {
41

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

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

57 58 59 60 61 62
		output, err := doInit(req.Context().ConfigRoot, force, nBitsForKeypair)
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}
		res.SetOutput(output)
63 64
	},
}
Matt Bell's avatar
Matt Bell committed
65

66
var errRepoExists = debugerror.New(`ipfs configuration file already exists!
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
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!

`

85 86
func initWithDefaults(repoRoot string) error {
	_, err := doInit(repoRoot, false, nBitsForKeypairDefault)
Brian Tiger Chow's avatar
Brian Tiger Chow committed
87
	return debugerror.Wrap(err)
88 89
}

90
func doInit(repoRoot string, force bool, nBitsForKeypair int) (interface{}, error) {
91

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

94
	if fsrepo.IsInitialized(repoRoot) && !force {
95
		return nil, errRepoExists
96
	}
Matt Bell's avatar
Matt Bell committed
97

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

Jeromy's avatar
Jeromy committed
115 116 117 118 119
	err = initializeIpnsKeyspace(repoRoot)
	if err != nil {
		return nil, err
	}

120 121 122 123
	return nil, nil
}

// addTheWelcomeFile adds a file containing the welcome message to the newly
124
// minted node.
125
func addTheWelcomeFile(repoRoot string) error {
126
	ctx, cancel := context.WithCancel(context.Background())
127 128 129 130 131 132
	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))
133
	if err != nil {
134
		return err
135 136 137 138
	}
	defer nd.Close()

	// Set up default file
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
139
	reader := bytes.NewBufferString(welcomeMsg)
Brian Tiger Chow's avatar
Brian Tiger Chow committed
140
	k, err := coreunix.Add(nd, reader)
141
	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

Jeromy's avatar
Jeromy committed
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
func initializeIpnsKeyspace(repoRoot string) error {
	ctx, cancel := context.WithCancel(context.Background())
	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))
	if err != nil {
		return err
	}
	defer nd.Close()

	err = nd.SetupOfflineRouting()
	if err != nil {
		return err
	}

	return ipns.InitializeKeyspace(nd, nd.PrivateKey)
}

171
func datastoreConfig() (*config.Datastore, error) {
172 173
	dspath, err := config.DataStorePath("")
	if err != nil {
174
		return nil, err
Brian Tiger Chow's avatar
Brian Tiger Chow committed
175
	}
176 177 178 179
	return &config.Datastore{
		Path: dspath,
		Type: "leveldb",
	}, nil
Brian Tiger Chow's avatar
Brian Tiger Chow committed
180
}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
181

182 183
func initConfig(nBitsForKeypair int) (*config.Config, error) {
	ds, err := datastoreConfig()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
184 185 186 187
	if err != nil {
		return nil, err
	}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
188
	identity, err := identityConfig(nBitsForKeypair)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
189 190 191 192
	if err != nil {
		return nil, err
	}

193 194 195 196 197
	bootstrapPeers, err := corecmds.DefaultBootstrapPeers()
	if err != nil {
		return nil, err
	}

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

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
200 201
		// setup the node's default addresses.
		// Note: two swarm listen addrs, one tcp, one utp.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
202
		Addresses: config.Addresses{
203 204
			Swarm: []string{
				"/ip4/0.0.0.0/tcp/4001",
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
205
				// "/ip4/0.0.0.0/udp/4002/utp", // disabled for now.
206 207
			},
			API: "/ip4/127.0.0.1/tcp/5001",
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
208 209
		},

210
		Bootstrap: bootstrapPeers,
211
		Datastore: *ds,
212
		Identity:  identity,
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227

		// 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
228 229
// identityConfig initializes a new identity.
func identityConfig(nbits int) (config.Identity, error) {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
230 231 232
	// TODO guard higher up
	ident := config.Identity{}
	if nbits < 1024 {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
233
		return ident, debugerror.New("Bitsize less than 1024 is considered unsafe.")
Brian Tiger Chow's avatar
Brian Tiger Chow committed
234 235
	}

236
	fmt.Printf("generating %v-bit RSA keypair...", nbits)
Brian Tiger Chow's avatar
Brian Tiger Chow committed
237 238 239 240
	sk, pk, err := ci.GenerateKeyPair(ci.RSA, nbits)
	if err != nil {
		return ident, err
	}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
241
	fmt.Printf("done\n")
Brian Tiger Chow's avatar
Brian Tiger Chow committed
242 243 244 245 246 247 248 249 250

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

251
	id, err := peer.IDFromPublicKey(pk)
Brian Tiger Chow's avatar
Brian Tiger Chow committed
252 253 254 255
	if err != nil {
		return ident, err
	}
	ident.PeerID = id.Pretty()
Brian Tiger Chow's avatar
Brian Tiger Chow committed
256
	fmt.Printf("peer identity: %s\n", ident.PeerID)
Brian Tiger Chow's avatar
Brian Tiger Chow committed
257 258
	return ident, nil
}