Use TxAddresses index in TxCache

pull/74/head
Martin Boehm 2018-10-08 14:55:21 +02:00
parent 384d24c6c2
commit 84b457b34a
7 changed files with 58 additions and 57 deletions

View File

@ -46,26 +46,26 @@ func (w *Worker) getAddressesFromVout(vout *bchain.Vout) (bchain.AddressDescript
// setSpendingTxToVout is helper function, that finds transaction that spent given output and sets it to the output
// there is not an index, it must be found using addresses -> txaddresses -> tx
func (w *Worker) setSpendingTxToVout(vout *Vout, txid string, height, bestheight uint32) error {
func (w *Worker) setSpendingTxToVout(vout *Vout, txid string, height uint32) error {
err := w.db.GetAddrDescTransactions(vout.ScriptPubKey.AddrDesc, height, ^uint32(0), func(t string, index uint32, isOutput bool) error {
if isOutput == false {
tsp, err := w.db.GetTxAddresses(t)
if err != nil {
return err
} else if tsp == nil {
glog.Warning("DB inconsistency: tx ", t, ": not found in txAddresses")
} else {
if len(tsp.Inputs) > int(index) {
if tsp.Inputs[index].ValueSat.Cmp(&vout.ValueSat) == 0 {
spentTx, spentHeight, err := w.txCache.GetTransaction(t, bestheight)
if err != nil {
glog.Warning("Tx ", t, ": not found")
} else {
if len(spentTx.Vin) > int(index) {
if spentTx.Vin[index].Txid == txid {
vout.SpentTxID = t
vout.SpentHeight = int(spentHeight)
vout.SpentIndex = int(index)
return &db.StopIteration{}
}
} else if len(tsp.Inputs) > int(index) {
if tsp.Inputs[index].ValueSat.Cmp(&vout.ValueSat) == 0 {
spentTx, spentHeight, err := w.txCache.GetTransaction(t)
if err != nil {
glog.Warning("Tx ", t, ": not found")
} else {
if len(spentTx.Vin) > int(index) {
if spentTx.Vin[index].Txid == txid {
vout.SpentTxID = t
vout.SpentHeight = int(spentHeight)
vout.SpentIndex = int(index)
return &db.StopIteration{}
}
}
}
@ -80,18 +80,14 @@ func (w *Worker) setSpendingTxToVout(vout *Vout, txid string, height, bestheight
// GetSpendingTxid returns transaction id of transaction that spent given output
func (w *Worker) GetSpendingTxid(txid string, n int) (string, error) {
start := time.Now()
bestheight, _, err := w.db.GetBestBlock()
if err != nil {
return "", err
}
tx, err := w.GetTransaction(txid, bestheight, false)
tx, err := w.GetTransaction(txid, false)
if err != nil {
return "", err
}
if n >= len(tx.Vout) || n < 0 {
return "", NewApiError(fmt.Sprintf("Passed incorrect vout index %v for tx %v, len vout %v", n, tx.Txid, len(tx.Vout)), false)
}
err = w.setSpendingTxToVout(&tx.Vout[n], tx.Txid, uint32(tx.Blockheight), bestheight)
err = w.setSpendingTxToVout(&tx.Vout[n], tx.Txid, uint32(tx.Blockheight))
if err != nil {
return "", err
}
@ -100,9 +96,9 @@ func (w *Worker) GetSpendingTxid(txid string, n int) (string, error) {
}
// GetTransaction reads transaction data from txid
func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTxs bool) (*Tx, error) {
func (w *Worker) GetTransaction(txid string, spendingTxs bool) (*Tx, error) {
start := time.Now()
bchainTx, height, err := w.txCache.GetTransaction(txid, bestheight)
bchainTx, height, err := w.txCache.GetTransaction(txid)
if err != nil {
return nil, NewApiError(fmt.Sprintf("Tx not found, %v", err), true)
}
@ -140,7 +136,7 @@ func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTxs bool
glog.Warning("DB inconsistency: tx ", bchainVin.Txid, ": not found in txAddresses")
}
// try to load from backend
otx, _, err := w.txCache.GetTransaction(bchainVin.Txid, bestheight)
otx, _, err := w.txCache.GetTransaction(bchainVin.Txid)
if err != nil {
return nil, errors.Annotatef(err, "txCache.GetTransaction %v", bchainVin.Txid)
}
@ -181,7 +177,7 @@ func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTxs bool
if ta != nil {
vout.Spent = ta.Outputs[i].Spent
if spendingTxs && vout.Spent {
err = w.setSpendingTxToVout(vout, bchainTx.Txid, height, bestheight)
err = w.setSpendingTxToVout(vout, bchainTx.Txid, height)
if err != nil {
glog.Errorf("setSpendingTxToVout error %v, %v, output %v", err, vout.ScriptPubKey.AddrDesc, vout.N)
}
@ -406,7 +402,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, onlyTxids b
// load mempool transactions
var uBalSat big.Int
for _, tx := range txm {
tx, err := w.GetTransaction(tx, bestheight, false)
tx, err := w.GetTransaction(tx, false)
// mempool transaction may fail
if err != nil {
glog.Error("GetTransaction in mempool ", tx, ": ", err)

View File

@ -237,7 +237,7 @@ func main() {
return
}
if txCache, err = db.NewTxCache(index, chain, metrics, !*noTxCache); err != nil {
if txCache, err = db.NewTxCache(index, chain, metrics, internalState, !*noTxCache); err != nil {
glog.Error("txCache ", err)
return
}

View File

@ -12,11 +12,12 @@ type TxCache struct {
db *RocksDB
chain bchain.BlockChain
metrics *common.Metrics
is *common.InternalState
enabled bool
}
// NewTxCache creates new TxCache interface and returns its handle
func NewTxCache(db *RocksDB, chain bchain.BlockChain, metrics *common.Metrics, enabled bool) (*TxCache, error) {
func NewTxCache(db *RocksDB, chain bchain.BlockChain, metrics *common.Metrics, is *common.InternalState, enabled bool) (*TxCache, error) {
if !enabled {
glog.Info("txcache: disabled")
}
@ -24,13 +25,14 @@ func NewTxCache(db *RocksDB, chain bchain.BlockChain, metrics *common.Metrics, e
db: db,
chain: chain,
metrics: metrics,
is: is,
enabled: enabled,
}, nil
}
// GetTransaction returns transaction either from RocksDB or if not present from blockchain
// it the transaction is confirmed, it is stored in the RocksDB
func (c *TxCache) GetTransaction(txid string, bestheight uint32) (*bchain.Tx, uint32, error) {
func (c *TxCache) GetTransaction(txid string) (*bchain.Tx, uint32, error) {
var tx *bchain.Tx
var h uint32
var err error
@ -41,6 +43,7 @@ func (c *TxCache) GetTransaction(txid string, bestheight uint32) (*bchain.Tx, ui
}
if tx != nil {
// number of confirmations is not stored in cache, they change all the time
_, bestheight, _ := c.is.GetSyncState()
tx.Confirmations = bestheight - h + 1
c.metrics.TxCacheEfficiency.With(common.Labels{"status": "hit"}).Inc()
return tx, h, nil
@ -51,10 +54,21 @@ func (c *TxCache) GetTransaction(txid string, bestheight uint32) (*bchain.Tx, ui
return nil, 0, err
}
c.metrics.TxCacheEfficiency.With(common.Labels{"status": "miss"}).Inc()
// do not cache mempool transactions
// cache only confirmed transactions
if tx.Confirmations > 0 {
// the transaction in the currently best block has 1 confirmation
h = bestheight - tx.Confirmations + 1
ta, err := c.db.GetTxAddresses(txid)
if err != nil {
return nil, 0, err
}
// the transaction may me not yet indexed, in that case get the height from the backend
if ta == nil {
h, err = c.chain.GetBestBlockHeight()
if err != nil {
return nil, 0, err
}
} else {
h = ta.Height
}
if c.enabled {
err = c.db.PutTx(tx, h, tx.Blocktime)
// do not return caching error, only log it

View File

@ -366,13 +366,11 @@ func setTxToTemplateData(td *TemplateData, tx *api.Tx) *TemplateData {
func (s *PublicServer) explorerTx(w http.ResponseWriter, r *http.Request) (tpl, *TemplateData, error) {
var tx *api.Tx
var err error
s.metrics.ExplorerViews.With(common.Labels{"action": "tx"}).Inc()
if i := strings.LastIndexByte(r.URL.Path, '/'); i > 0 {
txid := r.URL.Path[i+1:]
bestheight, _, err := s.db.GetBestBlock()
if err == nil {
tx, err = s.api.GetTransaction(txid, bestheight, false)
}
tx, err = s.api.GetTransaction(txid, false)
if err != nil {
return errorTpl, nil, err
}
@ -483,7 +481,6 @@ func (s *PublicServer) explorerSearch(w http.ResponseWriter, r *http.Request) (t
var tx *api.Tx
var address *api.Address
var block *api.Block
var bestheight uint32
var err error
s.metrics.ExplorerViews.With(common.Labels{"action": "search"}).Inc()
if len(q) > 0 {
@ -492,13 +489,10 @@ func (s *PublicServer) explorerSearch(w http.ResponseWriter, r *http.Request) (t
http.Redirect(w, r, joinURL("/block/", block.Hash), 302)
return noTpl, nil, nil
}
bestheight, _, err = s.db.GetBestBlock()
tx, err = s.api.GetTransaction(q, false)
if err == nil {
tx, err = s.api.GetTransaction(q, bestheight, false)
if err == nil {
http.Redirect(w, r, joinURL("/tx/", tx.Txid), 302)
return noTpl, nil, nil
}
http.Redirect(w, r, joinURL("/tx/", tx.Txid), 302)
return noTpl, nil, nil
}
address, err = s.api.GetAddress(q, 0, 1, true)
if err == nil {
@ -600,10 +594,7 @@ func (s *PublicServer) apiTx(r *http.Request) (interface{}, error) {
s.metrics.ExplorerViews.With(common.Labels{"action": "api-tx"}).Inc()
if i := strings.LastIndexByte(r.URL.Path, '/'); i > 0 {
txid := r.URL.Path[i+1:]
bestheight, _, err := s.db.GetBestBlock()
if err == nil {
tx, err = s.api.GetTransaction(txid, bestheight, true)
}
tx, err = s.api.GetTransaction(txid, true)
}
return tx, err
}

View File

@ -377,10 +377,6 @@ func (s *SocketIoServer) getAddressHistory(addr []string, opts *addrOpts) (res r
if err != nil {
return
}
bestheight, _, err := s.db.GetBestBlock()
if err != nil {
return
}
txids := txr.Result
res.Result.TotalCount = len(txids)
res.Result.Items = make([]addressHistoryItem, 0)
@ -389,7 +385,7 @@ func (s *SocketIoServer) getAddressHistory(addr []string, opts *addrOpts) (res r
to = opts.To
}
for txi := opts.From; txi < to; txi++ {
tx, err := s.api.GetTransaction(txids[txi], bestheight, false)
tx, err := s.api.GetTransaction(txids[txi], false)
// for i, txid := range txids {
// if i >= opts.From && i < opts.To {
// tx, err := s.api.GetTransaction(txid, bestheight, false)
@ -632,11 +628,7 @@ type resultGetDetailedTransaction struct {
}
func (s *SocketIoServer) getDetailedTransaction(txid string) (res resultGetDetailedTransaction, err error) {
bestheight, _, err := s.db.GetBestBlock()
if err != nil {
return
}
tx, err := s.api.GetTransaction(txid, bestheight, false)
tx, err := s.api.GetTransaction(txid, false)
if err != nil {
return res, err
}

View File

@ -176,6 +176,10 @@ func verifyAddresses(t *testing.T, d *db.RocksDB, h *TestHandler, rng Range) {
if err != nil {
t.Fatal(err)
}
if ta == nil {
t.Errorf("Tx %s: not found in TxAddresses", tx.Txid)
continue
}
txInfo := getTxInfo(tx)
taInfo, err := getTaInfo(parser, ta)

View File

@ -80,6 +80,10 @@ func verifyAddresses2(t *testing.T, d *db.RocksDB, chain bchain.BlockChain, blks
if err != nil {
t.Fatal(err)
}
if ta == nil {
t.Errorf("Tx %s: not found in TxAddresses", tx.Txid)
continue
}
txInfo := getTxInfo(&tx)
taInfo, err := getTaInfo(parser, ta)