github.com/cryptohub-digital/blockbook-fork@v0.0.0-20230713133354-673c927af7f1/api/worker.go (about)

     1  package api
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"math"
     8  	"math/big"
     9  	"os"
    10  	"sort"
    11  	"strconv"
    12  	"strings"
    13  	"sync"
    14  	"time"
    15  
    16  	"github.com/cryptohub-digital/blockbook-fork/bchain"
    17  	"github.com/cryptohub-digital/blockbook-fork/bchain/coins/eth"
    18  	"github.com/cryptohub-digital/blockbook-fork/bchain/coins/xcb"
    19  	"github.com/cryptohub-digital/blockbook-fork/common"
    20  	"github.com/cryptohub-digital/blockbook-fork/db"
    21  	"github.com/cryptohub-digital/blockbook-fork/fiat"
    22  	"github.com/golang/glog"
    23  	"github.com/juju/errors"
    24  )
    25  
    26  // Worker is handle to api worker
    27  type Worker struct {
    28  	db                *db.RocksDB
    29  	txCache           *db.TxCache
    30  	chain             bchain.BlockChain
    31  	chainParser       bchain.BlockChainParser
    32  	chainType         bchain.ChainType
    33  	useAddressAliases bool
    34  	mempool           bchain.Mempool
    35  	is                *common.InternalState
    36  	fiatRates         *fiat.FiatRates
    37  	metrics           *common.Metrics
    38  }
    39  
    40  // NewWorker creates new api worker
    41  func NewWorker(db *db.RocksDB, chain bchain.BlockChain, mempool bchain.Mempool, txCache *db.TxCache, metrics *common.Metrics, is *common.InternalState, fiatRates *fiat.FiatRates) (*Worker, error) {
    42  	w := &Worker{
    43  		db:                db,
    44  		txCache:           txCache,
    45  		chain:             chain,
    46  		chainParser:       chain.GetChainParser(),
    47  		chainType:         chain.GetChainParser().GetChainType(),
    48  		useAddressAliases: chain.GetChainParser().UseAddressAliases(),
    49  		mempool:           mempool,
    50  		is:                is,
    51  		fiatRates:         fiatRates,
    52  		metrics:           metrics,
    53  	}
    54  	if w.chainType == bchain.ChainBitcoinType {
    55  		w.initXpubCache()
    56  	}
    57  	return w, nil
    58  }
    59  
    60  func (w *Worker) getAddressesFromVout(vout *bchain.Vout) (bchain.AddressDescriptor, []string, bool, error) {
    61  	addrDesc, err := w.chainParser.GetAddrDescFromVout(vout)
    62  	if err != nil {
    63  		return nil, nil, false, err
    64  	}
    65  	a, s, err := w.chainParser.GetAddressesFromAddrDesc(addrDesc)
    66  	return addrDesc, a, s, err
    67  }
    68  
    69  // setSpendingTxToVout is helper function, that finds transaction that spent given output and sets it to the output
    70  // there is no direct index for the operation, it must be found using addresses -> txaddresses -> tx
    71  func (w *Worker) setSpendingTxToVout(vout *Vout, txid string, height uint32) error {
    72  	err := w.db.GetAddrDescTransactions(vout.AddrDesc, height, maxUint32, func(t string, height uint32, indexes []int32) error {
    73  		for _, index := range indexes {
    74  			// take only inputs
    75  			if index < 0 {
    76  				index = ^index
    77  				tsp, err := w.db.GetTxAddresses(t)
    78  				if err != nil {
    79  					return err
    80  				} else if tsp == nil {
    81  					glog.Warning("DB inconsistency:  tx ", t, ": not found in txAddresses")
    82  				} else if len(tsp.Inputs) > int(index) {
    83  					if tsp.Inputs[index].ValueSat.Cmp((*big.Int)(vout.ValueSat)) == 0 {
    84  						spentTx, spentHeight, err := w.txCache.GetTransaction(t)
    85  						if err != nil {
    86  							glog.Warning("Tx ", t, ": not found")
    87  						} else {
    88  							if len(spentTx.Vin) > int(index) {
    89  								if spentTx.Vin[index].Txid == txid {
    90  									vout.SpentTxID = t
    91  									vout.SpentHeight = int(spentHeight)
    92  									vout.SpentIndex = int(index)
    93  									return &db.StopIteration{}
    94  								}
    95  							}
    96  						}
    97  					}
    98  				}
    99  			}
   100  		}
   101  		return nil
   102  	})
   103  	return err
   104  }
   105  
   106  // GetSpendingTxid returns transaction id of transaction that spent given output
   107  func (w *Worker) GetSpendingTxid(txid string, n int) (string, error) {
   108  	if w.db.HasExtendedIndex() {
   109  		tsp, err := w.db.GetTxAddresses(txid)
   110  		if err != nil {
   111  			return "", err
   112  		} else if tsp == nil {
   113  			glog.Warning("DB inconsistency:  tx ", txid, ": not found in txAddresses")
   114  			return "", NewAPIError(fmt.Sprintf("Txid %v not found", txid), false)
   115  		}
   116  		if n >= len(tsp.Outputs) || n < 0 {
   117  			return "", NewAPIError(fmt.Sprintf("Passed incorrect vout index %v for tx %v, len vout %v", n, txid, len(tsp.Outputs)), false)
   118  		}
   119  		return tsp.Outputs[n].SpentTxid, nil
   120  	}
   121  	start := time.Now()
   122  	tx, err := w.getTransaction(txid, false, false, nil)
   123  	if err != nil {
   124  		return "", err
   125  	}
   126  	if n >= len(tx.Vout) || n < 0 {
   127  		return "", NewAPIError(fmt.Sprintf("Passed incorrect vout index %v for tx %v, len vout %v", n, tx.Txid, len(tx.Vout)), false)
   128  	}
   129  	err = w.setSpendingTxToVout(&tx.Vout[n], tx.Txid, uint32(tx.Blockheight))
   130  	if err != nil {
   131  		return "", err
   132  	}
   133  	glog.Info("GetSpendingTxid ", txid, " ", n, ", ", time.Since(start))
   134  	return tx.Vout[n].SpentTxID, nil
   135  }
   136  
   137  func aggregateAddress(m map[string]struct{}, a string) {
   138  	if m != nil && len(a) > 0 {
   139  		m[a] = struct{}{}
   140  	}
   141  }
   142  
   143  func aggregateAddresses(m map[string]struct{}, addresses []string, isAddress bool) {
   144  	if m != nil && isAddress {
   145  		for _, a := range addresses {
   146  			if len(a) > 0 {
   147  				m[a] = struct{}{}
   148  			}
   149  		}
   150  	}
   151  }
   152  
   153  func (w *Worker) newAddressesMapForAliases() map[string]struct{} {
   154  	// return non nil map only if the chain supports address aliases
   155  	if w.useAddressAliases {
   156  		return make(map[string]struct{})
   157  	}
   158  	// returning nil disables the processing of the address aliases
   159  	return nil
   160  }
   161  
   162  func (w *Worker) getAddressAliases(addresses map[string]struct{}) AddressAliasesMap {
   163  	if len(addresses) > 0 {
   164  		aliases := make(AddressAliasesMap)
   165  		var t string
   166  		if w.chainType == bchain.ChainEthereumType {
   167  			t = "ENS"
   168  		} else {
   169  			t = "Alias"
   170  		}
   171  		for a := range addresses {
   172  			if w.chainType == bchain.ChainEthereumType {
   173  				ci, err := w.db.GetContractInfoForAddress(a)
   174  				if err == nil && ci != nil && ci.Name != "" {
   175  					aliases[a] = AddressAlias{Type: "Contract", Alias: ci.Name}
   176  				}
   177  			}
   178  			n := w.db.GetAddressAlias(a)
   179  			if len(n) > 0 {
   180  				aliases[a] = AddressAlias{Type: t, Alias: n}
   181  			}
   182  		}
   183  		return aliases
   184  	}
   185  	return nil
   186  }
   187  
   188  // GetTransaction reads transaction data from txid
   189  func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool) (*Tx, error) {
   190  	addresses := w.newAddressesMapForAliases()
   191  	tx, err := w.getTransaction(txid, spendingTxs, specificJSON, addresses)
   192  	if err != nil {
   193  		return nil, err
   194  	}
   195  	tx.AddressAliases = w.getAddressAliases(addresses)
   196  	return tx, nil
   197  }
   198  
   199  // getTransaction reads transaction data from txid
   200  func (w *Worker) getTransaction(txid string, spendingTxs bool, specificJSON bool, addresses map[string]struct{}) (*Tx, error) {
   201  	bchainTx, height, err := w.txCache.GetTransaction(txid)
   202  	if err != nil {
   203  		if err == bchain.ErrTxNotFound {
   204  			return nil, NewAPIError(fmt.Sprintf("Transaction '%v' not found", txid), true)
   205  		}
   206  		return nil, NewAPIError(fmt.Sprintf("Transaction '%v' not found (%v)", txid, err), true)
   207  	}
   208  	return w.getTransactionFromBchainTx(bchainTx, height, spendingTxs, specificJSON, addresses)
   209  }
   210  
   211  func (w *Worker) getParsedEthereumInputData(data string) *bchain.EthereumParsedInputData {
   212  	var err error
   213  	var signatures *[]bchain.FourByteSignature
   214  	fourBytes := eth.GetSignatureFromData(data)
   215  	if fourBytes != 0 {
   216  		signatures, err = w.db.GetFourByteSignatures(fourBytes)
   217  		if err != nil {
   218  			glog.Errorf("GetFourByteSignatures(%v) error %v", fourBytes, err)
   219  			return nil
   220  		}
   221  		if signatures == nil {
   222  			return nil
   223  		}
   224  	}
   225  	return eth.ParseInputData(signatures, data)
   226  }
   227  
   228  // getConfirmationETA returns confirmation ETA in seconds and blocks
   229  func (w *Worker) getConfirmationETA(tx *Tx) (int64, uint32) {
   230  	var etaBlocks uint32
   231  	var etaSeconds int64
   232  	if w.chainType == bchain.ChainBitcoinType && tx.FeesSat != nil {
   233  		_, _, mempoolSize := w.is.GetMempoolSyncState()
   234  		// if there are a few transactions in the mempool, the estimate fee does not work well
   235  		// and the tx is most probably going to be confirmed in the first block
   236  		if mempoolSize < 32 {
   237  			etaBlocks = 1
   238  		} else {
   239  			var txFeePerKB int64
   240  			if tx.VSize > 0 {
   241  				txFeePerKB = 1000 * tx.FeesSat.AsInt64() / int64(tx.VSize)
   242  			} else if tx.Size > 0 {
   243  				txFeePerKB = 1000 * tx.FeesSat.AsInt64() / int64(tx.Size)
   244  			}
   245  			if txFeePerKB > 0 {
   246  				// binary search the estimate, split it to more common first 7 blocks and the rest up to 70 blocks
   247  				var b int
   248  				fee, _ := w.cachedEstimateFee(7, true)
   249  				if fee.Int64() <= txFeePerKB {
   250  					b = sort.Search(7, func(i int) bool {
   251  						// fee is in sats/kB
   252  						fee, _ := w.cachedEstimateFee(i+1, true)
   253  						return fee.Int64() <= txFeePerKB
   254  					})
   255  					b += 1
   256  				} else {
   257  					b = sort.Search(63, func(i int) bool {
   258  						fee, _ := w.cachedEstimateFee(i+7, true)
   259  						return fee.Int64() <= txFeePerKB
   260  					})
   261  					b += 7
   262  				}
   263  				etaBlocks = uint32(b)
   264  			}
   265  		}
   266  		etaSeconds = int64(etaBlocks * w.is.AvgBlockPeriod)
   267  	}
   268  	return etaSeconds, etaBlocks
   269  }
   270  
   271  // getTransactionFromBchainTx reads transaction data from txid
   272  func (w *Worker) getTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spendingTxs bool, specificJSON bool, addresses map[string]struct{}) (*Tx, error) {
   273  	var err error
   274  	var ta *db.TxAddresses
   275  	var tokens []TokenTransfer
   276  	var ethSpecific *EthereumSpecific
   277  	var xcbSpecific *CoreCoinSpecific
   278  	var blockhash string
   279  	if bchainTx.Confirmations > 0 {
   280  		if w.chainType == bchain.ChainBitcoinType {
   281  			ta, err = w.db.GetTxAddresses(bchainTx.Txid)
   282  			if err != nil {
   283  				return nil, errors.Annotatef(err, "GetTxAddresses %v", bchainTx.Txid)
   284  			}
   285  		}
   286  		blockhash, err = w.db.GetBlockHash(uint32(height))
   287  		if err != nil {
   288  			return nil, errors.Annotatef(err, "GetBlockHash %v", height)
   289  		}
   290  	}
   291  	var valInSat, valOutSat, feesSat big.Int
   292  	var pValInSat *big.Int
   293  	vins := make([]Vin, len(bchainTx.Vin))
   294  	rbf := false
   295  	for i := range bchainTx.Vin {
   296  		bchainVin := &bchainTx.Vin[i]
   297  		vin := &vins[i]
   298  		vin.Txid = bchainVin.Txid
   299  		vin.N = i
   300  		vin.Vout = bchainVin.Vout
   301  		vin.Sequence = int64(bchainVin.Sequence)
   302  		// detect explicit Replace-by-Fee transactions as defined by BIP125
   303  		if bchainTx.Confirmations == 0 && bchainVin.Sequence < 0xffffffff-1 {
   304  			rbf = true
   305  		}
   306  		vin.Hex = bchainVin.ScriptSig.Hex
   307  		vin.Coinbase = bchainVin.Coinbase
   308  		if w.chainType == bchain.ChainBitcoinType {
   309  			//  bchainVin.Txid=="" is coinbase transaction
   310  			if bchainVin.Txid != "" {
   311  				// load spending addresses from TxAddresses
   312  				tas, err := w.db.GetTxAddresses(bchainVin.Txid)
   313  				if err != nil {
   314  					return nil, errors.Annotatef(err, "GetTxAddresses %v", bchainVin.Txid)
   315  				}
   316  				if tas == nil {
   317  					// try to load from backend
   318  					otx, _, err := w.txCache.GetTransaction(bchainVin.Txid)
   319  					if err != nil {
   320  						if err == bchain.ErrTxNotFound {
   321  							// try to get AddrDesc using coin specific handling and continue processing the tx
   322  							vin.AddrDesc = w.chainParser.GetAddrDescForUnknownInput(bchainTx, i)
   323  							vin.Addresses, vin.IsAddress, err = w.chainParser.GetAddressesFromAddrDesc(vin.AddrDesc)
   324  							if err != nil {
   325  								glog.Warning("GetAddressesFromAddrDesc tx ", bchainVin.Txid, ", addrDesc ", vin.AddrDesc, ": ", err)
   326  							}
   327  							aggregateAddresses(addresses, vin.Addresses, vin.IsAddress)
   328  							continue
   329  						}
   330  						return nil, errors.Annotatef(err, "txCache.GetTransaction %v", bchainVin.Txid)
   331  					}
   332  					// mempool transactions are not in TxAddresses but confirmed should be there, log a problem
   333  					// ignore when Confirmations==1, it may be just a timing problem
   334  					if bchainTx.Confirmations > 1 {
   335  						glog.Warning("DB inconsistency:  tx ", bchainVin.Txid, ": not found in txAddresses, confirmations ", bchainTx.Confirmations)
   336  					}
   337  					if len(otx.Vout) > int(vin.Vout) {
   338  						vout := &otx.Vout[vin.Vout]
   339  						vin.ValueSat = (*Amount)(&vout.ValueSat)
   340  						vin.AddrDesc, vin.Addresses, vin.IsAddress, err = w.getAddressesFromVout(vout)
   341  						if err != nil {
   342  							glog.Errorf("getAddressesFromVout error %v, vout %+v", err, vout)
   343  						}
   344  						aggregateAddresses(addresses, vin.Addresses, vin.IsAddress)
   345  					}
   346  				} else {
   347  					if len(tas.Outputs) > int(vin.Vout) {
   348  						output := &tas.Outputs[vin.Vout]
   349  						vin.ValueSat = (*Amount)(&output.ValueSat)
   350  						vin.AddrDesc = output.AddrDesc
   351  						vin.Addresses, vin.IsAddress, err = output.Addresses(w.chainParser)
   352  						if err != nil {
   353  							glog.Errorf("output.Addresses error %v, tx %v, output %v", err, bchainVin.Txid, i)
   354  						}
   355  						aggregateAddresses(addresses, vin.Addresses, vin.IsAddress)
   356  					}
   357  				}
   358  				if vin.ValueSat != nil {
   359  					valInSat.Add(&valInSat, (*big.Int)(vin.ValueSat))
   360  				}
   361  			}
   362  		} else if w.chainType == bchain.ChainEthereumType || w.chainType == bchain.ChainCoreCoinType {
   363  			if len(bchainVin.Addresses) > 0 {
   364  				vin.AddrDesc, err = w.chainParser.GetAddrDescFromAddress(bchainVin.Addresses[0])
   365  				if err != nil {
   366  					glog.Errorf("GetAddrDescFromAddress error %v, tx %v, bchainVin %v", err, bchainTx.Txid, bchainVin)
   367  				}
   368  				vin.Addresses = bchainVin.Addresses
   369  				vin.IsAddress = true
   370  				aggregateAddresses(addresses, vin.Addresses, vin.IsAddress)
   371  			}
   372  		}
   373  	}
   374  	vouts := make([]Vout, len(bchainTx.Vout))
   375  	for i := range bchainTx.Vout {
   376  		bchainVout := &bchainTx.Vout[i]
   377  		vout := &vouts[i]
   378  		vout.N = i
   379  		vout.ValueSat = (*Amount)(&bchainVout.ValueSat)
   380  		valOutSat.Add(&valOutSat, &bchainVout.ValueSat)
   381  		vout.Hex = bchainVout.ScriptPubKey.Hex
   382  		vout.AddrDesc, vout.Addresses, vout.IsAddress, err = w.getAddressesFromVout(bchainVout)
   383  		if err != nil {
   384  			glog.V(2).Infof("getAddressesFromVout error %v, %v, output %v", err, bchainTx.Txid, bchainVout.N)
   385  		}
   386  		aggregateAddresses(addresses, vout.Addresses, vout.IsAddress)
   387  		if ta != nil {
   388  			vout.Spent = ta.Outputs[i].Spent
   389  			if vout.Spent {
   390  				if w.db.HasExtendedIndex() {
   391  					vout.SpentTxID = ta.Outputs[i].SpentTxid
   392  					vout.SpentIndex = int(ta.Outputs[i].SpentIndex)
   393  					vout.SpentHeight = int(ta.Outputs[i].SpentHeight)
   394  				} else if spendingTxs {
   395  					err = w.setSpendingTxToVout(vout, bchainTx.Txid, uint32(height))
   396  					if err != nil {
   397  						glog.Errorf("setSpendingTxToVout error %v, %v, output %v", err, vout.AddrDesc, vout.N)
   398  					}
   399  				}
   400  			}
   401  		}
   402  	}
   403  	if w.chainType == bchain.ChainBitcoinType {
   404  		// for coinbase transactions valIn is 0
   405  		feesSat.Sub(&valInSat, &valOutSat)
   406  		if feesSat.Sign() == -1 {
   407  			feesSat.SetUint64(0)
   408  		}
   409  		pValInSat = &valInSat
   410  	} else if w.chainType == bchain.ChainEthereumType {
   411  		tokenTransfers, err := w.chainParser.EthereumTypeGetTokenTransfersFromTx(bchainTx)
   412  		if err != nil {
   413  			glog.Errorf("GetTokenTransfersFromTx error %v, %v", err, bchainTx)
   414  		}
   415  		tokens = w.getEthereumTokensTransfers(tokenTransfers, addresses)
   416  		ethTxData := eth.GetEthereumTxData(bchainTx)
   417  
   418  		var internalData *bchain.EthereumInternalData
   419  		if eth.ProcessInternalTransactions {
   420  			internalData, err = w.db.GetEthereumInternalData(bchainTx.Txid)
   421  			if err != nil {
   422  				return nil, err
   423  			}
   424  		}
   425  
   426  		parsedInputData := w.getParsedEthereumInputData(ethTxData.Data)
   427  
   428  		// mempool txs do not have fees yet
   429  		if ethTxData.GasUsed != nil {
   430  			feesSat.Mul(ethTxData.GasPrice, ethTxData.GasUsed)
   431  		}
   432  		if len(bchainTx.Vout) > 0 {
   433  			valOutSat = bchainTx.Vout[0].ValueSat
   434  		}
   435  		ethSpecific = &EthereumSpecific{
   436  			GasLimit:   ethTxData.GasLimit,
   437  			GasPrice:   (*Amount)(ethTxData.GasPrice),
   438  			GasUsed:    ethTxData.GasUsed,
   439  			Nonce:      ethTxData.Nonce,
   440  			Status:     ethTxData.Status,
   441  			Data:       ethTxData.Data,
   442  			ParsedData: parsedInputData,
   443  		}
   444  		if internalData != nil {
   445  			ethSpecific.Type = internalData.Type
   446  			ethSpecific.CreatedContract = internalData.Contract
   447  			ethSpecific.Error = internalData.Error
   448  			ethSpecific.InternalTransfers = make([]EthereumInternalTransfer, len(internalData.Transfers))
   449  			for i := range internalData.Transfers {
   450  				f := &internalData.Transfers[i]
   451  				t := &ethSpecific.InternalTransfers[i]
   452  				t.From = f.From
   453  				aggregateAddress(addresses, t.From)
   454  				t.To = f.To
   455  				aggregateAddress(addresses, t.To)
   456  				t.Type = f.Type
   457  				t.Value = (*Amount)(&f.Value)
   458  			}
   459  		}
   460  
   461  	} else if w.chainType == bchain.ChainCoreCoinType {
   462  		tokenTransfers, err := w.chainParser.CoreCoinTypeGetTokenTransfersFromTx(bchainTx)
   463  		if err != nil {
   464  			glog.Errorf("CoreCoinTypeGetTokenTransfersFromTx error %v, %v", err, bchainTx)
   465  		}
   466  		tokens = w.getCoreCoinTokens(tokenTransfers)
   467  		xcbTxData := xcb.GetCoreCoinTxData(bchainTx)
   468  		// mempool txs do not have fees yet
   469  		if xcbTxData.EnergyUsed != nil {
   470  			feesSat.Mul(xcbTxData.EnergyPrice, xcbTxData.EnergyUsed)
   471  		}
   472  		if len(bchainTx.Vout) > 0 {
   473  			valOutSat = bchainTx.Vout[0].ValueSat
   474  		}
   475  		xcbSpecific = &CoreCoinSpecific{
   476  			EnergyLimit: xcbTxData.EnergyLimit,
   477  			EnergyPrice: (*Amount)(xcbTxData.EnergyPrice),
   478  			EnergyUsed:  xcbTxData.EnergyUsed,
   479  			Nonce:       xcbTxData.Nonce,
   480  			Status:      xcbTxData.Status,
   481  			Data:        xcbTxData.Data,
   482  		}
   483  
   484  	}
   485  	var sj json.RawMessage
   486  	// return CoinSpecificData for all mempool transactions or if requested
   487  	if specificJSON || bchainTx.Confirmations == 0 {
   488  		sj, err = w.chain.GetTransactionSpecific(bchainTx)
   489  		if err != nil {
   490  			return nil, err
   491  		}
   492  	}
   493  	r := &Tx{
   494  		Blockhash:        blockhash,
   495  		Blockheight:      height,
   496  		Blocktime:        bchainTx.Blocktime,
   497  		Confirmations:    bchainTx.Confirmations,
   498  		FeesSat:          (*Amount)(&feesSat),
   499  		Locktime:         bchainTx.LockTime,
   500  		Txid:             bchainTx.Txid,
   501  		ValueInSat:       (*Amount)(pValInSat),
   502  		ValueOutSat:      (*Amount)(&valOutSat),
   503  		Version:          bchainTx.Version,
   504  		Size:             len(bchainTx.Hex) >> 1,
   505  		VSize:            int(bchainTx.VSize),
   506  		Hex:              bchainTx.Hex,
   507  		Rbf:              rbf,
   508  		Vin:              vins,
   509  		Vout:             vouts,
   510  		CoinSpecificData: sj,
   511  		TokenTransfers:   tokens,
   512  		EthereumSpecific: ethSpecific,
   513  		CoreCoinSpecific: xcbSpecific,
   514  	}
   515  	if bchainTx.Confirmations == 0 {
   516  		r.Blocktime = int64(w.mempool.GetTransactionTime(bchainTx.Txid))
   517  		r.ConfirmationETASeconds, r.ConfirmationETABlocks = w.getConfirmationETA(r)
   518  	}
   519  	return r, nil
   520  }
   521  
   522  // GetTransactionFromMempoolTx converts bchain.MempoolTx to Tx, with limited amount of data
   523  // it is not doing any request to backend or to db
   524  func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx, error) {
   525  	var err error
   526  	var valInSat, valOutSat, feesSat big.Int
   527  	var pValInSat *big.Int
   528  	var tokens []TokenTransfer
   529  	var ethSpecific *EthereumSpecific
   530  	var xcbSpecific *CoreCoinSpecific
   531  	addresses := w.newAddressesMapForAliases()
   532  	vins := make([]Vin, len(mempoolTx.Vin))
   533  	rbf := false
   534  	for i := range mempoolTx.Vin {
   535  		bchainVin := &mempoolTx.Vin[i]
   536  		vin := &vins[i]
   537  		vin.Txid = bchainVin.Txid
   538  		vin.N = i
   539  		vin.Vout = bchainVin.Vout
   540  		vin.Sequence = int64(bchainVin.Sequence)
   541  		// detect explicit Replace-by-Fee transactions as defined by BIP125
   542  		if bchainVin.Sequence < 0xffffffff-1 {
   543  			rbf = true
   544  		}
   545  		vin.Hex = bchainVin.ScriptSig.Hex
   546  		vin.Coinbase = bchainVin.Coinbase
   547  		if w.chainType == bchain.ChainBitcoinType {
   548  			//  bchainVin.Txid=="" is coinbase transaction
   549  			if bchainVin.Txid != "" {
   550  				vin.ValueSat = (*Amount)(&bchainVin.ValueSat)
   551  				vin.AddrDesc = bchainVin.AddrDesc
   552  				vin.Addresses, vin.IsAddress, _ = w.chainParser.GetAddressesFromAddrDesc(vin.AddrDesc)
   553  				if vin.ValueSat != nil {
   554  					valInSat.Add(&valInSat, (*big.Int)(vin.ValueSat))
   555  				}
   556  				aggregateAddresses(addresses, vin.Addresses, vin.IsAddress)
   557  			}
   558  		} else if w.chainType == bchain.ChainEthereumType || w.chainType == bchain.ChainCoreCoinType {
   559  			if len(bchainVin.Addresses) > 0 {
   560  				vin.AddrDesc, err = w.chainParser.GetAddrDescFromAddress(bchainVin.Addresses[0])
   561  				if err != nil {
   562  					glog.Errorf("GetAddrDescFromAddress error %v, tx %v, bchainVin %v", err, mempoolTx.Txid, bchainVin)
   563  				}
   564  				vin.Addresses = bchainVin.Addresses
   565  				vin.IsAddress = true
   566  				aggregateAddresses(addresses, vin.Addresses, vin.IsAddress)
   567  			}
   568  		}
   569  	}
   570  	vouts := make([]Vout, len(mempoolTx.Vout))
   571  	for i := range mempoolTx.Vout {
   572  		bchainVout := &mempoolTx.Vout[i]
   573  		vout := &vouts[i]
   574  		vout.N = i
   575  		vout.ValueSat = (*Amount)(&bchainVout.ValueSat)
   576  		valOutSat.Add(&valOutSat, &bchainVout.ValueSat)
   577  		vout.Hex = bchainVout.ScriptPubKey.Hex
   578  		vout.AddrDesc, vout.Addresses, vout.IsAddress, err = w.getAddressesFromVout(bchainVout)
   579  		if err != nil {
   580  			glog.V(2).Infof("getAddressesFromVout error %v, %v, output %v", err, mempoolTx.Txid, bchainVout.N)
   581  		}
   582  		aggregateAddresses(addresses, vout.Addresses, vout.IsAddress)
   583  	}
   584  	if w.chainType == bchain.ChainBitcoinType {
   585  		// for coinbase transactions valIn is 0
   586  		feesSat.Sub(&valInSat, &valOutSat)
   587  		if feesSat.Sign() == -1 {
   588  			feesSat.SetUint64(0)
   589  		}
   590  		pValInSat = &valInSat
   591  	} else if w.chainType == bchain.ChainEthereumType {
   592  		if len(mempoolTx.Vout) > 0 {
   593  			valOutSat = mempoolTx.Vout[0].ValueSat
   594  		}
   595  		tokens = w.getEthereumTokensTransfers(mempoolTx.TokenTransfers, addresses)
   596  		ethTxData := eth.GetEthereumTxDataFromSpecificData(mempoolTx.CoinSpecificData)
   597  		ethSpecific = &EthereumSpecific{
   598  			GasLimit: ethTxData.GasLimit,
   599  			GasPrice: (*Amount)(ethTxData.GasPrice),
   600  			GasUsed:  ethTxData.GasUsed,
   601  			Nonce:    ethTxData.Nonce,
   602  			Status:   ethTxData.Status,
   603  			Data:     ethTxData.Data,
   604  		}
   605  	} else if w.chainType == bchain.ChainCoreCoinType {
   606  		if len(mempoolTx.Vout) > 0 {
   607  			valOutSat = mempoolTx.Vout[0].ValueSat
   608  		}
   609  		tokens = w.getCoreCoinTokens(mempoolTx.TokenTransfers)
   610  		xcbTxData := xcb.GetCoreCoinTxDataFromSpecificData(mempoolTx.CoinSpecificData)
   611  		xcbSpecific = &CoreCoinSpecific{
   612  			EnergyLimit: xcbTxData.EnergyLimit,
   613  			EnergyPrice: (*Amount)(xcbTxData.EnergyPrice),
   614  			EnergyUsed:  xcbTxData.EnergyUsed,
   615  			Nonce:       xcbTxData.Nonce,
   616  			Status:      xcbTxData.Status,
   617  			Data:        xcbTxData.Data,
   618  		}
   619  	}
   620  	r := &Tx{
   621  		Blocktime:        mempoolTx.Blocktime,
   622  		FeesSat:          (*Amount)(&feesSat),
   623  		Locktime:         mempoolTx.LockTime,
   624  		Txid:             mempoolTx.Txid,
   625  		ValueInSat:       (*Amount)(pValInSat),
   626  		ValueOutSat:      (*Amount)(&valOutSat),
   627  		Version:          mempoolTx.Version,
   628  		Size:             len(mempoolTx.Hex) >> 1,
   629  		VSize:            int(mempoolTx.VSize),
   630  		Hex:              mempoolTx.Hex,
   631  		Rbf:              rbf,
   632  		Vin:              vins,
   633  		Vout:             vouts,
   634  		TokenTransfers:   tokens,
   635  		EthereumSpecific: ethSpecific,
   636  		CoreCoinSpecific: xcbSpecific,
   637  		AddressAliases:   w.getAddressAliases(addresses),
   638  	}
   639  	r.ConfirmationETASeconds, r.ConfirmationETABlocks = w.getConfirmationETA(r)
   640  	return r, nil
   641  }
   642  
   643  func (w *Worker) getContractInfo(contract string, typeFromContext bchain.TokenTypeName) (*bchain.ContractInfo, bool, error) {
   644  	cd, err := w.chainParser.GetAddrDescFromAddress(contract)
   645  	if err != nil {
   646  		return nil, false, err
   647  	}
   648  	return w.getContractDescriptorInfo(cd, typeFromContext)
   649  }
   650  
   651  func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, typeFromContext bchain.TokenTypeName) (*bchain.ContractInfo, bool, error) {
   652  	var err error
   653  	validContract := true
   654  	contractInfo, err := w.db.GetContractInfo(cd, typeFromContext)
   655  	if err != nil {
   656  		return nil, false, err
   657  	}
   658  	if contractInfo == nil {
   659  		// log warning only if the contract should have been known from processing of the internal data
   660  		if eth.ProcessInternalTransactions {
   661  			glog.Warningf("Contract %v %v not found in DB", cd, typeFromContext)
   662  		}
   663  		contractInfo, err = w.chain.GetContractInfo(cd)
   664  		if err != nil {
   665  			glog.Errorf("GetContractInfo from chain error %v, contract %v", err, cd)
   666  		}
   667  		if contractInfo == nil {
   668  			contractInfo = &bchain.ContractInfo{Type: bchain.UnknownTokenType, Decimals: w.chainParser.AmountDecimals()}
   669  			addresses, _, _ := w.chainParser.GetAddressesFromAddrDesc(cd)
   670  			if len(addresses) > 0 {
   671  				contractInfo.Contract = addresses[0]
   672  			}
   673  
   674  			validContract = false
   675  		} else {
   676  			if typeFromContext != bchain.UnknownTokenType && contractInfo.Type == bchain.UnknownTokenType {
   677  				contractInfo.Type = typeFromContext
   678  			}
   679  			if err = w.db.StoreContractInfo(contractInfo); err != nil {
   680  				glog.Errorf("StoreContractInfo error %v, contract %v", err, cd)
   681  			}
   682  		}
   683  	} else if (len(contractInfo.Name) > 0 && contractInfo.Name[0] == 0) || (len(contractInfo.Symbol) > 0 && contractInfo.Symbol[0] == 0) {
   684  		// fix contract name/symbol that was parsed as a string consisting of zeroes
   685  		blockchainContractInfo, err := w.chain.GetContractInfo(cd)
   686  		if err != nil {
   687  			glog.Errorf("GetContractInfo from chain error %v, contract %v", err, cd)
   688  		} else {
   689  			if blockchainContractInfo != nil && len(blockchainContractInfo.Name) > 0 && blockchainContractInfo.Name[0] != 0 {
   690  				contractInfo.Name = blockchainContractInfo.Name
   691  			} else {
   692  				contractInfo.Name = ""
   693  			}
   694  			if blockchainContractInfo != nil && len(blockchainContractInfo.Symbol) > 0 && blockchainContractInfo.Symbol[0] != 0 {
   695  				contractInfo.Symbol = blockchainContractInfo.Symbol
   696  			} else {
   697  				contractInfo.Symbol = ""
   698  			}
   699  			if blockchainContractInfo != nil {
   700  				contractInfo.Decimals = blockchainContractInfo.Decimals
   701  			}
   702  			if err = w.db.StoreContractInfo(contractInfo); err != nil {
   703  				glog.Errorf("StoreContractInfo error %v, contract %v", err, cd)
   704  			}
   705  		}
   706  	}
   707  	return contractInfo, validContract, nil
   708  }
   709  
   710  func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers, addresses map[string]struct{}) []TokenTransfer {
   711  	sort.Sort(transfers)
   712  	tokens := make([]TokenTransfer, len(transfers))
   713  	for i := range transfers {
   714  		t := transfers[i]
   715  		typeName := bchain.TokenTypeMap[t.Type]
   716  		contractInfo, _, err := w.getContractInfo(t.Contract, typeName)
   717  		if err != nil {
   718  			glog.Errorf("getContractInfo error %v, contract %v", err, t.Contract)
   719  			continue
   720  		}
   721  		var value *Amount
   722  		var values []MultiTokenValue
   723  		if t.Type == bchain.MultiToken {
   724  			values = make([]MultiTokenValue, len(t.MultiTokenValues))
   725  			for j := range values {
   726  				values[j].Id = (*Amount)(&t.MultiTokenValues[j].Id)
   727  				values[j].Value = (*Amount)(&t.MultiTokenValues[j].Value)
   728  			}
   729  		} else {
   730  			value = (*Amount)(&t.Value)
   731  		}
   732  		aggregateAddress(addresses, t.From)
   733  		aggregateAddress(addresses, t.To)
   734  		tokens[i] = TokenTransfer{
   735  			Type:             typeName,
   736  			Contract:         t.Contract,
   737  			From:             t.From,
   738  			To:               t.To,
   739  			Value:            value,
   740  			MultiTokenValues: values,
   741  			Decimals:         contractInfo.Decimals,
   742  			Name:             contractInfo.Name,
   743  			Symbol:           contractInfo.Symbol,
   744  		}
   745  	}
   746  	return tokens
   747  }
   748  
   749  func (w *Worker) getCoreCoinTokens(transfers bchain.TokenTransfers) []TokenTransfer {
   750  	tokens := make([]TokenTransfer, len(transfers))
   751  	for i := range transfers {
   752  		e := transfers[i]
   753  		cd, err := w.chainParser.GetAddrDescFromAddress(e.Contract)
   754  		if err != nil {
   755  			glog.Errorf("GetAddrDescFromAddress error %v, contract %v", err, e.Contract)
   756  			continue
   757  		}
   758  		typeName := bchain.TokenTypeMap[e.Type]
   759  		с, err := w.chain.GetContractInfo(cd)
   760  		if err != nil {
   761  			glog.Errorf("getCoreCoinTokens error %v, contract %v", err, e.Contract)
   762  		}
   763  		if с == nil {
   764  			с = &bchain.ContractInfo{Name: e.Contract}
   765  		}
   766  		tokens[i] = TokenTransfer{
   767  			Type:     typeName,
   768  			Contract: e.Contract,
   769  			From:     e.From,
   770  			To:       e.To,
   771  			Decimals: с.Decimals,
   772  			Value:    (*Amount)(&e.Value),
   773  			Name:     с.Name,
   774  			Symbol:   с.Symbol,
   775  		}
   776  	}
   777  	return tokens
   778  }
   779  
   780  func (w *Worker) GetEthereumTokenURI(contract string, id string) (string, *bchain.ContractInfo, error) {
   781  	cd, err := w.chainParser.GetAddrDescFromAddress(contract)
   782  	if err != nil {
   783  		return "", nil, err
   784  	}
   785  	tokenId, ok := new(big.Int).SetString(id, 10)
   786  	if !ok {
   787  		return "", nil, errors.New("Invalid token id")
   788  	}
   789  	uri, err := w.chain.GetTokenURI(cd, tokenId)
   790  	if err != nil {
   791  		return "", nil, err
   792  	}
   793  	ci, _, err := w.getContractDescriptorInfo(cd, bchain.UnknownTokenType)
   794  	if err != nil {
   795  		return "", nil, err
   796  	}
   797  	return uri, ci, nil
   798  }
   799  
   800  func (w *Worker) getAddressTxids(addrDesc bchain.AddressDescriptor, mempool bool, filter *AddressFilter, maxResults int) ([]string, error) {
   801  	var err error
   802  	txids := make([]string, 0, 4)
   803  	var callback db.GetTransactionsCallback
   804  	if filter.Vout == AddressFilterVoutOff {
   805  		callback = func(txid string, height uint32, indexes []int32) error {
   806  			txids = append(txids, txid)
   807  			if len(txids) >= maxResults {
   808  				return &db.StopIteration{}
   809  			}
   810  			return nil
   811  		}
   812  	} else {
   813  		callback = func(txid string, height uint32, indexes []int32) error {
   814  			for _, index := range indexes {
   815  				vout := index
   816  				if vout < 0 {
   817  					vout = ^vout
   818  				}
   819  				if (filter.Vout == AddressFilterVoutInputs && index < 0) ||
   820  					(filter.Vout == AddressFilterVoutOutputs && index >= 0) ||
   821  					(vout == int32(filter.Vout)) {
   822  					txids = append(txids, txid)
   823  					if len(txids) >= maxResults {
   824  						return &db.StopIteration{}
   825  					}
   826  					break
   827  				}
   828  			}
   829  			return nil
   830  		}
   831  	}
   832  	if mempool {
   833  		uniqueTxs := make(map[string]struct{})
   834  		o, err := w.mempool.GetAddrDescTransactions(addrDesc)
   835  		if err != nil {
   836  			return nil, err
   837  		}
   838  		for _, m := range o {
   839  			if _, found := uniqueTxs[m.Txid]; !found {
   840  				l := len(txids)
   841  				callback(m.Txid, 0, []int32{m.Vout})
   842  				if len(txids) > l {
   843  					uniqueTxs[m.Txid] = struct{}{}
   844  				}
   845  			}
   846  		}
   847  	} else {
   848  		to := filter.ToHeight
   849  		if to == 0 {
   850  			to = maxUint32
   851  		}
   852  		err = w.db.GetAddrDescTransactions(addrDesc, filter.FromHeight, to, callback)
   853  		if err != nil {
   854  			return nil, err
   855  		}
   856  	}
   857  	return txids, nil
   858  }
   859  
   860  func (t *Tx) getAddrVoutValue(addrDesc bchain.AddressDescriptor) *big.Int {
   861  	var val big.Int
   862  	for _, vout := range t.Vout {
   863  		if bytes.Equal(vout.AddrDesc, addrDesc) && vout.ValueSat != nil {
   864  			val.Add(&val, (*big.Int)(vout.ValueSat))
   865  		}
   866  	}
   867  	return &val
   868  }
   869  func (t *Tx) getAddrEthereumTypeMempoolInputValue(addrDesc bchain.AddressDescriptor) *big.Int {
   870  	var val big.Int
   871  	if len(t.Vin) > 0 && len(t.Vout) > 0 && bytes.Equal(t.Vin[0].AddrDesc, addrDesc) {
   872  		val.Add(&val, (*big.Int)(t.Vout[0].ValueSat))
   873  		// add maximum possible fee (the used value is not yet known)
   874  		if t.EthereumSpecific != nil && t.EthereumSpecific.GasLimit != nil && t.EthereumSpecific.GasPrice != nil {
   875  			var fees big.Int
   876  			fees.Mul((*big.Int)(t.EthereumSpecific.GasPrice), t.EthereumSpecific.GasLimit)
   877  			val.Add(&val, &fees)
   878  		}
   879  	}
   880  	return &val
   881  }
   882  
   883  func (t *Tx) getAddrCoreCoinTypeMempoolInputValue(addrDesc bchain.AddressDescriptor) *big.Int {
   884  	var val big.Int
   885  	if len(t.Vin) > 0 && len(t.Vout) > 0 && bytes.Equal(t.Vin[0].AddrDesc, addrDesc) {
   886  		val.Add(&val, (*big.Int)(t.Vout[0].ValueSat))
   887  		// add maximum possible fee (the used value is not yet known)
   888  		if t.CoreCoinSpecific != nil && t.CoreCoinSpecific.EnergyLimit != nil && t.CoreCoinSpecific.EnergyPrice != nil {
   889  			var fees big.Int
   890  			fees.Mul((*big.Int)(t.CoreCoinSpecific.EnergyPrice), t.CoreCoinSpecific.EnergyLimit)
   891  			val.Add(&val, &fees)
   892  		}
   893  	}
   894  	return &val
   895  }
   896  
   897  func (t *Tx) getAddrVinValue(addrDesc bchain.AddressDescriptor) *big.Int {
   898  	var val big.Int
   899  	for _, vin := range t.Vin {
   900  		if bytes.Equal(vin.AddrDesc, addrDesc) && vin.ValueSat != nil {
   901  			val.Add(&val, (*big.Int)(vin.ValueSat))
   902  		}
   903  	}
   904  	return &val
   905  }
   906  
   907  // GetUniqueTxids removes duplicate transactions
   908  func GetUniqueTxids(txids []string) []string {
   909  	ut := make([]string, len(txids))
   910  	txidsMap := make(map[string]struct{})
   911  	i := 0
   912  	for _, txid := range txids {
   913  		_, e := txidsMap[txid]
   914  		if !e {
   915  			ut[i] = txid
   916  			i++
   917  			txidsMap[txid] = struct{}{}
   918  		}
   919  	}
   920  	return ut[0:i]
   921  }
   922  
   923  func (w *Worker) txFromTxAddress(txid string, ta *db.TxAddresses, bi *db.BlockInfo, bestheight uint32, addresses map[string]struct{}) *Tx {
   924  	var err error
   925  	var valInSat, valOutSat, feesSat big.Int
   926  	vins := make([]Vin, len(ta.Inputs))
   927  	for i := range ta.Inputs {
   928  		tai := &ta.Inputs[i]
   929  		vin := &vins[i]
   930  		vin.N = i
   931  		vin.ValueSat = (*Amount)(&tai.ValueSat)
   932  		valInSat.Add(&valInSat, &tai.ValueSat)
   933  		vin.Addresses, vin.IsAddress, err = tai.Addresses(w.chainParser)
   934  		if err != nil {
   935  			glog.Errorf("tai.Addresses error %v, tx %v, input %v, tai %+v", err, txid, i, tai)
   936  		}
   937  		if w.db.HasExtendedIndex() {
   938  			vin.Txid = tai.Txid
   939  			vin.Vout = tai.Vout
   940  		}
   941  		aggregateAddresses(addresses, vin.Addresses, vin.IsAddress)
   942  	}
   943  	vouts := make([]Vout, len(ta.Outputs))
   944  	for i := range ta.Outputs {
   945  		tao := &ta.Outputs[i]
   946  		vout := &vouts[i]
   947  		vout.N = i
   948  		vout.ValueSat = (*Amount)(&tao.ValueSat)
   949  		valOutSat.Add(&valOutSat, &tao.ValueSat)
   950  		vout.Addresses, vout.IsAddress, err = tao.Addresses(w.chainParser)
   951  		if err != nil {
   952  			glog.Errorf("tai.Addresses error %v, tx %v, output %v, tao %+v", err, txid, i, tao)
   953  		}
   954  		vout.Spent = tao.Spent
   955  		if vout.Spent && w.db.HasExtendedIndex() {
   956  			vout.SpentTxID = tao.SpentTxid
   957  			vout.SpentIndex = int(tao.SpentIndex)
   958  			vout.SpentHeight = int(tao.SpentHeight)
   959  		}
   960  		aggregateAddresses(addresses, vout.Addresses, vout.IsAddress)
   961  	}
   962  	// for coinbase transactions valIn is 0
   963  	feesSat.Sub(&valInSat, &valOutSat)
   964  	if feesSat.Sign() == -1 {
   965  		feesSat.SetUint64(0)
   966  	}
   967  	r := &Tx{
   968  		Blockhash:     bi.Hash,
   969  		Blockheight:   int(ta.Height),
   970  		Blocktime:     bi.Time,
   971  		Confirmations: bestheight - ta.Height + 1,
   972  		FeesSat:       (*Amount)(&feesSat),
   973  		Txid:          txid,
   974  		ValueInSat:    (*Amount)(&valInSat),
   975  		ValueOutSat:   (*Amount)(&valOutSat),
   976  		Vin:           vins,
   977  		Vout:          vouts,
   978  	}
   979  	if w.chainParser.SupportsVSize() {
   980  		r.VSize = int(ta.VSize)
   981  	} else {
   982  		r.Size = int(ta.VSize)
   983  	}
   984  	return r
   985  }
   986  
   987  func computePaging(count, page, itemsOnPage int) (Paging, int, int, int) {
   988  	from := page * itemsOnPage
   989  	totalPages := (count - 1) / itemsOnPage
   990  	if totalPages < 0 {
   991  		totalPages = 0
   992  	}
   993  	if from >= count {
   994  		page = totalPages
   995  	}
   996  	from = page * itemsOnPage
   997  	to := (page + 1) * itemsOnPage
   998  	if to > count {
   999  		to = count
  1000  	}
  1001  	return Paging{
  1002  		ItemsOnPage: itemsOnPage,
  1003  		Page:        page + 1,
  1004  		TotalPages:  totalPages + 1,
  1005  	}, from, to, page
  1006  }
  1007  
  1008  func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, index int, c *db.AddrContract, details AccountDetails, ticker *common.CurrencyRatesTicker, secondaryCoin string) (*Token, error) {
  1009  	typeName := bchain.TokenTypeMap[c.Type]
  1010  	ci, validContract, err := w.getContractDescriptorInfo(c.Contract, typeName)
  1011  	if err != nil {
  1012  		return nil, errors.Annotatef(err, "getEthereumContractBalance %v", c.Contract)
  1013  	}
  1014  	t := Token{
  1015  		Contract:      ci.Contract,
  1016  		Name:          ci.Name,
  1017  		Symbol:        ci.Symbol,
  1018  		Type:          typeName,
  1019  		Transfers:     int(c.Txs),
  1020  		Decimals:      ci.Decimals,
  1021  		ContractIndex: strconv.Itoa(index),
  1022  	}
  1023  	// return contract balances/values only at or above AccountDetailsTokenBalances
  1024  	if details >= AccountDetailsTokenBalances && validContract {
  1025  		if c.Type == bchain.FungibleToken {
  1026  			// get Erc20 Contract Balance from blockchain, balance obtained from adding and subtracting transfers is not correct
  1027  			b, err := w.chain.EthereumTypeGetErc20ContractBalance(addrDesc, c.Contract)
  1028  			if err != nil {
  1029  				// return nil, nil, nil, errors.Annotatef(err, "EthereumTypeGetErc20ContractBalance %v %v", addrDesc, c.Contract)
  1030  				glog.Warningf("EthereumTypeGetErc20ContractBalance addr %v, contract %v, %v", addrDesc, c.Contract, err)
  1031  			} else {
  1032  				t.BalanceSat = (*Amount)(b)
  1033  				if secondaryCoin != "" {
  1034  					baseRate, found := w.GetContractBaseRate(ticker, t.Contract, 0)
  1035  					if found {
  1036  						value, err := strconv.ParseFloat(t.BalanceSat.DecimalString(t.Decimals), 64)
  1037  						if err == nil {
  1038  							t.BaseValue = value * baseRate
  1039  							if ticker != nil {
  1040  								secondaryRate, found := ticker.Rates[secondaryCoin]
  1041  								if found {
  1042  									t.SecondaryValue = t.BaseValue * float64(secondaryRate)
  1043  								}
  1044  							}
  1045  						}
  1046  					}
  1047  				}
  1048  			}
  1049  		} else {
  1050  			if len(c.Ids) > 0 {
  1051  				ids := make([]Amount, len(c.Ids))
  1052  				for j := range ids {
  1053  					ids[j] = (Amount)(c.Ids[j])
  1054  				}
  1055  				t.Ids = ids
  1056  			}
  1057  			if len(c.MultiTokenValues) > 0 {
  1058  				idValues := make([]MultiTokenValue, len(c.MultiTokenValues))
  1059  				for j := range idValues {
  1060  					idValues[j].Id = (*Amount)(&c.MultiTokenValues[j].Id)
  1061  					idValues[j].Value = (*Amount)(&c.MultiTokenValues[j].Value)
  1062  				}
  1063  				t.MultiTokenValues = idValues
  1064  			}
  1065  		}
  1066  	}
  1067  
  1068  	return &t, nil
  1069  }
  1070  
  1071  func (w *Worker) getCoreCoinContractBalance(addrDesc bchain.AddressDescriptor, index int, c *db.AddrContract, details AccountDetails) (*Token, error) {
  1072  	typeName := bchain.TokenTypeMap[c.Type]
  1073  	ci, validContract, err := w.getContractDescriptorInfo(c.Contract, typeName)
  1074  	if err != nil {
  1075  		return nil, errors.Annotatef(err, "getCoreCoinContractBalance %v", c.Contract)
  1076  	}
  1077  	t := Token{
  1078  		Contract:      ci.Contract,
  1079  		Name:          ci.Name,
  1080  		Symbol:        ci.Symbol,
  1081  		Type:          typeName,
  1082  		Transfers:     int(c.Txs),
  1083  		Decimals:      ci.Decimals,
  1084  		ContractIndex: strconv.Itoa(index),
  1085  	}
  1086  	// return contract balances/values only at or above AccountDetailsTokenBalances
  1087  	if details >= AccountDetailsTokenBalances && validContract {
  1088  		if c.Type == bchain.FungibleToken {
  1089  			// get Crc20 Contract Balance from blockchain, balance obtained from adding and subtracting transfers is not correct
  1090  			b, err := w.chain.CoreCoinTypeGetCrc20ContractBalance(addrDesc, c.Contract)
  1091  			if err != nil {
  1092  				// return nil, nil, nil, errors.Annotatef(err, "CoreCoinTypeGetCrc20ContractBalance %v %v", addrDesc, c.Contract)
  1093  				glog.Warningf("CoreCoinTypeGetCrc20ContractBalance addr %v, contract %v, %v", addrDesc, c.Contract, err)
  1094  			} else {
  1095  				t.BalanceSat = (*Amount)(b)
  1096  			}
  1097  		} else {
  1098  			if len(c.Ids) > 0 {
  1099  				ids := make([]Amount, len(c.Ids))
  1100  				for j := range ids {
  1101  					ids[j] = (Amount)(c.Ids[j])
  1102  				}
  1103  				t.Ids = ids
  1104  			}
  1105  		}
  1106  	}
  1107  
  1108  	return &t, nil
  1109  }
  1110  
  1111  // a fallback method in case internal transactions are not processed and there is no indexed info about contract balance for an address
  1112  func (w *Worker) getCoreCoinContractBalanceFromBlockchain(addrDesc, contract bchain.AddressDescriptor, details AccountDetails) (*Token, error) {
  1113  	var b *big.Int
  1114  	ci, validContract, err := w.getContractDescriptorInfo(contract, bchain.UnknownTokenType)
  1115  	if err != nil {
  1116  		return nil, errors.Annotatef(err, "GetContractInfo %v", contract)
  1117  	}
  1118  	// do not read contract balances etc in case of Basic option
  1119  	if details >= AccountDetailsTokenBalances && validContract {
  1120  		b, err = w.chain.CoreCoinTypeGetCrc20ContractBalance(addrDesc, contract)
  1121  		if err != nil {
  1122  			// return nil, nil, nil, errors.Annotatef(err, "CoreCoinTypeGetCrc20ContractBalance %v %v", addrDesc, c.Contract)
  1123  			glog.Warningf("CoreCoinTypeGetCrc20ContractBalance addr %v, contract %v, %v", addrDesc, contract, err)
  1124  		}
  1125  	} else {
  1126  		b = nil
  1127  	}
  1128  	return &Token{
  1129  		Type:          ci.Type,
  1130  		BalanceSat:    (*Amount)(b),
  1131  		Contract:      ci.Contract,
  1132  		Name:          ci.Name,
  1133  		Symbol:        ci.Symbol,
  1134  		Transfers:     0,
  1135  		Decimals:      ci.Decimals,
  1136  		ContractIndex: "0",
  1137  	}, nil
  1138  }
  1139  
  1140  // a fallback method in case internal transactions are not processed and there is no indexed info about contract balance for an address
  1141  func (w *Worker) getEthereumContractBalanceFromBlockchain(addrDesc, contract bchain.AddressDescriptor, details AccountDetails) (*Token, error) {
  1142  	var b *big.Int
  1143  	ci, validContract, err := w.getContractDescriptorInfo(contract, bchain.UnknownTokenType)
  1144  	if err != nil {
  1145  		return nil, errors.Annotatef(err, "GetContractInfo %v", contract)
  1146  	}
  1147  	// do not read contract balances etc in case of Basic option
  1148  	if details >= AccountDetailsTokenBalances && validContract {
  1149  		b, err = w.chain.EthereumTypeGetErc20ContractBalance(addrDesc, contract)
  1150  		if err != nil {
  1151  			// return nil, nil, nil, errors.Annotatef(err, "EthereumTypeGetErc20ContractBalance %v %v", addrDesc, c.Contract)
  1152  			glog.Warningf("EthereumTypeGetErc20ContractBalance addr %v, contract %v, %v", addrDesc, contract, err)
  1153  		}
  1154  	} else {
  1155  		b = nil
  1156  	}
  1157  	return &Token{
  1158  		Type:          ci.Type,
  1159  		BalanceSat:    (*Amount)(b),
  1160  		Contract:      ci.Contract,
  1161  		Name:          ci.Name,
  1162  		Symbol:        ci.Symbol,
  1163  		Transfers:     0,
  1164  		Decimals:      ci.Decimals,
  1165  		ContractIndex: "0",
  1166  	}, nil
  1167  }
  1168  
  1169  // GetContractBaseRate returns contract rate in base coin from the ticker or DB at the timestamp. Zero timestamp means now.
  1170  func (w *Worker) GetContractBaseRate(ticker *common.CurrencyRatesTicker, token string, timestamp int64) (float64, bool) {
  1171  	if ticker == nil {
  1172  		return 0, false
  1173  	}
  1174  	rate, found := ticker.GetTokenRate(token)
  1175  	if !found {
  1176  		if timestamp == 0 {
  1177  			ticker = w.fiatRates.GetCurrentTicker("", token)
  1178  		} else {
  1179  			tickers, err := w.fiatRates.GetTickersForTimestamps([]int64{timestamp}, "", token)
  1180  			if err != nil || tickers == nil || len(*tickers) == 0 {
  1181  				ticker = nil
  1182  			} else {
  1183  				ticker = (*tickers)[0]
  1184  			}
  1185  		}
  1186  		if ticker == nil {
  1187  			return 0, false
  1188  		}
  1189  		rate, found = ticker.GetTokenRate(token)
  1190  	}
  1191  
  1192  	return float64(rate), found
  1193  }
  1194  
  1195  type ethereumTypeAddressData struct {
  1196  	tokens               Tokens
  1197  	contractInfo         *bchain.ContractInfo
  1198  	nonce                string
  1199  	nonContractTxs       int
  1200  	internalTxs          int
  1201  	totalResults         int
  1202  	tokensBaseValue      float64
  1203  	tokensSecondaryValue float64
  1204  }
  1205  
  1206  func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, details AccountDetails, filter *AddressFilter, secondaryCoin string) (*db.AddrBalance, *ethereumTypeAddressData, error) {
  1207  	var ba *db.AddrBalance
  1208  	var n uint64
  1209  	// unknown number of results for paging initially
  1210  	d := ethereumTypeAddressData{totalResults: -1}
  1211  	ca, err := w.db.GetAddrDescContracts(addrDesc)
  1212  	if err != nil {
  1213  		return nil, nil, NewAPIError(fmt.Sprintf("Address not found, %v", err), true)
  1214  	}
  1215  	b, err := w.chain.EthereumTypeGetBalance(addrDesc)
  1216  	if err != nil {
  1217  		return nil, nil, errors.Annotatef(err, "EthereumTypeGetBalance %v", addrDesc)
  1218  	}
  1219  	var filterDesc bchain.AddressDescriptor
  1220  	if filter.Contract != "" {
  1221  		filterDesc, err = w.chainParser.GetAddrDescFromAddress(filter.Contract)
  1222  		if err != nil {
  1223  			return nil, nil, NewAPIError(fmt.Sprintf("Invalid contract filter, %v", err), true)
  1224  		}
  1225  	}
  1226  	if ca != nil {
  1227  		ba = &db.AddrBalance{
  1228  			Txs: uint32(ca.TotalTxs),
  1229  		}
  1230  		if b != nil {
  1231  			ba.BalanceSat = *b
  1232  		}
  1233  		n, err = w.chain.EthereumTypeGetNonce(addrDesc)
  1234  		if err != nil {
  1235  			return nil, nil, errors.Annotatef(err, "EthereumTypeGetNonce %v", addrDesc)
  1236  		}
  1237  		ticker := w.fiatRates.GetCurrentTicker("", "")
  1238  		if details > AccountDetailsBasic {
  1239  			d.tokens = make([]Token, len(ca.Contracts))
  1240  			var j int
  1241  			for i := range ca.Contracts {
  1242  				c := &ca.Contracts[i]
  1243  				if len(filterDesc) > 0 {
  1244  					if !bytes.Equal(filterDesc, c.Contract) {
  1245  						continue
  1246  					}
  1247  					// filter only transactions of this contract
  1248  					filter.Vout = i + db.ContractIndexOffset
  1249  				}
  1250  				t, err := w.getEthereumContractBalance(addrDesc, i+db.ContractIndexOffset, c, details, ticker, secondaryCoin)
  1251  				if err != nil {
  1252  					return nil, nil, err
  1253  				}
  1254  				d.tokens[j] = *t
  1255  				d.tokensBaseValue += t.BaseValue
  1256  				d.tokensSecondaryValue += t.SecondaryValue
  1257  				j++
  1258  			}
  1259  			d.tokens = d.tokens[:j]
  1260  			sort.Sort(d.tokens)
  1261  		}
  1262  		d.contractInfo, err = w.db.GetContractInfo(addrDesc, "")
  1263  		if err != nil {
  1264  			return nil, nil, err
  1265  		}
  1266  		if filter.FromHeight == 0 && filter.ToHeight == 0 {
  1267  			// compute total results for paging
  1268  			if filter.Vout == AddressFilterVoutOff {
  1269  				d.totalResults = int(ca.TotalTxs)
  1270  			} else if filter.Vout == 0 {
  1271  				d.totalResults = int(ca.NonContractTxs)
  1272  			} else if filter.Vout == db.InternalTxIndexOffset {
  1273  				d.totalResults = int(ca.InternalTxs)
  1274  			} else if filter.Vout >= db.ContractIndexOffset && filter.Vout-db.ContractIndexOffset < len(ca.Contracts) {
  1275  				d.totalResults = int(ca.Contracts[filter.Vout-db.ContractIndexOffset].Txs)
  1276  			} else if filter.Vout == AddressFilterVoutQueryNotNecessary {
  1277  				d.totalResults = 0
  1278  			}
  1279  		}
  1280  		d.nonContractTxs = int(ca.NonContractTxs)
  1281  		d.internalTxs = int(ca.InternalTxs)
  1282  	} else {
  1283  		// addresses without any normal transactions can have internal transactions that were not processed and therefore balance
  1284  		if b != nil {
  1285  			ba = &db.AddrBalance{
  1286  				BalanceSat: *b,
  1287  			}
  1288  		}
  1289  	}
  1290  	// returns 0 for unknown address
  1291  	d.nonce = strconv.Itoa(int(n))
  1292  	// special handling if filtering for a contract, return the contract details even though the address had no transactions with it
  1293  	if len(d.tokens) == 0 && len(filterDesc) > 0 && details >= AccountDetailsTokens {
  1294  		t, err := w.getEthereumContractBalanceFromBlockchain(addrDesc, filterDesc, details)
  1295  		if err != nil {
  1296  			return nil, nil, err
  1297  		}
  1298  		d.tokens = []Token{*t}
  1299  		// switch off query for transactions, there are no transactions
  1300  		filter.Vout = AddressFilterVoutQueryNotNecessary
  1301  		d.totalResults = -1
  1302  	}
  1303  	return ba, &d, nil
  1304  }
  1305  
  1306  func (w *Worker) getCoreCoinTypeAddressBalances(addrDesc bchain.AddressDescriptor, details AccountDetails, filter *AddressFilter) (*db.AddrBalance, *ethereumTypeAddressData, error) {
  1307  	var ba *db.AddrBalance
  1308  	var n uint64
  1309  	// unknown number of results for paging initially
  1310  	d := ethereumTypeAddressData{totalResults: -1}
  1311  	ca, err := w.db.GetCoreCoinAddrDescContracts(addrDesc)
  1312  	if err != nil {
  1313  		return nil, nil, NewAPIError(fmt.Sprintf("Address not found, %v", err), true)
  1314  	}
  1315  	b, err := w.chain.CoreCoinTypeGetBalance(addrDesc)
  1316  	if err != nil {
  1317  		return nil, nil, errors.Annotatef(err, "CoreCoinTypeGetBalance %v", addrDesc)
  1318  	}
  1319  	var filterDesc bchain.AddressDescriptor
  1320  	if filter.Contract != "" {
  1321  		filterDesc, err = w.chainParser.GetAddrDescFromAddress(filter.Contract)
  1322  		if err != nil {
  1323  			return nil, nil, NewAPIError(fmt.Sprintf("Invalid contract filter, %v", err), true)
  1324  		}
  1325  	}
  1326  	if ca != nil {
  1327  		ba = &db.AddrBalance{
  1328  			Txs: uint32(ca.TotalTxs),
  1329  		}
  1330  		if b != nil {
  1331  			ba.BalanceSat = *b
  1332  		}
  1333  		n, err = w.chain.CoreCoinTypeGetNonce(addrDesc)
  1334  		if err != nil {
  1335  			return nil, nil, errors.Annotatef(err, "CoreCoinTypeGetNonce %v", addrDesc)
  1336  		}
  1337  		if details > AccountDetailsBasic {
  1338  			d.tokens = make([]Token, len(ca.Contracts))
  1339  			var j int
  1340  			for i := range ca.Contracts {
  1341  				c := &ca.Contracts[i]
  1342  				if len(filterDesc) > 0 {
  1343  					if !bytes.Equal(filterDesc, c.Contract) {
  1344  						continue
  1345  					}
  1346  					// filter only transactions of this contract
  1347  					filter.Vout = i + db.ContractIndexOffset
  1348  				}
  1349  				t, err := w.getCoreCoinContractBalance(addrDesc, i+db.ContractIndexOffset, c, details)
  1350  				if err != nil {
  1351  					return nil, nil, err
  1352  				}
  1353  				d.tokens[j] = *t
  1354  				j++
  1355  			}
  1356  			d.tokens = d.tokens[:j]
  1357  			sort.Sort(d.tokens)
  1358  		}
  1359  		d.contractInfo, err = w.db.GetContractInfo(addrDesc, "")
  1360  		if err != nil {
  1361  			return nil, nil, err
  1362  		}
  1363  		if filter.FromHeight == 0 && filter.ToHeight == 0 {
  1364  			// compute total results for paging
  1365  			if filter.Vout == AddressFilterVoutOff {
  1366  				d.totalResults = int(ca.TotalTxs)
  1367  			} else if filter.Vout == 0 {
  1368  				d.totalResults = int(ca.NonContractTxs)
  1369  			} else if filter.Vout == db.InternalTxIndexOffset {
  1370  				d.totalResults = int(ca.InternalTxs)
  1371  			} else if filter.Vout >= db.ContractIndexOffset && filter.Vout-db.ContractIndexOffset < len(ca.Contracts) {
  1372  				d.totalResults = int(ca.Contracts[filter.Vout-db.ContractIndexOffset].Txs)
  1373  			} else if filter.Vout == AddressFilterVoutQueryNotNecessary {
  1374  				d.totalResults = 0
  1375  			}
  1376  		}
  1377  		d.nonContractTxs = int(ca.NonContractTxs)
  1378  	} else {
  1379  		// addresses without any normal transactions can have internal transactions that were not processed and therefore balance
  1380  		if b != nil {
  1381  			ba = &db.AddrBalance{
  1382  				BalanceSat: *b,
  1383  			}
  1384  		}
  1385  	}
  1386  	// returns 0 for unknown address
  1387  	d.nonce = strconv.Itoa(int(n))
  1388  	// special handling if filtering for a contract, return the contract details even though the address had no transactions with it
  1389  	if len(d.tokens) == 0 && len(filterDesc) > 0 && details >= AccountDetailsTokens {
  1390  		t, err := w.getCoreCoinContractBalanceFromBlockchain(addrDesc, filterDesc, details)
  1391  		if err != nil {
  1392  			return nil, nil, err
  1393  		}
  1394  		d.tokens = []Token{*t}
  1395  		// switch off query for transactions, there are no transactions
  1396  		filter.Vout = AddressFilterVoutQueryNotNecessary
  1397  		d.totalResults = -1
  1398  	}
  1399  	return ba, &d, nil
  1400  }
  1401  
  1402  func (w *Worker) getCoreCoinToken(index int, addrDesc, contract bchain.AddressDescriptor, details AccountDetails, txs int) (*Token, error) {
  1403  	var b *big.Int
  1404  	validContract := true
  1405  	ci, err := w.chain.GetContractInfo(contract)
  1406  	if err != nil {
  1407  		return nil, errors.Annotatef(err, "GetContractInfo %v", contract)
  1408  	}
  1409  	if ci == nil {
  1410  		ci = &bchain.ContractInfo{}
  1411  		addresses, _, _ := w.chainParser.GetAddressesFromAddrDesc(contract)
  1412  		if len(addresses) > 0 {
  1413  			ci.Contract = addresses[0]
  1414  			ci.Name = addresses[0]
  1415  		}
  1416  		validContract = false
  1417  	}
  1418  	// do not read contract balances etc in case of Basic option
  1419  	if details >= AccountDetailsTokenBalances && validContract {
  1420  		b, err = w.chain.CoreCoinTypeGetCrc20ContractBalance(addrDesc, contract)
  1421  		if err != nil {
  1422  			// return nil, nil, nil, errors.Annotatef(err, "CoreCoinTypeGetCrc20ContractBalance %v %v", addrDesc, c.Contract)
  1423  			glog.Warningf("CoreCoinTypeGetCrc20ContractBalance addr %v, contract %v, %v", addrDesc, contract, err)
  1424  		}
  1425  	} else {
  1426  		b = nil
  1427  	}
  1428  	return &Token{
  1429  		Type:          ci.Type,
  1430  		BalanceSat:    (*Amount)(b),
  1431  		Contract:      ci.Contract,
  1432  		Name:          ci.Name,
  1433  		Symbol:        ci.Symbol,
  1434  		Transfers:     txs,
  1435  		Decimals:      ci.Decimals,
  1436  		ContractIndex: strconv.Itoa(index),
  1437  	}, nil
  1438  }
  1439  
  1440  func (w *Worker) txFromTxid(txid string, bestHeight uint32, option AccountDetails, blockInfo *db.BlockInfo, addresses map[string]struct{}) (*Tx, error) {
  1441  	var tx *Tx
  1442  	var err error
  1443  	// only ChainBitcoinType supports TxHistoryLight
  1444  	if option == AccountDetailsTxHistoryLight && w.chainType == bchain.ChainBitcoinType {
  1445  		ta, err := w.db.GetTxAddresses(txid)
  1446  		if err != nil {
  1447  			return nil, errors.Annotatef(err, "GetTxAddresses %v", txid)
  1448  		}
  1449  		if ta == nil {
  1450  			glog.Warning("DB inconsistency:  tx ", txid, ": not found in txAddresses")
  1451  			// as fallback, get tx from backend
  1452  			tx, err = w.getTransaction(txid, false, false, addresses)
  1453  			if err != nil {
  1454  				return nil, errors.Annotatef(err, "getTransaction %v", txid)
  1455  			}
  1456  		} else {
  1457  			if blockInfo == nil {
  1458  				blockInfo, err = w.db.GetBlockInfo(ta.Height)
  1459  				if err != nil {
  1460  					return nil, errors.Annotatef(err, "GetBlockInfo %v", ta.Height)
  1461  				}
  1462  				if blockInfo == nil {
  1463  					glog.Warning("DB inconsistency:  block height ", ta.Height, ": not found in db")
  1464  					// provide empty BlockInfo to return the rest of tx data
  1465  					blockInfo = &db.BlockInfo{}
  1466  				}
  1467  			}
  1468  			tx = w.txFromTxAddress(txid, ta, blockInfo, bestHeight, addresses)
  1469  		}
  1470  	} else {
  1471  		tx, err = w.getTransaction(txid, false, false, addresses)
  1472  		if err != nil {
  1473  			return nil, errors.Annotatef(err, "getTransaction %v", txid)
  1474  		}
  1475  	}
  1476  	return tx, nil
  1477  }
  1478  
  1479  func (w *Worker) getAddrDescAndNormalizeAddress(address string) (bchain.AddressDescriptor, string, error) {
  1480  	addrDesc, err := w.chainParser.GetAddrDescFromAddress(address)
  1481  	if err != nil {
  1482  		var errAd error
  1483  		// try if the address is not address descriptor converted to string
  1484  		addrDesc, errAd = bchain.AddressDescriptorFromString(address)
  1485  		if errAd != nil {
  1486  			return nil, "", NewAPIError(fmt.Sprintf("Invalid address, %v", err), true)
  1487  		}
  1488  	}
  1489  	// convert the address to the format defined by the parser
  1490  	addresses, _, err := w.chainParser.GetAddressesFromAddrDesc(addrDesc)
  1491  	if err != nil {
  1492  		glog.V(2).Infof("GetAddressesFromAddrDesc error %v, %v", err, addrDesc)
  1493  	}
  1494  	if len(addresses) == 1 {
  1495  		address = addresses[0]
  1496  	}
  1497  	return addrDesc, address, nil
  1498  }
  1499  
  1500  func isOwnAddress(address string, addresses []string) bool {
  1501  	if len(addresses) == 1 {
  1502  		return address == addresses[0]
  1503  	}
  1504  	return false
  1505  }
  1506  
  1507  func setIsOwnAddress(tx *Tx, address string) {
  1508  	for j := range tx.Vin {
  1509  		vin := &tx.Vin[j]
  1510  		if isOwnAddress(address, vin.Addresses) {
  1511  			vin.IsOwn = true
  1512  		}
  1513  	}
  1514  	for j := range tx.Vout {
  1515  		vout := &tx.Vout[j]
  1516  		if isOwnAddress(address, vout.Addresses) {
  1517  			vout.IsOwn = true
  1518  		}
  1519  	}
  1520  }
  1521  
  1522  // GetAddress computes address value and gets transactions for given address
  1523  func (w *Worker) GetAddress(address string, page int, txsOnPage int, option AccountDetails, filter *AddressFilter, secondaryCoin string) (*Address, error) {
  1524  	start := time.Now()
  1525  	page--
  1526  	if page < 0 {
  1527  		page = 0
  1528  	}
  1529  	var (
  1530  		ba                       *db.AddrBalance
  1531  		txm                      []string
  1532  		txs                      []*Tx
  1533  		txids                    []string
  1534  		pg                       Paging
  1535  		uBalSat                  big.Int
  1536  		totalReceived, totalSent *big.Int
  1537  		unconfirmedTxs           int
  1538  		totalResults             int
  1539  	)
  1540  	ed := &ethereumTypeAddressData{}
  1541  	addrDesc, address, err := w.getAddrDescAndNormalizeAddress(address)
  1542  	if err != nil {
  1543  		return nil, err
  1544  	}
  1545  	if w.chainType == bchain.ChainEthereumType {
  1546  		ba, ed, err = w.getEthereumTypeAddressBalances(addrDesc, option, filter, secondaryCoin)
  1547  		if err != nil {
  1548  			return nil, err
  1549  		}
  1550  		totalResults = ed.totalResults
  1551  	} else if w.chainType == bchain.ChainCoreCoinType {
  1552  		// var nonce uint64
  1553  		ba, ed, err = w.getCoreCoinTypeAddressBalances(addrDesc, option, filter)
  1554  		// ed.nonce = strconv.Itoa(int(nonce))
  1555  		if err != nil {
  1556  			return nil, err
  1557  		}
  1558  		totalResults = ed.totalResults
  1559  	} else {
  1560  		// ba can be nil if the address is only in mempool!
  1561  		ba, err = w.db.GetAddrDescBalance(addrDesc, db.AddressBalanceDetailNoUTXO)
  1562  		if err != nil {
  1563  			return nil, NewAPIError(fmt.Sprintf("Address not found, %v", err), true)
  1564  		}
  1565  		if ba != nil {
  1566  			// totalResults is known only if there is no filter
  1567  			if filter.Vout == AddressFilterVoutOff && filter.FromHeight == 0 && filter.ToHeight == 0 {
  1568  				totalResults = int(ba.Txs)
  1569  			} else {
  1570  				totalResults = -1
  1571  			}
  1572  		}
  1573  	}
  1574  	// if there are only unconfirmed transactions, there is no paging
  1575  	if ba == nil {
  1576  		ba = &db.AddrBalance{}
  1577  		page = 0
  1578  	}
  1579  	addresses := w.newAddressesMapForAliases()
  1580  	// process mempool, only if toHeight is not specified
  1581  	if filter.ToHeight == 0 && !filter.OnlyConfirmed {
  1582  		txm, err = w.getAddressTxids(addrDesc, true, filter, maxInt)
  1583  		if err != nil {
  1584  			return nil, errors.Annotatef(err, "getAddressTxids %v true", addrDesc)
  1585  		}
  1586  		for _, txid := range txm {
  1587  			tx, err := w.getTransaction(txid, false, true, addresses)
  1588  			// mempool transaction may fail
  1589  			if err != nil || tx == nil {
  1590  				glog.Warning("GetTransaction in mempool: ", err)
  1591  			} else {
  1592  				// skip already confirmed txs, mempool may be out of sync
  1593  				if tx.Confirmations == 0 {
  1594  					unconfirmedTxs++
  1595  					uBalSat.Add(&uBalSat, tx.getAddrVoutValue(addrDesc))
  1596  					// ethereum and corecoin have a different logic - value not in input and add maximum possible fees
  1597  					if w.chainType == bchain.ChainEthereumType {
  1598  						uBalSat.Sub(&uBalSat, tx.getAddrEthereumTypeMempoolInputValue(addrDesc))
  1599  					} else if w.chainType == bchain.ChainCoreCoinType {
  1600  						uBalSat.Sub(&uBalSat, tx.getAddrCoreCoinTypeMempoolInputValue(addrDesc))
  1601  					} else {
  1602  						uBalSat.Sub(&uBalSat, tx.getAddrVinValue(addrDesc))
  1603  					}
  1604  					if page == 0 {
  1605  						if option == AccountDetailsTxidHistory {
  1606  							txids = append(txids, tx.Txid)
  1607  						} else if option >= AccountDetailsTxHistoryLight {
  1608  							setIsOwnAddress(tx, address)
  1609  							txs = append(txs, tx)
  1610  						}
  1611  					}
  1612  				}
  1613  			}
  1614  		}
  1615  	}
  1616  	// get tx history if requested by option or check mempool if there are some transactions for a new address
  1617  	if option >= AccountDetailsTxidHistory && filter.Vout != AddressFilterVoutQueryNotNecessary {
  1618  		txc, err := w.getAddressTxids(addrDesc, false, filter, (page+1)*txsOnPage)
  1619  		if err != nil {
  1620  			return nil, errors.Annotatef(err, "getAddressTxids %v false", addrDesc)
  1621  		}
  1622  		bestheight, _, err := w.db.GetBestBlock()
  1623  		if err != nil {
  1624  			return nil, errors.Annotatef(err, "GetBestBlock")
  1625  		}
  1626  		var from, to int
  1627  		pg, from, to, page = computePaging(len(txc), page, txsOnPage)
  1628  		if len(txc) >= txsOnPage {
  1629  			if totalResults < 0 {
  1630  				pg.TotalPages = -1
  1631  			} else {
  1632  				pg, _, _, _ = computePaging(totalResults, page, txsOnPage)
  1633  			}
  1634  		}
  1635  		for i := from; i < to; i++ {
  1636  			txid := txc[i]
  1637  			if option == AccountDetailsTxidHistory {
  1638  				txids = append(txids, txid)
  1639  			} else {
  1640  				tx, err := w.txFromTxid(txid, bestheight, option, nil, addresses)
  1641  				if err != nil {
  1642  					return nil, err
  1643  				}
  1644  				setIsOwnAddress(tx, address)
  1645  				txs = append(txs, tx)
  1646  			}
  1647  		}
  1648  	}
  1649  	if w.chainType == bchain.ChainBitcoinType {
  1650  		totalReceived = ba.ReceivedSat()
  1651  		totalSent = &ba.SentSat
  1652  	}
  1653  	var secondaryRate, totalSecondaryValue, totalBaseValue, secondaryValue float64
  1654  	if secondaryCoin != "" {
  1655  		ticker := w.fiatRates.GetCurrentTicker("", "")
  1656  		balance, err := strconv.ParseFloat((*Amount)(&ba.BalanceSat).DecimalString(w.chainParser.AmountDecimals()), 64)
  1657  		if ticker != nil && err == nil {
  1658  			r, found := ticker.Rates[secondaryCoin]
  1659  			if found {
  1660  				secondaryRate = float64(r)
  1661  			}
  1662  		}
  1663  		secondaryValue = secondaryRate * balance
  1664  		if w.chainType == bchain.ChainEthereumType {
  1665  			totalBaseValue += balance + ed.tokensBaseValue
  1666  			totalSecondaryValue = secondaryRate * totalBaseValue
  1667  		}
  1668  	}
  1669  	r := &Address{
  1670  		Paging:                pg,
  1671  		AddrStr:               address,
  1672  		BalanceSat:            (*Amount)(&ba.BalanceSat),
  1673  		TotalReceivedSat:      (*Amount)(totalReceived),
  1674  		TotalSentSat:          (*Amount)(totalSent),
  1675  		Txs:                   int(ba.Txs),
  1676  		NonTokenTxs:           ed.nonContractTxs,
  1677  		InternalTxs:           ed.internalTxs,
  1678  		UnconfirmedBalanceSat: (*Amount)(&uBalSat),
  1679  		UnconfirmedTxs:        unconfirmedTxs,
  1680  		Transactions:          txs,
  1681  		Txids:                 txids,
  1682  		Tokens:                ed.tokens,
  1683  		SecondaryValue:        secondaryValue,
  1684  		TokensBaseValue:       ed.tokensBaseValue,
  1685  		TokensSecondaryValue:  ed.tokensSecondaryValue,
  1686  		TotalBaseValue:        totalBaseValue,
  1687  		TotalSecondaryValue:   totalSecondaryValue,
  1688  		ContractInfo:          ed.contractInfo,
  1689  		Nonce:                 ed.nonce,
  1690  		AddressAliases:        w.getAddressAliases(addresses),
  1691  	}
  1692  	// keep address backward compatible, set deprecated Erc20Contract value if ERC20 token
  1693  	if ed.contractInfo != nil && (ed.contractInfo.Type == bchain.ERC20TokenType || ed.contractInfo.Type == xcb.CRC20TokenType) {
  1694  		r.Erc20Contract = ed.contractInfo
  1695  	}
  1696  	glog.Info("GetAddress ", address, ", ", time.Since(start))
  1697  	return r, nil
  1698  }
  1699  
  1700  func (w *Worker) balanceHistoryHeightsFromTo(fromTimestamp, toTimestamp int64) (uint32, uint32, uint32, uint32) {
  1701  	fromUnix := uint32(0)
  1702  	toUnix := maxUint32
  1703  	fromHeight := uint32(0)
  1704  	toHeight := maxUint32
  1705  	if fromTimestamp != 0 {
  1706  		fromUnix = uint32(fromTimestamp)
  1707  		fromHeight = w.is.GetBlockHeightOfTime(fromUnix)
  1708  	}
  1709  	if toTimestamp != 0 {
  1710  		toUnix = uint32(toTimestamp)
  1711  		toHeight = w.is.GetBlockHeightOfTime(toUnix)
  1712  	}
  1713  	return fromUnix, fromHeight, toUnix, toHeight
  1714  }
  1715  
  1716  func (w *Worker) balanceHistoryForTxid(addrDesc bchain.AddressDescriptor, txid string, fromUnix, toUnix uint32, selfAddrDesc map[string]struct{}) (*BalanceHistory, error) {
  1717  	var time uint32
  1718  	var err error
  1719  	var ta *db.TxAddresses
  1720  	var bchainTx *bchain.Tx
  1721  	var height uint32
  1722  	if w.chainType == bchain.ChainBitcoinType {
  1723  		ta, err = w.db.GetTxAddresses(txid)
  1724  		if err != nil {
  1725  			return nil, err
  1726  		}
  1727  		if ta == nil {
  1728  			glog.Warning("DB inconsistency:  tx ", txid, ": not found in txAddresses")
  1729  			return nil, nil
  1730  		}
  1731  		height = ta.Height
  1732  	} else if w.chainType == bchain.ChainEthereumType || w.chainType == bchain.ChainCoreCoinType {
  1733  		var h int
  1734  		bchainTx, h, err = w.txCache.GetTransaction(txid)
  1735  		if err != nil {
  1736  			return nil, err
  1737  		}
  1738  		if bchainTx == nil {
  1739  			glog.Warning("Inconsistency:  tx ", txid, ": not found in the blockchain")
  1740  			return nil, nil
  1741  		}
  1742  		height = uint32(h)
  1743  	}
  1744  	time = w.is.GetBlockTime(height)
  1745  	if time < fromUnix || time >= toUnix {
  1746  		return nil, nil
  1747  	}
  1748  	bh := BalanceHistory{
  1749  		Time:          time,
  1750  		Txs:           1,
  1751  		ReceivedSat:   &Amount{},
  1752  		SentSat:       &Amount{},
  1753  		SentToSelfSat: &Amount{},
  1754  		Txid:          txid,
  1755  	}
  1756  	countSentToSelf := false
  1757  	if w.chainType == bchain.ChainBitcoinType {
  1758  		// detect if this input is the first of selfAddrDesc
  1759  		// to not to count sentToSelf multiple times if counting multiple xpub addresses
  1760  		ownInputIndex := -1
  1761  		for i := range ta.Inputs {
  1762  			tai := &ta.Inputs[i]
  1763  			if _, found := selfAddrDesc[string(tai.AddrDesc)]; found {
  1764  				if ownInputIndex < 0 {
  1765  					ownInputIndex = i
  1766  				}
  1767  			}
  1768  			if bytes.Equal(addrDesc, tai.AddrDesc) {
  1769  				(*big.Int)(bh.SentSat).Add((*big.Int)(bh.SentSat), &tai.ValueSat)
  1770  				if ownInputIndex == i {
  1771  					countSentToSelf = true
  1772  				}
  1773  			}
  1774  		}
  1775  		for i := range ta.Outputs {
  1776  			tao := &ta.Outputs[i]
  1777  			if bytes.Equal(addrDesc, tao.AddrDesc) {
  1778  				(*big.Int)(bh.ReceivedSat).Add((*big.Int)(bh.ReceivedSat), &tao.ValueSat)
  1779  			}
  1780  			if countSentToSelf {
  1781  				if _, found := selfAddrDesc[string(tao.AddrDesc)]; found {
  1782  					(*big.Int)(bh.SentToSelfSat).Add((*big.Int)(bh.SentToSelfSat), &tao.ValueSat)
  1783  				}
  1784  			}
  1785  		}
  1786  	} else if w.chainType == bchain.ChainEthereumType {
  1787  		var value big.Int
  1788  		ethTxData := eth.GetEthereumTxData(bchainTx)
  1789  		// add received amount only for OK or unknown status (old) transactions
  1790  		if ethTxData.Status == eth.TxStatusOK || ethTxData.Status == eth.TxStatusUnknown {
  1791  			if len(bchainTx.Vout) > 0 {
  1792  				bchainVout := &bchainTx.Vout[0]
  1793  				value = bchainVout.ValueSat
  1794  				if len(bchainVout.ScriptPubKey.Addresses) > 0 {
  1795  					txAddrDesc, err := w.chainParser.GetAddrDescFromAddress(bchainVout.ScriptPubKey.Addresses[0])
  1796  					if err != nil {
  1797  						return nil, err
  1798  					}
  1799  					if bytes.Equal(addrDesc, txAddrDesc) {
  1800  						(*big.Int)(bh.ReceivedSat).Add((*big.Int)(bh.ReceivedSat), &value)
  1801  					}
  1802  					if _, found := selfAddrDesc[string(txAddrDesc)]; found {
  1803  						countSentToSelf = true
  1804  					}
  1805  				}
  1806  			}
  1807  			// process internal transactions
  1808  			if eth.ProcessInternalTransactions {
  1809  				internalData, err := w.db.GetEthereumInternalData(txid)
  1810  				if err != nil {
  1811  					return nil, err
  1812  				}
  1813  				if internalData != nil {
  1814  					for i := range internalData.Transfers {
  1815  						f := &internalData.Transfers[i]
  1816  						txAddrDesc, err := w.chainParser.GetAddrDescFromAddress(f.From)
  1817  						if err != nil {
  1818  							return nil, err
  1819  						}
  1820  						if bytes.Equal(addrDesc, txAddrDesc) {
  1821  							(*big.Int)(bh.SentSat).Add((*big.Int)(bh.SentSat), &f.Value)
  1822  							if f.From == f.To {
  1823  								(*big.Int)(bh.SentToSelfSat).Add((*big.Int)(bh.SentToSelfSat), &f.Value)
  1824  							}
  1825  						}
  1826  						txAddrDesc, err = w.chainParser.GetAddrDescFromAddress(f.To)
  1827  						if err != nil {
  1828  							return nil, err
  1829  						}
  1830  						if bytes.Equal(addrDesc, txAddrDesc) {
  1831  							(*big.Int)(bh.ReceivedSat).Add((*big.Int)(bh.ReceivedSat), &f.Value)
  1832  						}
  1833  					}
  1834  				}
  1835  			}
  1836  		}
  1837  		for i := range bchainTx.Vin {
  1838  			bchainVin := &bchainTx.Vin[i]
  1839  			if len(bchainVin.Addresses) > 0 {
  1840  				txAddrDesc, err := w.chainParser.GetAddrDescFromAddress(bchainVin.Addresses[0])
  1841  				if err != nil {
  1842  					return nil, err
  1843  				}
  1844  				if bytes.Equal(addrDesc, txAddrDesc) {
  1845  					// add received amount only for OK or unknown status (old) transactions, fees always
  1846  					if ethTxData.Status == eth.TxStatusOK || ethTxData.Status == eth.TxStatusUnknown {
  1847  						(*big.Int)(bh.SentSat).Add((*big.Int)(bh.SentSat), &value)
  1848  						if countSentToSelf {
  1849  							if _, found := selfAddrDesc[string(txAddrDesc)]; found {
  1850  								(*big.Int)(bh.SentToSelfSat).Add((*big.Int)(bh.SentToSelfSat), &value)
  1851  							}
  1852  						}
  1853  					}
  1854  					var feesSat big.Int
  1855  					// mempool txs do not have fees yet
  1856  					if ethTxData.GasUsed != nil {
  1857  						feesSat.Mul(ethTxData.GasPrice, ethTxData.GasUsed)
  1858  					}
  1859  					(*big.Int)(bh.SentSat).Add((*big.Int)(bh.SentSat), &feesSat)
  1860  				}
  1861  			}
  1862  		}
  1863  	} else if w.chainType == bchain.ChainCoreCoinType {
  1864  
  1865  		var value big.Int
  1866  		xcbTxData := xcb.GetCoreCoinTxData(bchainTx)
  1867  		// add received amount only for OK or unknown status (old) transactions
  1868  		if xcbTxData.Status == xcb.TxStatusOK || xcbTxData.Status == xcb.TxStatusUnknown {
  1869  			if len(bchainTx.Vout) > 0 {
  1870  				bchainVout := &bchainTx.Vout[0]
  1871  				value = bchainVout.ValueSat
  1872  				if len(bchainVout.ScriptPubKey.Addresses) > 0 {
  1873  					txAddrDesc, err := w.chainParser.GetAddrDescFromAddress(bchainVout.ScriptPubKey.Addresses[0])
  1874  					if err != nil {
  1875  						return nil, err
  1876  					}
  1877  					if bytes.Equal(addrDesc, txAddrDesc) {
  1878  						(*big.Int)(bh.ReceivedSat).Add((*big.Int)(bh.ReceivedSat), &value)
  1879  					}
  1880  					if _, found := selfAddrDesc[string(txAddrDesc)]; found {
  1881  						countSentToSelf = true
  1882  					}
  1883  				}
  1884  			}
  1885  		}
  1886  		for i := range bchainTx.Vin {
  1887  			bchainVin := &bchainTx.Vin[i]
  1888  			if len(bchainVin.Addresses) > 0 {
  1889  				txAddrDesc, err := w.chainParser.GetAddrDescFromAddress(bchainVin.Addresses[0])
  1890  				if err != nil {
  1891  					return nil, err
  1892  				}
  1893  				if bytes.Equal(addrDesc, txAddrDesc) {
  1894  					// add received amount only for OK or unknown status (old) transactions, fees always
  1895  					if xcbTxData.Status == xcb.TxStatusOK || xcbTxData.Status == xcb.TxStatusUnknown {
  1896  						(*big.Int)(bh.SentSat).Add((*big.Int)(bh.SentSat), &value)
  1897  						if countSentToSelf {
  1898  							if _, found := selfAddrDesc[string(txAddrDesc)]; found {
  1899  								(*big.Int)(bh.SentToSelfSat).Add((*big.Int)(bh.SentToSelfSat), &value)
  1900  							}
  1901  						}
  1902  					}
  1903  					var feesSat big.Int
  1904  					// mempool txs do not have fees yet
  1905  					if xcbTxData.EnergyUsed != nil {
  1906  						feesSat.Mul(xcbTxData.EnergyPrice, xcbTxData.EnergyUsed)
  1907  					}
  1908  					(*big.Int)(bh.SentSat).Add((*big.Int)(bh.SentSat), &feesSat)
  1909  				}
  1910  			}
  1911  		}
  1912  	}
  1913  	return &bh, nil
  1914  }
  1915  
  1916  func (w *Worker) setFiatRateToBalanceHistories(histories BalanceHistories, currencies []string) error {
  1917  	for i := range histories {
  1918  		bh := &histories[i]
  1919  		tickers, err := w.fiatRates.GetTickersForTimestamps([]int64{int64(bh.Time)}, "", "")
  1920  		if err != nil || tickers == nil || len(*tickers) == 0 {
  1921  			glog.Errorf("Error finding ticker by date %v. Error: %v", bh.Time, err)
  1922  			continue
  1923  		}
  1924  		ticker := (*tickers)[0]
  1925  		if ticker == nil {
  1926  			continue
  1927  		}
  1928  		if len(currencies) == 0 {
  1929  			bh.FiatRates = ticker.Rates
  1930  		} else {
  1931  			rates := make(map[string]float32)
  1932  			for _, currency := range currencies {
  1933  				currency = strings.ToLower(currency)
  1934  				if rate, found := ticker.Rates[currency]; found {
  1935  					rates[currency] = rate
  1936  				} else {
  1937  					rates[currency] = -1
  1938  				}
  1939  			}
  1940  			bh.FiatRates = rates
  1941  		}
  1942  	}
  1943  	return nil
  1944  }
  1945  
  1946  // GetBalanceHistory returns history of balance for given address
  1947  func (w *Worker) GetBalanceHistory(address string, fromTimestamp, toTimestamp int64, currencies []string, groupBy uint32) (BalanceHistories, error) {
  1948  	currencies = removeEmpty(currencies)
  1949  	bhs := make(BalanceHistories, 0)
  1950  	start := time.Now()
  1951  	addrDesc, _, err := w.getAddrDescAndNormalizeAddress(address)
  1952  	if err != nil {
  1953  		return nil, err
  1954  	}
  1955  	fromUnix, fromHeight, toUnix, toHeight := w.balanceHistoryHeightsFromTo(fromTimestamp, toTimestamp)
  1956  	if fromHeight >= toHeight {
  1957  		return bhs, nil
  1958  	}
  1959  	txs, err := w.getAddressTxids(addrDesc, false, &AddressFilter{Vout: AddressFilterVoutOff, FromHeight: fromHeight, ToHeight: toHeight}, maxInt)
  1960  	if err != nil {
  1961  		return nil, err
  1962  	}
  1963  	selfAddrDesc := map[string]struct{}{string(addrDesc): {}}
  1964  	for txi := len(txs) - 1; txi >= 0; txi-- {
  1965  		bh, err := w.balanceHistoryForTxid(addrDesc, txs[txi], fromUnix, toUnix, selfAddrDesc)
  1966  		if err != nil {
  1967  			return nil, err
  1968  		}
  1969  		if bh != nil {
  1970  			bhs = append(bhs, *bh)
  1971  		}
  1972  	}
  1973  	bha := bhs.SortAndAggregate(groupBy)
  1974  	err = w.setFiatRateToBalanceHistories(bha, currencies)
  1975  	if err != nil {
  1976  		return nil, err
  1977  	}
  1978  	glog.Info("GetBalanceHistory ", address, ", blocks ", fromHeight, "-", toHeight, ", count ", len(bha), ", ", time.Since(start))
  1979  	return bha, nil
  1980  }
  1981  
  1982  func (w *Worker) waitForBackendSync() {
  1983  	// wait a short time if blockbook is synchronizing with backend
  1984  	inSync, _, _, _ := w.is.GetSyncState()
  1985  	count := 30
  1986  	for !inSync && count > 0 {
  1987  		time.Sleep(time.Millisecond * 100)
  1988  		count--
  1989  		inSync, _, _, _ = w.is.GetSyncState()
  1990  	}
  1991  }
  1992  
  1993  func (w *Worker) getAddrDescUtxo(addrDesc bchain.AddressDescriptor, ba *db.AddrBalance, onlyConfirmed bool, onlyMempool bool) (Utxos, error) {
  1994  	w.waitForBackendSync()
  1995  	var err error
  1996  	utxos := make(Utxos, 0, 8)
  1997  	// store txids from mempool so that they are not added twice in case of import of new block while processing utxos, issue #275
  1998  	inMempool := make(map[string]struct{})
  1999  	// outputs could be spent in mempool, record and check mempool spends
  2000  	spentInMempool := make(map[string]struct{})
  2001  	if !onlyConfirmed {
  2002  		// get utxo from mempool
  2003  		txm, err := w.getAddressTxids(addrDesc, true, &AddressFilter{Vout: AddressFilterVoutOff}, maxInt)
  2004  		if err != nil {
  2005  			return nil, err
  2006  		}
  2007  		if len(txm) > 0 {
  2008  			mc := make([]*bchain.Tx, len(txm))
  2009  			for i, txid := range txm {
  2010  				// get mempool txs and process their inputs to detect spends between mempool txs
  2011  				bchainTx, _, err := w.txCache.GetTransaction(txid)
  2012  				// mempool transaction may fail
  2013  				if err != nil {
  2014  					glog.Error("GetTransaction in mempool ", txid, ": ", err)
  2015  				} else {
  2016  					mc[i] = bchainTx
  2017  					// get outputs spent by the mempool tx
  2018  					for i := range bchainTx.Vin {
  2019  						vin := &bchainTx.Vin[i]
  2020  						spentInMempool[vin.Txid+strconv.Itoa(int(vin.Vout))] = struct{}{}
  2021  					}
  2022  				}
  2023  			}
  2024  			for _, bchainTx := range mc {
  2025  				if bchainTx != nil {
  2026  					for i := range bchainTx.Vout {
  2027  						vout := &bchainTx.Vout[i]
  2028  						vad, err := w.chainParser.GetAddrDescFromVout(vout)
  2029  						if err == nil && bytes.Equal(addrDesc, vad) {
  2030  							// report only outpoints that are not spent in mempool
  2031  							_, e := spentInMempool[bchainTx.Txid+strconv.Itoa(i)]
  2032  							if !e {
  2033  								coinbase := false
  2034  								if len(bchainTx.Vin) == 1 && len(bchainTx.Vin[0].Coinbase) > 0 {
  2035  									coinbase = true
  2036  								}
  2037  								utxos = append(utxos, Utxo{
  2038  									Txid:      bchainTx.Txid,
  2039  									Vout:      int32(i),
  2040  									AmountSat: (*Amount)(&vout.ValueSat),
  2041  									Locktime:  bchainTx.LockTime,
  2042  									Coinbase:  coinbase,
  2043  								})
  2044  								inMempool[bchainTx.Txid] = struct{}{}
  2045  							}
  2046  						}
  2047  					}
  2048  				}
  2049  			}
  2050  		}
  2051  	}
  2052  	if !onlyMempool {
  2053  		// get utxo from index
  2054  		if ba == nil {
  2055  			ba, err = w.db.GetAddrDescBalance(addrDesc, db.AddressBalanceDetailUTXO)
  2056  			if err != nil {
  2057  				return nil, NewAPIError(fmt.Sprintf("Address not found, %v", err), true)
  2058  			}
  2059  		}
  2060  		// ba can be nil if the address is only in mempool!
  2061  		if ba != nil && len(ba.Utxos) > 0 {
  2062  			b, _, err := w.db.GetBestBlock()
  2063  			if err != nil {
  2064  				return nil, err
  2065  			}
  2066  			bestheight := int(b)
  2067  			var checksum big.Int
  2068  			checksum.Set(&ba.BalanceSat)
  2069  			// go backwards to get the newest first
  2070  			for i := len(ba.Utxos) - 1; i >= 0; i-- {
  2071  				utxo := &ba.Utxos[i]
  2072  				txid, err := w.chainParser.UnpackTxid(utxo.BtxID)
  2073  				if err != nil {
  2074  					return nil, err
  2075  				}
  2076  				_, e := spentInMempool[txid+strconv.Itoa(int(utxo.Vout))]
  2077  				if !e {
  2078  					confirmations := bestheight - int(utxo.Height) + 1
  2079  					coinbase := false
  2080  					// for performance reasons, check coinbase transactions only in minimum confirmantion range
  2081  					if confirmations < w.chainParser.MinimumCoinbaseConfirmations() {
  2082  						ta, err := w.db.GetTxAddresses(txid)
  2083  						if err != nil {
  2084  							return nil, err
  2085  						}
  2086  						if len(ta.Inputs) == 1 && len(ta.Inputs[0].AddrDesc) == 0 && IsZeroBigInt(&ta.Inputs[0].ValueSat) {
  2087  							coinbase = true
  2088  						}
  2089  					}
  2090  					_, e = inMempool[txid]
  2091  					if !e {
  2092  						utxos = append(utxos, Utxo{
  2093  							Txid:          txid,
  2094  							Vout:          utxo.Vout,
  2095  							AmountSat:     (*Amount)(&utxo.ValueSat),
  2096  							Height:        int(utxo.Height),
  2097  							Confirmations: confirmations,
  2098  							Coinbase:      coinbase,
  2099  						})
  2100  					}
  2101  				}
  2102  				checksum.Sub(&checksum, &utxo.ValueSat)
  2103  			}
  2104  			if checksum.Uint64() != 0 {
  2105  				glog.Warning("DB inconsistency:  ", addrDesc, ": checksum is not zero, checksum=", checksum.Int64())
  2106  			}
  2107  		}
  2108  	}
  2109  	return utxos, nil
  2110  }
  2111  
  2112  // GetAddressUtxo returns unspent outputs for given address
  2113  func (w *Worker) GetAddressUtxo(address string, onlyConfirmed bool) (Utxos, error) {
  2114  	if w.chainType != bchain.ChainBitcoinType {
  2115  		return nil, NewAPIError("Not supported", true)
  2116  	}
  2117  	start := time.Now()
  2118  	addrDesc, err := w.chainParser.GetAddrDescFromAddress(address)
  2119  	if err != nil {
  2120  		return nil, NewAPIError(fmt.Sprintf("Invalid address '%v', %v", address, err), true)
  2121  	}
  2122  	r, err := w.getAddrDescUtxo(addrDesc, nil, onlyConfirmed, false)
  2123  	if err != nil {
  2124  		return nil, err
  2125  	}
  2126  	glog.Info("GetAddressUtxo ", address, ", ", len(r), " utxos, ", time.Since(start))
  2127  	return r, nil
  2128  }
  2129  
  2130  // GetBlocks returns BlockInfo for blocks on given page
  2131  func (w *Worker) GetBlocks(page int, blocksOnPage int) (*Blocks, error) {
  2132  	start := time.Now()
  2133  	page--
  2134  	if page < 0 {
  2135  		page = 0
  2136  	}
  2137  	b, _, err := w.db.GetBestBlock()
  2138  	bestheight := int(b)
  2139  	if err != nil {
  2140  		return nil, errors.Annotatef(err, "GetBestBlock")
  2141  	}
  2142  	pg, from, to, page := computePaging(bestheight+1, page, blocksOnPage)
  2143  	r := &Blocks{Paging: pg}
  2144  	r.Blocks = make([]db.BlockInfo, to-from)
  2145  	for i := from; i < to; i++ {
  2146  		bi, err := w.db.GetBlockInfo(uint32(bestheight - i))
  2147  		if err != nil {
  2148  			return nil, err
  2149  		}
  2150  		if bi == nil {
  2151  			r.Blocks = r.Blocks[:i]
  2152  			break
  2153  		}
  2154  		r.Blocks[i-from] = *bi
  2155  	}
  2156  	glog.Info("GetBlocks page ", page, ", ", time.Since(start))
  2157  	return r, nil
  2158  }
  2159  
  2160  // removeEmpty removes empty strings from a slice
  2161  func removeEmpty(stringSlice []string) []string {
  2162  	var ret []string
  2163  	for _, str := range stringSlice {
  2164  		if str != "" {
  2165  			ret = append(ret, str)
  2166  		}
  2167  	}
  2168  	return ret
  2169  }
  2170  
  2171  // getFiatRatesResult checks if CurrencyRatesTicker contains all necessary data and returns formatted result
  2172  func (w *Worker) getFiatRatesResult(currencies []string, ticker *common.CurrencyRatesTicker, token string) (*FiatTicker, error) {
  2173  	if token != "" {
  2174  		rates := make(map[string]float32)
  2175  		if len(currencies) == 0 {
  2176  			for currency := range ticker.Rates {
  2177  				currency = strings.ToLower(currency)
  2178  				rate := ticker.TokenRateInCurrency(token, currency)
  2179  				if rate <= 0 {
  2180  					rate = -1
  2181  				}
  2182  				rates[currency] = rate
  2183  			}
  2184  		} else {
  2185  			for _, currency := range currencies {
  2186  				currency = strings.ToLower(currency)
  2187  				rate := ticker.TokenRateInCurrency(token, currency)
  2188  				if rate <= 0 {
  2189  					rate = -1
  2190  				}
  2191  				rates[currency] = rate
  2192  			}
  2193  		}
  2194  		return &FiatTicker{
  2195  			Timestamp: ticker.Timestamp.UTC().Unix(),
  2196  			Rates:     rates,
  2197  		}, nil
  2198  	}
  2199  	if len(currencies) == 0 {
  2200  		// Return all available ticker rates
  2201  		return &FiatTicker{
  2202  			Timestamp: ticker.Timestamp.UTC().Unix(),
  2203  			Rates:     ticker.Rates,
  2204  		}, nil
  2205  	}
  2206  	// Check if currencies from the list are available in the ticker rates
  2207  	rates := make(map[string]float32)
  2208  	for _, currency := range currencies {
  2209  		currency = strings.ToLower(currency)
  2210  		if rate, found := ticker.Rates[currency]; found {
  2211  			rates[currency] = rate
  2212  		} else {
  2213  			rates[currency] = -1
  2214  		}
  2215  	}
  2216  	return &FiatTicker{
  2217  		Timestamp: ticker.Timestamp.UTC().Unix(),
  2218  		Rates:     rates,
  2219  	}, nil
  2220  }
  2221  
  2222  // GetCurrentFiatRates returns last available fiat rates
  2223  func (w *Worker) GetCurrentFiatRates(currencies []string, token string) (*FiatTicker, error) {
  2224  	vsCurrency := ""
  2225  	currencies = removeEmpty(currencies)
  2226  	if len(currencies) == 1 {
  2227  		vsCurrency = currencies[0]
  2228  	}
  2229  	ticker := w.fiatRates.GetCurrentTicker(vsCurrency, token)
  2230  	var err error
  2231  	if ticker == nil {
  2232  		ticker, err = w.db.FiatRatesFindLastTicker(vsCurrency, token)
  2233  		if err != nil {
  2234  			return nil, NewAPIError(fmt.Sprintf("Error finding ticker: %v", err), false)
  2235  		} else if ticker == nil {
  2236  			return nil, NewAPIError("No tickers found!", true)
  2237  		}
  2238  	}
  2239  	result, err := w.getFiatRatesResult(currencies, ticker, token)
  2240  	if err != nil {
  2241  		return nil, err
  2242  	}
  2243  	return result, nil
  2244  }
  2245  
  2246  // makeErrorRates returns a map of currencies, with each value equal to -1
  2247  // used when there was an error finding ticker
  2248  func makeErrorRates(currencies []string) map[string]float32 {
  2249  	rates := make(map[string]float32)
  2250  	for _, currency := range currencies {
  2251  		rates[strings.ToLower(currency)] = -1
  2252  	}
  2253  	return rates
  2254  }
  2255  
  2256  // GetFiatRatesForTimestamps returns fiat rates for each of the provided dates
  2257  func (w *Worker) GetFiatRatesForTimestamps(timestamps []int64, currencies []string, token string) (*FiatTickers, error) {
  2258  	if len(timestamps) == 0 {
  2259  		return nil, NewAPIError("No timestamps provided", true)
  2260  	}
  2261  	vsCurrency := ""
  2262  	currencies = removeEmpty(currencies)
  2263  	if len(currencies) == 1 {
  2264  		vsCurrency = currencies[0]
  2265  	}
  2266  	tickers, err := w.fiatRates.GetTickersForTimestamps(timestamps, vsCurrency, token)
  2267  	if err != nil {
  2268  		return nil, err
  2269  	}
  2270  	if tickers == nil {
  2271  		return nil, NewAPIError("No tickers found", true)
  2272  	}
  2273  	if len(*tickers) != len(timestamps) {
  2274  		glog.Error("GetFiatRatesForTimestamps: number of tickers does not match timestamps ", len(*tickers), ", ", len(timestamps))
  2275  		return nil, NewAPIError("No tickers found", false)
  2276  	}
  2277  	fiatTickers := make([]FiatTicker, len(*tickers))
  2278  	for i, t := range *tickers {
  2279  		if t == nil {
  2280  			fiatTickers[i] = FiatTicker{Timestamp: timestamps[i], Rates: makeErrorRates(currencies)}
  2281  			continue
  2282  		}
  2283  		result, err := w.getFiatRatesResult(currencies, t, token)
  2284  		if err != nil {
  2285  			if apiErr, ok := err.(*APIError); ok {
  2286  				if apiErr.Public {
  2287  					return nil, err
  2288  				}
  2289  			}
  2290  			fiatTickers[i] = FiatTicker{Timestamp: timestamps[i], Rates: makeErrorRates(currencies)}
  2291  			continue
  2292  		}
  2293  		fiatTickers[i] = *result
  2294  	}
  2295  	return &FiatTickers{Tickers: fiatTickers}, nil
  2296  }
  2297  
  2298  // GetFiatRatesForBlockID returns fiat rates for block height or block hash
  2299  func (w *Worker) GetFiatRatesForBlockID(blockID string, currencies []string, token string) (*FiatTicker, error) {
  2300  	bi, err := w.getBlockInfoFromBlockID(blockID)
  2301  	if err != nil {
  2302  		if err == bchain.ErrBlockNotFound {
  2303  			return nil, NewAPIError(fmt.Sprintf("Block %v not found", blockID), true)
  2304  		}
  2305  		return nil, NewAPIError(fmt.Sprintf("Block %v not found, error: %v", blockID, err), false)
  2306  	}
  2307  	tickers, err := w.GetFiatRatesForTimestamps([]int64{bi.Time}, currencies, token)
  2308  	if err != nil || tickers == nil || len(tickers.Tickers) == 0 {
  2309  		return nil, err
  2310  	}
  2311  	return &tickers.Tickers[0], nil
  2312  }
  2313  
  2314  // GetAvailableVsCurrencies returns the list of available versus currencies for exchange rates
  2315  func (w *Worker) GetAvailableVsCurrencies(timestamp int64, token string) (*AvailableVsCurrencies, error) {
  2316  	tickers, err := w.fiatRates.GetTickersForTimestamps([]int64{timestamp}, "", token)
  2317  	if err != nil {
  2318  		return nil, NewAPIError(fmt.Sprintf("Error finding ticker: %v", err), false)
  2319  	}
  2320  	if tickers == nil || len(*tickers) == 0 {
  2321  		return nil, NewAPIError("No tickers found", true)
  2322  	}
  2323  	ticker := (*tickers)[0]
  2324  	keys := make([]string, 0, len(ticker.Rates))
  2325  	for k := range ticker.Rates {
  2326  		keys = append(keys, k)
  2327  	}
  2328  	sort.Strings(keys) // sort to get deterministic results
  2329  
  2330  	return &AvailableVsCurrencies{
  2331  		Timestamp: ticker.Timestamp.Unix(),
  2332  		Tickers:   keys,
  2333  	}, nil
  2334  }
  2335  
  2336  // getBlockHashBlockID returns block hash from block height or block hash
  2337  func (w *Worker) getBlockHashBlockID(bid string) string {
  2338  	// try to decide if passed string (bid) is block height or block hash
  2339  	// if it's a number, must be less than int32
  2340  	var hash string
  2341  	height, err := strconv.Atoi(bid)
  2342  	if err == nil && height < int(maxUint32) {
  2343  		hash, err = w.db.GetBlockHash(uint32(height))
  2344  		if err != nil {
  2345  			hash = bid
  2346  		}
  2347  	} else {
  2348  		hash = bid
  2349  	}
  2350  	return hash
  2351  }
  2352  
  2353  // getBlockInfoFromBlockID returns block info from block height or block hash
  2354  func (w *Worker) getBlockInfoFromBlockID(bid string) (*bchain.BlockInfo, error) {
  2355  	hash := w.getBlockHashBlockID(bid)
  2356  	if hash == "" {
  2357  		return nil, NewAPIError("Block not found", true)
  2358  	}
  2359  	bi, err := w.chain.GetBlockInfo(hash)
  2360  	return bi, err
  2361  }
  2362  
  2363  // GetFeeStats returns statistics about block fees
  2364  func (w *Worker) GetFeeStats(bid string) (*FeeStats, error) {
  2365  	// txSpecific extends Tx with an additional Size and Vsize info
  2366  	type txSpecific struct {
  2367  		*bchain.Tx
  2368  		Vsize int `json:"vsize,omitempty"`
  2369  		Size  int `json:"size,omitempty"`
  2370  	}
  2371  
  2372  	start := time.Now()
  2373  	bi, err := w.getBlockInfoFromBlockID(bid)
  2374  	if err != nil {
  2375  		if err == bchain.ErrBlockNotFound {
  2376  			return nil, NewAPIError("Block not found", true)
  2377  		}
  2378  		return nil, NewAPIError(fmt.Sprintf("Block not found, %v", err), true)
  2379  	}
  2380  
  2381  	feesPerKb := make([]int64, 0, len(bi.Txids))
  2382  	totalFeesSat := big.NewInt(0)
  2383  	averageFeePerKb := int64(0)
  2384  
  2385  	for _, txid := range bi.Txids {
  2386  		// Get a raw JSON with transaction details, including size, vsize, hex
  2387  		txSpecificJSON, err := w.chain.GetTransactionSpecific(&bchain.Tx{Txid: txid})
  2388  		if err != nil {
  2389  			return nil, errors.Annotatef(err, "GetTransactionSpecific")
  2390  		}
  2391  
  2392  		// Serialize the raw JSON into TxSpecific struct
  2393  		var txSpec txSpecific
  2394  		err = json.Unmarshal(txSpecificJSON, &txSpec)
  2395  		if err != nil {
  2396  			return nil, errors.Annotatef(err, "Unmarshal")
  2397  		}
  2398  
  2399  		// Calculate the TX size in bytes
  2400  		txSize := 0
  2401  		if txSpec.Vsize > 0 {
  2402  			txSize = txSpec.Vsize
  2403  		} else if txSpec.Size > 0 {
  2404  			txSize = txSpec.Size
  2405  		} else if txSpec.Hex != "" {
  2406  			txSize = len(txSpec.Hex) / 2
  2407  		} else {
  2408  			errMsg := "Cannot determine the transaction size from neither Vsize, Size nor Hex! Txid: " + txid
  2409  			return nil, NewAPIError(errMsg, true)
  2410  		}
  2411  
  2412  		// Get values of TX inputs and outputs
  2413  		txAddresses, err := w.db.GetTxAddresses(txid)
  2414  		if err != nil {
  2415  			return nil, errors.Annotatef(err, "GetTxAddresses")
  2416  		}
  2417  
  2418  		// Calculate total fees in Satoshis
  2419  		feeSat := big.NewInt(0)
  2420  		for _, input := range txAddresses.Inputs {
  2421  			feeSat = feeSat.Add(&input.ValueSat, feeSat)
  2422  		}
  2423  
  2424  		// Zero inputs means it's a Coinbase TX - skip it
  2425  		if feeSat.Cmp(big.NewInt(0)) == 0 {
  2426  			continue
  2427  		}
  2428  
  2429  		for _, output := range txAddresses.Outputs {
  2430  			feeSat = feeSat.Sub(feeSat, &output.ValueSat)
  2431  		}
  2432  		totalFeesSat.Add(totalFeesSat, feeSat)
  2433  
  2434  		// Convert feeSat to fee per kilobyte and add to an array for decile calculation
  2435  		feePerKb := int64(float64(feeSat.Int64()) / float64(txSize) * 1000)
  2436  		averageFeePerKb += feePerKb
  2437  		feesPerKb = append(feesPerKb, feePerKb)
  2438  	}
  2439  
  2440  	var deciles [11]int64
  2441  	n := len(feesPerKb)
  2442  
  2443  	if n > 0 {
  2444  		averageFeePerKb /= int64(n)
  2445  
  2446  		// Sort fees and calculate the deciles
  2447  		sort.Slice(feesPerKb, func(i, j int) bool { return feesPerKb[i] < feesPerKb[j] })
  2448  		for k := 0; k <= 10; k++ {
  2449  			index := int(math.Floor(0.5+float64(k)*float64(n+1)/10)) - 1
  2450  			if index < 0 {
  2451  				index = 0
  2452  			} else if index >= n {
  2453  				index = n - 1
  2454  			}
  2455  			deciles[k] = feesPerKb[index]
  2456  		}
  2457  	}
  2458  
  2459  	glog.Info("GetFeeStats ", bid, " (", len(feesPerKb), " txs), ", time.Since(start))
  2460  
  2461  	return &FeeStats{
  2462  		TxCount:         len(feesPerKb),
  2463  		AverageFeePerKb: averageFeePerKb,
  2464  		TotalFeesSat:    (*Amount)(totalFeesSat),
  2465  		DecilesFeePerKb: deciles,
  2466  	}, nil
  2467  }
  2468  
  2469  // GetBlock returns paged data about block
  2470  func (w *Worker) GetBlock(bid string, page int, txsOnPage int) (*Block, error) {
  2471  	start := time.Now()
  2472  	page--
  2473  	if page < 0 {
  2474  		page = 0
  2475  	}
  2476  	bi, err := w.getBlockInfoFromBlockID(bid)
  2477  	if err != nil {
  2478  		if err == bchain.ErrBlockNotFound {
  2479  			return nil, NewAPIError("Block not found", true)
  2480  		}
  2481  		return nil, NewAPIError(fmt.Sprintf("Block not found, %v", err), true)
  2482  	}
  2483  	dbi := &db.BlockInfo{
  2484  		Hash:   bi.Hash,
  2485  		Height: bi.Height,
  2486  		Time:   bi.Time,
  2487  	}
  2488  	txCount := len(bi.Txids)
  2489  	bestheight, _, err := w.db.GetBestBlock()
  2490  	if err != nil {
  2491  		return nil, errors.Annotatef(err, "GetBestBlock")
  2492  	}
  2493  	pg, from, to, page := computePaging(txCount, page, txsOnPage)
  2494  	txs := make([]*Tx, to-from)
  2495  	txi := 0
  2496  	addresses := w.newAddressesMapForAliases()
  2497  	for i := from; i < to; i++ {
  2498  		txs[txi], err = w.txFromTxid(bi.Txids[i], bestheight, AccountDetailsTxHistoryLight, dbi, addresses)
  2499  		if err != nil {
  2500  			return nil, err
  2501  		}
  2502  		txi++
  2503  	}
  2504  	if bi.Prev == "" && bi.Height != 0 {
  2505  		bi.Prev, _ = w.db.GetBlockHash(bi.Height - 1)
  2506  	}
  2507  	if bi.Next == "" && bi.Height != bestheight {
  2508  		bi.Next, _ = w.db.GetBlockHash(bi.Height + 1)
  2509  	}
  2510  	txs = txs[:txi]
  2511  	bi.Txids = nil
  2512  	glog.Info("GetBlock ", bid, ", page ", page, ", ", time.Since(start))
  2513  	return &Block{
  2514  		Paging: pg,
  2515  		BlockInfo: BlockInfo{
  2516  			Hash:          bi.Hash,
  2517  			Prev:          bi.Prev,
  2518  			Next:          bi.Next,
  2519  			Height:        bi.Height,
  2520  			Confirmations: bi.Confirmations,
  2521  			Size:          bi.Size,
  2522  			Time:          bi.Time,
  2523  			Bits:          bi.Bits,
  2524  			Difficulty:    string(bi.Difficulty),
  2525  			MerkleRoot:    bi.MerkleRoot,
  2526  			Nonce:         string(bi.Nonce),
  2527  			Txids:         bi.Txids,
  2528  			Version:       bi.Version,
  2529  		},
  2530  		TxCount:        txCount,
  2531  		Transactions:   txs,
  2532  		AddressAliases: w.getAddressAliases(addresses),
  2533  	}, nil
  2534  }
  2535  
  2536  // GetBlock returns paged data about block
  2537  func (w *Worker) GetBlockRaw(bid string) (*BlockRaw, error) {
  2538  	hash := w.getBlockHashBlockID(bid)
  2539  	if hash == "" {
  2540  		return nil, NewAPIError("Block not found", true)
  2541  	}
  2542  	hex, err := w.chain.GetBlockRaw(hash)
  2543  	if err != nil {
  2544  		if err == bchain.ErrBlockNotFound {
  2545  			return nil, NewAPIError("Block not found", true)
  2546  		}
  2547  		return nil, err
  2548  	}
  2549  	return &BlockRaw{Hex: hex}, err
  2550  }
  2551  
  2552  // ComputeFeeStats computes fee distribution in defined blocks and logs them to log
  2553  func (w *Worker) ComputeFeeStats(blockFrom, blockTo int, stopCompute chan os.Signal) error {
  2554  	bestheight, _, err := w.db.GetBestBlock()
  2555  	if err != nil {
  2556  		return errors.Annotatef(err, "GetBestBlock")
  2557  	}
  2558  	for block := blockFrom; block <= blockTo; block++ {
  2559  		hash, err := w.db.GetBlockHash(uint32(block))
  2560  		if err != nil {
  2561  			return err
  2562  		}
  2563  		bi, err := w.chain.GetBlockInfo(hash)
  2564  		if err != nil {
  2565  			return err
  2566  		}
  2567  		// process only blocks with enough transactions
  2568  		if len(bi.Txids) > 20 {
  2569  			dbi := &db.BlockInfo{
  2570  				Hash:   bi.Hash,
  2571  				Height: bi.Height,
  2572  				Time:   bi.Time,
  2573  			}
  2574  			txids := bi.Txids
  2575  			if w.chainType == bchain.ChainBitcoinType {
  2576  				// skip the coinbase transaction
  2577  				txids = txids[1:]
  2578  			}
  2579  			fees := make([]int64, len(txids))
  2580  			sum := int64(0)
  2581  			for i, txid := range txids {
  2582  				select {
  2583  				case <-stopCompute:
  2584  					glog.Info("ComputeFeeStats interrupted at height ", block)
  2585  					return db.ErrOperationInterrupted
  2586  				default:
  2587  					tx, err := w.txFromTxid(txid, bestheight, AccountDetailsTxHistoryLight, dbi, nil)
  2588  					if err != nil {
  2589  						return err
  2590  					}
  2591  					fee := tx.FeesSat.AsInt64()
  2592  					fees[i] = fee
  2593  					sum += fee
  2594  				}
  2595  			}
  2596  			sort.Slice(fees, func(i, j int) bool { return fees[i] < fees[j] })
  2597  			step := float64(len(fees)) / 10
  2598  			percentils := ""
  2599  			for i := float64(0); i < float64(len(fees)+1); i += step {
  2600  				ii := int(math.Round(i))
  2601  				if ii >= len(fees) {
  2602  					ii = len(fees) - 1
  2603  				}
  2604  				percentils += "," + strconv.FormatInt(fees[ii], 10)
  2605  			}
  2606  			glog.Info(block, ",", time.Unix(bi.Time, 0).Format(time.RFC3339), ",", len(bi.Txids), ",", sum, ",", float64(sum)/float64(len(bi.Txids)), percentils)
  2607  		}
  2608  	}
  2609  	return nil
  2610  }
  2611  
  2612  func nonZeroTime(t time.Time) *time.Time {
  2613  	if t.IsZero() {
  2614  		return nil
  2615  	}
  2616  	return &t
  2617  }
  2618  
  2619  // GetSystemInfo returns information about system
  2620  func (w *Worker) GetSystemInfo(internal bool) (*SystemInfo, error) {
  2621  	start := time.Now().UTC()
  2622  	vi := common.GetVersionInfo()
  2623  	inSync, bestHeight, lastBlockTime, startSync := w.is.GetSyncState()
  2624  	if !inSync && !w.is.InitialSync {
  2625  		// if less than 5 seconds into syncing, return inSync=true to avoid short time not in sync reports that confuse monitoring
  2626  		if startSync.Add(5 * time.Second).After(start) {
  2627  			inSync = true
  2628  		}
  2629  	}
  2630  	inSyncMempool, lastMempoolTime, mempoolSize := w.is.GetMempoolSyncState()
  2631  	ci, err := w.chain.GetChainInfo()
  2632  	var backendError string
  2633  	if err != nil {
  2634  		glog.Error("GetChainInfo error ", err)
  2635  		backendError = errors.Annotatef(err, "GetChainInfo").Error()
  2636  		ci = &bchain.ChainInfo{}
  2637  		// set not in sync in case of backend error
  2638  		inSync = false
  2639  		inSyncMempool = false
  2640  	}
  2641  	var columnStats []common.InternalStateColumn
  2642  	var internalDBSize int64
  2643  	if internal {
  2644  		columnStats = w.is.GetAllDBColumnStats()
  2645  		internalDBSize = w.is.DBSizeTotal()
  2646  	}
  2647  	var currentFiatRatesTime time.Time
  2648  	ct := w.fiatRates.GetCurrentTicker("", "")
  2649  	if ct != nil {
  2650  		currentFiatRatesTime = ct.Timestamp
  2651  	}
  2652  	blockbookInfo := &BlockbookInfo{
  2653  		Coin:                         w.is.Coin,
  2654  		Host:                         w.is.Host,
  2655  		Version:                      vi.Version,
  2656  		GitCommit:                    vi.GitCommit,
  2657  		BuildTime:                    vi.BuildTime,
  2658  		SyncMode:                     w.is.SyncMode,
  2659  		InitialSync:                  w.is.InitialSync,
  2660  		InSync:                       inSync,
  2661  		BestHeight:                   bestHeight,
  2662  		LastBlockTime:                lastBlockTime,
  2663  		InSyncMempool:                inSyncMempool,
  2664  		LastMempoolTime:              lastMempoolTime,
  2665  		MempoolSize:                  mempoolSize,
  2666  		Decimals:                     w.chainParser.AmountDecimals(),
  2667  		HasFiatRates:                 w.is.HasFiatRates,
  2668  		HasTokenFiatRates:            w.is.HasTokenFiatRates,
  2669  		CurrentFiatRatesTime:         nonZeroTime(currentFiatRatesTime),
  2670  		HistoricalFiatRatesTime:      nonZeroTime(w.is.HistoricalFiatRatesTime),
  2671  		HistoricalTokenFiatRatesTime: nonZeroTime(w.is.HistoricalTokenFiatRatesTime),
  2672  		DbSize:                       w.db.DatabaseSizeOnDisk(),
  2673  		DbSizeFromColumns:            internalDBSize,
  2674  		DbColumns:                    columnStats,
  2675  		About:                        Text.BlockbookAbout,
  2676  	}
  2677  	backendInfo := &common.BackendInfo{
  2678  		BackendError:     backendError,
  2679  		BestBlockHash:    ci.Bestblockhash,
  2680  		Blocks:           ci.Blocks,
  2681  		Chain:            ci.Chain,
  2682  		Difficulty:       ci.Difficulty,
  2683  		Headers:          ci.Headers,
  2684  		ProtocolVersion:  ci.ProtocolVersion,
  2685  		SizeOnDisk:       ci.SizeOnDisk,
  2686  		Subversion:       ci.Subversion,
  2687  		Timeoffset:       ci.Timeoffset,
  2688  		Version:          ci.Version,
  2689  		Warnings:         ci.Warnings,
  2690  		ConsensusVersion: ci.ConsensusVersion,
  2691  		Consensus:        ci.Consensus,
  2692  	}
  2693  	w.is.SetBackendInfo(backendInfo)
  2694  	glog.Info("GetSystemInfo, ", time.Since(start))
  2695  	return &SystemInfo{blockbookInfo, backendInfo}, nil
  2696  }
  2697  
  2698  // GetMempool returns a page of mempool txids
  2699  func (w *Worker) GetMempool(page int, itemsOnPage int) (*MempoolTxids, error) {
  2700  	page--
  2701  	if page < 0 {
  2702  		page = 0
  2703  	}
  2704  	entries := w.mempool.GetAllEntries()
  2705  	pg, from, to, _ := computePaging(len(entries), page, itemsOnPage)
  2706  	r := &MempoolTxids{
  2707  		Paging:      pg,
  2708  		MempoolSize: len(entries),
  2709  	}
  2710  	r.Mempool = make([]MempoolTxid, to-from)
  2711  	for i := from; i < to; i++ {
  2712  		entry := &entries[i]
  2713  		r.Mempool[i-from] = MempoolTxid{
  2714  			Txid: entry.Txid,
  2715  			Time: int64(entry.Time),
  2716  		}
  2717  	}
  2718  	return r, nil
  2719  }
  2720  
  2721  type bitcoinTypeEstimatedFee struct {
  2722  	timestamp int64
  2723  	fee       big.Int
  2724  	lock      sync.Mutex
  2725  }
  2726  
  2727  const estimatedFeeCacheSize = 300
  2728  
  2729  var estimatedFeeCache [estimatedFeeCacheSize]bitcoinTypeEstimatedFee
  2730  var estimatedFeeConservativeCache [estimatedFeeCacheSize]bitcoinTypeEstimatedFee
  2731  
  2732  func (w *Worker) cachedEstimateFee(blocks int, conservative bool) (big.Int, error) {
  2733  	var s *bitcoinTypeEstimatedFee
  2734  	if conservative {
  2735  		s = &estimatedFeeConservativeCache[blocks]
  2736  	} else {
  2737  		s = &estimatedFeeCache[blocks]
  2738  	}
  2739  	s.lock.Lock()
  2740  	defer s.lock.Unlock()
  2741  	// 10 seconds cache
  2742  	threshold := time.Now().Unix() - 10
  2743  	if s.timestamp >= threshold {
  2744  		return s.fee, nil
  2745  	}
  2746  	fee, err := w.chain.EstimateSmartFee(blocks, conservative)
  2747  	if err == nil {
  2748  		s.timestamp = time.Now().Unix()
  2749  		s.fee = fee
  2750  		// store metrics for the first 32 block estimates
  2751  		if blocks < 33 {
  2752  			w.metrics.EstimatedFee.With(common.Labels{
  2753  				"blocks":       strconv.Itoa(blocks),
  2754  				"conservative": strconv.FormatBool(conservative),
  2755  			}).Set(float64(fee.Int64()))
  2756  		}
  2757  	}
  2758  	return fee, err
  2759  }
  2760  
  2761  // EstimateFee returns a fee estimation for given number of blocks
  2762  // it uses 10 second cache to reduce calls to the backend
  2763  func (w *Worker) EstimateFee(blocks int, conservative bool) (big.Int, error) {
  2764  	if blocks >= estimatedFeeCacheSize {
  2765  		return w.chain.EstimateSmartFee(blocks, conservative)
  2766  	}
  2767  	return w.cachedEstimateFee(blocks, conservative)
  2768  }