Use TxAddresses index in TxCache
This commit is contained in:
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
|
// 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
|
// 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 {
|
err := w.db.GetAddrDescTransactions(vout.ScriptPubKey.AddrDesc, height, ^uint32(0), func(t string, index uint32, isOutput bool) error {
|
||||||
if isOutput == false {
|
if isOutput == false {
|
||||||
tsp, err := w.db.GetTxAddresses(t)
|
tsp, err := w.db.GetTxAddresses(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if tsp == nil {
|
||||||
glog.Warning("DB inconsistency: tx ", t, ": not found in txAddresses")
|
glog.Warning("DB inconsistency: tx ", t, ": not found in txAddresses")
|
||||||
} else {
|
} else if len(tsp.Inputs) > int(index) {
|
||||||
if len(tsp.Inputs) > int(index) {
|
if tsp.Inputs[index].ValueSat.Cmp(&vout.ValueSat) == 0 {
|
||||||
if tsp.Inputs[index].ValueSat.Cmp(&vout.ValueSat) == 0 {
|
spentTx, spentHeight, err := w.txCache.GetTransaction(t)
|
||||||
spentTx, spentHeight, err := w.txCache.GetTransaction(t, bestheight)
|
if err != nil {
|
||||||
if err != nil {
|
glog.Warning("Tx ", t, ": not found")
|
||||||
glog.Warning("Tx ", t, ": not found")
|
} else {
|
||||||
} else {
|
if len(spentTx.Vin) > int(index) {
|
||||||
if len(spentTx.Vin) > int(index) {
|
if spentTx.Vin[index].Txid == txid {
|
||||||
if spentTx.Vin[index].Txid == txid {
|
vout.SpentTxID = t
|
||||||
vout.SpentTxID = t
|
vout.SpentHeight = int(spentHeight)
|
||||||
vout.SpentHeight = int(spentHeight)
|
vout.SpentIndex = int(index)
|
||||||
vout.SpentIndex = int(index)
|
return &db.StopIteration{}
|
||||||
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
|
// GetSpendingTxid returns transaction id of transaction that spent given output
|
||||||
func (w *Worker) GetSpendingTxid(txid string, n int) (string, error) {
|
func (w *Worker) GetSpendingTxid(txid string, n int) (string, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
bestheight, _, err := w.db.GetBestBlock()
|
tx, err := w.GetTransaction(txid, false)
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
tx, err := w.GetTransaction(txid, bestheight, false)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if n >= len(tx.Vout) || n < 0 {
|
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)
|
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 {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -100,9 +96,9 @@ func (w *Worker) GetSpendingTxid(txid string, n int) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetTransaction reads transaction data from txid
|
// 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()
|
start := time.Now()
|
||||||
bchainTx, height, err := w.txCache.GetTransaction(txid, bestheight)
|
bchainTx, height, err := w.txCache.GetTransaction(txid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewApiError(fmt.Sprintf("Tx not found, %v", err), true)
|
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")
|
glog.Warning("DB inconsistency: tx ", bchainVin.Txid, ": not found in txAddresses")
|
||||||
}
|
}
|
||||||
// try to load from backend
|
// try to load from backend
|
||||||
otx, _, err := w.txCache.GetTransaction(bchainVin.Txid, bestheight)
|
otx, _, err := w.txCache.GetTransaction(bchainVin.Txid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Annotatef(err, "txCache.GetTransaction %v", bchainVin.Txid)
|
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 {
|
if ta != nil {
|
||||||
vout.Spent = ta.Outputs[i].Spent
|
vout.Spent = ta.Outputs[i].Spent
|
||||||
if spendingTxs && vout.Spent {
|
if spendingTxs && vout.Spent {
|
||||||
err = w.setSpendingTxToVout(vout, bchainTx.Txid, height, bestheight)
|
err = w.setSpendingTxToVout(vout, bchainTx.Txid, height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("setSpendingTxToVout error %v, %v, output %v", err, vout.ScriptPubKey.AddrDesc, vout.N)
|
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
|
// load mempool transactions
|
||||||
var uBalSat big.Int
|
var uBalSat big.Int
|
||||||
for _, tx := range txm {
|
for _, tx := range txm {
|
||||||
tx, err := w.GetTransaction(tx, bestheight, false)
|
tx, err := w.GetTransaction(tx, false)
|
||||||
// mempool transaction may fail
|
// mempool transaction may fail
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Error("GetTransaction in mempool ", tx, ": ", err)
|
glog.Error("GetTransaction in mempool ", tx, ": ", err)
|
||||||
|
|||||||
@ -237,7 +237,7 @@ func main() {
|
|||||||
return
|
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)
|
glog.Error("txCache ", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,11 +12,12 @@ type TxCache struct {
|
|||||||
db *RocksDB
|
db *RocksDB
|
||||||
chain bchain.BlockChain
|
chain bchain.BlockChain
|
||||||
metrics *common.Metrics
|
metrics *common.Metrics
|
||||||
|
is *common.InternalState
|
||||||
enabled bool
|
enabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTxCache creates new TxCache interface and returns its handle
|
// 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 {
|
if !enabled {
|
||||||
glog.Info("txcache: disabled")
|
glog.Info("txcache: disabled")
|
||||||
}
|
}
|
||||||
@ -24,13 +25,14 @@ func NewTxCache(db *RocksDB, chain bchain.BlockChain, metrics *common.Metrics, e
|
|||||||
db: db,
|
db: db,
|
||||||
chain: chain,
|
chain: chain,
|
||||||
metrics: metrics,
|
metrics: metrics,
|
||||||
|
is: is,
|
||||||
enabled: enabled,
|
enabled: enabled,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTransaction returns transaction either from RocksDB or if not present from blockchain
|
// GetTransaction returns transaction either from RocksDB or if not present from blockchain
|
||||||
// it the transaction is confirmed, it is stored in the RocksDB
|
// 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 tx *bchain.Tx
|
||||||
var h uint32
|
var h uint32
|
||||||
var err error
|
var err error
|
||||||
@ -41,6 +43,7 @@ func (c *TxCache) GetTransaction(txid string, bestheight uint32) (*bchain.Tx, ui
|
|||||||
}
|
}
|
||||||
if tx != nil {
|
if tx != nil {
|
||||||
// number of confirmations is not stored in cache, they change all the time
|
// number of confirmations is not stored in cache, they change all the time
|
||||||
|
_, bestheight, _ := c.is.GetSyncState()
|
||||||
tx.Confirmations = bestheight - h + 1
|
tx.Confirmations = bestheight - h + 1
|
||||||
c.metrics.TxCacheEfficiency.With(common.Labels{"status": "hit"}).Inc()
|
c.metrics.TxCacheEfficiency.With(common.Labels{"status": "hit"}).Inc()
|
||||||
return tx, h, nil
|
return tx, h, nil
|
||||||
@ -51,10 +54,21 @@ func (c *TxCache) GetTransaction(txid string, bestheight uint32) (*bchain.Tx, ui
|
|||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
c.metrics.TxCacheEfficiency.With(common.Labels{"status": "miss"}).Inc()
|
c.metrics.TxCacheEfficiency.With(common.Labels{"status": "miss"}).Inc()
|
||||||
// do not cache mempool transactions
|
// cache only confirmed transactions
|
||||||
if tx.Confirmations > 0 {
|
if tx.Confirmations > 0 {
|
||||||
// the transaction in the currently best block has 1 confirmation
|
ta, err := c.db.GetTxAddresses(txid)
|
||||||
h = bestheight - tx.Confirmations + 1
|
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 {
|
if c.enabled {
|
||||||
err = c.db.PutTx(tx, h, tx.Blocktime)
|
err = c.db.PutTx(tx, h, tx.Blocktime)
|
||||||
// do not return caching error, only log it
|
// 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) {
|
func (s *PublicServer) explorerTx(w http.ResponseWriter, r *http.Request) (tpl, *TemplateData, error) {
|
||||||
var tx *api.Tx
|
var tx *api.Tx
|
||||||
|
var err error
|
||||||
s.metrics.ExplorerViews.With(common.Labels{"action": "tx"}).Inc()
|
s.metrics.ExplorerViews.With(common.Labels{"action": "tx"}).Inc()
|
||||||
if i := strings.LastIndexByte(r.URL.Path, '/'); i > 0 {
|
if i := strings.LastIndexByte(r.URL.Path, '/'); i > 0 {
|
||||||
txid := r.URL.Path[i+1:]
|
txid := r.URL.Path[i+1:]
|
||||||
bestheight, _, err := s.db.GetBestBlock()
|
tx, err = s.api.GetTransaction(txid, false)
|
||||||
if err == nil {
|
|
||||||
tx, err = s.api.GetTransaction(txid, bestheight, false)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorTpl, nil, err
|
return errorTpl, nil, err
|
||||||
}
|
}
|
||||||
@ -483,7 +481,6 @@ func (s *PublicServer) explorerSearch(w http.ResponseWriter, r *http.Request) (t
|
|||||||
var tx *api.Tx
|
var tx *api.Tx
|
||||||
var address *api.Address
|
var address *api.Address
|
||||||
var block *api.Block
|
var block *api.Block
|
||||||
var bestheight uint32
|
|
||||||
var err error
|
var err error
|
||||||
s.metrics.ExplorerViews.With(common.Labels{"action": "search"}).Inc()
|
s.metrics.ExplorerViews.With(common.Labels{"action": "search"}).Inc()
|
||||||
if len(q) > 0 {
|
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)
|
http.Redirect(w, r, joinURL("/block/", block.Hash), 302)
|
||||||
return noTpl, nil, nil
|
return noTpl, nil, nil
|
||||||
}
|
}
|
||||||
bestheight, _, err = s.db.GetBestBlock()
|
tx, err = s.api.GetTransaction(q, false)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
tx, err = s.api.GetTransaction(q, bestheight, false)
|
http.Redirect(w, r, joinURL("/tx/", tx.Txid), 302)
|
||||||
if err == nil {
|
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)
|
address, err = s.api.GetAddress(q, 0, 1, true)
|
||||||
if err == nil {
|
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()
|
s.metrics.ExplorerViews.With(common.Labels{"action": "api-tx"}).Inc()
|
||||||
if i := strings.LastIndexByte(r.URL.Path, '/'); i > 0 {
|
if i := strings.LastIndexByte(r.URL.Path, '/'); i > 0 {
|
||||||
txid := r.URL.Path[i+1:]
|
txid := r.URL.Path[i+1:]
|
||||||
bestheight, _, err := s.db.GetBestBlock()
|
tx, err = s.api.GetTransaction(txid, true)
|
||||||
if err == nil {
|
|
||||||
tx, err = s.api.GetTransaction(txid, bestheight, true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return tx, err
|
return tx, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -377,10 +377,6 @@ func (s *SocketIoServer) getAddressHistory(addr []string, opts *addrOpts) (res r
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
bestheight, _, err := s.db.GetBestBlock()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
txids := txr.Result
|
txids := txr.Result
|
||||||
res.Result.TotalCount = len(txids)
|
res.Result.TotalCount = len(txids)
|
||||||
res.Result.Items = make([]addressHistoryItem, 0)
|
res.Result.Items = make([]addressHistoryItem, 0)
|
||||||
@ -389,7 +385,7 @@ func (s *SocketIoServer) getAddressHistory(addr []string, opts *addrOpts) (res r
|
|||||||
to = opts.To
|
to = opts.To
|
||||||
}
|
}
|
||||||
for txi := opts.From; txi < to; txi++ {
|
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 {
|
// for i, txid := range txids {
|
||||||
// if i >= opts.From && i < opts.To {
|
// if i >= opts.From && i < opts.To {
|
||||||
// tx, err := s.api.GetTransaction(txid, bestheight, false)
|
// 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) {
|
func (s *SocketIoServer) getDetailedTransaction(txid string) (res resultGetDetailedTransaction, err error) {
|
||||||
bestheight, _, err := s.db.GetBestBlock()
|
tx, err := s.api.GetTransaction(txid, false)
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tx, err := s.api.GetTransaction(txid, bestheight, false)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -176,6 +176,10 @@ func verifyAddresses(t *testing.T, d *db.RocksDB, h *TestHandler, rng Range) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
if ta == nil {
|
||||||
|
t.Errorf("Tx %s: not found in TxAddresses", tx.Txid)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
txInfo := getTxInfo(tx)
|
txInfo := getTxInfo(tx)
|
||||||
taInfo, err := getTaInfo(parser, ta)
|
taInfo, err := getTaInfo(parser, ta)
|
||||||
|
|||||||
@ -80,6 +80,10 @@ func verifyAddresses2(t *testing.T, d *db.RocksDB, chain bchain.BlockChain, blks
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
if ta == nil {
|
||||||
|
t.Errorf("Tx %s: not found in TxAddresses", tx.Txid)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
txInfo := getTxInfo(&tx)
|
txInfo := getTxInfo(&tx)
|
||||||
taInfo, err := getTaInfo(parser, ta)
|
taInfo, err := getTaInfo(parser, ta)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user