diff --git a/api/types.go b/api/types.go index 287e04af..be289bd6 100644 --- a/api/types.go +++ b/api/types.go @@ -61,4 +61,7 @@ type Address struct { UnconfirmedTxApperances int `json:"unconfirmedTxApperances"` TxApperances int `json:"txApperances"` Transactions []*Tx `json:"transactions"` + Page int `json:"page"` + TotalPages int `json:"totalPages"` + TxsOnPage int `json:"txsOnPage"` } diff --git a/api/worker.go b/api/worker.go index 01064560..64a93bff 100644 --- a/api/worker.go +++ b/api/worker.go @@ -4,13 +4,12 @@ import ( "blockbook/bchain" "blockbook/common" "blockbook/db" + "errors" "math/big" "github.com/golang/glog" ) -const txsOnPage = 30 - // Worker is handle to api worker type Worker struct { db *db.RocksDB @@ -172,7 +171,83 @@ func UniqueTxidsInReverse(txids []string) []string { } // GetAddress computes address value and gets transactions for given address -func (w *Worker) GetAddress(addrID string, page int) (*Address, error) { +func (w *Worker) GetAddressNew(address string, page int, txsOnPage int) (*Address, error) { + glog.Info(address, " start") + ba, err := w.db.GetAddressBalance(address) + if err != nil { + return nil, err + } + if ba == nil { + return nil, errors.New("Address not found") + } + txc, err := w.getAddressTxids(address, false) + txc = UniqueTxidsInReverse(txc) + if err != nil { + return nil, err + } + txm, err := w.getAddressTxids(address, true) + if err != nil { + return nil, err + } + txm = UniqueTxidsInReverse(txm) + bestheight, _, err := w.db.GetBestBlock() + if err != nil { + return nil, err + } + // paging + if page < 0 { + page = 0 + } + from := page * txsOnPage + totalPages := len(txc) / txsOnPage + if from >= len(txc) { + page = totalPages - 1 + if page < 0 { + page = 0 + } + } + from = page * txsOnPage + to := (page + 1) * txsOnPage + if to > len(txc) { + to = len(txc) + } + txs := make([]*Tx, len(txm)+to-from) + txi := 0 + // load mempool transactions + var uBalSat big.Int + for _, tx := range txm { + tx, err := w.GetTransaction(tx, bestheight, false) + // mempool transaction may fail + if err != nil { + glog.Error("GetTransaction ", tx, ": ", err) + } else { + uBalSat.Sub(tx.getAddrVoutValue(address), tx.getAddrVinValue(address)) + txs[txi] = tx + txi++ + } + } + if len(txc) != int(ba.Txs) { + glog.Warning("DB inconsistency in address ", address, ": number of txs from column addresses ", len(txc), ", from addressBalance ", ba.Txs) + } + + r := &Address{ + AddrStr: address, + Balance: w.chainParser.AmountToDecimalString(&ba.BalanceSat), + TotalReceived: w.chainParser.AmountToDecimalString(ba.ReceivedSat()), + TotalSent: w.chainParser.AmountToDecimalString(&ba.SentSat), + TxApperances: len(txc), + UnconfirmedBalance: w.chainParser.AmountToDecimalString(&uBalSat), + UnconfirmedTxApperances: len(txm), + Page: page, + TotalPages: totalPages, + TxsOnPage: txsOnPage, + } + glog.Info(address, " finished") + return r, nil +} + +// GetAddress computes address value and gets transactions for given address +func (w *Worker) GetAddress(addrID string, page int, txsOnPage int) (*Address, error) { glog.Info(addrID, " start") txc, err := w.getAddressTxids(addrID, false) txc = UniqueTxidsInReverse(txc) diff --git a/db/rocksdb.go b/db/rocksdb.go index 5cd59268..ef3be7db 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -545,10 +545,10 @@ type AddrBalance struct { BalanceSat big.Int } -func (ab *AddrBalance) ReceivedSat() big.Int { +func (ab *AddrBalance) ReceivedSat() *big.Int { var r big.Int r.Add(&ab.BalanceSat, &ab.SentSat) - return r + return &r } type blockTxs struct { diff --git a/server/public.go b/server/public.go index acafd08c..abe67790 100644 --- a/server/public.go +++ b/server/public.go @@ -18,8 +18,10 @@ import ( ) const blockbookAbout = "Blockbook - blockchain indexer for TREZOR wallet https://trezor.io/. Do not use for any other purpose." +const txsOnPage = 30 +const txsInAPI = 1000 -// PublicServer is handle to public http server +// PublicServer is a handle to public http server type PublicServer struct { binding string certFiles string @@ -37,7 +39,7 @@ type PublicServer struct { addressTpl *template.Template } -// NewPublicServerS creates new public server http interface to blockbook and returns its handle +// NewPublicServer creates new public server http interface to blockbook and returns its handle func NewPublicServer(binding string, certFiles string, db *db.RocksDB, chain bchain.BlockChain, txCache *db.TxCache, explorerURL string, metrics *common.Metrics, is *common.InternalState) (*PublicServer, error) { api, err := api.NewWorker(db, chain, txCache, is) @@ -233,9 +235,10 @@ func (s *PublicServer) explorerAddress(w http.ResponseWriter, r *http.Request) { page = 0 } addrID := r.URL.Path[i+1:] - address, err = s.api.GetAddress(addrID, page) + address, err = s.api.GetAddress(addrID, page, txsOnPage) if err != nil { glog.Error(err) + // TODO return error.html } } w.Header().Set("Content-Type", "text/html; charset=utf-8") @@ -351,7 +354,7 @@ func (s *PublicServer) apiAddress(w http.ResponseWriter, r *http.Request) { page = 0 } addrID := r.URL.Path[i+1:] - address, err = s.api.GetAddress(addrID, page) + address, err = s.api.GetAddress(addrID, page, txsInAPI) if err != nil { glog.Error(err) }