diff --git a/api/types.go b/api/types.go index 44751c3d..d644dd3d 100644 --- a/api/types.go +++ b/api/types.go @@ -204,8 +204,8 @@ type Paging struct { ItemsOnPage int `json:"itemsOnPage,omitempty"` } -// TokenDetailLevel specifies detail level of tokens returned by GetAddress and GetXpubAddress -type TokenDetailLevel int +// TokensToReturn specifies what tokens are returned by GetAddress and GetXpubAddress +type TokensToReturn int const ( // AddressFilterVoutOff disables filtering of transactions by vout @@ -215,21 +215,21 @@ const ( // AddressFilterVoutOutputs specifies that only txs where the address is as output are returned AddressFilterVoutOutputs = -3 - // TokenDetailNonzeroBalance - use to return only tokens with nonzero balance - TokenDetailNonzeroBalance TokenDetailLevel = 0 - // TokenDetailUsed - use to return tokens with some transfers (even if they have zero balance now) - TokenDetailUsed TokenDetailLevel = 1 - // TokenDetailDiscovered - use to return all discovered tokens - TokenDetailDiscovered TokenDetailLevel = 2 + // TokensToReturnNonzeroBalance - return only tokens with nonzero balance + TokensToReturnNonzeroBalance TokensToReturn = 0 + // TokensToReturnUsed - return tokens with some transfers (even if they have zero balance now) + TokensToReturnUsed TokensToReturn = 1 + // TokensToReturnDerived - return all derived tokens + TokensToReturnDerived TokensToReturn = 2 ) // AddressFilter is used to filter data returned from GetAddress api method type AddressFilter struct { - Vout int - Contract string - FromHeight uint32 - ToHeight uint32 - TokenLevel TokenDetailLevel + Vout int + Contract string + FromHeight uint32 + ToHeight uint32 + TokensToReturn TokensToReturn // OnlyConfirmed set to true will ignore mempool transactions; mempool is also ignored if FromHeight/ToHeight filter is specified OnlyConfirmed bool } diff --git a/api/worker.go b/api/worker.go index 208ef867..f315ab33 100644 --- a/api/worker.go +++ b/api/worker.go @@ -906,6 +906,9 @@ func (w *Worker) GetAddressUtxo(address string, onlyConfirmed bool) (Utxos, erro return nil, NewAPIError(fmt.Sprintf("Invalid address '%v', %v", address, err), true) } r, err := w.getAddrDescUtxo(addrDesc, nil, onlyConfirmed, false) + if err != nil { + return nil, err + } glog.Info("GetAddressUtxo ", address, ", ", len(r), " utxos, finished in ", time.Since(start)) return r, nil } diff --git a/api/xpub.go b/api/xpub.go index 3419b945..00c91696 100644 --- a/api/xpub.go +++ b/api/xpub.go @@ -502,9 +502,9 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc } if option > AccountDetailsBasic { token := w.tokenFromXpubAddress(data, ad, ci, i, option) - if filter.TokenLevel == TokenDetailDiscovered || - filter.TokenLevel == TokenDetailUsed && ad.balance != nil || - filter.TokenLevel == TokenDetailNonzeroBalance && ad.balance != nil && !IsZeroBigInt(&ad.balance.BalanceSat) { + if filter.TokensToReturn == TokensToReturnDerived || + filter.TokensToReturn == TokensToReturnUsed && ad.balance != nil || + filter.TokensToReturn == TokensToReturnNonzeroBalance && ad.balance != nil && !IsZeroBigInt(&ad.balance.BalanceSat) { tokens = append(tokens, token) } xpubAddresses[token.Name] = struct{}{} diff --git a/server/public.go b/server/public.go index 3b21a4ff..7759164e 100644 --- a/server/public.go +++ b/server/public.go @@ -607,7 +607,7 @@ func (s *PublicServer) explorerAddress(w http.ResponseWriter, r *http.Request) ( return addressTpl, data, nil } -func (s *PublicServer) getXpubAddress(r *http.Request, xpub string, pageSize int, option api.AccountDetails) (*api.Address, api.TokenDetailLevel, error) { +func (s *PublicServer) getXpubAddress(r *http.Request, xpub string, pageSize int, option api.AccountDetails) (*api.Address, api.TokensToReturn, error) { var fn = api.AddressFilterVoutOff page, ec := strconv.Atoi(r.URL.Query().Get("page")) if ec != nil { @@ -622,7 +622,6 @@ func (s *PublicServer) getXpubAddress(r *http.Request, xpub string, pageSize int } else { fn, ec = strconv.Atoi(filter) if ec != nil || fn < 0 { - filter = "" fn = api.AddressFilterVoutOff } } @@ -631,26 +630,26 @@ func (s *PublicServer) getXpubAddress(r *http.Request, xpub string, pageSize int if ec != nil { gap = 0 } - tokenLevel := api.TokenDetailNonzeroBalance - switch r.URL.Query().Get("tokenlevel") { - case "discovered": - tokenLevel = api.TokenDetailDiscovered + tokensToReturn := api.TokensToReturnNonzeroBalance + switch r.URL.Query().Get("tokens") { + case "derived": + tokensToReturn = api.TokensToReturnDerived case "used": - tokenLevel = api.TokenDetailUsed + tokensToReturn = api.TokensToReturnUsed case "nonzero": - tokenLevel = api.TokenDetailNonzeroBalance + tokensToReturn = api.TokensToReturnNonzeroBalance } - a, err := s.api.GetXpubAddress(xpub, page, pageSize, option, &api.AddressFilter{Vout: fn, TokenLevel: tokenLevel}, gap) - return a, tokenLevel, err + a, err := s.api.GetXpubAddress(xpub, page, pageSize, option, &api.AddressFilter{Vout: fn, TokensToReturn: tokensToReturn}, gap) + return a, tokensToReturn, err } func (s *PublicServer) explorerXpub(w http.ResponseWriter, r *http.Request) (tpl, *TemplateData, error) { var address *api.Address - var tokenLevel api.TokenDetailLevel + var tokensToReturn api.TokensToReturn var err error s.metrics.ExplorerViews.With(common.Labels{"action": "xpub"}).Inc() if i := strings.LastIndexByte(r.URL.Path, '/'); i > 0 { - address, tokenLevel, err = s.getXpubAddress(r, r.URL.Path[i+1:], txsOnPage, api.AccountDetailsTxHistoryLight) + address, tokensToReturn, err = s.getXpubAddress(r, r.URL.Path[i+1:], txsOnPage, api.AccountDetailsTxHistoryLight) if err != nil { return errorTpl, nil, err } @@ -665,7 +664,7 @@ func (s *PublicServer) explorerXpub(w http.ResponseWriter, r *http.Request) (tpl data.PageParams = template.URL("&filter=" + filter) data.Address.Filter = filter } - data.NonZeroBalanceTokens = tokenLevel == api.TokenDetailNonzeroBalance + data.NonZeroBalanceTokens = tokensToReturn == api.TokensToReturnNonzeroBalance return xpubTpl, data, nil } diff --git a/server/public_test.go b/server/public_test.go index 6f2be7f1..2ed5a9ab 100644 --- a/server/public_test.go +++ b/server/public_test.go @@ -461,7 +461,7 @@ func httpTests_BitcoinType(t *testing.T, ts *httptest.Server) { }, }, { - name: "apiXpub v2 tokenlevel=nonzero", + name: "apiXpub v2 tokens=nonzero", r: newGetRequest(ts.URL + "/api/v2/xpub/" + dbtestdata.Xpub), status: http.StatusOK, contentType: "application/json; charset=utf-8", @@ -470,8 +470,8 @@ func httpTests_BitcoinType(t *testing.T, ts *httptest.Server) { }, }, { - name: "apiXpub v2 tokenlevel=used", - r: newGetRequest(ts.URL + "/api/v2/xpub/" + dbtestdata.Xpub + "?tokenlevel=used"), + name: "apiXpub v2 tokens=used", + r: newGetRequest(ts.URL + "/api/v2/xpub/" + dbtestdata.Xpub + "?tokens=used"), status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ @@ -479,8 +479,8 @@ func httpTests_BitcoinType(t *testing.T, ts *httptest.Server) { }, }, { - name: "apiXpub v2 tokenlevel=discovered", - r: newGetRequest(ts.URL + "/api/v2/xpub/" + dbtestdata.Xpub + "?tokenlevel=discovered"), + name: "apiXpub v2 tokens=derived", + r: newGetRequest(ts.URL + "/api/v2/xpub/" + dbtestdata.Xpub + "?tokens=derived"), status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ diff --git a/server/websocket.go b/server/websocket.go index e71b0c7e..bf7a0934 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -330,6 +330,7 @@ func (s *WebsocketServer) onRequest(c *websocketChannel, req *websocketReq) { type accountInfoReq struct { Descriptor string `json:"descriptor"` Details string `json:"details"` + Tokens string `json:"tokens"` PageSize int `json:"pageSize"` Page int `json:"page"` FromHeight int `json:"from"` @@ -360,12 +361,21 @@ func (s *WebsocketServer) getAccountInfo(req *accountInfoReq) (res *api.Address, default: opt = api.AccountDetailsBasic } + var tokensToReturn api.TokensToReturn + switch req.Tokens { + case "used": + tokensToReturn = api.TokensToReturnUsed + case "nonzero": + tokensToReturn = api.TokensToReturnNonzeroBalance + default: + tokensToReturn = api.TokensToReturnDerived + } filter := api.AddressFilter{ - FromHeight: uint32(req.FromHeight), - ToHeight: uint32(req.ToHeight), - Contract: req.ContractFilter, - Vout: api.AddressFilterVoutOff, - TokenLevel: api.TokenDetailDiscovered, + FromHeight: uint32(req.FromHeight), + ToHeight: uint32(req.ToHeight), + Contract: req.ContractFilter, + Vout: api.AddressFilterVoutOff, + TokensToReturn: tokensToReturn, } a, err := s.api.GetXpubAddress(req.Descriptor, req.Page, req.PageSize, opt, &filter, 0) if err != nil { diff --git a/static/templates/xpub.html b/static/templates/xpub.html index d67c76a3..df324738 100644 --- a/static/templates/xpub.html +++ b/static/templates/xpub.html @@ -51,7 +51,7 @@ {{- end -}} {{- if $data.NonZeroBalanceTokens -}} - Show all XPUB addresses + Show all XPUB addresses {{- end -}} diff --git a/static/test-websocket.html b/static/test-websocket.html index 37274254..50f046a8 100644 --- a/static/test-websocket.html +++ b/static/test-websocket.html @@ -125,9 +125,11 @@ const contractFilter = document.getElementById("getAccountInfoContract").value.trim(); const pageSize = 10; const method = 'getAccountInfo'; + const tokens = "derived"; // could be "nonzero", "used", default is "derived" i.e. all const params = { descriptor, details, + tokens, page, pageSize, from,