github.com/clem109/go-ethereum@v1.8.3-0.20180316121352-fe6cf00f480a/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/core" 31 "github.com/ethereum/go-ethereum/core/types" 32 "github.com/ethereum/go-ethereum/internal/debug" 33 "github.com/ethereum/go-ethereum/log" 34 "github.com/ethereum/go-ethereum/node" 35 "github.com/ethereum/go-ethereum/rlp" 36 ) 37 38 const ( 39 importBatchSize = 2500 40 ) 41 42 // Fatalf formats a message to standard error and exits the program. 43 // The message is also printed to standard output if standard error 44 // is redirected to a different file. 45 func Fatalf(format string, args ...interface{}) { 46 w := io.MultiWriter(os.Stdout, os.Stderr) 47 if runtime.GOOS == "windows" { 48 // The SameFile check below doesn't work on Windows. 49 // stdout is unlikely to get redirected though, so just print there. 50 w = os.Stdout 51 } else { 52 outf, _ := os.Stdout.Stat() 53 errf, _ := os.Stderr.Stat() 54 if outf != nil && errf != nil && os.SameFile(outf, errf) { 55 w = os.Stderr 56 } 57 } 58 fmt.Fprintf(w, "Fatal: "+format+"\n", args...) 59 os.Exit(1) 60 } 61 62 func StartNode(stack *node.Node) { 63 if err := stack.Start(); err != nil { 64 Fatalf("Error starting protocol stack: %v", err) 65 } 66 go func() { 67 sigc := make(chan os.Signal, 1) 68 signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM) 69 defer signal.Stop(sigc) 70 <-sigc 71 log.Info("Got interrupt, shutting down...") 72 go stack.Stop() 73 for i := 10; i > 0; i-- { 74 <-sigc 75 if i > 1 { 76 log.Warn("Already shutting down, interrupt more to panic.", "times", i-1) 77 } 78 } 79 debug.Exit() // ensure trace and CPU profile data is flushed. 80 debug.LoudPanic("boom") 81 }() 82 } 83 84 func ImportChain(chain *core.BlockChain, fn string) error { 85 // Watch for Ctrl-C while the import is running. 86 // If a signal is received, the import will stop at the next batch. 87 interrupt := make(chan os.Signal, 1) 88 stop := make(chan struct{}) 89 signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM) 90 defer signal.Stop(interrupt) 91 defer close(interrupt) 92 go func() { 93 if _, ok := <-interrupt; ok { 94 log.Info("Interrupted during import, stopping at next batch") 95 } 96 close(stop) 97 }() 98 checkInterrupt := func() bool { 99 select { 100 case <-stop: 101 return true 102 default: 103 return false 104 } 105 } 106 107 log.Info("Importing blockchain", "file", fn) 108 fh, err := os.Open(fn) 109 if err != nil { 110 return err 111 } 112 defer fh.Close() 113 114 var reader io.Reader = fh 115 if strings.HasSuffix(fn, ".gz") { 116 if reader, err = gzip.NewReader(reader); err != nil { 117 return err 118 } 119 } 120 stream := rlp.NewStream(reader, 0) 121 122 // Run actual the import. 123 blocks := make(types.Blocks, importBatchSize) 124 n := 0 125 for batch := 0; ; batch++ { 126 // Load a batch of RLP blocks. 127 if checkInterrupt() { 128 return fmt.Errorf("interrupted") 129 } 130 i := 0 131 for ; i < importBatchSize; i++ { 132 var b types.Block 133 if err := stream.Decode(&b); err == io.EOF { 134 break 135 } else if err != nil { 136 return fmt.Errorf("at block %d: %v", n, err) 137 } 138 // don't import first block 139 if b.NumberU64() == 0 { 140 i-- 141 continue 142 } 143 blocks[i] = &b 144 n++ 145 } 146 if i == 0 { 147 break 148 } 149 // Import the batch. 150 if checkInterrupt() { 151 return fmt.Errorf("interrupted") 152 } 153 missing := missingBlocks(chain, blocks[:i]) 154 if len(missing) == 0 { 155 log.Info("Skipping batch as all blocks present", "batch", batch, "first", blocks[0].Hash(), "last", blocks[i-1].Hash()) 156 continue 157 } 158 if _, err := chain.InsertChain(missing); err != nil { 159 return fmt.Errorf("invalid block %d: %v", n, err) 160 } 161 } 162 return nil 163 } 164 165 func missingBlocks(chain *core.BlockChain, blocks []*types.Block) []*types.Block { 166 head := chain.CurrentBlock() 167 for i, block := range blocks { 168 // If we're behind the chain head, only check block, state is available at head 169 if head.NumberU64() > block.NumberU64() { 170 if !chain.HasBlock(block.Hash(), block.NumberU64()) { 171 return blocks[i:] 172 } 173 continue 174 } 175 // If we're above the chain head, state availability is a must 176 if !chain.HasBlockAndState(block.Hash(), block.NumberU64()) { 177 return blocks[i:] 178 } 179 } 180 return nil 181 } 182 183 func ExportChain(blockchain *core.BlockChain, fn string) error { 184 log.Info("Exporting blockchain", "file", fn) 185 fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) 186 if err != nil { 187 return err 188 } 189 defer fh.Close() 190 191 var writer io.Writer = fh 192 if strings.HasSuffix(fn, ".gz") { 193 writer = gzip.NewWriter(writer) 194 defer writer.(*gzip.Writer).Close() 195 } 196 197 if err := blockchain.Export(writer); err != nil { 198 return err 199 } 200 log.Info("Exported blockchain", "file", fn) 201 202 return nil 203 } 204 205 func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, last uint64) error { 206 log.Info("Exporting blockchain", "file", fn) 207 // TODO verify mode perms 208 fh, err := os.OpenFile(fn, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm) 209 if err != nil { 210 return err 211 } 212 defer fh.Close() 213 214 var writer io.Writer = fh 215 if strings.HasSuffix(fn, ".gz") { 216 writer = gzip.NewWriter(writer) 217 defer writer.(*gzip.Writer).Close() 218 } 219 220 if err := blockchain.ExportN(writer, first, last); err != nil { 221 return err 222 } 223 log.Info("Exported blockchain to", "file", fn) 224 return nil 225 }