mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 01:41:11 +01:00
Add explorer endpoints and dtos
Signed-off-by: HenrikJannsen <boilingfrog@gmx.com>
This commit is contained in:
parent
9e0845e7f7
commit
bd9136fc97
261
restapi/src/main/java/bisq/restapi/DaoExplorerService.java
Normal file
261
restapi/src/main/java/bisq/restapi/DaoExplorerService.java
Normal file
@ -0,0 +1,261 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<String, Set<String>> 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<TxOutputKey, String> 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<String> 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<String> 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<JsonTx> 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<JsonTxInput> getJsonTxInputs(Tx tx) {
|
||||
return tx.getTxInputs().stream()
|
||||
.map(txInput -> {
|
||||
Optional<TxOutput> 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<JsonTxOutput> 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;
|
||||
}
|
||||
}
|
@ -20,11 +20,16 @@ package bisq.restapi;
|
||||
|
||||
import bisq.core.account.witness.AccountAgeWitnessService;
|
||||
import bisq.core.app.misc.ExecutableForAppWithP2p;
|
||||
import bisq.core.dao.DaoFacade;
|
||||
import bisq.core.dao.SignVerifyService;
|
||||
import bisq.core.dao.governance.bond.reputation.BondedReputationRepository;
|
||||
import bisq.core.dao.governance.bond.role.BondedRolesRepository;
|
||||
import bisq.core.dao.governance.period.CycleService;
|
||||
import bisq.core.dao.governance.proposal.ProposalService;
|
||||
import bisq.core.dao.state.DaoStateService;
|
||||
import bisq.core.dao.state.DaoStateSnapshotService;
|
||||
import bisq.core.offer.OfferBookService;
|
||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||
import bisq.core.user.Preferences;
|
||||
|
||||
import bisq.common.app.Version;
|
||||
@ -47,6 +52,18 @@ public class RestApi extends ExecutableForAppWithP2p {
|
||||
private SignVerifyService signVerifyService;
|
||||
private DaoStateSnapshotService daoStateSnapshotService;
|
||||
private Preferences preferences;
|
||||
@Getter
|
||||
private DaoExplorerService daoExplorerService;
|
||||
@Getter
|
||||
private DaoFacade daoFacade;
|
||||
@Getter
|
||||
private ProposalService proposalService;
|
||||
@Getter
|
||||
private CycleService cycleService;
|
||||
@Getter
|
||||
private TradeStatisticsManager tradeStatisticsManager;
|
||||
@Getter
|
||||
private OfferBookService offerBookService;
|
||||
|
||||
public RestApi() {
|
||||
super("Bisq Rest Api", "bisq_restapi", "bisq_restapi", Version.VERSION);
|
||||
@ -74,6 +91,12 @@ public class RestApi extends ExecutableForAppWithP2p {
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -31,6 +31,10 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import bisq.restapi.endpoints.AccountAgeApi;
|
||||
import bisq.restapi.endpoints.BondedReputationApi;
|
||||
import bisq.restapi.endpoints.BondedRoleVerificationApi;
|
||||
import bisq.restapi.endpoints.ExplorerBlocksApi;
|
||||
import bisq.restapi.endpoints.ExplorerDaoApi;
|
||||
import bisq.restapi.endpoints.ExplorerMarketsApi;
|
||||
import bisq.restapi.endpoints.ExplorerTransactionsApi;
|
||||
import bisq.restapi.endpoints.ProofOfBurnApi;
|
||||
import bisq.restapi.endpoints.SignedWitnessApi;
|
||||
import bisq.restapi.error.CustomExceptionMapper;
|
||||
@ -63,6 +67,10 @@ public class RestApiMain extends ResourceConfig {
|
||||
.register(BondedRoleVerificationApi.class)
|
||||
.register(AccountAgeApi.class)
|
||||
.register(SignedWitnessApi.class)
|
||||
.register(ExplorerMarketsApi.class)
|
||||
.register(ExplorerDaoApi.class)
|
||||
.register(ExplorerBlocksApi.class)
|
||||
.register(ExplorerTransactionsApi.class)
|
||||
.register(SwaggerResolution.class);
|
||||
daoNodeApplication.startServer(config.daoNodeApiUrl, config.daoNodeApiPort);
|
||||
});
|
||||
|
@ -19,7 +19,6 @@ package bisq.restapi.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
||||
|
||||
@ -30,7 +29,6 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
* Need to be in sync with the Bisq 2 BondedReputationDto class.
|
||||
*/
|
||||
@Getter
|
||||
@Slf4j
|
||||
@ToString
|
||||
@Schema(title = "BondedReputation")
|
||||
public class BondedReputationDto {
|
||||
|
@ -18,6 +18,7 @@
|
||||
package bisq.restapi.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@ -30,6 +31,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
* Need to be in sync with the Bisq 2 BondedRoleDto class.
|
||||
*/
|
||||
@Getter
|
||||
@ToString
|
||||
@Schema(title = "BondedRoleVerification")
|
||||
public class BondedRoleVerificationDto {
|
||||
@Nullable
|
||||
|
14
restapi/src/main/java/bisq/restapi/dto/BsqStatsDto.java
Normal file
14
restapi/src/main/java/bisq/restapi/dto/BsqStatsDto.java
Normal file
@ -0,0 +1,14 @@
|
||||
package bisq.restapi.dto;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
public class BsqStatsDto {
|
||||
long minted;
|
||||
long burnt;
|
||||
int addresses;
|
||||
int unspent_txos;
|
||||
int spent_txos;
|
||||
int height;
|
||||
int genesisHeight;
|
||||
}
|
31
restapi/src/main/java/bisq/restapi/dto/JsonBlock.java
Normal file
31
restapi/src/main/java/bisq/restapi/dto/JsonBlock.java
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.restapi.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
public class JsonBlock {
|
||||
int height;
|
||||
long time; // in ms
|
||||
String hash;
|
||||
String previousBlockHash;
|
||||
List<JsonTx> txs;
|
||||
}
|
28
restapi/src/main/java/bisq/restapi/dto/JsonBlocks.java
Normal file
28
restapi/src/main/java/bisq/restapi/dto/JsonBlocks.java
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.restapi.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
class JsonBlocks {
|
||||
int chainHeight;
|
||||
List<JsonBlock> blocks;
|
||||
}
|
28
restapi/src/main/java/bisq/restapi/dto/JsonCurrency.java
Normal file
28
restapi/src/main/java/bisq/restapi/dto/JsonCurrency.java
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.restapi.dto;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
public class JsonCurrency {
|
||||
String code;
|
||||
String name;
|
||||
int precision;
|
||||
String _type; // "fiat" or "crypto"
|
||||
}
|
31
restapi/src/main/java/bisq/restapi/dto/JsonDaoCycle.java
Normal file
31
restapi/src/main/java/bisq/restapi/dto/JsonDaoCycle.java
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.restapi.dto;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
public class JsonDaoCycle {
|
||||
int heightOfFirstBlock;
|
||||
int cycleIndex;
|
||||
long startDate; // in ms
|
||||
int proposalCount;
|
||||
long burnedAmount;
|
||||
long issuedAmount;
|
||||
Boolean inProgress;
|
||||
}
|
29
restapi/src/main/java/bisq/restapi/dto/JsonOffer.java
Normal file
29
restapi/src/main/java/bisq/restapi/dto/JsonOffer.java
Normal file
@ -0,0 +1,29 @@
|
||||
package bisq.restapi.dto;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
public class JsonOffer {
|
||||
String direction;
|
||||
String currencyCode;
|
||||
long minAmount;
|
||||
long amount;
|
||||
long price;
|
||||
long date;
|
||||
boolean useMarketBasedPrice;
|
||||
double marketPriceMargin;
|
||||
String paymentMethod;
|
||||
String id;
|
||||
String currencyPair;
|
||||
String primaryMarketDirection;
|
||||
String priceDisplayString;
|
||||
String primaryMarketAmountDisplayString;
|
||||
String primaryMarketMinAmountDisplayString;
|
||||
String primaryMarketVolumeDisplayString;
|
||||
String primaryMarketMinVolumeDisplayString;
|
||||
long primaryMarketPrice;
|
||||
long primaryMarketAmount;
|
||||
long primaryMarketMinAmount;
|
||||
long primaryMarketVolume;
|
||||
long primaryMarketMinVolume;
|
||||
}
|
43
restapi/src/main/java/bisq/restapi/dto/JsonScriptPubKey.java
Normal file
43
restapi/src/main/java/bisq/restapi/dto/JsonScriptPubKey.java
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.restapi.dto;
|
||||
|
||||
import bisq.core.dao.state.model.blockchain.PubKeyScript;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Value;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@Value
|
||||
public class JsonScriptPubKey {
|
||||
List<String> addresses;
|
||||
String asm;
|
||||
String hex;
|
||||
int reqSigs;
|
||||
String type;
|
||||
|
||||
public JsonScriptPubKey(PubKeyScript pubKeyScript) {
|
||||
addresses = pubKeyScript.getAddresses();
|
||||
asm = pubKeyScript.getAsm();
|
||||
hex = pubKeyScript.getHex();
|
||||
reqSigs = pubKeyScript.getReqSigs();
|
||||
type = pubKeyScript.getScriptType().toString();
|
||||
}
|
||||
}
|
35
restapi/src/main/java/bisq/restapi/dto/JsonSpentInfo.java
Normal file
35
restapi/src/main/java/bisq/restapi/dto/JsonSpentInfo.java
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.restapi.dto;
|
||||
|
||||
import bisq.core.dao.state.model.blockchain.SpentInfo;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
public class JsonSpentInfo {
|
||||
long height;
|
||||
int inputIndex;
|
||||
String txId;
|
||||
|
||||
public JsonSpentInfo(SpentInfo spentInfo) {
|
||||
height = spentInfo.getBlockHeight();
|
||||
inputIndex = spentInfo.getInputIndex();
|
||||
txId = spentInfo.getTxId();
|
||||
}
|
||||
}
|
34
restapi/src/main/java/bisq/restapi/dto/JsonTradeInfo.java
Normal file
34
restapi/src/main/java/bisq/restapi/dto/JsonTradeInfo.java
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.restapi.dto;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
// equivalent of bisq.core.trade.statistics.TradeStatisticsForJson
|
||||
@Value
|
||||
public class JsonTradeInfo {
|
||||
String currency;
|
||||
long tradePrice;
|
||||
long tradeAmount;
|
||||
long tradeDate;
|
||||
String paymentMethod;
|
||||
String currencyPair;
|
||||
long primaryMarketTradePrice;
|
||||
long primaryMarketTradeAmount;
|
||||
long primaryMarketTradeVolume;
|
||||
}
|
86
restapi/src/main/java/bisq/restapi/dto/JsonTx.java
Normal file
86
restapi/src/main/java/bisq/restapi/dto/JsonTx.java
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.restapi.dto;
|
||||
|
||||
import bisq.common.app.Version;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
public class JsonTx {
|
||||
String txVersion = Version.BSQ_TX_VERSION;
|
||||
String id;
|
||||
int blockHeight;
|
||||
String blockHash;
|
||||
long time;
|
||||
List<JsonTxInput> inputs;
|
||||
List<JsonTxOutput> outputs;
|
||||
JsonTxType txType;
|
||||
String txTypeDisplayString;
|
||||
long burntFee;
|
||||
long invalidatedBsq;
|
||||
// If not set it is -1. LockTime of 0 is a valid value.
|
||||
int unlockBlockHeight;
|
||||
|
||||
public JsonTx(String id, int blockHeight, String blockHash, long time, List<JsonTxInput> inputs,
|
||||
List<JsonTxOutput> outputs, JsonTxType txType, String txTypeDisplayString, long burntFee,
|
||||
long invalidatedBsq, int unlockBlockHeight) {
|
||||
this.id = id;
|
||||
this.blockHeight = blockHeight;
|
||||
this.blockHash = blockHash;
|
||||
this.time = time;
|
||||
this.inputs = inputs;
|
||||
this.outputs = outputs;
|
||||
this.txType = txType;
|
||||
this.txTypeDisplayString = txTypeDisplayString;
|
||||
this.burntFee = burntFee;
|
||||
this.invalidatedBsq = invalidatedBsq;
|
||||
this.unlockBlockHeight = unlockBlockHeight;
|
||||
}
|
||||
|
||||
// Enums must not be used directly for hashCode or equals as it delivers the Object.hashCode (internal address)!
|
||||
// The equals and hashCode methods cannot be overwritten in Enums.
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof JsonTx)) return false;
|
||||
if (!super.equals(o)) return false;
|
||||
JsonTx jsonTx = (JsonTx) o;
|
||||
return blockHeight == jsonTx.blockHeight &&
|
||||
time == jsonTx.time &&
|
||||
burntFee == jsonTx.burntFee &&
|
||||
invalidatedBsq == jsonTx.invalidatedBsq &&
|
||||
unlockBlockHeight == jsonTx.unlockBlockHeight &&
|
||||
Objects.equals(txVersion, jsonTx.txVersion) &&
|
||||
Objects.equals(id, jsonTx.id) &&
|
||||
Objects.equals(blockHash, jsonTx.blockHash) &&
|
||||
Objects.equals(inputs, jsonTx.inputs) &&
|
||||
Objects.equals(outputs, jsonTx.outputs) &&
|
||||
txType.name().equals(jsonTx.txType.name()) &&
|
||||
Objects.equals(txTypeDisplayString, jsonTx.txTypeDisplayString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), txVersion, id, blockHeight, blockHash, time, inputs, outputs,
|
||||
txType.name(), txTypeDisplayString, burntFee, invalidatedBsq, unlockBlockHeight);
|
||||
}
|
||||
}
|
33
restapi/src/main/java/bisq/restapi/dto/JsonTxInput.java
Normal file
33
restapi/src/main/java/bisq/restapi/dto/JsonTxInput.java
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.restapi.dto;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Value
|
||||
@Immutable
|
||||
public class JsonTxInput {
|
||||
int spendingTxOutputIndex; // connectedTxOutputIndex
|
||||
String spendingTxId; // connectedTxOutputTxId
|
||||
long bsqAmount;
|
||||
Boolean isVerified; // isBsqTxOutputType
|
||||
String address;
|
||||
long time;
|
||||
}
|
134
restapi/src/main/java/bisq/restapi/dto/JsonTxOutput.java
Normal file
134
restapi/src/main/java/bisq/restapi/dto/JsonTxOutput.java
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.restapi.dto;
|
||||
|
||||
import bisq.common.app.Version;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@Value
|
||||
public class JsonTxOutput {
|
||||
String txVersion = Version.BSQ_TX_VERSION;
|
||||
String txId;
|
||||
int index;
|
||||
long bsqAmount;
|
||||
long btcAmount;
|
||||
int height;
|
||||
Boolean isVerified; // isBsqTxOutputType
|
||||
long burntFee;
|
||||
long invalidatedBsq;
|
||||
String address;
|
||||
@Nullable
|
||||
JsonScriptPubKey scriptPubKey;
|
||||
@Nullable
|
||||
JsonSpentInfo spentInfo;
|
||||
long time;
|
||||
JsonTxType txType;
|
||||
String txTypeDisplayString;
|
||||
JsonTxOutputType txOutputType;
|
||||
String txOutputTypeDisplayString;
|
||||
@Nullable
|
||||
String opReturn;
|
||||
int lockTime;
|
||||
Boolean isUnspent;
|
||||
|
||||
public JsonTxOutput(String txId,
|
||||
int index,
|
||||
long bsqAmount,
|
||||
long btcAmount,
|
||||
int height,
|
||||
boolean isVerified,
|
||||
long burntFee,
|
||||
long invalidatedBsq,
|
||||
String address,
|
||||
JsonScriptPubKey scriptPubKey,
|
||||
JsonSpentInfo spentInfo,
|
||||
long time,
|
||||
JsonTxType txType,
|
||||
String txTypeDisplayString,
|
||||
JsonTxOutputType txOutputType,
|
||||
String txOutputTypeDisplayString,
|
||||
String opReturn,
|
||||
int lockTime,
|
||||
boolean isUnspent) {
|
||||
this.txId = txId;
|
||||
this.index = index;
|
||||
this.bsqAmount = bsqAmount;
|
||||
this.btcAmount = btcAmount;
|
||||
this.height = height;
|
||||
this.isVerified = isVerified;
|
||||
this.burntFee = burntFee;
|
||||
this.invalidatedBsq = invalidatedBsq;
|
||||
this.address = address;
|
||||
this.scriptPubKey = scriptPubKey;
|
||||
this.spentInfo = spentInfo;
|
||||
this.time = time;
|
||||
this.txType = txType;
|
||||
this.txTypeDisplayString = txTypeDisplayString;
|
||||
this.txOutputType = txOutputType;
|
||||
this.txOutputTypeDisplayString = txOutputTypeDisplayString;
|
||||
this.opReturn = opReturn;
|
||||
this.lockTime = lockTime;
|
||||
this.isUnspent = isUnspent;
|
||||
}
|
||||
|
||||
String getId() {
|
||||
return txId + ":" + index;
|
||||
}
|
||||
|
||||
// Enums must not be used directly for hashCode or equals as it delivers the Object.hashCode (internal address)!
|
||||
// The equals and hashCode methods cannot be overwritten in Enums.
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof JsonTxOutput)) return false;
|
||||
if (!super.equals(o)) return false;
|
||||
JsonTxOutput that = (JsonTxOutput) o;
|
||||
return index == that.index &&
|
||||
bsqAmount == that.bsqAmount &&
|
||||
btcAmount == that.btcAmount &&
|
||||
height == that.height &&
|
||||
isVerified == that.isVerified &&
|
||||
burntFee == that.burntFee &&
|
||||
invalidatedBsq == that.invalidatedBsq &&
|
||||
time == that.time &&
|
||||
lockTime == that.lockTime &&
|
||||
isUnspent == that.isUnspent &&
|
||||
Objects.equals(txVersion, that.txVersion) &&
|
||||
Objects.equals(txId, that.txId) &&
|
||||
Objects.equals(address, that.address) &&
|
||||
Objects.equals(scriptPubKey, that.scriptPubKey) &&
|
||||
Objects.equals(spentInfo, that.spentInfo) &&
|
||||
txType.name().equals(that.txType.name()) &&
|
||||
Objects.equals(txTypeDisplayString, that.txTypeDisplayString) &&
|
||||
txOutputType == that.txOutputType &&
|
||||
Objects.equals(txOutputTypeDisplayString, that.txOutputTypeDisplayString) &&
|
||||
Objects.equals(opReturn, that.opReturn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), txVersion, txId, index, bsqAmount, btcAmount, height, isVerified,
|
||||
burntFee, invalidatedBsq, address, scriptPubKey, spentInfo, time, txType.name(), txTypeDisplayString,
|
||||
txOutputType, txOutputTypeDisplayString, opReturn, lockTime, isUnspent);
|
||||
}
|
||||
}
|
51
restapi/src/main/java/bisq/restapi/dto/JsonTxOutputType.java
Normal file
51
restapi/src/main/java/bisq/restapi/dto/JsonTxOutputType.java
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.restapi.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
// Need to be in sync with TxOutputType
|
||||
public enum JsonTxOutputType {
|
||||
UNDEFINED("Undefined"),
|
||||
UNDEFINED_OUTPUT("Undefined output"),
|
||||
GENESIS_OUTPUT("Genesis"),
|
||||
BSQ_OUTPUT("BSQ"),
|
||||
BTC_OUTPUT("BTC"),
|
||||
PROPOSAL_OP_RETURN_OUTPUT("Proposal opReturn"),
|
||||
COMP_REQ_OP_RETURN_OUTPUT("Compensation request opReturn"),
|
||||
REIMBURSEMENT_OP_RETURN_OUTPUT("Reimbursement request opReturn"),
|
||||
CONFISCATE_BOND_OP_RETURN_OUTPUT("Confiscate bond opReturn"),
|
||||
ISSUANCE_CANDIDATE_OUTPUT("Issuance candidate"),
|
||||
BLIND_VOTE_LOCK_STAKE_OUTPUT("Blind vote lock stake"),
|
||||
BLIND_VOTE_OP_RETURN_OUTPUT("Blind vote opReturn"),
|
||||
VOTE_REVEAL_UNLOCK_STAKE_OUTPUT("Vote reveal unlock stake"),
|
||||
VOTE_REVEAL_OP_RETURN_OUTPUT("Vote reveal opReturn"),
|
||||
ASSET_LISTING_FEE_OP_RETURN_OUTPUT("Asset listing fee OpReturn"),
|
||||
PROOF_OF_BURN_OP_RETURN_OUTPUT("Proof of burn opReturn"),
|
||||
LOCKUP_OUTPUT("Lockup"),
|
||||
LOCKUP_OP_RETURN_OUTPUT("Lockup opReturn"),
|
||||
UNLOCK_OUTPUT("Unlock"),
|
||||
INVALID_OUTPUT("Invalid");
|
||||
|
||||
private final String displayString;
|
||||
|
||||
JsonTxOutputType(String displayString) {
|
||||
this.displayString = displayString;
|
||||
}
|
||||
}
|
48
restapi/src/main/java/bisq/restapi/dto/JsonTxType.java
Normal file
48
restapi/src/main/java/bisq/restapi/dto/JsonTxType.java
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.restapi.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
// Need to be in sync with TxOutputType
|
||||
public enum JsonTxType {
|
||||
UNDEFINED("Undefined"),
|
||||
UNDEFINED_TX_TYPE("Undefined tx type"),
|
||||
UNVERIFIED("Unverified"),
|
||||
INVALID("Invalid"),
|
||||
GENESIS("Genesis"),
|
||||
TRANSFER_BSQ("Transfer BSQ"),
|
||||
PAY_TRADE_FEE("Pay trade fee"),
|
||||
PROPOSAL("Proposal"),
|
||||
COMPENSATION_REQUEST("Compensation request"),
|
||||
REIMBURSEMENT_REQUEST("Reimbursement request"),
|
||||
BLIND_VOTE("Blind vote"),
|
||||
VOTE_REVEAL("Vote reveal"),
|
||||
LOCKUP("Lockup"),
|
||||
UNLOCK("Unlock"),
|
||||
ASSET_LISTING_FEE("Asset listing fee"),
|
||||
PROOF_OF_BURN("Proof of burn"),
|
||||
IRREGULAR("Irregular");
|
||||
|
||||
private final String displayString;
|
||||
|
||||
JsonTxType(String displayString) {
|
||||
this.displayString = displayString;
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
package bisq.restapi.endpoints;
|
||||
|
||||
import bisq.core.dao.state.model.blockchain.Block;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
|
||||
|
||||
import bisq.restapi.DaoExplorerService;
|
||||
import bisq.restapi.RestApi;
|
||||
import bisq.restapi.RestApiMain;
|
||||
import bisq.restapi.dto.JsonBlock;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.Application;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
@Slf4j
|
||||
@Path("/explorer/blocks")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "BLOCKS API")
|
||||
public class ExplorerBlocksApi {
|
||||
private final DaoExplorerService daoExplorerService;
|
||||
private final RestApi restApi;
|
||||
|
||||
public ExplorerBlocksApi(@Context Application application) {
|
||||
restApi = ((RestApiMain) application).getRestApi();
|
||||
daoExplorerService = restApi.getDaoExplorerService();
|
||||
}
|
||||
|
||||
// http://localhost:8081/api/v1/explorer/blocks/get-bsq-block-by-height/139
|
||||
@Operation(description = "Request BSQ block details")
|
||||
@ApiResponse(responseCode = "200", description = "The BSQ block",
|
||||
content = {@Content(mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(allOf = JsonBlock.class))}
|
||||
)
|
||||
@GET
|
||||
@Path("get-bsq-block-by-height/{block-height}")
|
||||
public JsonBlock getBsqBlockByHeight(@Parameter(description = "Block Height") @PathParam("block-height") int blockHeight) {
|
||||
List<Block> blocks = restApi.getDaoStateService().getBlocks();
|
||||
Optional<JsonBlock> jsonBlock = checkNotNull(blocks.stream())
|
||||
.filter(block -> block.getHeight() == blockHeight)
|
||||
.map(this::getJsonBlock)
|
||||
.findFirst();
|
||||
if (jsonBlock.isPresent()) {
|
||||
log.info("supplying block at height {} to client.", blockHeight);
|
||||
return jsonBlock.get();
|
||||
}
|
||||
log.warn("block {} not found!", blockHeight);
|
||||
return null;
|
||||
}
|
||||
|
||||
//http://localhost:8081/api/v1/explorer/blocks/get-bsq-block-by-hash/2e90186bd0958e8d4821e0b2546e018d70e3b4f136af8676e3571ca2363ce7f8
|
||||
@GET
|
||||
@Path("get-bsq-block-by-hash/{block-hash}")
|
||||
public JsonBlock getBsqBlockByHash(@Parameter(description = "Block Hash") @PathParam("block-hash") String hash) {
|
||||
List<Block> blocks = restApi.getDaoStateService().getBlocks();
|
||||
Optional<JsonBlock> jsonBlock = checkNotNull(blocks.stream())
|
||||
.filter(block -> block.getHash().equalsIgnoreCase(hash))
|
||||
.map(this::getJsonBlock)
|
||||
.findFirst();
|
||||
if (jsonBlock.isPresent()) {
|
||||
log.info("supplying block {} to client.", hash);
|
||||
return jsonBlock.get();
|
||||
}
|
||||
log.warn("block {} not found!", hash);
|
||||
return null;
|
||||
}
|
||||
|
||||
private JsonBlock getJsonBlock(Block block) {
|
||||
return daoExplorerService.getJsonBlock(block);
|
||||
}
|
||||
}
|
124
restapi/src/main/java/bisq/restapi/endpoints/ExplorerDaoApi.java
Normal file
124
restapi/src/main/java/bisq/restapi/endpoints/ExplorerDaoApi.java
Normal file
@ -0,0 +1,124 @@
|
||||
package bisq.restapi.endpoints;
|
||||
|
||||
import bisq.core.dao.DaoFacade;
|
||||
import bisq.core.dao.governance.period.CycleService;
|
||||
import bisq.core.dao.governance.proposal.ProposalService;
|
||||
import bisq.core.dao.state.DaoStateService;
|
||||
import bisq.core.dao.state.model.blockchain.Tx;
|
||||
import bisq.core.dao.state.model.governance.Issuance;
|
||||
import bisq.core.dao.state.model.governance.IssuanceType;
|
||||
import bisq.core.dao.state.model.governance.Proposal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
||||
|
||||
import bisq.restapi.DaoExplorerService;
|
||||
import bisq.restapi.RestApi;
|
||||
import bisq.restapi.RestApiMain;
|
||||
import bisq.restapi.dto.BsqStatsDto;
|
||||
import bisq.restapi.dto.JsonDaoCycle;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.Application;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
@Slf4j
|
||||
@Path("/explorer/dao")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "EXPLORER API")
|
||||
public class ExplorerDaoApi {
|
||||
private final RestApi restApi;
|
||||
private final DaoStateService daoStateService;
|
||||
private final DaoExplorerService daoExplorerService;
|
||||
|
||||
public ExplorerDaoApi(@Context Application application) {
|
||||
restApi = ((RestApiMain) application).getRestApi();
|
||||
daoStateService = restApi.getDaoStateService();
|
||||
daoExplorerService = restApi.getDaoExplorerService();
|
||||
}
|
||||
|
||||
//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();
|
||||
long minted = genesisSupply + issuedByCompensations + issuedByReimbursements;
|
||||
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());
|
||||
return new BsqStatsDto(minted, burnt, numAddresses, unspentTxos, spentTxos,
|
||||
daoExplorerService.getLastKnownBlockHeight(), daoFacade.getGenesisBlockHeight());
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("query-dao-cycles")
|
||||
public List<JsonDaoCycle> queryDaoCycles() {
|
||||
Set<Integer> cyclesAdded = new HashSet<>();
|
||||
List<JsonDaoCycle> 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()))
|
||||
.filter(cycle -> cycleService.getCycleIndex(cycle) >= 0) // change this if you only need the latest n cycles
|
||||
.forEach(cycle -> {
|
||||
long cycleStartTime = daoStateService.getBlockTimeAtBlockHeight(cycle.getHeightOfFirstBlock());
|
||||
int cycleIndex = cycleService.getCycleIndex(cycle);
|
||||
boolean isCycleInProgress = cycleService.isBlockHeightInCycle(daoFacade.getChainHeight(), cycle);
|
||||
log.info("Cycle {} {}", cycleIndex, isCycleInProgress ? "pending" : "complete");
|
||||
List<Proposal> proposalsForCycle = proposalService.getValidatedProposals().stream()
|
||||
.filter(proposal -> cycleService.isTxInCycle(cycle, proposal.getTxId()))
|
||||
.collect(Collectors.toList());
|
||||
int tempProposalCount = 0;
|
||||
if (isCycleInProgress) {
|
||||
tempProposalCount = (int) proposalService.getTempProposalsAsArrayList().stream()
|
||||
.filter(proposal -> cycleService.isTxInCycle(cycle, proposal.getTxId()))
|
||||
.count();
|
||||
}
|
||||
|
||||
long burnedAmount = daoFacade.getBurntFeeTxs().stream()
|
||||
.filter(e -> cycleService.isBlockHeightInCycle(e.getBlockHeight(), cycle))
|
||||
.mapToLong(Tx::getBurntFee)
|
||||
.sum();
|
||||
|
||||
int proposalCount = proposalsForCycle.size() + tempProposalCount;
|
||||
long issuedAmount = daoFacade.getIssuanceForCycle(cycle);
|
||||
JsonDaoCycle resultsOfCycle = new JsonDaoCycle(
|
||||
cycle.getHeightOfFirstBlock(),
|
||||
cycleIndex + 1,
|
||||
cycleStartTime,
|
||||
proposalCount,
|
||||
burnedAmount,
|
||||
issuedAmount,
|
||||
isCycleInProgress);
|
||||
cyclesAdded.add(resultsOfCycle.getHeightOfFirstBlock());
|
||||
result.add(resultsOfCycle);
|
||||
});
|
||||
result.sort(Comparator.comparing(e -> ((JsonDaoCycle) e).getCycleIndex()).reversed());
|
||||
log.info("client requested dao cycles, returning {} records", result.size());
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
package bisq.restapi.endpoints;
|
||||
|
||||
import bisq.core.locale.CurrencyUtil;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.trade.statistics.TradeStatistics3;
|
||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||
|
||||
import bisq.common.util.MathUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
||||
|
||||
import bisq.restapi.RestApi;
|
||||
import bisq.restapi.RestApiMain;
|
||||
import bisq.restapi.dto.JsonCurrency;
|
||||
import bisq.restapi.dto.JsonOffer;
|
||||
import bisq.restapi.dto.JsonTradeInfo;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.Application;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
@Slf4j
|
||||
@Path("/explorer/markets")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "EXPLORER API")
|
||||
public class ExplorerMarketsApi {
|
||||
private static final long MONTH = TimeUnit.DAYS.toMillis(30);
|
||||
|
||||
private final RestApi restApi;
|
||||
|
||||
public ExplorerMarketsApi(@Context Application application) {
|
||||
restApi = ((RestApiMain) application).getRestApi();
|
||||
}
|
||||
|
||||
// http://localhost:8081/api/v1/explorer/markets/get-currencies
|
||||
@GET
|
||||
@Path("get-currencies")
|
||||
public List<JsonCurrency> getBisqCurrencies() {
|
||||
ArrayList<JsonCurrency> fiatCurrencyList = CurrencyUtil.getMatureMarketCurrencies().stream()
|
||||
.map(e -> new JsonCurrency(e.getCode(), e.getName(), 8, "fiat"))
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
ArrayList<JsonCurrency> cryptoCurrencyList = CurrencyUtil.getMainCryptoCurrencies().stream()
|
||||
.map(e -> new JsonCurrency(e.getCode(), e.getName(), 8, "crypto"))
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
List<JsonCurrency> result = Stream.concat(fiatCurrencyList.stream(), cryptoCurrencyList.stream()).collect(Collectors.toList());
|
||||
log.info("client requested currencies, returning {} currencies", result.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("get-offers")
|
||||
public List<JsonOffer> getBisqOffers() {
|
||||
List<JsonOffer> result = restApi.getOfferBookService().getOfferForJsonList().stream()
|
||||
.map(offerForJson -> new JsonOffer(
|
||||
offerForJson.direction.name(),
|
||||
offerForJson.currencyCode,
|
||||
offerForJson.minAmount,
|
||||
offerForJson.amount,
|
||||
offerForJson.price,
|
||||
offerForJson.date,
|
||||
offerForJson.useMarketBasedPrice,
|
||||
offerForJson.marketPriceMargin,
|
||||
offerForJson.paymentMethod,
|
||||
offerForJson.id,
|
||||
offerForJson.currencyPair,
|
||||
offerForJson.direction.name(),
|
||||
offerForJson.priceDisplayString,
|
||||
offerForJson.primaryMarketAmountDisplayString,
|
||||
offerForJson.primaryMarketMinAmountDisplayString,
|
||||
offerForJson.primaryMarketVolumeDisplayString,
|
||||
offerForJson.primaryMarketMinVolumeDisplayString,
|
||||
offerForJson.primaryMarketPrice,
|
||||
offerForJson.primaryMarketAmount,
|
||||
offerForJson.primaryMarketMinAmount,
|
||||
offerForJson.primaryMarketVolume,
|
||||
offerForJson.primaryMarketMinVolume)
|
||||
)
|
||||
.collect(Collectors.toList());
|
||||
log.info("client requested offers, returning {} offers", result.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("get-trades/{newestTimestamp}/{oldestTimestamp}")
|
||||
public List<JsonTradeInfo> getBisqTrades(@PathParam("newestTimestamp") long newestTimestamp,
|
||||
@PathParam("oldestTimestamp") long oldestTimestamp) {
|
||||
log.info("newestTimestamp: {} oldestTimestamp: {}", newestTimestamp, oldestTimestamp);
|
||||
|
||||
long to = new Date().getTime();
|
||||
long from = newestTimestamp > 0 ? newestTimestamp : to - MONTH; // 30 days default
|
||||
TradeStatisticsManager tradeStatisticsManager = restApi.getTradeStatisticsManager();
|
||||
ArrayList<JsonTradeInfo> result = new ArrayList<>();
|
||||
List<TradeStatistics3> tradeStatisticsList = tradeStatisticsManager.getTradeStatisticsList(from, to);
|
||||
log.info("requesting a fresh batch of trades {}", tradeStatisticsList.size());
|
||||
if (tradeStatisticsList.size() < 200 && oldestTimestamp > 0) {
|
||||
to = oldestTimestamp;
|
||||
from = to - MONTH;
|
||||
List<TradeStatistics3> additional = tradeStatisticsManager.getTradeStatisticsList(from, to);
|
||||
tradeStatisticsList.addAll(additional);
|
||||
log.info("requesting an additional older batch of trades {}", additional.size());
|
||||
}
|
||||
tradeStatisticsList.forEach(x -> {
|
||||
try {
|
||||
String currencyPair = Res.getBaseCurrencyCode() + "/" + x.getCurrency();
|
||||
// we use precision 4 for fiat based price but on the markets api we use precision 8 so we scale up by 10000
|
||||
long primaryMarketTradePrice = (long) MathUtils.scaleUpByPowerOf10(x.getTradePrice().getValue(), 4);
|
||||
long primaryMarketTradeAmount = x.getAmount();
|
||||
// we use precision 4 for fiat but on the markets api we use precision 8 so we scale up by 10000
|
||||
long primaryMarketTradeVolume = x.getTradeVolume() != null ?
|
||||
(long) MathUtils.scaleUpByPowerOf10(x.getTradeVolume().getValue(), 4) : 0;
|
||||
|
||||
if (CurrencyUtil.isCryptoCurrency(x.getCurrency())) {
|
||||
currencyPair = x.getCurrency() + "/" + Res.getBaseCurrencyCode();
|
||||
primaryMarketTradePrice = x.getTradePrice().getValue();
|
||||
primaryMarketTradeAmount = x.getTradeVolume().getValue(); // getVolumeByAmount?
|
||||
primaryMarketTradeVolume = x.getAmount();
|
||||
}
|
||||
JsonTradeInfo jsonTradeInfo = new JsonTradeInfo(x.getCurrency(), x.getPrice(), x.getAmount(),
|
||||
x.getDateAsLong(), x.getPaymentMethodId(), currencyPair, primaryMarketTradePrice,
|
||||
primaryMarketTradeAmount, primaryMarketTradeVolume);
|
||||
result.add(jsonTradeInfo);
|
||||
} catch (Throwable t) {
|
||||
log.error("Iterating tradeStatisticsList failed", t);
|
||||
}
|
||||
});
|
||||
log.info("client requested trades, returning {} trades", result.size());
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.restapi.endpoints;
|
||||
|
||||
import bisq.core.dao.state.model.blockchain.BaseTx;
|
||||
import bisq.core.dao.state.model.blockchain.Tx;
|
||||
import bisq.core.dao.state.model.blockchain.TxType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
||||
|
||||
import bisq.restapi.DaoExplorerService;
|
||||
import bisq.restapi.RestApi;
|
||||
import bisq.restapi.RestApiMain;
|
||||
import bisq.restapi.dto.JsonTx;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.Application;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
@Slf4j
|
||||
@Path("/explorer/transactions")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "TRANSACTIONS API")
|
||||
public class ExplorerTransactionsApi {
|
||||
private final RestApi restApi;
|
||||
private final DaoExplorerService daoExplorerService;
|
||||
|
||||
public ExplorerTransactionsApi(@Context Application application) {
|
||||
restApi = ((RestApiMain) application).getRestApi();
|
||||
daoExplorerService = restApi.getDaoExplorerService();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("get-bsq-tx/{txid}")
|
||||
public JsonTx getTx(@Parameter(description = "TxId")
|
||||
@PathParam("txid") String txId) {
|
||||
Optional<JsonTx> jsonTx = restApi.getDaoStateService().getUnorderedTxStream()
|
||||
.filter(t -> t.getId().equals(txId))
|
||||
.map(this::getJsonTx)
|
||||
.findFirst();
|
||||
if (jsonTx.isPresent()) {
|
||||
log.info("supplying tx {} to client.", txId);
|
||||
return jsonTx.get();
|
||||
}
|
||||
log.warn("txid {} not found!", txId);
|
||||
return null;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("get-bsq-tx-for-addr/{addr}")
|
||||
public List<JsonTx> getBisqTxForAddr(@PathParam("addr") String address) {
|
||||
Map<String, Set<String>> addressToTxIds = daoExplorerService.getTxIdsByAddress();
|
||||
List<JsonTx> result = new ArrayList<>();
|
||||
Set<String> strings = addressToTxIds.get(address);
|
||||
strings.forEach(txId -> {
|
||||
restApi.getDaoStateService().getTx(txId).stream()
|
||||
.map(this::getJsonTx)
|
||||
.forEach(result::add);
|
||||
});
|
||||
log.info("getBisqTxForAddr: returning {} items.", result.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("query-txs-paginated/{start}/{count}/{filters}")
|
||||
public List<JsonTx> queryTxsPaginated(@PathParam("start") int start,
|
||||
@PathParam("count") int count,
|
||||
@PathParam("filters") String filters) {
|
||||
log.info("filters: {}", filters);
|
||||
List<JsonTx> jsonTxs = restApi.getDaoStateService().getUnorderedTxStream()
|
||||
.sorted(Comparator.comparing(BaseTx::getTime).reversed())
|
||||
.filter(tx -> hasMatchingTxType(tx, filters))
|
||||
.skip(start)
|
||||
.limit(count)
|
||||
.map(this::getJsonTx)
|
||||
.collect(Collectors.toList());
|
||||
log.info("supplying {} jsonTxs to client from index {}", jsonTxs.size(), start);
|
||||
return jsonTxs;
|
||||
}
|
||||
|
||||
private boolean hasMatchingTxType(Tx tx, String filters) {
|
||||
String[] filterTokens = filters.split("~");
|
||||
if (filterTokens.length < 1 || filters.equalsIgnoreCase("~")) {
|
||||
return true;
|
||||
}
|
||||
for (String filter : filterTokens) {
|
||||
try {
|
||||
TxType txType = Enum.valueOf(TxType.class, filter);
|
||||
if (tx.getTxType() == txType) {
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Could not resolve TxType Enum from " + filter, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private JsonTx getJsonTx(Tx tx) {
|
||||
return daoExplorerService.getJsonTx(tx);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user