From c5134e14c28663e16b07a1eaf1ab63e4428f1f5a Mon Sep 17 00:00:00 2001 From: Dominykas Mostauskis Date: Thu, 18 Jun 2020 15:00:23 +0200 Subject: [PATCH] Replace Tuple3 with memoization --- .../bisq/core/grpc/CoreWalletsService.java | 89 +++++++++++-------- 1 file changed, 53 insertions(+), 36 deletions(-) diff --git a/core/src/main/java/bisq/core/grpc/CoreWalletsService.java b/core/src/main/java/bisq/core/grpc/CoreWalletsService.java index be44122ab2e..26a64e50696 100644 --- a/core/src/main/java/bisq/core/grpc/CoreWalletsService.java +++ b/core/src/main/java/bisq/core/grpc/CoreWalletsService.java @@ -30,6 +30,10 @@ import javax.annotation.Nullable; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.LoadingCache; + import static java.lang.String.format; import static java.util.concurrent.TimeUnit.SECONDS; @@ -88,6 +92,20 @@ public String getAddressBalanceInfo(String addressString) { + ((numConfirmations > 0) ? (" confirmations: " + format("%6d", numConfirmations)) : ""); } + /** + * Memoization stores the results of expensive function calls and returns + * the cached result when the same input occurs again. + * + * Resulting LoadingCache is used by calling `.get(input I)` or + * `.getUnchecked(input I)`, depending on whether or not `f` can return null. + * That's because CacheLoader throws an exception on null output from `f`. + */ + private static LoadingCache memoize(Function f) { + // f::apply is used, because Guava 20.0 Function doesn't yet extend + // Java Function. + return CacheBuilder.newBuilder().build(CacheLoader.from(f::apply)); + } + public String getFundingAddresses() { if (!walletsManager.areWalletsAvailable()) throw new IllegalStateException("wallet is not yet available"); @@ -98,44 +116,43 @@ public String getFundingAddresses() { if (btcWalletService.getAvailableAddressEntries().size() == 0) btcWalletService.getFreshAddressEntry(); - // Populate a list of Tuple3 - List> addrBalanceConfirms = - btcWalletService.getAvailableAddressEntries().stream() - .map(a -> new Tuple3<>(a.getAddressString(), - getAddressBalance(a.getAddressString()), - getNumConfirmationsForMostRecentTransaction(a.getAddressString()))) - .collect(Collectors.toList()); - - // Check to see if at least one of the existing addresses has a zero balance. - boolean hasZeroBalance = false; - for (Tuple3 abc : addrBalanceConfirms) { - if (abc.second == 0) { - hasZeroBalance = true; - break; - } - } - if (!hasZeroBalance) { - // None of the existing addresses have a zero balance, create a new address. - addrBalanceConfirms.add( - new Tuple3<>(btcWalletService.getFreshAddressEntry().getAddressString(), - 0L, - 0)); + List addressStrings = + btcWalletService + .getAvailableAddressEntries() + .stream() + .map(addressEntry -> addressEntry.getAddressString()) + .collect(Collectors.toList()); + + // getAddressBalance is memoized, because we'll map it over addresses twice. + // To get the balances, we'll be using .getUnchecked, because we know that + // this::getAddressBalance cannot return null. + var balances = memoize(this::getAddressBalance); + + boolean noAddressHasZeroBalance = + addressStrings.stream() + .allMatch(addressString -> balances.getUnchecked(addressString) != 0); + + if (noAddressHasZeroBalance) { + var newZeroBalanceAddress = btcWalletService.getFreshAddressEntry(); + addressStrings.add(newZeroBalanceAddress.getAddressString()); } - // Iterate the list of Tuple3 objects - // and build the formatted info string. - StringBuilder addressInfoBuilder = new StringBuilder(); - addrBalanceConfirms.forEach(a -> { - var btcBalance = formatSatoshis.apply(a.second); - var numConfirmations = getNumConfirmationsForMostRecentTransaction(a.first); - String addressInfo = "" + a.first - + " balance: " + format("%13s", btcBalance) - + ((a.second > 0) ? (" confirmations: " + format("%6d", numConfirmations)) : "") - + "\n"; - addressInfoBuilder.append(addressInfo); - }); - - return addressInfoBuilder.toString().trim(); + String fundingAddressTable = + addressStrings.stream() + .map(addressString -> { + var balance = balances.getUnchecked(addressString); + var stringFormattedBalance = formatSatoshis.apply(balance); + var numConfirmations = + getNumConfirmationsForMostRecentTransaction(addressString); + String addressInfo = + "" + addressString + + " balance: " + format("%13s", stringFormattedBalance) + + ((balance > 0) ? (" confirmations: " + format("%6d", numConfirmations)) : ""); + return addressInfo; + }) + .collect(Collectors.joining("\n")); + + return fundingAddressTable; } public int getNumConfirmationsForMostRecentTransaction(String addressString) {