-
Notifications
You must be signed in to change notification settings - Fork 18
/
config.go
565 lines (470 loc) · 18.6 KB
/
config.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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
package config
// This code reads command line arguments and config file
import (
"encoding/json"
"errors"
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/gelembjuk/oursql/lib/net"
"github.com/gelembjuk/oursql/lib/utils"
"github.com/gelembjuk/oursql/node/database"
)
const (
CommandImportWallet = "importwallet"
CommandExportWallet = "exportwallet"
CommandDumpBlockchain = "dumpblockchain"
CommandRestoreBlockchain = "restoreblockchain"
)
var commandsDoesNotNeedConfig = []string{
CommandImportWallet,
CommandExportWallet,
"createwallet",
"importblockchain",
"interactiveautocreate",
"listaddresses",
"help",
"restoreblockchain",
"importandstart"}
// Thi is the struct with all possible command line arguments
type AllPossibleArgs struct {
AppName string
Address string
From string
To string
Port int
Host string
NodePort int
NodeHost string
NodeAddress string
DefaultAddresses string
Genesis string
Amount float64
LogDest string
LogDestDefault bool // to know if logs destination was specified or not
Transaction string
View string
Clean bool
MySQLHost string
MySQLPort int
MySQLSocket string
MySQLUser string
MySQLPassword string
MySQLDBName string
DBTablesPrefix string
DumpFile string
DestinationFile string
SQL string
ConsensusFileToCopy string
FilePath string
AllowNonEmpty bool
Trace bool
}
// Input summary
type AppInput struct {
Command string
MinterAddress string
ProxyKey string
Logs string
Port int
LocalPort int
Host string
ConfigDir string
Nodes []net.NodeAddr
Args AllPossibleArgs
Database database.DatabaseConfig
DBProxyAddress string
ConseususConfigFile string
ConseususConfigFilePresent bool
}
type AppConfig struct {
Minter string
ProxyKey string
Port int
Host string
LocalPort int
Nodes []net.NodeAddr
Logs []string
LogsDestination string
Database database.DatabaseConfig
DBProxyAddress string
}
// Parses input and config file. Command line arguments ovverride config file options
func GetAppInput() (AppInput, error) {
return parseConfig("")
}
func GetAppInputFromDir(dirpath string) (AppInput, error) {
return parseConfig(dirpath)
}
// Parses input
func parseConfig(dirpath string) (AppInput, error) {
input := AppInput{}
if len(os.Args) < 2 {
input.Command = "help"
} else {
input.Command = os.Args[1]
cmd := flag.NewFlagSet(input.Command, flag.ExitOnError)
cmd.StringVar(&input.Args.AppName, "appname", "", "Application Name")
cmd.StringVar(&input.Args.Address, "address", "", "Address of operation")
cmd.StringVar(&input.Logs, "logs", "", "List of enabled logs groups")
cmd.StringVar(&input.MinterAddress, "minter", "", "Wallet address which signs blocks")
cmd.StringVar(&input.ProxyKey, "proxykey", "", "Wallet address which is used to sign SQL transactions in a proxy")
cmd.StringVar(&input.Args.Genesis, "genesis", "", "Genesis block text")
cmd.StringVar(&input.Args.Transaction, "transaction", "", "Transaction ID")
cmd.StringVar(&input.Args.From, "from", "", "Address to send money from")
cmd.StringVar(&input.Args.To, "to", "", "Address to send money to")
cmd.StringVar(&input.Args.Host, "host", "", "Node Server Host")
cmd.StringVar(&input.Args.NodeHost, "nodehost", "", "Remote Node Server Host")
cmd.IntVar(&input.Args.Port, "port", 0, "Node Server port")
cmd.IntVar(&input.LocalPort, "localport", 0, "Node Server local port to listen on it")
cmd.IntVar(&input.Args.NodePort, "nodeport", 0, "Remote Node Server port")
cmd.StringVar(&input.Args.NodeAddress, "nodeaddress", "", "Remote Node Server Address")
cmd.StringVar(&input.Args.DefaultAddresses, "defaultaddresses", "", "List of addresses to set as default for consensus config")
cmd.Float64Var(&input.Args.Amount, "amount", 0, "Amount money to send")
cmd.StringVar(&input.Args.LogDest, "logdest", "", "Destination of logs. file or stdout")
cmd.StringVar(&input.Args.View, "view", "", "View format")
cmd.BoolVar(&input.Args.Clean, "clean", false, "Clean data/cache")
cmd.BoolVar(&input.Args.Trace, "trace", false, "Trace process with printing to console")
cmd.BoolVar(&input.Args.AllowNonEmpty, "allownotempty", false, "Allow to init blockchain on non-empty DB")
cmd.StringVar(&input.Args.MySQLHost, "mysqlhost", "", "MySQL server host name")
cmd.IntVar(&input.Args.MySQLPort, "mysqlport", 3306, "MySQL server port")
cmd.StringVar(&input.Args.MySQLSocket, "mysqlsocket", "", "MySQL server unix socket")
cmd.StringVar(&input.Args.MySQLUser, "mysqluser", "", "MySQL user")
cmd.StringVar(&input.Args.MySQLPassword, "mysqlpass", "", "MySQL password")
cmd.StringVar(&input.Args.MySQLDBName, "mysqldb", "", "MySQL database")
cmd.StringVar(&input.Args.DBTablesPrefix, "tablesprefix", "", "MySQL blockchain tables prefix")
cmd.StringVar(&input.DBProxyAddress, "dbproxyaddr", "", "MySQL DB proxy address host:port")
cmd.StringVar(&input.Args.DumpFile, "dumpfile", "", "File where to dump DB")
cmd.StringVar(&input.Args.DestinationFile, "destfile", "", "Destination file for export")
cmd.StringVar(&input.Args.SQL, "sql", "", "SQL command to execute")
cmd.StringVar(&input.Args.ConsensusFileToCopy, "consensusfile", "", "Consensus file source")
cmd.StringVar(&input.Args.FilePath, "filepath", "", "File path")
configdirPtr := cmd.String("configdir", "", "Location of config files")
err := cmd.Parse(os.Args[2:])
if err != nil {
return input, err
}
if *configdirPtr != "" {
input.ConfigDir = *configdirPtr
}
}
if input.ConfigDir == "" && dirpath != "" {
input.ConfigDir = dirpath
}
if input.ConfigDir == "" {
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err == nil {
input.ConfigDir = dir + "/conf/"
}
}
if input.ConfigDir != "" {
if input.ConfigDir[len(input.ConfigDir)-1:] != "/" {
input.ConfigDir += "/"
}
}
if _, err := os.Stat(input.ConfigDir); os.IsNotExist(err) {
if !input.CommandNeedsConfig() {
os.Mkdir(input.ConfigDir, 0755)
} else {
return input, errors.New("Config directory is not found")
}
}
input.Port = input.Args.Port
input.Host = input.Args.Host
input.Args.LogDestDefault = true
// read config file . command line arguments are more important than a config
config, err := input.GetConfig()
if err != nil {
return input, err
}
if config != nil {
if input.MinterAddress == "" && config.Minter != "" {
input.MinterAddress = config.Minter
}
if input.ProxyKey == "" && config.ProxyKey != "" {
input.ProxyKey = config.ProxyKey
}
if input.Port < 1 && config.Port > 0 {
input.Port = config.Port
}
if input.LocalPort < 1 && config.LocalPort > 0 {
input.LocalPort = config.LocalPort
}
if input.Host == "" && config.Host != "" {
input.Host = config.Host
}
if len(config.Nodes) > 0 {
input.Nodes = config.Nodes
}
if input.Logs == "" && len(config.Logs) > 0 {
input.Logs = strings.Join(config.Logs, ",")
}
if input.Args.LogDest == "" && config.LogsDestination != "" {
input.Args.LogDest = config.LogsDestination
input.Args.LogDestDefault = false
} else if input.Args.LogDest == "" {
input.Args.LogDest = "file"
} else {
input.Args.LogDestDefault = false
}
if input.DBProxyAddress == "" && config.DBProxyAddress != "" {
input.DBProxyAddress = config.DBProxyAddress
}
input.Database = config.Database
}
if !(input.Args.NodeHost != "" && input.Args.NodePort > 0) &&
input.Args.NodeAddress != "" {
// get host and port from address.
na := net.NodeAddr{}
na.LoadFromString(input.Args.NodeAddress)
input.Args.NodeHost = na.Host
input.Args.NodePort = na.Port
}
input.completeDBConfig()
if !input.Database.HasMinimum() && input.CommandNeedsConfig() {
return input, errors.New("No database config")
}
if input.Host == "" {
input.Host = "localhost"
}
if input.LocalPort < 1 && input.Port > 0 {
input.LocalPort = input.Port
}
// set consensus config file
ccpath := input.ConfigDir + "consensusconfig.json"
if input.Args.ConsensusFileToCopy != "" &&
(input.Command == "interactiveautocreate" ||
input.Command == "importblockchain" ||
input.Command == "initblockchain" ||
input.Command == "importandstart") {
// if there is no consensus file yet, copy new file
// NOTE . This is dangerous operation. If to rpelace this file only in single
// node, it can be blocked by other of consensus is different
err := copyFile(input.Args.ConsensusFileToCopy, ccpath)
if err != nil {
return input, err
}
}
input.ConseususConfigFile = ccpath
if _, err := os.Stat(ccpath); os.IsNotExist(err) {
input.ConseususConfigFilePresent = false
} else {
input.ConseususConfigFilePresent = true
}
return input, nil
}
func (c *AppInput) completeDBConfig() {
if c.Database.DatabaseName == "" && c.Args.MySQLDBName != "" {
c.Database.DatabaseName = c.Args.MySQLDBName
}
if c.Database.MysqlHost == "" {
if c.Args.MySQLHost != "" {
c.Database.MysqlHost = c.Args.MySQLHost
} else {
c.Database.MysqlHost = "localhost"
}
}
if c.Database.MysqlPort == 0 {
if c.Args.MySQLPort > 0 {
c.Database.MysqlPort = c.Args.MySQLPort
} else {
c.Database.MysqlPort = 3306
}
}
if c.Database.MysqlSocket == "" && c.Args.MySQLSocket != "" {
c.Database.MysqlSocket = c.Args.MySQLSocket
}
if c.Database.DbUser == "" && c.Args.MySQLUser != "" {
c.Database.DbUser = c.Args.MySQLUser
}
if c.Database.DbPassword == "" && c.Args.MySQLPassword != "" {
c.Database.DbPassword = c.Args.MySQLPassword
}
if c.Database.TablesPrefix == "" && c.Args.DBTablesPrefix != "" {
c.Database.TablesPrefix = c.Args.DBTablesPrefix
}
}
// check if this commands really needs a config file
func (c AppInput) CommandNeedsConfig() bool {
if utils.StringInSlice(c.Command, commandsDoesNotNeedConfig) {
return false
}
return true
}
func (c AppInput) GetConfig() (*AppConfig, error) {
file, errf := os.Open(c.ConfigDir + "config.json")
if errf != nil && !os.IsNotExist(errf) {
// error is bad only if file exists but we can not open to read
return nil, errf
}
if errf != nil {
// config file not found
return nil, nil
}
config := AppConfig{}
// we open a file only if it exists. in other case options can be set with command line
decoder := json.NewDecoder(file)
err := decoder.Decode(&config)
if err != nil {
return nil, err
}
return &config, nil
}
func (c AppInput) CheckNeedsHelp() bool {
if c.Command == "help" || c.Command == "" {
return true
}
return false
}
func (c AppInput) CheckConfigUpdateNeeded() bool {
if c.Command == "updateconfig" {
return true
}
return false
}
func (c AppInput) UpdateConfig() error {
config, err := c.GetConfig()
if err != nil {
return err
}
if config == nil {
config = &AppConfig{}
}
configfile := c.ConfigDir + "config.json"
if c.MinterAddress != "" {
config.Minter = c.MinterAddress
}
if c.ProxyKey != "" {
config.ProxyKey = c.ProxyKey
}
if c.Host != "" {
config.Host = c.Host
}
if c.Port > 0 {
config.Port = c.Port
}
if c.DBProxyAddress != "" {
config.DBProxyAddress = c.DBProxyAddress
}
if c.Args.NodeHost != "" && c.Args.NodePort > 0 {
node := net.NewNodeAddr(c.Args.NodeHost, c.Args.NodePort)
used := false
for _, n := range config.Nodes {
if node.CompareToAddress(n) {
used = true
break
}
}
if !used {
config.Nodes = append(config.Nodes, node)
}
}
if config.Nodes == nil {
config.Nodes = []net.NodeAddr{}
}
if c.Logs != "" {
if c.Logs == "no" {
config.Logs = []string{}
} else {
config.Logs = strings.Split(c.Logs, ",")
}
}
if c.Args.LogDest != "" && !c.Args.LogDestDefault {
config.LogsDestination = c.Args.LogDest
}
if config.Logs == nil {
config.Logs = []string{}
}
// DB setings
if c.Args.MySQLHost != "" {
config.Database.MysqlHost = c.Args.MySQLHost
}
if c.Args.MySQLSocket != "" {
config.Database.MysqlSocket = c.Args.MySQLSocket
}
if c.Args.MySQLPort > 0 {
config.Database.MysqlPort = c.Args.MySQLPort
}
if c.Args.MySQLUser != "" {
config.Database.DbUser = c.Args.MySQLUser
}
if c.Args.MySQLPassword != "" {
config.Database.DbPassword = c.Args.MySQLPassword
}
if c.Args.MySQLDBName != "" {
config.Database.DatabaseName = c.Args.MySQLDBName
}
if c.Args.DBTablesPrefix != "" {
config.Database.TablesPrefix = c.Args.DBTablesPrefix
}
// convert back to JSON and save to config file
file, errf := os.OpenFile(configfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if errf != nil {
return errf
}
encoder := json.NewEncoder(file)
err = encoder.Encode(&config)
if err != nil {
return err
}
file.Close()
return nil
}
func copyFile(srcpath, dstpath string) error {
input, err := ioutil.ReadFile(srcpath)
if err != nil {
return err
}
err = ioutil.WriteFile(dstpath, input, 0644)
if err != nil {
return err
}
return nil
}
func (c AppInput) PrintUsage() {
fmt.Println("Usage:")
fmt.Println(" help - Prints this help")
fmt.Println(" == Any of next commands can have optional argument [-configdir /path/to/dir] [-logdest stdout]==")
fmt.Println("=[Auth keys operations]")
fmt.Println(" createwallet\n\t- Generates a new key-pair and saves it into the wallet file")
fmt.Println(" ", CommandImportWallet, " -filepath FILEPATH\n\t- Imports wallets from external wallets file.")
fmt.Println(" ", CommandExportWallet, " -filepath FILEPATH\n\t- Exports wallets file to given destination. Can be used for backup of wallets")
fmt.Println(" listaddresses\n\t- Lists all addresses from the wallet file")
fmt.Println("=[Blockchain init operations]")
//fmt.Println(" interactiveautocreate [-consensusfile FILEPATH] [-mysqlhost HOST] [-mysqlport PORT] [-mysqluser USER] [-mysqlpass PASSWORD] [-mysqldb DBNAME] [-tablesprefix PREFIX]\n\t- Create a blockchain if it doesn't exist yet, creates a wallet if no wallets yet, starts a node in interactive mode.")
//fmt.Println(" importandstart [-nodeaddress HOST:PORT] [-consensusfile FILEPATH] [-mysqlhost HOST] [-mysqlport PORT] [-mysqluser USER] [-mysqlpass PASSWORD] [-mysqldb DBNAME] [-tablesprefix PREFIX]\n\t- Loads a blockchain from other node to init the DB. Cretes a wallet of no wallets, starts a node in interactive mode.")
//fmt.Println(" pullupdates \n\t- Pulls recent updates from other nodes in a network.")
fmt.Println(" initblockchain [-minter ADDRESS] [-consensusfile FILEPATH] [-allownotempty] [-trace] [-mysqlhost HOST] [-mysqlport PORT] [-mysqluser USER] [-mysqlpass PASSWORD] [-mysqldb DBNAME] [-tablesprefix PREFIX]\n\t- Create a blockchain and send genesis block reward to ADDRESS")
fmt.Println(" importblockchain [-consensusfile FILEPATH] [-nodeaddress HOST:PORT] [-mysqlhost HOST] [-mysqlport PORT] [-mysqluser USER] [-mysqlpass PASSWORD] [-mysqldb DBNAME] [-tablesprefix PREFIX]\n\t- Loads a blockchain from other node to init the DB. If consensusfile is set and it contains initial node address, it will be used")
fmt.Println(" restoreblockchain -dumpfile FILEPATH [-mysqlhost HOST] [-mysqlport PORT] [-mysqluser USER] [-mysqlpass PASSWORD] [-mysqldb DBNAME] [-tablesprefix PREFIX]\n\t- Loads a blockchain from dump file and restores it to given DB. A DB credentials can be optional if they are present in config file")
fmt.Println(" dumpblockchain -dumpfile FILEPATH\n\t- Dump blockchain DB to a file. This fle can be used to restore a BC")
fmt.Println(" exportconsensusconfig -destfile FILEPATH [-defaultaddresses own,host:port] [-appname NAME]\n\t- Save consensus config file. Can include this node address as initial address.")
fmt.Println(" updateconfig [-minter ADDRESS] [-proxykey ADDRESS] [-host HOST] [-port PORT] [-nodehost HOST] [-nodeport PORT] [-mysqlhost HOST] [-mysqlport PORT] [-mysqluser USER] [-mysqlpass PASSWORD] [-mysqldb DBNAME] [-tablesprefix PREFIX] [-dbproxyaddr ADDR]\n\t- Update config file. Allows to set this node minter address, host and port and remote node host and port")
fmt.Println("=[Blockchain manage operations]")
fmt.Println(" printchain [-view short|long]\n\t- Print all the blocks of the blockchain. Default view is long")
fmt.Println(" makeblock [-minter ADDRESS]\n\t- Try to mine new block if there are enough transactions")
fmt.Println(" dropblock\n\t- Delete last block fro the block chain. All transaction are returned back to unapproved state")
fmt.Println("=[SQL operations]")
fmt.Println(" sql -from FROM -sql SQLCOMMAND\n\t- Execute SQL query signed by FROM address")
fmt.Println("=[Currency transactions and control operations]")
fmt.Println(" reindexcache\n\t- Rebuilds the database of unspent transactions outputs and transaction pointers")
fmt.Println(" showunspent -address ADDRESS\n\t- Print the list of all unspent transactions and balance")
fmt.Println(" getbalance -address ADDRESS\n\t- Get balance of ADDRESS")
fmt.Println(" getbalances\n\t- Lists all addresses from the wallet file and show balance for each")
fmt.Println(" addrhistory -address ADDRESS\n\t- Shows all transactions for a wallet address")
fmt.Println(" send -from FROM -to TO -amount AMOUNT\n\t- Send AMOUNT of coins from FROM address to TO. ")
fmt.Println("=[Transactions]")
fmt.Println(" canceltransaction -transaction TRANSACTIONID\n\t- Cancel unapproved transaction. NOTE!. This cancels only from local cache!")
fmt.Println(" unapprovedtransactions [-clean]\n\t- Print the list of transactions not included in any block yet. If the option -clean provided then cleans the cache")
fmt.Println("=[Node server operations]")
fmt.Println(" startnode [-minter ADDRESS] [-host HOST] [-port PORT] [-proxykey ADDRESS] [-dbproxyaddr ADDR]\n\t- Start a node server. -minter defines minting address, -host - hostname of the node server , -port - listening port, -dbproxyaddr mysql proxy listening address `host:port`")
fmt.Println(" startintnode [-minter ADDRESS] [-port PORT] [-proxykey ADDRESS] [-dbproxyaddr ADDR]\n\t- Start a node server in interactive mode (no deamon). -minter defines minting address and -port - listening port")
fmt.Println(" stopnode\n\t- Stop runnning node")
fmt.Println(" nodestate\n\t- Print state of the node process")
fmt.Println(" shownodes\n\t- Display list of nodes addresses, including inactive")
fmt.Println(" addnode -nodehost HOST -nodeport PORT\n\t- Adds new node to list of connections")
fmt.Println(" removenode -nodehost HOST -nodeport PORT\n\t- Removes a node from list of connections")
}