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  }