You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

438 lines
13 KiB

  1. package main
  2. import "io"
  3. import "os"
  4. import "time"
  5. import "fmt"
  6. import "bytes"
  7. import "strings"
  8. import "strconv"
  9. import "encoding/hex"
  10. import "encoding/json"
  11. import "path/filepath"
  12. import "github.com/chzyer/readline"
  13. import "github.com/docopt/docopt-go"
  14. import log "github.com/sirupsen/logrus"
  15. import "github.com/deroproject/derosuite/p2p"
  16. import "github.com/deroproject/derosuite/globals"
  17. import "github.com/deroproject/derosuite/blockchain"
  18. import "github.com/deroproject/derosuite/crypto"
  19. var command_line string = `derod
  20. DERO : A secure, private blockchain with smart-contracts
  21. Usage:
  22. derod [--help] [--version] [--testnet] [--debug] [--socks-proxy=<socks_ip:port>] [--p2p-bind-port=<18090>] [--add-exclusive-node=<ip:port>]...
  23. derod -h | --help
  24. derod --version
  25. Options:
  26. -h --help Show this screen.
  27. --version Show version.
  28. --testnet Run in testnet mode.
  29. --debug Debug mode enabled, print log messages
  30. --socks-proxy=<socks_ip:port> Use a proxy to connect to network.
  31. --p2p-bind-port=<18090> p2p server listens on this port.
  32. --add-exclusive-node=<ip:port> Connect to this peer only (disabled for this version)`
  33. func main() {
  34. var err error
  35. globals.Arguments, err = docopt.Parse(command_line, nil, true, "DERO daemon : work in progress", false)
  36. if err != nil {
  37. log.Fatalf("Error while parsing options err: %s\n", err)
  38. }
  39. // We need to initialize readline first, so it changes stderr to ansi processor on windows
  40. l, err := readline.NewEx(&readline.Config{
  41. //Prompt: "\033[92mDERO:\033[32m»\033[0m",
  42. Prompt: "\033[92mDERO:\033[32m>>>\033[0m ",
  43. HistoryFile: filepath.Join(os.TempDir(), "derod_readline.tmp"),
  44. AutoComplete: completer,
  45. InterruptPrompt: "^C",
  46. EOFPrompt: "exit",
  47. HistorySearchFold: true,
  48. FuncFilterInputRune: filterInput,
  49. })
  50. if err != nil {
  51. panic(err)
  52. }
  53. defer l.Close()
  54. // parse arguments and setup testnet mainnet
  55. globals.Initialize() // setup network and proxy
  56. globals.Logger.Infof("") // a dummy write is required to fully activate logrus
  57. // all screen output must go through the readline
  58. globals.Logger.Out = l.Stdout()
  59. globals.Logger.Debugf("Arguments %+v", globals.Arguments)
  60. globals.Logger.Infof("DERO daemon : This version is under heavy development, use it for testing/evaluations purpose only")
  61. globals.Logger.Infof("Daemon in %s mode", globals.Config.Name)
  62. chain, _ := blockchain.Blockchain_Start(nil)
  63. params := map[string]interface{}{}
  64. params["chain"] = chain
  65. p2p.P2P_Init(params)
  66. // This tiny goroutine continuously updates status as required
  67. go func() {
  68. last_our_height := uint64(0)
  69. last_best_height := uint64(0)
  70. last_peer_count := uint64(0)
  71. for {
  72. if globals.Exit_In_Progress {
  73. return
  74. }
  75. our_height := chain.Get_Height()
  76. best_height := p2p.Best_Peer_Height()
  77. peer_count := p2p.Peer_Count()
  78. // only update prompt if needed
  79. if last_our_height != our_height || last_best_height != best_height || last_peer_count != peer_count {
  80. // choose color based on urgency
  81. color := "\033[32m" // default is green color
  82. if our_height < best_height {
  83. color = "\033[33m" // make prompt yellow
  84. } else if our_height > best_height {
  85. color = "\033[31m" // make prompt red
  86. }
  87. pcolor := "\033[32m" // default is green color
  88. if peer_count < 1 {
  89. pcolor = "\033[31m" // make prompt red
  90. } else if peer_count <= 8 {
  91. pcolor = "\033[33m" // make prompt yellow
  92. }
  93. l.SetPrompt(fmt.Sprintf("\033[1m\033[32mDERO: \033[0m"+color+"%d/%d "+pcolor+"P %d\033[32m>>>\033[0m ", our_height, best_height, peer_count))
  94. l.Refresh()
  95. last_our_height = our_height
  96. last_best_height = best_height
  97. last_peer_count = peer_count
  98. }
  99. time.Sleep(1 * time.Second)
  100. }
  101. }()
  102. setPasswordCfg := l.GenPasswordConfig()
  103. setPasswordCfg.SetListener(func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) {
  104. l.SetPrompt(fmt.Sprintf("Enter password(%v): ", len(line)))
  105. l.Refresh()
  106. return nil, 0, false
  107. })
  108. l.Refresh() // refresh the prompt
  109. for {
  110. line, err := l.Readline()
  111. if err == readline.ErrInterrupt {
  112. if len(line) == 0 {
  113. fmt.Print("Ctrl-C received, Exit in progress\n")
  114. globals.Exit_In_Progress = true
  115. break
  116. } else {
  117. continue
  118. }
  119. } else if err == io.EOF {
  120. break
  121. }
  122. line = strings.TrimSpace(line)
  123. line_parts := strings.Fields(line)
  124. command := ""
  125. if len(line_parts) >= 1 {
  126. command = strings.ToLower(line_parts[0])
  127. }
  128. switch {
  129. case strings.HasPrefix(line, "mode "):
  130. switch line[5:] {
  131. case "vi":
  132. l.SetVimMode(true)
  133. case "emacs":
  134. l.SetVimMode(false)
  135. default:
  136. println("invalid mode:", line[5:])
  137. }
  138. case line == "mode":
  139. if l.IsVimMode() {
  140. println("current mode: vim")
  141. } else {
  142. println("current mode: emacs")
  143. }
  144. case line == "login":
  145. pswd, err := l.ReadPassword("please enter your password: ")
  146. if err != nil {
  147. break
  148. }
  149. println("you enter:", strconv.Quote(string(pswd)))
  150. case line == "help":
  151. usage(l.Stderr())
  152. case line == "setpassword":
  153. pswd, err := l.ReadPasswordWithConfig(setPasswordCfg)
  154. if err == nil {
  155. println("you set:", strconv.Quote(string(pswd)))
  156. }
  157. case strings.HasPrefix(line, "setprompt"):
  158. if len(line) <= 10 {
  159. log.Println("setprompt <prompt>")
  160. break
  161. }
  162. l.SetPrompt(line[10:])
  163. case strings.HasPrefix(line, "say"):
  164. line := strings.TrimSpace(line[3:])
  165. if len(line) == 0 {
  166. log.Println("say what?")
  167. break
  168. }
  169. go func() {
  170. for range time.Tick(time.Second) {
  171. log.Println(line)
  172. }
  173. }()
  174. case command == "print_bc":
  175. log.Info("printing block chain")
  176. // first is starting point, second is ending point
  177. start := int64(0)
  178. stop := int64(0)
  179. if len(line_parts) != 3 {
  180. fmt.Printf("This function requires 2 parameters, start and endpoint\n")
  181. continue
  182. }
  183. if s, err := strconv.ParseInt(line_parts[1], 10, 64); err == nil {
  184. start = s
  185. } else {
  186. fmt.Printf("Invalid start value")
  187. continue
  188. }
  189. if s, err := strconv.ParseInt(line_parts[2], 10, 64); err == nil {
  190. stop = s
  191. } else {
  192. fmt.Printf("Invalid stop value")
  193. continue
  194. }
  195. if start < 0 || start >= int64(chain.Get_Height()) {
  196. fmt.Printf("Start value should be be between 0 and current height\n")
  197. continue
  198. }
  199. if start > stop && stop >= int64(chain.Get_Height()) {
  200. fmt.Printf("Stop value should be > start and current height\n")
  201. continue
  202. }
  203. fmt.Printf("Printing block chain from %d to %d\n", start, stop)
  204. for i := start; i < stop; i++ {
  205. // get block id at height
  206. current_block_id, err := chain.Load_BL_ID_at_Height(uint64(i))
  207. if err != nil {
  208. fmt.Printf("Skipping block at height %d due to error %s\n", i, err)
  209. continue
  210. }
  211. timestamp := chain.Load_Block_Timestamp(current_block_id)
  212. parent_block_id := chain.Load_Block_Parent_ID(current_block_id)
  213. // calculate difficulty
  214. //parent_cdiff := chain.Load_Block_Cumulative_Difficulty(parent_block_id)
  215. //block_cdiff := chain.Load_Block_Cumulative_Difficulty(current_block_id)
  216. diff := chain.Get_Difficulty_At_Block(parent_block_id)
  217. //size := chain.
  218. fmt.Printf("height: %10d, timestamp: %10d, difficulty: %12d\n", i, timestamp, diff)
  219. fmt.Printf("Block Id: %x , prev block id:%x\n", current_block_id, parent_block_id)
  220. fmt.Printf("\n")
  221. }
  222. case command == "print_block":
  223. fmt.Printf("printing block\n")
  224. if len(line_parts) == 2 && len(line_parts[1]) == 64 {
  225. txid, err := hex.DecodeString(strings.ToLower(line_parts[1]))
  226. if err != nil {
  227. fmt.Printf("err while decoding txid err %s\n", err)
  228. continue
  229. }
  230. var hash crypto.Hash
  231. copy(hash[:32], []byte(txid))
  232. fmt.Printf("block id: %x\n", hash[:])
  233. bl, err := chain.Load_BL_FROM_ID(hash)
  234. if err == nil {
  235. fmt.Printf("Block : %x\n", bl.Serialize())
  236. } else {
  237. fmt.Printf("Err %s\n", err)
  238. }
  239. } else if len(line_parts) == 2 {
  240. if s, err := strconv.ParseInt(line_parts[1], 10, 64); err == nil {
  241. // first load block id from height
  242. hash, err := chain.Load_BL_ID_at_Height(uint64(s))
  243. if err == nil {
  244. bl, err := chain.Load_BL_FROM_ID(hash)
  245. if err == nil {
  246. fmt.Printf("block id: %x\n", hash[:])
  247. fmt.Printf("Block : %x\n", bl.Serialize())
  248. json_bytes, err := json.Marshal(bl)
  249. fmt.Printf("%s err : %s\n", string(prettyprint_json(json_bytes)), err)
  250. } else {
  251. fmt.Printf("Err %s\n", err)
  252. }
  253. } else {
  254. fmt.Printf("err %s\n", err)
  255. }
  256. }
  257. } else {
  258. fmt.Printf("print_tx needs a single transaction id as arugument\n")
  259. }
  260. case command == "print_tx":
  261. if len(line_parts) == 2 && len(line_parts[1]) == 64 {
  262. txid, err := hex.DecodeString(strings.ToLower(line_parts[1]))
  263. if err != nil {
  264. fmt.Printf("err while decoding txid err %s\n", err)
  265. continue
  266. }
  267. var hash crypto.Hash
  268. copy(hash[:32], []byte(txid))
  269. tx, err := chain.Load_TX_FROM_ID(hash)
  270. if err == nil {
  271. s_bytes := tx.Serialize()
  272. fmt.Printf("tx : %x\n", s_bytes)
  273. json_bytes, err := json.MarshalIndent(tx, "", " ")
  274. _ = err
  275. fmt.Printf("%s\n", string(json_bytes))
  276. } else {
  277. fmt.Printf("Err %s\n", err)
  278. }
  279. } else {
  280. fmt.Printf("print_tx needs a single transaction id as arugument\n")
  281. }
  282. case strings.ToLower(line) == "diff":
  283. fmt.Printf("Network %s BH %d, Diff %d, NW Hashrate %0.03f MH/sec TH %x\n", globals.Config.Name, chain.Get_Height(), chain.Get_Difficulty(), float64(chain.Get_Network_HashRate())/1000000.0, chain.Get_Top_ID())
  284. case strings.ToLower(line) == "status":
  285. // fmt.Printf("chain diff %d\n",chain.Get_Difficulty_At_Block(chain.Top_ID))
  286. //fmt.Printf("chain nw rate %d\n", chain.Get_Network_HashRate())
  287. inc, out := p2p.Peer_Direction_Count()
  288. fmt.Printf("Network %s Height %d NW Hashrate %0.03f MH/sec TH %x Peers %d INC, %d OUT\n", globals.Config.Name, chain.Get_Height(), float64(chain.Get_Network_HashRate())/1000000.0, chain.Get_Top_ID(), inc,out )
  289. case strings.ToLower(line) == "sync_info":
  290. p2p.Connection_Print()
  291. case strings.ToLower(line) == "bye":
  292. fallthrough
  293. case strings.ToLower(line) == "exit":
  294. fallthrough
  295. case strings.ToLower(line) == "quit":
  296. goto exit
  297. case line == "sleep":
  298. log.Println("sleep 4 second")
  299. time.Sleep(4 * time.Second)
  300. case line == "":
  301. default:
  302. log.Println("you said:", strconv.Quote(line))
  303. }
  304. }
  305. exit:
  306. globals.Logger.Infof("Exit in Progress, Please wait")
  307. time.Sleep(100 * time.Millisecond) // give prompt update time to finish
  308. p2p.P2P_Shutdown() // shutdown p2p subsystem
  309. chain.Shutdown() // shutdown chain subsysem
  310. for globals.Subsystem_Active > 0 {
  311. time.Sleep(100 * time.Millisecond)
  312. }
  313. }
  314. func prettyprint_json(b []byte) []byte {
  315. var out bytes.Buffer
  316. err := json.Indent(&out, b, "", " ")
  317. _ = err
  318. return out.Bytes()
  319. }
  320. func usage(w io.Writer) {
  321. io.WriteString(w, "commands:\n")
  322. //io.WriteString(w, completer.Tree(" "))
  323. io.WriteString(w,"\t\033[1mhelp\033[0m\t\tthis help\n")
  324. io.WriteString(w,"\t\033[1mdiff\033[0m\t\tShow difficulty\n")
  325. io.WriteString(w,"\t\033[1mprint_bc\033[0m\tPrint blockchain info in a given blocks range, print_bc <begin_height> <end_height>\n")
  326. io.WriteString(w,"\t\033[1mprint_block\033[0m\tPrint block, print_block <block_hash> or <block_height>\n")
  327. io.WriteString(w,"\t\033[1mprint_height\033[0m\tPrint local blockchain height\n")
  328. io.WriteString(w,"\t\033[1mprint_tx\033[0m\tPrint transaction, print_tx <transaction_hash>\n")
  329. io.WriteString(w,"\t\033[1mstatus\033[0m\t\tShow genereal information\n")
  330. io.WriteString(w,"\t\033[1msync_info\033[0m\tPrint information about connected peers and their state\n")
  331. io.WriteString(w,"\t\033[1mbye\033[0m\t\tQuit the daemon\n")
  332. io.WriteString(w,"\t\033[1mexit\033[0m\t\tQuit the daemon\n")
  333. io.WriteString(w,"\t\033[1mquit\033[0m\t\tQuit the daemon\n")
  334. }
  335. var completer = readline.NewPrefixCompleter(
  336. /* readline.PcItem("mode",
  337. readline.PcItem("vi"),
  338. readline.PcItem("emacs"),
  339. ),
  340. readline.PcItem("login"),
  341. readline.PcItem("say",
  342. readline.PcItem("hello"),
  343. readline.PcItem("bye"),
  344. ),
  345. readline.PcItem("setprompt"),
  346. readline.PcItem("setpassword"),
  347. readline.PcItem("bye"),
  348. */
  349. readline.PcItem("help"),
  350. /* readline.PcItem("go",
  351. readline.PcItem("build", readline.PcItem("-o"), readline.PcItem("-v")),
  352. readline.PcItem("install",
  353. readline.PcItem("-v"),
  354. readline.PcItem("-vv"),
  355. readline.PcItem("-vvv"),
  356. ),
  357. readline.PcItem("test"),
  358. ),
  359. readline.PcItem("sleep"),
  360. */
  361. readline.PcItem("diff"),
  362. readline.PcItem("print_bc"),
  363. readline.PcItem("print_block"),
  364. readline.PcItem("print_height"),
  365. readline.PcItem("print_tx"),
  366. readline.PcItem("status"),
  367. readline.PcItem("sync_info"),
  368. readline.PcItem("bye"),
  369. readline.PcItem("exit"),
  370. readline.PcItem("quit"),
  371. )
  372. func filterInput(r rune) (rune, bool) {
  373. switch r {
  374. // block CtrlZ feature
  375. case readline.CharCtrlZ:
  376. return r, false
  377. }
  378. return r, true
  379. }