package com.gravilink.zcash.request; import android.net.Uri; import android.util.Log; import com.gravilink.zcash.JSONParser; import com.gravilink.zcash.WalletCallback; import com.gravilink.zcash.ZCashException; import com.gravilink.zcash.ZCashTransactionDetails_taddr; import com.gravilink.zcash.crypto.Base58; import java.io.IOException; import java.net.HttpURLConnection; import java.util.LinkedList; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; import java.util.Vector; import javax.net.ssl.HttpsURLConnection; public class UpdateTransactionCache_taddr extends AbstractZCashRequest implements Runnable { Vector<ZCashTransactionDetails_taddr> cache; boolean rescan; String pubKey; WalletCallback<String, Void> callback; final static Object requestExistsMutex = new Object(); static boolean requestExist; static { requestExist = false; } public UpdateTransactionCache_taddr(Vector<ZCashTransactionDetails_taddr> cache, boolean rescan, String pubKey, WalletCallback<String, Void> callback) { this.cache = cache; this.rescan = rescan; this.pubKey = pubKey; this.callback = callback; } public static List<ZCashTransactionDetails_taddr> getReceivedTransactions(String pubKey, int limit, int offset) throws ZCashException { String uri = Uri.parse(explorerAddress).buildUpon() .appendEncodedPath("accounts") .appendEncodedPath(pubKey) .appendEncodedPath("recv") .appendQueryParameter("limit", String.valueOf(limit)) .appendQueryParameter("offset", String.valueOf(offset)) .build().toString(); HttpURLConnection conn = queryExplorerForConnection(uri); try { return JSONParser.parseTxArray(conn.getInputStream()); } catch (IOException e) { throw new ZCashException("Cannot parse response from explorer.", e); } } public static List<ZCashTransactionDetails_taddr> getSentTransactions(String pubKey, int limit, int offset) throws ZCashException { String uri = Uri.parse(explorerAddress).buildUpon() .appendEncodedPath("accounts") .appendEncodedPath(pubKey) .appendEncodedPath("sent") .appendQueryParameter("limit", String.valueOf(limit)) .appendQueryParameter("offset", String.valueOf(offset)) .build().toString(); HttpURLConnection conn = queryExplorerForConnection(uri); try { return JSONParser.parseTxArray(conn.getInputStream()); } catch (IOException e) { throw new ZCashException("Cannot parse response from explorer.", e); } } @Override public void run() { synchronized (requestExistsMutex) { if (requestExist) { callback.onResponse("Another UpdateTransacionCache_taddr request exists.", null); return; } else { requestExist = true; } } Vector<ZCashTransactionDetails_taddr> transactions = cache; if (transactions == null) { synchronized (requestExistsMutex) { requestExist = false; } callback.onResponse("Wallet is not imported.", null); return; } SortedSet<ZCashTransactionDetails_taddr> uniqueTransactions = new TreeSet<>(); long lastBlock = 0; if (transactions.isEmpty()) { rescan = true; } else { lastBlock = transactions.lastElement().blockHeight; } if (rescan) { synchronized (transactions) { transactions.clear(); } } try { getAllRecv(20, 0, rescan, lastBlock, uniqueTransactions); getAllSent(20, 0, rescan, lastBlock, uniqueTransactions); } catch (ZCashException e) { synchronized (requestExistsMutex) { requestExist = false; } callback.onResponse(e.getMessage(), null); return; } boolean initialized = ZCashTransactionDetails_taddr.prepareAfterParsing(uniqueTransactions); if (initialized) { transactions.addAll(uniqueTransactions); } else { synchronized (requestExistsMutex) { requestExist = false; } return; } synchronized (requestExistsMutex) { requestExist = false; } callback.onResponse("ok", null); } private void getAllSent(int limit, int offset, boolean rescan, long lastBlock, SortedSet<ZCashTransactionDetails_taddr> uniqueTransactions) throws ZCashException { List<ZCashTransactionDetails_taddr> nextPack; do { List<ZCashTransactionDetails_taddr> uncachedTransactions = new LinkedList<>(); nextPack = getSentTransactions(pubKey, limit, offset); //Log.i("UPDATE CACHE:", String.format("Downloaded %d transactions", nextPack.size())); if (!rescan) { boolean cachedAll = true; for (ZCashTransactionDetails_taddr details : nextPack) { boolean cached = details.blockHeight <= lastBlock; if (!cached) { uncachedTransactions.add(details); } cachedAll &= details.blockHeight <= lastBlock; } if (cachedAll) { break; } } else { uncachedTransactions = nextPack; } uniqueTransactions.addAll(uncachedTransactions); Log.i("UPDATE CACHE:", String.format("Downloaded %d transactions", uniqueTransactions.size())); offset += limit; } while (nextPack.size() == limit); } private void getAllRecv(int limit, int offset, boolean rescan, long lastBlock, SortedSet<ZCashTransactionDetails_taddr> uniqueTransactions) throws ZCashException { List<ZCashTransactionDetails_taddr> nextPack; do { List<ZCashTransactionDetails_taddr> uncachedTransactions = new LinkedList<>(); nextPack = getReceivedTransactions(pubKey, limit, offset); //Log.i("UPDATE CACHE:", String.format("Downloaded %d transactions", nextPack.size())); if (!rescan) { boolean cachedAll = true; for (ZCashTransactionDetails_taddr details : nextPack) { boolean cached = details.blockHeight <= lastBlock; if (!cached) { uncachedTransactions.add(details); } cachedAll &= details.blockHeight <= lastBlock; } if (cachedAll) { break; } } else { uncachedTransactions = nextPack; } uniqueTransactions.addAll(uncachedTransactions); Log.i("UPDATE CACHE:", String.format("Downloaded %d transactions", uniqueTransactions.size())); offset += limit; } while (nextPack.size() == limit); } }