mirror of
https://github.com/ipfs/kubo.git
synced 2026-02-23 19:37:46 +08:00
this is a major refactor of the entire codebase it changes the monolithic peer.Peer into using a peer.ID and a peer.Peerstore. Other changes: - removed handshake3. - testutil vastly simplified peer - secio bugfix + debugging logs - testutil: RandKeyPair - backpressure bugfix: w.o.w. - peer: added hex enc/dec - peer: added a PeerInfo struct PeerInfo is a small struct used to pass around a peer with a set of addresses and keys. This is not meant to be a complete view of the system, but rather to model updates to the peerstore. It is used by things like the routing system. - updated peer/queue + peerset - latency metrics - testutil: use crand for PeerID gen RandPeerID generates random "valid" peer IDs. it does not NEED to generate keys because it is as if we lost the key right away. fine to read some randomness and hash it. to generate proper keys and an ID, use: sk, pk, _ := testutil.RandKeyPair() id, _ := peer.IDFromPublicKey(pk) Also added RandPeerIDFatal helper - removed old spipe - updated seccat - core: cleanup initIdentity - removed old getFromPeerList
317 lines
8.4 KiB
Go
317 lines
8.4 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
|
|
context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
|
|
cmds "github.com/jbenet/go-ipfs/commands"
|
|
config "github.com/jbenet/go-ipfs/config"
|
|
core "github.com/jbenet/go-ipfs/core"
|
|
ci "github.com/jbenet/go-ipfs/crypto"
|
|
imp "github.com/jbenet/go-ipfs/importer"
|
|
chunk "github.com/jbenet/go-ipfs/importer/chunk"
|
|
peer "github.com/jbenet/go-ipfs/peer"
|
|
repo "github.com/jbenet/go-ipfs/repo"
|
|
u "github.com/jbenet/go-ipfs/util"
|
|
debugerror "github.com/jbenet/go-ipfs/util/debugerror"
|
|
)
|
|
|
|
const nBitsForKeypairDefault = 4096
|
|
|
|
var initCmd = &cmds.Command{
|
|
Helptext: cmds.HelpText{
|
|
Tagline: "Initializes IPFS config file",
|
|
ShortDescription: "Initializes IPFS configuration files and generates a new keypair.",
|
|
},
|
|
|
|
Options: []cmds.Option{
|
|
cmds.IntOption("bits", "b", "Number of bits to use in the generated RSA private key (defaults to 4096)"),
|
|
cmds.StringOption("passphrase", "p", "Passphrase for encrypting the private key"),
|
|
cmds.BoolOption("force", "f", "Overwrite existing config (if it exists)"),
|
|
cmds.StringOption("datastore", "d", "Location for the IPFS data store"),
|
|
|
|
// 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"),
|
|
},
|
|
Run: func(req cmds.Request) (interface{}, error) {
|
|
|
|
dspathOverride, _, err := req.Option("d").String() // if !found it's okay. Let == ""
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
force, _, err := req.Option("f").Bool() // if !found, it's okay force == false
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
nBitsForKeypair, bitsOptFound, err := req.Option("b").Int()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !bitsOptFound {
|
|
nBitsForKeypair = nBitsForKeypairDefault
|
|
}
|
|
|
|
return doInit(req.Context().ConfigRoot, dspathOverride, force, nBitsForKeypair)
|
|
},
|
|
}
|
|
|
|
var errCannotInitConfigExists = debugerror.New(`ipfs configuration file already exists!
|
|
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'
|
|
`
|
|
|
|
func initWithDefaults(configRoot string) error {
|
|
_, err := doInit(configRoot, "", false, nBitsForKeypairDefault)
|
|
return debugerror.Wrap(err)
|
|
}
|
|
|
|
func doInit(configRoot string, dspathOverride string, force bool, nBitsForKeypair int) (interface{}, error) {
|
|
|
|
u.POut("initializing ipfs node at %s\n", configRoot)
|
|
|
|
configFilename, err := config.Filename(configRoot)
|
|
if err != nil {
|
|
return nil, debugerror.New("Couldn't get home directory path")
|
|
}
|
|
|
|
if u.FileExists(configFilename) && !force {
|
|
return nil, errCannotInitConfigExists
|
|
}
|
|
|
|
conf, err := initConfig(configFilename, dspathOverride, nBitsForKeypair)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = addTheWelcomeFile(conf)
|
|
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
|
|
func addTheWelcomeFile(conf *config.Config) error {
|
|
// TODO extract this file creation operation into a function
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
nd, err := core.NewIpfsNode(ctx, conf, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer nd.Close()
|
|
defer cancel()
|
|
|
|
// Set up default file
|
|
reader := bytes.NewBufferString(welcomeMsg)
|
|
|
|
defnd, err := imp.BuildDagFromReader(reader, nd.DAG, nd.Pinning.GetManual(), chunk.DefaultSplitter)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
k, err := defnd.Key()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to write test file: %s", err)
|
|
}
|
|
fmt.Printf("\nto get started, enter: ipfs cat %s\n", k)
|
|
return nil
|
|
}
|
|
|
|
func datastoreConfig(dspath string) (config.Datastore, error) {
|
|
ds := config.Datastore{}
|
|
if len(dspath) == 0 {
|
|
var err error
|
|
dspath, err = config.DataStorePath("")
|
|
if err != nil {
|
|
return ds, err
|
|
}
|
|
}
|
|
ds.Path = dspath
|
|
ds.Type = "leveldb"
|
|
|
|
err := initCheckDir(dspath)
|
|
if err != nil {
|
|
return ds, debugerror.Errorf("datastore: %s", err)
|
|
}
|
|
|
|
return ds, nil
|
|
}
|
|
|
|
func initConfig(configFilename string, dspathOverride string, nBitsForKeypair int) (*config.Config, error) {
|
|
ds, err := datastoreConfig(dspathOverride)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
identity, err := identityConfig(nBitsForKeypair)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
logConfig, err := initLogs("") // TODO allow user to override dir
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
conf := &config.Config{
|
|
|
|
// setup the node's default addresses.
|
|
// Note: two swarm listen addrs, one tcp, one utp.
|
|
Addresses: config.Addresses{
|
|
Swarm: []string{
|
|
"/ip4/0.0.0.0/tcp/4001",
|
|
"/ip4/0.0.0.0/udp/4002/utp",
|
|
},
|
|
API: "/ip4/127.0.0.1/tcp/5001",
|
|
},
|
|
|
|
Bootstrap: []*config.BootstrapPeer{
|
|
&config.BootstrapPeer{ // Use these hardcoded bootstrap peers for now.
|
|
// mars.i.ipfs.io
|
|
PeerID: "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ",
|
|
Address: "/ip4/104.131.131.82/tcp/4001",
|
|
},
|
|
&config.BootstrapPeer{
|
|
// Neptune
|
|
PeerID: "QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z",
|
|
Address: "/ip4/104.236.176.52/tcp/4001",
|
|
},
|
|
&config.BootstrapPeer{
|
|
// Pluto
|
|
PeerID: "QmSoLpPVmHKQ4XTPdz8tjDFgdeRFkpV8JgYq8JVJ69RrZm",
|
|
Address: "/ip4/104.236.179.241/tcp/4001",
|
|
},
|
|
&config.BootstrapPeer{
|
|
// Uranus
|
|
PeerID: "QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm",
|
|
Address: "/ip4/162.243.248.213/tcp/4001",
|
|
},
|
|
&config.BootstrapPeer{
|
|
// Saturn
|
|
PeerID: "QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu",
|
|
Address: "/ip4/128.199.219.111/tcp/4001",
|
|
},
|
|
},
|
|
|
|
Datastore: ds,
|
|
|
|
Logs: logConfig,
|
|
|
|
Identity: identity,
|
|
|
|
// 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(),
|
|
}
|
|
|
|
if err := config.WriteConfigFile(configFilename, conf); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return conf, nil
|
|
}
|
|
|
|
// identityConfig initializes a new identity.
|
|
func identityConfig(nbits int) (config.Identity, error) {
|
|
// TODO guard higher up
|
|
ident := config.Identity{}
|
|
if nbits < 1024 {
|
|
return ident, debugerror.New("Bitsize less than 1024 is considered unsafe.")
|
|
}
|
|
|
|
fmt.Printf("generating key pair...")
|
|
sk, pk, err := ci.GenerateKeyPair(ci.RSA, nbits)
|
|
if err != nil {
|
|
return ident, err
|
|
}
|
|
fmt.Printf("done\n")
|
|
|
|
// 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)
|
|
|
|
id, err := peer.IDFromPublicKey(pk)
|
|
if err != nil {
|
|
return ident, err
|
|
}
|
|
ident.PeerID = id.Pretty()
|
|
fmt.Printf("peer identity: %s\n", ident.PeerID)
|
|
return ident, nil
|
|
}
|
|
|
|
// initLogs initializes the event logger at the specified path. It uses the
|
|
// default log path if no path is provided.
|
|
func initLogs(logpath string) (config.Logs, error) {
|
|
if len(logpath) == 0 {
|
|
var err error
|
|
logpath, err = config.LogsPath("")
|
|
if err != nil {
|
|
return config.Logs{}, debugerror.Wrap(err)
|
|
}
|
|
}
|
|
err := initCheckDir(logpath)
|
|
if err != nil {
|
|
return config.Logs{}, debugerror.Errorf("logs: %s", err)
|
|
}
|
|
conf := config.Logs{
|
|
Filename: path.Join(logpath, "events.log"),
|
|
}
|
|
err = repo.ConfigureEventLogger(conf)
|
|
if err != nil {
|
|
return conf, err
|
|
}
|
|
return conf, nil
|
|
}
|
|
|
|
// initCheckDir ensures the directory exists and is writable
|
|
func initCheckDir(path string) error {
|
|
// Construct the path if missing
|
|
if err := os.MkdirAll(path, os.ModePerm); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Check the directory is writeable
|
|
if f, err := os.Create(filepath.Join(path, "._check_writeable")); err == nil {
|
|
os.Remove(f.Name())
|
|
} else {
|
|
return debugerror.New("'" + path + "' is not writeable")
|
|
}
|
|
return nil
|
|
}
|