github.com/aychain/blockbook@v0.1.1-0.20181121092459-6d1fc7e07c5b/db/rocksdb.go (about) 1 package db 2 3 import ( 4 "blockbook/bchain" 5 "blockbook/common" 6 "bytes" 7 "encoding/binary" 8 "encoding/hex" 9 "fmt" 10 "math/big" 11 "os" 12 "path/filepath" 13 "strconv" 14 "time" 15 16 "github.com/bsm/go-vlq" 17 "github.com/golang/glog" 18 "github.com/juju/errors" 19 "github.com/tecbot/gorocksdb" 20 ) 21 22 // iterator creates snapshot, which takes lots of resources 23 // when doing huge scan, it is better to close it and reopen from time to time to free the resources 24 const refreshIterator = 5000000 25 const packedHeightBytes = 4 26 const dbVersion = 3 27 const maxAddrDescLen = 1024 28 29 // RepairRocksDB calls RocksDb db repair function 30 func RepairRocksDB(name string) error { 31 glog.Infof("rocksdb: repair") 32 opts := gorocksdb.NewDefaultOptions() 33 return gorocksdb.RepairDb(name, opts) 34 } 35 36 type connectBlockStats struct { 37 txAddressesHit int 38 txAddressesMiss int 39 balancesHit int 40 balancesMiss int 41 } 42 43 // RocksDB handle 44 type RocksDB struct { 45 path string 46 db *gorocksdb.DB 47 wo *gorocksdb.WriteOptions 48 ro *gorocksdb.ReadOptions 49 cfh []*gorocksdb.ColumnFamilyHandle 50 chainParser bchain.BlockChainParser 51 is *common.InternalState 52 metrics *common.Metrics 53 cache *gorocksdb.Cache 54 maxOpenFiles int 55 cbs connectBlockStats 56 } 57 58 const ( 59 cfDefault = iota 60 cfHeight 61 cfAddresses 62 cfTxAddresses 63 cfAddressBalance 64 cfBlockTxs 65 cfTransactions 66 ) 67 68 var cfNames = []string{"default", "height", "addresses", "txAddresses", "addressBalance", "blockTxs", "transactions"} 69 70 func openDB(path string, c *gorocksdb.Cache, openFiles int) (*gorocksdb.DB, []*gorocksdb.ColumnFamilyHandle, error) { 71 // opts with bloom filter 72 opts := createAndSetDBOptions(10, c, openFiles) 73 // opts for addresses without bloom filter 74 // from documentation: if most of your queries are executed using iterators, you shouldn't set bloom filter 75 optsAddresses := createAndSetDBOptions(0, c, openFiles) 76 // default, height, addresses, txAddresses, addressBalance, blockTxids, transactions 77 fcOptions := []*gorocksdb.Options{opts, opts, optsAddresses, opts, opts, opts, opts} 78 db, cfh, err := gorocksdb.OpenDbColumnFamilies(opts, path, cfNames, fcOptions) 79 if err != nil { 80 return nil, nil, err 81 } 82 return db, cfh, nil 83 } 84 85 // NewRocksDB opens an internal handle to RocksDB environment. Close 86 // needs to be called to release it. 87 func NewRocksDB(path string, cacheSize, maxOpenFiles int, parser bchain.BlockChainParser, metrics *common.Metrics) (d *RocksDB, err error) { 88 glog.Infof("rocksdb: opening %s, required data version %v, cache size %v, max open files %v", path, dbVersion, cacheSize, maxOpenFiles) 89 c := gorocksdb.NewLRUCache(cacheSize) 90 db, cfh, err := openDB(path, c, maxOpenFiles) 91 if err != nil { 92 return nil, err 93 } 94 wo := gorocksdb.NewDefaultWriteOptions() 95 ro := gorocksdb.NewDefaultReadOptions() 96 return &RocksDB{path, db, wo, ro, cfh, parser, nil, metrics, c, maxOpenFiles, connectBlockStats{}}, nil 97 } 98 99 func (d *RocksDB) closeDB() error { 100 for _, h := range d.cfh { 101 h.Destroy() 102 } 103 d.db.Close() 104 d.db = nil 105 return nil 106 } 107 108 // Close releases the RocksDB environment opened in NewRocksDB. 109 func (d *RocksDB) Close() error { 110 if d.db != nil { 111 // store the internal state of the app 112 if d.is != nil && d.is.DbState == common.DbStateOpen { 113 d.is.DbState = common.DbStateClosed 114 if err := d.StoreInternalState(d.is); err != nil { 115 glog.Info("internalState: ", err) 116 } 117 } 118 glog.Infof("rocksdb: close") 119 d.closeDB() 120 d.wo.Destroy() 121 d.ro.Destroy() 122 } 123 return nil 124 } 125 126 // Reopen reopens the database 127 // It closes and reopens db, nobody can access the database during the operation! 128 func (d *RocksDB) Reopen() error { 129 err := d.closeDB() 130 if err != nil { 131 return err 132 } 133 d.db = nil 134 db, cfh, err := openDB(d.path, d.cache, d.maxOpenFiles) 135 if err != nil { 136 return err 137 } 138 d.db, d.cfh = db, cfh 139 return nil 140 } 141 142 func atoi(s string) int { 143 i, err := strconv.Atoi(s) 144 if err != nil { 145 return 0 146 } 147 return i 148 } 149 150 // GetMemoryStats returns memory usage statistics as reported by RocksDB 151 func (d *RocksDB) GetMemoryStats() string { 152 var total, indexAndFilter, memtable int 153 type columnStats struct { 154 name string 155 indexAndFilter string 156 memtable string 157 } 158 cs := make([]columnStats, len(cfNames)) 159 for i := 0; i < len(cfNames); i++ { 160 cs[i].name = cfNames[i] 161 cs[i].indexAndFilter = d.db.GetPropertyCF("rocksdb.estimate-table-readers-mem", d.cfh[i]) 162 cs[i].memtable = d.db.GetPropertyCF("rocksdb.cur-size-all-mem-tables", d.cfh[i]) 163 indexAndFilter += atoi(cs[i].indexAndFilter) 164 memtable += atoi(cs[i].memtable) 165 } 166 m := struct { 167 cacheUsage int 168 pinnedCacheUsage int 169 columns []columnStats 170 }{ 171 cacheUsage: d.cache.GetUsage(), 172 pinnedCacheUsage: d.cache.GetPinnedUsage(), 173 columns: cs, 174 } 175 total = m.cacheUsage + indexAndFilter + memtable 176 return fmt.Sprintf("Total %d, indexAndFilter %d, memtable %d, %+v", total, indexAndFilter, memtable, m) 177 } 178 179 // StopIteration is returned by callback function to signal stop of iteration 180 type StopIteration struct{} 181 182 func (e *StopIteration) Error() string { 183 return "" 184 } 185 186 // GetTransactions finds all input/output transactions for address 187 // Transaction are passed to callback function. 188 func (d *RocksDB) GetTransactions(address string, lower uint32, higher uint32, fn func(txid string, vout uint32, isOutput bool) error) (err error) { 189 if glog.V(1) { 190 glog.Infof("rocksdb: address get %s %d-%d ", address, lower, higher) 191 } 192 addrDesc, err := d.chainParser.GetAddrDescFromAddress(address) 193 if err != nil { 194 return err 195 } 196 return d.GetAddrDescTransactions(addrDesc, lower, higher, fn) 197 } 198 199 // GetAddrDescTransactions finds all input/output transactions for address descriptor 200 // Transaction are passed to callback function. 201 func (d *RocksDB) GetAddrDescTransactions(addrDesc bchain.AddressDescriptor, lower uint32, higher uint32, fn func(txid string, vout uint32, isOutput bool) error) (err error) { 202 kstart := packAddressKey(addrDesc, lower) 203 kstop := packAddressKey(addrDesc, higher) 204 205 it := d.db.NewIteratorCF(d.ro, d.cfh[cfAddresses]) 206 defer it.Close() 207 208 for it.Seek(kstart); it.Valid(); it.Next() { 209 key := it.Key().Data() 210 val := it.Value().Data() 211 if bytes.Compare(key, kstop) > 0 { 212 break 213 } 214 outpoints, err := d.unpackOutpoints(val) 215 if err != nil { 216 return err 217 } 218 if glog.V(2) { 219 glog.Infof("rocksdb: output %s: %s", hex.EncodeToString(key), hex.EncodeToString(val)) 220 } 221 for _, o := range outpoints { 222 var vout uint32 223 var isOutput bool 224 if o.index < 0 { 225 vout = uint32(^o.index) 226 isOutput = false 227 } else { 228 vout = uint32(o.index) 229 isOutput = true 230 } 231 tx, err := d.chainParser.UnpackTxid(o.btxID) 232 if err != nil { 233 return err 234 } 235 if err := fn(tx, vout, isOutput); err != nil { 236 if _, ok := err.(*StopIteration); ok { 237 return nil 238 } 239 return err 240 } 241 } 242 } 243 return nil 244 } 245 246 const ( 247 opInsert = 0 248 opDelete = 1 249 ) 250 251 // ConnectBlock indexes addresses in the block and stores them in db 252 func (d *RocksDB) ConnectBlock(block *bchain.Block) error { 253 return d.writeBlock(block, opInsert) 254 } 255 256 // DisconnectBlock removes addresses in the block from the db 257 func (d *RocksDB) DisconnectBlock(block *bchain.Block) error { 258 return d.writeBlock(block, opDelete) 259 } 260 261 func (d *RocksDB) writeBlock(block *bchain.Block, op int) error { 262 wb := gorocksdb.NewWriteBatch() 263 defer wb.Destroy() 264 265 if glog.V(2) { 266 switch op { 267 case opInsert: 268 glog.Infof("rocksdb: insert %d %s", block.Height, block.Hash) 269 case opDelete: 270 glog.Infof("rocksdb: delete %d %s", block.Height, block.Hash) 271 } 272 } 273 274 isUTXO := d.chainParser.IsUTXOChain() 275 276 if err := d.writeHeightFromBlock(wb, block, op); err != nil { 277 return err 278 } 279 if isUTXO { 280 if op == opDelete { 281 // block does not contain mapping tx-> input address, which is necessary to recreate 282 // unspentTxs; therefore it is not possible to DisconnectBlocks this way 283 return errors.New("DisconnectBlock is not supported for UTXO chains") 284 } 285 addresses := make(map[string][]outpoint) 286 txAddressesMap := make(map[string]*TxAddresses) 287 balances := make(map[string]*AddrBalance) 288 if err := d.processAddressesUTXO(block, addresses, txAddressesMap, balances); err != nil { 289 return err 290 } 291 if err := d.storeAddresses(wb, block.Height, addresses); err != nil { 292 return err 293 } 294 if err := d.storeTxAddresses(wb, txAddressesMap); err != nil { 295 return err 296 } 297 if err := d.storeBalances(wb, balances); err != nil { 298 return err 299 } 300 if err := d.storeAndCleanupBlockTxs(wb, block); err != nil { 301 return err 302 } 303 } else { 304 if err := d.writeAddressesNonUTXO(wb, block, op); err != nil { 305 return err 306 } 307 } 308 309 return d.db.Write(d.wo, wb) 310 } 311 312 // Addresses index 313 314 type outpoint struct { 315 btxID []byte 316 index int32 317 } 318 319 type TxInput struct { 320 AddrDesc bchain.AddressDescriptor 321 ValueSat big.Int 322 } 323 324 func (ti *TxInput) Addresses(p bchain.BlockChainParser) ([]string, bool, error) { 325 return p.GetAddressesFromAddrDesc(ti.AddrDesc) 326 } 327 328 type TxOutput struct { 329 AddrDesc bchain.AddressDescriptor 330 Spent bool 331 ValueSat big.Int 332 } 333 334 func (to *TxOutput) Addresses(p bchain.BlockChainParser) ([]string, bool, error) { 335 return p.GetAddressesFromAddrDesc(to.AddrDesc) 336 } 337 338 type TxAddresses struct { 339 Height uint32 340 Inputs []TxInput 341 Outputs []TxOutput 342 } 343 344 type AddrBalance struct { 345 Txs uint32 346 SentSat big.Int 347 BalanceSat big.Int 348 } 349 350 func (ab *AddrBalance) ReceivedSat() *big.Int { 351 var r big.Int 352 r.Add(&ab.BalanceSat, &ab.SentSat) 353 return &r 354 } 355 356 type blockTxs struct { 357 btxID []byte 358 inputs []outpoint 359 } 360 361 func (d *RocksDB) resetValueSatToZero(valueSat *big.Int, addrDesc bchain.AddressDescriptor, logText string) { 362 ad, _, err := d.chainParser.GetAddressesFromAddrDesc(addrDesc) 363 if err != nil { 364 glog.Warningf("rocksdb: unparsable address hex '%v' reached negative %s %v, resetting to 0. Parser error %v", addrDesc, logText, valueSat.String(), err) 365 } else { 366 glog.Warningf("rocksdb: address %v hex '%v' reached negative %s %v, resetting to 0", ad, addrDesc, logText, valueSat.String()) 367 } 368 valueSat.SetInt64(0) 369 } 370 371 // GetAndResetConnectBlockStats gets statistics about cache usage in connect blocks and resets the counters 372 func (d *RocksDB) GetAndResetConnectBlockStats() string { 373 s := fmt.Sprintf("%+v", d.cbs) 374 d.cbs = connectBlockStats{} 375 return s 376 } 377 378 func (d *RocksDB) processAddressesUTXO(block *bchain.Block, addresses map[string][]outpoint, txAddressesMap map[string]*TxAddresses, balances map[string]*AddrBalance) error { 379 blockTxIDs := make([][]byte, len(block.Txs)) 380 blockTxAddresses := make([]*TxAddresses, len(block.Txs)) 381 // first process all outputs so that inputs can point to txs in this block 382 for txi := range block.Txs { 383 tx := &block.Txs[txi] 384 btxID, err := d.chainParser.PackTxid(tx.Txid) 385 if err != nil { 386 return err 387 } 388 blockTxIDs[txi] = btxID 389 ta := TxAddresses{Height: block.Height} 390 ta.Outputs = make([]TxOutput, len(tx.Vout)) 391 txAddressesMap[string(btxID)] = &ta 392 blockTxAddresses[txi] = &ta 393 for i, output := range tx.Vout { 394 tao := &ta.Outputs[i] 395 tao.ValueSat = output.ValueSat 396 addrDesc, err := d.chainParser.GetAddrDescFromVout(&output) 397 if err != nil || len(addrDesc) == 0 || len(addrDesc) > maxAddrDescLen { 398 if err != nil { 399 // do not log ErrAddressMissing, transactions can be without to address (for example eth contracts) 400 if err != bchain.ErrAddressMissing { 401 glog.Warningf("rocksdb: addrDesc: %v - height %d, tx %v, output %v", err, block.Height, tx.Txid, output) 402 } 403 } else { 404 glog.Infof("rocksdb: height %d, tx %v, vout %v, skipping addrDesc of length %d", block.Height, tx.Txid, i, len(addrDesc)) 405 } 406 continue 407 } 408 tao.AddrDesc = addrDesc 409 strAddrDesc := string(addrDesc) 410 // check that the address was used already in this block 411 o, processed := addresses[strAddrDesc] 412 if processed { 413 // check that the address was already used in this tx 414 processed = processedInTx(o, btxID) 415 } 416 addresses[strAddrDesc] = append(o, outpoint{ 417 btxID: btxID, 418 index: int32(i), 419 }) 420 ab, e := balances[strAddrDesc] 421 if !e { 422 ab, err = d.GetAddrDescBalance(addrDesc) 423 if err != nil { 424 return err 425 } 426 if ab == nil { 427 ab = &AddrBalance{} 428 } 429 balances[strAddrDesc] = ab 430 d.cbs.balancesMiss++ 431 } else { 432 d.cbs.balancesHit++ 433 } 434 // add number of trx in balance only once, address can be multiple times in tx 435 if !processed { 436 ab.Txs++ 437 } 438 ab.BalanceSat.Add(&ab.BalanceSat, &output.ValueSat) 439 } 440 } 441 // process inputs 442 for txi := range block.Txs { 443 tx := &block.Txs[txi] 444 spendingTxid := blockTxIDs[txi] 445 ta := blockTxAddresses[txi] 446 ta.Inputs = make([]TxInput, len(tx.Vin)) 447 logged := false 448 for i, input := range tx.Vin { 449 tai := &ta.Inputs[i] 450 btxID, err := d.chainParser.PackTxid(input.Txid) 451 if err != nil { 452 // do not process inputs without input txid 453 if err == bchain.ErrTxidMissing { 454 continue 455 } 456 return err 457 } 458 stxID := string(btxID) 459 ita, e := txAddressesMap[stxID] 460 if !e { 461 ita, err = d.getTxAddresses(btxID) 462 if err != nil { 463 return err 464 } 465 if ita == nil { 466 glog.Warningf("rocksdb: height %d, tx %v, input tx %v not found in txAddresses", block.Height, tx.Txid, input.Txid) 467 continue 468 } 469 txAddressesMap[stxID] = ita 470 d.cbs.txAddressesMiss++ 471 } else { 472 d.cbs.txAddressesHit++ 473 } 474 if len(ita.Outputs) <= int(input.Vout) { 475 glog.Warningf("rocksdb: height %d, tx %v, input tx %v vout %v is out of bounds of stored tx", block.Height, tx.Txid, input.Txid, input.Vout) 476 continue 477 } 478 ot := &ita.Outputs[int(input.Vout)] 479 if ot.Spent { 480 glog.Warningf("rocksdb: height %d, tx %v, input tx %v vout %v is double spend", block.Height, tx.Txid, input.Txid, input.Vout) 481 } 482 tai.AddrDesc = ot.AddrDesc 483 tai.ValueSat = ot.ValueSat 484 // mark the output as spent in tx 485 ot.Spent = true 486 if len(ot.AddrDesc) == 0 { 487 if !logged { 488 glog.Warningf("rocksdb: height %d, tx %v, input tx %v vout %v skipping empty address", block.Height, tx.Txid, input.Txid, input.Vout) 489 logged = true 490 } 491 continue 492 } 493 strAddrDesc := string(ot.AddrDesc) 494 // check that the address was used already in this block 495 o, processed := addresses[strAddrDesc] 496 if processed { 497 // check that the address was already used in this tx 498 processed = processedInTx(o, spendingTxid) 499 } 500 addresses[strAddrDesc] = append(o, outpoint{ 501 btxID: spendingTxid, 502 index: ^int32(i), 503 }) 504 ab, e := balances[strAddrDesc] 505 if !e { 506 ab, err = d.GetAddrDescBalance(ot.AddrDesc) 507 if err != nil { 508 return err 509 } 510 if ab == nil { 511 ab = &AddrBalance{} 512 } 513 balances[strAddrDesc] = ab 514 d.cbs.balancesMiss++ 515 } else { 516 d.cbs.balancesHit++ 517 } 518 // add number of trx in balance only once, address can be multiple times in tx 519 if !processed { 520 ab.Txs++ 521 } 522 ab.BalanceSat.Sub(&ab.BalanceSat, &ot.ValueSat) 523 if ab.BalanceSat.Sign() < 0 { 524 d.resetValueSatToZero(&ab.BalanceSat, ot.AddrDesc, "balance") 525 } 526 ab.SentSat.Add(&ab.SentSat, &ot.ValueSat) 527 } 528 } 529 return nil 530 } 531 532 func processedInTx(o []outpoint, btxID []byte) bool { 533 for _, op := range o { 534 if bytes.Equal(btxID, op.btxID) { 535 return true 536 } 537 } 538 return false 539 } 540 541 func (d *RocksDB) storeAddresses(wb *gorocksdb.WriteBatch, height uint32, addresses map[string][]outpoint) error { 542 for addrDesc, outpoints := range addresses { 543 ba := bchain.AddressDescriptor(addrDesc) 544 key := packAddressKey(ba, height) 545 val := d.packOutpoints(outpoints) 546 wb.PutCF(d.cfh[cfAddresses], key, val) 547 } 548 return nil 549 } 550 551 func (d *RocksDB) storeTxAddresses(wb *gorocksdb.WriteBatch, am map[string]*TxAddresses) error { 552 varBuf := make([]byte, maxPackedBigintBytes) 553 buf := make([]byte, 1024) 554 for txID, ta := range am { 555 buf = packTxAddresses(ta, buf, varBuf) 556 wb.PutCF(d.cfh[cfTxAddresses], []byte(txID), buf) 557 } 558 return nil 559 } 560 561 func (d *RocksDB) storeBalances(wb *gorocksdb.WriteBatch, abm map[string]*AddrBalance) error { 562 // allocate buffer big enough for number of txs + 2 bigints 563 buf := make([]byte, vlq.MaxLen32+2*maxPackedBigintBytes) 564 for addrDesc, ab := range abm { 565 // balance with 0 transactions is removed from db - happens in disconnect 566 if ab == nil || ab.Txs <= 0 { 567 wb.DeleteCF(d.cfh[cfAddressBalance], bchain.AddressDescriptor(addrDesc)) 568 } else { 569 l := packVaruint(uint(ab.Txs), buf) 570 ll := packBigint(&ab.SentSat, buf[l:]) 571 l += ll 572 ll = packBigint(&ab.BalanceSat, buf[l:]) 573 l += ll 574 wb.PutCF(d.cfh[cfAddressBalance], bchain.AddressDescriptor(addrDesc), buf[:l]) 575 } 576 } 577 return nil 578 } 579 580 func (d *RocksDB) storeAndCleanupBlockTxs(wb *gorocksdb.WriteBatch, block *bchain.Block) error { 581 pl := d.chainParser.PackedTxidLen() 582 buf := make([]byte, 0, pl*len(block.Txs)) 583 varBuf := make([]byte, vlq.MaxLen64) 584 zeroTx := make([]byte, pl) 585 for i := range block.Txs { 586 tx := &block.Txs[i] 587 o := make([]outpoint, len(tx.Vin)) 588 for v := range tx.Vin { 589 vin := &tx.Vin[v] 590 btxID, err := d.chainParser.PackTxid(vin.Txid) 591 if err != nil { 592 // do not process inputs without input txid 593 if err == bchain.ErrTxidMissing { 594 btxID = zeroTx 595 } else { 596 return err 597 } 598 } 599 o[v].btxID = btxID 600 o[v].index = int32(vin.Vout) 601 } 602 btxID, err := d.chainParser.PackTxid(tx.Txid) 603 if err != nil { 604 return err 605 } 606 buf = append(buf, btxID...) 607 l := packVaruint(uint(len(o)), varBuf) 608 buf = append(buf, varBuf[:l]...) 609 buf = append(buf, d.packOutpoints(o)...) 610 } 611 key := packUint(block.Height) 612 wb.PutCF(d.cfh[cfBlockTxs], key, buf) 613 keep := d.chainParser.KeepBlockAddresses() 614 // cleanup old block address 615 if block.Height > uint32(keep) { 616 for rh := block.Height - uint32(keep); rh < block.Height; rh-- { 617 key = packUint(rh) 618 val, err := d.db.GetCF(d.ro, d.cfh[cfBlockTxs], key) 619 if err != nil { 620 return err 621 } 622 if val.Size() == 0 { 623 break 624 } 625 val.Free() 626 d.db.DeleteCF(d.wo, d.cfh[cfBlockTxs], key) 627 } 628 } 629 return nil 630 } 631 632 func (d *RocksDB) getBlockTxs(height uint32) ([]blockTxs, error) { 633 pl := d.chainParser.PackedTxidLen() 634 val, err := d.db.GetCF(d.ro, d.cfh[cfBlockTxs], packUint(height)) 635 if err != nil { 636 return nil, err 637 } 638 defer val.Free() 639 buf := val.Data() 640 bt := make([]blockTxs, 0, 8) 641 for i := 0; i < len(buf); { 642 if len(buf)-i < pl { 643 glog.Error("rocksdb: Inconsistent data in blockTxs ", hex.EncodeToString(buf)) 644 return nil, errors.New("Inconsistent data in blockTxs") 645 } 646 txid := make([]byte, pl) 647 copy(txid, buf[i:]) 648 i += pl 649 o, ol, err := d.unpackNOutpoints(buf[i:]) 650 if err != nil { 651 glog.Error("rocksdb: Inconsistent data in blockTxs ", hex.EncodeToString(buf)) 652 return nil, errors.New("Inconsistent data in blockTxs") 653 } 654 bt = append(bt, blockTxs{ 655 btxID: txid, 656 inputs: o, 657 }) 658 i += ol 659 } 660 return bt, nil 661 } 662 663 // GetAddrDescBalance returns AddrBalance for given addrDesc 664 func (d *RocksDB) GetAddrDescBalance(addrDesc bchain.AddressDescriptor) (*AddrBalance, error) { 665 val, err := d.db.GetCF(d.ro, d.cfh[cfAddressBalance], addrDesc) 666 if err != nil { 667 return nil, err 668 } 669 defer val.Free() 670 buf := val.Data() 671 // 3 is minimum length of addrBalance - 1 byte txs, 1 byte sent, 1 byte balance 672 if len(buf) < 3 { 673 return nil, nil 674 } 675 txs, l := unpackVaruint(buf) 676 sentSat, sl := unpackBigint(buf[l:]) 677 balanceSat, _ := unpackBigint(buf[l+sl:]) 678 return &AddrBalance{ 679 Txs: uint32(txs), 680 SentSat: sentSat, 681 BalanceSat: balanceSat, 682 }, nil 683 } 684 685 // GetAddressBalance returns address balance for an address or nil if address not found 686 func (d *RocksDB) GetAddressBalance(address string) (*AddrBalance, error) { 687 addrDesc, err := d.chainParser.GetAddrDescFromAddress(address) 688 if err != nil { 689 return nil, err 690 } 691 return d.GetAddrDescBalance(addrDesc) 692 } 693 694 func (d *RocksDB) getTxAddresses(btxID []byte) (*TxAddresses, error) { 695 val, err := d.db.GetCF(d.ro, d.cfh[cfTxAddresses], btxID) 696 if err != nil { 697 return nil, err 698 } 699 defer val.Free() 700 buf := val.Data() 701 // 2 is minimum length of addrBalance - 1 byte height, 1 byte inputs len, 1 byte outputs len 702 if len(buf) < 3 { 703 return nil, nil 704 } 705 return unpackTxAddresses(buf) 706 } 707 708 // GetTxAddresses returns TxAddresses for given txid or nil if not found 709 func (d *RocksDB) GetTxAddresses(txid string) (*TxAddresses, error) { 710 btxID, err := d.chainParser.PackTxid(txid) 711 if err != nil { 712 return nil, err 713 } 714 return d.getTxAddresses(btxID) 715 } 716 717 func packTxAddresses(ta *TxAddresses, buf []byte, varBuf []byte) []byte { 718 buf = buf[:0] 719 l := packVaruint(uint(ta.Height), varBuf) 720 buf = append(buf, varBuf[:l]...) 721 l = packVaruint(uint(len(ta.Inputs)), varBuf) 722 buf = append(buf, varBuf[:l]...) 723 for i := range ta.Inputs { 724 buf = appendTxInput(&ta.Inputs[i], buf, varBuf) 725 } 726 l = packVaruint(uint(len(ta.Outputs)), varBuf) 727 buf = append(buf, varBuf[:l]...) 728 for i := range ta.Outputs { 729 buf = appendTxOutput(&ta.Outputs[i], buf, varBuf) 730 } 731 return buf 732 } 733 734 func appendTxInput(txi *TxInput, buf []byte, varBuf []byte) []byte { 735 la := len(txi.AddrDesc) 736 l := packVaruint(uint(la), varBuf) 737 buf = append(buf, varBuf[:l]...) 738 buf = append(buf, txi.AddrDesc...) 739 l = packBigint(&txi.ValueSat, varBuf) 740 buf = append(buf, varBuf[:l]...) 741 return buf 742 } 743 744 func appendTxOutput(txo *TxOutput, buf []byte, varBuf []byte) []byte { 745 la := len(txo.AddrDesc) 746 if txo.Spent { 747 la = ^la 748 } 749 l := packVarint(la, varBuf) 750 buf = append(buf, varBuf[:l]...) 751 buf = append(buf, txo.AddrDesc...) 752 l = packBigint(&txo.ValueSat, varBuf) 753 buf = append(buf, varBuf[:l]...) 754 return buf 755 } 756 757 func unpackTxAddresses(buf []byte) (*TxAddresses, error) { 758 ta := TxAddresses{} 759 height, l := unpackVaruint(buf) 760 ta.Height = uint32(height) 761 inputs, ll := unpackVaruint(buf[l:]) 762 l += ll 763 ta.Inputs = make([]TxInput, inputs) 764 for i := uint(0); i < inputs; i++ { 765 l += unpackTxInput(&ta.Inputs[i], buf[l:]) 766 } 767 outputs, ll := unpackVaruint(buf[l:]) 768 l += ll 769 ta.Outputs = make([]TxOutput, outputs) 770 for i := uint(0); i < outputs; i++ { 771 l += unpackTxOutput(&ta.Outputs[i], buf[l:]) 772 } 773 return &ta, nil 774 } 775 776 func unpackTxInput(ti *TxInput, buf []byte) int { 777 al, l := unpackVaruint(buf) 778 ti.AddrDesc = make([]byte, al) 779 copy(ti.AddrDesc, buf[l:l+int(al)]) 780 al += uint(l) 781 ti.ValueSat, l = unpackBigint(buf[al:]) 782 return l + int(al) 783 } 784 785 func unpackTxOutput(to *TxOutput, buf []byte) int { 786 al, l := unpackVarint(buf) 787 if al < 0 { 788 to.Spent = true 789 al = ^al 790 } 791 to.AddrDesc = make([]byte, al) 792 copy(to.AddrDesc, buf[l:l+al]) 793 al += l 794 to.ValueSat, l = unpackBigint(buf[al:]) 795 return l + al 796 } 797 798 func (d *RocksDB) packOutpoints(outpoints []outpoint) []byte { 799 buf := make([]byte, 0, 32) 800 bvout := make([]byte, vlq.MaxLen32) 801 for _, o := range outpoints { 802 l := packVarint32(o.index, bvout) 803 buf = append(buf, []byte(o.btxID)...) 804 buf = append(buf, bvout[:l]...) 805 } 806 return buf 807 } 808 809 func (d *RocksDB) unpackOutpoints(buf []byte) ([]outpoint, error) { 810 txidUnpackedLen := d.chainParser.PackedTxidLen() 811 outpoints := make([]outpoint, 0, 8) 812 for i := 0; i < len(buf); { 813 btxID := append([]byte(nil), buf[i:i+txidUnpackedLen]...) 814 i += txidUnpackedLen 815 vout, voutLen := unpackVarint32(buf[i:]) 816 i += voutLen 817 outpoints = append(outpoints, outpoint{ 818 btxID: btxID, 819 index: vout, 820 }) 821 } 822 return outpoints, nil 823 } 824 825 func (d *RocksDB) unpackNOutpoints(buf []byte) ([]outpoint, int, error) { 826 txidUnpackedLen := d.chainParser.PackedTxidLen() 827 n, p := unpackVaruint(buf) 828 outpoints := make([]outpoint, n) 829 for i := uint(0); i < n; i++ { 830 if p+txidUnpackedLen >= len(buf) { 831 return nil, 0, errors.New("Inconsistent data in unpackNOutpoints") 832 } 833 btxID := append([]byte(nil), buf[p:p+txidUnpackedLen]...) 834 p += txidUnpackedLen 835 vout, voutLen := unpackVarint32(buf[p:]) 836 p += voutLen 837 outpoints[i] = outpoint{ 838 btxID: btxID, 839 index: vout, 840 } 841 } 842 return outpoints, p, nil 843 } 844 845 func (d *RocksDB) addAddrDescToRecords(op int, wb *gorocksdb.WriteBatch, records map[string][]outpoint, addrDesc bchain.AddressDescriptor, btxid []byte, vout int32, bh uint32) error { 846 if len(addrDesc) > 0 { 847 if len(addrDesc) > maxAddrDescLen { 848 glog.Infof("rocksdb: block %d, skipping addrDesc of length %d", bh, len(addrDesc)) 849 } else { 850 strAddrDesc := string(addrDesc) 851 records[strAddrDesc] = append(records[strAddrDesc], outpoint{ 852 btxID: btxid, 853 index: vout, 854 }) 855 if op == opDelete { 856 // remove transactions from cache 857 d.internalDeleteTx(wb, btxid) 858 } 859 } 860 } 861 return nil 862 } 863 864 func (d *RocksDB) writeAddressesNonUTXO(wb *gorocksdb.WriteBatch, block *bchain.Block, op int) error { 865 addresses := make(map[string][]outpoint) 866 for _, tx := range block.Txs { 867 btxID, err := d.chainParser.PackTxid(tx.Txid) 868 if err != nil { 869 return err 870 } 871 for _, output := range tx.Vout { 872 addrDesc, err := d.chainParser.GetAddrDescFromVout(&output) 873 if err != nil { 874 // do not log ErrAddressMissing, transactions can be without to address (for example eth contracts) 875 if err != bchain.ErrAddressMissing { 876 glog.Warningf("rocksdb: addrDesc: %v - height %d, tx %v, output %v", err, block.Height, tx.Txid, output) 877 } 878 continue 879 } 880 err = d.addAddrDescToRecords(op, wb, addresses, addrDesc, btxID, int32(output.N), block.Height) 881 if err != nil { 882 return err 883 } 884 } 885 // store inputs in format txid ^index 886 for _, input := range tx.Vin { 887 for i, a := range input.Addresses { 888 addrDesc, err := d.chainParser.GetAddrDescFromAddress(a) 889 if err != nil { 890 glog.Warningf("rocksdb: addrDesc: %v - %d %s", err, block.Height, addrDesc) 891 continue 892 } 893 err = d.addAddrDescToRecords(op, wb, addresses, addrDesc, btxID, int32(^i), block.Height) 894 if err != nil { 895 return err 896 } 897 } 898 } 899 } 900 for addrDesc, outpoints := range addresses { 901 key := packAddressKey(bchain.AddressDescriptor(addrDesc), block.Height) 902 switch op { 903 case opInsert: 904 val := d.packOutpoints(outpoints) 905 wb.PutCF(d.cfh[cfAddresses], key, val) 906 case opDelete: 907 wb.DeleteCF(d.cfh[cfAddresses], key) 908 } 909 } 910 return nil 911 } 912 913 // Block index 914 915 // BlockInfo holds information about blocks kept in column height 916 type BlockInfo struct { 917 Hash string 918 Time int64 919 Txs uint32 920 Size uint32 921 Height uint32 // Height is not packed! 922 } 923 924 func (d *RocksDB) packBlockInfo(block *BlockInfo) ([]byte, error) { 925 packed := make([]byte, 0, 64) 926 varBuf := make([]byte, vlq.MaxLen64) 927 b, err := d.chainParser.PackBlockHash(block.Hash) 928 if err != nil { 929 return nil, err 930 } 931 packed = append(packed, b...) 932 packed = append(packed, packUint(uint32(block.Time))...) 933 l := packVaruint(uint(block.Txs), varBuf) 934 packed = append(packed, varBuf[:l]...) 935 l = packVaruint(uint(block.Size), varBuf) 936 packed = append(packed, varBuf[:l]...) 937 return packed, nil 938 } 939 940 func (d *RocksDB) unpackBlockInfo(buf []byte) (*BlockInfo, error) { 941 pl := d.chainParser.PackedTxidLen() 942 // minimum length is PackedTxidLen + 4 bytes time + 1 byte txs + 1 byte size 943 if len(buf) < pl+4+2 { 944 return nil, nil 945 } 946 txid, err := d.chainParser.UnpackBlockHash(buf[:pl]) 947 if err != nil { 948 return nil, err 949 } 950 t := unpackUint(buf[pl:]) 951 txs, l := unpackVaruint(buf[pl+4:]) 952 size, _ := unpackVaruint(buf[pl+4+l:]) 953 return &BlockInfo{ 954 Hash: txid, 955 Time: int64(t), 956 Txs: uint32(txs), 957 Size: uint32(size), 958 }, nil 959 } 960 961 // GetBestBlock returns the block hash of the block with highest height in the db 962 func (d *RocksDB) GetBestBlock() (uint32, string, error) { 963 it := d.db.NewIteratorCF(d.ro, d.cfh[cfHeight]) 964 defer it.Close() 965 if it.SeekToLast(); it.Valid() { 966 bestHeight := unpackUint(it.Key().Data()) 967 info, err := d.unpackBlockInfo(it.Value().Data()) 968 if info != nil { 969 if glog.V(1) { 970 glog.Infof("rocksdb: bestblock %d %+v", bestHeight, info) 971 } 972 return bestHeight, info.Hash, err 973 } 974 } 975 return 0, "", nil 976 } 977 978 // GetBlockHash returns block hash at given height or empty string if not found 979 func (d *RocksDB) GetBlockHash(height uint32) (string, error) { 980 key := packUint(height) 981 val, err := d.db.GetCF(d.ro, d.cfh[cfHeight], key) 982 if err != nil { 983 return "", err 984 } 985 defer val.Free() 986 info, err := d.unpackBlockInfo(val.Data()) 987 if info == nil { 988 return "", err 989 } 990 return info.Hash, nil 991 } 992 993 // GetBlockInfo returns block info stored in db 994 func (d *RocksDB) GetBlockInfo(height uint32) (*BlockInfo, error) { 995 key := packUint(height) 996 val, err := d.db.GetCF(d.ro, d.cfh[cfHeight], key) 997 if err != nil { 998 return nil, err 999 } 1000 defer val.Free() 1001 bi, err := d.unpackBlockInfo(val.Data()) 1002 if err != nil || bi == nil { 1003 return nil, err 1004 } 1005 bi.Height = height 1006 return bi, err 1007 } 1008 1009 func (d *RocksDB) writeHeightFromBlock(wb *gorocksdb.WriteBatch, block *bchain.Block, op int) error { 1010 return d.writeHeight(wb, block.Height, &BlockInfo{ 1011 Hash: block.Hash, 1012 Time: block.Time, 1013 Txs: uint32(len(block.Txs)), 1014 Size: uint32(block.Size), 1015 Height: block.Height, 1016 }, op) 1017 } 1018 1019 func (d *RocksDB) writeHeight(wb *gorocksdb.WriteBatch, height uint32, bi *BlockInfo, op int) error { 1020 key := packUint(height) 1021 switch op { 1022 case opInsert: 1023 val, err := d.packBlockInfo(bi) 1024 if err != nil { 1025 return err 1026 } 1027 wb.PutCF(d.cfh[cfHeight], key, val) 1028 d.is.UpdateBestHeight(height) 1029 case opDelete: 1030 wb.DeleteCF(d.cfh[cfHeight], key) 1031 d.is.UpdateBestHeight(height - 1) 1032 } 1033 return nil 1034 } 1035 1036 // Disconnect blocks 1037 1038 func (d *RocksDB) allAddressesScan(lower uint32, higher uint32) ([][]byte, [][]byte, error) { 1039 glog.Infof("db: doing full scan of addresses column") 1040 addrKeys := [][]byte{} 1041 addrValues := [][]byte{} 1042 var totalOutputs, count uint64 1043 var seekKey []byte 1044 for { 1045 var key []byte 1046 it := d.db.NewIteratorCF(d.ro, d.cfh[cfAddresses]) 1047 if totalOutputs == 0 { 1048 it.SeekToFirst() 1049 } else { 1050 it.Seek(seekKey) 1051 it.Next() 1052 } 1053 for count = 0; it.Valid() && count < refreshIterator; it.Next() { 1054 totalOutputs++ 1055 count++ 1056 key = it.Key().Data() 1057 l := len(key) 1058 if l > packedHeightBytes { 1059 height := unpackUint(key[l-packedHeightBytes : l]) 1060 if height >= lower && height <= higher { 1061 addrKey := make([]byte, len(key)) 1062 copy(addrKey, key) 1063 addrKeys = append(addrKeys, addrKey) 1064 value := it.Value().Data() 1065 addrValue := make([]byte, len(value)) 1066 copy(addrValue, value) 1067 addrValues = append(addrValues, addrValue) 1068 } 1069 } 1070 } 1071 seekKey = make([]byte, len(key)) 1072 copy(seekKey, key) 1073 valid := it.Valid() 1074 it.Close() 1075 if !valid { 1076 break 1077 } 1078 } 1079 glog.Infof("rocksdb: scanned %d addresses, found %d to disconnect", totalOutputs, len(addrKeys)) 1080 return addrKeys, addrValues, nil 1081 } 1082 1083 func (d *RocksDB) disconnectTxAddresses(wb *gorocksdb.WriteBatch, height uint32, txid string, inputs []outpoint, txa *TxAddresses, 1084 txAddressesToUpdate map[string]*TxAddresses, balances map[string]*AddrBalance) error { 1085 addresses := make(map[string]struct{}) 1086 getAddressBalance := func(addrDesc bchain.AddressDescriptor) (*AddrBalance, error) { 1087 var err error 1088 s := string(addrDesc) 1089 b, fb := balances[s] 1090 if !fb { 1091 b, err = d.GetAddrDescBalance(addrDesc) 1092 if err != nil { 1093 return nil, err 1094 } 1095 balances[s] = b 1096 } 1097 return b, nil 1098 } 1099 for i, t := range txa.Inputs { 1100 if len(t.AddrDesc) > 0 { 1101 s := string(t.AddrDesc) 1102 _, exist := addresses[s] 1103 if !exist { 1104 addresses[s] = struct{}{} 1105 } 1106 b, err := getAddressBalance(t.AddrDesc) 1107 if err != nil { 1108 return err 1109 } 1110 if b != nil { 1111 // subtract number of txs only once 1112 if !exist { 1113 b.Txs-- 1114 } 1115 b.SentSat.Sub(&b.SentSat, &t.ValueSat) 1116 if b.SentSat.Sign() < 0 { 1117 d.resetValueSatToZero(&b.SentSat, t.AddrDesc, "sent amount") 1118 } 1119 b.BalanceSat.Add(&b.BalanceSat, &t.ValueSat) 1120 } else { 1121 ad, _, _ := d.chainParser.GetAddressesFromAddrDesc(t.AddrDesc) 1122 glog.Warningf("Balance for address %s (%s) not found", ad, t.AddrDesc) 1123 } 1124 s = string(inputs[i].btxID) 1125 sa, exist := txAddressesToUpdate[s] 1126 if !exist { 1127 sa, err = d.getTxAddresses(inputs[i].btxID) 1128 if err != nil { 1129 return err 1130 } 1131 txAddressesToUpdate[s] = sa 1132 } 1133 sa.Outputs[inputs[i].index].Spent = false 1134 } 1135 } 1136 for _, t := range txa.Outputs { 1137 if len(t.AddrDesc) > 0 { 1138 s := string(t.AddrDesc) 1139 _, exist := addresses[s] 1140 if !exist { 1141 addresses[s] = struct{}{} 1142 } 1143 b, err := getAddressBalance(t.AddrDesc) 1144 if err != nil { 1145 return err 1146 } 1147 if b != nil { 1148 // subtract number of txs only once 1149 if !exist { 1150 b.Txs-- 1151 } 1152 b.BalanceSat.Sub(&b.BalanceSat, &t.ValueSat) 1153 if b.BalanceSat.Sign() < 0 { 1154 d.resetValueSatToZero(&b.BalanceSat, t.AddrDesc, "balance") 1155 } 1156 } else { 1157 ad, _, _ := d.chainParser.GetAddressesFromAddrDesc(t.AddrDesc) 1158 glog.Warningf("Balance for address %s (%s) not found", ad, t.AddrDesc) 1159 } 1160 } 1161 } 1162 for a := range addresses { 1163 key := packAddressKey([]byte(a), height) 1164 wb.DeleteCF(d.cfh[cfAddresses], key) 1165 } 1166 return nil 1167 } 1168 1169 // DisconnectBlockRangeUTXO removes all data belonging to blocks in range lower-higher 1170 // if they are in the range kept in the cfBlockTxids column 1171 func (d *RocksDB) DisconnectBlockRangeUTXO(lower uint32, higher uint32) error { 1172 glog.Infof("db: disconnecting blocks %d-%d", lower, higher) 1173 blocks := make([][]blockTxs, higher-lower+1) 1174 for height := lower; height <= higher; height++ { 1175 blockTxs, err := d.getBlockTxs(height) 1176 if err != nil { 1177 return err 1178 } 1179 if len(blockTxs) == 0 { 1180 return errors.Errorf("Cannot disconnect blocks with height %v and lower. It is necessary to rebuild index.", height) 1181 } 1182 blocks[height-lower] = blockTxs 1183 } 1184 wb := gorocksdb.NewWriteBatch() 1185 defer wb.Destroy() 1186 txAddressesToUpdate := make(map[string]*TxAddresses) 1187 txsToDelete := make(map[string]struct{}) 1188 balances := make(map[string]*AddrBalance) 1189 for height := higher; height >= lower; height-- { 1190 blockTxs := blocks[height-lower] 1191 glog.Info("Disconnecting block ", height, " containing ", len(blockTxs), " transactions") 1192 // go backwards to avoid interim negative balance 1193 // when connecting block, amount is first in tx on the output side, then in another tx on the input side 1194 // when disconnecting, it must be done backwards 1195 for i := len(blockTxs) - 1; i >= 0; i-- { 1196 txid := blockTxs[i].btxID 1197 s := string(txid) 1198 txsToDelete[s] = struct{}{} 1199 txa, err := d.getTxAddresses(txid) 1200 if err != nil { 1201 return err 1202 } 1203 if txa == nil { 1204 ut, _ := d.chainParser.UnpackTxid(txid) 1205 glog.Warning("TxAddress for txid ", ut, " not found") 1206 continue 1207 } 1208 if err := d.disconnectTxAddresses(wb, height, s, blockTxs[i].inputs, txa, txAddressesToUpdate, balances); err != nil { 1209 return err 1210 } 1211 } 1212 key := packUint(height) 1213 wb.DeleteCF(d.cfh[cfBlockTxs], key) 1214 wb.DeleteCF(d.cfh[cfHeight], key) 1215 } 1216 d.storeTxAddresses(wb, txAddressesToUpdate) 1217 d.storeBalances(wb, balances) 1218 for s := range txsToDelete { 1219 b := []byte(s) 1220 wb.DeleteCF(d.cfh[cfTransactions], b) 1221 wb.DeleteCF(d.cfh[cfTxAddresses], b) 1222 } 1223 err := d.db.Write(d.wo, wb) 1224 if err == nil { 1225 glog.Infof("rocksdb: blocks %d-%d disconnected", lower, higher) 1226 } 1227 return err 1228 } 1229 1230 // DisconnectBlockRangeNonUTXO performs full range scan to remove a range of blocks 1231 // it is very slow operation 1232 func (d *RocksDB) DisconnectBlockRangeNonUTXO(lower uint32, higher uint32) error { 1233 glog.Infof("db: disconnecting blocks %d-%d", lower, higher) 1234 addrKeys, _, err := d.allAddressesScan(lower, higher) 1235 if err != nil { 1236 return err 1237 } 1238 glog.Infof("rocksdb: about to disconnect %d addresses ", len(addrKeys)) 1239 wb := gorocksdb.NewWriteBatch() 1240 defer wb.Destroy() 1241 for _, addrKey := range addrKeys { 1242 if glog.V(2) { 1243 glog.Info("address ", hex.EncodeToString(addrKey)) 1244 } 1245 // delete address:height from the index 1246 wb.DeleteCF(d.cfh[cfAddresses], addrKey) 1247 } 1248 for height := lower; height <= higher; height++ { 1249 if glog.V(2) { 1250 glog.Info("height ", height) 1251 } 1252 wb.DeleteCF(d.cfh[cfHeight], packUint(height)) 1253 } 1254 err = d.db.Write(d.wo, wb) 1255 if err == nil { 1256 glog.Infof("rocksdb: blocks %d-%d disconnected", lower, higher) 1257 } 1258 return err 1259 } 1260 1261 func dirSize(path string) (int64, error) { 1262 var size int64 1263 err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error { 1264 if err == nil { 1265 if !info.IsDir() { 1266 size += info.Size() 1267 } 1268 } 1269 return err 1270 }) 1271 return size, err 1272 } 1273 1274 // DatabaseSizeOnDisk returns size of the database in bytes 1275 func (d *RocksDB) DatabaseSizeOnDisk() int64 { 1276 size, err := dirSize(d.path) 1277 if err != nil { 1278 glog.Error("rocksdb: DatabaseSizeOnDisk: ", err) 1279 return 0 1280 } 1281 return size 1282 } 1283 1284 // GetTx returns transaction stored in db and height of the block containing it 1285 func (d *RocksDB) GetTx(txid string) (*bchain.Tx, uint32, error) { 1286 key, err := d.chainParser.PackTxid(txid) 1287 if err != nil { 1288 return nil, 0, err 1289 } 1290 val, err := d.db.GetCF(d.ro, d.cfh[cfTransactions], key) 1291 if err != nil { 1292 return nil, 0, err 1293 } 1294 defer val.Free() 1295 data := val.Data() 1296 if len(data) > 4 { 1297 return d.chainParser.UnpackTx(data) 1298 } 1299 return nil, 0, nil 1300 } 1301 1302 // PutTx stores transactions in db 1303 func (d *RocksDB) PutTx(tx *bchain.Tx, height uint32, blockTime int64) error { 1304 key, err := d.chainParser.PackTxid(tx.Txid) 1305 if err != nil { 1306 return nil 1307 } 1308 buf, err := d.chainParser.PackTx(tx, height, blockTime) 1309 if err != nil { 1310 return err 1311 } 1312 err = d.db.PutCF(d.wo, d.cfh[cfTransactions], key, buf) 1313 if err == nil { 1314 d.is.AddDBColumnStats(cfTransactions, 1, int64(len(key)), int64(len(buf))) 1315 } 1316 return err 1317 } 1318 1319 // DeleteTx removes transactions from db 1320 func (d *RocksDB) DeleteTx(txid string) error { 1321 key, err := d.chainParser.PackTxid(txid) 1322 if err != nil { 1323 return nil 1324 } 1325 // use write batch so that this delete matches other deletes 1326 wb := gorocksdb.NewWriteBatch() 1327 defer wb.Destroy() 1328 d.internalDeleteTx(wb, key) 1329 return d.db.Write(d.wo, wb) 1330 } 1331 1332 // internalDeleteTx checks if tx is cached and updates internal state accordingly 1333 func (d *RocksDB) internalDeleteTx(wb *gorocksdb.WriteBatch, key []byte) { 1334 val, err := d.db.GetCF(d.ro, d.cfh[cfTransactions], key) 1335 // ignore error, it is only for statistics 1336 if err == nil { 1337 l := len(val.Data()) 1338 if l > 0 { 1339 d.is.AddDBColumnStats(cfTransactions, -1, int64(-len(key)), int64(-l)) 1340 } 1341 defer val.Free() 1342 } 1343 wb.DeleteCF(d.cfh[cfTransactions], key) 1344 } 1345 1346 // internal state 1347 const internalStateKey = "internalState" 1348 1349 // LoadInternalState loads from db internal state or initializes a new one if not yet stored 1350 func (d *RocksDB) LoadInternalState(rpcCoin string) (*common.InternalState, error) { 1351 val, err := d.db.GetCF(d.ro, d.cfh[cfDefault], []byte(internalStateKey)) 1352 if err != nil { 1353 return nil, err 1354 } 1355 defer val.Free() 1356 data := val.Data() 1357 var is *common.InternalState 1358 if len(data) == 0 { 1359 is = &common.InternalState{Coin: rpcCoin} 1360 } else { 1361 is, err = common.UnpackInternalState(data) 1362 if err != nil { 1363 return nil, err 1364 } 1365 // verify that the rpc coin matches DB coin 1366 // running it mismatched would corrupt the database 1367 if is.Coin == "" { 1368 is.Coin = rpcCoin 1369 } else if is.Coin != rpcCoin { 1370 return nil, errors.Errorf("Coins do not match. DB coin %v, RPC coin %v", is.Coin, rpcCoin) 1371 } 1372 } 1373 // make sure that column stats match the columns 1374 sc := is.DbColumns 1375 nc := make([]common.InternalStateColumn, len(cfNames)) 1376 for i := 0; i < len(nc); i++ { 1377 nc[i].Name = cfNames[i] 1378 nc[i].Version = dbVersion 1379 for j := 0; j < len(sc); j++ { 1380 if sc[j].Name == nc[i].Name { 1381 // check the version of the column, if it does not match, the db is not compatible 1382 if sc[j].Version != dbVersion { 1383 return nil, errors.Errorf("DB version %v of column '%v' does not match the required version %v. DB is not compatible.", sc[j].Version, sc[j].Name, dbVersion) 1384 } 1385 nc[i].Rows = sc[j].Rows 1386 nc[i].KeyBytes = sc[j].KeyBytes 1387 nc[i].ValueBytes = sc[j].ValueBytes 1388 nc[i].Updated = sc[j].Updated 1389 break 1390 } 1391 } 1392 } 1393 is.DbColumns = nc 1394 // after load, reset the synchronization data 1395 is.IsSynchronized = false 1396 is.IsMempoolSynchronized = false 1397 var t time.Time 1398 is.LastMempoolSync = t 1399 is.SyncMode = false 1400 return is, nil 1401 } 1402 1403 // SetInconsistentState sets the internal state to DbStateInconsistent or DbStateOpen based on inconsistent parameter 1404 // db in left in DbStateInconsistent state cannot be used and must be recreated 1405 func (d *RocksDB) SetInconsistentState(inconsistent bool) error { 1406 if d.is == nil { 1407 return errors.New("Internal state not created") 1408 } 1409 if inconsistent { 1410 d.is.DbState = common.DbStateInconsistent 1411 } else { 1412 d.is.DbState = common.DbStateOpen 1413 } 1414 return d.storeState(d.is) 1415 } 1416 1417 // SetInternalState sets the InternalState to be used by db to collect internal state 1418 func (d *RocksDB) SetInternalState(is *common.InternalState) { 1419 d.is = is 1420 } 1421 1422 // StoreInternalState stores the internal state to db 1423 func (d *RocksDB) StoreInternalState(is *common.InternalState) error { 1424 if d.metrics != nil { 1425 for c := 0; c < len(cfNames); c++ { 1426 rows, keyBytes, valueBytes := d.is.GetDBColumnStatValues(c) 1427 d.metrics.DbColumnRows.With(common.Labels{"column": cfNames[c]}).Set(float64(rows)) 1428 d.metrics.DbColumnSize.With(common.Labels{"column": cfNames[c]}).Set(float64(keyBytes + valueBytes)) 1429 } 1430 } 1431 return d.storeState(is) 1432 } 1433 1434 func (d *RocksDB) storeState(is *common.InternalState) error { 1435 buf, err := is.Pack() 1436 if err != nil { 1437 return err 1438 } 1439 return d.db.PutCF(d.wo, d.cfh[cfDefault], []byte(internalStateKey), buf) 1440 } 1441 1442 func (d *RocksDB) computeColumnSize(col int, stopCompute chan os.Signal) (int64, int64, int64, error) { 1443 var rows, keysSum, valuesSum int64 1444 var seekKey []byte 1445 // do not use cache 1446 ro := gorocksdb.NewDefaultReadOptions() 1447 ro.SetFillCache(false) 1448 for { 1449 var key []byte 1450 it := d.db.NewIteratorCF(ro, d.cfh[col]) 1451 if rows == 0 { 1452 it.SeekToFirst() 1453 } else { 1454 glog.Info("db: Column ", cfNames[col], ": rows ", rows, ", key bytes ", keysSum, ", value bytes ", valuesSum, ", in progress...") 1455 it.Seek(seekKey) 1456 it.Next() 1457 } 1458 for count := 0; it.Valid() && count < refreshIterator; it.Next() { 1459 select { 1460 case <-stopCompute: 1461 return 0, 0, 0, errors.New("Interrupted") 1462 default: 1463 } 1464 key = it.Key().Data() 1465 count++ 1466 rows++ 1467 keysSum += int64(len(key)) 1468 valuesSum += int64(len(it.Value().Data())) 1469 } 1470 seekKey = append([]byte{}, key...) 1471 valid := it.Valid() 1472 it.Close() 1473 if !valid { 1474 break 1475 } 1476 } 1477 return rows, keysSum, valuesSum, nil 1478 } 1479 1480 // ComputeInternalStateColumnStats computes stats of all db columns and sets them to internal state 1481 // can be very slow operation 1482 func (d *RocksDB) ComputeInternalStateColumnStats(stopCompute chan os.Signal) error { 1483 start := time.Now() 1484 glog.Info("db: ComputeInternalStateColumnStats start") 1485 for c := 0; c < len(cfNames); c++ { 1486 rows, keysSum, valuesSum, err := d.computeColumnSize(c, stopCompute) 1487 if err != nil { 1488 return err 1489 } 1490 d.is.SetDBColumnStats(c, rows, keysSum, valuesSum) 1491 glog.Info("db: Column ", cfNames[c], ": rows ", rows, ", key bytes ", keysSum, ", value bytes ", valuesSum) 1492 } 1493 glog.Info("db: ComputeInternalStateColumnStats finished in ", time.Since(start)) 1494 return nil 1495 } 1496 1497 // Helpers 1498 1499 func packAddressKey(addrDesc bchain.AddressDescriptor, height uint32) []byte { 1500 bheight := packUint(height) 1501 buf := make([]byte, 0, len(addrDesc)+len(bheight)) 1502 buf = append(buf, addrDesc...) 1503 buf = append(buf, bheight...) 1504 return buf 1505 } 1506 1507 func unpackAddressKey(key []byte) ([]byte, uint32, error) { 1508 i := len(key) - packedHeightBytes 1509 if i <= 0 { 1510 return nil, 0, errors.New("Invalid address key") 1511 } 1512 return key[:i], unpackUint(key[i : i+packedHeightBytes]), nil 1513 } 1514 1515 func packUint(i uint32) []byte { 1516 buf := make([]byte, 4) 1517 binary.BigEndian.PutUint32(buf, i) 1518 return buf 1519 } 1520 1521 func unpackUint(buf []byte) uint32 { 1522 return binary.BigEndian.Uint32(buf) 1523 } 1524 1525 func packVarint32(i int32, buf []byte) int { 1526 return vlq.PutInt(buf, int64(i)) 1527 } 1528 1529 func packVarint(i int, buf []byte) int { 1530 return vlq.PutInt(buf, int64(i)) 1531 } 1532 1533 func packVaruint(i uint, buf []byte) int { 1534 return vlq.PutUint(buf, uint64(i)) 1535 } 1536 1537 func unpackVarint32(buf []byte) (int32, int) { 1538 i, ofs := vlq.Int(buf) 1539 return int32(i), ofs 1540 } 1541 1542 func unpackVarint(buf []byte) (int, int) { 1543 i, ofs := vlq.Int(buf) 1544 return int(i), ofs 1545 } 1546 1547 func unpackVaruint(buf []byte) (uint, int) { 1548 i, ofs := vlq.Uint(buf) 1549 return uint(i), ofs 1550 } 1551 1552 const ( 1553 // number of bits in a big.Word 1554 wordBits = 32 << (uint64(^big.Word(0)) >> 63) 1555 // number of bytes in a big.Word 1556 wordBytes = wordBits / 8 1557 // max packed bigint words 1558 maxPackedBigintWords = (256 - wordBytes) / wordBytes 1559 maxPackedBigintBytes = 249 1560 ) 1561 1562 // big int is packed in BigEndian order without memory allocation as 1 byte length followed by bytes of big int 1563 // number of written bytes is returned 1564 // limitation: bigints longer than 248 bytes are truncated to 248 bytes 1565 // caution: buffer must be big enough to hold the packed big int, buffer 249 bytes big is always safe 1566 func packBigint(bi *big.Int, buf []byte) int { 1567 w := bi.Bits() 1568 lw := len(w) 1569 // zero returns only one byte - zero length 1570 if lw == 0 { 1571 buf[0] = 0 1572 return 1 1573 } 1574 // pack the most significant word in a special way - skip leading zeros 1575 w0 := w[lw-1] 1576 fb := 8 1577 mask := big.Word(0xff) << (wordBits - 8) 1578 for w0&mask == 0 { 1579 fb-- 1580 mask >>= 8 1581 } 1582 for i := fb; i > 0; i-- { 1583 buf[i] = byte(w0) 1584 w0 >>= 8 1585 } 1586 // if the big int is too big (> 2^1984), the number of bytes would not fit to 1 byte 1587 // in this case, truncate the number, it is not expected to work with this big numbers as amounts 1588 s := 0 1589 if lw > maxPackedBigintWords { 1590 s = lw - maxPackedBigintWords 1591 } 1592 // pack the rest of the words in reverse order 1593 for j := lw - 2; j >= s; j-- { 1594 d := w[j] 1595 for i := fb + wordBytes; i > fb; i-- { 1596 buf[i] = byte(d) 1597 d >>= 8 1598 } 1599 fb += wordBytes 1600 } 1601 buf[0] = byte(fb) 1602 return fb + 1 1603 } 1604 1605 func unpackBigint(buf []byte) (big.Int, int) { 1606 var r big.Int 1607 l := int(buf[0]) + 1 1608 r.SetBytes(buf[1:l]) 1609 return r, l 1610 }