github.com/prysmaticlabs/prysm@v1.4.4/tools/exploredb/main.go (about) 1 /** 2 * Explore DB contents 3 * 4 * Given a beacon-chain DB, This tool provides many option to 5 * inspect and explore it. For every non-empty bucket, print 6 * the number of rows, bucket size,min/average/max size of values 7 */ 8 9 package main 10 11 import ( 12 "context" 13 "flag" 14 "fmt" 15 "os" 16 "path/filepath" 17 "time" 18 19 "github.com/dustin/go-humanize" 20 "github.com/prysmaticlabs/prysm/beacon-chain/db/kv" 21 pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" 22 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 23 "github.com/prysmaticlabs/prysm/shared/bytesutil" 24 log "github.com/sirupsen/logrus" 25 "github.com/status-im/keycard-go/hexutils" 26 bolt "go.etcd.io/bbolt" 27 ) 28 29 var ( 30 datadir = flag.String("datadir", "", "Path to data directory.") 31 dbName = flag.String("dbname", "", "database name.") 32 bucketStats = flag.Bool("bucket-stats", false, "Show all the bucket stats.") 33 bucketContents = flag.Bool("bucket-contents", false, "Show contents of a given bucket.") 34 bucketName = flag.String("bucket-name", "", "bucket to show contents.") 35 rowLimit = flag.Uint64("limit", 10, "limit to rows.") 36 ) 37 38 func main() { 39 flag.Parse() 40 41 // Check for the mandatory flags. 42 if *datadir == "" { 43 log.Fatal("Please specify --datadir <db path> to read the database") 44 } 45 if *dbName == "" { 46 log.Fatal("Please specify --dbname <db file name> to specify the database file.") 47 } 48 49 // check if the database file is present. 50 dbNameWithPath := filepath.Join(*datadir, *dbName) 51 if _, err := os.Stat(*datadir); os.IsNotExist(err) { 52 log.Fatalf("could not locate database file : %s, %v", dbNameWithPath, err) 53 } 54 55 // show stats of all the buckets. 56 if *bucketStats { 57 showBucketStats(dbNameWithPath) 58 return 59 } 60 61 // show teh contents of the specified bucket. 62 if *bucketContents { 63 switch *bucketName { 64 case "state", "state-summary": 65 printBucketContents(dbNameWithPath, *rowLimit, *bucketName) 66 default: 67 log.Fatal("Oops, Only 'state' and 'state-summary' buckets are supported for now.") 68 } 69 } 70 } 71 72 func showBucketStats(dbNameWithPath string) { 73 // open the raw database file. If the file is busy, then exit. 74 db, openErr := bolt.Open(dbNameWithPath, 0600, &bolt.Options{Timeout: 1 * time.Second}) 75 if openErr != nil { 76 log.Fatalf("could not open db to show bucket stats, %v", openErr) 77 } 78 79 // make sure we close the database before ejecting out of this function. 80 defer func() { 81 closeErr := db.Close() 82 if closeErr != nil { 83 log.Fatalf("could not close db after showing bucket stats, %v", closeErr) 84 } 85 }() 86 87 // get a list of all the existing buckets. 88 var buckets []string 89 if viewErr1 := db.View(func(tx *bolt.Tx) error { 90 return tx.ForEach(func(name []byte, buc *bolt.Bucket) error { 91 buckets = append(buckets, string(name)) 92 return nil 93 }) 94 }); viewErr1 != nil { 95 log.Fatalf("could not read buckets from db while getting list of buckets: %v", viewErr1) 96 } 97 98 // for every bucket, calculate the stats and display them. 99 // TODO: parallelize the execution 100 for _, bName := range buckets { 101 count := uint64(0) 102 minValueSize := ^uint64(0) 103 maxValueSize := uint64(0) 104 totalValueSize := uint64(0) 105 minKeySize := ^uint64(0) 106 maxKeySize := uint64(0) 107 totalKeySize := uint64(0) 108 if viewErr2 := db.View(func(tx *bolt.Tx) error { 109 b := tx.Bucket([]byte(bName)) 110 if forEachErr := b.ForEach(func(k, v []byte) error { 111 count++ 112 valueSize := uint64(len(v)) 113 if valueSize < minValueSize { 114 minValueSize = valueSize 115 } 116 if valueSize > maxValueSize { 117 maxValueSize = valueSize 118 } 119 totalValueSize += valueSize 120 121 keyize := uint64(len(k)) 122 if keyize < minKeySize { 123 minKeySize = keyize 124 } 125 if keyize > maxKeySize { 126 maxKeySize = keyize 127 } 128 totalKeySize += uint64(len(k)) 129 return nil 130 }); forEachErr != nil { 131 log.Errorf("could not process row %d for bucket: %s, %v", count, bName, forEachErr) 132 return forEachErr 133 } 134 return nil 135 }); viewErr2 != nil { 136 log.Errorf("could not get stats for bucket: %s, %v", bName, viewErr2) 137 continue 138 } 139 140 if count != 0 { 141 averageValueSize := totalValueSize / count 142 averageKeySize := totalKeySize / count 143 fmt.Println("------ ", bName, " --------") 144 fmt.Println("NumberOfRows = ", count) 145 fmt.Println("TotalBucketSize = ", humanize.Bytes(totalValueSize+totalKeySize)) 146 fmt.Println("KeySize = ", humanize.Bytes(totalKeySize), "(min = "+humanize.Bytes(minKeySize)+", avg = "+humanize.Bytes(averageKeySize)+", max = "+humanize.Bytes(maxKeySize)+")") 147 fmt.Println("ValueSize = ", humanize.Bytes(totalValueSize), "(min = "+humanize.Bytes(minValueSize)+", avg = "+humanize.Bytes(averageValueSize)+", max = "+humanize.Bytes(maxValueSize)+")") 148 } 149 } 150 } 151 152 func printBucketContents(dbNameWithPath string, rowLimit uint64, bucketName string) { 153 // get the keys within the supplied limit for the given bucket. 154 bucketNameInBytes := []byte(bucketName) 155 keys, sizes := keysOfBucket(dbNameWithPath, bucketNameInBytes, rowLimit) 156 157 // create a new KV Store. 158 dbDirectory := filepath.Dir(dbNameWithPath) 159 db, openErr := kv.NewKVStore(context.Background(), dbDirectory, &kv.Config{}) 160 if openErr != nil { 161 log.Fatalf("could not open db, %v", openErr) 162 } 163 164 // dont forget to close it when ejecting out of this function. 165 defer func() { 166 closeErr := db.Close() 167 if closeErr != nil { 168 log.Fatalf("could not close db, %v", closeErr) 169 } 170 }() 171 172 // retrieve every element for keys in the list and call the respective display function. 173 ctx := context.Background() 174 rowCount := uint64(0) 175 for index, key := range keys { 176 switch bucketName { 177 case "state": 178 printState(ctx, db, key, rowCount, sizes[index]) 179 case "state-summary": 180 printStateSummary(ctx, db, key, rowCount) 181 } 182 rowCount++ 183 } 184 } 185 186 func printState(ctx context.Context, db *kv.Store, key []byte, rowCount, valueSize uint64) { 187 st, stateErr := db.State(ctx, bytesutil.ToBytes32(key)) 188 if stateErr != nil { 189 log.Errorf("could not get state for key : , %v", stateErr) 190 } 191 rowStr := fmt.Sprintf("---- row = %04d ----", rowCount) 192 fmt.Println(rowStr) 193 fmt.Println("key :", key) 194 fmt.Println("value : compressed size = ", humanize.Bytes(valueSize)) 195 fmt.Println("genesis_time :", st.GenesisTime()) 196 fmt.Println("genesis_validators_root :", hexutils.BytesToHex(st.GenesisValidatorRoot())) 197 fmt.Println("slot :", st.Slot()) 198 fmt.Println("fork : previous_version: ", st.Fork().PreviousVersion, ", current_version: ", st.Fork().CurrentVersion) 199 fmt.Println("latest_block_header : sizeSSZ = ", humanize.Bytes(uint64(st.LatestBlockHeader().SizeSSZ()))) 200 size, count := sizeAndCountOfByteList(st.BlockRoots()) 201 fmt.Println("block_roots : size = ", humanize.Bytes(size), ", count = ", count) 202 size, count = sizeAndCountOfByteList(st.StateRoots()) 203 fmt.Println("state_roots : size = ", humanize.Bytes(size), ", count = ", count) 204 size, count = sizeAndCountOfByteList(st.HistoricalRoots()) 205 fmt.Println("historical_roots : size = ", humanize.Bytes(size), ", count = ", count) 206 fmt.Println("eth1_data : sizeSSZ = ", humanize.Bytes(uint64(st.Eth1Data().SizeSSZ()))) 207 size, count = sizeAndCountGeneric(st.Eth1DataVotes(), nil) 208 fmt.Println("eth1_data_votes : sizeSSZ = ", humanize.Bytes(size), ", count = ", count) 209 fmt.Println("eth1_deposit_index :", st.Eth1DepositIndex()) 210 size, count = sizeAndCountGeneric(st.Validators(), nil) 211 fmt.Println("validators : sizeSSZ = ", humanize.Bytes(size), ", count = ", count) 212 size, count = sizeAndCountOfUin64List(st.Balances()) 213 fmt.Println("balances : size = ", humanize.Bytes(size), ", count = ", count) 214 size, count = sizeAndCountOfByteList(st.RandaoMixes()) 215 fmt.Println("randao_mixes : size = ", humanize.Bytes(size), ", count = ", count) 216 size, count = sizeAndCountOfUin64List(st.Slashings()) 217 fmt.Println("slashings : size = ", humanize.Bytes(size), ", count = ", count) 218 size, count = sizeAndCountGeneric(st.PreviousEpochAttestations()) 219 fmt.Println("previous_epoch_attestations : sizeSSZ ", humanize.Bytes(size), ", count = ", count) 220 size, count = sizeAndCountGeneric(st.CurrentEpochAttestations()) 221 fmt.Println("current_epoch_attestations : sizeSSZ = ", humanize.Bytes(size), ", count = ", count) 222 fmt.Println("justification_bits : size = ", humanize.Bytes(st.JustificationBits().Len()), ", count = ", st.JustificationBits().Count()) 223 fmt.Println("previous_justified_checkpoint : sizeSSZ = ", humanize.Bytes(uint64(st.PreviousJustifiedCheckpoint().SizeSSZ()))) 224 fmt.Println("current_justified_checkpoint : sizeSSZ = ", humanize.Bytes(uint64(st.CurrentJustifiedCheckpoint().SizeSSZ()))) 225 fmt.Println("finalized_checkpoint : sizeSSZ = ", humanize.Bytes(uint64(st.FinalizedCheckpoint().SizeSSZ()))) 226 } 227 228 func printStateSummary(ctx context.Context, db *kv.Store, key []byte, rowCount uint64) { 229 ss, ssErr := db.StateSummary(ctx, bytesutil.ToBytes32(key)) 230 if ssErr != nil { 231 log.Errorf("could not get state summary for key : , %v", ssErr) 232 } 233 rowCountStr := fmt.Sprintf("row : %04d, ", rowCount) 234 fmt.Println(rowCountStr, "slot : ", ss.Slot, ", root : ", hexutils.BytesToHex(ss.Root)) 235 } 236 237 func keysOfBucket(dbNameWithPath string, bucketName []byte, rowLimit uint64) ([][]byte, []uint64) { 238 // open the raw database file. If the file is busy, then exit. 239 db, openErr := bolt.Open(dbNameWithPath, 0600, &bolt.Options{Timeout: 1 * time.Second}) 240 if openErr != nil { 241 log.Fatalf("could not open db while getting keys of a bucket, %v", openErr) 242 } 243 244 // make sure we close the database before ejecting out of this function. 245 defer func() { 246 closeErr := db.Close() 247 if closeErr != nil { 248 log.Fatalf("could not close db while getting keys of a bucket, %v", closeErr) 249 } 250 }() 251 252 // get all the keys of the given bucket. 253 var keys [][]byte 254 var sizes []uint64 255 if viewErr := db.View(func(tx *bolt.Tx) error { 256 b := tx.Bucket(bucketName) 257 c := b.Cursor() 258 count := uint64(0) 259 for k, v := c.First(); k != nil; k, v = c.Next() { 260 if count >= rowLimit { 261 return nil 262 } 263 keys = append(keys, k) 264 sizes = append(sizes, uint64(len(v))) 265 count++ 266 } 267 return nil 268 }); viewErr != nil { 269 log.Fatalf("could not read keys of bucket from db: %v", viewErr) 270 } 271 return keys, sizes 272 } 273 274 func sizeAndCountOfByteList(list [][]byte) (uint64, uint64) { 275 size := uint64(0) 276 count := uint64(0) 277 for _, root := range list { 278 size += uint64(len(root)) 279 count += 1 280 } 281 return size, count 282 } 283 284 func sizeAndCountOfUin64List(list []uint64) (uint64, uint64) { 285 size := uint64(0) 286 count := uint64(0) 287 for i := 0; i < len(list); i++ { 288 size += uint64(8) 289 count += 1 290 } 291 return size, count 292 } 293 294 func sizeAndCountGeneric(genericItems interface{}, err error) (uint64, uint64) { 295 size := uint64(0) 296 count := uint64(0) 297 if err != nil { 298 return size, count 299 } 300 301 switch items := genericItems.(type) { 302 case []*ethpb.Eth1Data: 303 for _, item := range items { 304 size += uint64(item.SizeSSZ()) 305 } 306 count = uint64(len(items)) 307 case []*ethpb.Validator: 308 for _, item := range items { 309 size += uint64(item.SizeSSZ()) 310 } 311 count = uint64(len(items)) 312 case []*pbp2p.PendingAttestation: 313 for _, item := range items { 314 size += uint64(item.SizeSSZ()) 315 } 316 count = uint64(len(items)) 317 default: 318 return 0, 0 319 } 320 321 return size, count 322 }