github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/cmd/utils/cmd.go (about) 1 // Copyright 2014 The Spectrum Authors 2 // This file is part of Spectrum. 3 // 4 // Spectrum 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 // Spectrum 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 Spectrum. If not, see <http://www.gnu.org/licenses/>. 16 17 // Package utils contains internal helper functions for Spectrum commands. 18 package utils 19 20 import ( 21 "compress/gzip" 22 "fmt" 23 "io" 24 "os" 25 "os/signal" 26 "runtime" 27 "strings" 28 29 "github.com/SmartMeshFoundation/Spectrum/core" 30 "github.com/SmartMeshFoundation/Spectrum/core/types" 31 "github.com/SmartMeshFoundation/Spectrum/internal/debug" 32 "github.com/SmartMeshFoundation/Spectrum/log" 33 "github.com/SmartMeshFoundation/Spectrum/node" 34 "github.com/SmartMeshFoundation/Spectrum/rlp" 35 ) 36 37 const ( 38 importBatchSize = 2500 39 ) 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 61 func StartNode(stack *node.Node) { 62 if err := stack.Start(); err != nil { 63 Fatalf("Error starting protocol stack: %v", err) 64 } 65 go func() { 66 sigc := make(chan os.Signal, 1) 67 signal.Notify(sigc, os.Interrupt) 68 defer signal.Stop(sigc) 69 <-sigc 70 log.Info("Got interrupt, shutting down...") 71 go stack.Stop() 72 for i := 10; i > 0; i-- { 73 <-sigc 74 if i > 1 { 75 log.Warn("Already shutting down, interrupt more to panic.", "times", i-1) 76 } 77 } 78 debug.Exit() // ensure trace and CPU profile data is flushed. 79 debug.LoudPanic("boom") 80 }() 81 } 82 83 func ImportChain(chain *core.BlockChain, fn string) error { 84 // Watch for Ctrl-C while the import is running. 85 // If a signal is received, the import will stop at the next batch. 86 interrupt := make(chan os.Signal, 1) 87 stop := make(chan struct{}) 88 signal.Notify(interrupt, os.Interrupt) 89 defer signal.Stop(interrupt) 90 defer close(interrupt) 91 go func() { 92 if _, ok := <-interrupt; ok { 93 log.Info("Interrupted during import, stopping at next batch") 94 } 95 close(stop) 96 }() 97 checkInterrupt := func() bool { 98 select { 99 case <-stop: 100 return true 101 default: 102 return false 103 } 104 } 105 106 log.Info("Importing blockchain", "file", fn) 107 fh, err := os.Open(fn) 108 if err != nil { 109 return err 110 } 111 defer fh.Close() 112 113 var reader io.Reader = fh 114 if strings.HasSuffix(fn, ".gz") { 115 if reader, err = gzip.NewReader(reader); err != nil { 116 return err 117 } 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 if hasAllBlocks(chain, blocks[:i]) { 154 log.Info("Skipping batch as all blocks present", "batch", batch, "first", blocks[0].Hash(), "last", blocks[i-1].Hash()) 155 continue 156 } 157 158 if _, err := chain.InsertChain(blocks[:i]); err != nil { 159 return fmt.Errorf("invalid block %d: %v", n, err) 160 } 161 } 162 return nil 163 } 164 165 func hasAllBlocks(chain *core.BlockChain, bs []*types.Block) bool { 166 for _, b := range bs { 167 if !chain.HasBlock(b.Hash(), b.NumberU64()) { 168 return false 169 } 170 } 171 return true 172 } 173 174 func ExportChain(blockchain *core.BlockChain, fn string) error { 175 log.Info("Exporting blockchain", "file", fn) 176 fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) 177 if err != nil { 178 return err 179 } 180 defer fh.Close() 181 182 var writer io.Writer = fh 183 if strings.HasSuffix(fn, ".gz") { 184 writer = gzip.NewWriter(writer) 185 defer writer.(*gzip.Writer).Close() 186 } 187 188 if err := blockchain.Export(writer); err != nil { 189 return err 190 } 191 log.Info("Exported blockchain", "file", fn) 192 193 return nil 194 } 195 196 func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, last uint64) error { 197 log.Info("Exporting blockchain", "file", fn) 198 // TODO verify mode perms 199 fh, err := os.OpenFile(fn, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm) 200 if err != nil { 201 return err 202 } 203 defer fh.Close() 204 205 var writer io.Writer = fh 206 if strings.HasSuffix(fn, ".gz") { 207 writer = gzip.NewWriter(writer) 208 defer writer.(*gzip.Writer).Close() 209 } 210 211 if err := blockchain.ExportN(writer, first, last); err != nil { 212 return err 213 } 214 log.Info("Exported blockchain to", "file", fn) 215 return nil 216 }