Filter address transactions by height from-to and contract

This commit is contained in:
Martin Boehm 2018-12-14 16:42:35 +01:00
parent 35a13e0647
commit a04b2b67b5
7 changed files with 97 additions and 51 deletions

View File

@ -74,14 +74,14 @@ func (a *Amount) AsBigInt() big.Int {
// ScriptSig contains input script // ScriptSig contains input script
type ScriptSig struct { type ScriptSig struct {
Hex string `json:"hex"` Hex string `json:"hex,omitempty"`
Asm string `json:"asm,omitempty"` Asm string `json:"asm,omitempty"`
} }
// Vin contains information about single transaction input // Vin contains information about single transaction input
type Vin struct { type Vin struct {
Txid string `json:"txid"` Txid string `json:"txid,omitempty"`
Vout uint32 `json:"vout"` Vout uint32 `json:"vout,omitempty"`
Sequence int64 `json:"sequence,omitempty"` Sequence int64 `json:"sequence,omitempty"`
N int `json:"n"` N int `json:"n"`
ScriptSig ScriptSig `json:"scriptSig"` ScriptSig ScriptSig `json:"scriptSig"`
@ -93,7 +93,7 @@ type Vin struct {
// ScriptPubKey contains output script and addresses derived from it // ScriptPubKey contains output script and addresses derived from it
type ScriptPubKey struct { type ScriptPubKey struct {
Hex string `json:"hex"` Hex string `json:"hex,omitempty"`
Asm string `json:"asm,omitempty"` Asm string `json:"asm,omitempty"`
AddrDesc bchain.AddressDescriptor `json:"-"` AddrDesc bchain.AddressDescriptor `json:"-"`
Addresses []string `json:"addresses"` Addresses []string `json:"addresses"`
@ -106,7 +106,7 @@ type Vout struct {
ValueSat *Amount `json:"value,omitempty"` ValueSat *Amount `json:"value,omitempty"`
N int `json:"n"` N int `json:"n"`
ScriptPubKey ScriptPubKey `json:"scriptPubKey"` ScriptPubKey ScriptPubKey `json:"scriptPubKey"`
Spent bool `json:"spent"` Spent bool `json:"spent,omitempty"`
SpentTxID string `json:"spentTxId,omitempty"` SpentTxID string `json:"spentTxId,omitempty"`
SpentIndex int `json:"spentIndex,omitempty"` SpentIndex int `json:"spentIndex,omitempty"`
SpentHeight int `json:"spentHeight,omitempty"` SpentHeight int `json:"spentHeight,omitempty"`
@ -115,7 +115,7 @@ type Vout struct {
// Erc20Token contains info about ERC20 token held by an address // Erc20Token contains info about ERC20 token held by an address
type Erc20Token struct { type Erc20Token struct {
Contract string `json:"contract"` Contract string `json:"contract"`
Txs int `json:"txs"` Transfers int `json:"transfers"`
Name string `json:"name"` Name string `json:"name"`
Symbol string `json:"symbol"` Symbol string `json:"symbol"`
Decimals int `json:"decimals"` Decimals int `json:"decimals"`
@ -155,8 +155,8 @@ type Tx struct {
Confirmations uint32 `json:"confirmations"` Confirmations uint32 `json:"confirmations"`
Time int64 `json:"time,omitempty"` Time int64 `json:"time,omitempty"`
Blocktime int64 `json:"blocktime"` Blocktime int64 `json:"blocktime"`
ValueOutSat *Amount `json:"valueOut,omitempty"`
Size int `json:"size,omitempty"` Size int `json:"size,omitempty"`
ValueOutSat *Amount `json:"valueOut,omitempty"`
ValueInSat *Amount `json:"valueIn,omitempty"` ValueInSat *Amount `json:"valueIn,omitempty"`
FeesSat *Amount `json:"fees,omitempty"` FeesSat *Amount `json:"fees,omitempty"`
Hex string `json:"hex,omitempty"` Hex string `json:"hex,omitempty"`
@ -173,14 +173,21 @@ type Paging struct {
ItemsOnPage int `json:"itemsOnPage,omitempty"` ItemsOnPage int `json:"itemsOnPage,omitempty"`
} }
// AddressFilterNone disables filtering of transactions const (
const AddressFilterNone = -1 // AddressFilterVoutOff disables filtering of transactions by vout
AddressFilterVoutOff = -1
// AddressFilterVoutInputs specifies that only txs where the address is as input are returned
AddressFilterVoutInputs = -2
// AddressFilterVoutOutputs specifies that only txs where the address is as output are returned
AddressFilterVoutOutputs = -3
)
// AddressFilterInputs specifies that only txs where the address is as input are returned type AddressFilter struct {
const AddressFilterInputs = -2 Vout int
Contract string
// AddressFilterOutputs specifies that only txs where the address is as output are returned FromHeight uint32
const AddressFilterOutputs = -3 ToHeight uint32
}
// Address holds information about address and its transactions // Address holds information about address and its transactions
type Address struct { type Address struct {

View File

@ -128,6 +128,7 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height uint32,
} }
} }
var valInSat, valOutSat, feesSat big.Int var valInSat, valOutSat, feesSat big.Int
var pValInSat *big.Int
vins := make([]Vin, len(bchainTx.Vin)) vins := make([]Vin, len(bchainTx.Vin))
for i := range bchainTx.Vin { for i := range bchainTx.Vin {
bchainVin := &bchainTx.Vin[i] bchainVin := &bchainTx.Vin[i]
@ -215,6 +216,7 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height uint32,
if feesSat.Sign() == -1 { if feesSat.Sign() == -1 {
feesSat.SetUint64(0) feesSat.SetUint64(0)
} }
pValInSat = &valInSat
} else if w.chainType == bchain.ChainEthereumType { } else if w.chainType == bchain.ChainEthereumType {
ets, err := eth.GetErc20FromTx(bchainTx) ets, err := eth.GetErc20FromTx(bchainTx)
if err != nil { if err != nil {
@ -251,9 +253,8 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height uint32,
feesSat.Mul(ethTxData.GasPrice, ethTxData.GasUsed) feesSat.Mul(ethTxData.GasPrice, ethTxData.GasUsed)
} }
if len(bchainTx.Vout) > 0 { if len(bchainTx.Vout) > 0 {
valInSat = bchainTx.Vout[0].ValueSat valOutSat = bchainTx.Vout[0].ValueSat
} }
valOutSat = valInSat
ethSpecific = &EthereumSpecific{ ethSpecific = &EthereumSpecific{
GasLimit: ethTxData.GasLimit, GasLimit: ethTxData.GasLimit,
GasPrice: (*Amount)(ethTxData.GasPrice), GasPrice: (*Amount)(ethTxData.GasPrice),
@ -280,7 +281,7 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height uint32,
Locktime: bchainTx.LockTime, Locktime: bchainTx.LockTime,
Time: bchainTx.Time, Time: bchainTx.Time,
Txid: bchainTx.Txid, Txid: bchainTx.Txid,
ValueInSat: (*Amount)(&valInSat), ValueInSat: (*Amount)(pValInSat),
ValueOutSat: (*Amount)(&valOutSat), ValueOutSat: (*Amount)(&valOutSat),
Version: bchainTx.Version, Version: bchainTx.Version,
Hex: bchainTx.Hex, Hex: bchainTx.Hex,
@ -294,14 +295,14 @@ 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 int) ([]string, error) { func (w *Worker) getAddressTxids(addrDesc bchain.AddressDescriptor, mempool bool, filter *AddressFilter) ([]string, error) {
var err error var err error
txids := make([]string, 0, 4) txids := make([]string, 0, 4)
addFilteredTxid := func(txid string, vout int32, isOutput bool) error { addFilteredTxid := func(txid string, vout int32, isOutput bool) error {
if filter == AddressFilterNone || if filter.Vout == AddressFilterVoutOff ||
(filter == AddressFilterInputs && !isOutput) || (filter.Vout == AddressFilterVoutInputs && !isOutput) ||
(filter == AddressFilterOutputs && isOutput) || (filter.Vout == AddressFilterVoutOutputs && isOutput) ||
(vout == int32(filter)) { (vout == int32(filter.Vout)) {
txids = append(txids, txid) txids = append(txids, txid)
} }
return nil return nil
@ -321,7 +322,11 @@ func (w *Worker) getAddressTxids(addrDesc bchain.AddressDescriptor, mempool bool
addFilteredTxid(m.Txid, vout, isOutput) addFilteredTxid(m.Txid, vout, isOutput)
} }
} else { } else {
err = w.db.GetAddrDescTransactions(addrDesc, 0, ^uint32(0), addFilteredTxid) to := filter.ToHeight
if to == 0 {
to = ^uint32(0)
}
err = w.db.GetAddrDescTransactions(addrDesc, filter.FromHeight, to, addFilteredTxid)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -332,7 +337,7 @@ func (w *Worker) getAddressTxids(addrDesc bchain.AddressDescriptor, mempool bool
func (t *Tx) getAddrVoutValue(addrDesc bchain.AddressDescriptor) *big.Int { func (t *Tx) getAddrVoutValue(addrDesc bchain.AddressDescriptor) *big.Int {
var val big.Int var val big.Int
for _, vout := range t.Vout { for _, vout := range t.Vout {
if bytes.Equal(vout.ScriptPubKey.AddrDesc, addrDesc) { if bytes.Equal(vout.ScriptPubKey.AddrDesc, addrDesc) && vout.ValueSat != nil {
val.Add(&val, (*big.Int)(vout.ValueSat)) val.Add(&val, (*big.Int)(vout.ValueSat))
} }
} }
@ -342,7 +347,7 @@ func (t *Tx) getAddrVoutValue(addrDesc bchain.AddressDescriptor) *big.Int {
func (t *Tx) getAddrVinValue(addrDesc bchain.AddressDescriptor) *big.Int { func (t *Tx) getAddrVinValue(addrDesc bchain.AddressDescriptor) *big.Int {
var val big.Int var val big.Int
for _, vin := range t.Vin { for _, vin := range t.Vin {
if bytes.Equal(vin.AddrDesc, addrDesc) { if bytes.Equal(vin.AddrDesc, addrDesc) && vin.ValueSat != nil {
val.Add(&val, (*big.Int)(vin.ValueSat)) val.Add(&val, (*big.Int)(vin.ValueSat))
} }
} }
@ -435,7 +440,7 @@ 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) (*db.AddrBalance, []Erc20Token, *bchain.Erc20Contract, uint64, error) { func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, option GetAddressOption, filter *AddressFilter) (*db.AddrBalance, []Erc20Token, *bchain.Erc20Contract, uint64, error) {
var ( var (
ba *db.AddrBalance ba *db.AddrBalance
erc20t []Erc20Token erc20t []Erc20Token
@ -462,8 +467,23 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
if err != nil { if err != nil {
return nil, nil, nil, 0, errors.Annotatef(err, "EthereumTypeGetNonce %v", addrDesc) return nil, nil, nil, 0, errors.Annotatef(err, "EthereumTypeGetNonce %v", addrDesc)
} }
var filterDesc bchain.AddressDescriptor
if filter.Contract != "" {
filterDesc, err = w.chainParser.GetAddrDescFromAddress(filter.Contract)
if err != nil {
return nil, nil, nil, 0, NewAPIError(fmt.Sprintf("Invalid contract filter, %v", err), true)
}
}
erc20t = make([]Erc20Token, len(ca.Contracts)) erc20t = make([]Erc20Token, len(ca.Contracts))
var j int
for i, c := range ca.Contracts { for i, c := range ca.Contracts {
if len(filterDesc) > 0 {
if !bytes.Equal(filterDesc, c.Contract) {
continue
}
// filter only transactions by this contract
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, errors.Annotatef(err, "EthereumTypeGetErc20ContractInfo %v", c.Contract) return nil, nil, nil, 0, errors.Annotatef(err, "EthereumTypeGetErc20ContractInfo %v", c.Contract)
@ -486,16 +506,18 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
} else { } else {
b = nil b = nil
} }
erc20t[i] = Erc20Token{ erc20t[j] = Erc20Token{
BalanceSat: (*Amount)(b), BalanceSat: (*Amount)(b),
Contract: ci.Contract, Contract: ci.Contract,
Name: ci.Name, Name: ci.Name,
Symbol: ci.Symbol, Symbol: ci.Symbol,
Txs: int(c.Txs), Transfers: int(c.Txs),
Decimals: ci.Decimals, Decimals: ci.Decimals,
ContractIndex: strconv.Itoa(i + 1), ContractIndex: strconv.Itoa(i + 1),
} }
j++
} }
erc20t = erc20t[:j]
ci, err = w.chain.EthereumTypeGetErc20ContractInfo(addrDesc) ci, err = w.chain.EthereumTypeGetErc20ContractInfo(addrDesc)
if err != nil { if err != nil {
return nil, nil, nil, 0, err return nil, nil, nil, 0, err
@ -505,7 +527,7 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
} }
// GetAddress computes address value and gets transactions for given address // GetAddress computes address value and gets transactions for given address
func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetAddressOption, filter int) (*Address, error) { func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetAddressOption, filter *AddressFilter) (*Address, error) {
start := time.Now() start := time.Now()
page-- page--
if page < 0 { if page < 0 {
@ -529,7 +551,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
) )
if w.chainType == bchain.ChainEthereumType { if w.chainType == bchain.ChainEthereumType {
var n uint64 var n uint64
ba, erc20t, erc20c, n, err = w.getEthereumTypeAddressBalances(addrDesc, option) ba, erc20t, erc20c, n, err = w.getEthereumTypeAddressBalances(addrDesc, option, filter)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -598,9 +620,6 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
} }
} }
} }
if len(txc) != int(ba.Txs) && w.chainType == bchain.ChainBitcoinType && filter == AddressFilterNone {
glog.Warning("DB inconsistency for address ", address, ": number of txs from column addresses ", len(txc), ", from addressBalance ", ba.Txs)
}
for i := from; i < to; i++ { for i := from; i < to; i++ {
txid := txc[i] txid := txc[i]
if option == TxidHistory { if option == TxidHistory {
@ -674,7 +693,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, AddressFilterNone) txm, err := w.getAddressTxids(addrDesc, true, &AddressFilter{Vout: AddressFilterVoutOff})
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "getAddressTxids %v true", address) return nil, errors.Annotatef(err, "getAddressTxids %v true", address)
} }

View File

@ -498,7 +498,7 @@ func (s *PublicServer) explorerSpendingTx(w http.ResponseWriter, r *http.Request
func (s *PublicServer) explorerAddress(w http.ResponseWriter, r *http.Request) (tpl, *TemplateData, error) { func (s *PublicServer) explorerAddress(w http.ResponseWriter, r *http.Request) (tpl, *TemplateData, error) {
var address *api.Address var address *api.Address
var filter string var filter string
var fn = api.AddressFilterNone var fn = api.AddressFilterVoutOff
var err error var err error
s.metrics.ExplorerViews.With(common.Labels{"action": "address"}).Inc() s.metrics.ExplorerViews.With(common.Labels{"action": "address"}).Inc()
if i := strings.LastIndexByte(r.URL.Path, '/'); i > 0 { if i := strings.LastIndexByte(r.URL.Path, '/'); i > 0 {
@ -509,18 +509,18 @@ func (s *PublicServer) explorerAddress(w http.ResponseWriter, r *http.Request) (
filter = r.URL.Query().Get("filter") filter = r.URL.Query().Get("filter")
if len(filter) > 0 { if len(filter) > 0 {
if filter == "inputs" { if filter == "inputs" {
fn = api.AddressFilterInputs fn = api.AddressFilterVoutInputs
} else if filter == "outputs" { } else if filter == "outputs" {
fn = api.AddressFilterOutputs fn = api.AddressFilterVoutOutputs
} else { } else {
fn, ec = strconv.Atoi(filter) fn, ec = strconv.Atoi(filter)
if ec != nil || fn < 0 { if ec != nil || fn < 0 {
filter = "" filter = ""
fn = api.AddressFilterNone fn = api.AddressFilterVoutOff
} }
} }
} }
address, err = s.api.GetAddress(r.URL.Path[i+1:], page, txsOnPage, api.TxHistory, fn) address, err = s.api.GetAddress(r.URL.Path[i+1:], page, txsOnPage, api.TxHistory, &api.AddressFilter{Vout: fn})
if err != nil { if err != nil {
return errorTpl, nil, err return errorTpl, nil, err
} }
@ -608,7 +608,7 @@ func (s *PublicServer) explorerSearch(w http.ResponseWriter, r *http.Request) (t
http.Redirect(w, r, joinURL("/tx/", tx.Txid), 302) http.Redirect(w, r, joinURL("/tx/", tx.Txid), 302)
return noTpl, nil, nil return noTpl, nil, nil
} }
address, err = s.api.GetAddress(q, 0, 1, api.Basic, api.AddressFilterNone) address, err = s.api.GetAddress(q, 0, 1, api.Basic, &api.AddressFilter{Vout: api.AddressFilterVoutOff})
if err == nil { if err == nil {
http.Redirect(w, r, joinURL("/address/", address.AddrStr), 302) http.Redirect(w, r, joinURL("/address/", address.AddrStr), 302)
return noTpl, nil, nil return noTpl, nil, nil
@ -766,7 +766,7 @@ func (s *PublicServer) apiAddress(r *http.Request, apiVersion int) (interface{},
if ec != nil { if ec != nil {
page = 0 page = 0
} }
address, err = s.api.GetAddress(r.URL.Path[i+1:], page, txsInAPI, api.TxidHistory, api.AddressFilterNone) address, err = s.api.GetAddress(r.URL.Path[i+1:], page, txsInAPI, api.TxidHistory, &api.AddressFilter{Vout: api.AddressFilterVoutOff})
if err == nil && apiVersion == apiV1 { if err == nil && apiVersion == apiV1 {
return s.api.AddressToV1(address), nil return s.api.AddressToV1(address), nil
} }

View File

@ -379,7 +379,7 @@ func httpTests_BitcoinType(t *testing.T, ts *httptest.Server) {
status: http.StatusOK, status: http.StatusOK,
contentType: "application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
body: []string{ body: []string{
`{"txid":"05e2e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07","vin":[{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vout":2,"n":0,"scriptSig":{"hex":""},"addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"],"value":"0.00009876"}],"vout":[{"value":"0.00009","n":0,"scriptPubKey":{"hex":"a914e921fc4912a315078f370d959f2c4f7b6d2a683c87","addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"]},"spent":false}],"blockhash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","blockheight":225494,"confirmations":1,"time":22549400002,"blocktime":22549400002,"valueOut":"0.00009","valueIn":"0.00009876","fees":"0.00000876","hex":""}`, `{"txid":"05e2e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07","vin":[{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vout":2,"n":0,"scriptSig":{},"addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"],"value":"0.00009876"}],"vout":[{"value":"0.00009","n":0,"scriptPubKey":{"hex":"a914e921fc4912a315078f370d959f2c4f7b6d2a683c87","addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"]},"spent":false}],"blockhash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","blockheight":225494,"confirmations":1,"time":22549400002,"blocktime":22549400002,"valueOut":"0.00009","valueIn":"0.00009876","fees":"0.00000876","hex":""}`,
}, },
}, },
{ {
@ -397,7 +397,7 @@ func httpTests_BitcoinType(t *testing.T, ts *httptest.Server) {
status: http.StatusOK, status: http.StatusOK,
contentType: "application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
body: []string{ body: []string{
`{"txid":"05e2e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07","vin":[{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vout":2,"n":0,"scriptSig":{"hex":""},"addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"],"value":"9876"}],"vout":[{"value":"9000","n":0,"scriptPubKey":{"hex":"a914e921fc4912a315078f370d959f2c4f7b6d2a683c87","addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"]},"spent":false}],"blockhash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","blockheight":225494,"confirmations":1,"time":22549400002,"blocktime":22549400002,"valueOut":"9000","valueIn":"9876","fees":"876"}`, `{"txid":"05e2e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07","vin":[{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vout":2,"n":0,"scriptSig":{},"addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"],"value":"9876"}],"vout":[{"value":"9000","n":0,"scriptPubKey":{"hex":"a914e921fc4912a315078f370d959f2c4f7b6d2a683c87","addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"]}}],"blockhash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","blockheight":225494,"confirmations":1,"time":22549400002,"blocktime":22549400002,"valueOut":"9000","valueIn":"9876","fees":"876"}`,
}, },
}, },
{ {

View File

@ -284,10 +284,13 @@ func (s *WebsocketServer) onRequest(c *websocketChannel, req *websocketReq) {
} }
type accountInfoReq struct { type accountInfoReq struct {
Descriptor string `json:"descriptor"` Descriptor string `json:"descriptor"`
Details string `json:"details"` Details string `json:"details"`
PageSize int `json:"pageSize"` PageSize int `json:"pageSize"`
Page int `json:"page"` Page int `json:"page"`
FromHeight int `json:"from"`
ToHeight int `json:"to"`
ContractFilter string `json:"contractFilter"`
} }
func unmarshalGetAccountInfoRequest(params []byte) (*accountInfoReq, error) { func unmarshalGetAccountInfoRequest(params []byte) (*accountInfoReq, error) {
@ -312,7 +315,13 @@ func (s *WebsocketServer) getAccountInfo(req *accountInfoReq) (res *api.Address,
default: default:
opt = api.Basic opt = api.Basic
} }
return s.api.GetAddress(req.Descriptor, req.Page, req.PageSize, opt, api.AddressFilterNone)
return s.api.GetAddress(req.Descriptor, req.Page, req.PageSize, opt, &api.AddressFilter{
FromHeight: uint32(req.FromHeight),
ToHeight: uint32(req.ToHeight),
Contract: req.ContractFilter,
Vout: api.AddressFilterVoutOff,
})
} }
return nil, errors.New("Not implemented") return nil, errors.New("Not implemented")
} }

View File

@ -38,7 +38,7 @@
<tr> <tr>
<td class="data ellipsis"><a href="/address/{{$et.Contract}}">{{$et.Name}}</a></td> <td class="data ellipsis"><a href="/address/{{$et.Contract}}">{{$et.Name}}</a></td>
<td class="data">{{formatAmountWithDecimals $et.BalanceSat $et.Decimals}} {{$et.Symbol}}</td> <td class="data">{{formatAmountWithDecimals $et.BalanceSat $et.Decimals}} {{$et.Symbol}}</td>
<td class="data">{{$et.Txs}}</td> <td class="data">{{$et.Transfers}}</td>
</tr> </tr>
{{- end -}} {{- end -}}
</tbody> </tbody>

View File

@ -102,6 +102,9 @@
const selectDetails = document.getElementById('getAccountInfoDetails'); const selectDetails = document.getElementById('getAccountInfoDetails');
const details = selectDetails.options[selectDetails.selectedIndex].value; const details = selectDetails.options[selectDetails.selectedIndex].value;
const page = parseInt(document.getElementById("getAccountInfoPage").value); const page = parseInt(document.getElementById("getAccountInfoPage").value);
const from = parseInt(document.getElementById("getAccountInfoFrom").value);
const to = parseInt(document.getElementById("getAccountInfoTo").value);
const contractFilter = document.getElementById("getAccountInfoContract").value.trim();
const pageSize = 10; const pageSize = 10;
const method = 'getAccountInfo'; const method = 'getAccountInfo';
const params = { const params = {
@ -109,6 +112,9 @@
details, details,
page, page,
pageSize, pageSize,
from,
to,
contractFilter
}; };
send(method, params, function (result) { send(method, params, function (result) {
document.getElementById('getAccountInfoResult').innerText = JSON.stringify(result).replace(/,/g, ", "); document.getElementById('getAccountInfoResult').innerText = JSON.stringify(result).replace(/,/g, ", ");
@ -257,14 +263,19 @@
</div> </div>
<div class="col-8"> <div class="col-8">
<div class="row" style="margin: 0;"> <div class="row" style="margin: 0;">
<input type="text" style="width: 67.7%" class="form-control" id="getAccountInfoDescriptor" value="0x103262f243e6f67d12d6a4ea0d45302c1fa4bb0a"> <input type="text" style="width: 79%" class="form-control" id="getAccountInfoDescriptor" value="0x65513ecd11fd3a5b1fefdcc6a500b025008405a2">
<select id="getAccountInfoDetails" style="width: 20%; margin-left: 5px;"> <select id="getAccountInfoDetails" style="width: 20%; margin-left: 5px;">
<option value="basic">Basic</option> <option value="basic">Basic</option>
<option value="balance">Balance</option> <option value="balance">Balance</option>
<option value="txids">Txids</option> <option value="txids">Txids</option>
<option value="txs">Transactions</option> <option value="txs">Transactions</option>
</select> </select>
<input type="text" style="width: 10%; margin-left: 5px; margin-right: 5px;" class="form-control" id="getAccountInfoPage" value="0"> </div>
<div class="row" style="margin: 0; margin-top: 5px;">
<input type="text" placeholder="page" style="width: 10%; margin-right: 5px;" class="form-control" id="getAccountInfoPage">
<input type="text" placeholder="from" style="width: 15%;margin-left: 5px;margin-right: 5px;" class="form-control" id="getAccountInfoFrom">
<input type="text" placeholder="to" style="width: 15%; margin-left: 5px; margin-right: 5px;" class="form-control" id="getAccountInfoTo">
<input type="text" placeholder="contract" style="width: 55%; margin-left: 5px; margin-right: 5px;" class="form-control" id="getAccountInfoContract">
</div> </div>
</div> </div>
<div class="col form-inline"></div> <div class="col form-inline"></div>