· 5 years ago · May 20, 2020, 12:44 AM
1package main
2
3import (
4 "bufio"
5 "bytes"
6 "crypto/sha256"
7 "encoding/binary"
8 "encoding/hex"
9 "encoding/json"
10 "flag"
11 "fmt"
12 "io/ioutil"
13 "net"
14 "net/http"
15 "os"
16 "path/filepath"
17 "regexp"
18 "strconv"
19 "strings"
20 "time"
21
22 "github.com/fatih/color"
23 externalip "github.com/glendc/go-external-ip"
24 "github.com/gorilla/mux"
25 shell "github.com/ipfs/go-ipfs-api"
26 "github.com/sirupsen/logrus"
27 rashedCrypto "github.com/turtlecoin/go-turtlecoin/crypto"
28 rashedMnemonic "github.com/turtlecoin/go-turtlecoin/walletbackend/mnemonics"
29)
30
31// Attribution constants
32const appName = "go-karai"
33const appDev = "The TurtleCoin Developers"
34const appDescription = appName + " - Karai Transaction Channels"
35const appLicense = "https://choosealicense.com/licenses/mit/"
36const appRepository = "https://github.com/karai/go-karai"
37const appURL = "https://karai.io"
38
39// File & folder constants
40const credentialsFile = "private_credentials.karai"
41const currentJSON = "./config/milestone.json"
42const graphDir = "./graph"
43const hashDat = graphDir + "/ipfs-hash-list.dat"
44const p2pConfigDir = "./config/p2p"
45const configPeerIDFile = p2pConfigDir + "/peer.id"
46
47// Coordinator values
48var isCoordinator bool = false
49var karaiPort int
50var p2pPeerID string
51
52// Client Header
53var clientHeaderAppName string = appName
54var clientHeaderAppVersion string = semverInfo()
55var clientHeaderPeerID string
56
57// Version string
58func semverInfo() string {
59 var majorSemver, minorSemver, patchSemver, wholeString string
60 majorSemver = "0"
61 minorSemver = "5"
62 patchSemver = "3"
63 wholeString = majorSemver + "." + minorSemver + "." + patchSemver
64 return wholeString
65}
66
67// Graph This is the structure of the Graph
68type Graph struct {
69 Transactions []*GraphTx `json:"graph_transactions"`
70}
71
72// GraphTx This is the structure of the transaction
73type GraphTx struct {
74 Type int `json:"tx_type"`
75 Hash []byte `json:"tx_hash"`
76 Data []byte `json:"tx_data"`
77 Prev []byte `json:"tx_prev"`
78}
79
80func parseFlags() {
81 flag.IntVar(&karaiPort, "port", 4200, "Port to run Karai Coordinator on.")
82 flag.BoolVar(&isCoordinator, "coordinator", false, "Run as coordinator.")
83 // flag.StringVar(&karaiPort, "karaiPort", "4200", "Port to run Karai")
84 flag.Parse()
85}
86
87func announce() {
88 if isCoordinator {
89 logrus.Info("Coordinator: ", isCoordinator)
90 revealIP()
91
92 logrus.Info("Running on port: ", karaiPort)
93 } else {
94 logrus.Debug("launching as normal user on port: ", karaiPort)
95 }
96}
97
98// Hello Karai
99func main() {
100 parseFlags()
101 announce()
102 clearPeerID(configPeerIDFile)
103 locateGraphDir()
104 checkCreds()
105 ascii()
106 if !isCoordinator {
107 logrus.Debug("isCoordinator == false, skipping webserver deployment")
108 } else {
109 go restAPI()
110 }
111 inputHandler()
112}
113
114func restAPI() {
115 r := mux.NewRouter()
116 api := r.PathPrefix("/api/v1").Subrouter()
117 api.HandleFunc("/", home).Methods(http.MethodGet)
118 api.HandleFunc("/peer", returnPeerID).Methods(http.MethodGet)
119 api.HandleFunc("/version", returnVersion).Methods(http.MethodGet)
120 api.HandleFunc("/transactions", returnTransactions).Methods(http.MethodGet)
121 api.HandleFunc("/transaction/send", sendTransaction).Methods(http.MethodPost)
122 logrus.Error(http.ListenAndServe(":"+strconv.Itoa(karaiPort), r))
123}
124
125func sendTransaction(w http.ResponseWriter, r *http.Request) {
126
127}
128
129func revealIP() string {
130 // consensus := externalip.
131 consensus := externalip.DefaultConsensus(nil, nil)
132 ip, err := consensus.ExternalIP()
133 handle("Something went wrong getting the external IP: ", err)
134 logrus.Info("External IP: ", ip.String())
135 return ip.String()
136}
137
138func notFound(w http.ResponseWriter, r *http.Request) {
139 w.Header().Set("Content-Type", "application/json")
140 w.WriteHeader(http.StatusNotFound)
141 w.Write([]byte(`{"bruh": "lol"}`))
142}
143
144func home(w http.ResponseWriter, r *http.Request) {
145 w.Header().Set("Content-Type", "application/json")
146 w.WriteHeader(http.StatusOK)
147
148 w.Write([]byte("Hello " + appName + " v" + semverInfo()))
149}
150
151func returnPeerID(w http.ResponseWriter, r *http.Request) {
152 w.Header().Set("Content-Type", "application/json")
153 w.WriteHeader(http.StatusOK)
154
155 peerFile, err := os.OpenFile(configPeerIDFile,
156 os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
157 handle("Can't find peer.id file: ", err)
158 defer peerFile.Close()
159
160 fileToRead, err := ioutil.ReadFile(configPeerIDFile)
161 // fmt.Println(fileToRead)
162 handle("Error: ", err)
163 w.Write([]byte("{\"p2p_peer_ID\": \"" + string(fileToRead) + "\"}"))
164
165}
166
167func returnVersion(w http.ResponseWriter, r *http.Request) {
168 w.Header().Set("Content-Type", "application/json")
169 w.WriteHeader(http.StatusOK)
170 w.Write([]byte("{\"karai_version\": \"" + semverInfo() + "\"}"))
171}
172
173func returnTransactions(w http.ResponseWriter, r *http.Request) {
174 w.Header().Set("Content-Type", "application/json")
175 w.WriteHeader(http.StatusOK)
176 matches, _ := filepath.Glob(graphDir + "/*.json")
177 w.Write([]byte("[\n\t"))
178 for _, match := range matches {
179 w.Write([]byte(printTx(match)))
180 }
181 w.Write([]byte("{}"))
182 w.Write([]byte("\n]"))
183}
184
185// Splash logo
186func ascii() {
187 fmt.Printf("\n")
188 color.Set(color.FgGreen, color.Bold)
189 fmt.Printf("| _ _ _ .\n")
190 fmt.Printf("|( (_| | (_| |\n")
191}
192
193// checkCreds locate or create Karai credentials
194func checkCreds() {
195 if _, err := os.Stat(credentialsFile); err == nil {
196 logrus.Debug("Karai Credentials Found!")
197 } else {
198 logrus.Debug("No Credentials Found! Generating Credentials...")
199 generateEd25519()
200 }
201}
202
203// generateEd25519 use TRTL Crypto to generate credentials
204func generateEd25519() {
205 logrus.Debug("Generating credentials")
206 priv, pub, err := rashedCrypto.GenerateKeys()
207 seed := rashedMnemonic.PrivateKeyToMnemonic(priv)
208 timeUnixNow := strconv.FormatInt(time.Now().Unix(), 10)
209 // TODO: Replace manually entered JSON
210 logrus.Debug("Writing credentials to file")
211 writeFile := []byte("{\n\t\"date_generated\": " + timeUnixNow + ",\n\t\"key_priv\": \"" + hex.EncodeToString(priv[:]) + "\",\n\t\"key_pub\": \"" + hex.EncodeToString(pub[:]) + "\",\n\t\"seed\": \"" + seed + "\"\n}")
212 logrus.Debug("Writing main file")
213 errWriteFile := ioutil.WriteFile("./"+credentialsFile, writeFile, 0644)
214 logrus.Debug(errWriteFile)
215 handle("Error writing file: ", err)
216 logrus.Debug("Writing backup credential file")
217 errWriteBackupFile := ioutil.WriteFile("./."+credentialsFile+"."+timeUnixNow+".backup", writeFile, 0644)
218 handle("Error writing file backup: ", err)
219 logrus.Debug(errWriteBackupFile)
220}
221
222// hashTx This will compute the tx hash using sha256
223func (graphTx *GraphTx) hashTx() {
224 // logrus.Debug("Hashing a Tx ", graphTx.Hash)
225 data := bytes.Join([][]byte{graphTx.Data, graphTx.Prev}, []byte{})
226 hash := sha256.Sum256(data)
227 graphTx.Hash = hash[:]
228}
229
230// addTx This will add a transaction to the graph
231func (graph *Graph) addTx(txType int, data string) {
232 if !isCoordinator {
233 fmt.Println("It looks like you're not a channel coordinator. \n Run Karai with '-coordinator' option to run this command.")
234 } else {
235 logrus.Debug("Adding a Tx")
236 prevTx := graph.Transactions[len(graph.Transactions)-1]
237 new := txConstructor(txType, data, prevTx.Hash)
238 graph.Transactions = append(graph.Transactions, new)
239 }
240}
241
242// // printGraph This will add a transaction to the graph
243// func printGraph(directory string) {
244// jsonFile, err := os.Open(graphDir + "/" + "Tx_1.json")
245// handle("derp we cant open this JSON: ", err)
246// fmt.Println("Successfully Opened: " + graphDir)
247// defer jsonFile.Close()
248// byteValue, _ := ioutil.ReadAll(jsonFile)
249// var result map[string]interface{}
250// json.Unmarshal([]byte(byteValue), &result)
251// fmt.Println(result["graph"])
252// }
253
254// printGraph This will add a transaction to the graph
255func printGraph(directory string) {
256 jsonFile, err := os.Open(directory + "/" + "Tx_1.json")
257 handle("Derp we can't open this JSON: ", err)
258 byteValue, _ := ioutil.ReadAll(jsonFile)
259 var Graph Graph
260 json.Unmarshal(byteValue, &Graph)
261 for i := 0; i < 20; i++ {
262 fmt.Println("\nhere we go")
263 fmt.Println(Graph.Transactions[i].Hash)
264 fmt.Println(Graph.Transactions[i].Prev)
265 fmt.Println(Graph.Transactions[i].Type)
266 fmt.Println(Graph.Transactions[i].Data)
267 }
268 defer jsonFile.Close()
269}
270
271func createCID() {
272 start := time.Now()
273 matches, _ := filepath.Glob(graphDir + "/*.json")
274 for _, match := range matches {
275 pushTx(match)
276 }
277 end := time.Since(start)
278 fmt.Println("Finished in: ", end)
279}
280
281func pushTx(file string) string {
282 dat, _ := ioutil.ReadFile(file)
283 color.Set(color.FgBlack, color.Bold)
284 fmt.Print(string(dat) + "\n")
285 sh := shell.NewShell("localhost:5001")
286 cid, err := sh.Add(strings.NewReader(string(dat)))
287 handle("Something went wrong pushing the tx: ", err)
288 fmt.Printf(color.GreenString("%v %v\n%v %v", color.YellowString("Tx:"), color.GreenString(file), color.YellowString("CID: "), color.GreenString(cid)))
289 appendGraphCID(cid)
290 return cid
291}
292
293func printTx(file string) string {
294 dat, err := ioutil.ReadFile(file)
295 handle("derp, something went wrong", err)
296 datString := string(dat) + ",\n"
297 return datString
298}
299
300func appendGraphCID(cid string) {
301 if !isCoordinator {
302 fmt.Println("It looks like you're not a channel coordinator. \n Run Karai with '-coordinator' option to run this command.")
303 } else {
304 hashfile, err := os.OpenFile(hashDat,
305 os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
306 handle("Something went wrong appending the graph CID: ", err)
307 defer hashfile.Close()
308 if isExist(cid, hashDat) {
309 fmt.Printf("%v", color.RedString("\nDuplicate! Skipping...\n"))
310 } else {
311 hashfile.WriteString(cid + "\n")
312 }
313 }
314}
315
316func isExist(str, filepath string) bool {
317 accused, _ := ioutil.ReadFile(filepath)
318 isExist, _ := regexp.Match(str, accused)
319 return isExist
320}
321
322// addMilestone This will add a milestone to the graph
323func (graph *Graph) addMilestone(data string) {
324 if !isCoordinator {
325 fmt.Println("It looks like you're not a channel coordinator. \n Run Karai with '-coordinator' option to run this command.")
326 } else {
327 prevTransaction := graph.Transactions[len(graph.Transactions)-1]
328 // paramFile, _ = os.Open("./config/milestone.json")
329 new := txConstructor(1, data, prevTransaction.Hash)
330 graph.Transactions = append(graph.Transactions, new)
331 }
332}
333
334// txConstructor This will construct a tx
335func txConstructor(txType int, data string, prevHash []byte) *GraphTx {
336 transaction := &GraphTx{txType, []byte{}, []byte(data), prevHash}
337 transaction.hashTx()
338 return transaction
339
340}
341
342// rootTx Transaction channels start with a rootTx transaction always
343func rootTx() *GraphTx {
344 fmt.Printf("Coordinator status: %t", isCoordinator)
345 return txConstructor(0, "Karai Transaction Channel - Root", []byte{})
346}
347
348// spawnGraph starts a new transaction channel with Root Tx
349func spawnGraph() *Graph {
350 return &Graph{[]*GraphTx{rootTx()}}
351}
352
353// v4ToHex convert an ip4 to hex
354func v4ToHex(addr string) string {
355 ip := net.ParseIP(addr).To4()
356 buffer := new(bytes.Buffer)
357 for _, s := range ip {
358 binary.Write(buffer, binary.BigEndian, uint8(s))
359 }
360 var dec uint32
361 binary.Read(buffer, binary.BigEndian, &dec)
362 return fmt.Sprintf("%08x", dec)
363}
364
365// portToHex convert a port to hex
366func portToHex(port string) string {
367 portNum, _ := strconv.ParseUint(port, 10, 16)
368 return fmt.Sprintf("%04x", portNum)
369}
370
371// generatePointer create the TRTL <=> Karai pointer
372func generatePointer() {
373 if !isCoordinator {
374 fmt.Println("It looks like you're not a channel coordinator. \n Run Karai with '-coordinator' option to run this command.")
375 } else {
376 logrus.Debug("Creating a new Karai <=> TRTL pointer")
377 readerKtxIP := bufio.NewReader(os.Stdin)
378 fmt.Print("Enter Karai Coordinator IP: ")
379 ktxIP, _ := readerKtxIP.ReadString('\n')
380 readerKtxPort := bufio.NewReader(os.Stdin)
381 fmt.Print("Enter Karai Coordinator Port: ")
382 ktxPort, _ := readerKtxPort.ReadString('\n')
383 ip := v4ToHex(strings.TrimRight(ktxIP, "\n"))
384 port := portToHex(strings.TrimRight(ktxPort, "\n"))
385 fmt.Printf("\nGenerating pointer for %s:%s\n", strings.TrimRight(ktxIP, "\n"), ktxPort)
386 fmt.Println("Your pointer is: ")
387 fmt.Printf("Hex:\t6b747828%s%s29", ip, port)
388 fmt.Println("\nAscii:\tktx(" + strings.TrimRight(ktxIP, "\n") + ":" + strings.TrimRight(ktxPort, "\n") + ")")
389 }
390}
391
392// loadMilestoneJSON Read pending milestone Tx JSON
393func loadMilestoneJSON() string {
394 // TODO: Check if milestone is ready first, avoid re-use
395 dat, _ := ioutil.ReadFile(currentJSON)
396 datMilestone := string(dat)
397 return datMilestone
398 // Kek
399}
400
401func validateKTX(channel string) bool {
402 // validate the ktx string with regex
403 // if it is valid, return bool true
404 return true
405}
406
407func clearPeerID(file string) {
408 err := os.Remove(file)
409 logrus.Debug(err)
410}
411
412func sendClientHeader(name, version, id, channel string) bool {
413 // var clientHeaderAppName string = appName
414 // var clientHeaderAppVersion string = semverInfo()
415 // var clientHeaderPeerID string
416 return true
417}
418
419func (graphTx *GraphTx) generalHash(response string) [32]byte {
420 hashedData := bytes.Join([][]byte{graphTx.Data, graphTx.Prev}, []byte{})
421 hash := sha256.Sum256(hashedData)
422 return hash
423}
424
425// func connectToChannel(channel string) {
426// if validateKTX(channel) {
427// if validateCoordVersion(channel) {
428// //send client header to coord
429// if sendClientHeader(clientHeaderAppName, clientHeaderAppVersion, clientHeaderPeerID, channel) {
430// //coord should respond with most recent milestone
431// //hash the milestone
432// if generalHash(res.Body) == milestone.Hash {
433// sendClientMilestoneHash(channel)
434// }
435// //send the hash to coord
436// //coord approves
437// //send join tx
438// //listen for events
439// } else if sendClientHeader(clientHeaderAppName, clientHeaderAppVersion, clientHeaderPeerID, channel) {
440// logrus.Error("Problem constructing or sending client header.")
441// }
442// } else if !validateCoordVersion(channel) {
443// logrus.Error("Coordinator Version Not Accepted")
444// }
445// } else if !validateKTX(channel) {
446// logrus.Error("KTX Invalid")
447// }
448// }
449
450// func coordVersionHandler(w http.ResponseWriter, r *http.Request) {
451// return
452// }
453
454// func validateCoordVersion(channel string) bool {
455// logrus.Info("fetching coordinator version for ", channel)
456// req, err := http.NewRequest("GET", channel, nil)
457// handle("Error getting coord info: ", err)
458// client := &http.Client{Timeout: time.Second * 10}
459// resp, err := client.Do(req)
460// handle("Error getting coord info: ", err)
461// defer resp.Body.Close()
462// body, err := ioutil.ReadAll(resp.Body)
463// logrus.Debug(body)
464// handle("Error getting coord info: ", err)
465// return true
466// }
467
468// spawnChannel Create a Tx Channel, Root Tx and Milestone, listen for Tx
469func spawnChannel() {
470 if !isCoordinator {
471 fmt.Println("It looks like you're not a channel coordinator. \n Run Karai with '-coordinator' option to run this command.")
472 } else {
473 // Generate Root Tx
474 graph := spawnGraph()
475 // Add the current milestone.json in config
476 graph.addMilestone(loadMilestoneJSON())
477 graph.addTx(2, "{\"tx_slot\": 3}")
478 // go txHandler()
479 // Report Txs
480 fmt.Printf("\n\nTx Legend: %v %v %v\n", color.YellowString("Root"), color.GreenString("Milestone"), color.BlueString("Normal"))
481 for key, transaction := range graph.Transactions {
482 var hash string = fmt.Sprintf("%x", transaction.Hash)
483 var prevHash string = fmt.Sprintf("%x", transaction.Prev)
484 // Root Tx will not have a previous hash
485 if prevHash == "" {
486 dataString := "{\n\t\"tx_type\": " + strconv.Itoa(transaction.Type) + ",\n\t\"tx_hash\": \"" + hash + "\",\n\t\"tx_data\": \"" + string(transaction.Data) + "\"\n}"
487 f, _ := os.Create(graphDir + "/" + "Tx_" + strconv.Itoa(key) + ".json")
488 w := bufio.NewWriter(f)
489 w.WriteString(dataString)
490 w.Flush()
491 // fmt.Printf("\nTx(%x) %x\n", key, transaction.Hash)
492 fmt.Printf("\nTx(%v) %x\n", color.YellowString(strconv.Itoa(key)), transaction.Hash)
493 } else if len(prevHash) > 2 {
494 dataString := "{\n\t\"tx_type\": " + strconv.Itoa(transaction.Type) + ",\n\t\"tx_hash\": \"" + hash + "\",\n\t\"tx_prev\": \"" + prevHash + "\",\n\t\"tx_data\": " + string(transaction.Data) + "\n}"
495 f, _ := os.Create(graphDir + "/" + "Tx_" + strconv.Itoa(key) + ".json")
496 w := bufio.NewWriter(f)
497 w.WriteString(dataString)
498 w.Flush()
499 // Indicate Tx type by color
500 if transaction.Type == 0 {
501 // Root Tx
502 fmt.Printf("Tx(%v) %x\n", color.YellowString(strconv.Itoa(key)), transaction.Hash)
503 } else if transaction.Type == 1 {
504 // Milestone Tx
505 fmt.Printf("Tx(%v) %x\n", color.GreenString(strconv.Itoa(key)), transaction.Hash)
506 } else if transaction.Type == 2 {
507 // Normal Tx
508 fmt.Printf("Tx(%v) %x\n", color.BlueString(strconv.Itoa(key)), transaction.Hash)
509 }
510 }
511 }
512 fmt.Println()
513 }
514}
515
516// benchmark Add a number of transactions and time the execution
517func benchmark() {
518 if !isCoordinator {
519 fmt.Println("It looks like you're not a channel coordinator. \n Run Karai with '-coordinator' option to run this command.")
520
521 } else {
522 benchTxCount := 1000000
523 graph := spawnGraph()
524 graph.addMilestone(loadMilestoneJSON())
525 count := 0
526 ascii()
527 fmt.Printf("Benchmark: %d transactions\n", benchTxCount)
528 fmt.Println("Starting in 5 seconds. Press CTRL C to interrupt.")
529 time.Sleep(5 * time.Second)
530 start := time.Now()
531 for i := 1; i < benchTxCount; i++ {
532 count += i
533 dataString := "{\"tx_slot\": " + strconv.Itoa(i+1) + "}"
534 graph.addTx(2, dataString)
535 }
536 end := time.Since(start)
537 fmt.Printf("\n\nTx Legend: %v %v %v\n", color.YellowString("Root"), color.GreenString("Milestone"), color.BlueString("Normal"))
538 for key, transaction := range graph.Transactions {
539 var hash string = fmt.Sprintf("%x", transaction.Hash)
540 var prevHash string = fmt.Sprintf("%x", transaction.Prev)
541 // Root Tx will not have a previous hash
542 if prevHash == "" {
543 dataString := "{\n\t\"tx_type\": " + strconv.Itoa(transaction.Type) + ",\n\t\"tx_hash\": \"" + hash + "\",\n\t\"tx_data\": \"" + string(transaction.Data) + "\"\n}"
544 f, _ := os.Create(graphDir + "/" + "Tx_" + strconv.Itoa(key) + ".json")
545 w := bufio.NewWriter(f)
546 w.WriteString(dataString)
547 w.Flush()
548 // fmt.Printf("\nTx(%x) %x\n", key, transaction.Hash)
549 fmt.Printf("\nTx(%v) %x\n", color.YellowString(strconv.Itoa(key)), transaction.Hash)
550 } else if len(prevHash) > 2 {
551 dataString := "{\n\t\"tx_type\": " + strconv.Itoa(transaction.Type) + ",\n\t\"tx_hash\": \"" + hash + "\",\n\t\"tx_prev\": \"" + prevHash + "\",\n\t\"tx_data\": " + string(transaction.Data) + "\n}"
552 f, _ := os.Create(graphDir + "/" + "Tx_" + strconv.Itoa(key) + ".json")
553 w := bufio.NewWriter(f)
554 w.WriteString(dataString)
555 w.Flush()
556 // Indicate Tx type by color
557 if transaction.Type == 0 {
558 // Root Tx
559 fmt.Printf("Tx(%v) %x\n", color.YellowString(strconv.Itoa(key)), transaction.Hash)
560 } else if transaction.Type == 1 {
561 // Milestone Tx
562 fmt.Printf("Tx(%v) %x\n", color.GreenString(strconv.Itoa(key)), transaction.Hash)
563 } else if transaction.Type == 2 {
564 // Normal Tx
565 fmt.Printf("Tx(%v) %x\n", color.BlueString(strconv.Itoa(key)), transaction.Hash)
566 }
567 }
568 }
569 fmt.Println()
570 fmt.Printf("%d Transactions in %s", benchTxCount, end)
571 }
572}
573
574// locateGraphDir find graph storage, create if missing.
575func locateGraphDir() {
576 if _, err := os.Stat(graphDir); os.IsNotExist(err) {
577 logrus.Debug("Graph directory does not exist.")
578 err = os.MkdirAll("./graph", 0755)
579 handle("Error locating graph directory: ", err)
580 }
581}
582
583// inputHandler present menu, accept user input
584func inputHandler() {
585 reader := bufio.NewReader(os.Stdin)
586 for {
587 fmt.Printf("\n%v%v%v\n", color.WhiteString("Type '"), color.GreenString("menu"), color.WhiteString("' to view a list of commands"))
588 fmt.Print(color.GreenString("-> "))
589 text, _ := reader.ReadString('\n')
590 text = strings.Replace(text, "\n", "", -1)
591 if strings.Compare("help", text) == 0 {
592 menu()
593 } else if strings.Compare("?", text) == 0 {
594 menu()
595 } else if strings.Compare("menu", text) == 0 {
596 menu()
597 } else if strings.Compare("version", text) == 0 {
598 logrus.Debug("Displaying version")
599 menuVersion()
600 } else if strings.Compare("license", text) == 0 {
601 logrus.Debug("Displaying license")
602 printLicense()
603 } else if strings.Compare("create-wallet", text) == 0 {
604 logrus.Debug("Creating Wallet")
605 menuCreateWallet()
606 } else if strings.Compare("open-wallet", text) == 0 {
607 logrus.Debug("Opening Wallet")
608 menuOpenWallet()
609 } else if strings.Compare("transaction-history", text) == 0 {
610 logrus.Debug("Opening Transaction History")
611 menuGetContainerTransactions()
612 } else if strings.Compare("push-graph", text) == 0 {
613 logrus.Debug("Opening Graph History")
614 createCID()
615 } else if strings.Compare("open-wallet-info", text) == 0 {
616 logrus.Debug("Opening Wallet Info")
617 menuOpenWalletInfo()
618 } else if strings.Compare("benchmark", text) == 0 {
619 logrus.Debug("Benchmark")
620 benchmark()
621 } else if strings.Compare("print-graph", text) == 0 {
622 logrus.Debug("Print-graph")
623 printGraph(graphDir)
624 } else if strings.HasPrefix(text, "connect-channel") {
625 // connectToChannel(strings.TrimPrefix(text, "connect-channel "))
626 } else if strings.Compare("exit", text) == 0 {
627 logrus.Warning("Exiting")
628 menuExit()
629 } else if strings.Compare("create-channel", text) == 0 {
630 logrus.Debug("Creating Karai Transaction Channel")
631 spawnChannel()
632 } else if strings.Compare("generate-pointer", text) == 0 {
633 generatePointer()
634 } else if strings.Compare("quit", text) == 0 {
635 logrus.Warning("Exiting")
636 menuExit()
637 } else if strings.Compare("close", text) == 0 {
638 logrus.Warning("Exiting")
639 menuExit()
640 } else if strings.Compare("\n", text) == 0 {
641 fmt.Println("")
642 } else {
643 fmt.Println("\nChoose an option from the menu")
644 menu()
645 }
646 }
647}
648
649// provide list of commands
650func menu() {
651 color.Set(color.FgGreen)
652 fmt.Println("\nCHANNEL_OPTIONS")
653 color.Set(color.FgWhite)
654 if !isCoordinator {
655 } else {
656 fmt.Println("create-channel \t\t Create a karai transaction channel")
657 fmt.Println("generate-pointer \t Generate a Karai <=> TRTL pointer")
658 fmt.Println("benchmark \t\t Conducts timed benchmark")
659 fmt.Println("push-graph \t\t Prints graph history")
660 }
661 color.Set(color.FgGreen)
662 fmt.Println("\nWALLET_API_OPTIONS")
663 color.Set(color.FgWhite)
664 fmt.Println("open-wallet \t\t Open a TRTL wallet")
665 fmt.Println("open-wallet-info \t Show wallet and connection info")
666 fmt.Println("create-wallet \t\t Create a TRTL wallet")
667 color.Set(color.FgHiBlack)
668 fmt.Println("wallet-balance \t\t Displays wallet balance")
669 color.Set(color.FgGreen)
670 fmt.Println("\nKARAI_OPTIONS")
671 color.Set(color.FgWhite)
672 fmt.Println("connect-channel <ktx> \t Connects to channel")
673 color.Set(color.FgHiBlack)
674 fmt.Println("list-servers \t\t Lists pinning servers")
675 color.Set(color.FgGreen)
676 fmt.Println("\nGENERAL_OPTIONS")
677 color.Set(color.FgWhite)
678 fmt.Println("version \t\t Displays version")
679 fmt.Println("license \t\t Displays license")
680 fmt.Println("exit \t\t\t Quit immediately")
681 fmt.Println("")
682}
683
684// Some basic TRTL API stats
685func menuOpenWalletInfo() {
686 walletInfoPrimaryAddressBalance()
687 getNodeInfo()
688 getWalletAPIStatus()
689}
690
691// Get Wallet-API transactions
692func menuGetContainerTransactions() {
693 req, err := http.NewRequest("GET", "http://127.0.0.1:8070/transactions", nil)
694 handle("Error getting container transactions: ", err)
695 req.Header.Set("X-API-KEY", "pineapples")
696 client := &http.Client{Timeout: time.Second * 10}
697 resp, err := client.Do(req)
698 handle("Error getting container transactions: ", err)
699 defer resp.Body.Close()
700 body, err := ioutil.ReadAll(resp.Body)
701 handle("Error getting container transactions: ", err)
702 fmt.Printf("%s\n", body)
703}
704
705// Get Wallet-API status
706func getWalletAPIStatus() {
707 logrus.Info("[Wallet-API Status]")
708 req, err := http.NewRequest("GET", "http://127.0.0.1:8070/status", nil)
709 handle("Error getting Wallet-API status: ", err)
710 req.Header.Set("X-API-KEY", "pineapples")
711 client := &http.Client{Timeout: time.Second * 10}
712 resp, err := client.Do(req)
713 handle("Error getting Wallet-API status: ", err)
714 defer resp.Body.Close()
715 body, err := ioutil.ReadAll(resp.Body)
716 handle("Error getting Wallet-API status: ", err)
717 fmt.Printf("%s\n", body)
718}
719
720// Get TRTL Node Info
721func getNodeInfo() {
722 logrus.Info("[Node Info]")
723 req, err := http.NewRequest("GET", "http://127.0.0.1:8070/node", nil)
724 handle("Error getting node info: ", err)
725 req.Header.Set("X-API-KEY", "pineapples")
726 client := &http.Client{Timeout: time.Second * 10}
727 resp, err := client.Do(req)
728 handle("Error getting node info: ", err)
729 defer resp.Body.Close()
730 body, err := ioutil.ReadAll(resp.Body)
731 handle("Error getting node info: ", err)
732 fmt.Printf("%s\n", body)
733}
734
735// Get primary TRTL address balance
736func walletInfoPrimaryAddressBalance() {
737 logrus.Info("[Primary Address]")
738 req, err := http.NewRequest("GET", "http://127.0.0.1:8070/balances", nil)
739 handle("Error getting wallet info primary address: ", err)
740 req.Header.Set("X-API-KEY", "pineapples")
741 client := &http.Client{Timeout: time.Second * 10}
742 resp, err := client.Do(req)
743 handle("Error getting wallet info primary address: ", err)
744 defer resp.Body.Close()
745 body, err := ioutil.ReadAll(resp.Body)
746 handle("Error getting wallet info primary address: ", err)
747 fmt.Printf("%s\n", body)
748}
749
750// Print the license for the user
751func printLicense() {
752 fmt.Printf(color.GreenString("\n"+appName+" v"+semverInfo()) + color.WhiteString(" by "+appDev))
753 color.Set(color.FgGreen)
754 fmt.Println("\n" + appRepository + "\n" + appURL + "\n")
755
756 color.Set(color.FgHiWhite)
757 fmt.Println("\nMIT License\nCopyright (c) 2020-2021 RockSteady, TurtleCoin Developers")
758 color.Set(color.FgHiBlack)
759 fmt.Println("\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in allcopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.")
760 fmt.Println()
761}
762
763// Create a wallet in the wallet-api container
764func menuCreateWallet() {
765 logrus.Debug("Creating Wallet")
766 url := "http://127.0.0.1:8070/wallet/create"
767 data := []byte(`{"daemonHost": "127.0.0.1", "daemonPort": 11898, "filename": "karai-wallet.wallet", "password": "supersecretpassword"}`)
768 req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
769 handle("Error creating wallet: ", err)
770 req.Header.Set("Content-Type", "application/json")
771 req.Header.Set("X-API-KEY", "pineapples")
772 client := &http.Client{Timeout: time.Second * 10}
773 logrus.Info(req.Header)
774 resp, err := client.Do(req)
775 handle("Error creating wallet: ", err)
776 defer resp.Body.Close()
777 logrus.Info("response Status:", resp.Status)
778 logrus.Info("response Headers:", resp.Header)
779 body, err := ioutil.ReadAll(resp.Body)
780 handle("Error creating wallet: ", err)
781 fmt.Printf("%s\n", body)
782}
783
784// Open a wallet file
785func menuOpenWallet() {
786 logrus.Debug("Opening Wallet")
787 url := "http://127.0.0.1:8070/wallet/open"
788 data := []byte(`{"daemonHost": "127.0.0.1", "daemonPort": 11898, "filename": "karai-wallet.wallet", "password": "supersecretpassword"}`)
789 req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
790 handle("Error opening wallet: ", err)
791 req.Header.Set("Content-Type", "application/json")
792 req.Header.Set("X-API-KEY", "pineapples")
793 client := &http.Client{Timeout: time.Second * 10}
794 logrus.Info(req.Header)
795 resp, err := client.Do(req)
796 handle("Error opening wallet: ", err)
797 defer resp.Body.Close()
798 logrus.Info("response Status:", resp.Status)
799 logrus.Info("response Headers:", resp.Header)
800 body, err := ioutil.ReadAll(resp.Body)
801 handle("Error opening wallet: ", err)
802 fmt.Printf("%s\n", body)
803}
804
805// Print the version string for the user
806func menuVersion() {
807 fmt.Println(appName + " - v" + semverInfo())
808}
809
810// Exit the program
811func menuExit() {
812 os.Exit(0)
813}
814
815func handle(msg string, err error) {
816 if err != nil {
817 logrus.Error(msg, err)
818 }
819}