Use TxAddresses index in TxCache
parent
384d24c6c2
commit
84b457b34a
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue