1// Copyright (c) 2013-2016 The btcsuite developers
2// Use of this source code is governed by an ISC
3// license that can be found in the LICENSE file.
4
5package main
6
7import (
8	"fmt"
9	"os"
10	"path/filepath"
11
12	"github.com/btcsuite/btcd/chaincfg"
13	"github.com/btcsuite/btcd/database"
14	_ "github.com/btcsuite/btcd/database/ffldb"
15	"github.com/btcsuite/btcd/wire"
16	"github.com/btcsuite/btcutil"
17	flags "github.com/jessevdk/go-flags"
18)
19
20const (
21	minCandidates        = 1
22	maxCandidates        = 20
23	defaultNumCandidates = 5
24	defaultDbType        = "ffldb"
25)
26
27var (
28	btcdHomeDir     = btcutil.AppDataDir("btcd", false)
29	defaultDataDir  = filepath.Join(btcdHomeDir, "data")
30	knownDbTypes    = database.SupportedDrivers()
31	activeNetParams = &chaincfg.MainNetParams
32)
33
34// config defines the configuration options for findcheckpoint.
35//
36// See loadConfig for details on the configuration load process.
37type config struct {
38	DataDir        string `short:"b" long:"datadir" description:"Location of the btcd data directory"`
39	DbType         string `long:"dbtype" description:"Database backend to use for the Block Chain"`
40	TestNet3       bool   `long:"testnet" description:"Use the test network"`
41	RegressionTest bool   `long:"regtest" description:"Use the regression test network"`
42	SimNet         bool   `long:"simnet" description:"Use the simulation test network"`
43	NumCandidates  int    `short:"n" long:"numcandidates" description:"Max num of checkpoint candidates to show {1-20}"`
44	UseGoOutput    bool   `short:"g" long:"gooutput" description:"Display the candidates using Go syntax that is ready to insert into the btcchain checkpoint list"`
45}
46
47// validDbType returns whether or not dbType is a supported database type.
48func validDbType(dbType string) bool {
49	for _, knownType := range knownDbTypes {
50		if dbType == knownType {
51			return true
52		}
53	}
54
55	return false
56}
57
58// netName returns the name used when referring to a bitcoin network.  At the
59// time of writing, btcd currently places blocks for testnet version 3 in the
60// data and log directory "testnet", which does not match the Name field of the
61// chaincfg parameters.  This function can be used to override this directory name
62// as "testnet" when the passed active network matches wire.TestNet3.
63//
64// A proper upgrade to move the data and log directories for this network to
65// "testnet3" is planned for the future, at which point this function can be
66// removed and the network parameter's name used instead.
67func netName(chainParams *chaincfg.Params) string {
68	switch chainParams.Net {
69	case wire.TestNet3:
70		return "testnet"
71	default:
72		return chainParams.Name
73	}
74}
75
76// loadConfig initializes and parses the config using command line options.
77func loadConfig() (*config, []string, error) {
78	// Default config.
79	cfg := config{
80		DataDir:       defaultDataDir,
81		DbType:        defaultDbType,
82		NumCandidates: defaultNumCandidates,
83	}
84
85	// Parse command line options.
86	parser := flags.NewParser(&cfg, flags.Default)
87	remainingArgs, err := parser.Parse()
88	if err != nil {
89		if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
90			parser.WriteHelp(os.Stderr)
91		}
92		return nil, nil, err
93	}
94
95	// Multiple networks can't be selected simultaneously.
96	funcName := "loadConfig"
97	numNets := 0
98	// Count number of network flags passed; assign active network params
99	// while we're at it
100	if cfg.TestNet3 {
101		numNets++
102		activeNetParams = &chaincfg.TestNet3Params
103	}
104	if cfg.RegressionTest {
105		numNets++
106		activeNetParams = &chaincfg.RegressionNetParams
107	}
108	if cfg.SimNet {
109		numNets++
110		activeNetParams = &chaincfg.SimNetParams
111	}
112	if numNets > 1 {
113		str := "%s: The testnet, regtest, and simnet params can't be " +
114			"used together -- choose one of the three"
115		err := fmt.Errorf(str, funcName)
116		fmt.Fprintln(os.Stderr, err)
117		parser.WriteHelp(os.Stderr)
118		return nil, nil, err
119	}
120
121	// Validate database type.
122	if !validDbType(cfg.DbType) {
123		str := "%s: The specified database type [%v] is invalid -- " +
124			"supported types %v"
125		err := fmt.Errorf(str, "loadConfig", cfg.DbType, knownDbTypes)
126		fmt.Fprintln(os.Stderr, err)
127		parser.WriteHelp(os.Stderr)
128		return nil, nil, err
129	}
130
131	// Append the network type to the data directory so it is "namespaced"
132	// per network.  In addition to the block database, there are other
133	// pieces of data that are saved to disk such as address manager state.
134	// All data is specific to a network, so namespacing the data directory
135	// means each individual piece of serialized data does not have to
136	// worry about changing names per network and such.
137	cfg.DataDir = filepath.Join(cfg.DataDir, netName(activeNetParams))
138
139	// Validate the number of candidates.
140	if cfg.NumCandidates < minCandidates || cfg.NumCandidates > maxCandidates {
141		str := "%s: The specified number of candidates is out of " +
142			"range -- parsed [%v]"
143		err = fmt.Errorf(str, "loadConfig", cfg.NumCandidates)
144		fmt.Fprintln(os.Stderr, err)
145		parser.WriteHelp(os.Stderr)
146		return nil, nil, err
147	}
148
149	return &cfg, remainingArgs, nil
150}
151