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  }