cmd.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. // Copyright 2014 The go-ethereum Authors
  2. // This file is part of go-ethereum.
  3. //
  4. // go-ethereum is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // go-ethereum is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
  16. // Package utils contains internal helper functions for go-ethereum commands.
  17. package utils
  18. import (
  19. "compress/gzip"
  20. "fmt"
  21. "io"
  22. "os"
  23. "os/signal"
  24. "runtime"
  25. "strings"
  26. "syscall"
  27. "github.com/ethereum/go-ethereum/common"
  28. "github.com/ethereum/go-ethereum/core"
  29. "github.com/ethereum/go-ethereum/core/rawdb"
  30. "github.com/ethereum/go-ethereum/core/types"
  31. "github.com/ethereum/go-ethereum/crypto"
  32. "github.com/ethereum/go-ethereum/ethdb"
  33. "github.com/ethereum/go-ethereum/internal/debug"
  34. "github.com/ethereum/go-ethereum/log"
  35. "github.com/ethereum/go-ethereum/node"
  36. "github.com/ethereum/go-ethereum/rlp"
  37. )
  38. const (
  39. importBatchSize = 2500
  40. )
  41. // Fatalf formats a message to standard error and exits the program.
  42. // The message is also printed to standard output if standard error
  43. // is redirected to a different file.
  44. func Fatalf(format string, args ...interface{}) {
  45. w := io.MultiWriter(os.Stdout, os.Stderr)
  46. if runtime.GOOS == "windows" {
  47. // The SameFile check below doesn't work on Windows.
  48. // stdout is unlikely to get redirected though, so just print there.
  49. w = os.Stdout
  50. } else {
  51. outf, _ := os.Stdout.Stat()
  52. errf, _ := os.Stderr.Stat()
  53. if outf != nil && errf != nil && os.SameFile(outf, errf) {
  54. w = os.Stderr
  55. }
  56. }
  57. fmt.Fprintf(w, "Fatal: "+format+"\n", args...)
  58. os.Exit(1)
  59. }
  60. func StartNode(stack *node.Node) {
  61. if err := stack.Start(); err != nil {
  62. Fatalf("Error starting protocol stack: %v", err)
  63. }
  64. go func() {
  65. sigc := make(chan os.Signal, 1)
  66. signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
  67. defer signal.Stop(sigc)
  68. <-sigc
  69. log.Info("Got interrupt, shutting down...")
  70. go stack.Stop()
  71. for i := 10; i > 0; i-- {
  72. <-sigc
  73. if i > 1 {
  74. log.Warn("Already shutting down, interrupt more to panic.", "times", i-1)
  75. }
  76. }
  77. debug.Exit() // ensure trace and CPU profile data is flushed.
  78. debug.LoudPanic("boom")
  79. }()
  80. }
  81. func ImportChain(chain *core.BlockChain, fn string) error {
  82. // Watch for Ctrl-C while the import is running.
  83. // If a signal is received, the import will stop at the next batch.
  84. interrupt := make(chan os.Signal, 1)
  85. stop := make(chan struct{})
  86. signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
  87. defer signal.Stop(interrupt)
  88. defer close(interrupt)
  89. go func() {
  90. if _, ok := <-interrupt; ok {
  91. log.Info("Interrupted during import, stopping at next batch")
  92. }
  93. close(stop)
  94. }()
  95. checkInterrupt := func() bool {
  96. select {
  97. case <-stop:
  98. return true
  99. default:
  100. return false
  101. }
  102. }
  103. log.Info("Importing blockchain", "file", fn)
  104. // Open the file handle and potentially unwrap the gzip stream
  105. fh, err := os.Open(fn)
  106. if err != nil {
  107. return err
  108. }
  109. defer fh.Close()
  110. var reader io.Reader = fh
  111. if strings.HasSuffix(fn, ".gz") {
  112. if reader, err = gzip.NewReader(reader); err != nil {
  113. return err
  114. }
  115. }
  116. stream := rlp.NewStream(reader, 0)
  117. // Run actual the import.
  118. blocks := make(types.Blocks, importBatchSize)
  119. n := 0
  120. for batch := 0; ; batch++ {
  121. // Load a batch of RLP blocks.
  122. if checkInterrupt() {
  123. return fmt.Errorf("interrupted")
  124. }
  125. i := 0
  126. for ; i < importBatchSize; i++ {
  127. var b types.Block
  128. if err := stream.Decode(&b); err == io.EOF {
  129. break
  130. } else if err != nil {
  131. return fmt.Errorf("at block %d: %v", n, err)
  132. }
  133. // don't import first block
  134. if b.NumberU64() == 0 {
  135. i--
  136. continue
  137. }
  138. blocks[i] = &b
  139. n++
  140. }
  141. if i == 0 {
  142. break
  143. }
  144. // Import the batch.
  145. if checkInterrupt() {
  146. return fmt.Errorf("interrupted")
  147. }
  148. missing := missingBlocks(chain, blocks[:i])
  149. if len(missing) == 0 {
  150. log.Info("Skipping batch as all blocks present", "batch", batch, "first", blocks[0].Hash(), "last", blocks[i-1].Hash())
  151. continue
  152. }
  153. if _, err := chain.InsertChain(missing); err != nil {
  154. return fmt.Errorf("invalid block %d: %v", n, err)
  155. }
  156. }
  157. return nil
  158. }
  159. func missingBlocks(chain *core.BlockChain, blocks []*types.Block) []*types.Block {
  160. head := chain.CurrentBlock()
  161. for i, block := range blocks {
  162. // If we're behind the chain head, only check block, state is available at head
  163. if head.NumberU64() > block.NumberU64() {
  164. if !chain.HasBlock(block.Hash(), block.NumberU64()) {
  165. return blocks[i:]
  166. }
  167. continue
  168. }
  169. // If we're above the chain head, state availability is a must
  170. if !chain.HasBlockAndState(block.Hash(), block.NumberU64()) {
  171. return blocks[i:]
  172. }
  173. }
  174. return nil
  175. }
  176. // ExportChain exports a blockchain into the specified file, truncating any data
  177. // already present in the file.
  178. func ExportChain(blockchain *core.BlockChain, fn string) error {
  179. log.Info("Exporting blockchain", "file", fn)
  180. // Open the file handle and potentially wrap with a gzip stream
  181. fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
  182. if err != nil {
  183. return err
  184. }
  185. defer fh.Close()
  186. var writer io.Writer = fh
  187. if strings.HasSuffix(fn, ".gz") {
  188. writer = gzip.NewWriter(writer)
  189. defer writer.(*gzip.Writer).Close()
  190. }
  191. // Iterate over the blocks and export them
  192. if err := blockchain.Export(writer); err != nil {
  193. return err
  194. }
  195. log.Info("Exported blockchain", "file", fn)
  196. return nil
  197. }
  198. // ExportAppendChain exports a blockchain into the specified file, appending to
  199. // the file if data already exists in it.
  200. func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, last uint64) error {
  201. log.Info("Exporting blockchain", "file", fn)
  202. // Open the file handle and potentially wrap with a gzip stream
  203. fh, err := os.OpenFile(fn, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm)
  204. if err != nil {
  205. return err
  206. }
  207. defer fh.Close()
  208. var writer io.Writer = fh
  209. if strings.HasSuffix(fn, ".gz") {
  210. writer = gzip.NewWriter(writer)
  211. defer writer.(*gzip.Writer).Close()
  212. }
  213. // Iterate over the blocks and export them
  214. if err := blockchain.ExportN(writer, first, last); err != nil {
  215. return err
  216. }
  217. log.Info("Exported blockchain to", "file", fn)
  218. return nil
  219. }
  220. // ImportPreimages imports a batch of exported hash preimages into the database.
  221. func ImportPreimages(db *ethdb.LDBDatabase, fn string) error {
  222. log.Info("Importing preimages", "file", fn)
  223. // Open the file handle and potentially unwrap the gzip stream
  224. fh, err := os.Open(fn)
  225. if err != nil {
  226. return err
  227. }
  228. defer fh.Close()
  229. var reader io.Reader = fh
  230. if strings.HasSuffix(fn, ".gz") {
  231. if reader, err = gzip.NewReader(reader); err != nil {
  232. return err
  233. }
  234. }
  235. stream := rlp.NewStream(reader, 0)
  236. // Import the preimages in batches to prevent disk trashing
  237. preimages := make(map[common.Hash][]byte)
  238. for {
  239. // Read the next entry and ensure it's not junk
  240. var blob []byte
  241. if err := stream.Decode(&blob); err != nil {
  242. if err == io.EOF {
  243. break
  244. }
  245. return err
  246. }
  247. // Accumulate the preimages and flush when enough ws gathered
  248. preimages[crypto.Keccak256Hash(blob)] = common.CopyBytes(blob)
  249. if len(preimages) > 1024 {
  250. rawdb.WritePreimages(db, 0, preimages)
  251. preimages = make(map[common.Hash][]byte)
  252. }
  253. }
  254. // Flush the last batch preimage data
  255. if len(preimages) > 0 {
  256. rawdb.WritePreimages(db, 0, preimages)
  257. }
  258. return nil
  259. }
  260. // ExportPreimages exports all known hash preimages into the specified file,
  261. // truncating any data already present in the file.
  262. func ExportPreimages(db *ethdb.LDBDatabase, fn string) error {
  263. log.Info("Exporting preimages", "file", fn)
  264. // Open the file handle and potentially wrap with a gzip stream
  265. fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
  266. if err != nil {
  267. return err
  268. }
  269. defer fh.Close()
  270. var writer io.Writer = fh
  271. if strings.HasSuffix(fn, ".gz") {
  272. writer = gzip.NewWriter(writer)
  273. defer writer.(*gzip.Writer).Close()
  274. }
  275. // Iterate over the preimages and export them
  276. it := db.NewIteratorWithPrefix([]byte("secure-key-"))
  277. for it.Next() {
  278. if err := rlp.Encode(writer, it.Value()); err != nil {
  279. return err
  280. }
  281. }
  282. log.Info("Exported preimages", "file", fn)
  283. return nil
  284. }