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