diff --git a/core/src/main/java/bisq/core/dao/state/DaoStateService.java b/core/src/main/java/bisq/core/dao/state/DaoStateService.java index 947b6c9adf..3f4ee711c2 100644 --- a/core/src/main/java/bisq/core/dao/state/DaoStateService.java +++ b/core/src/main/java/bisq/core/dao/state/DaoStateService.java @@ -46,9 +46,11 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.TreeMap; @@ -75,6 +77,7 @@ public class DaoStateService implements DaoSetupService { @Getter private boolean parseBlockChainComplete; private boolean allowDaoStateChange; + private final Map> cachedTxIdSetByAddress = new HashMap<>(); /////////////////////////////////////////////////////////////////////////////////////////// @@ -299,6 +302,10 @@ public void onParseBlockComplete(Block block) { // generate a hash of the state. allowDaoStateChange = false; daoStateListeners.forEach(l -> l.onDaoStateChanged(block)); + + if (!block.getTxs().isEmpty()) { + cachedTxIdSetByAddress.clear(); + } } // Called after parsing of all pending blocks is completed @@ -1031,6 +1038,52 @@ public Optional getSpentInfo(TxOutput txOutput) { } + /////////////////////////////////////////////////////////////////////////////////////////// + // Addresses + /////////////////////////////////////////////////////////////////////////////////////////// + + public Map> getTxIdSetByAddress() { + // We clear it at each new (non-empty) block, so it gets recreated + if (!cachedTxIdSetByAddress.isEmpty()) { + return cachedTxIdSetByAddress; + } + + Map txIdByConnectedTxOutputKey = new HashMap<>(); + // Add tx ids and addresses from tx outputs + getUnorderedTxStream() + .forEach(tx -> { + tx.getTxOutputs().stream() + .filter(this::isBsqTxOutputType) + .filter(txOutput -> txOutput.getAddress() != null) + .forEach(txOutput -> { + String address = txOutput.getAddress(); + Set txIdSet = cachedTxIdSetByAddress.getOrDefault(address, new HashSet<>()); + String txId = tx.getId(); + txIdSet.add(txId); + cachedTxIdSetByAddress.put(address, txIdSet); + tx.getTxInputs().forEach(txInput -> { + txIdByConnectedTxOutputKey.put(txInput.getConnectedTxOutputKey(), txId); + }); + }); + }); + + // Add tx ids and addresses from connected outputs (inputs) + getUnorderedTxOutputStream() + .filter(this::isBsqTxOutputType) + .filter(txOutput -> txOutput.getAddress() != null) + .forEach(txOutput -> { + String txId = txIdByConnectedTxOutputKey.get(txOutput.getKey()); + if (txId != null) { + String address = txOutput.getAddress(); + Set txIdSet = cachedTxIdSetByAddress.getOrDefault(address, new HashSet<>()); + txIdSet.add(txId); + cachedTxIdSetByAddress.put(address, txIdSet); + } + }); + + return cachedTxIdSetByAddress; + } + /////////////////////////////////////////////////////////////////////////////////////////// // Vote result data /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/restapi/src/main/java/bisq/restapi/BlockDataToJsonConverter.java b/restapi/src/main/java/bisq/restapi/BlockDataToJsonConverter.java new file mode 100644 index 0000000000..8356fdb787 --- /dev/null +++ b/restapi/src/main/java/bisq/restapi/BlockDataToJsonConverter.java @@ -0,0 +1,128 @@ +package bisq.restapi; + +import bisq.core.dao.state.DaoStateService; +import bisq.core.dao.state.model.blockchain.Block; +import bisq.core.dao.state.model.blockchain.PubKeyScript; +import bisq.core.dao.state.model.blockchain.Tx; +import bisq.core.dao.state.model.blockchain.TxOutput; +import bisq.core.dao.state.model.blockchain.TxType; + +import com.google.common.io.BaseEncoding; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import lombok.extern.slf4j.Slf4j; + + + +import bisq.restapi.dto.JsonBlock; +import bisq.restapi.dto.JsonScriptPubKey; +import bisq.restapi.dto.JsonSpentInfo; +import bisq.restapi.dto.JsonTx; +import bisq.restapi.dto.JsonTxInput; +import bisq.restapi.dto.JsonTxOutput; +import bisq.restapi.dto.JsonTxOutputType; +import bisq.restapi.dto.JsonTxType; + +@Slf4j +public class BlockDataToJsonConverter { + public static JsonBlock getJsonBlock(DaoStateService daoStateService, Block block) { + List jsonTxs = block.getTxs().stream() + .map(tx -> getJsonTx(daoStateService, tx)) + .collect(Collectors.toList()); + return new JsonBlock(block.getHeight(), + block.getTime(), + block.getHash(), + block.getPreviousBlockHash(), + jsonTxs); + } + + public static JsonTx getJsonTx(DaoStateService daoStateService, Tx tx) { + JsonTxType jsonTxType = getJsonTxType(tx); + String jsonTxTypeDisplayString = getJsonTxTypeDisplayString(jsonTxType); + return new JsonTx(tx.getId(), + tx.getBlockHeight(), + tx.getBlockHash(), + tx.getTime(), + getJsonTxInputs(daoStateService, tx), + getJsonTxOutputs(daoStateService, tx), + jsonTxType, + jsonTxTypeDisplayString, + tx.getBurntFee(), + tx.getInvalidatedBsq(), + tx.getUnlockBlockHeight()); + } + + private static List getJsonTxInputs(DaoStateService daoStateService, Tx tx) { + return tx.getTxInputs().stream() + .map(txInput -> { + Optional optionalTxOutput = daoStateService.getConnectedTxOutput(txInput); + if (optionalTxOutput.isPresent()) { + TxOutput connectedTxOutput = optionalTxOutput.get(); + boolean isBsqTxOutputType = daoStateService.isBsqTxOutputType(connectedTxOutput); + return new JsonTxInput(txInput.getConnectedTxOutputIndex(), + txInput.getConnectedTxOutputTxId(), + connectedTxOutput.getValue(), + isBsqTxOutputType, + connectedTxOutput.getAddress(), + tx.getTime()); + } else { + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + private static List getJsonTxOutputs(DaoStateService daoStateService, Tx tx) { + JsonTxType jsonTxType = getJsonTxType(tx); + String jsonTxTypeDisplayString = getJsonTxTypeDisplayString(jsonTxType); + return tx.getTxOutputs().stream() + .map(txOutput -> { + boolean isBsqTxOutputType = daoStateService.isBsqTxOutputType(txOutput); + long bsqAmount = isBsqTxOutputType ? txOutput.getValue() : 0; + long btcAmount = !isBsqTxOutputType ? txOutput.getValue() : 0; + PubKeyScript pubKeyScript = txOutput.getPubKeyScript(); + JsonScriptPubKey scriptPubKey = pubKeyScript != null ? new JsonScriptPubKey(pubKeyScript) : null; + JsonSpentInfo spentInfo = daoStateService.getSpentInfo(txOutput).map(JsonSpentInfo::new).orElse(null); + JsonTxOutputType txOutputType = JsonTxOutputType.valueOf(txOutput.getTxOutputType().name()); + int lockTime = txOutput.getLockTime(); + BaseEncoding HEX = BaseEncoding.base16().lowerCase(); + String opReturn = txOutput.getOpReturnData() != null ? HEX.encode(txOutput.getOpReturnData()) : null; + boolean isUnspent = daoStateService.isUnspent(txOutput.getKey()); + return new JsonTxOutput(tx.getId(), + txOutput.getIndex(), + bsqAmount, + btcAmount, + tx.getBlockHeight(), + isBsqTxOutputType, + tx.getBurntFee(), + tx.getInvalidatedBsq(), + txOutput.getAddress(), + scriptPubKey, + spentInfo, + tx.getTime(), + jsonTxType, + jsonTxTypeDisplayString, + txOutputType, + txOutputType.getDisplayString(), + opReturn, + lockTime, + isUnspent + ); + }) + .collect(Collectors.toList()); + } + + private static String getJsonTxTypeDisplayString(JsonTxType jsonTxType) { + return jsonTxType != null ? jsonTxType.getDisplayString() : ""; + } + + private static JsonTxType getJsonTxType(Tx tx) { + TxType txType = tx.getTxType(); + return txType != null ? JsonTxType.valueOf(txType.name()) : null; + } +} diff --git a/restapi/src/main/java/bisq/restapi/DaoExplorerService.java b/restapi/src/main/java/bisq/restapi/DaoExplorerService.java deleted file mode 100644 index 3c99e9ce43..0000000000 --- a/restapi/src/main/java/bisq/restapi/DaoExplorerService.java +++ /dev/null @@ -1,261 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.restapi; - -import bisq.core.dao.DaoFacade; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.model.blockchain.PubKeyScript; -import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.dao.state.model.blockchain.TxOutput; -import bisq.core.dao.state.model.blockchain.TxOutputKey; -import bisq.core.dao.state.model.blockchain.TxType; -import bisq.core.provider.price.PriceFeedService; - -import bisq.network.p2p.P2PService; -import bisq.network.p2p.P2PServiceListener; - -import bisq.common.UserThread; - -import com.google.inject.Inject; - -import com.google.common.io.BaseEncoding; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import lombok.Getter; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; - - - -import bisq.restapi.dto.JsonBlock; -import bisq.restapi.dto.JsonScriptPubKey; -import bisq.restapi.dto.JsonSpentInfo; -import bisq.restapi.dto.JsonTx; -import bisq.restapi.dto.JsonTxInput; -import bisq.restapi.dto.JsonTxOutput; -import bisq.restapi.dto.JsonTxOutputType; -import bisq.restapi.dto.JsonTxType; - -@Getter -@Slf4j -public class DaoExplorerService { - private final DaoStateService daoStateService; - private final DaoFacade daoFacade; - private final DaoStateListener daoStateListener; - private final Map> txIdsByAddress = new HashMap<>(); - @Setter - private int lastKnownBlockHeight = 0; - - @Inject - public DaoExplorerService(DaoStateService daoStateService, - DaoFacade daoFacade, - P2PService p2PService, - PriceFeedService priceFeedService) { - this.daoStateService = daoStateService; - this.daoFacade = daoFacade; - daoStateListener = new DaoStateListener() { - @Override - public void onParseBlockChainComplete() { - UserThread.execute(() -> updateTxIdsByAddress()); - } - - @Override - public void onDaoStateChanged(Block block) { - UserThread.execute(() -> updateTxIdsByAddress()); - } - - }; - daoFacade.addBsqStateListener(daoStateListener); - - p2PService.addP2PServiceListener(new P2PServiceListener() { - @Override - public void onDataReceived() { - } - - @Override - public void onNoSeedNodeAvailable() { - } - - @Override - public void onNoPeersAvailable() { - } - - @Override - public void onUpdatedDataReceived() { - } - - @Override - public void onTorNodeReady() { - // We want to get early connected to the price relay so we call it already now - priceFeedService.setCurrencyCodeOnInit(); - priceFeedService.initialRequestPriceFeed(); - } - - @Override - public void onHiddenServicePublished() { - } - }); - } - - public void updateTxIdsByAddress() { - Map txIdByTxOutputKey = new HashMap<>(); - txIdsByAddress.clear(); - daoStateService.getUnorderedTxStream() - .forEach(tx -> { - tx.getTxOutputs().forEach(txOutput -> { - String address = txOutput.getAddress(); - if (address != null && !address.isEmpty() && daoStateService.isBsqTxOutputType(txOutput)) { - Set txIdSet = txIdsByAddress.getOrDefault(address, new HashSet<>()); - String txId = tx.getId(); - txIdSet.add(txId); - txIdsByAddress.put(address, txIdSet); - tx.getTxInputs().forEach(txInput -> { - txIdByTxOutputKey.put(txInput.getConnectedTxOutputKey(), txId); - }); - } - }); - }); - log.info("txIdByTxOutputKey {}", txIdByTxOutputKey.size()); - // todo check if needed - daoStateService.getUnorderedTxOutputStream() - .filter(daoStateService::isBsqTxOutputType) - .filter(txOutput -> Objects.nonNull(txOutput.getAddress())) - .forEach(txOutput -> { - String txId = txIdByTxOutputKey.get(txOutput.getKey()); - if (txId != null) { - String address = txOutput.getAddress(); - Set txIdSet = txIdsByAddress.getOrDefault(address, new HashSet<>()); - txIdSet.add(txId); - txIdsByAddress.put(address, txIdSet); - } - }); - - log.info("result txIdByTxOutputKey {}", txIdByTxOutputKey.size()); - } - - public JsonBlock getJsonBlock(Block block) { - List jsonTxs = block.getTxs().stream() - .map(this::getJsonTx) - .collect(Collectors.toList()); - return new JsonBlock(block.getHeight(), - block.getTime(), - block.getHash(), - block.getPreviousBlockHash(), - jsonTxs); - } - - public JsonTx getJsonTx(Tx tx) { - JsonTxType jsonTxType = getJsonTxType(tx); - String jsonTxTypeDisplayString = getJsonTxTypeDisplayString(jsonTxType); - return new JsonTx(tx.getId(), - tx.getBlockHeight(), - tx.getBlockHash(), - tx.getTime(), - getJsonTxInputs(tx), - getJsonTxOutputs(tx), - jsonTxType, - jsonTxTypeDisplayString, - tx.getBurntFee(), - tx.getInvalidatedBsq(), - tx.getUnlockBlockHeight()); - } - - public int getNumAddresses() { - return txIdsByAddress.size(); - } - - private List getJsonTxInputs(Tx tx) { - return tx.getTxInputs().stream() - .map(txInput -> { - Optional optionalTxOutput = daoStateService.getConnectedTxOutput(txInput); - if (optionalTxOutput.isPresent()) { - TxOutput connectedTxOutput = optionalTxOutput.get(); - boolean isBsqTxOutputType = daoStateService.isBsqTxOutputType(connectedTxOutput); - return new JsonTxInput(txInput.getConnectedTxOutputIndex(), - txInput.getConnectedTxOutputTxId(), - connectedTxOutput.getValue(), - isBsqTxOutputType, - connectedTxOutput.getAddress(), - tx.getTime()); - } else { - return null; - } - }) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - } - - private List getJsonTxOutputs(Tx tx) { - JsonTxType jsonTxType = getJsonTxType(tx); - String jsonTxTypeDisplayString = getJsonTxTypeDisplayString(jsonTxType); - return tx.getTxOutputs().stream() - .map(txOutput -> { - boolean isBsqTxOutputType = daoStateService.isBsqTxOutputType(txOutput); - long bsqAmount = isBsqTxOutputType ? txOutput.getValue() : 0; - long btcAmount = !isBsqTxOutputType ? txOutput.getValue() : 0; - PubKeyScript pubKeyScript = txOutput.getPubKeyScript(); - JsonScriptPubKey scriptPubKey = pubKeyScript != null ? new JsonScriptPubKey(pubKeyScript) : null; - JsonSpentInfo spentInfo = daoStateService.getSpentInfo(txOutput).map(JsonSpentInfo::new).orElse(null); - JsonTxOutputType txOutputType = JsonTxOutputType.valueOf(txOutput.getTxOutputType().name()); - int lockTime = txOutput.getLockTime(); - BaseEncoding HEX = BaseEncoding.base16().lowerCase(); - String opReturn = txOutput.getOpReturnData() != null ? HEX.encode(txOutput.getOpReturnData()) : null; - boolean isUnspent = daoStateService.isUnspent(txOutput.getKey()); - return new JsonTxOutput(tx.getId(), - txOutput.getIndex(), - bsqAmount, - btcAmount, - tx.getBlockHeight(), - isBsqTxOutputType, - tx.getBurntFee(), - tx.getInvalidatedBsq(), - txOutput.getAddress(), - scriptPubKey, - spentInfo, - tx.getTime(), - jsonTxType, - jsonTxTypeDisplayString, - txOutputType, - txOutputType.getDisplayString(), - opReturn, - lockTime, - isUnspent - ); - }) - .collect(Collectors.toList()); - } - - private String getJsonTxTypeDisplayString(JsonTxType jsonTxType) { - return jsonTxType != null ? jsonTxType.getDisplayString() : ""; - } - - private JsonTxType getJsonTxType(Tx tx) { - TxType txType = tx.getTxType(); - return txType != null ? JsonTxType.valueOf(txType.name()) : null; - } -} diff --git a/restapi/src/main/java/bisq/restapi/RestApi.java b/restapi/src/main/java/bisq/restapi/RestApi.java index 96d2cdad36..e992b6cca2 100644 --- a/restapi/src/main/java/bisq/restapi/RestApi.java +++ b/restapi/src/main/java/bisq/restapi/RestApi.java @@ -29,6 +29,7 @@ import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.DaoStateSnapshotService; import bisq.core.offer.OfferBookService; +import bisq.core.provider.price.PriceFeedService; import bisq.core.trade.statistics.TradeStatisticsManager; import bisq.core.user.Preferences; @@ -53,8 +54,6 @@ public class RestApi extends ExecutableForAppWithP2p { private DaoStateSnapshotService daoStateSnapshotService; private Preferences preferences; @Getter - private DaoExplorerService daoExplorerService; - @Getter private DaoFacade daoFacade; @Getter private ProposalService proposalService; @@ -64,6 +63,7 @@ public class RestApi extends ExecutableForAppWithP2p { private TradeStatisticsManager tradeStatisticsManager; @Getter private OfferBookService offerBookService; + private PriceFeedService priceFeedService; public RestApi() { super("Bisq Rest Api", "bisq_restapi", "bisq_restapi", Version.VERSION); @@ -91,12 +91,12 @@ protected void applyInjector() { bondedRolesRepository = injector.getInstance(BondedRolesRepository.class); signVerifyService = injector.getInstance(SignVerifyService.class); daoStateSnapshotService = injector.getInstance(DaoStateSnapshotService.class); - daoExplorerService = injector.getInstance(DaoExplorerService.class); daoFacade = injector.getInstance(DaoFacade.class); proposalService = injector.getInstance(ProposalService.class); cycleService = injector.getInstance(CycleService.class); tradeStatisticsManager = injector.getInstance(TradeStatisticsManager.class); offerBookService = injector.getInstance(OfferBookService.class); + priceFeedService = injector.getInstance(PriceFeedService.class); } @Override @@ -111,5 +111,7 @@ protected void onHiddenServicePublished() { super.onHiddenServicePublished(); accountAgeWitnessService.onAllServicesInitialized(); + priceFeedService.setCurrencyCodeOnInit(); + priceFeedService.initialRequestPriceFeed(); } } diff --git a/restapi/src/main/java/bisq/restapi/endpoints/AccountAgeApi.java b/restapi/src/main/java/bisq/restapi/endpoints/AccountAgeApi.java index 0aaed70d04..7b4908f4b8 100644 --- a/restapi/src/main/java/bisq/restapi/endpoints/AccountAgeApi.java +++ b/restapi/src/main/java/bisq/restapi/endpoints/AccountAgeApi.java @@ -56,11 +56,10 @@ @Tag(name = "Account age API") public class AccountAgeApi { private static final String DESC_HASH = "The hash of the account age witness as hex string"; - private final RestApi restApi; private final AccountAgeWitnessService accountAgeWitnessService; public AccountAgeApi(@Context Application application) { - restApi = ((RestApiMain) application).getRestApi(); + RestApi restApi = ((RestApiMain) application).getRestApi(); accountAgeWitnessService = checkNotNull(restApi.getAccountAgeWitnessService()); } diff --git a/restapi/src/main/java/bisq/restapi/endpoints/ExplorerBlocksApi.java b/restapi/src/main/java/bisq/restapi/endpoints/ExplorerBlocksApi.java index 2a0f2c5b76..f7b159698e 100644 --- a/restapi/src/main/java/bisq/restapi/endpoints/ExplorerBlocksApi.java +++ b/restapi/src/main/java/bisq/restapi/endpoints/ExplorerBlocksApi.java @@ -1,5 +1,6 @@ package bisq.restapi.endpoints; +import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.model.blockchain.Block; import java.util.List; @@ -11,7 +12,7 @@ -import bisq.restapi.DaoExplorerService; +import bisq.restapi.BlockDataToJsonConverter; import bisq.restapi.RestApi; import bisq.restapi.RestApiMain; import bisq.restapi.dto.JsonBlock; @@ -34,12 +35,11 @@ @Produces(MediaType.APPLICATION_JSON) @Tag(name = "BLOCKS API") public class ExplorerBlocksApi { - private final DaoExplorerService daoExplorerService; - private final RestApi restApi; + private final DaoStateService daoStateService; public ExplorerBlocksApi(@Context Application application) { - restApi = ((RestApiMain) application).getRestApi(); - daoExplorerService = restApi.getDaoExplorerService(); + RestApi restApi = ((RestApiMain) application).getRestApi(); + daoStateService = restApi.getDaoStateService(); } // http://localhost:8081/api/v1/explorer/blocks/get-bsq-block-by-height/139 @@ -51,10 +51,10 @@ public ExplorerBlocksApi(@Context Application application) { @GET @Path("get-bsq-block-by-height/{block-height}") public JsonBlock getBsqBlockByHeight(@Parameter(description = "Block Height") @PathParam("block-height") int blockHeight) { - List blocks = restApi.getDaoStateService().getBlocks(); + List blocks = daoStateService.getBlocks(); Optional jsonBlock = checkNotNull(blocks.stream()) .filter(block -> block.getHeight() == blockHeight) - .map(this::getJsonBlock) + .map(block -> BlockDataToJsonConverter.getJsonBlock(daoStateService, block)) .findFirst(); if (jsonBlock.isPresent()) { log.info("supplying block at height {} to client.", blockHeight); @@ -68,10 +68,10 @@ public JsonBlock getBsqBlockByHeight(@Parameter(description = "Block Height") @P @GET @Path("get-bsq-block-by-hash/{block-hash}") public JsonBlock getBsqBlockByHash(@Parameter(description = "Block Hash") @PathParam("block-hash") String hash) { - List blocks = restApi.getDaoStateService().getBlocks(); + List blocks = daoStateService.getBlocks(); Optional jsonBlock = checkNotNull(blocks.stream()) .filter(block -> block.getHash().equalsIgnoreCase(hash)) - .map(this::getJsonBlock) + .map(block -> BlockDataToJsonConverter.getJsonBlock(daoStateService, block)) .findFirst(); if (jsonBlock.isPresent()) { log.info("supplying block {} to client.", hash); @@ -80,8 +80,4 @@ public JsonBlock getBsqBlockByHash(@Parameter(description = "Block Hash") @PathP log.warn("block {} not found!", hash); return null; } - - private JsonBlock getJsonBlock(Block block) { - return daoExplorerService.getJsonBlock(block); - } } diff --git a/restapi/src/main/java/bisq/restapi/endpoints/ExplorerDaoApi.java b/restapi/src/main/java/bisq/restapi/endpoints/ExplorerDaoApi.java index 0c0869d45c..662a9b4e03 100644 --- a/restapi/src/main/java/bisq/restapi/endpoints/ExplorerDaoApi.java +++ b/restapi/src/main/java/bisq/restapi/endpoints/ExplorerDaoApi.java @@ -20,7 +20,6 @@ -import bisq.restapi.DaoExplorerService; import bisq.restapi.RestApi; import bisq.restapi.RestApiMain; import bisq.restapi.dto.BsqStatsDto; @@ -38,27 +37,23 @@ @Produces(MediaType.APPLICATION_JSON) @Tag(name = "EXPLORER API") public class ExplorerDaoApi { - private final RestApi restApi; private final DaoStateService daoStateService; - private final DaoExplorerService daoExplorerService; + private final DaoFacade daoFacade; + private final ProposalService proposalService; + private final CycleService cycleService; public ExplorerDaoApi(@Context Application application) { - restApi = ((RestApiMain) application).getRestApi(); + RestApi restApi = ((RestApiMain) application).getRestApi(); daoStateService = restApi.getDaoStateService(); - daoExplorerService = restApi.getDaoExplorerService(); + proposalService = restApi.getProposalService(); + cycleService = restApi.getCycleService(); + daoFacade = restApi.getDaoFacade(); } //http://localhost:8081/api/v1/explorer/dao/get-bsq-stats @GET @Path("get-bsq-stats") public BsqStatsDto getBsqStats() { - DaoFacade daoFacade = restApi.getDaoFacade(); - if (daoExplorerService.getLastKnownBlockHeight() != daoFacade.getChainHeight()) { - log.info("we recalculate the BSQ address map {} / {}", daoExplorerService.getLastKnownBlockHeight(), daoFacade.getChainHeight()); - daoExplorerService.updateTxIdsByAddress(); - daoExplorerService.setLastKnownBlockHeight(daoFacade.getChainHeight()); - } - DaoStateService daoStateService = restApi.getDaoStateService(); long genesisSupply = daoFacade.getGenesisTotalSupply().getValue(); long issuedByCompensations = daoStateService.getIssuanceSetForType(IssuanceType.COMPENSATION).stream().mapToLong(Issuance::getAmount).sum(); long issuedByReimbursements = daoStateService.getIssuanceSetForType(IssuanceType.REIMBURSEMENT).stream().mapToLong(Issuance::getAmount).sum(); @@ -66,10 +61,10 @@ public BsqStatsDto getBsqStats() { long burnt = daoStateService.getTotalAmountOfBurntBsq(); int unspentTxos = daoStateService.getUnspentTxOutputMap().size(); int spentTxos = daoStateService.getSpentInfoMap().size(); - int numAddresses = daoExplorerService.getNumAddresses(); - log.info("client requested BSQ stats, height={}", daoExplorerService.getLastKnownBlockHeight()); + int numAddresses = daoStateService.getTxIdSetByAddress().size(); + log.info("client requested BSQ stats, height={}", daoFacade.getChainHeight()); return new BsqStatsDto(minted, burnt, numAddresses, unspentTxos, spentTxos, - daoExplorerService.getLastKnownBlockHeight(), daoFacade.getGenesisBlockHeight()); + daoFacade.getChainHeight(), daoFacade.getGenesisBlockHeight()); } @GET @@ -77,9 +72,6 @@ public BsqStatsDto getBsqStats() { public List queryDaoCycles() { Set cyclesAdded = new HashSet<>(); List result = new ArrayList<>(); - ProposalService proposalService = restApi.getProposalService(); - CycleService cycleService = restApi.getCycleService(); - DaoFacade daoFacade = restApi.getDaoFacade(); // Creating our data structure is a bit expensive so we ensure to only create the CycleListItems once. daoStateService.getCycles().stream() .filter(cycle -> !cyclesAdded.contains(cycle.getHeightOfFirstBlock())) diff --git a/restapi/src/main/java/bisq/restapi/endpoints/ExplorerMarketsApi.java b/restapi/src/main/java/bisq/restapi/endpoints/ExplorerMarketsApi.java index c7628f4dc1..8211ebf12a 100644 --- a/restapi/src/main/java/bisq/restapi/endpoints/ExplorerMarketsApi.java +++ b/restapi/src/main/java/bisq/restapi/endpoints/ExplorerMarketsApi.java @@ -2,6 +2,7 @@ import bisq.core.locale.CurrencyUtil; import bisq.core.locale.Res; +import bisq.core.offer.OfferBookService; import bisq.core.trade.statistics.TradeStatistics3; import bisq.core.trade.statistics.TradeStatisticsManager; @@ -39,10 +40,13 @@ public class ExplorerMarketsApi { private static final long MONTH = TimeUnit.DAYS.toMillis(30); - private final RestApi restApi; + private final OfferBookService offerBookService; + private final TradeStatisticsManager tradeStatisticsManager; public ExplorerMarketsApi(@Context Application application) { - restApi = ((RestApiMain) application).getRestApi(); + RestApi restApi = ((RestApiMain) application).getRestApi(); + offerBookService = restApi.getOfferBookService(); + tradeStatisticsManager = restApi.getTradeStatisticsManager(); } // http://localhost:8081/api/v1/explorer/markets/get-currencies @@ -63,7 +67,7 @@ public List getBisqCurrencies() { @GET @Path("get-offers") public List getBisqOffers() { - List result = restApi.getOfferBookService().getOfferForJsonList().stream() + List result = offerBookService.getOfferForJsonList().stream() .map(offerForJson -> new JsonOffer( offerForJson.direction.name(), offerForJson.currencyCode, @@ -101,7 +105,6 @@ public List getBisqTrades(@PathParam("newestTimestamp") long newe long to = new Date().getTime(); long from = newestTimestamp > 0 ? newestTimestamp : to - MONTH; // 30 days default - TradeStatisticsManager tradeStatisticsManager = restApi.getTradeStatisticsManager(); ArrayList result = new ArrayList<>(); List tradeStatisticsList = tradeStatisticsManager.getTradeStatisticsList(from, to); log.info("requesting a fresh batch of trades {}", tradeStatisticsList.size()); diff --git a/restapi/src/main/java/bisq/restapi/endpoints/ExplorerTransactionsApi.java b/restapi/src/main/java/bisq/restapi/endpoints/ExplorerTransactionsApi.java index 719b57c7c0..2ba57bb0e7 100644 --- a/restapi/src/main/java/bisq/restapi/endpoints/ExplorerTransactionsApi.java +++ b/restapi/src/main/java/bisq/restapi/endpoints/ExplorerTransactionsApi.java @@ -17,6 +17,7 @@ package bisq.restapi.endpoints; +import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.model.blockchain.BaseTx; import bisq.core.dao.state.model.blockchain.Tx; import bisq.core.dao.state.model.blockchain.TxType; @@ -33,7 +34,7 @@ -import bisq.restapi.DaoExplorerService; +import bisq.restapi.BlockDataToJsonConverter; import bisq.restapi.RestApi; import bisq.restapi.RestApiMain; import bisq.restapi.dto.JsonTx; @@ -52,21 +53,20 @@ @Produces(MediaType.APPLICATION_JSON) @Tag(name = "TRANSACTIONS API") public class ExplorerTransactionsApi { - private final RestApi restApi; - private final DaoExplorerService daoExplorerService; + private final DaoStateService daoStateService; public ExplorerTransactionsApi(@Context Application application) { - restApi = ((RestApiMain) application).getRestApi(); - daoExplorerService = restApi.getDaoExplorerService(); + RestApi restApi = ((RestApiMain) application).getRestApi(); + daoStateService = restApi.getDaoStateService(); } @GET @Path("get-bsq-tx/{txid}") public JsonTx getTx(@Parameter(description = "TxId") @PathParam("txid") String txId) { - Optional jsonTx = restApi.getDaoStateService().getUnorderedTxStream() + Optional jsonTx = daoStateService.getUnorderedTxStream() .filter(t -> t.getId().equals(txId)) - .map(this::getJsonTx) + .map(tx -> BlockDataToJsonConverter.getJsonTx(daoStateService, tx)) .findFirst(); if (jsonTx.isPresent()) { log.info("supplying tx {} to client.", txId); @@ -79,12 +79,12 @@ public JsonTx getTx(@Parameter(description = "TxId") @GET @Path("get-bsq-tx-for-addr/{addr}") public List getBisqTxForAddr(@PathParam("addr") String address) { - Map> addressToTxIds = daoExplorerService.getTxIdsByAddress(); + Map> addressToTxIds = daoStateService.getTxIdSetByAddress(); List result = new ArrayList<>(); Set strings = addressToTxIds.get(address); strings.forEach(txId -> { - restApi.getDaoStateService().getTx(txId).stream() - .map(this::getJsonTx) + daoStateService.getTx(txId).stream() + .map(tx -> BlockDataToJsonConverter.getJsonTx(daoStateService, tx)) .forEach(result::add); }); log.info("getBisqTxForAddr: returning {} items.", result.size()); @@ -97,12 +97,12 @@ public List queryTxsPaginated(@PathParam("start") int start, @PathParam("count") int count, @PathParam("filters") String filters) { log.info("filters: {}", filters); - List jsonTxs = restApi.getDaoStateService().getUnorderedTxStream() + List jsonTxs = daoStateService.getUnorderedTxStream() .sorted(Comparator.comparing(BaseTx::getTime).reversed()) .filter(tx -> hasMatchingTxType(tx, filters)) .skip(start) .limit(count) - .map(this::getJsonTx) + .map(tx -> BlockDataToJsonConverter.getJsonTx(daoStateService, tx)) .collect(Collectors.toList()); log.info("supplying {} jsonTxs to client from index {}", jsonTxs.size(), start); return jsonTxs; @@ -127,7 +127,4 @@ private boolean hasMatchingTxType(Tx tx, String filters) { return false; } - private JsonTx getJsonTx(Tx tx) { - return daoExplorerService.getJsonTx(tx); - } }