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