Limit number of found transactions for address by page size

This commit is contained in:
Martin Boehm 2019-01-09 11:48:19 +01:00
parent 2c3af5129e
commit 00c1fd6661
4 changed files with 111 additions and 64 deletions

View File

@ -9,6 +9,8 @@ import (
"time"
)
const maxInt = int(^uint(0) >> 1)
// GetAddressOption specifies what data returns GetAddress api call
type GetAddressOption int

View File

@ -300,13 +300,16 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height uint32,
return r, nil
}
func (w *Worker) getAddressTxids(addrDesc bchain.AddressDescriptor, mempool bool, filter *AddressFilter) ([]string, error) {
func (w *Worker) getAddressTxids(addrDesc bchain.AddressDescriptor, mempool bool, filter *AddressFilter, maxResults int) ([]string, error) {
var err error
txids := make([]string, 0, 4)
var callback db.GetTransactionsCallback
if filter.Vout == AddressFilterVoutOff {
callback = func(txid string, height uint32, indexes []int32) error {
txids = append(txids, txid)
if len(txids) >= maxResults {
return &db.StopIteration{}
}
return nil
}
} else {
@ -320,6 +323,9 @@ func (w *Worker) getAddressTxids(addrDesc bchain.AddressDescriptor, mempool bool
(filter.Vout == AddressFilterVoutOutputs && index >= 0) ||
(vout == int32(filter.Vout)) {
txids = append(txids, txid)
if len(txids) >= maxResults {
return &db.StopIteration{}
}
break
}
}
@ -460,16 +466,19 @@ func computePaging(count, page, itemsOnPage int) (Paging, int, int, int) {
}, from, to, page
}
func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, option GetAddressOption, filter *AddressFilter) (*db.AddrBalance, []Token, *bchain.Erc20Contract, uint64, int, error) {
func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, option GetAddressOption, filter *AddressFilter) (*db.AddrBalance, []Token, *bchain.Erc20Contract, uint64, int, int, error) {
var (
ba *db.AddrBalance
tokens []Token
ci *bchain.Erc20Contract
n uint64
ba *db.AddrBalance
tokens []Token
ci *bchain.Erc20Contract
n uint64
nonContractTxs int
)
// unknown number of results for paging
totalResults := -1
ca, err := w.db.GetAddrDescContracts(addrDesc)
if err != nil {
return nil, nil, nil, 0, 0, NewAPIError(fmt.Sprintf("Address not found, %v", err), true)
return nil, nil, nil, 0, 0, 0, NewAPIError(fmt.Sprintf("Address not found, %v", err), true)
}
if ca != nil {
ba = &db.AddrBalance{
@ -478,20 +487,20 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
var b *big.Int
b, err = w.chain.EthereumTypeGetBalance(addrDesc)
if err != nil {
return nil, nil, nil, 0, 0, errors.Annotatef(err, "EthereumTypeGetBalance %v", addrDesc)
return nil, nil, nil, 0, 0, 0, errors.Annotatef(err, "EthereumTypeGetBalance %v", addrDesc)
}
if b != nil {
ba.BalanceSat = *b
}
n, err = w.chain.EthereumTypeGetNonce(addrDesc)
if err != nil {
return nil, nil, nil, 0, 0, errors.Annotatef(err, "EthereumTypeGetNonce %v", addrDesc)
return nil, nil, nil, 0, 0, 0, errors.Annotatef(err, "EthereumTypeGetNonce %v", addrDesc)
}
var filterDesc bchain.AddressDescriptor
if filter.Contract != "" {
filterDesc, err = w.chainParser.GetAddrDescFromAddress(filter.Contract)
if err != nil {
return nil, nil, nil, 0, 0, NewAPIError(fmt.Sprintf("Invalid contract filter, %v", err), true)
return nil, nil, nil, 0, 0, 0, NewAPIError(fmt.Sprintf("Invalid contract filter, %v", err), true)
}
}
tokens = make([]Token, len(ca.Contracts))
@ -501,12 +510,12 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
if !bytes.Equal(filterDesc, c.Contract) {
continue
}
// filter only transactions by this contract
// filter only transactions of this contract
filter.Vout = i + 1
}
ci, err := w.chain.EthereumTypeGetErc20ContractInfo(c.Contract)
if err != nil {
return nil, nil, nil, 0, 0, errors.Annotatef(err, "EthereumTypeGetErc20ContractInfo %v", c.Contract)
return nil, nil, nil, 0, 0, 0, errors.Annotatef(err, "EthereumTypeGetErc20ContractInfo %v", c.Contract)
}
if ci == nil {
ci = &bchain.Erc20Contract{}
@ -541,10 +550,21 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
tokens = tokens[:j]
ci, err = w.chain.EthereumTypeGetErc20ContractInfo(addrDesc)
if err != nil {
return nil, nil, nil, 0, 0, err
return nil, nil, nil, 0, 0, 0, err
}
if filter.FromHeight == 0 && filter.ToHeight == 0 {
// compute total results for paging
if filter.Vout == AddressFilterVoutOff {
totalResults = int(ca.TotalTxs)
} else if filter.Vout == 0 {
totalResults = int(ca.NonContractTxs)
} else if filter.Vout > 0 && filter.Vout-1 < len(ca.Contracts) {
totalResults = int(ca.Contracts[filter.Vout-1].Txs)
}
}
nonContractTxs = int(ca.NonContractTxs)
}
return ba, tokens, ci, n, int(ca.NonContractTxs), nil
return ba, tokens, ci, n, nonContractTxs, totalResults, nil
}
// GetAddress computes address value and gets transactions for given address
@ -570,10 +590,11 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
totalReceived, totalSent *big.Int
nonce string
nonTokenTxs int
totalResults int
)
if w.chainType == bchain.ChainEthereumType {
var n uint64
ba, tokens, erc20c, n, nonTokenTxs, err = w.getEthereumTypeAddressBalances(addrDesc, option, filter)
ba, tokens, erc20c, n, nonTokenTxs, totalResults, err = w.getEthereumTypeAddressBalances(addrDesc, option, filter)
if err != nil {
return nil, err
}
@ -584,6 +605,14 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
if err != nil {
return nil, NewAPIError(fmt.Sprintf("Address not found, %v", err), true)
}
if ba != nil {
// totalResults is known only if there is no filter
if filter.Vout == AddressFilterVoutOff && filter.FromHeight == 0 && filter.ToHeight == 0 {
totalResults = int(ba.Txs)
} else {
totalResults = -1
}
}
}
// get tx history if requested by option or check mempool if there are some transactions for a new address
if option >= TxidHistory || ba == nil {
@ -595,9 +624,12 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
if len(addresses) == 1 {
address = addresses[0]
}
txm, err = w.getAddressTxids(addrDesc, true, filter)
if err != nil {
return nil, errors.Annotatef(err, "getAddressTxids %v true", addrDesc)
// get txs from mempool only if blockheight filter is off
if filter.FromHeight == 0 && filter.ToHeight == 0 {
txm, err = w.getAddressTxids(addrDesc, true, filter, maxInt)
if err != nil {
return nil, errors.Annotatef(err, "getAddressTxids %v true", addrDesc)
}
}
// if there are only unconfirmed transactions, there is no paging
if ba == nil {
@ -605,7 +637,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
page = 0
}
if option >= TxidHistory {
txc, err := w.getAddressTxids(addrDesc, false, filter)
txc, err := w.getAddressTxids(addrDesc, false, filter, (page+1)*txsOnPage)
if err != nil {
return nil, errors.Annotatef(err, "getAddressTxids %v false", addrDesc)
}
@ -615,6 +647,13 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
}
var from, to int
pg, from, to, page = computePaging(len(txc), page, txsOnPage)
if len(txc) >= txsOnPage {
if totalResults < 0 {
pg.TotalPages = -1
} else {
pg, _, _, _ = computePaging(totalResults, page, txsOnPage)
}
}
if option == TxidHistory {
txids = make([]string, len(txm)+to-from)
} else {
@ -654,7 +693,8 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
}
if ta == nil {
glog.Warning("DB inconsistency: tx ", txid, ": not found in txAddresses")
continue
// as fallback, provide empty TxAddresses to return at least something
ta = &db.TxAddresses{}
}
bi, err := w.db.GetBlockInfo(ta.Height)
if err != nil {
@ -662,7 +702,8 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
}
if bi == nil {
glog.Warning("DB inconsistency: block height ", ta.Height, ": not found in db")
continue
// provide empty BlockInfo to return the rest of tx data
bi = &db.BlockInfo{}
}
txs[txi] = w.txFromTxAddress(txid, ta, bi, bestheight)
} else {
@ -716,7 +757,7 @@ func (w *Worker) GetAddressUtxo(address string, onlyConfirmed bool) ([]AddressUt
r := make([]AddressUtxo, 0, 8)
if !onlyConfirmed {
// get utxo from mempool
txm, err := w.getAddressTxids(addrDesc, true, &AddressFilter{Vout: AddressFilterVoutOff})
txm, err := w.getAddressTxids(addrDesc, true, &AddressFilter{Vout: AddressFilterVoutOff}, maxInt)
if err != nil {
return nil, errors.Annotatef(err, "getAddressTxids %v true", address)
}

View File

@ -640,55 +640,59 @@ func (s *PublicServer) explorerSendTx(w http.ResponseWriter, r *http.Request) (t
}
func getPagingRange(page int, total int) ([]int, int, int) {
if total < 2 {
// total==-1 means total is unknown, show only prev/next buttons
if total >= 0 && total < 2 {
return nil, 0, 0
}
var r []int
pp, np := page-1, page+1
if np > total {
np = total
}
if pp < 1 {
pp = 1
}
r := make([]int, 0, 8)
if total < 6 {
for i := 1; i <= total; i++ {
r = append(r, i)
if total > 0 {
if np > total {
np = total
}
} else {
r = append(r, 1)
if page > 3 {
r = append(r, 0)
}
if pp == 1 {
if page == 1 {
r = append(r, np)
r = append(r, np+1)
r = append(r, np+2)
} else {
r = append(r, page)
r = append(r, np)
r = append(r, np+1)
}
} else if np == total {
if page == total {
r = append(r, pp-2)
r = append(r, pp-1)
r = append(r, pp)
} else {
r = append(r, pp-1)
r = append(r, pp)
r = append(r, page)
r = make([]int, 0, 8)
if total < 6 {
for i := 1; i <= total; i++ {
r = append(r, i)
}
} else {
r = append(r, pp)
r = append(r, page)
r = append(r, np)
r = append(r, 1)
if page > 3 {
r = append(r, 0)
}
if pp == 1 {
if page == 1 {
r = append(r, np)
r = append(r, np+1)
r = append(r, np+2)
} else {
r = append(r, page)
r = append(r, np)
r = append(r, np+1)
}
} else if np == total {
if page == total {
r = append(r, pp-2)
r = append(r, pp-1)
r = append(r, pp)
} else {
r = append(r, pp-1)
r = append(r, pp)
r = append(r, page)
}
} else {
r = append(r, pp)
r = append(r, page)
r = append(r, np)
}
if page <= total-3 {
r = append(r, 0)
}
r = append(r, total)
}
if page <= total-3 {
r = append(r, 0)
}
r = append(r, total)
}
return r, pp, np
}

View File

@ -1,11 +1,11 @@
{{- define "paging"}}{{$data := . -}}{{if $data.PagingRange -}}
{{- define "paging"}}{{$data := .}}{{if $data.PrevPage -}}
<ul class="pagination justify-content-end">
<li class="page-item"><a class="page-link" href="?page={{$data.PrevPage}}{{$data.PageParams}}">&lt;</a></li>
{{- range $p := $data.PagingRange -}}
<li class="page-item{{if eq $data.Page $p}} active{{end}}">
{{- if $p}}<a class="page-link" href="?page={{$p}}{{$data.PageParams}}">{{$p}}</a>
{{- else -}}<span class="page-text">...</span>{{- end -}}
{{- else}}<span class="page-text">...</span>{{end -}}
</li>{{- end -}}
<li class="page-item"><a class="page-link" href="?page={{$data.NextPage}}{{$data.PageParams}}">&gt;</a></li>
</ul>
{{- end -}}{{- end -}}
{{- end}}{{end -}}