From 7b590d9958e5c7f692c16432fc5d4ca577bc3854 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Thu, 28 Feb 2019 15:07:07 +0100 Subject: [PATCH] Unify AccountDetails levels in GetAccount and GetXpub api calls --- api/types.go | 26 +++++----- api/worker.go | 102 +++++++++++++++++++------------------ api/xpub.go | 34 +++++++------ server/public.go | 14 ++--- server/websocket.go | 12 +++-- static/test-websocket.html | 1 + 6 files changed, 99 insertions(+), 90 deletions(-) diff --git a/api/types.go b/api/types.go index 7b4c6034..44751c3d 100644 --- a/api/types.go +++ b/api/types.go @@ -14,20 +14,22 @@ const maxUint32 = ^uint32(0) const maxInt = int(^uint(0) >> 1) const maxInt64 = int64(^uint64(0) >> 1) -// GetAddressOption specifies what data returns GetAddress api call -type GetAddressOption int +// AccountDetails specifies what data returns GetAddress and GetXpub calls +type AccountDetails int const ( - // Basic - only that address is indexed and some basic info - Basic GetAddressOption = iota - // Tokens - basic info + tokens - Tokens - // TxidHistory - basic + tokens + txids, subject to paging - TxidHistory - // TxHistoryLight - basic + tokens + easily obtained tx data (not requiring request to backend), subject to paging - TxHistoryLight - // TxHistory - basic + tokens + full tx data, subject to paging - TxHistory + // AccountDetailsBasic - only that address is indexed and some basic info + AccountDetailsBasic AccountDetails = iota + // AccountDetailsTokens - basic info + tokens + AccountDetailsTokens + // AccountDetailsTokenBalances - basic info + token with balance + AccountDetailsTokenBalances + // AccountDetailsTxidHistory - basic + token balances + txids, subject to paging + AccountDetailsTxidHistory + // AccountDetailsTxHistoryLight - basic + tokens + easily obtained tx data (not requiring requests to backend), subject to paging + AccountDetailsTxHistoryLight + // AccountDetailsTxHistory - basic + tokens + full tx data, subject to paging + AccountDetailsTxHistory ) // ErrUnsupportedXpub is returned when coin type does not support xpub address derivation or provided string is not an xpub diff --git a/api/worker.go b/api/worker.go index 80e19871..d912105e 100644 --- a/api/worker.go +++ b/api/worker.go @@ -479,7 +479,7 @@ func computePaging(count, page, itemsOnPage int) (Paging, int, int, int) { }, from, to, page } -func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, option GetAddressOption, filter *AddressFilter) (*db.AddrBalance, []Token, *bchain.Erc20Contract, uint64, int, int, error) { +func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, details AccountDetails, filter *AddressFilter) (*db.AddrBalance, []Token, *bchain.Erc20Contract, uint64, int, int, error) { var ( ba *db.AddrBalance tokens []Token @@ -516,53 +516,55 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto return nil, nil, nil, 0, 0, 0, NewAPIError(fmt.Sprintf("Invalid contract filter, %v", err), true) } } - tokens = make([]Token, len(ca.Contracts)) - var j int - for i, c := range ca.Contracts { - if len(filterDesc) > 0 { - if !bytes.Equal(filterDesc, c.Contract) { - continue + if details > AccountDetailsBasic { + tokens = make([]Token, len(ca.Contracts)) + var j int + for i, c := range ca.Contracts { + if len(filterDesc) > 0 { + if !bytes.Equal(filterDesc, c.Contract) { + continue + } + // filter only transactions of this contract + filter.Vout = i + 1 } - // filter only transactions of this contract - filter.Vout = i + 1 - } - validContract := true - ci, err := w.chain.EthereumTypeGetErc20ContractInfo(c.Contract) - if err != nil { - return nil, nil, nil, 0, 0, 0, errors.Annotatef(err, "EthereumTypeGetErc20ContractInfo %v", c.Contract) - } - if ci == nil { - ci = &bchain.Erc20Contract{} - addresses, _, _ := w.chainParser.GetAddressesFromAddrDesc(c.Contract) - if len(addresses) > 0 { - ci.Contract = addresses[0] - ci.Name = addresses[0] - } - validContract = false - } - // do not read contract balances etc in case of Basic option - if option != Basic && validContract { - b, err = w.chain.EthereumTypeGetErc20ContractBalance(addrDesc, c.Contract) + validContract := true + ci, err := w.chain.EthereumTypeGetErc20ContractInfo(c.Contract) if err != nil { - // return nil, nil, nil, errors.Annotatef(err, "EthereumTypeGetErc20ContractBalance %v %v", addrDesc, c.Contract) - glog.Warningf("EthereumTypeGetErc20ContractBalance addr %v, contract %v, %v", addrDesc, c.Contract, err) + return nil, nil, nil, 0, 0, 0, errors.Annotatef(err, "EthereumTypeGetErc20ContractInfo %v", c.Contract) } - } else { - b = nil + if ci == nil { + ci = &bchain.Erc20Contract{} + addresses, _, _ := w.chainParser.GetAddressesFromAddrDesc(c.Contract) + if len(addresses) > 0 { + ci.Contract = addresses[0] + ci.Name = addresses[0] + } + validContract = false + } + // do not read contract balances etc in case of Basic option + if details >= AccountDetailsTokenBalances && validContract { + b, err = w.chain.EthereumTypeGetErc20ContractBalance(addrDesc, c.Contract) + if err != nil { + // return nil, nil, nil, errors.Annotatef(err, "EthereumTypeGetErc20ContractBalance %v %v", addrDesc, c.Contract) + glog.Warningf("EthereumTypeGetErc20ContractBalance addr %v, contract %v, %v", addrDesc, c.Contract, err) + } + } else { + b = nil + } + tokens[j] = Token{ + Type: ERC20TokenType, + BalanceSat: (*Amount)(b), + Contract: ci.Contract, + Name: ci.Name, + Symbol: ci.Symbol, + Transfers: int(c.Txs), + Decimals: ci.Decimals, + ContractIndex: strconv.Itoa(i + 1), + } + j++ } - tokens[j] = Token{ - Type: ERC20TokenType, - BalanceSat: (*Amount)(b), - Contract: ci.Contract, - Name: ci.Name, - Symbol: ci.Symbol, - Transfers: int(c.Txs), - Decimals: ci.Decimals, - ContractIndex: strconv.Itoa(i + 1), - } - j++ + tokens = tokens[:j] } - tokens = tokens[:j] ci, err = w.chain.EthereumTypeGetErc20ContractInfo(addrDesc) if err != nil { return nil, nil, nil, 0, 0, 0, err @@ -582,11 +584,11 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto return ba, tokens, ci, n, nonContractTxs, totalResults, nil } -func (w *Worker) txFromTxid(txid string, bestheight uint32, option GetAddressOption) (*Tx, error) { +func (w *Worker) txFromTxid(txid string, bestheight uint32, option AccountDetails) (*Tx, error) { var tx *Tx var err error // only ChainBitcoinType supports TxHistoryLight - if option == TxHistoryLight && w.chainType == bchain.ChainBitcoinType { + if option == AccountDetailsTxHistoryLight && w.chainType == bchain.ChainBitcoinType { ta, err := w.db.GetTxAddresses(txid) if err != nil { return nil, errors.Annotatef(err, "GetTxAddresses %v", txid) @@ -632,7 +634,7 @@ func (w *Worker) getAddrDescAndNormalizeAddress(address string) (bchain.AddressD } // GetAddress computes address value and gets transactions for given address -func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetAddressOption, filter *AddressFilter) (*Address, error) { +func (w *Worker) GetAddress(address string, page int, txsOnPage int, option AccountDetails, filter *AddressFilter) (*Address, error) { start := time.Now() page-- if page < 0 { @@ -700,9 +702,9 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA uBalSat.Add(&uBalSat, tx.getAddrVoutValue(addrDesc)) uBalSat.Sub(&uBalSat, tx.getAddrVinValue(addrDesc)) if page == 0 { - if option == TxidHistory { + if option == AccountDetailsTxidHistory { txids = append(txids, tx.Txid) - } else if option >= TxHistoryLight { + } else if option >= AccountDetailsTxHistoryLight { txs = append(txs, tx) } } @@ -711,7 +713,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA } } // get tx history if requested by option or check mempool if there are some transactions for a new address - if option >= TxidHistory { + if option >= AccountDetailsTxidHistory { txc, err := w.getAddressTxids(addrDesc, false, filter, (page+1)*txsOnPage) if err != nil { return nil, errors.Annotatef(err, "getAddressTxids %v false", addrDesc) @@ -731,7 +733,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA } for i := from; i < to; i++ { txid := txc[i] - if option == TxidHistory { + if option == AccountDetailsTxidHistory { txids = append(txids, txid) } else { tx, err := w.txFromTxid(txid, bestheight, option) diff --git a/api/xpub.go b/api/xpub.go index e577f8fa..3419b945 100644 --- a/api/xpub.go +++ b/api/xpub.go @@ -212,7 +212,7 @@ func (w *Worker) xpubScanAddresses(xpub string, data *xpubData, addresses []xpub return lastUsed, addresses, nil } -func (w *Worker) tokenFromXpubAddress(data *xpubData, ad *xpubAddress, changeIndex int, index int) Token { +func (w *Worker) tokenFromXpubAddress(data *xpubData, ad *xpubAddress, changeIndex int, index int, option AccountDetails) Token { a, _, _ := w.chainParser.GetAddressesFromAddrDesc(ad.addrDesc) var address string if len(a) > 0 { @@ -221,10 +221,12 @@ func (w *Worker) tokenFromXpubAddress(data *xpubData, ad *xpubAddress, changeInd var balance, totalReceived, totalSent *big.Int var transfers int if ad.balance != nil { - balance = &ad.balance.BalanceSat - totalSent = &ad.balance.SentSat - totalReceived = ad.balance.ReceivedSat() transfers = int(ad.balance.Txs) + if option >= AccountDetailsTokenBalances { + balance = &ad.balance.BalanceSat + totalSent = &ad.balance.SentSat + totalReceived = ad.balance.ReceivedSat() + } } return Token{ Type: XPUBAddressTokenType, @@ -260,7 +262,7 @@ func evictXpubCacheItems() { glog.Info("Evicted ", count, " items from xpub cache, oldest item accessed at ", time.Unix(oldest, 0), ", cache size ", len(cachedXpubs)) } -func (w *Worker) getXpubData(xpub string, page int, txsOnPage int, option GetAddressOption, filter *AddressFilter, gap int) (*xpubData, uint32, error) { +func (w *Worker) getXpubData(xpub string, page int, txsOnPage int, option AccountDetails, filter *AddressFilter, gap int) (*xpubData, uint32, error) { if w.chainType != bchain.ChainBitcoinType || len(xpub) != xpubLen { return nil, 0, ErrUnsupportedXpub } @@ -325,7 +327,7 @@ func (w *Worker) getXpubData(xpub string, page int, txsOnPage int, option GetAdd return nil, 0, err } } - if option >= TxidHistory { + if option >= AccountDetailsTxidHistory { for _, da := range [][]xpubAddress{data.addresses, data.changeAddresses} { for i := range da { if err = w.xpubCheckAndLoadTxids(&da[i], filter, bestheight, (page+1)*txsOnPage); err != nil { @@ -346,7 +348,7 @@ func (w *Worker) getXpubData(xpub string, page int, txsOnPage int, option GetAdd } // GetXpubAddress computes address value and gets transactions for given address -func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option GetAddressOption, filter *AddressFilter, gap int) (*Address, error) { +func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option AccountDetails, filter *AddressFilter, gap int) (*Address, error) { start := time.Now() page-- if page < 0 { @@ -419,9 +421,9 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Get uBalSat.Add(&uBalSat, tx.getAddrVoutValue(ad.addrDesc)) uBalSat.Sub(&uBalSat, tx.getAddrVinValue(ad.addrDesc)) if page == 0 && !foundTx && (useTxids == nil || useTxids(&txid, ad)) { - if option == TxidHistory { + if option == AccountDetailsTxidHistory { txids = append(txids, tx.Txid) - } else if option >= TxHistoryLight { + } else if option >= AccountDetailsTxHistoryLight { txs = append(txs, tx) } } @@ -431,7 +433,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Get } } } - if option >= TxidHistory { + if option >= AccountDetailsTxidHistory { txcMap := make(map[string]bool) txc = make(xpubTxids, 0, 32) for _, da := range [][]xpubAddress{data.addresses, data.changeAddresses} { @@ -472,7 +474,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Get // get confirmed transactions for i := from; i < to; i++ { xpubTxid := &txc[i] - if option == TxidHistory { + if option == AccountDetailsTxidHistory { txids = append(txids, xpubTxid.txid) } else { tx, err := w.txFromTxid(xpubTxid.txid, bestheight, option) @@ -488,7 +490,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Get totalTokens := 0 var tokens []Token var xpubAddresses map[string]struct{} - if option != Basic { + if option > AccountDetailsBasic { tokens = make([]Token, 0, 4) xpubAddresses = make(map[string]struct{}) } @@ -498,8 +500,8 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Get if ad.balance != nil { totalTokens++ } - if option != Basic { - token := w.tokenFromXpubAddress(data, ad, ci, i) + 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) { @@ -533,7 +535,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Get // GetXpubUtxo returns unspent outputs for given xpub func (w *Worker) GetXpubUtxo(xpub string, onlyConfirmed bool, gap int) (Utxos, error) { start := time.Now() - data, _, err := w.getXpubData(xpub, 0, 1, Basic, &AddressFilter{ + data, _, err := w.getXpubData(xpub, 0, 1, AccountDetailsBasic, &AddressFilter{ Vout: AddressFilterVoutOff, OnlyConfirmed: onlyConfirmed, }, gap) @@ -556,7 +558,7 @@ func (w *Worker) GetXpubUtxo(xpub string, onlyConfirmed bool, gap int) (Utxos, e return nil, err } if len(utxos) > 0 { - t := w.tokenFromXpubAddress(data, ad, ci, i) + t := w.tokenFromXpubAddress(data, ad, ci, i, AccountDetailsTokens) for j := range utxos { a := &utxos[j] a.Address = t.Name diff --git a/server/public.go b/server/public.go index 40376e33..3b21a4ff 100644 --- a/server/public.go +++ b/server/public.go @@ -590,7 +590,7 @@ func (s *PublicServer) explorerAddress(w http.ResponseWriter, r *http.Request) ( } } } - address, err = s.api.GetAddress(r.URL.Path[i+1:], page, txsOnPage, api.TxHistoryLight, &api.AddressFilter{Vout: fn}) + address, err = s.api.GetAddress(r.URL.Path[i+1:], page, txsOnPage, api.AccountDetailsTxHistoryLight, &api.AddressFilter{Vout: fn}) if err != nil { return errorTpl, nil, err } @@ -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.GetAddressOption) (*api.Address, api.TokenDetailLevel, error) { +func (s *PublicServer) getXpubAddress(r *http.Request, xpub string, pageSize int, option api.AccountDetails) (*api.Address, api.TokenDetailLevel, error) { var fn = api.AddressFilterVoutOff page, ec := strconv.Atoi(r.URL.Query().Get("page")) if ec != nil { @@ -650,7 +650,7 @@ func (s *PublicServer) explorerXpub(w http.ResponseWriter, r *http.Request) (tpl 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.TxHistoryLight) + address, tokenLevel, err = s.getXpubAddress(r, r.URL.Path[i+1:], txsOnPage, api.AccountDetailsTxHistoryLight) if err != nil { return errorTpl, nil, err } @@ -730,7 +730,7 @@ func (s *PublicServer) explorerSearch(w http.ResponseWriter, r *http.Request) (t var err error s.metrics.ExplorerViews.With(common.Labels{"action": "search"}).Inc() if len(q) > 0 { - address, err = s.api.GetXpubAddress(q, 0, 1, api.Basic, &api.AddressFilter{Vout: api.AddressFilterVoutOff}, 0) + address, err = s.api.GetXpubAddress(q, 0, 1, api.AccountDetailsBasic, &api.AddressFilter{Vout: api.AddressFilterVoutOff}, 0) if err == nil { http.Redirect(w, r, joinURL("/xpub/", address.AddrStr), 302) return noTpl, nil, nil @@ -745,7 +745,7 @@ func (s *PublicServer) explorerSearch(w http.ResponseWriter, r *http.Request) (t http.Redirect(w, r, joinURL("/tx/", tx.Txid), 302) return noTpl, nil, nil } - address, err = s.api.GetAddress(q, 0, 1, api.Basic, &api.AddressFilter{Vout: api.AddressFilterVoutOff}) + address, err = s.api.GetAddress(q, 0, 1, api.AccountDetailsBasic, &api.AddressFilter{Vout: api.AddressFilterVoutOff}) if err == nil { http.Redirect(w, r, joinURL("/address/", address.AddrStr), 302) return noTpl, nil, nil @@ -907,7 +907,7 @@ func (s *PublicServer) apiAddress(r *http.Request, apiVersion int) (interface{}, if ec != nil { page = 0 } - address, err = s.api.GetAddress(r.URL.Path[i+1:], page, txsInAPI, api.TxidHistory, &api.AddressFilter{Vout: api.AddressFilterVoutOff}) + address, err = s.api.GetAddress(r.URL.Path[i+1:], page, txsInAPI, api.AccountDetailsTxidHistory, &api.AddressFilter{Vout: api.AddressFilterVoutOff}) if err == nil && apiVersion == apiV1 { return s.api.AddressToV1(address), nil } @@ -920,7 +920,7 @@ func (s *PublicServer) apiXpub(r *http.Request, apiVersion int) (interface{}, er var err error s.metrics.ExplorerViews.With(common.Labels{"action": "api-xpub"}).Inc() if i := strings.LastIndexByte(r.URL.Path, '/'); i > 0 { - address, _, err = s.getXpubAddress(r, r.URL.Path[i+1:], txsInAPI, api.TxidHistory) + address, _, err = s.getXpubAddress(r, r.URL.Path[i+1:], txsInAPI, api.AccountDetailsTxidHistory) if err == nil && apiVersion == apiV1 { return s.api.AddressToV1(address), nil } diff --git a/server/websocket.go b/server/websocket.go index aa1d746e..e71b0c7e 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -347,16 +347,18 @@ func unmarshalGetAccountInfoRequest(params []byte) (*accountInfoReq, error) { } func (s *WebsocketServer) getAccountInfo(req *accountInfoReq) (res *api.Address, err error) { - var opt api.GetAddressOption + var opt api.AccountDetails switch req.Details { case "tokens": - opt = api.Tokens + opt = api.AccountDetailsTokens + case "tokenBalances": + opt = api.AccountDetailsTokenBalances case "txids": - opt = api.TxidHistory + opt = api.AccountDetailsTxidHistory case "txs": - opt = api.TxHistory + opt = api.AccountDetailsTxHistory default: - opt = api.Basic + opt = api.AccountDetailsBasic } filter := api.AddressFilter{ FromHeight: uint32(req.FromHeight), diff --git a/static/test-websocket.html b/static/test-websocket.html index ed28c05f..37274254 100644 --- a/static/test-websocket.html +++ b/static/test-websocket.html @@ -305,6 +305,7 @@