forked from spacemeshos/go-spacemesh
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
285 lines (226 loc) · 7.75 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
// Package app provides the cli app shell of a Spacemesh p2p node
package app
import (
"fmt"
"os"
"os/signal"
"path/filepath"
"runtime"
"sort"
"github.com/spacemeshos/go-spacemesh/accounts"
api "github.com/spacemeshos/go-spacemesh/api"
apiconf "github.com/spacemeshos/go-spacemesh/api/config"
"github.com/spacemeshos/go-spacemesh/filesystem"
"github.com/spacemeshos/go-spacemesh/log"
"github.com/spacemeshos/go-spacemesh/p2p"
"gopkg.in/urfave/cli.v1"
"gopkg.in/urfave/cli.v1/altsrc"
"github.com/spacemeshos/go-spacemesh/app/config"
nodeparams "github.com/spacemeshos/go-spacemesh/p2p/nodeconfig"
"github.com/spacemeshos/go-spacemesh/p2p/timesync"
)
// EntryPointCreated channel is used to announce that the main App instance was created
// mainly used for testing now.
var EntryPointCreated = make(chan bool, 1)
// SpacemeshApp is the cli app singleton
type SpacemeshApp struct {
*cli.App
Node p2p.LocalNode
NodeInitCallback chan bool
grpcAPIService *api.SpaceMeshGrpcService
jsonAPIService *api.JSONHTTPServer
}
// App is main app entry point.
// It provides access the local node and other top-level modules.
var App *SpacemeshApp
var (
appFlags = []cli.Flag{
config.LoadConfigFileFlag,
config.DataFolderPathFlag,
// add all additional app flags here ...
}
nodeFlags = []cli.Flag{
nodeparams.KSecurityFlag,
nodeparams.LocalTCPPortFlag,
nodeparams.NodeIDFlag,
nodeparams.NetworkDialTimeout,
nodeparams.NetworkConnKeepAlive,
nodeparams.NetworkIDFlag,
nodeparams.SwarmBootstrap,
nodeparams.RoutingTableBucketSizdFlag,
nodeparams.RoutingTableAlphaFlag,
nodeparams.RandomConnectionsFlag,
nodeparams.BootstrapNodesFlag,
//timesync flags
nodeparams.MaxAllowedDriftFlag,
nodeparams.NtpQueriesFlag,
nodeparams.DefaultTimeoutLatencyFlag,
nodeparams.RefreshNtpIntervalFlag,
// add all additional node flags here ...
}
apiFlags = []cli.Flag{
apiconf.StartGrpcAPIServerFlag,
apiconf.GrpcServerPortFlag,
apiconf.StartJSONApiServerFlag,
apiconf.JSONServerPortFlag,
}
// ExitApp is a channel used to signal the app to gracefully exit.
ExitApp = make(chan bool, 1)
// Version is the app's semantic version. Designed to be overwritten by make.
Version = "0.0.1"
// Branch is the git branch used to build the App. Designed to be overwritten by make.
Branch = ""
// Commit is the git commit used to build the app. Designed to be overwritten by make.
Commit = ""
)
// add toml config file support and sample toml file
func newSpacemeshApp() *SpacemeshApp {
app := cli.NewApp()
app.Name = filepath.Base(os.Args[0])
app.Author = config.AppAuthor
app.Email = config.AppAuthorEmail
app.Version = Version
if len(Commit) > 8 {
app.Version += " " + Commit[:8]
}
app.Usage = config.AppUsage
app.HideVersion = true
app.Copyright = config.AppCopyrightNotice
app.Commands = []cli.Command{
config.NewVersionCommand(Version, Branch, Commit),
// add all other commands here
}
app.Flags = append(app.Flags, appFlags...)
app.Flags = append(app.Flags, nodeFlags...)
app.Flags = append(app.Flags, apiFlags...)
sort.Sort(cli.FlagsByName(app.Flags))
sma := &SpacemeshApp{app, nil, make(chan bool, 1), nil, nil}
// setup callbacks
app.Before = sma.before
app.Action = sma.startSpacemeshNode
app.After = sma.cleanup
return sma
}
// startSpacemeshNode starts the spacemesh local node.
func startSpacemeshNode(ctx *cli.Context) error {
return App.startSpacemeshNode(ctx)
}
// setupLogging configured the app logging system.
func (app *SpacemeshApp) setupLogging() {
// setup logging early
dataDir, err := filesystem.GetSpacemeshDataDirectoryPath()
if err != nil {
log.Error("Failed to setup spacemesh data dir")
panic(err)
}
// app-level logging
log.InitSpacemeshLoggingSystem(dataDir, "spacemesh.log")
log.Info("\n\nSpacemesh app session starting... %s", app.getAppInfo())
}
func (app *SpacemeshApp) getAppInfo() string {
return fmt.Sprintf("App version: %s. Git: %s - %s . Go Version: %s. OS: %s-%s ",
Version, Branch, Commit, runtime.Version(), runtime.GOOS, runtime.GOARCH)
}
func (app *SpacemeshApp) before(ctx *cli.Context) error {
// exit gracefully - e.g. with app cleanup on sig abort (ctrl-c)
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, os.Interrupt)
go func() {
for range signalChan {
log.Info("Received an interrupt, stopping services...\n")
ExitApp <- true
}
}()
if configPath := filesystem.GetCanonicalPath(config.ConfigValues.ConfigFilePath); len(configPath) > 1 {
if filesystem.PathExists(configPath) {
log.Info("Loading config file (path):", configPath)
err := altsrc.InitInputSourceWithContext(ctx.App.Flags, func(context *cli.Context) (altsrc.InputSourceContext, error) {
toml, err := altsrc.NewTomlSourceFromFile(configPath)
return toml, err
})(ctx)
if err != nil {
log.Error("Config file had an error:", err)
return err
}
} else {
log.Warning("Could not find config file using default values path:", configPath)
}
} else {
log.Warning("No config file defined using default config")
}
app.setupLogging()
// todo: add misc app setup here (metrics, debug, etc....)
drift, err := timesync.CheckSystemClockDrift()
if err != nil {
return err
}
log.Info("System clock synchronized with ntp. drift: %s", drift)
// ensure all data folders exist
filesystem.EnsureSpacemeshDataDirectories()
// load all accounts from store
accounts.LoadAllAccounts()
// todo: set coinbase account (and unlock it) based on flags
return nil
}
// Spacemesh app cleanup tasks.
func (app *SpacemeshApp) cleanup(ctx *cli.Context) error {
log.Info("App cleanup starting...")
if app.jsonAPIService != nil {
app.jsonAPIService.StopService()
}
if app.grpcAPIService != nil {
app.grpcAPIService.StopService()
}
// add any other cleanup tasks here....
log.Info("App cleanup completed\n\n")
return nil
}
func (app *SpacemeshApp) startSpacemeshNode(ctx *cli.Context) error {
log.Info("Starting local node...")
port := *nodeparams.LocalTCPPortFlag.Destination
address := fmt.Sprintf("0.0.0.0:%d", port)
// start a new node passing the app-wide node config values and persist it to store
// so future sessions use the same local node id
node, err := p2p.NewLocalNode(address, nodeparams.ConfigValues, true)
if err != nil {
return err
}
node.NotifyOnShutdown(ExitApp)
app.Node = node
app.NodeInitCallback <- true
conf := &apiconf.ConfigValues
// todo: if there's no loaded account - do the new account interactive flow here
// todo: if node has no loaded coin-base account then set the node coinbase to first account
// todo: if node has a locked coinbase account then prompt for account passphrase to unlock it
// todo: if node has no POS then start POS creation flow here unless user doesn't want to be a validator via cli
// todo: start node consensus protocol here only after we have an unlocked account
// start api servers
if conf.StartGrpcServer || conf.StartJSONServer {
// start grpc if specified or if json rpc specified
app.grpcAPIService = api.NewGrpcService()
app.grpcAPIService.StartService(nil)
}
if conf.StartJSONServer {
app.jsonAPIService = api.NewJSONHTTPServer()
app.jsonAPIService.StartService(nil)
}
log.Info("App started.")
// app blocks until it receives a signal to exit
// this signal may come from the node or from sig-abort (ctrl-c)
<-ExitApp
return nil
}
// Main is the entry point for the Spacemesh console app - responsible for parsing and routing cli flags and commands.
// This is the root of all evil, called from Main.main().
func Main(commit, branch, version string) {
// setup vars before creating the app - ugly but works
Version = version
Branch = branch
Commit = commit
App = newSpacemeshApp()
EntryPointCreated <- true
if err := App.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}