pktd.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. // Copyrgith © 2021 Jeffrey H. Johnson <trnsz@pobox.com>
  2. // Copyright © 2013-2016 The btcsuite developers
  3. //
  4. // Use of this source code is governed by an ISC
  5. // license that can be found in the LICENSE file.
  6. package main
  7. import (
  8. "fmt"
  9. "net"
  10. "net/http"
  11. _ "net/http/pprof"
  12. "os"
  13. "path/filepath"
  14. "runtime"
  15. "runtime/debug"
  16. "runtime/pprof"
  17. "time"
  18. "github.com/arl/statsviz"
  19. "github.com/pkt-cash/pktd/blockchain/indexers"
  20. "github.com/pkt-cash/pktd/btcutil/er"
  21. "github.com/pkt-cash/pktd/database"
  22. "github.com/pkt-cash/pktd/limits"
  23. "github.com/pkt-cash/pktd/pktconfig/version"
  24. "github.com/pkt-cash/pktd/pktlog/log"
  25. pktdLegal "go4.org/legal"
  26. )
  27. const (
  28. // blockDbNamePrefix is the prefix for the block database name. The
  29. // database type is appended to this value to form the full block
  30. // database name.
  31. blockDbNamePrefix = "blocks"
  32. )
  33. var cfg *config
  34. // winServiceMain is only invoked on Windows. It detects when pktd is running
  35. // as a service and reacts accordingly.
  36. var winServiceMain func() (bool, er.R)
  37. // pktdMain is the real main function for pktd. It is necessary to work around
  38. // the fact that deferred functions do not run when os.Exit() is called. The
  39. // optional serverChan parameter is mainly used by the service code to be
  40. // notified with the server once it is setup so it can gracefully stop it when
  41. // requested from the service control manager.
  42. func pktdMain(serverChan chan<- *server) er.R {
  43. // Unconditionally, show version and system information at early startup.
  44. log.Infof(
  45. "Version %s\nBuilt with %v %v for %v/%v\n%v logical CPUs, %v goroutines available",
  46. version.Version(),
  47. runtime.Compiler,
  48. runtime.Version(),
  49. runtime.GOOS,
  50. runtime.GOARCH,
  51. runtime.NumCPU(),
  52. runtime.GOMAXPROCS(-1),
  53. )
  54. // Load configuration and parse command line. This function also
  55. // initializes logging and configures it accordingly.
  56. tcfg, _, err := loadConfig()
  57. if err != nil {
  58. return err
  59. }
  60. cfg = tcfg
  61. // Warn if running a pre-released pktd
  62. log.WarnIfPrerelease()
  63. // Warn if not running on a 64-bit architecture
  64. is64bit := uint64(^uintptr(0)) == ^uint64(0)
  65. if !is64bit {
  66. log.Warnf(
  67. "UNSUPPORTED ARCHITECTURE: ONLY 64-BIT ARCHITECTURES ARE SUPPORTED",
  68. )
  69. }
  70. //
  71. // Get a channel that will be closed when a shutdown signal has been
  72. // triggered either from an OS signal such as SIGINT (Ctrl+C) or from
  73. // another subsystem such as the RPC server.
  74. interrupt := interruptListener()
  75. defer log.Info("Shutdown complete")
  76. // Enable http profiling server if requested.
  77. if cfg.Profile != "" {
  78. go func() {
  79. listenAddr := net.JoinHostPort("", cfg.Profile)
  80. log.Infof("Profile server listening on %s", listenAddr)
  81. profileRedirect := http.RedirectHandler("/debug/pprof",
  82. http.StatusSeeOther)
  83. http.Handle("/", profileRedirect)
  84. log.Errorf("%v", http.ListenAndServe(listenAddr, nil))
  85. }()
  86. }
  87. // Write cpu profile if requested.
  88. if cfg.CPUProfile != "" {
  89. f, errr := os.Create(cfg.CPUProfile)
  90. if errr != nil {
  91. log.Errorf("Unable to create cpu profile: %v", err)
  92. return er.E(errr)
  93. }
  94. if errp := pprof.StartCPUProfile(f); errp != nil {
  95. log.Errorf("could not start CPU profile: ", errp)
  96. return er.E(errp)
  97. }
  98. defer f.Close()
  99. defer pprof.StopCPUProfile()
  100. }
  101. // Enable StatsViz server if requested.
  102. if cfg.StatsViz != "" {
  103. statsvizAddr := net.JoinHostPort("", cfg.StatsViz)
  104. log.Infof("StatsViz server listening on %s", statsvizAddr)
  105. smux := http.NewServeMux()
  106. statsvizRedirect := http.RedirectHandler("/debug/statsviz", http.StatusSeeOther)
  107. smux.Handle("/", statsvizRedirect)
  108. if err := statsviz.Register(smux, statsviz.Root("/debug/statsviz")); err != nil {
  109. log.Errorf("%v", err)
  110. }
  111. go func() {
  112. log.Errorf("%v", http.ListenAndServe(statsvizAddr, smux))
  113. }()
  114. }
  115. // Perform upgrades to pktd as new versions require it.
  116. runtime.LockOSThread()
  117. if err := doUpgrades(); err != nil {
  118. log.Errorf("%v", err)
  119. runtime.UnlockOSThread()
  120. return err
  121. }
  122. // Return now if an interrupt signal was triggered.
  123. runtime.UnlockOSThread()
  124. if interruptRequested(interrupt) {
  125. runtime.LockOSThread()
  126. return nil
  127. }
  128. // Load the block database.
  129. runtime.UnlockOSThread()
  130. db, err := loadBlockDB()
  131. if err != nil {
  132. log.Errorf("%v", err)
  133. return err
  134. }
  135. defer func() {
  136. // Ensure the database is sync'd and closed on shutdown.
  137. log.Infof("Gracefully shutting down the database...")
  138. runtime.UnlockOSThread()
  139. runtime.GC()
  140. runtime.Gosched()
  141. db.Close()
  142. runtime.GC()
  143. debug.FreeOSMemory()
  144. }()
  145. // Return now if an interrupt signal was triggered.
  146. if interruptRequested(interrupt) {
  147. runtime.GC()
  148. debug.FreeOSMemory()
  149. return nil
  150. }
  151. // Drop indexes and exit if requested.
  152. //
  153. // NOTE: The order is important here because dropping the tx index also
  154. // drops the address index since it relies on it.
  155. if cfg.DropAddrIndex {
  156. if err := indexers.DropAddrIndex(db, interrupt); err != nil {
  157. log.Errorf("%v", err)
  158. return err
  159. }
  160. return nil
  161. }
  162. if cfg.DropTxIndex {
  163. if err := indexers.DropTxIndex(db, interrupt); err != nil {
  164. log.Errorf("%v", err)
  165. return err
  166. }
  167. return nil
  168. }
  169. if cfg.DropCfIndex {
  170. if err := indexers.DropCfIndex(db, interrupt); err != nil {
  171. log.Errorf("%v", err)
  172. return err
  173. }
  174. return nil
  175. }
  176. // Create server and start it.
  177. server, err := newServer(cfg.Listeners, cfg.AgentBlacklist,
  178. cfg.AgentWhitelist, db, activeNetParams.Params, interrupt)
  179. if err != nil {
  180. // TODO: this logging could do with some beautifying.
  181. log.Errorf("Unable to start server on %v: %v",
  182. cfg.Listeners, err)
  183. return err
  184. }
  185. defer func() {
  186. // Shut down in 5 minutes, or just pull the plug.
  187. const shutdownTimeout = 5 * time.Minute
  188. log.Infof("Preparing for shutdown...")
  189. runtime.Gosched()
  190. runtime.GC()
  191. debug.FreeOSMemory()
  192. log.Infof("Attempting graceful shutdown (%s timeout, %v active goroutines)...", shutdownTimeout, runtime.NumGoroutine())
  193. runtime.Gosched()
  194. server.Stop()
  195. shutdownDone := make(chan struct{})
  196. go func() {
  197. server.WaitForShutdown()
  198. shutdownDone <- struct{}{}
  199. }()
  200. select {
  201. case <-shutdownDone:
  202. case <-time.Tick(shutdownTimeout):
  203. log.Errorf("Graceful shutdown in %s failed - forcefully terminating process in 5s...", shutdownTimeout)
  204. time.Sleep(3 * time.Second)
  205. panic("Forcefully terminating the server process...")
  206. time.Sleep(1 * time.Second)
  207. runtime.Goexit()
  208. time.Sleep(1 * time.Second)
  209. panic("\nCowards die many times before their deaths\nThe valiant never taste of death but once.\n")
  210. }
  211. log.Infof("Server shutdown complete")
  212. }()
  213. server.Start()
  214. if serverChan != nil {
  215. serverChan <- server
  216. }
  217. // Wait until the interrupt signal is received from an OS signal or
  218. // shutdown is requested through one of the subsystems such as the RPC
  219. // server.
  220. <-interrupt
  221. return nil
  222. }
  223. // removeRegressionDB removes the existing regression test database if running
  224. // in regression test mode and it already exists.
  225. func removeRegressionDB(dbPath string) er.R {
  226. // Don't do anything if not in regression test mode.
  227. if !cfg.RegressionTest {
  228. return nil
  229. }
  230. // Remove the old regression test database if it already exists.
  231. fi, err := os.Stat(dbPath)
  232. if err == nil {
  233. log.Infof("Removing regression test database from '%s'", dbPath)
  234. if fi.IsDir() {
  235. errr := os.RemoveAll(dbPath)
  236. if errr != nil {
  237. return er.E(errr)
  238. }
  239. } else {
  240. errr := os.Remove(dbPath)
  241. if errr != nil {
  242. return er.E(errr)
  243. }
  244. }
  245. }
  246. return nil
  247. }
  248. // dbPath returns the path to the block database given a database type.
  249. func blockDbPath(dbType string) string {
  250. // The database name is based on the database type.
  251. dbName := blockDbNamePrefix + "_" + dbType
  252. if dbType == "sqlite" {
  253. dbName = dbName + ".db"
  254. }
  255. dbPath := filepath.Join(cfg.DataDir, dbName)
  256. return dbPath
  257. }
  258. // warnMultipleDBs shows a warning if multiple block database types are detected.
  259. // This is not a situation most users want. It is handy for development however
  260. // to support multiple side-by-side databases.
  261. func warnMultipleDBs() {
  262. // This is intentionally not using the known db types which depend
  263. // on the database types compiled into the binary since we want to
  264. // detect legacy db types as well.
  265. dbTypes := []string{"ffldb", "leveldb", "sqlite"}
  266. duplicateDbPaths := make([]string, 0, len(dbTypes)-1)
  267. for _, dbType := range dbTypes {
  268. if dbType == cfg.DbType {
  269. continue
  270. }
  271. // Store db path as a duplicate db if it exists.
  272. dbPath := blockDbPath(dbType)
  273. if fileExists(dbPath) {
  274. duplicateDbPaths = append(duplicateDbPaths, dbPath)
  275. }
  276. }
  277. // Warn if there are extra databases.
  278. if len(duplicateDbPaths) > 0 {
  279. selectedDbPath := blockDbPath(cfg.DbType)
  280. log.Warnf("WARNING: There are multiple block chain databases "+
  281. "using different database types.\nYou probably don't "+
  282. "want to waste disk space by having more than one.\n"+
  283. "Your current database is located at [%v].\nThe "+
  284. "additional database is located at %v", selectedDbPath,
  285. duplicateDbPaths)
  286. }
  287. }
  288. // loadBlockDB loads (or creates when needed) the block database taking into
  289. // account the selected database backend and returns a handle to it. It also
  290. // contains additional logic such warning the user if there are multiple
  291. // databases which consume space on the file system and ensuring the regression
  292. // test database is clean when in regression test mode.
  293. func loadBlockDB() (database.DB, er.R) {
  294. // The memdb backend does not have a file path associated with it, so
  295. // handle it uniquely. We also don't want to worry about the multiple
  296. // database type warnings when running with the memory database.
  297. if cfg.DbType == "memdb" {
  298. log.Infof("Creating block database in memory.")
  299. db, err := database.Create(cfg.DbType)
  300. if err != nil {
  301. return nil, err
  302. }
  303. return db, nil
  304. }
  305. warnMultipleDBs()
  306. // The database name is based on the database type.
  307. dbPath := blockDbPath(cfg.DbType)
  308. // The regression test is special in that it needs a clean database for
  309. // each run, so remove it now if it already exists.
  310. removeRegressionDB(dbPath)
  311. log.Infof("Loading block database from '%s'", dbPath)
  312. db, err := database.Open(cfg.DbType, dbPath, activeNetParams.Net)
  313. if err != nil {
  314. // Return the error if it's not because the database doesn't
  315. // exist.
  316. if !database.ErrDbDoesNotExist.Is(err) {
  317. return nil, err
  318. }
  319. // Create the db if it does not exist.
  320. errr := os.MkdirAll(cfg.DataDir, 0o700)
  321. if errr != nil {
  322. return nil, er.E(errr)
  323. }
  324. db, err = database.Create(cfg.DbType, dbPath, activeNetParams.Net)
  325. if err != nil {
  326. return nil, err
  327. }
  328. }
  329. log.Info("Block database loaded")
  330. return db, nil
  331. }
  332. func main() {
  333. version.SetUserAgentName("pktd")
  334. runtime.GOMAXPROCS(runtime.NumCPU() * 6)
  335. // Block and transaction processing can cause bursty allocations. This
  336. // limits the garbage collector from excessively over-allocating during
  337. // bursts. This value was arrived at with the help of profiling live
  338. // usage.
  339. debug.SetGCPercent(10)
  340. // Up some limits.
  341. if err := limits.SetLimits(); err != nil {
  342. fmt.Fprintf(os.Stderr, "failed to set limits: %v\n", err)
  343. os.Exit(1)
  344. }
  345. // Call serviceMain on Windows to handle running as a service. When
  346. // the return isService flag is true, exit now since we ran as a
  347. // service. Otherwise, just fall through to normal operation.
  348. if runtime.GOOS == "windows" {
  349. isService, err := winServiceMain()
  350. if err != nil {
  351. fmt.Println(err)
  352. os.Exit(1)
  353. }
  354. if isService {
  355. os.Exit(0)
  356. }
  357. }
  358. // Work around defer not working after os.Exit()
  359. if err := pktdMain(nil); err != nil {
  360. os.Exit(1)
  361. panic("time to die...")
  362. }
  363. }
  364. func init() {
  365. // Clean slate
  366. debug.FreeOSMemory()
  367. debug.SetPanicOnFault(false)
  368. debug.SetTraceback("all")
  369. // Register licensing
  370. pktdLegal.RegisterLicense(
  371. "\nISC License\n\nCopyright © 2021 Anode LLC.\nCopyright © 2019-2021 Caleb James DeLisle.\nCopyright © 2021 Gridfinity, LLC.\nCopyright © 2021 Jeffrey H. Johnson. <trnsz@pobox.com>\nCopyright © 2021 Filippo Valsorda.\nCopyright © 2020 Frank Denis <j at pureftpd dot org>.\nCopyright © 2020 The Go Authors.\nCopyright © 2015-2021 Lightning Labs and The Lightning Network Developers.\nCopyright © 2015-2018 Lightning Labs.\nCopyright © 2013-2017 The btcsuite developers.\nCopyright © 2016-2017 The Lightning Network Developers.\nCopyright © 2015-2016 The Decred developers.\nCopyright © 2015 Google, Inc.\n\nPermission to use, copy, modify, and distribute this software for any\npurpose with or without fee is hereby granted, provided that the above\ncopyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\nWITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\nANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\nACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\nOR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n",
  372. )
  373. }