Add GetBalanceHistory for an address of bitcoin type
parent
262ca3e2e4
commit
6cfb881a04
|
@ -296,6 +296,15 @@ func (a Utxos) Less(i, j int) bool {
|
|||
return hi >= hj
|
||||
}
|
||||
|
||||
// BalanceHistory contains info about one point in time of balance history
|
||||
type BalanceHistory struct {
|
||||
Time int64 `json:"blockTime"`
|
||||
Txs int `json:"txs"`
|
||||
ReceivedSat *Amount `json:"received"`
|
||||
SentSat *Amount `json:"sent"`
|
||||
BalanceSat *Amount `json:"balance"`
|
||||
}
|
||||
|
||||
// Blocks is list of blocks with paging information
|
||||
type Blocks struct {
|
||||
Paging
|
||||
|
|
|
@ -801,6 +801,80 @@ 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) ([]BalanceHistory, error) {
|
||||
var bh []BalanceHistory
|
||||
var b BalanceHistory
|
||||
var bi *db.BlockInfo
|
||||
var balance big.Int
|
||||
start := time.Now()
|
||||
addrDesc, _, err := w.getAddrDescAndNormalizeAddress(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txs, err := w.getAddressTxids(addrDesc, false, &AddressFilter{Vout: AddressFilterVoutOff}, maxInt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for txi := len(txs) - 1; txi >= 0; txi-- {
|
||||
ta, err := w.db.GetTxAddresses(txs[txi])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ta == nil {
|
||||
glog.Warning("DB inconsistency: tx ", txs[txi], ": not found in txAddresses")
|
||||
continue
|
||||
}
|
||||
counted := false
|
||||
if bi == nil || bi.Height != ta.Height {
|
||||
bi, err = w.db.GetBlockInfo(ta.Height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
hour := bi.Time - bi.Time%3600
|
||||
if b.Time != hour {
|
||||
if b.Txs != 0 {
|
||||
b.BalanceSat = (*Amount)(new(big.Int).Set(&balance))
|
||||
bh = append(bh, b)
|
||||
}
|
||||
b = BalanceHistory{
|
||||
Time: hour,
|
||||
SentSat: &Amount{},
|
||||
ReceivedSat: &Amount{},
|
||||
}
|
||||
}
|
||||
for i := range ta.Inputs {
|
||||
tai := &ta.Inputs[i]
|
||||
if bytes.Equal(addrDesc, tai.AddrDesc) {
|
||||
(*big.Int)(b.SentSat).Add((*big.Int)(b.SentSat), &tai.ValueSat)
|
||||
balance.Sub(&balance, &tai.ValueSat)
|
||||
if !counted {
|
||||
counted = true
|
||||
b.Txs++
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := range ta.Outputs {
|
||||
tao := &ta.Outputs[i]
|
||||
if bytes.Equal(addrDesc, tao.AddrDesc) {
|
||||
(*big.Int)(b.ReceivedSat).Add((*big.Int)(b.ReceivedSat), &tao.ValueSat)
|
||||
balance.Add(&balance, &tao.ValueSat)
|
||||
if !counted {
|
||||
counted = true
|
||||
b.Txs++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if b.Txs != 0 {
|
||||
b.BalanceSat = (*Amount)(&balance)
|
||||
bh = append(bh, b)
|
||||
}
|
||||
glog.Info("GetBalanceHistory ", address, ", count ", len(bh), " finished in ", time.Since(start))
|
||||
return bh, nil
|
||||
}
|
||||
|
||||
func (w *Worker) waitForBackendSync() {
|
||||
// wait a short time if blockbook is synchronizing with backend
|
||||
inSync, _, _ := w.is.GetSyncState()
|
||||
|
|
|
@ -589,3 +589,8 @@ func (w *Worker) GetXpubUtxo(xpub string, onlyConfirmed bool, gap int) (Utxos, e
|
|||
glog.Info("GetXpubUtxo ", xpub[:16], ", ", len(r), " utxos, finished in ", time.Since(start))
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// GetUtxoBalanceHistory returns history of balance for given xpub
|
||||
func (w *Worker) GetUtxoBalanceHistory(xpub string, gap int) ([]BalanceHistory, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
|
|
@ -180,7 +180,7 @@ func verifyAfterBitcoinTypeBlock1(t *testing.T, d *RocksDB, afterDisconnect bool
|
|||
if err := checkColumn(d, cfHeight, []keyPair{
|
||||
{
|
||||
"000370d5",
|
||||
"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997" + uintToHex(1534858021) + varuintToHex(2) + varuintToHex(1234567),
|
||||
"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997" + uintToHex(1521515026) + varuintToHex(2) + varuintToHex(1234567),
|
||||
nil,
|
||||
},
|
||||
}); err != nil {
|
||||
|
@ -286,12 +286,12 @@ func verifyAfterBitcoinTypeBlock2(t *testing.T, d *RocksDB) {
|
|||
if err := checkColumn(d, cfHeight, []keyPair{
|
||||
{
|
||||
"000370d5",
|
||||
"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997" + uintToHex(1534858021) + varuintToHex(2) + varuintToHex(1234567),
|
||||
"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997" + uintToHex(1521515026) + varuintToHex(2) + varuintToHex(1234567),
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"000370d6",
|
||||
"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6" + uintToHex(1534859123) + varuintToHex(4) + varuintToHex(2345678),
|
||||
"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6" + uintToHex(1521595678) + varuintToHex(4) + varuintToHex(2345678),
|
||||
nil,
|
||||
},
|
||||
}); err != nil {
|
||||
|
@ -606,7 +606,7 @@ func TestRocksDB_Index_BitcoinType(t *testing.T) {
|
|||
Hash: "00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6",
|
||||
Txs: 4,
|
||||
Size: 2345678,
|
||||
Time: 1534859123,
|
||||
Time: 1521595678,
|
||||
Height: 225494,
|
||||
}
|
||||
if !reflect.DeepEqual(info, iw) {
|
||||
|
|
|
@ -175,6 +175,7 @@ func (s *PublicServer) ConnectFullPublicInterface() {
|
|||
serveMux.HandleFunc(path+"api/block/", s.jsonHandler(s.apiBlock, apiDefault))
|
||||
serveMux.HandleFunc(path+"api/sendtx/", s.jsonHandler(s.apiSendTx, apiDefault))
|
||||
serveMux.HandleFunc(path+"api/estimatefee/", s.jsonHandler(s.apiEstimateFee, apiDefault))
|
||||
serveMux.HandleFunc(path+"api/balancehistory/", s.jsonHandler(s.apiBalanceHistory, apiDefault))
|
||||
// v2 format
|
||||
serveMux.HandleFunc(path+"api/v2/block-index/", s.jsonHandler(s.apiBlockIndex, apiV2))
|
||||
serveMux.HandleFunc(path+"api/v2/tx-specific/", s.jsonHandler(s.apiTxSpecific, apiV2))
|
||||
|
@ -186,6 +187,7 @@ func (s *PublicServer) ConnectFullPublicInterface() {
|
|||
serveMux.HandleFunc(path+"api/v2/sendtx/", s.jsonHandler(s.apiSendTx, apiV2))
|
||||
serveMux.HandleFunc(path+"api/v2/estimatefee/", s.jsonHandler(s.apiEstimateFee, apiV2))
|
||||
serveMux.HandleFunc(path+"api/v2/feestats/", s.jsonHandler(s.apiFeeStats, apiV2))
|
||||
serveMux.HandleFunc(path+"api/v2/balancehistory/", s.jsonHandler(s.apiBalanceHistory, apiDefault))
|
||||
// socket.io interface
|
||||
serveMux.Handle(path+"socket.io/", s.socketio.GetHandler())
|
||||
// websocket interface
|
||||
|
@ -1025,6 +1027,25 @@ func (s *PublicServer) apiUtxo(r *http.Request, apiVersion int) (interface{}, er
|
|||
return utxo, err
|
||||
}
|
||||
|
||||
func (s *PublicServer) apiBalanceHistory(r *http.Request, apiVersion int) (interface{}, error) {
|
||||
var history []api.BalanceHistory
|
||||
var err error
|
||||
if i := strings.LastIndexByte(r.URL.Path, '/'); i > 0 {
|
||||
gap, ec := strconv.Atoi(r.URL.Query().Get("gap"))
|
||||
if ec != nil {
|
||||
gap = 0
|
||||
}
|
||||
history, err = s.api.GetUtxoBalanceHistory(r.URL.Path[i+1:], gap)
|
||||
if err == nil {
|
||||
s.metrics.ExplorerViews.With(common.Labels{"action": "api-xpub-balancehistory"}).Inc()
|
||||
} else {
|
||||
history, err = s.api.GetBalanceHistory(r.URL.Path[i+1:])
|
||||
s.metrics.ExplorerViews.With(common.Labels{"action": "api-address-balancehistory"}).Inc()
|
||||
}
|
||||
}
|
||||
return history, err
|
||||
}
|
||||
|
||||
func (s *PublicServer) apiBlock(r *http.Request, apiVersion int) (interface{}, error) {
|
||||
var block *api.Block
|
||||
var err error
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -68,7 +68,7 @@ func GetTestBitcoinTypeBlock1(parser bchain.BlockChainParser) *bchain.Block {
|
|||
Height: 225493,
|
||||
Hash: "0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997",
|
||||
Size: 1234567,
|
||||
Time: 1534858021,
|
||||
Time: 1521515026,
|
||||
Confirmations: 2,
|
||||
},
|
||||
Txs: []bchain.Tx{
|
||||
|
@ -91,8 +91,8 @@ func GetTestBitcoinTypeBlock1(parser bchain.BlockChainParser) *bchain.Block {
|
|||
ValueSat: *SatB1T1A2,
|
||||
},
|
||||
},
|
||||
Blocktime: 22549300000,
|
||||
Time: 22549300000,
|
||||
Blocktime: 1521515026,
|
||||
Time: 1521515026,
|
||||
Confirmations: 2,
|
||||
},
|
||||
{
|
||||
|
@ -120,8 +120,8 @@ func GetTestBitcoinTypeBlock1(parser bchain.BlockChainParser) *bchain.Block {
|
|||
ValueSat: *SatB1T2A5,
|
||||
},
|
||||
},
|
||||
Blocktime: 22549300001,
|
||||
Time: 22549300001,
|
||||
Blocktime: 1521515026,
|
||||
Time: 1521515026,
|
||||
Confirmations: 2,
|
||||
},
|
||||
},
|
||||
|
@ -135,7 +135,7 @@ func GetTestBitcoinTypeBlock2(parser bchain.BlockChainParser) *bchain.Block {
|
|||
Height: 225494,
|
||||
Hash: "00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6",
|
||||
Size: 2345678,
|
||||
Time: 1534859123,
|
||||
Time: 1521595678,
|
||||
Confirmations: 1,
|
||||
},
|
||||
Txs: []bchain.Tx{
|
||||
|
@ -176,8 +176,8 @@ func GetTestBitcoinTypeBlock2(parser bchain.BlockChainParser) *bchain.Block {
|
|||
ValueSat: *SatZero,
|
||||
},
|
||||
},
|
||||
Blocktime: 22549400000,
|
||||
Time: 22549400000,
|
||||
Blocktime: 1521595678,
|
||||
Time: 1521595678,
|
||||
Confirmations: 1,
|
||||
},
|
||||
{
|
||||
|
@ -210,8 +210,8 @@ func GetTestBitcoinTypeBlock2(parser bchain.BlockChainParser) *bchain.Block {
|
|||
ValueSat: *SatB2T2A9,
|
||||
},
|
||||
},
|
||||
Blocktime: 22549400001,
|
||||
Time: 22549400001,
|
||||
Blocktime: 1521595678,
|
||||
Time: 1521595678,
|
||||
Confirmations: 1,
|
||||
},
|
||||
// transaction from the same address in the previous block
|
||||
|
@ -233,8 +233,8 @@ func GetTestBitcoinTypeBlock2(parser bchain.BlockChainParser) *bchain.Block {
|
|||
ValueSat: *SatB2T3A5,
|
||||
},
|
||||
},
|
||||
Blocktime: 22549400002,
|
||||
Time: 22549400002,
|
||||
Blocktime: 1521595678,
|
||||
Time: 1521595678,
|
||||
Confirmations: 1,
|
||||
},
|
||||
// mining transaction
|
||||
|
@ -259,8 +259,8 @@ func GetTestBitcoinTypeBlock2(parser bchain.BlockChainParser) *bchain.Block {
|
|||
ValueSat: *SatZero,
|
||||
},
|
||||
},
|
||||
Blocktime: 22549400003,
|
||||
Time: 22549400003,
|
||||
Blocktime: 1521595678,
|
||||
Time: 1521595678,
|
||||
Confirmations: 1,
|
||||
},
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue