github.com/deroproject/derosuite@v2.1.6-1.0.20200307070847-0f2e589c7a2b+incompatible/cmd/derod/main.go (about) 1 // Copyright 2017-2018 DERO Project. All rights reserved. 2 // Use of this source code in any form is governed by RESEARCH license. 3 // license can be found in the LICENSE file. 4 // GPG: 0F39 E425 8C65 3947 702A 8234 08B2 0360 A03A 9DE8 5 // 6 // 7 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 8 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 9 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 10 // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 11 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 12 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 13 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 14 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 15 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 17 package main 18 19 import "io" 20 import "os" 21 import "time" 22 import "fmt" 23 import "bytes" 24 import "bufio" 25 import "strings" 26 import "strconv" 27 import "runtime" 28 import "math/big" 29 import "os/signal" 30 31 //import "crypto/sha1" 32 import "encoding/hex" 33 import "encoding/json" 34 import "path/filepath" 35 import "runtime/pprof" 36 37 import "golang.org/x/crypto/sha3" 38 import "github.com/romana/rlog" 39 import "github.com/chzyer/readline" 40 import "github.com/docopt/docopt-go" 41 import log "github.com/sirupsen/logrus" 42 43 //import "github.com/deroproject/derosuite/p2p" 44 import "github.com/deroproject/derosuite/p2p" 45 import "github.com/deroproject/derosuite/globals" 46 import "github.com/deroproject/derosuite/block" 47 import "github.com/deroproject/derosuite/config" 48 //import "github.com/deroproject/derosuite/address" 49 import "github.com/deroproject/derosuite/blockchain" 50 import "github.com/deroproject/derosuite/transaction" 51 52 //import "github.com/deroproject/derosuite/checkpoints" 53 import "github.com/deroproject/derosuite/crypto" 54 import "github.com/deroproject/derosuite/cryptonight" 55 56 //import "github.com/deroproject/derosuite/crypto/ringct" 57 import "github.com/deroproject/derosuite/blockchain/rpcserver" 58 import "github.com/deroproject/derosuite/walletapi" 59 60 //import "github.com/deroproject/derosuite/address" 61 62 var command_line string = `derod 63 DERO : A secure, private blockchain with smart-contracts 64 65 Usage: 66 derod [--help] [--version] [--testnet] [--debug] [--sync-node] [--boltdb | --badgerdb] [--disable-checkpoints] [--socks-proxy=<socks_ip:port>] [--data-dir=<directory>] [--p2p-bind=<0.0.0.0:18089>] [--add-exclusive-node=<ip:port>]... [--add-priority-node=<ip:port>]... [--min-peers=<11>] [--rpc-bind=<127.0.0.1:9999>] [--lowcpuram] [--mining-address=<wallet_address>] [--mining-threads=<cpu_num>] [--node-tag=<unique name>] 67 derod -h | --help 68 derod --version 69 70 Options: 71 -h --help Show this screen. 72 --version Show version. 73 --testnet Run in testnet mode. 74 --debug Debug mode enabled, print log messages 75 --boltdb Use boltdb as backend (default on 64 bit systems) 76 --badgerdb Use Badgerdb as backend (default on 32 bit systems) 77 --disable-checkpoints Disable checkpoints, work in truly async, slow mode 1 block at a time 78 --socks-proxy=<socks_ip:port> Use a proxy to connect to network. 79 --data-dir=<directory> Store blockchain data at this location 80 --rpc-bind=<127.0.0.1:9999> RPC listens on this ip:port 81 --p2p-bind=<0.0.0.0:18089> p2p server listens on this ip:port, specify port 0 to disable listening server 82 --add-exclusive-node=<ip:port> Connect to specific peer only 83 --add-priority-node=<ip:port> Maintain persistant connection to specified peer 84 --sync-node Sync node automatically with the seeds nodes. This option is for rare use. 85 --min-peers=<11> Number of connections the daemon tries to maintain 86 --lowcpuram Disables some RAM consuming sections (deactivates mining/ultra compact protocol etc). 87 --mining-address=<wallet_address> This address is rewarded when a block is mined sucessfully 88 --mining-threads=<cpu_num> Number of CPU threads for mining 89 --node-tag=<unique name> Unique name of node, visible to everyone 90 91 ` 92 93 var Exit_In_Progress = make(chan bool) 94 95 func main() { 96 var err error 97 globals.Init_rlog() 98 globals.Arguments, err = docopt.Parse(command_line, nil, true, config.Version.String(), false) 99 100 if err != nil { 101 log.Fatalf("Error while parsing options err: %s\n", err) 102 } 103 104 // We need to initialize readline first, so it changes stderr to ansi processor on windows 105 106 l, err := readline.NewEx(&readline.Config{ 107 //Prompt: "\033[92mDERO:\033[32m»\033[0m", 108 Prompt: "\033[92mDERO:\033[32m>>>\033[0m ", 109 HistoryFile: filepath.Join(os.TempDir(), "derod_readline.tmp"), 110 AutoComplete: completer, 111 InterruptPrompt: "^C", 112 EOFPrompt: "exit", 113 114 HistorySearchFold: true, 115 FuncFilterInputRune: filterInput, 116 }) 117 if err != nil { 118 panic(err) 119 } 120 defer l.Close() 121 122 // parse arguments and setup testnet mainnet 123 globals.Initialize() // setup network and proxy 124 globals.Logger.Infof("") // a dummy write is required to fully activate logrus 125 126 // all screen output must go through the readline 127 globals.Logger.Out = l.Stdout() 128 129 rlog.Infof("Arguments %+v", globals.Arguments) 130 131 globals.Logger.Infof("DERO Atlantis daemon : It is an alpha version, use it for testing/evaluations purpose only.") 132 133 globals.Logger.Infof("Copyright 2017-2018 DERO Project. All rights reserved.") 134 globals.Logger.Infof("OS:%s ARCH:%s GOMAXPROCS:%d", runtime.GOOS, runtime.GOARCH, runtime.GOMAXPROCS(0)) 135 globals.Logger.Infof("Version v%s", config.Version.String()) 136 137 globals.Logger.Infof("Daemon in %s mode", globals.Config.Name) 138 globals.Logger.Infof("Daemon data directory %s", globals.GetDataDirectory()) 139 140 go check_update_loop () 141 142 params := map[string]interface{}{} 143 144 //params["--disable-checkpoints"] = globals.Arguments["--disable-checkpoints"].(bool) 145 chain, err := blockchain.Blockchain_Start(params) 146 147 if err != nil { 148 globals.Logger.Warnf("Error starting blockchain err '%s'",err) 149 return 150 } 151 152 params["chain"] = chain 153 154 // setup miner flag, before starting p2p 155 //if globals.Arguments["--miner"].(bool) { 156 // params["--miner"] = true 157 //} 158 159 // since user is using a proxy, he definitely does not want to give out his IP 160 /*if globals.Arguments["--socks-proxy"] != nil { 161 globals.Arguments["--p2p-bind"] = ":0" 162 //globals.Logger.Warnf("Disabling P2P server since we are using socks proxy") 163 if globals.Arguments["--enable-p2p-v1"].(bool) { // disable v1 p2p server 164 globals.Arguments["--p2p-bind-v1"] = ":0" 165 } 166 }*/ 167 168 if cryptonight.HardwareAES { 169 rlog.Infof("Hardware AES detected") 170 } 171 172 p2p.P2P_Init(params) 173 174 //rpcserver.DEBUG_MODE = true 175 rpc, _ := rpcserver.RPCServer_Start(params) 176 177 // setup function pointers 178 // these pointers need to fixed 179 chain.Mempool.P2P_TX_Relayer = func(tx *transaction.Transaction, peerid uint64) (count int) { 180 count += p2p.Broadcast_Tx(tx, peerid) 181 return 182 } 183 184 chain.P2P_Block_Relayer = func(cbl *block.Complete_Block, peerid uint64) { 185 p2p.Broadcast_Block(cbl, peerid) 186 } 187 188 if globals.Arguments["--lowcpuram"].(bool) == false && globals.Arguments["--sync-node"].(bool) == false { // enable v1 of protocol only if requested 189 190 // if an address has been provided, verify that it satisfies //mainnet/testnet criteria 191 if globals.Arguments["--mining-address"] != nil { 192 193 addr, err := globals.ParseValidateAddress(globals.Arguments["--mining-address"].(string)) 194 if err != nil { 195 globals.Logger.Fatalf("Mining address is invalid: err %s", err) 196 } 197 params["mining-address"] = addr 198 199 //log.Debugf("Setting up proxy using %s", Arguments["--socks-proxy"].(string)) 200 } 201 202 if globals.Arguments["--mining-threads"] != nil { 203 thread_count := 0 204 if s, err := strconv.Atoi(globals.Arguments["--mining-threads"].(string)); err == nil { 205 //fmt.Printf("%T, %v", s, s) 206 thread_count = s 207 208 } else { 209 globals.Logger.Fatalf("Mining threads argument cannot be parsed: err %s", err) 210 } 211 212 if thread_count > runtime.GOMAXPROCS(0) { 213 globals.Logger.Fatalf("Mining threads (%d) is more than available CPUs (%d). This is NOT optimal", thread_count, runtime.GOMAXPROCS(0)) 214 215 } 216 params["mining-threads"] = thread_count 217 218 if _, ok := params["mining-address"]; !ok { 219 globals.Logger.Fatalf("Mining threads require a valid wallet address") 220 } 221 222 globals.Logger.Infof("System will mine to %s with %d threads. Good Luck!!", globals.Arguments["--mining-address"].(string), thread_count) 223 224 // go start_miner(chain, params["mining-address"].(*address.Address), thread_count) 225 } 226 227 } 228 229 go time_check_routine() // check whether server time is in sync 230 231 // This tiny goroutine continuously updates status as required 232 go func() { 233 last_our_height := int64(0) 234 last_best_height := int64(0) 235 last_peer_count := uint64(0) 236 last_topo_height := int64(0) 237 last_mempool_tx_count := 0 238 last_counter := uint64(0) 239 last_counter_time := time.Now() 240 last_mining_state := false 241 for { 242 select { 243 case <-Exit_In_Progress: 244 return 245 default: 246 } 247 our_height := chain.Get_Height() 248 best_height, best_topo_height := p2p.Best_Peer_Height() 249 peer_count := p2p.Peer_Count() 250 topo_height := chain.Load_TOPO_HEIGHT(nil) 251 252 mempool_tx_count := len(chain.Mempool.Mempool_List_TX()) 253 254 // only update prompt if needed 255 if last_mining_state != mining || mining || last_our_height != our_height || last_best_height != best_height || last_peer_count != peer_count || last_topo_height != topo_height || last_mempool_tx_count != mempool_tx_count { 256 // choose color based on urgency 257 color := "\033[32m" // default is green color 258 if our_height < best_height { 259 color = "\033[33m" // make prompt yellow 260 } else if our_height > best_height { 261 color = "\033[31m" // make prompt red 262 } 263 264 pcolor := "\033[32m" // default is green color 265 if peer_count < 1 { 266 pcolor = "\033[31m" // make prompt red 267 } else if peer_count <= 8 { 268 pcolor = "\033[33m" // make prompt yellow 269 } 270 271 mining_string := "" 272 273 if mining { 274 mining_speed := float64(counter-last_counter) / (float64(uint64(time.Since(last_counter_time))) / 1000000000.0) 275 last_counter = counter 276 last_counter_time = time.Now() 277 switch { 278 case mining_speed > 1000000: 279 mining_string = fmt.Sprintf("MINING %.1f MH/s", float32(mining_speed)/1000000.0) 280 case mining_speed > 1000: 281 mining_string = fmt.Sprintf("MINING %.1f KH/s", float32(mining_speed)/1000.0) 282 case mining_speed > 0: 283 mining_string = fmt.Sprintf("MINING %.0f H/s", mining_speed) 284 } 285 } 286 last_mining_state = mining 287 288 hash_rate_string := "" 289 hash_rate := chain.Get_Network_HashRate() 290 switch { 291 case hash_rate > 1000000000000: 292 hash_rate_string = fmt.Sprintf("%.1f TH/s", float64(hash_rate)/1000000000000.0) 293 case hash_rate > 1000000000: 294 hash_rate_string = fmt.Sprintf("%.1f GH/s", float64(hash_rate)/1000000000.0) 295 case hash_rate > 1000000: 296 hash_rate_string = fmt.Sprintf("%.1f MH/s", float64(hash_rate)/1000000.0) 297 case hash_rate > 1000: 298 hash_rate_string = fmt.Sprintf("%.1f KH/s", float64(hash_rate)/1000.0) 299 case hash_rate > 0: 300 hash_rate_string = fmt.Sprintf("%d H/s", hash_rate) 301 } 302 303 testnet_string := "" 304 if !globals.IsMainnet() { 305 testnet_string = "\033[31m TESTNET" 306 } 307 308 l.SetPrompt(fmt.Sprintf("\033[1m\033[32mDERO: \033[0m"+color+"%d/%d [%d/%d] "+pcolor+"P %d TXp %d \033[32mNW %s %s>%s>>\033[0m ", our_height, topo_height, best_height, best_topo_height, peer_count, mempool_tx_count, hash_rate_string, mining_string, testnet_string)) 309 l.Refresh() 310 last_our_height = our_height 311 last_best_height = best_height 312 last_peer_count = peer_count 313 last_mempool_tx_count = mempool_tx_count 314 last_topo_height = best_topo_height 315 } 316 time.Sleep(1 * time.Second) 317 } 318 }() 319 320 setPasswordCfg := l.GenPasswordConfig() 321 setPasswordCfg.SetListener(func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) { 322 l.SetPrompt(fmt.Sprintf("Enter password(%v): ", len(line))) 323 l.Refresh() 324 return nil, 0, false 325 }) 326 l.Refresh() // refresh the prompt 327 328 go func (){ 329 var gracefulStop = make(chan os.Signal) 330 signal.Notify(gracefulStop,os.Interrupt) // listen to all signals 331 for { 332 sig := <-gracefulStop 333 fmt.Printf("received signal %s\n", sig) 334 335 if sig.String() == "interrupt" { 336 close(Exit_In_Progress) 337 } 338 } 339 }() 340 341 for { 342 line, err := l.Readline() 343 if err == readline.ErrInterrupt { 344 if len(line) == 0 { 345 fmt.Print("Ctrl-C received, Exit in progress\n") 346 close(Exit_In_Progress) 347 break 348 } else { 349 continue 350 } 351 } else if err == io.EOF { 352 <-Exit_In_Progress 353 break 354 } 355 356 line = strings.TrimSpace(line) 357 line_parts := strings.Fields(line) 358 359 command := "" 360 if len(line_parts) >= 1 { 361 command = strings.ToLower(line_parts[0]) 362 } 363 364 switch { 365 case line == "help": 366 usage(l.Stderr()) 367 368 case strings.HasPrefix(line, "say"): 369 line := strings.TrimSpace(line[3:]) 370 if len(line) == 0 { 371 log.Println("say what?") 372 break 373 } 374 // 375 case command == "import_chain": // this migrates existing chain from DERO to DERO atlantis 376 f, err := os.Open("/tmp/raw_export.txt") 377 if err != nil { 378 globals.Logger.Warnf("error opening file /tmp/raw_export.txt %s", err) 379 continue 380 } 381 reader := bufio.NewReader(f) 382 383 account, _ := walletapi.Generate_Keys_From_Random() // create a random address 384 385 for { 386 line, err = reader.ReadString('\n') 387 388 if err != nil || len(line) < 10 { 389 break 390 } 391 392 var txs []string 393 394 err = json.Unmarshal([]byte(line), &txs) 395 396 if err != nil { 397 fmt.Printf("err while unmarshalling json err %s", err) 398 continue 399 } 400 401 if len(txs) < 1 { 402 panic("TX cannot be zero") 403 } 404 405 cbl, bl := chain.Create_new_miner_block(account.GetAddress()) 406 407 for i := range txs { 408 409 var tx transaction.Transaction 410 411 tx_bytes, err := hex.DecodeString(txs[i]) 412 if err != nil { 413 globals.Logger.Warnf("TX could not be decoded") 414 } 415 416 err = tx.DeserializeHeader(tx_bytes) 417 if err != nil { 418 globals.Logger.Warnf("TX could not be Deserialized") 419 } 420 421 globals.Logger.Infof(" txhash %s", tx.GetHash()) 422 if i == 0 { 423 bl.Miner_TX = tx 424 cbl.Bl.Miner_TX = tx 425 426 if bl.Miner_TX.GetHash() != tx.GetHash() || cbl.Bl.Miner_TX.GetHash() != tx.GetHash() { 427 panic("miner TX hash mismatch") 428 } 429 430 } else { 431 bl.Tx_hashes = append(bl.Tx_hashes, tx.GetHash()) 432 cbl.Bl.Tx_hashes = append(cbl.Bl.Tx_hashes, tx.GetHash()) 433 cbl.Txs = append(cbl.Txs, &tx) 434 } 435 } 436 437 if err, ok := chain.Add_Complete_Block(cbl); ok { 438 globals.Logger.Warnf("Block Successfully accepted by chain at height %d", cbl.Bl.Miner_TX.Vin[0].(transaction.Txin_gen).Height) 439 } else { 440 globals.Logger.Warnf("Block rejected by chain at height %d, please investigate, err %s", cbl.Bl.Miner_TX.Vin[0].(transaction.Txin_gen).Height,err) 441 globals.Logger.Warnf("Stopping import") 442 443 } 444 445 } 446 447 globals.Logger.Infof("File imported Successfully") 448 f.Close() 449 450 case command == "profile": // writes cpu and memory profile 451 // TODO enable profile over http rpc to enable better testing/tracking 452 cpufile, err := os.Create(filepath.Join(globals.GetDataDirectory(), "cpuprofile.prof")) 453 if err != nil { 454 globals.Logger.Warnf("Could not start cpu profiling, err %s", err) 455 continue 456 } 457 if err := pprof.StartCPUProfile(cpufile); err != nil { 458 globals.Logger.Warnf("could not start CPU profile: %s ", err) 459 } 460 globals.Logger.Infof("CPU profiling will be available after program shutsdown") 461 defer pprof.StopCPUProfile() 462 463 /* 464 memoryfile,err := os.Create(filepath.Join(globals.GetDataDirectory(), "memoryprofile.prof")) 465 if err != nil{ 466 globals.Logger.Warnf("Could not start memory profiling, err %s", err) 467 continue 468 } 469 if err := pprof.WriteHeapProfile(memoryfile); err != nil { 470 globals.Logger.Warnf("could not start memory profile: ", err) 471 } 472 memoryfile.Close() 473 */ 474 475 case command == "print_bc": 476 log.Info("printing block chain") 477 // first is starting point, second is ending point 478 start := int64(0) 479 stop := int64(0) 480 481 if len(line_parts) != 3 { 482 log.Warnf("This function requires 2 parameters, start and endpoint\n") 483 continue 484 } 485 if s, err := strconv.ParseInt(line_parts[1], 10, 64); err == nil { 486 start = s 487 } else { 488 log.Warnf("Invalid start value err %s", err) 489 continue 490 } 491 492 if s, err := strconv.ParseInt(line_parts[2], 10, 64); err == nil { 493 stop = s 494 } else { 495 log.Warnf("Invalid stop value err %s", err) 496 continue 497 } 498 499 if start < 0 || start > int64(chain.Load_TOPO_HEIGHT(nil)) { 500 log.Warnf("Start value should be be between 0 and current height\n") 501 continue 502 } 503 if start > stop || stop > int64(chain.Load_TOPO_HEIGHT(nil)) { 504 log.Warnf("Stop value should be > start and current height\n") 505 continue 506 507 } 508 509 log.Infof("Printing block chain from %d to %d\n", start, stop) 510 511 for i := start; i <= stop; i++ { 512 // get block id at height 513 current_block_id, err := chain.Load_Block_Topological_order_at_index(nil, i) 514 if err != nil { 515 log.Infof("Skipping block at height %d due to error %s\n", i, err) 516 continue 517 } 518 timestamp := chain.Load_Block_Timestamp(nil, current_block_id) 519 520 cdiff := chain.Load_Block_Cumulative_Difficulty(nil, current_block_id) 521 522 diff := chain.Load_Block_Difficulty(nil, current_block_id) 523 //size := chain. 524 525 log.Infof("topo height: %10d, height %d, timestamp: %10d, difficulty: %s cdiff: %s\n", i, chain.Load_Height_for_BL_ID(nil, current_block_id), timestamp, diff.String(), cdiff.String()) 526 527 log.Infof("Block Id: %s , \n", current_block_id) 528 log.Infof("") 529 530 } 531 case command == "mempool_print": 532 chain.Mempool.Mempool_Print() 533 534 case command == "mempool_flush": 535 chain.Mempool.Mempool_flush() 536 case command == "mempool_delete_tx": 537 if len(line_parts) == 2 && len(line_parts[1]) == 64 { 538 txid, err := hex.DecodeString(strings.ToLower(line_parts[1])) 539 if err != nil { 540 fmt.Printf("err while decoding txid err %s\n", err) 541 continue 542 } 543 var hash crypto.Hash 544 copy(hash[:32], []byte(txid)) 545 546 chain.Mempool.Mempool_Delete_TX(hash) 547 } else { 548 fmt.Printf("mempool_delete_tx needs a single transaction id as arugument\n") 549 } 550 case command == "version": 551 fmt.Printf("Version %s OS:%s ARCH:%s \n", config.Version.String(), runtime.GOOS, runtime.GOARCH) 552 553 case command == "start_mining_legacy": // it needs 2 parameters, one dero address, second number of threads 554 if mining { 555 fmt.Printf("Mining is already started\n") 556 continue 557 } 558 559 if globals.Arguments["--lowcpuram"].(bool) { 560 globals.Logger.Warnf("Mining is deactivated since daemon is running in low cpu mode, please check program options.") 561 continue 562 } 563 564 if globals.Arguments["--sync-node"].(bool) { 565 globals.Logger.Warnf("Mining is deactivated since daemon is running with --sync-mode, please check program options.") 566 continue 567 } 568 569 if len(line_parts) != 3 { 570 fmt.Printf("This function requires 2 parameters 1) dero address 2) number of threads\n") 571 continue 572 } 573 574 addr, err := globals.ParseValidateAddress(line_parts[1]) 575 if err != nil { 576 globals.Logger.Warnf("Mining address is invalid: err %s", err) 577 continue 578 } 579 580 thread_count := 0 581 if s, err := strconv.Atoi(line_parts[2]); err == nil { 582 //fmt.Printf("%T, %v", s, s) 583 thread_count = s 584 585 } else { 586 globals.Logger.Warnf("Mining threads argument cannot be parsed: err %s", err) 587 continue 588 } 589 590 if thread_count > runtime.GOMAXPROCS(0) { 591 globals.Logger.Warnf("Mining threads (%d) is more than available CPUs (%d). This is NOT optimal", thread_count, runtime.GOMAXPROCS(0)) 592 593 } 594 595 go start_miner(chain, addr, thread_count) 596 fmt.Printf("Mining started for %s on %d threads", addr, thread_count) 597 598 case command == "stop_mining": 599 if mining == true { 600 fmt.Printf("mining stopped\n") 601 } 602 mining = false 603 604 case command == "print_tree": // prints entire block chain tree 605 //WriteBlockChainTree(chain, "/tmp/graph.dot") 606 607 case command == "print_block": 608 fmt.Printf("printing block\n") 609 if len(line_parts) == 2 && len(line_parts[1]) == 64 { 610 bl_raw, err := hex.DecodeString(strings.ToLower(line_parts[1])) 611 612 if err != nil { 613 fmt.Printf("err while decoding txid err %s\n", err) 614 continue 615 } 616 var hash crypto.Hash 617 copy(hash[:32], []byte(bl_raw)) 618 619 bl, err := chain.Load_BL_FROM_ID(nil, hash) 620 if err == nil { 621 fmt.Printf("Block ID : %s\n", hash) 622 fmt.Printf("Block : %x\n", bl.Serialize()) 623 fmt.Printf("difficulty: %s\n", chain.Load_Block_Difficulty(nil, hash).String()) 624 fmt.Printf("cdifficulty: %s\n", chain.Load_Block_Cumulative_Difficulty(nil, hash).String()) 625 fmt.Printf("PoW: %s\n", bl.GetPoWHash()) 626 //fmt.Printf("Orphan: %v\n",chain.Is_Block_Orphan(hash)) 627 628 json_bytes, err := json.Marshal(bl) 629 630 fmt.Printf("%s err : %s\n", string(prettyprint_json(json_bytes)), err) 631 } else { 632 fmt.Printf("Err %s\n", err) 633 } 634 } else if len(line_parts) == 2 { 635 if s, err := strconv.ParseInt(line_parts[1], 10, 64); err == nil { 636 _ = s 637 // first load block id from topo height 638 639 hash, err := chain.Load_Block_Topological_order_at_index(nil, s) 640 if err != nil { 641 fmt.Printf("Skipping block at topo height %d due to error %s\n", s, err) 642 continue 643 } 644 bl, err := chain.Load_BL_FROM_ID(nil, hash) 645 if err == nil { 646 fmt.Printf("Block ID : %s\n", hash) 647 fmt.Printf("Block : %x\n", bl.Serialize()) 648 fmt.Printf("difficulty: %s\n", chain.Load_Block_Difficulty(nil, hash).String()) 649 fmt.Printf("cdifficulty: %s\n", chain.Load_Block_Cumulative_Difficulty(nil, hash).String()) 650 fmt.Printf("Height: %d\n", chain.Load_Height_for_BL_ID(nil, hash)) 651 fmt.Printf("TopoHeight: %d\n", s) 652 653 fmt.Printf("PoW: %s\n", bl.GetPoWHash()) 654 //fmt.Printf("Orphan: %v\n",chain.Is_Block_Orphan(hash)) 655 656 json_bytes, err := json.Marshal(bl) 657 658 fmt.Printf("%s err : %s\n", string(prettyprint_json(json_bytes)), err) 659 } else { 660 fmt.Printf("Err %s\n", err) 661 } 662 663 } else { 664 fmt.Printf("print_block needs a single transaction id as arugument\n") 665 } 666 } 667 668 // can be used to debug/deserialize blocks 669 // it can be used for blocks not in chain 670 case command == "parse_block": 671 if len(line_parts) != 2 { 672 globals.Logger.Warnf("parse_block needs a block in hex format") 673 continue 674 } 675 676 block_raw, err := hex.DecodeString(strings.ToLower(line_parts[1])) 677 if err != nil { 678 fmt.Printf("err while hex decoding block err %s\n", err) 679 continue 680 } 681 682 var bl block.Block 683 err = bl.Deserialize(block_raw) 684 if err != nil { 685 globals.Logger.Warnf("Error deserializing block err %s", err) 686 continue 687 } 688 689 // decode and print block as much as possible 690 fmt.Printf("Block ID : %s\n", bl.GetHash()) 691 fmt.Printf("PoW: %s\n", bl.GetPoWHash()) // block height 692 fmt.Printf("Height: %d\n", bl.Miner_TX.Vin[0].(transaction.Txin_gen).Height) 693 tips_found := true 694 for i := range bl.Tips { 695 _, err := chain.Load_BL_FROM_ID(nil, bl.Tips[i]) 696 if err != nil { 697 fmt.Printf("Tips %s not in our DB", bl.Tips[i]) 698 tips_found = false 699 break 700 } 701 } 702 fmt.Printf("Tips: %d %+v\n", len(bl.Tips), bl.Tips) // block height 703 fmt.Printf("Txs: %d %+v\n", len(bl.Tx_hashes), bl.Tx_hashes) // block height 704 expected_difficulty := new(big.Int).SetUint64(0) 705 if tips_found { // we can solve diffculty 706 expected_difficulty = chain.Get_Difficulty_At_Tips(nil, bl.Tips) 707 fmt.Printf("Difficulty: %s\n", expected_difficulty.String()) 708 709 powsuccess := chain.VerifyPoW(nil, &bl) 710 fmt.Printf("PoW verification %+v\n", powsuccess) 711 712 PoW := bl.GetPoWHash() 713 for i := expected_difficulty.Uint64(); i >= 1; i-- { 714 if blockchain.CheckPowHashBig(PoW, new(big.Int).SetUint64(i)) == true { 715 fmt.Printf("Block actually has max Difficulty: %d\n", i) 716 break 717 } 718 } 719 720 } else { // difficulty cann not solved 721 722 } 723 724 case command == "print_tx": 725 726 if len(line_parts) == 2 && len(line_parts[1]) == 64 { 727 txid, err := hex.DecodeString(strings.ToLower(line_parts[1])) 728 729 if err != nil { 730 fmt.Printf("err while decoding txid err %s\n", err) 731 continue 732 } 733 var hash crypto.Hash 734 copy(hash[:32], []byte(txid)) 735 736 tx, err := chain.Load_TX_FROM_ID(nil, hash) 737 if err == nil { 738 //s_bytes := tx.Serialize() 739 //fmt.Printf("tx : %x\n", s_bytes) 740 json_bytes, err := json.MarshalIndent(tx, "", " ") 741 _ = err 742 fmt.Printf("%s\n", string(json_bytes)) 743 744 //tx.RctSignature.Message = ringct.Key(tx.GetPrefixHash()) 745 //ringct.Get_pre_mlsag_hash(tx.RctSignature) 746 //chain.Expand_Transaction_v2(tx) 747 748 } else { 749 fmt.Printf("Err %s\n", err) 750 } 751 } else { 752 fmt.Printf("print_tx needs a single transaction id as arugument\n") 753 } 754 case command == "dev_verify_pool": // verifies and discards any tx which cannot be verified 755 tx_list := chain.Mempool.Mempool_List_TX() 756 for i := range tx_list { // check tx for nay double spend 757 tx := chain.Mempool.Mempool_Get_TX(tx_list[i]) 758 if tx != nil { 759 if !chain.Verify_Transaction_NonCoinbase_DoubleSpend_Check(nil, tx) { 760 fmt.Printf("TX %s is double spended, this TX should not be in pool", tx_list[i]) 761 chain.Mempool.Mempool_Delete_TX(tx_list[i]) 762 } 763 } 764 } 765 766 case strings.ToLower(line) == "diff": 767 fmt.Printf("Network %s BH %d, Diff %d, NW Hashrate %0.03f MH/sec TH %s\n", globals.Config.Name, chain.Get_Height(), chain.Get_Difficulty(), float64(chain.Get_Network_HashRate())/1000000.0, chain.Get_Top_ID()) 768 769 case strings.ToLower(line) == "status": 770 // fmt.Printf("chain diff %d\n",chain.Get_Difficulty_At_Block(chain.Top_ID)) 771 //fmt.Printf("chain nw rate %d\n", chain.Get_Network_HashRate()) 772 inc, out := p2p.Peer_Direction_Count() 773 774 supply := chain.Load_Already_Generated_Coins_for_Topo_Index(nil, chain.Load_TOPO_HEIGHT(nil)) 775 776 if supply > (1000000 * 1000000000000) { 777 supply -= (1000000 * 1000000000000) // remove premine 778 } 779 fmt.Printf("Network %s Height %d NW Hashrate %0.03f MH/sec TH %s Peers %d inc, %d out MEMPOOL size %d Total Supply %s DERO \n", globals.Config.Name, chain.Get_Height(), float64(chain.Get_Network_HashRate())/1000000.0, chain.Get_Top_ID(), inc, out, len(chain.Mempool.Mempool_List_TX()), globals.FormatMoney(supply)) 780 781 // print hardfork status on second line 782 hf_state, _, _, threshold, version, votes, window := chain.Get_HF_info() 783 switch hf_state { 784 case 0: // voting in progress 785 locked := false 786 if window == 0 { 787 window = 1 788 } 789 if votes >= (threshold*100)/window { 790 locked = true 791 } 792 fmt.Printf("Hard-Fork v%d in-progress need %d/%d votes to lock in, votes: %d, LOCKED:%+v\n", version, ((threshold * 100) / window), window, votes, locked) 793 case 1: // daemon is old and needs updation 794 fmt.Printf("Please update this daemon to support Hard-Fork v%d\n", version) 795 case 2: // everything is okay 796 fmt.Printf("Hard-Fork v%d\n", version) 797 798 } 799 case strings.ToLower(line) == "peer_list": // print peer list 800 801 p2p.PeerList_Print() 802 803 case strings.ToLower(line) == "sync_info": // print active connections 804 805 p2p.Connection_Print() 806 case strings.ToLower(line) == "bye": 807 fallthrough 808 case strings.ToLower(line) == "exit": 809 fallthrough 810 case strings.ToLower(line) == "quit": 811 close(Exit_In_Progress) 812 goto exit 813 case strings.ToLower(line) == "graph": 814 blockchain.WriteBlockChainTree(chain, "/tmp/graph.dot") 815 816 case command == "pop": 817 818 switch len(line_parts) { 819 case 1: 820 chain.Rewind_Chain(1) 821 case 2: 822 pop_count := 0 823 if s, err := strconv.Atoi(line_parts[1]); err == nil { 824 //fmt.Printf("%T, %v", s, s) 825 pop_count = s 826 827 if chain.Rewind_Chain(int(pop_count)) { 828 globals.Logger.Infof("Rewind successful") 829 } else { 830 globals.Logger.Infof("Rewind failed") 831 } 832 833 } else { 834 fmt.Printf("POP needs argument n to pop this many blocks from the top\n") 835 } 836 837 default: 838 fmt.Printf("POP needs argument n to pop this many blocks from the top\n") 839 } 840 841 case command == "ban": 842 if len(line_parts) >= 4 || len(line_parts) == 1 { 843 fmt.Printf("IP address required to ban\n") 844 break 845 } 846 847 if len(line_parts) == 3 { // process ban time if provided 848 // if user provided a time, apply ban for specific time 849 if s, err := strconv.ParseInt(line_parts[2], 10, 64); err == nil && s >= 0 { 850 p2p.Ban_Address(line_parts[1], uint64(s)) 851 break 852 } else { 853 fmt.Printf("err parsing ban time (only positive number) %s", err) 854 break 855 } 856 } 857 858 err := p2p.Ban_Address(line_parts[1], 10*60) // default ban is 10 minutes 859 if err != nil { 860 fmt.Printf("err parsing address %s", err) 861 break 862 } 863 864 case command == "unban": 865 if len(line_parts) >= 3 || len(line_parts) == 1 { 866 fmt.Printf("IP address required to unban\n") 867 break 868 } 869 870 err := p2p.UnBan_Address(line_parts[1]) 871 if err != nil { 872 fmt.Printf("err unbanning %s, err = %s", line_parts[1], err) 873 } else { 874 fmt.Printf("unbann %s successful", line_parts[1]) 875 } 876 case command == "bans": 877 p2p.BanList_Print() // print ban list 878 879 case strings.ToLower(line) == "checkpoints": // save all knowns block id 880 881 var block_id crypto.Hash 882 checksums := "mainnet_checksums.dat" 883 if !globals.IsMainnet() { 884 checksums = "testnet_checksums.dat" 885 } 886 887 filename_checksums := filepath.Join(os.TempDir(), checksums) 888 889 fchecksum, err := os.Create(filename_checksums) 890 if err != nil { 891 globals.Logger.Warnf("error creating new file %s", err) 892 continue 893 } 894 895 wchecksums := bufio.NewWriter(fchecksum) 896 897 chain.Lock() // we do not want any reorgs during this op 898 height := chain.Load_TOPO_HEIGHT(nil) 899 for i := int64(0); i <= height; i++ { 900 901 block_id, err = chain.Load_Block_Topological_order_at_index(nil, i) 902 if err != nil { 903 break 904 } 905 906 // calculate sha1 of file 907 h := sha3.New256() 908 bl, err := chain.Load_BL_FROM_ID(nil, block_id) 909 if err == nil { 910 h.Write(bl.Serialize()) // write serialized block 911 } else { 912 break 913 } 914 for j := range bl.Tx_hashes { 915 tx, err := chain.Load_TX_FROM_ID(nil, bl.Tx_hashes[j]) 916 if err == nil { 917 h.Write(tx.Serialize()) // write serialized transaction 918 } else { 919 break 920 } 921 } 922 if err != nil { 923 break 924 } 925 926 wchecksums.Write(h.Sum(nil)) // write sha3 256 sum 927 928 } 929 if err != nil { 930 globals.Logger.Warnf("error writing checkpoints err: %s", err) 931 } else { 932 globals.Logger.Infof("Successfully wrote %d checksums to file %s", height, filename_checksums) 933 } 934 935 wchecksums.Flush() 936 fchecksum.Close() 937 938 chain.Unlock() 939 case line == "sleep": 940 log.Println("sleep 4 second") 941 time.Sleep(4 * time.Second) 942 case line == "": 943 default: 944 log.Println("you said:", strconv.Quote(line)) 945 } 946 } 947 exit: 948 949 globals.Logger.Infof("Exit in Progress, Please wait") 950 time.Sleep(100 * time.Millisecond) // give prompt update time to finish 951 952 rpc.RPCServer_Stop() 953 p2p.P2P_Shutdown() // shutdown p2p subsystem 954 chain.Shutdown() // shutdown chain subsysem 955 956 for globals.Subsystem_Active > 0 { 957 time.Sleep(100 * time.Millisecond) 958 } 959 } 960 961 func prettyprint_json(b []byte) []byte { 962 var out bytes.Buffer 963 err := json.Indent(&out, b, "", " ") 964 _ = err 965 return out.Bytes() 966 } 967 968 func usage(w io.Writer) { 969 io.WriteString(w, "commands:\n") 970 //io.WriteString(w, completer.Tree(" ")) 971 io.WriteString(w, "\t\033[1mhelp\033[0m\t\tthis help\n") 972 io.WriteString(w, "\t\033[1mdiff\033[0m\t\tShow difficulty\n") 973 io.WriteString(w, "\t\033[1mprint_bc\033[0m\tPrint blockchain info in a given blocks range, print_bc <begin_height> <end_height>\n") 974 io.WriteString(w, "\t\033[1mprint_block\033[0m\tPrint block, print_block <block_hash> or <block_height>\n") 975 io.WriteString(w, "\t\033[1mprint_height\033[0m\tPrint local blockchain height\n") 976 io.WriteString(w, "\t\033[1mprint_tx\033[0m\tPrint transaction, print_tx <transaction_hash>\n") 977 io.WriteString(w, "\t\033[1mstatus\033[0m\t\tShow general information\n") 978 // io.WriteString(w, "\t\033[1mstart_mining\033[0m\tStart mining <dero address> <number of threads>\n") 979 io.WriteString(w, "\t\033[1mstop_mining\033[0m\tStop daemon mining\n") 980 io.WriteString(w, "\t\033[1mpeer_list\033[0m\tPrint peer list\n") 981 io.WriteString(w, "\t\033[1msync_info\033[0m\tPrint information about connected peers and their state\n") 982 io.WriteString(w, "\t\033[1mbye\033[0m\t\tQuit the daemon\n") 983 io.WriteString(w, "\t\033[1mban\033[0m\t\tBan specific ip from making any connections\n") 984 io.WriteString(w, "\t\033[1munban\033[0m\t\tRevoke restrictions on previously banned ips\n") 985 io.WriteString(w, "\t\033[1mbans\033[0m\t\tPrint current ban list\n") 986 io.WriteString(w, "\t\033[1mversion\033[0m\t\tShow version\n") 987 io.WriteString(w, "\t\033[1mexit\033[0m\t\tQuit the daemon\n") 988 io.WriteString(w, "\t\033[1mquit\033[0m\t\tQuit the daemon\n") 989 990 } 991 992 var completer = readline.NewPrefixCompleter( 993 /* readline.PcItem("mode", 994 readline.PcItem("vi"), 995 readline.PcItem("emacs"), 996 ), 997 readline.PcItem("login"), 998 readline.PcItem("say", 999 readline.PcItem("hello"), 1000 readline.PcItem("bye"), 1001 ), 1002 readline.PcItem("setprompt"), 1003 readline.PcItem("setpassword"), 1004 readline.PcItem("bye"), 1005 */ 1006 readline.PcItem("help"), 1007 /* readline.PcItem("go", 1008 readline.PcItem("build", readline.PcItem("-o"), readline.PcItem("-v")), 1009 readline.PcItem("install", 1010 readline.PcItem("-v"), 1011 readline.PcItem("-vv"), 1012 readline.PcItem("-vvv"), 1013 ), 1014 readline.PcItem("test"), 1015 ), 1016 readline.PcItem("sleep"), 1017 */ 1018 readline.PcItem("diff"), 1019 // readline.PcItem("dev_verify_pool"), 1020 // readline.PcItem("dev_verify_chain_doublespend"), 1021 readline.PcItem("mempool_flush"), 1022 readline.PcItem("mempool_delete_tx"), 1023 readline.PcItem("mempool_print"), 1024 readline.PcItem("peer_list"), 1025 readline.PcItem("print_bc"), 1026 readline.PcItem("print_block"), 1027 readline.PcItem("print_height"), 1028 readline.PcItem("print_tx"), 1029 readline.PcItem("status"), 1030 // readline.PcItem("start_mining"), 1031 // readline.PcItem("stop_mining"), 1032 readline.PcItem("sync_info"), 1033 readline.PcItem("version"), 1034 readline.PcItem("bye"), 1035 readline.PcItem("exit"), 1036 readline.PcItem("quit"), 1037 ) 1038 1039 func filterInput(r rune) (rune, bool) { 1040 switch r { 1041 // block CtrlZ feature 1042 case readline.CharCtrlZ: 1043 return r, false 1044 } 1045 return r, true 1046 }