Skip to content

Commit

Permalink
Merge explorer rest api code
Browse files Browse the repository at this point in the history
Signed-off-by: HenrikJannsen <[email protected]>
  • Loading branch information
HenrikJannsen committed Jul 21, 2024
1 parent f81ab85 commit 93f156b
Show file tree
Hide file tree
Showing 29 changed files with 1,430 additions and 5 deletions.
12 changes: 12 additions & 0 deletions core/src/main/java/bisq/core/dao/DaoFacade.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import bisq.core.dao.governance.param.Param;
import bisq.core.dao.governance.period.CycleService;
import bisq.core.dao.governance.period.PeriodService;
import bisq.core.dao.governance.proposal.IssuanceProposal;
import bisq.core.dao.governance.proposal.MyProposalListService;
import bisq.core.dao.governance.proposal.ProposalConsensus;
import bisq.core.dao.governance.proposal.ProposalListPresentation;
Expand Down Expand Up @@ -66,6 +67,7 @@
import bisq.core.dao.state.model.governance.BondedRoleType;
import bisq.core.dao.state.model.governance.Cycle;
import bisq.core.dao.state.model.governance.DaoPhase;
import bisq.core.dao.state.model.governance.EvaluatedProposal;
import bisq.core.dao.state.model.governance.IssuanceType;
import bisq.core.dao.state.model.governance.Proposal;
import bisq.core.dao.state.model.governance.Role;
Expand Down Expand Up @@ -805,4 +807,14 @@ public Set<String> getAllDonationAddresses() {
public boolean isParseBlockChainComplete() {
return daoStateService.isParseBlockChainComplete();
}

public long getIssuanceForCycle(Cycle cycle) {
return daoStateService.getEvaluatedProposalList().stream()
.filter(EvaluatedProposal::isAccepted)
.filter(evaluatedProposal -> cycleService.isTxInCycle(cycle, evaluatedProposal.getProposal().getTxId()))
.filter(e -> e.getProposal() instanceof IssuanceProposal)
.map(e -> (IssuanceProposal) e.getProposal())
.mapToLong(e -> e.getRequestedBsq().value)
.sum();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,16 @@ public boolean isTxInCycle(Cycle cycle, String txId) {
return daoStateService.getTx(txId).filter(tx -> isBlockHeightInCycle(tx.getBlockHeight(), cycle)).isPresent();
}

public boolean isBlockHeightInCycle(int blockHeight, Cycle cycle) {
return blockHeight >= cycle.getHeightOfFirstBlock() &&
blockHeight <= cycle.getHeightOfLastBlock();
}


///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////

private boolean isBlockHeightInCycle(int blockHeight, Cycle cycle) {
return blockHeight >= cycle.getHeightOfFirstBlock() &&
blockHeight <= cycle.getHeightOfLastBlock();
}

private Optional<Cycle> maybeCreateNewCycle(int blockHeight, LinkedList<Cycle> cycles) {
// We want to set the correct phase and cycle before we start parsing a new block.
// For Genesis block we did it already in the start method.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,11 @@ public double getRequiredThreshold(Proposal proposal) {
return daoStateService.getParamValueAsPercentDouble(proposal.getThresholdParam(), chainHeight);
}

// We use it in the RestApi do not have a dependency to javafx/
public List<Proposal> getTempProposalsAsArrayList() {
return new ArrayList<>(tempProposals);
}


///////////////////////////////////////////////////////////////////////////////////////////
// Private
Expand Down
59 changes: 59 additions & 0 deletions core/src/main/java/bisq/core/dao/state/DaoStateService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -75,6 +77,7 @@ public class DaoStateService implements DaoSetupService {
@Getter
private boolean parseBlockChainComplete;
private boolean allowDaoStateChange;
private final Map<String, Set<String>> cachedTxIdSetByAddress = new HashMap<>();


///////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -493,6 +500,10 @@ public TreeMap<TxOutputKey, TxOutput> getUnspentTxOutputMap() {
return daoState.getUnspentTxOutputMap();
}

public TreeMap<TxOutputKey, SpentInfo> getSpentInfoMap() {
return daoState.getSpentInfoMap();
}

public void addUnspentTxOutput(TxOutput txOutput) {
assertDaoStateChange();
getUnspentTxOutputMap().put(txOutput.getKey(), txOutput);
Expand Down Expand Up @@ -1027,6 +1038,54 @@ public Optional<SpentInfo> getSpentInfo(TxOutput txOutput) {
}


///////////////////////////////////////////////////////////////////////////////////////////
// Addresses
///////////////////////////////////////////////////////////////////////////////////////////

public Map<String, Set<String>> getTxIdSetByAddress() {
// We clear it at each new (non-empty) block, so it gets recreated
if (!cachedTxIdSetByAddress.isEmpty()) {
return cachedTxIdSetByAddress;
}

Map<TxOutputKey, String> 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)
.filter(txOutput -> !txOutput.getAddress().isEmpty())
.forEach(txOutput -> {
String address = txOutput.getAddress();
Set<String> 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)
.filter(txOutput -> !txOutput.getAddress().isEmpty())
.forEach(txOutput -> {
String txId = txIdByConnectedTxOutputKey.get(txOutput.getKey());
if (txId != null) {
String address = txOutput.getAddress();
Set<String> txIdSet = cachedTxIdSetByAddress.getOrDefault(address, new HashSet<>());
txIdSet.add(txId);
cachedTxIdSetByAddress.put(address, txIdSet);
}
});

return cachedTxIdSetByAddress;
}

///////////////////////////////////////////////////////////////////////////////////////////
// Vote result data
///////////////////////////////////////////////////////////////////////////////////////////
Expand Down
27 changes: 27 additions & 0 deletions core/src/main/java/bisq/core/offer/OfferBookService.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package bisq.core.offer;

import bisq.core.filter.FilterManager;
import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.Res;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.util.JsonUtil;
Expand Down Expand Up @@ -213,6 +214,32 @@ public void addOfferBookChangedListener(OfferBookChangedListener offerBookChange
offerBookChangedListeners.add(offerBookChangedListener);
}

public List<OfferForJson> getOfferForJsonList() {
return getOffers().stream()
.map(offer -> {
try {
OfferDirection inverseDirection = offer.getDirection() == OfferDirection.BUY ? OfferDirection.SELL : OfferDirection.BUY;
OfferDirection offerDirection = CurrencyUtil.isCryptoCurrency(offer.getCurrencyCode()) ? inverseDirection : offer.getDirection();
return new OfferForJson(offerDirection,
offer.getCurrencyCode(),
offer.getMinAmount(),
offer.getAmount(),
offer.getPrice(),
offer.getDate(),
offer.getId(),
offer.isUseMarketBasedPrice(),
offer.getMarketPriceMargin(),
offer.getPaymentMethod()
);
} catch (Throwable t) {
// In case an offer was corrupted with null values we ignore it
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
}


///////////////////////////////////////////////////////////////////////////////////////////
// Private
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,13 @@ public ObservableSet<TradeStatistics3> getObservableTradeStatisticsSet() {
return observableTradeStatisticsSet;
}

public List<TradeStatistics3> getTradeStatisticsList(long dateStart, long dateEnd) {
return observableTradeStatisticsSet.stream()
.filter(x -> x.getDateAsLong() > dateStart && x.getDateAsLong() <= dateEnd)
.sorted((o1, o2) -> (Long.compare(o2.getDateAsLong(), o1.getDateAsLong())))
.collect(Collectors.toList());
}

private void maybeDumpStatistics() {
if (!dumpStatistics) {
return;
Expand Down
16 changes: 16 additions & 0 deletions gradle/verification-metadata.xml
Original file line number Diff line number Diff line change
Expand Up @@ -863,11 +863,27 @@
<sha256 value="5de4666f7f6b003d982f48f18c8e22facef6707365a74e20df7cbad98c931dd7" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.protobuf" name="protobuf-java" version="3.6.1">
<artifact name="protobuf-java-3.6.1.jar">
<sha256 value="fb66d913ff0578553b2e28a3338cbbbe2657e6cfe0e98d939f23aea219daf508"
origin="Generated by Gradle"/>
</artifact>
<artifact name="protobuf-java-3.6.1.pom">
<sha256 value="3d1cf96c47b28508d2290b86266f4d0e8ea534b0c7656825050d5bbce2fe51cc"
origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.protobuf" name="protobuf-parent" version="3.19.1">
<artifact name="protobuf-parent-3.19.1.pom">
<sha256 value="83d413b2a79d6357d2ca78fd623143424e8f6ecc72cfa83bf2ae2ae258a93a44" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.protobuf" name="protobuf-parent" version="3.6.1">
<artifact name="protobuf-parent-3.6.1.pom">
<sha256 value="b83819781441b566ca1d334813cdd203aa7e2f7ae2baad674dceb1fe2d4bb441"
origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.protobuf" name="protoc" version="3.19.1">
<artifact name="protoc-3.19.1-linux-x86_64.exe">
<sha256 value="0b1099f537d44ee862b0c3708f32f1d66cfd76e2e2c1437f0c57a98476c20605" origin="Generated by Gradle"/>
Expand Down
8 changes: 8 additions & 0 deletions restapi/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ dependencies {
implementation(libs.google.guice) {
exclude(module: 'guava')
}
implementation(libs.bitcoinj) {
exclude(module: 'bcprov-jdk15on')
exclude(module: 'guava')
exclude(module: 'jsr305')
exclude(module: 'okhttp')
exclude(module: 'okio')
exclude(module: 'slf4j-api')
}

implementation libs.google.guava
implementation libs.google.guice
Expand Down
128 changes: 128 additions & 0 deletions restapi/src/main/java/bisq/restapi/BlockDataToJsonConverter.java
Original file line number Diff line number Diff line change
@@ -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<JsonTx> 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<JsonTxInput> getJsonTxInputs(DaoStateService daoStateService, 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 static List<JsonTxOutput> 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;
}
}
Loading

0 comments on commit 93f156b

Please sign in to comment.