Add get balance history for xpub

This commit is contained in:
Martin Boehm 2019-12-03 16:02:20 +01:00
parent 62208b9634
commit c913a022ef
4 changed files with 114 additions and 40 deletions

View File

@ -801,14 +801,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
return r, nil return r, nil
} }
// GetBalanceHistory returns history of balance for given address func (w *Worker) balanceHistoryHeightsFromTo(fromTime, toTime time.Time) (uint32, uint32, uint32, uint32) {
func (w *Worker) GetBalanceHistory(address string, fromTime, toTime time.Time) (BalanceHistories, error) {
bhs := make(BalanceHistories, 0)
start := time.Now()
addrDesc, _, err := w.getAddrDescAndNormalizeAddress(address)
if err != nil {
return nil, err
}
fromUnix := uint32(0) fromUnix := uint32(0)
toUnix := maxUint32 toUnix := maxUint32
fromHeight := uint32(0) fromHeight := uint32(0)
@ -821,32 +814,28 @@ func (w *Worker) GetBalanceHistory(address string, fromTime, toTime time.Time) (
toUnix = uint32(toTime.Unix()) toUnix = uint32(toTime.Unix())
toHeight = w.is.GetBlockHeightOfTime(toUnix) toHeight = w.is.GetBlockHeightOfTime(toUnix)
} }
if fromHeight >= toHeight { return fromUnix, fromHeight, toUnix, toHeight
return bhs, nil }
}
txs, err := w.getAddressTxids(addrDesc, false, &AddressFilter{Vout: AddressFilterVoutOff, FromHeight: fromHeight, ToHeight: toHeight}, maxInt) func (w *Worker) balanceHistoryForTxid(addrDesc bchain.AddressDescriptor, txid string, fromUnix, toUnix uint32) (*BalanceHistory, error) {
if err != nil { ta, err := w.db.GetTxAddresses(txid)
return nil, err
}
for txi := len(txs) - 1; txi >= 0; txi-- {
ta, err := w.db.GetTxAddresses(txs[txi])
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ta == nil { if ta == nil {
glog.Warning("DB inconsistency: tx ", txs[txi], ": not found in txAddresses") glog.Warning("DB inconsistency: tx ", txid, ": not found in txAddresses")
continue return nil, nil
} }
time := w.is.GetBlockTime(ta.Height) time := w.is.GetBlockTime(ta.Height)
if time < fromUnix || time >= toUnix { if time < fromUnix || time >= toUnix {
continue return nil, nil
} }
bh := BalanceHistory{ bh := BalanceHistory{
Time: time, Time: time,
Txs: 1, Txs: 1,
SentSat: &Amount{}, SentSat: &Amount{},
ReceivedSat: &Amount{}, ReceivedSat: &Amount{},
Txid: txs[txi], Txid: txid,
} }
for i := range ta.Inputs { for i := range ta.Inputs {
tai := &ta.Inputs[i] tai := &ta.Inputs[i]
@ -860,10 +849,36 @@ func (w *Worker) GetBalanceHistory(address string, fromTime, toTime time.Time) (
(*big.Int)(bh.ReceivedSat).Add((*big.Int)(bh.ReceivedSat), &tao.ValueSat) (*big.Int)(bh.ReceivedSat).Add((*big.Int)(bh.ReceivedSat), &tao.ValueSat)
} }
} }
bhs = append(bhs, bh) return &bh, nil
}
// GetBalanceHistory returns history of balance for given address
func (w *Worker) GetBalanceHistory(address string, fromTime, toTime time.Time) (BalanceHistories, error) {
bhs := make(BalanceHistories, 0)
start := time.Now()
addrDesc, _, err := w.getAddrDescAndNormalizeAddress(address)
if err != nil {
return nil, err
}
fromUnix, fromHeight, toUnix, toHeight := w.balanceHistoryHeightsFromTo(fromTime, toTime)
if fromHeight >= toHeight {
return bhs, nil
}
txs, err := w.getAddressTxids(addrDesc, false, &AddressFilter{Vout: AddressFilterVoutOff, FromHeight: fromHeight, ToHeight: toHeight}, maxInt)
if err != nil {
return nil, err
}
for txi := len(txs) - 1; txi >= 0; txi-- {
bh, err := w.balanceHistoryForTxid(addrDesc, txs[txi], fromUnix, toUnix)
if err != nil {
return nil, err
}
if bh != nil {
bhs = append(bhs, *bh)
}
} }
bha := bhs.SortAndAggregate(3600) bha := bhs.SortAndAggregate(3600)
glog.Info("GetBalanceHistory ", address, ", count ", len(bha), " finished in ", time.Since(start)) glog.Info("GetBalanceHistory ", address, ", blocks ", fromHeight, "-", toHeight, ", count ", len(bha), " finished in ", time.Since(start))
return bha, nil return bha, nil
} }

View File

@ -591,6 +591,38 @@ func (w *Worker) GetXpubUtxo(xpub string, onlyConfirmed bool, gap int) (Utxos, e
} }
// GetUtxoBalanceHistory returns history of balance for given xpub // GetUtxoBalanceHistory returns history of balance for given xpub
func (w *Worker) GetUtxoBalanceHistory(xpub string, gap int) ([]BalanceHistory, error) { func (w *Worker) GetUtxoBalanceHistory(xpub string, fromTime, toTime time.Time, gap int) (BalanceHistories, error) {
return nil, errors.New("not implemented") bhs := make(BalanceHistories, 0)
start := time.Now()
fromUnix, fromHeight, toUnix, toHeight := w.balanceHistoryHeightsFromTo(fromTime, toTime)
if fromHeight >= toHeight {
return bhs, nil
}
data, _, err := w.getXpubData(xpub, 0, 1, AccountDetailsTxidHistory, &AddressFilter{
Vout: AddressFilterVoutOff,
OnlyConfirmed: true,
FromHeight: fromHeight,
ToHeight: toHeight,
}, gap)
if err != nil {
return nil, err
}
for _, da := range [][]xpubAddress{data.addresses, data.changeAddresses} {
for i := range da {
ad := &da[i]
txids := ad.txids
for txi := len(txids) - 1; txi >= 0; txi-- {
bh, err := w.balanceHistoryForTxid(ad.addrDesc, txids[txi].txid, fromUnix, toUnix)
if err != nil {
return nil, err
}
if bh != nil {
bhs = append(bhs, *bh)
}
}
}
}
bha := bhs.SortAndAggregate(3600)
glog.Info("GetUtxoBalanceHistory ", xpub[:16], ", blocks ", fromHeight, "-", toHeight, ", count ", len(bha), ", finished in ", time.Since(start))
return bha, nil
} }

View File

@ -1051,7 +1051,7 @@ func (s *PublicServer) apiBalanceHistory(r *http.Request, apiVersion int) (inter
// time.RFC3339 // time.RFC3339
toTime, _ = time.Parse("2006-01-02", t) toTime, _ = time.Parse("2006-01-02", t)
} }
history, err = s.api.GetUtxoBalanceHistory(r.URL.Path[i+1:], gap) history, err = s.api.GetUtxoBalanceHistory(r.URL.Path[i+1:], fromTime, toTime, gap)
if err == nil { if err == nil {
s.metrics.ExplorerViews.With(common.Labels{"action": "api-xpub-balancehistory"}).Inc() s.metrics.ExplorerViews.With(common.Labels{"action": "api-xpub-balancehistory"}).Inc()
} else { } else {

View File

@ -636,6 +636,33 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
`[{"blockTime":1521514800,"txs":1,"received":"12345","sent":"0"}]`, `[{"blockTime":1521514800,"txs":1,"received":"12345","sent":"0"}]`,
}, },
}, },
{
name: "apiBalanceHistory xpub v2",
r: newGetRequest(ts.URL + "/api/v2/balancehistory/" + dbtestdata.Xpub),
status: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: []string{
`[{"blockTime":1521514800,"txs":1,"received":"1","sent":"0"},{"blockTime":1521594000,"txs":1,"received":"118641975500","sent":"1"}]`,
},
},
{
name: "apiBalanceHistory xpub v2 from=2018-03-20&to=2018-03-21",
r: newGetRequest(ts.URL + "/api/v2/balancehistory/" + dbtestdata.Xpub + "?from=2018-03-20&to=2018-03-21"),
status: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: []string{
`[{"blockTime":1521514800,"txs":1,"received":"1","sent":"0"}]`,
},
},
{
name: "apiBalanceHistory xpub v2 from=2018-03-21",
r: newGetRequest(ts.URL + "/api/v2/balancehistory/" + dbtestdata.Xpub + "?from=2018-03-21"),
status: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: []string{
`[{"blockTime":1521594000,"txs":1,"received":"118641975500","sent":"1"}]`,
},
},
{ {
name: "apiSendTx", name: "apiSendTx",
r: newGetRequest(ts.URL + "/api/v2/sendtx/1234567890"), r: newGetRequest(ts.URL + "/api/v2/sendtx/1234567890"),