Skip to content

Commit

Permalink
insight: DcrToInsightTxns can RPC for prevout addresses
Browse files Browse the repository at this point in the history
For non-coinbase/stakebase inputs, the previous outpoint itself may be
in mempool, in which case it is necessary to retrieve the inputs' txns
and decode the pkScript.

Do not compute fee for coinbase, but do for votes.
  • Loading branch information
chappjc committed Jul 18, 2019
1 parent 537a24d commit 63f9080
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 47 deletions.
85 changes: 45 additions & 40 deletions api/insight/converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/decred/dcrd/dcrjson/v2"
"github.com/decred/dcrd/dcrutil"
apitypes "github.com/decred/dcrdata/api/types/v3"
"github.com/decred/dcrdata/txhelpers/v2"
)

// TxConverter converts dcrd-tx to insight tx
Expand Down Expand Up @@ -36,17 +37,25 @@ func (iapi *InsightApi) DcrToInsightTxns(txs []*dcrjson.TxRawResult, noAsm, noSc
}

// Vins
var vInSum float64
var vInSum dcrutil.Amount
for vinID, vin := range tx.Vin {
amt, _ := dcrutil.NewAmount(vin.AmountIn)
vInSum += amt

InsightVin := &apitypes.InsightVin{
Txid: vin.Txid,
Vout: newUint32Ptr(vin.Vout),
Sequence: newUint32Ptr(vin.Sequence),
N: vinID,
Value: vin.AmountIn,
ValueSat: int64(amt),
CoinBase: vin.Coinbase,
}

// Identify a vin corresponding to generated coins.
vinGenerated := vin.IsCoinBase() || vin.IsStakeBase()
txNew.IsCoinBase = txNew.IsCoinBase || vin.IsCoinBase() // exclude stakebase

// init ScriptPubKey
if !noScriptSig {
InsightVin.ScriptSig = new(apitypes.InsightScriptSig)
Expand All @@ -58,30 +67,32 @@ func (iapi *InsightApi) DcrToInsightTxns(txs []*dcrjson.TxRawResult, noAsm, noSc
}
}

// Note: this only gathers information from the database, which does
// not include mempool transactions.
_, addresses, value, err := iapi.BlockData.ChainDB.AddressIDsByOutpoint(vin.Txid, vin.Vout)
if err == nil {
if len(addresses) > 0 {
// Update Vin due to DCRD AMOUNTIN - START
// NOTE THIS IS ONLY USEFUL FOR INPUT AMOUNTS THAT ARE NOT ALSO FROM MEMPOOL
if tx.Confirmations == 0 {
InsightVin.Value = dcrutil.Amount(value).ToCoin()
}
// Update Vin due to DCRD AMOUNTIN - END
// First, attempt to get input addresses from our DB, which should
// work if the funding transaction is confirmed. Otherwise use RPC
// to get the funding transaction outpoint addresses.
if !vinGenerated {
_, addresses, _, err := iapi.BlockData.ChainDB.AddressIDsByOutpoint(vin.Txid, vin.Vout)
if err == nil && len(addresses) > 0 {
InsightVin.Addr = addresses[0]
} else {
// If the previous outpoint is from an unconfirmed transaction,
// fetch the prevout's addresses since dcrd does not include
// these details in the info for the spending transaction.
addrs, _, err := txhelpers.OutPointAddressesFromString(
vin.Txid, vin.Vout, vin.Tree, iapi.nodeClient, iapi.params)
if err != nil {
apiLog.Errorf("OutPointAddresses: %v", err)
} else if len(addrs) > 0 {
InsightVin.Addr = addrs[0]
}
}
}
dcramt, _ := dcrutil.NewAmount(InsightVin.Value)
InsightVin.ValueSat = int64(dcramt)

vInSum += InsightVin.Value
txNew.Vins = append(txNew.Vins, InsightVin)

}

// Vouts
var vOutSum float64
var vOutSum dcrutil.Amount
for _, v := range tx.Vout {
InsightVout := &apitypes.InsightVout{
Value: v.Value,
Expand All @@ -92,32 +103,26 @@ func (iapi *InsightApi) DcrToInsightTxns(txs []*dcrjson.TxRawResult, noAsm, noSc
Hex: v.ScriptPubKey.Hex,
},
}

if !noAsm {
InsightVout.ScriptPubKey.Asm = v.ScriptPubKey.Asm
}

amt, _ := dcrutil.NewAmount(v.Value)
vOutSum += amt

txNew.Vouts = append(txNew.Vouts, InsightVout)
vOutSum += v.Value
}

dcramt, _ := dcrutil.NewAmount(vOutSum)
txNew.ValueOut = dcramt.ToCoin()

dcramt, _ = dcrutil.NewAmount(vInSum)
txNew.ValueIn = dcramt.ToCoin()

dcramt, _ = dcrutil.NewAmount(txNew.ValueIn - txNew.ValueOut)
txNew.Fees = dcramt.ToCoin()

// Return true if coinbase value is not empty, return 0 at some fields.
if txNew.Vins != nil && txNew.Vins[0].CoinBase != "" {
txNew.IsCoinBase = true
txNew.ValueIn = 0
txNew.Fees = 0
for _, v := range txNew.Vins {
v.Value = 0
v.ValueSat = 0
}
txNew.ValueIn = vInSum.ToCoin()
txNew.ValueOut = vOutSum.ToCoin()
// A coinbase transaction, but not necessarily a stakebase/vote, has no
// fee. Further, a coinbase transaction collects all of the mining fees
// for the transactions in the block, but there is no input that
// accounts for these collected fees. As such, never attempt to compute
// a transaction fee for the block's coinbase transaction.
if !txNew.IsCoinBase {
txNew.Fees = (vInSum - vOutSum).ToCoin()
}

if !noSpent {
Expand All @@ -128,10 +133,10 @@ func (iapi *InsightApi) DcrToInsightTxns(txs []*dcrjson.TxRawResult, noAsm, noSc
if err != nil {
return nil, err
}
for _, dbaddr := range addrFull {
txNew.Vouts[dbaddr.FundingTxVoutIndex].SpentIndex = dbaddr.SpendingTxVinIndex
txNew.Vouts[dbaddr.FundingTxVoutIndex].SpentTxID = dbaddr.SpendingTxHash
txNew.Vouts[dbaddr.FundingTxVoutIndex].SpentHeight = dbaddr.BlockHeight
for _, dbAddr := range addrFull {
txNew.Vouts[dbAddr.FundingTxVoutIndex].SpentIndex = dbAddr.SpendingTxVinIndex
txNew.Vouts[dbAddr.FundingTxVoutIndex].SpentTxID = dbAddr.SpendingTxHash
txNew.Vouts[dbAddr.FundingTxVoutIndex].SpentHeight = dbAddr.BlockHeight
}
}
newTxs = append(newTxs, txNew)
Expand Down
5 changes: 2 additions & 3 deletions db/dcrpg/insightapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,13 @@ func (pgb *ChainDB) InsightAddressTransactions(addr []string, recentBlockHeight
}

// AddressIDsByOutpoint fetches all address row IDs for a given outpoint
// (txHash:voutIndex). TODO: Update the vin due to the issue with amountin
// invalid for unconfirmed txns.
// (txHash:voutIndex).
func (pgb *ChainDB) AddressIDsByOutpoint(txHash string, voutIndex uint32) ([]uint64, []string, int64, error) {
ctx, cancel := context.WithTimeout(pgb.ctx, pgb.queryTimeout)
defer cancel()
ids, addrs, val, err := RetrieveAddressIDsByOutpoint(ctx, pgb.db, txHash, voutIndex)
return ids, addrs, val, pgb.replaceCancelError(err)
} // Update Vin due to DCRD AMOUNTIN - END
}

// InsightSearchRPCAddressTransactions performs a searchrawtransactions for the
// specified address, max number of transactions, and offset into the transaction
Expand Down
7 changes: 3 additions & 4 deletions db/dcrpg/queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -2024,9 +2024,8 @@ func scanAddressQueryRows(rows *sql.Rows, queryType int) (addressRows []*dbtypes
return
}

// RetrieveAddressIDsByOutpoint fetches all address row IDs for a given outpoint
// (hash:index).
// Update Vin due to DCRD AMOUNTIN - START - DO NOT MERGE CHANGES IF DCRD FIXED
// RetrieveAddressIDsByOutpoint gets all address row IDs, addresses, and values
// for a given outpoint.
func RetrieveAddressIDsByOutpoint(ctx context.Context, db *sql.DB, txHash string, voutIndex uint32) ([]uint64, []string, int64, error) {
var ids []uint64
var addresses []string
Expand All @@ -2049,7 +2048,7 @@ func RetrieveAddressIDsByOutpoint(ctx context.Context, db *sql.DB, txHash string
addresses = append(addresses, addr)
}
return ids, addresses, value, err
} // Update Vin due to DCRD AMOUNTIN - END
}

// retrieveOldestTxBlockTime helps choose the most appropriate address page
// graph grouping to load by default depending on when the first transaction to
Expand Down

0 comments on commit 63f9080

Please sign in to comment.