Add get balance history for xpub
parent
62208b9634
commit
c913a022ef
|
@ -801,14 +801,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
|
|||
return r, 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
|
||||
}
|
||||
func (w *Worker) balanceHistoryHeightsFromTo(fromTime, toTime time.Time) (uint32, uint32, uint32, uint32) {
|
||||
fromUnix := uint32(0)
|
||||
toUnix := maxUint32
|
||||
fromHeight := uint32(0)
|
||||
|
@ -821,6 +814,53 @@ func (w *Worker) GetBalanceHistory(address string, fromTime, toTime time.Time) (
|
|||
toUnix = uint32(toTime.Unix())
|
||||
toHeight = w.is.GetBlockHeightOfTime(toUnix)
|
||||
}
|
||||
return fromUnix, fromHeight, toUnix, toHeight
|
||||
}
|
||||
|
||||
func (w *Worker) balanceHistoryForTxid(addrDesc bchain.AddressDescriptor, txid string, fromUnix, toUnix uint32) (*BalanceHistory, error) {
|
||||
ta, err := w.db.GetTxAddresses(txid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ta == nil {
|
||||
glog.Warning("DB inconsistency: tx ", txid, ": not found in txAddresses")
|
||||
return nil, nil
|
||||
}
|
||||
time := w.is.GetBlockTime(ta.Height)
|
||||
if time < fromUnix || time >= toUnix {
|
||||
return nil, nil
|
||||
}
|
||||
bh := BalanceHistory{
|
||||
Time: time,
|
||||
Txs: 1,
|
||||
SentSat: &Amount{},
|
||||
ReceivedSat: &Amount{},
|
||||
Txid: txid,
|
||||
}
|
||||
for i := range ta.Inputs {
|
||||
tai := &ta.Inputs[i]
|
||||
if bytes.Equal(addrDesc, tai.AddrDesc) {
|
||||
(*big.Int)(bh.SentSat).Add((*big.Int)(bh.SentSat), &tai.ValueSat)
|
||||
}
|
||||
}
|
||||
for i := range ta.Outputs {
|
||||
tao := &ta.Outputs[i]
|
||||
if bytes.Equal(addrDesc, tao.AddrDesc) {
|
||||
(*big.Int)(bh.ReceivedSat).Add((*big.Int)(bh.ReceivedSat), &tao.ValueSat)
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
@ -829,41 +869,16 @@ func (w *Worker) GetBalanceHistory(address string, fromTime, toTime time.Time) (
|
|||
return nil, err
|
||||
}
|
||||
for txi := len(txs) - 1; txi >= 0; txi-- {
|
||||
ta, err := w.db.GetTxAddresses(txs[txi])
|
||||
bh, err := w.balanceHistoryForTxid(addrDesc, txs[txi], fromUnix, toUnix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ta == nil {
|
||||
glog.Warning("DB inconsistency: tx ", txs[txi], ": not found in txAddresses")
|
||||
continue
|
||||
if bh != nil {
|
||||
bhs = append(bhs, *bh)
|
||||
}
|
||||
time := w.is.GetBlockTime(ta.Height)
|
||||
if time < fromUnix || time >= toUnix {
|
||||
continue
|
||||
}
|
||||
bh := BalanceHistory{
|
||||
Time: time,
|
||||
Txs: 1,
|
||||
SentSat: &Amount{},
|
||||
ReceivedSat: &Amount{},
|
||||
Txid: txs[txi],
|
||||
}
|
||||
for i := range ta.Inputs {
|
||||
tai := &ta.Inputs[i]
|
||||
if bytes.Equal(addrDesc, tai.AddrDesc) {
|
||||
(*big.Int)(bh.SentSat).Add((*big.Int)(bh.SentSat), &tai.ValueSat)
|
||||
}
|
||||
}
|
||||
for i := range ta.Outputs {
|
||||
tao := &ta.Outputs[i]
|
||||
if bytes.Equal(addrDesc, tao.AddrDesc) {
|
||||
(*big.Int)(bh.ReceivedSat).Add((*big.Int)(bh.ReceivedSat), &tao.ValueSat)
|
||||
}
|
||||
}
|
||||
bhs = append(bhs, bh)
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
|
|
36
api/xpub.go
36
api/xpub.go
|
@ -591,6 +591,38 @@ func (w *Worker) GetXpubUtxo(xpub string, onlyConfirmed bool, gap int) (Utxos, e
|
|||
}
|
||||
|
||||
// GetUtxoBalanceHistory returns history of balance for given xpub
|
||||
func (w *Worker) GetUtxoBalanceHistory(xpub string, gap int) ([]BalanceHistory, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
func (w *Worker) GetUtxoBalanceHistory(xpub string, fromTime, toTime time.Time, gap int) (BalanceHistories, error) {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1051,7 +1051,7 @@ func (s *PublicServer) apiBalanceHistory(r *http.Request, apiVersion int) (inter
|
|||
// time.RFC3339
|
||||
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 {
|
||||
s.metrics.ExplorerViews.With(common.Labels{"action": "api-xpub-balancehistory"}).Inc()
|
||||
} else {
|
||||
|
|
|
@ -636,6 +636,33 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
|
|||
`[{"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",
|
||||
r: newGetRequest(ts.URL + "/api/v2/sendtx/1234567890"),
|
||||
|
|
Loading…
Reference in New Issue