-
Notifications
You must be signed in to change notification settings - Fork 0
/
cli.go
266 lines (238 loc) · 6.89 KB
/
cli.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
package main
import (
"encoding/json"
"fmt"
"os"
"regexp"
cli "github.com/urfave/cli"
)
/*
Sample commands's flag to run with the specific configuration and database storage file:
Note: the configuration path can be provided after the flag `-c` or `--config`, `.exe`
to make the binaries file executable in Windows environment.
Normal:
.\pdpapp.exe -c node1 -n node1 start
Verbose:
.\pdpapp.exe --config node1 --node node1 start
Aliases:
.\pdpapp.exe -c node2 -n node2 ims
Sample command that will create wallet and store its contents to 'config.json' file:
Normal:
.\pdpapp.exe --wa node1 create-wallet
Verbose:
.\pdpapp.exe --wallet-addr node1 create-wallet
Alias:
.\pdpapp.exe --wa node1 cw
Sample command of creating new transaction: (eg: send from node2 -> node1)
Step 1: .\pdpapp.exe -c node2 -n node2 ims
Step 2: .\pdpapp.exe crtx -c node2 -n node2 --to localhost:3331 -v 1 -f test.txt
Step 3: Checking the `test.txt` file for more details.
*/
// newCLIApp create the new CLI application with some custom commands.
func newCLIApp() *cli.App {
app := cli.NewApp()
app.Name = "ImChain"
app.Usage = "Implementation Blockchain in GoLang"
// NOTE: global commands and flags.
app.Flags = []cli.Flag{}
app.Commands = []cli.Command{}
startServerCLI(app)
createWalletCLI(app)
createTransactionCLI(app)
createValidationPrfCLI(app)
return app
}
func createWalletCLI(app *cli.App) {
var cfgPath string
app.Commands = append(app.Commands, []cli.Command{
{
Name: "create-wallet",
Aliases: []string{"cw"},
Usage: "create new storable wallet address",
Action: func(ctx *cli.Context) error {
execCreateWallet(ctx, cfgPath)
return nil
},
},
}...)
app.Flags = append(app.Flags, []cli.Flag{
cli.StringFlag{
Name: "wallet-addr, wa",
Value: DEFAULT_CFG_PATH,
Usage: "Export Wallet's configuration to specific `FILE`",
Destination: &cfgPath,
},
}...)
}
// startServerCLI starts the blockchain server and connects to the network.
func startServerCLI(app *cli.App) {
var cfgPath, nodeDb string
app.Commands = append(app.Commands, []cli.Command{
{
Name: "start",
Aliases: []string{"ims"},
Usage: "start blockchain server",
Action: func(ctx *cli.Context) error {
execStartServer(ctx, cfgPath, nodeDb)
if len(ctx.GlobalFlagNames()) > 0 {
if ctx.String("c") != "" {
cfgPath = ctx.String("c")
} else if ctx.String("config") != "" {
cfgPath = ctx.String("config")
} else {
cfgPath = DEFAULT_CFG_PATH
}
}
return nil
},
},
}...)
app.Flags = append(app.Flags, []cli.Flag{
cli.StringFlag{
Name: "config, c",
Value: DEFAULT_CFG_PATH,
Usage: "Load configuration from specific `FILE`",
Destination: &cfgPath,
},
cli.StringFlag{
Name: "node, n",
Value: "",
Usage: "Load database storage from specified `NODE`",
Destination: &nodeDb,
},
}...)
}
func createTransactionCLI(app *cli.App) {
var cfgPath, nodeDb, toAddr, exportFile string
var totalVal int
app.Commands = append(app.Commands, []cli.Command{
{
Name: "create-tx",
Aliases: []string{"crtx"},
Usage: "crtx -c {cfgPath} -n {node} -to {address} -v {value} -f {exportFile}",
Action: func(ctx *cli.Context) error {
execCreateTx(ctx, totalVal, cfgPath, nodeDb, toAddr, exportFile)
return nil
},
Flags: []cli.Flag{
cli.StringFlag{
Name: "c",
// cfgPath[0]
Destination: &cfgPath,
},
cli.StringFlag{
Name: "n",
// cfgPath[1]
Destination: &nodeDb,
},
cli.IntFlag{
Name: "v",
Destination: &totalVal,
},
cli.StringFlag{
Name: "to",
// cfgPath[2]
Destination: &toAddr,
},
cli.StringFlag{
Name: "f",
// cfgPath[3]
Destination: &exportFile,
},
},
},
}...)
}
func createValidationPrfCLI(app *cli.App) {
var cfgPath string
app.Commands = append(app.Commands, []cli.Command{
{
Name: "validate",
Aliases: []string{"v"},
Usage: "",
Action: func(ctx *cli.Context) error {
execValidateBlock(ctx, cfgPath)
return nil
},
Flags: []cli.Flag{
cli.StringFlag{
Name: "n",
Destination: &cfgPath,
},
},
},
}...)
}
// execStartServer executes the specified commands from the terminal.
func execStartServer(ctx *cli.Context, cfgPath ...string) {
// `cfg[0]` = path to the configuration file.
// `cfg[1]` = path to the database storage file.
initNwCfg(cfgPath[0])
// If `DB_FILE` haven't existed, initialize an empty blockchain.
// Else, read this file to get the blockchain structure.
bc := getLocalBC(cfgPath[1])
if bc == nil {
Info.Printf("Local blockchain database not found. Initialize empty blockchain instead.")
bc = initBlockChain(cfgPath[1])
} else {
Info.Printf("Import blockchain database from local storage completed!")
}
syncNeighborBC(bc)
if bc == nil || bc.IsEmpty() {
Info.Printf("Pull failed, no available node for synchronization. Create new blockchain instead.\n")
firstTx := []Transaction{}
bc.AddBlock(newGenesisBlock(firstTx))
}
startBCServer(bc)
defer bc.DB.Close()
}
// execCreateWallet creates new a `Wallet` instance.
func execCreateWallet(ctx *cli.Context, cfgPath string) {
config := initNwCfg(cfgPath)
wallet := newWallet()
config.WJson = *wallet.ToJson()
config.ExportNetworkCfg(cfgPath)
fmt.Printf("New wallet is created successfully! Wallet is exported to : * %s *\n", cfgPath)
fmt.Printf("%s\n", config.WJson)
}
// @@@ FIXME: to be more cleaner!
func execCreateTx(ctx *cli.Context, val int, cfgPath ...string) {
// `cfg[0]` = path to the configuration file.
// `cfg[1]` = path to the database storage file.
// `cfg[2]` = the node's port address.
// `cfg[3]` = the path to the output file.
var sourceAddr string
re := regexp.MustCompile("[0-9]+")
sourceIdxAddr := re.Find([]byte(cfgPath[0]))
sourceAddr = fmt.Sprintf("localhost:333%d", Bytestoi(sourceIdxAddr))
Info.Printf("Execute transaction: send %d coins from %s to address %s", val, sourceAddr, cfgPath[2])
initNwCfg(cfgPath[0])
wallet := getWallet()
bc := getLocalBC(cfgPath[1])
if bc == nil {
Error.Print("Local blockchain not found. Need one existed first!")
os.Exit(1)
}
tx := bc.NewTx(wallet, cfgPath[2], val)
msgReq := createMsgReqAddTx(tx)
if isExist := checkFileExists(cfgPath[3]); isExist {
contents, _ := json.MarshalIndent(msgReq, "", " ")
appendFile(cfgPath[3], contents)
} else {
msgReq.Export(cfgPath[3])
}
defer bc.DB.Close()
}
// execCreateWallet creates new a `Wallet` instance.
func execValidateBlock(ctx *cli.Context, cfgPath string) {
bc := getLocalBC(cfgPath)
if bc == nil {
Info.Printf("Local blockchain database not found. Initialize empty blockchain instead.")
bc = initBlockChain(cfgPath)
} else {
Info.Printf("Import blockchain database from local storage completed!")
}
for idx := 1; idx <= bc.GetDepth(); idx++ {
checkBlockPrf(bc, idx)
}
}