github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/client/main.go (about)

     1  package main
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"os/signal"
     9  	"runtime/debug"
    10  	"syscall"
    11  	"time"
    12  	"unsafe"
    13  
    14  	"github.com/piotrnar/gocoin"
    15  	"github.com/piotrnar/gocoin/client/common"
    16  	"github.com/piotrnar/gocoin/client/network"
    17  	"github.com/piotrnar/gocoin/client/network/peersdb"
    18  	"github.com/piotrnar/gocoin/client/rpcapi"
    19  	"github.com/piotrnar/gocoin/client/usif"
    20  	"github.com/piotrnar/gocoin/client/usif/textui"
    21  	"github.com/piotrnar/gocoin/client/usif/webui"
    22  	"github.com/piotrnar/gocoin/client/wallet"
    23  	"github.com/piotrnar/gocoin/lib/btc"
    24  	"github.com/piotrnar/gocoin/lib/chain"
    25  	"github.com/piotrnar/gocoin/lib/others/qdb"
    26  	"github.com/piotrnar/gocoin/lib/others/sys"
    27  )
    28  
    29  var (
    30  	retryCachedBlocks bool
    31  	SaveBlockChain    *time.Timer = time.NewTimer(24 * time.Hour)
    32  
    33  	NetBlocksSize sys.SyncInt
    34  
    35  	exitat *uint = flag.Uint("exitat", 0, "Auto exit node after comitting block with the given height")
    36  )
    37  
    38  const (
    39  	SaveBlockChainAfter       = 2 * time.Second
    40  	SaveBlockChainAfterNoSync = 10 * time.Minute
    41  )
    42  
    43  func reset_save_timer() {
    44  	SaveBlockChain.Stop()
    45  	for len(SaveBlockChain.C) > 0 {
    46  		<-SaveBlockChain.C
    47  	}
    48  	if common.BlockChainSynchronized {
    49  		SaveBlockChain.Reset(SaveBlockChainAfter)
    50  	} else {
    51  		SaveBlockChain.Reset(SaveBlockChainAfterNoSync)
    52  	}
    53  }
    54  
    55  func blockMined(bl *btc.Block) {
    56  	network.BlockMined(bl)
    57  	if int(bl.LastKnownHeight)-int(bl.Height) < 144 { // do not run it when syncing chain
    58  		usif.ProcessBlockFees(bl.Height, bl)
    59  	}
    60  }
    61  
    62  func blockUndone(bl *btc.Block) {
    63  	network.BlockUndone(bl)
    64  }
    65  
    66  func exit_now() {
    67  	al, sy := sys.MemUsed()
    68  	cb, _ := common.MemUsed()
    69  	fmt.Printf("Sync to %d took %s  -  %.0f min.  Mem: %d %d %d MB  - cachempty: %d\n",
    70  		common.Last.Block.Height, time.Since(common.StartTime).String(),
    71  		float64(time.Since(common.StartTime))/float64(time.Minute), al>>20, sy>>20, cb>>20,
    72  		network.Fetch.CacheEmpty)
    73  	fmt.Printf("Wasted %dMB from %d blocks.  Max cache used: %d / %dMB\n",
    74  		network.Fetch.BlockBytesWasted>>20, network.Fetch.BlockSameRcvd,
    75  		network.MaxCachedBlocksSize.Get()>>20, common.SyncMaxCacheBytes.Get()>>20)
    76  	common.PrintBWStats()
    77  	fmt.Print("Reached given block ", *exitat, ". Now exiting....\n\n\n\n")
    78  	os.Exit(0)
    79  }
    80  
    81  func LocalAcceptBlock(newbl *network.BlockRcvd) (e error) {
    82  	bl := newbl.Block
    83  	if common.FLAG.TrustAll || newbl.BlockTreeNode.Trusted.Get() {
    84  		bl.Trusted.Set()
    85  	}
    86  
    87  	common.BlockChain.Unspent.AbortWriting() // abort saving of UTXO.db
    88  	common.BlockChain.Blocks.BlockAdd(newbl.BlockTreeNode.Height, bl)
    89  	newbl.TmQueue = time.Now()
    90  
    91  	if newbl.DoInvs {
    92  		common.Busy()
    93  		network.NetRouteInv(network.MSG_BLOCK, bl.Hash, newbl.Conn)
    94  	}
    95  
    96  	network.MutexRcv.Lock()
    97  	bl.LastKnownHeight = network.LastCommitedHeader.Height
    98  	network.MutexRcv.Unlock()
    99  	e = common.BlockChain.CommitBlock(bl, newbl.BlockTreeNode)
   100  	if bl.LastKnownHeight-bl.Height > common.GetUint32(&common.CFG.Memory.MaxCachedBlks) {
   101  		bl.Txs = nil // we won't be needing bl.Txs anymore, so might as well mark the memory as unused
   102  	}
   103  
   104  	if e == nil {
   105  		// new block accepted
   106  		newbl.TmAccepted = time.Now()
   107  
   108  		newbl.NonWitnessSize = bl.NoWitnessSize
   109  		newbl.TheWeight = bl.BlockWeight
   110  		newbl.ThePaidVSize = bl.PaidTxsVSize
   111  		newbl.TheOrdCnt = bl.OrbTxCnt
   112  		newbl.TheOrdSize = bl.OrbTxSize
   113  		newbl.TheOrdWeight = bl.OrbTxWeight
   114  
   115  		common.RecalcAverageBlockSize()
   116  
   117  		common.Last.Mutex.Lock()
   118  		common.Last.Time = time.Now()
   119  		common.Last.Block = common.BlockChain.LastBlock()
   120  		common.UpdateScriptFlags(bl.VerifyFlags)
   121  
   122  		if common.Last.ParseTill != nil && (common.Last.Block.Height%100e3) == 0 {
   123  			println("Parsing to", common.Last.Block.Height, "took", time.Since(newbl.TmStart).String())
   124  		}
   125  
   126  		if common.Last.ParseTill != nil && common.Last.Block == common.Last.ParseTill {
   127  			println("Initial parsing finished in", time.Since(newbl.TmStart).String())
   128  			common.Last.ParseTill = nil
   129  		}
   130  		if common.Last.ParseTill == nil && !common.BlockChainSynchronized &&
   131  			((common.Last.Block.Height%50e3) == 0 || common.Last.Block.Height == network.LastCommitedHeader.Height) {
   132  			al, sy := sys.MemUsed()
   133  			cb, _ := common.MemUsed()
   134  			fmt.Printf("Sync to %d took %s  -  %.1f min.  Mem: %d %d %d MB  - cachempty: %d\n",
   135  				common.Last.Block.Height, time.Since(common.StartTime).String(),
   136  				float64(time.Since(common.StartTime))/float64(time.Minute), al>>20, sy>>20, cb>>20,
   137  				network.Fetch.CacheEmpty)
   138  			if common.Last.Block.Height <= 200e3 {
   139  				// Cache underflow counter is not reliable at the beginning of chain sync,s o reset it here
   140  				network.Fetch.CacheEmpty = 0
   141  			}
   142  		}
   143  		if *exitat != 0 && uint(common.Last.Block.Height) == *exitat {
   144  			exit_now()
   145  		}
   146  		common.Last.Mutex.Unlock()
   147  	} else {
   148  		//fmt.Println("Warning: AcceptBlock failed. If the block was valid, you may need to rebuild the unspent DB (-r)")
   149  		new_end := common.BlockChain.LastBlock()
   150  		common.Last.Mutex.Lock()
   151  		common.Last.Block = new_end
   152  		common.UpdateScriptFlags(bl.VerifyFlags)
   153  		common.Last.Mutex.Unlock()
   154  		// update network.LastCommitedHeader
   155  		network.MutexRcv.Lock()
   156  		prev_last_header := network.LastCommitedHeader
   157  		network.DiscardBlock(newbl.BlockTreeNode) // this function can also modify network.LastCommitedHeader
   158  		if common.Last.Block.Height > network.LastCommitedHeader.Height {
   159  			network.LastCommitedHeader, _ = common.Last.Block.FindFarthestNode()
   160  		}
   161  		need_more_headers := prev_last_header != network.LastCommitedHeader
   162  		network.MutexRcv.Unlock()
   163  		if need_more_headers {
   164  			//println("LastCommitedHeader moved to", network.LastCommitedHeader.Height)
   165  			network.GetMoreHeaders()
   166  		}
   167  	}
   168  	reset_save_timer()
   169  	return
   170  }
   171  
   172  func retry_cached_blocks() bool {
   173  	var idx int
   174  	common.CountSafe("RedoCachedBlks")
   175  	for idx < len(network.CachedBlocks) {
   176  		newbl := network.CachedBlocks[idx]
   177  		if CheckParentDiscarded(newbl.BlockTreeNode) {
   178  			common.CountSafe("DiscardCachedBlock")
   179  			if newbl.Block == nil {
   180  				os.Remove(common.TempBlocksDir() + newbl.BlockTreeNode.BlockHash.String())
   181  			}
   182  			network.CachedBlocksDel(idx)
   183  			return len(network.CachedBlocks) > 0
   184  		}
   185  		if common.BlockChain.HasAllParents(newbl.BlockTreeNode) {
   186  			common.Busy()
   187  
   188  			if newbl.Block == nil {
   189  				tmpfn := common.TempBlocksDir() + newbl.BlockTreeNode.BlockHash.String()
   190  				dat, e := ioutil.ReadFile(tmpfn)
   191  				os.Remove(tmpfn)
   192  				if e != nil {
   193  					panic(e.Error())
   194  				}
   195  				if newbl.Block, e = btc.NewBlock(dat); e != nil {
   196  					panic(e.Error())
   197  				}
   198  				if e = newbl.Block.BuildTxList(); e != nil {
   199  					panic(e.Error())
   200  				}
   201  				newbl.Block.BlockExtraInfo = *newbl.BlockExtraInfo
   202  			}
   203  
   204  			e := LocalAcceptBlock(newbl)
   205  			if e != nil {
   206  				fmt.Println("AcceptBlock2", newbl.BlockTreeNode.BlockHash.String(), "-", e.Error())
   207  				newbl.Conn.Misbehave("LocalAcceptBl2", 250)
   208  			}
   209  			if usif.Exit_now.Get() {
   210  				return false
   211  			}
   212  			// remove it from cache
   213  			network.CachedBlocksDel(idx)
   214  			return len(network.CachedBlocks) > 0
   215  		} else {
   216  			idx++
   217  		}
   218  	}
   219  	return false
   220  }
   221  
   222  // CheckParentDiscarded returns true if the block's parent is on the DiscardedBlocks list.
   223  // Add it to DiscardedBlocks, if returning true.
   224  func CheckParentDiscarded(n *chain.BlockTreeNode) bool {
   225  	network.MutexRcv.Lock()
   226  	defer network.MutexRcv.Unlock()
   227  	if network.DiscardedBlocks[n.Parent.BlockHash.BIdx()] {
   228  		network.DiscardedBlocks[n.BlockHash.BIdx()] = true
   229  		return true
   230  	}
   231  	return false
   232  }
   233  
   234  // HandleNetBlock is called from the blockchain thread.
   235  func HandleNetBlock(newbl *network.BlockRcvd) {
   236  	if common.Last.ParseTill != nil {
   237  		NetBlocksSize.Add(-len(newbl.Block.Raw))
   238  	}
   239  
   240  	defer func() {
   241  		common.CountSafe("MainNetBlock")
   242  		if common.GetUint32(&common.WalletOnIn) > 0 {
   243  			common.SetUint32(&common.WalletOnIn, 5) // snooze the timer to 5 seconds from now
   244  		}
   245  	}()
   246  
   247  	if CheckParentDiscarded(newbl.BlockTreeNode) {
   248  		common.CountSafe("DiscardFreshBlockA")
   249  		if newbl.Block == nil {
   250  			os.Remove(common.TempBlocksDir() + newbl.BlockTreeNode.BlockHash.String())
   251  		}
   252  		retryCachedBlocks = len(network.CachedBlocks) > 0
   253  		return
   254  	}
   255  
   256  	if !common.BlockChain.HasAllParents(newbl.BlockTreeNode) {
   257  		// it's not linking - keep it for later
   258  		network.CachedBlocksAdd(newbl)
   259  		common.CountSafe("BlockPostone")
   260  		return
   261  	}
   262  
   263  	if newbl.Block == nil {
   264  		tmpfn := common.TempBlocksDir() + newbl.BlockTreeNode.BlockHash.String()
   265  		dat, e := ioutil.ReadFile(tmpfn)
   266  		os.Remove(tmpfn)
   267  		if e != nil {
   268  			panic(e.Error())
   269  		}
   270  		if newbl.Block, e = btc.NewBlock(dat); e != nil {
   271  			panic(e.Error())
   272  		}
   273  		if e = newbl.Block.BuildTxList(); e != nil {
   274  			panic(e.Error())
   275  		}
   276  		newbl.Block.BlockExtraInfo = *newbl.BlockExtraInfo
   277  	}
   278  
   279  	common.Busy()
   280  	if e := LocalAcceptBlock(newbl); e != nil {
   281  		common.CountSafe("DiscardFreshBlockB")
   282  		fmt.Println("AcceptBlock1", newbl.Block.Hash.String(), "-", e.Error())
   283  		newbl.Conn.Misbehave("LocalAcceptBl1", 250)
   284  	}
   285  	retryCachedBlocks = retry_cached_blocks()
   286  	if !retryCachedBlocks && network.BlocksToGetCnt() != 0 {
   287  		now := time.Now()
   288  		if network.Fetch.LastCacheEmpty.IsZero() || now.Sub(network.Fetch.LastCacheEmpty) >= time.Second {
   289  			network.Fetch.CacheEmpty++
   290  			network.Fetch.LastCacheEmpty = now
   291  		}
   292  	}
   293  }
   294  
   295  func HandleRpcBlock(msg *rpcapi.BlockSubmited) {
   296  	common.CountSafe("RPCNewBlock")
   297  
   298  	network.MutexRcv.Lock()
   299  	rb := network.ReceivedBlocks[msg.Block.Hash.BIdx()]
   300  	network.MutexRcv.Unlock()
   301  	if rb == nil {
   302  		panic("Block " + msg.Block.Hash.String() + " not in ReceivedBlocks map")
   303  	}
   304  
   305  	common.BlockChain.Unspent.AbortWriting()
   306  	rb.TmQueue = time.Now()
   307  
   308  	_, _, e := common.BlockChain.CheckBlock(msg.Block)
   309  	if e == nil {
   310  		e = common.BlockChain.AcceptBlock(msg.Block)
   311  		rb.TmAccepted = time.Now()
   312  	}
   313  	if e != nil {
   314  		common.CountSafe("RPCBlockError")
   315  		msg.Error = e.Error()
   316  		msg.Done.Done()
   317  		return
   318  	}
   319  
   320  	network.NetRouteInv(network.MSG_BLOCK, msg.Block.Hash, nil)
   321  	common.RecalcAverageBlockSize()
   322  
   323  	common.CountSafe("RPCBlockOK")
   324  	println("New mined block", msg.Block.Height, "accepted OK in", rb.TmAccepted.Sub(rb.TmQueue).String())
   325  
   326  	common.Last.Mutex.Lock()
   327  	common.Last.Time = time.Now()
   328  	common.Last.Block = common.BlockChain.LastBlock()
   329  	common.UpdateScriptFlags(msg.VerifyFlags)
   330  	common.Last.Mutex.Unlock()
   331  
   332  	msg.Done.Done()
   333  }
   334  
   335  func do_the_blocks(end *chain.BlockTreeNode) {
   336  	sta := time.Now()
   337  	last := common.BlockChain.LastBlock()
   338  	for last != end {
   339  		nxt := last.FindPathTo(end)
   340  		if nxt == nil {
   341  			break
   342  		}
   343  
   344  		if nxt.BlockSize == 0 {
   345  			println("BlockSize is zero - corrupt database")
   346  			break
   347  		}
   348  
   349  		pre := time.Now()
   350  		crec, trusted, _ := common.BlockChain.Blocks.BlockGetInternal(nxt.BlockHash, true)
   351  		if crec == nil || crec.Data == nil {
   352  			panic(fmt.Sprint("No data for block #", nxt.Height, " ", nxt.BlockHash.String()))
   353  		}
   354  
   355  		bl, er := btc.NewBlock(crec.Data)
   356  		if er != nil {
   357  			println("btc.NewBlock() error - corrupt database")
   358  			break
   359  		}
   360  		bl.Height = nxt.Height
   361  
   362  		// Recover the flags to be used when verifying scripts for non-trusted blocks (stored orphaned blocks)
   363  		common.BlockChain.ApplyBlockFlags(bl)
   364  
   365  		er = bl.BuildTxList()
   366  		if er != nil {
   367  			println("bl.BuildTxList() error - corrupt database")
   368  			break
   369  		}
   370  
   371  		bl.Trusted.Store(trusted)
   372  
   373  		tdl := time.Now()
   374  
   375  		rb := &network.OneReceivedBlock{TmStart: sta, TmPreproc: pre, TmDownload: tdl}
   376  		network.MutexRcv.Lock()
   377  		network.ReceivedBlocks[bl.Hash.BIdx()] = rb
   378  		network.MutexRcv.Unlock()
   379  
   380  		network.NetBlocks <- &network.BlockRcvd{Conn: nil, Block: bl, BlockTreeNode: nxt,
   381  			OneReceivedBlock: rb, BlockExtraInfo: nil}
   382  
   383  		NetBlocksSize.Add(len(bl.Raw))
   384  		for NetBlocksSize.Get() > 64*1024*1024 {
   385  			time.Sleep(100 * time.Millisecond)
   386  		}
   387  
   388  		last = nxt
   389  
   390  	}
   391  	//println("all blocks queued", len(network.NetBlocks))
   392  }
   393  
   394  func main() {
   395  	var ptr *byte
   396  	if unsafe.Sizeof(ptr) < 8 {
   397  		fmt.Println("WARNING: Gocoin client shall be build for 64-bit arch. It will likely crash now.")
   398  	}
   399  
   400  	fmt.Println("Gocoin client version", gocoin.Version)
   401  
   402  	// Disable Ctrl+C
   403  	signal.Notify(common.KillChan, os.Interrupt, syscall.SIGTERM)
   404  	defer func() {
   405  		if r := recover(); r != nil {
   406  			err, ok := r.(error)
   407  			if !ok {
   408  				err = fmt.Errorf("pkg: %v", r)
   409  			}
   410  			fmt.Println("main panic recovered:", err.Error())
   411  			fmt.Println(string(debug.Stack()))
   412  			network.NetCloseAll()
   413  			common.CloseBlockChain()
   414  			peersdb.ClosePeerDB()
   415  			sys.UnlockDatabaseDir()
   416  			os.Exit(1)
   417  		}
   418  	}()
   419  
   420  	common.InitConfig()
   421  
   422  	if common.FLAG.SaveConfig {
   423  		common.SaveConfig()
   424  		fmt.Println("Configuration file saved")
   425  		os.Exit(0)
   426  	}
   427  
   428  	if common.FLAG.VolatileUTXO {
   429  		fmt.Println("WARNING! Using UTXO database in a volatile mode. Make sure to close the client properly (do not kill it!)")
   430  	}
   431  
   432  	if common.FLAG.TrustAll {
   433  		fmt.Println("WARNING! Assuming all scripts inside new blocks to PASS. Verify the last block's hash when finished.")
   434  	}
   435  
   436  	host_init() // This will create the DB lock file and keep it open
   437  
   438  	os.RemoveAll(common.TempBlocksDir())
   439  	common.MkTempBlocksDir()
   440  
   441  	if common.FLAG.UndoBlocks > 0 {
   442  		usif.Exit_now.Set()
   443  	}
   444  
   445  	if common.FLAG.Rescan && common.FLAG.VolatileUTXO {
   446  
   447  		fmt.Println("UTXO database rebuilt complete in the volatile mode, so flush DB to disk and exit...")
   448  
   449  	} else if !usif.Exit_now.Get() {
   450  
   451  		common.RecalcAverageBlockSize()
   452  
   453  		peersTick := time.Tick(peersdb.ExpirePeersPeriod)
   454  		netTick := time.Tick(time.Second)
   455  
   456  		reset_save_timer() // we wil do one save try after loading, in case if ther was a rescan
   457  
   458  		peersdb.Testnet = common.Testnet
   459  		peersdb.ConnectOnly = common.CFG.ConnectOnly
   460  		peersdb.Services = common.Services
   461  		peersdb.InitPeers(common.GocoinHomeDir)
   462  		if common.FLAG.UnbanAllPeers {
   463  			var keys []qdb.KeyType
   464  			var vals [][]byte
   465  			peersdb.PeerDB.Browse(func(k qdb.KeyType, v []byte) uint32 {
   466  				peer := peersdb.NewPeer(v)
   467  				if peer.Banned != 0 {
   468  					fmt.Println("Unban", peer.NetAddr.String())
   469  					peer.Banned = 0
   470  					keys = append(keys, k)
   471  					vals = append(vals, peer.Bytes())
   472  				}
   473  				return 0
   474  			})
   475  			for i := range keys {
   476  				peersdb.PeerDB.Put(keys[i], vals[i])
   477  			}
   478  
   479  			fmt.Println(len(keys), "peers un-baned")
   480  		}
   481  
   482  		for k, v := range common.BlockChain.BlockIndex {
   483  			network.ReceivedBlocks[k] = &network.OneReceivedBlock{TmStart: time.Unix(int64(v.Timestamp()), 0)}
   484  		}
   485  
   486  		if common.Last.ParseTill != nil {
   487  			network.LastCommitedHeader = common.Last.ParseTill
   488  			println("Hold on network for now as we have",
   489  				common.Last.ParseTill.Height-common.Last.Block.Height, "new blocks on disk.")
   490  			go do_the_blocks(common.Last.ParseTill)
   491  		} else {
   492  			network.LastCommitedHeader = common.Last.Block
   493  		}
   494  
   495  		if common.CFG.TXPool.SaveOnDisk {
   496  			network.MempoolLoad2()
   497  		}
   498  
   499  		if common.CFG.TextUI_Enabled {
   500  			go textui.MainThread()
   501  		}
   502  
   503  		if common.CFG.WebUI.Interface != "" {
   504  			go webui.ServerThread()
   505  		}
   506  
   507  		if common.CFG.RPC.Enabled {
   508  			go rpcapi.StartServer(common.RPCPort())
   509  		}
   510  
   511  		usif.LoadBlockFees()
   512  
   513  		wallet.FetchingBalanceTick = func() bool {
   514  			select {
   515  			case rec := <-usif.LocksChan:
   516  				common.CountSafe("DoMainLocks")
   517  				rec.In.Done()
   518  				rec.Out.Wait()
   519  
   520  			case newtx := <-network.NetTxs:
   521  				common.CountSafe("DoMainNetTx")
   522  				network.HandleNetTx(newtx, false)
   523  
   524  			case <-netTick:
   525  				common.CountSafe("DoMainNetTick")
   526  				if common.Last.ParseTill != nil {
   527  					break
   528  				}
   529  				network.NetworkTick()
   530  
   531  			case on := <-wallet.OnOff:
   532  				if !on {
   533  					return true
   534  				}
   535  
   536  			default:
   537  			}
   538  			return usif.Exit_now.Get()
   539  		}
   540  
   541  		startup_ticks := 5 // give 5 seconds for finding out missing blocks
   542  		if !common.FLAG.NoWallet {
   543  			// snooze the timer to 10 seconds after startup_ticks goes down
   544  			common.SetUint32(&common.WalletOnIn, 10)
   545  		}
   546  
   547  		for !usif.Exit_now.Get() {
   548  			common.Busy()
   549  
   550  			common.CountSafe("MainThreadLoops")
   551  			for retryCachedBlocks {
   552  				retryCachedBlocks = retry_cached_blocks()
   553  				if !retryCachedBlocks && network.BlocksToGetCnt() != 0 {
   554  					now := time.Now()
   555  					if network.Fetch.LastCacheEmpty.IsZero() || now.Sub(network.Fetch.LastCacheEmpty) >= time.Second {
   556  						network.Fetch.CacheEmpty++
   557  						network.Fetch.LastCacheEmpty = now
   558  					}
   559  				}
   560  				// We have done one per loop - now do something else if pending...
   561  				if len(network.NetBlocks) > 0 || len(usif.UiChannel) > 0 {
   562  					break
   563  				}
   564  			}
   565  
   566  			// first check for priority messages; kill signal or a new block
   567  			select {
   568  			case <-common.KillChan:
   569  				common.Busy()
   570  				usif.Exit_now.Set()
   571  				continue
   572  
   573  			case newbl := <-network.NetBlocks:
   574  				common.Busy()
   575  				HandleNetBlock(newbl)
   576  
   577  			case rpcbl := <-rpcapi.RpcBlocks:
   578  				common.Busy()
   579  				HandleRpcBlock(rpcbl)
   580  
   581  			default: // timeout immediatelly if no priority message
   582  			}
   583  
   584  			common.Busy()
   585  
   586  			select {
   587  			case <-common.KillChan:
   588  				common.Busy()
   589  				usif.Exit_now.Set()
   590  				continue
   591  
   592  			case newbl := <-network.NetBlocks:
   593  				common.Busy()
   594  				HandleNetBlock(newbl)
   595  
   596  			case rpcbl := <-rpcapi.RpcBlocks:
   597  				common.Busy()
   598  				HandleRpcBlock(rpcbl)
   599  
   600  			case rec := <-usif.LocksChan:
   601  				common.Busy()
   602  				common.CountSafe("MainLocks")
   603  				rec.In.Done()
   604  				rec.Out.Wait()
   605  
   606  			case <-SaveBlockChain.C:
   607  				common.Busy()
   608  				common.CountSafe("SaveBlockChain")
   609  				if common.BlockChain.Idle() {
   610  					common.CountSafe("ChainIdleUsed")
   611  				}
   612  
   613  			case newtx := <-network.NetTxs:
   614  				common.Busy()
   615  				common.CountSafe("MainNetTx")
   616  				network.HandleNetTx(newtx, false)
   617  
   618  			case <-netTick:
   619  				common.Busy()
   620  				common.CountSafe("MainNetTick")
   621  				if common.Last.ParseTill != nil {
   622  					break
   623  				}
   624  				network.NetworkTick()
   625  
   626  				if common.BlockChainSynchronized {
   627  					if common.WalletPendingTick() {
   628  						wallet.OnOff <- true
   629  					}
   630  					break // BlockChainSynchronized so never mind checking it
   631  				}
   632  
   633  				// Now check if the chain is synchronized...
   634  				if (network.HeadersReceived.Get() > int(common.GetUint32(&common.CFG.Net.MaxOutCons)/2) ||
   635  					peersdb.ConnectOnly != "" && network.HeadersReceived.Get() >= 1) &&
   636  					network.BlocksToGetCnt() == 0 && len(network.NetBlocks) == 0 &&
   637  					len(network.CachedBlocks) == 0 {
   638  					// only when we have no pending blocks and rteceived header messages, startup_ticks can go down..
   639  					if startup_ticks > 0 {
   640  						startup_ticks--
   641  						break
   642  					}
   643  					common.SetBool(&common.BlockChainSynchronized, true)
   644  					if *exitat == 99999999 {
   645  						exit_now()
   646  					}
   647  					reset_save_timer()
   648  				} else {
   649  					startup_ticks = 5 // snooze by 5 seconds each time we're in here
   650  				}
   651  
   652  			case cmd := <-usif.UiChannel:
   653  				common.Busy()
   654  				common.CountSafe("MainUICmd")
   655  				cmd.Handler(cmd.Param)
   656  				cmd.Done.Done()
   657  
   658  			case <-peersTick:
   659  				common.Busy()
   660  				peersdb.ExpirePeers()
   661  				usif.ExpireBlockFees()
   662  
   663  			case on := <-wallet.OnOff:
   664  				common.Busy()
   665  				if on {
   666  					if common.BlockChainSynchronized {
   667  						usif.FetchingBalances.Set()
   668  						wallet.LoadBalance()
   669  						usif.FetchingBalances.Clr()
   670  					} else {
   671  						fmt.Println("Cannot enable wallet functionality with blockchain sync in progress")
   672  					}
   673  				} else {
   674  					wallet.Disable()
   675  					common.SetUint32(&common.WalletOnIn, 0)
   676  				}
   677  			}
   678  		}
   679  
   680  		common.BlockChain.Unspent.HurryUp()
   681  		wallet.UpdateMapSizes()
   682  		network.NetCloseAll()
   683  	}
   684  
   685  	sta := time.Now()
   686  	common.CloseBlockChain()
   687  	if common.FLAG.UndoBlocks == 0 {
   688  		network.MempoolSave(false)
   689  	}
   690  	fmt.Println("Blockchain closed in", time.Since(sta).String())
   691  	peersdb.ClosePeerDB()
   692  	usif.SaveBlockFees()
   693  	sys.UnlockDatabaseDir()
   694  	os.RemoveAll(common.TempBlocksDir())
   695  }