Limit number of found transactions for address by page size
This commit is contained in:
parent
2c3af5129e
commit
00c1fd6661
@ -9,6 +9,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const maxInt = int(^uint(0) >> 1)
|
||||||
|
|
||||||
// GetAddressOption specifies what data returns GetAddress api call
|
// GetAddressOption specifies what data returns GetAddress api call
|
||||||
type GetAddressOption int
|
type GetAddressOption int
|
||||||
|
|
||||||
|
|||||||
@ -300,13 +300,16 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height uint32,
|
|||||||
return r, nil
|
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
|
var err error
|
||||||
txids := make([]string, 0, 4)
|
txids := make([]string, 0, 4)
|
||||||
var callback db.GetTransactionsCallback
|
var callback db.GetTransactionsCallback
|
||||||
if filter.Vout == AddressFilterVoutOff {
|
if filter.Vout == AddressFilterVoutOff {
|
||||||
callback = func(txid string, height uint32, indexes []int32) error {
|
callback = func(txid string, height uint32, indexes []int32) error {
|
||||||
txids = append(txids, txid)
|
txids = append(txids, txid)
|
||||||
|
if len(txids) >= maxResults {
|
||||||
|
return &db.StopIteration{}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -320,6 +323,9 @@ func (w *Worker) getAddressTxids(addrDesc bchain.AddressDescriptor, mempool bool
|
|||||||
(filter.Vout == AddressFilterVoutOutputs && index >= 0) ||
|
(filter.Vout == AddressFilterVoutOutputs && index >= 0) ||
|
||||||
(vout == int32(filter.Vout)) {
|
(vout == int32(filter.Vout)) {
|
||||||
txids = append(txids, txid)
|
txids = append(txids, txid)
|
||||||
|
if len(txids) >= maxResults {
|
||||||
|
return &db.StopIteration{}
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -460,16 +466,19 @@ func computePaging(count, page, itemsOnPage int) (Paging, int, int, int) {
|
|||||||
}, from, to, page
|
}, 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 (
|
var (
|
||||||
ba *db.AddrBalance
|
ba *db.AddrBalance
|
||||||
tokens []Token
|
tokens []Token
|
||||||
ci *bchain.Erc20Contract
|
ci *bchain.Erc20Contract
|
||||||
n uint64
|
n uint64
|
||||||
|
nonContractTxs int
|
||||||
)
|
)
|
||||||
|
// unknown number of results for paging
|
||||||
|
totalResults := -1
|
||||||
ca, err := w.db.GetAddrDescContracts(addrDesc)
|
ca, err := w.db.GetAddrDescContracts(addrDesc)
|
||||||
if err != nil {
|
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 {
|
if ca != nil {
|
||||||
ba = &db.AddrBalance{
|
ba = &db.AddrBalance{
|
||||||
@ -478,20 +487,20 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
|
|||||||
var b *big.Int
|
var b *big.Int
|
||||||
b, err = w.chain.EthereumTypeGetBalance(addrDesc)
|
b, err = w.chain.EthereumTypeGetBalance(addrDesc)
|
||||||
if err != nil {
|
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 {
|
if b != nil {
|
||||||
ba.BalanceSat = *b
|
ba.BalanceSat = *b
|
||||||
}
|
}
|
||||||
n, err = w.chain.EthereumTypeGetNonce(addrDesc)
|
n, err = w.chain.EthereumTypeGetNonce(addrDesc)
|
||||||
if err != nil {
|
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
|
var filterDesc bchain.AddressDescriptor
|
||||||
if filter.Contract != "" {
|
if filter.Contract != "" {
|
||||||
filterDesc, err = w.chainParser.GetAddrDescFromAddress(filter.Contract)
|
filterDesc, err = w.chainParser.GetAddrDescFromAddress(filter.Contract)
|
||||||
if err != nil {
|
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))
|
tokens = make([]Token, len(ca.Contracts))
|
||||||
@ -501,12 +510,12 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
|
|||||||
if !bytes.Equal(filterDesc, c.Contract) {
|
if !bytes.Equal(filterDesc, c.Contract) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// filter only transactions by this contract
|
// filter only transactions of this contract
|
||||||
filter.Vout = i + 1
|
filter.Vout = i + 1
|
||||||
}
|
}
|
||||||
ci, err := w.chain.EthereumTypeGetErc20ContractInfo(c.Contract)
|
ci, err := w.chain.EthereumTypeGetErc20ContractInfo(c.Contract)
|
||||||
if err != nil {
|
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 {
|
if ci == nil {
|
||||||
ci = &bchain.Erc20Contract{}
|
ci = &bchain.Erc20Contract{}
|
||||||
@ -541,10 +550,21 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
|
|||||||
tokens = tokens[:j]
|
tokens = tokens[:j]
|
||||||
ci, err = w.chain.EthereumTypeGetErc20ContractInfo(addrDesc)
|
ci, err = w.chain.EthereumTypeGetErc20ContractInfo(addrDesc)
|
||||||
if err != nil {
|
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
|
// 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
|
totalReceived, totalSent *big.Int
|
||||||
nonce string
|
nonce string
|
||||||
nonTokenTxs int
|
nonTokenTxs int
|
||||||
|
totalResults int
|
||||||
)
|
)
|
||||||
if w.chainType == bchain.ChainEthereumType {
|
if w.chainType == bchain.ChainEthereumType {
|
||||||
var n uint64
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -584,6 +605,14 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewAPIError(fmt.Sprintf("Address not found, %v", err), true)
|
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
|
// get tx history if requested by option or check mempool if there are some transactions for a new address
|
||||||
if option >= TxidHistory || ba == nil {
|
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 {
|
if len(addresses) == 1 {
|
||||||
address = addresses[0]
|
address = addresses[0]
|
||||||
}
|
}
|
||||||
txm, err = w.getAddressTxids(addrDesc, true, filter)
|
// get txs from mempool only if blockheight filter is off
|
||||||
if err != nil {
|
if filter.FromHeight == 0 && filter.ToHeight == 0 {
|
||||||
return nil, errors.Annotatef(err, "getAddressTxids %v true", addrDesc)
|
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 there are only unconfirmed transactions, there is no paging
|
||||||
if ba == nil {
|
if ba == nil {
|
||||||
@ -605,7 +637,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
|
|||||||
page = 0
|
page = 0
|
||||||
}
|
}
|
||||||
if option >= TxidHistory {
|
if option >= TxidHistory {
|
||||||
txc, err := w.getAddressTxids(addrDesc, false, filter)
|
txc, err := w.getAddressTxids(addrDesc, false, filter, (page+1)*txsOnPage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Annotatef(err, "getAddressTxids %v false", addrDesc)
|
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
|
var from, to int
|
||||||
pg, from, to, page = computePaging(len(txc), page, txsOnPage)
|
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 {
|
if option == TxidHistory {
|
||||||
txids = make([]string, len(txm)+to-from)
|
txids = make([]string, len(txm)+to-from)
|
||||||
} else {
|
} else {
|
||||||
@ -654,7 +693,8 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
|
|||||||
}
|
}
|
||||||
if ta == nil {
|
if ta == nil {
|
||||||
glog.Warning("DB inconsistency: tx ", txid, ": not found in txAddresses")
|
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)
|
bi, err := w.db.GetBlockInfo(ta.Height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -662,7 +702,8 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
|
|||||||
}
|
}
|
||||||
if bi == nil {
|
if bi == nil {
|
||||||
glog.Warning("DB inconsistency: block height ", ta.Height, ": not found in db")
|
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)
|
txs[txi] = w.txFromTxAddress(txid, ta, bi, bestheight)
|
||||||
} else {
|
} else {
|
||||||
@ -716,7 +757,7 @@ func (w *Worker) GetAddressUtxo(address string, onlyConfirmed bool) ([]AddressUt
|
|||||||
r := make([]AddressUtxo, 0, 8)
|
r := make([]AddressUtxo, 0, 8)
|
||||||
if !onlyConfirmed {
|
if !onlyConfirmed {
|
||||||
// get utxo from mempool
|
// 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 {
|
if err != nil {
|
||||||
return nil, errors.Annotatef(err, "getAddressTxids %v true", address)
|
return nil, errors.Annotatef(err, "getAddressTxids %v true", address)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -640,55 +640,59 @@ func (s *PublicServer) explorerSendTx(w http.ResponseWriter, r *http.Request) (t
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getPagingRange(page int, total int) ([]int, int, int) {
|
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
|
return nil, 0, 0
|
||||||
}
|
}
|
||||||
|
var r []int
|
||||||
pp, np := page-1, page+1
|
pp, np := page-1, page+1
|
||||||
if np > total {
|
|
||||||
np = total
|
|
||||||
}
|
|
||||||
if pp < 1 {
|
if pp < 1 {
|
||||||
pp = 1
|
pp = 1
|
||||||
}
|
}
|
||||||
r := make([]int, 0, 8)
|
if total > 0 {
|
||||||
if total < 6 {
|
if np > total {
|
||||||
for i := 1; i <= total; i++ {
|
np = total
|
||||||
r = append(r, i)
|
|
||||||
}
|
}
|
||||||
} else {
|
r = make([]int, 0, 8)
|
||||||
r = append(r, 1)
|
if total < 6 {
|
||||||
if page > 3 {
|
for i := 1; i <= total; i++ {
|
||||||
r = append(r, 0)
|
r = append(r, i)
|
||||||
}
|
|
||||||
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 {
|
} else {
|
||||||
r = append(r, pp)
|
r = append(r, 1)
|
||||||
r = append(r, page)
|
if page > 3 {
|
||||||
r = append(r, np)
|
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
|
return r, pp, np
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
{{- define "paging"}}{{$data := . -}}{{if $data.PagingRange -}}
|
{{- define "paging"}}{{$data := .}}{{if $data.PrevPage -}}
|
||||||
<ul class="pagination justify-content-end">
|
<ul class="pagination justify-content-end">
|
||||||
<li class="page-item"><a class="page-link" href="?page={{$data.PrevPage}}{{$data.PageParams}}"><</a></li>
|
<li class="page-item"><a class="page-link" href="?page={{$data.PrevPage}}{{$data.PageParams}}"><</a></li>
|
||||||
{{- range $p := $data.PagingRange -}}
|
{{- range $p := $data.PagingRange -}}
|
||||||
<li class="page-item{{if eq $data.Page $p}} active{{end}}">
|
<li class="page-item{{if eq $data.Page $p}} active{{end}}">
|
||||||
{{- if $p}}<a class="page-link" href="?page={{$p}}{{$data.PageParams}}">{{$p}}</a>
|
{{- 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>{{- end -}}
|
||||||
<li class="page-item"><a class="page-link" href="?page={{$data.NextPage}}{{$data.PageParams}}">></a></li>
|
<li class="page-item"><a class="page-link" href="?page={{$data.NextPage}}{{$data.PageParams}}">></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
{{- end -}}{{- end -}}
|
{{- end}}{{end -}}
|
||||||
Loading…
Reference in New Issue
Block a user