github.com/aquanetwork/aquachain@v1.7.8/cmd/utils/cmd.go (about) 1 // Copyright 2014 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/log" 31 "gitlab.com/aquachain/aquachain/core" 32 "gitlab.com/aquachain/aquachain/core/types" 33 "gitlab.com/aquachain/aquachain/internal/debug" 34 "gitlab.com/aquachain/aquachain/node" 35 "gitlab.com/aquachain/aquachain/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(111) 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("%v", 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.SetVersion(chain.Config().GetBlockVersion(block.Number())), block.NumberU64()) { 171 return blocks[i:] 172 } 173 continue 174 } 175 176 // If we're above the chain head, state availability is a must 177 if !chain.HasBlockAndState(block.SetVersion(chain.Config().GetBlockVersion(block.Number())), block.NumberU64()) { 178 return blocks[i:] 179 } 180 } 181 return nil 182 } 183 184 func ExportChain(blockchain *core.BlockChain, fn string) error { 185 log.Info("Exporting blockchain", "file", fn) 186 fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) 187 if err != nil { 188 return err 189 } 190 defer fh.Close() 191 192 var writer io.Writer = fh 193 if strings.HasSuffix(fn, ".gz") { 194 writer = gzip.NewWriter(writer) 195 defer writer.(*gzip.Writer).Close() 196 } 197 198 if err := blockchain.Export(writer); err != nil { 199 return err 200 } 201 log.Info("Exported blockchain", "file", fn) 202 203 return nil 204 } 205 206 func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, last uint64) error { 207 log.Info("Exporting blockchain", "file", fn) 208 // TODO verify mode perms 209 fh, err := os.OpenFile(fn, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm) 210 if err != nil { 211 return err 212 } 213 defer fh.Close() 214 215 var writer io.Writer = fh 216 if strings.HasSuffix(fn, ".gz") { 217 writer = gzip.NewWriter(writer) 218 defer writer.(*gzip.Writer).Close() 219 } 220 221 if err := blockchain.ExportN(writer, first, last); err != nil { 222 return err 223 } 224 log.Info("Exported blockchain to", "file", fn) 225 return nil 226 }