Skip to content

Commit

Permalink
don't hammer gettxout when utxo cache has the answer
Browse files Browse the repository at this point in the history
  • Loading branch information
chappjc committed Sep 6, 2023
1 parent 1fdcc52 commit 1fb3106
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 44 deletions.
45 changes: 25 additions & 20 deletions cmd/dcrdata/internal/explorer/explorerroutes.go
Original file line number Diff line number Diff line change
Expand Up @@ -853,7 +853,8 @@ func (exp *explorerUI) TxPage(w http.ResponseWriter, r *http.Request) {
tx := exp.dataSource.GetExplorerTx(hash)
// If dcrd has no information about the transaction, pull the transaction
// details from the auxiliary DB database.
if tx == nil {
const checkDB = false // screw it
if tx == nil && checkDB {
log.Warnf("No transaction information for %v. Trying tables in case this is an orphaned txn.", hash)
// Search for occurrences of the transaction in the database.
dbTxs, err := exp.dataSource.Transaction(hash)
Expand Down Expand Up @@ -1051,7 +1052,7 @@ func (exp *explorerUI) TxPage(w http.ResponseWriter, r *http.Request) {
}
} // tx == nil (not found by dcrd)

// Check for any transaction outputs that appear unspent.
// Check for any transaction outputs that *appear* unspent.
unspents := types.UnspentOutputIndices(tx.Vout)
if len(unspents) > 0 {
// Grab the mempool transaction inputs that match this transaction.
Expand Down Expand Up @@ -1976,7 +1977,8 @@ func (exp *explorerUI) Search(w http.ResponseWriter, r *http.Request) {
}

// If it is not a valid hash, try proposals and give up.
if _, err = chainhash.NewHashFromStr(searchStrSplit[0]); err != nil {
hash, err := chainhash.NewHashFromStr(searchStrSplit[0])
if err != nil {
if tryProp() {
return
}
Expand All @@ -1985,28 +1987,31 @@ func (exp *explorerUI) Search(w http.ResponseWriter, r *http.Request) {
"", ExpStatusNotFound)
return
}
hashStr := hash.String()
if utxoLike {
searchStrRewritten = hashStr + "/out/" + searchStrSplit[1]
}

// A valid hash could be block, txid, or prop. First try blocks, then tx via
// getrawtransaction, then props, then tx via DB query.

if !utxoLike {
// Attempt to get a block index by calling GetBlockHeight to see if the
// value is a block hash and then redirect to the block page if it is.
_, err = exp.dataSource.GetBlockHeight(searchStrSplit[0])
_, err = exp.dataSource.GetBlockHeight(hashStr)
if err == nil {
http.Redirect(w, r, "/block/"+searchStrSplit[0], http.StatusPermanentRedirect)
http.Redirect(w, r, "/block/"+hashStr, http.StatusPermanentRedirect)
return
}
}

// It's unlikely to be a tx id with many leading/trailing zeros.
trimmedZeros := 2*chainhash.HashSize - len(strings.Trim(searchStrSplit[0], "0"))
trimmedZeros := 2*chainhash.HashSize - len(strings.Trim(hashStr, "0"))

// Call GetExplorerTx to see if the value is a transaction hash and then
// redirect to the tx page if it is.
// See if it's a transaction and then redirect to the tx page if it is.
if trimmedZeros < 10 {
tx := exp.dataSource.GetExplorerTx(searchStrSplit[0])
if tx != nil {
_, err = exp.dataSource.GetTransactionByHash(hashStr)
if err == nil {
http.Redirect(w, r, "/tx/"+searchStrRewritten, http.StatusPermanentRedirect)
return
}
Expand All @@ -2018,16 +2023,16 @@ func (exp *explorerUI) Search(w http.ResponseWriter, r *http.Request) {
}

// Also check the DB as it may have transactions from orphaned blocks.
if trimmedZeros < 10 {
dbTxs, err := exp.dataSource.Transaction(searchStrSplit[0])
if err != nil && !errors.Is(err, dbtypes.ErrNoResult) {
log.Errorf("Searching for transaction failed: %v", err)
}
if dbTxs != nil {
http.Redirect(w, r, "/tx/"+searchStrRewritten, http.StatusPermanentRedirect)
return
}
}
// if trimmedZeros < 10 {
// dbTxs, err := exp.dataSource.Transaction(searchStrSplit[0])
// if err != nil && !errors.Is(err, dbtypes.ErrNoResult) {
// log.Errorf("Searching for transaction failed: %v", err)
// }
// if dbTxs != nil {
// http.Redirect(w, r, "/tx/"+searchStrRewritten, http.StatusPermanentRedirect)
// return
// }
// }

message := "The search did not find any matching address, block, transaction or proposal token: " + searchStr
exp.StatusPage(w, "search failed", message, "", ExpStatusNotFound)
Expand Down
64 changes: 41 additions & 23 deletions db/dcrpg/pgblockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func UpdateTicketPoolData(interval dbtypes.TimeBasedGrouping, timeGraph *dbtypes

// utxoStore provides a UTXOData cache with thread-safe get/set methods.
type utxoStore struct {
sync.Mutex
sync.RWMutex
c map[string]map[uint32]*dbtypes.UTXOData
}

Expand Down Expand Up @@ -159,8 +159,8 @@ func (u *utxoStore) Get(txHash string, txIndex uint32) (*dbtypes.UTXOData, bool)
}

func (u *utxoStore) Peek(txHash string, txIndex uint32) *dbtypes.UTXOData {
u.Lock()
defer u.Unlock()
u.RLock()
defer u.RUnlock()
txVals, ok := u.c[txHash]
if !ok {
return nil
Expand Down Expand Up @@ -215,8 +215,8 @@ func (u *utxoStore) Reinit(utxos []dbtypes.UTXO) {

// Size returns the size of the utxo cache in number of UTXOs.
func (u *utxoStore) Size() (sz int) {
u.Lock()
defer u.Unlock()
u.RLock()
defer u.RUnlock()
for _, m := range u.c {
sz += len(m)
}
Expand Down Expand Up @@ -6053,7 +6053,7 @@ func (pgb *ChainDB) GetExplorerTx(txid string) *exptypes.TxInfo {
return nil
}

txBasic, txType := makeExplorerTxBasic(txraw, ticketPrice, msgTx, pgb.chainParams)
txBasic, _ /* txType */ := makeExplorerTxBasic(txraw, ticketPrice, msgTx, pgb.chainParams)
tx := &exptypes.TxInfo{
TxBasic: txBasic,
BlockHeight: txraw.BlockHeight,
Expand All @@ -6063,8 +6063,6 @@ func (pgb *ChainDB) GetExplorerTx(txid string) *exptypes.TxInfo {
Time: exptypes.NewTimeDefFromUNIX(txraw.Time),
}

// tree := txType stake.TxTypeRegular

inputs := make([]exptypes.Vin, 0, len(txraw.Vin))
for i := range txraw.Vin {
vin := &txraw.Vin[i]
Expand Down Expand Up @@ -6267,43 +6265,63 @@ func (pgb *ChainDB) GetExplorerTx(txid string) *exptypes.TxInfo {
}
}

tree := wire.TxTreeStake
if txType == stake.TxTypeRegular {
tree = wire.TxTreeRegular
}
// tree := wire.TxTreeStake
// if txType == stake.TxTypeRegular {
// tree = wire.TxTreeRegular
// }

CoinbaseMaturityInHours := (pgb.chainParams.TargetTimePerBlock.Hours() * float64(pgb.chainParams.CoinbaseMaturity))
tx.MaturityTimeTill = ((float64(pgb.chainParams.CoinbaseMaturity) -
float64(tx.Confirmations)) / float64(pgb.chainParams.CoinbaseMaturity)) * CoinbaseMaturityInHours

outputs := make([]exptypes.Vout, 0, len(txraw.Vout))
for i, vout := range txraw.Vout {
// Determine spent status with gettxout, including mempool.
txout, err := pgb.Client.GetTxOut(context.TODO(), txhash, uint32(i), tree, true)
if err != nil {
log.Warnf("Failed to determine if tx out is spent for output %d of tx %s: %v", i, txid, err)
for i := range txraw.Vout {
vout := &txraw.Vout[i]

// If uncertain (this tx is unconfirmed or the spender is unconfirmed),
// go with spent == false, since caller should check for unconfirmed
// spenders.
var spent bool
if txraw.Confirmations > 0 {
utxoData := pgb.utxoCache.Peek(txhash.String(), uint32(i))
spent = utxoData == nil // if true, definitely spent since this tx is confirmed; if false, spender might be unconfirmed!
}
// gettxout never indicates spent if the spending tx is unconfirmed,
// which is also necessarily the case if this tx is unconfirmed. Caller
// has to check mempool for that, so we will leave Spent false.

/* this tx is unconfirmed, gettxout won't ever say if the output is spent
else {
txout, err := pgb.Client.GetTxOut(pgb.ctx, txhash, uint32(i), tree, true)
if err != nil {
log.Warnf("Failed to determine if tx out is spent for output %d of tx %s: %v", i, txid, err)
}
spent = txout == nil
}
*/

spk := &vout.ScriptPubKey
var opReturn string
var opTAdd bool
if strings.HasPrefix(vout.ScriptPubKey.Asm, "OP_RETURN") {
opReturn = vout.ScriptPubKey.Asm
if strings.HasPrefix(spk.Asm, "OP_RETURN") {
opReturn = spk.Asm
} else {
opTAdd = strings.HasPrefix(vout.ScriptPubKey.Asm, "OP_TADD")
opTAdd = strings.HasPrefix(spk.Asm, "OP_TADD")
}
// Get a consistent script class string from dbtypes.ScriptClass.
pkScript, version := msgTx.TxOut[i].PkScript, msgTx.TxOut[i].Version
scriptClass := dbtypes.NewScriptClass(stdscript.DetermineScriptType(version, pkScript))
if scriptClass == dbtypes.SCNullData && vout.ScriptPubKey.CommitAmt != nil {
if scriptClass == dbtypes.SCNullData && spk.CommitAmt != nil {
scriptClass = dbtypes.SCStakeSubCommit
}
outputs = append(outputs, exptypes.Vout{
Addresses: vout.ScriptPubKey.Addresses,
Addresses: spk.Addresses,
Amount: vout.Value,
FormattedAmount: humanize.Commaf(vout.Value),
OP_RETURN: opReturn,
OP_TADD: opTAdd,
Type: scriptClass.String(),
Spent: txout == nil,
Spent: spent,
Index: vout.N,
Version: version,
})
Expand Down
1 change: 0 additions & 1 deletion gov/politeia/proposals.go
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,6 @@ func (db *ProposalsDB) proposal(searchBy, searchTerm string) (*pitypes.ProposalR
var proposal pitypes.ProposalRecord
err := db.dbP.Select(q.Eq(searchBy, searchTerm)).Limit(1).First(&proposal)
if err != nil {
log.Errorf("Failed to fetch data from Proposals DB: %v", err)
return nil, err
}

Expand Down

0 comments on commit 1fb3106

Please sign in to comment.