github.com/bcskill/bcschain/v3@v3.4.9-beta2/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 "context" 23 "fmt" 24 "io" 25 "os" 26 "os/signal" 27 "runtime" 28 "strings" 29 "syscall" 30 31 "github.com/bcskill/bcschain/v3/core" 32 "github.com/bcskill/bcschain/v3/core/types" 33 "github.com/bcskill/bcschain/v3/internal/debug" 34 "github.com/bcskill/bcschain/v3/log" 35 "github.com/bcskill/bcschain/v3/node" 36 "github.com/bcskill/bcschain/v3/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 os.Exit(1) 61 } 62 63 func StartNode(stack *node.Node) { 64 if err := stack.Start(); err != nil { 65 Fatalf("Error starting protocol stack: %v", err) 66 } 67 go func() { 68 sigc := make(chan os.Signal, 1) 69 signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM) 70 defer signal.Stop(sigc) 71 <-sigc 72 log.Info("Got interrupt, shutting down...") 73 go stack.Stop() 74 for i := 10; i > 0; i-- { 75 <-sigc 76 if i > 1 { 77 log.Warn("Already shutting down, interrupt more to panic.", "times", i-1) 78 } 79 } 80 debug.Exit() // ensure trace and CPU profile data is flushed. 81 debug.LoudPanic("boom") 82 }() 83 } 84 85 func ImportChain(ctx context.Context, chain *core.BlockChain, fn string) error { 86 // Watch for Ctrl-C while the import is running. 87 // If a signal is received, the import will stop at the next batch. 88 interrupt := make(chan os.Signal, 1) 89 stop := make(chan struct{}) 90 signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM) 91 defer signal.Stop(interrupt) 92 defer close(interrupt) 93 go func() { 94 if _, ok := <-interrupt; ok { 95 log.Info("Interrupted during import, stopping at next batch") 96 } 97 close(stop) 98 }() 99 checkInterrupt := func() bool { 100 select { 101 case <-stop: 102 return true 103 default: 104 return false 105 } 106 } 107 108 log.Info("Importing blockchain", "file", fn) 109 fh, err := os.Open(fn) 110 if err != nil { 111 return err 112 } 113 defer fh.Close() 114 115 var reader io.Reader = fh 116 if strings.HasSuffix(fn, ".gz") { 117 if reader, err = gzip.NewReader(reader); err != nil { 118 return err 119 } 120 } 121 stream := rlp.NewStream(reader, 0) 122 123 // Run actual the import. 124 blocks := make(types.Blocks, importBatchSize) 125 n := 0 126 for batch := 0; ; batch++ { 127 // Load a batch of RLP blocks. 128 if checkInterrupt() { 129 return fmt.Errorf("interrupted") 130 } 131 i := 0 132 for ; i < importBatchSize; i++ { 133 var b types.Block 134 if err := stream.Decode(&b); err == io.EOF { 135 break 136 } else if err != nil { 137 return fmt.Errorf("at block %d: %v", n, err) 138 } 139 // don't import first block 140 if b.NumberU64() == 0 { 141 i-- 142 continue 143 } 144 blocks[i] = &b 145 n++ 146 } 147 if i == 0 { 148 break 149 } 150 // Import the batch. 151 if checkInterrupt() { 152 return fmt.Errorf("interrupted") 153 } 154 missing := missingBlocks(chain, blocks[:i]) 155 if len(missing) == 0 { 156 log.Info("Skipping batch as all blocks present", "batch", batch, "first", blocks[0].Hash(), "last", blocks[i-1].Hash()) 157 continue 158 } 159 if _, err := chain.InsertChain(missing); err != nil { 160 return fmt.Errorf("invalid block %d: %v", n, err) 161 } 162 } 163 return nil 164 } 165 166 func missingBlocks(chain *core.BlockChain, blocks []*types.Block) []*types.Block { 167 head := chain.CurrentBlock() 168 for i, block := range blocks { 169 // If we're behind the chain head, only check block, state is available at head 170 if head.NumberU64() > block.NumberU64() { 171 if !chain.HasBlock(block.Hash(), block.NumberU64()) { 172 return blocks[i:] 173 } 174 continue 175 } 176 // If we're above the chain head, state availability is a must 177 if !chain.HasBlockAndState(block.Hash(), 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 }