mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 01:41:11 +01:00
Merge remote-tracking branch 'upstream/master'
# Conflicts: # core/src/main/java/bisq/core/dao/state/SnapshotManager.java
This commit is contained in:
commit
3689e4cbf1
@ -1,5 +1,6 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
@ -53,7 +53,7 @@ import bisq.core.dao.node.BsqNodeProvider;
|
||||
import bisq.core.dao.node.full.FullNode;
|
||||
import bisq.core.dao.node.full.RpcService;
|
||||
import bisq.core.dao.node.full.network.FullNodeNetworkService;
|
||||
import bisq.core.dao.node.json.JsonBlockChainExporter;
|
||||
import bisq.core.dao.node.json.ExportJsonFilesService;
|
||||
import bisq.core.dao.node.lite.LiteNode;
|
||||
import bisq.core.dao.node.lite.network.LiteNodeNetworkService;
|
||||
import bisq.core.dao.node.parser.BlockParser;
|
||||
@ -99,7 +99,7 @@ public class DaoModule extends AppModule {
|
||||
bind(BsqState.class).in(Singleton.class);
|
||||
bind(BsqStateService.class).in(Singleton.class);
|
||||
bind(SnapshotManager.class).in(Singleton.class);
|
||||
bind(JsonBlockChainExporter.class).in(Singleton.class);
|
||||
bind(ExportJsonFilesService.class).in(Singleton.class);
|
||||
|
||||
// Period
|
||||
bind(CycleService.class).in(Singleton.class);
|
||||
|
@ -25,6 +25,7 @@ import bisq.core.dao.governance.voteresult.VoteResultService;
|
||||
import bisq.core.dao.governance.votereveal.VoteRevealService;
|
||||
import bisq.core.dao.node.BsqNode;
|
||||
import bisq.core.dao.node.BsqNodeProvider;
|
||||
import bisq.core.dao.node.json.ExportJsonFilesService;
|
||||
import bisq.core.dao.state.BsqStateService;
|
||||
import bisq.core.dao.state.period.CycleService;
|
||||
|
||||
@ -35,7 +36,6 @@ import com.google.inject.Inject;
|
||||
/**
|
||||
* High level entry point for Dao domain.
|
||||
* We initialize all main service classes here to be sure they are started.
|
||||
*
|
||||
*/
|
||||
public class DaoSetup {
|
||||
private final BsqStateService bsqStateService;
|
||||
@ -47,6 +47,7 @@ public class DaoSetup {
|
||||
private final VoteRevealService voteRevealService;
|
||||
private final VoteResultService voteResultService;
|
||||
private final BsqNode bsqNode;
|
||||
private final ExportJsonFilesService exportJsonFilesService;
|
||||
|
||||
@Inject
|
||||
public DaoSetup(BsqNodeProvider bsqNodeProvider,
|
||||
@ -57,7 +58,8 @@ public class DaoSetup {
|
||||
BlindVoteListService blindVoteListService,
|
||||
MyBlindVoteListService myBlindVoteListService,
|
||||
VoteRevealService voteRevealService,
|
||||
VoteResultService voteResultService) {
|
||||
VoteResultService voteResultService,
|
||||
ExportJsonFilesService exportJsonFilesService) {
|
||||
this.bsqStateService = bsqStateService;
|
||||
this.cycleService = cycleService;
|
||||
this.proposalService = proposalService;
|
||||
@ -66,6 +68,7 @@ public class DaoSetup {
|
||||
this.myBlindVoteListService = myBlindVoteListService;
|
||||
this.voteRevealService = voteRevealService;
|
||||
this.voteResultService = voteResultService;
|
||||
this.exportJsonFilesService = exportJsonFilesService;
|
||||
|
||||
bsqNode = bsqNodeProvider.getBsqNode();
|
||||
}
|
||||
@ -81,6 +84,7 @@ public class DaoSetup {
|
||||
myBlindVoteListService.addListeners();
|
||||
voteRevealService.addListeners();
|
||||
voteResultService.addListeners();
|
||||
exportJsonFilesService.addListeners();
|
||||
|
||||
bsqStateService.start();
|
||||
cycleService.start();
|
||||
@ -90,6 +94,7 @@ public class DaoSetup {
|
||||
myBlindVoteListService.start();
|
||||
voteRevealService.start();
|
||||
voteResultService.start();
|
||||
exportJsonFilesService.start();
|
||||
|
||||
bsqNode.setErrorMessageHandler(errorMessageHandler);
|
||||
bsqNode.start();
|
||||
|
@ -22,6 +22,7 @@ import bisq.core.dao.DaoSetupService;
|
||||
import bisq.core.dao.governance.ballot.vote.Vote;
|
||||
import bisq.core.dao.governance.proposal.ProposalService;
|
||||
import bisq.core.dao.governance.proposal.storage.appendonly.ProposalPayload;
|
||||
import bisq.core.dao.state.period.PeriodService;
|
||||
|
||||
import bisq.common.proto.persistable.PersistedDataHost;
|
||||
import bisq.common.storage.Storage;
|
||||
@ -32,6 +33,7 @@ import javafx.collections.ListChangeListener;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@ -49,14 +51,16 @@ public class BallotListService implements PersistedDataHost, DaoSetupService {
|
||||
}
|
||||
|
||||
private final ProposalService proposalService;
|
||||
private final PeriodService periodService;
|
||||
private final Storage<BallotList> storage;
|
||||
|
||||
private final BallotList ballotList = new BallotList();
|
||||
private final List<BallotListChangeListener> listeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
@Inject
|
||||
public BallotListService(ProposalService proposalService, Storage<BallotList> storage) {
|
||||
public BallotListService(ProposalService proposalService, PeriodService periodService, Storage<BallotList> storage) {
|
||||
this.proposalService = proposalService;
|
||||
this.periodService = periodService;
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
@ -125,6 +129,12 @@ public class BallotListService implements PersistedDataHost, DaoSetupService {
|
||||
return ballotList;
|
||||
}
|
||||
|
||||
public List<Ballot> getBallotsOfCycle() {
|
||||
return ballotList.stream()
|
||||
.filter(ballot -> periodService.isTxInCorrectCycle(ballot.getTxId(), periodService.getChainHeight()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private
|
||||
|
@ -56,7 +56,7 @@ public class BlindVoteConsensus {
|
||||
}
|
||||
|
||||
public static BallotList getSortedBallotList(BallotListService ballotListService) {
|
||||
List<Ballot> ballotList = ballotListService.getBallotList().stream()
|
||||
List<Ballot> ballotList = ballotListService.getBallotsOfCycle().stream()
|
||||
.sorted(Comparator.comparing(Ballot::getTxId))
|
||||
.collect(Collectors.toList());
|
||||
log.info("Sorted ballotList: " + ballotList);
|
||||
|
@ -89,6 +89,11 @@ public class BlindVoteValidator {
|
||||
public boolean isTxInPhaseAndCycle(BlindVote blindVote) {
|
||||
String txId = blindVote.getTxId();
|
||||
Optional<Tx> optionalTx = bsqStateService.getTx(txId);
|
||||
if (!optionalTx.isPresent()) {
|
||||
log.warn("Tx is not in bsqStateService. blindVoteTxId={}", txId);
|
||||
return false;
|
||||
}
|
||||
|
||||
int txHeight = optionalTx.get().getBlockHeight();
|
||||
if (!periodService.isTxInCorrectCycle(txHeight, bsqStateService.getChainHeight())) {
|
||||
log.debug("Tx is not in current cycle. blindVote={}", blindVote);
|
||||
|
@ -271,10 +271,11 @@ public class MyBlindVoteListService implements PersistedDataHost, BsqStateListen
|
||||
|
||||
// blindVoteTxId is null if we use the method from the getCurrentlyAvailableMerit call.
|
||||
public MeritList getMerits(@Nullable String blindVoteTxId) {
|
||||
// Create a lookup set for txIds of own comp. requests
|
||||
// Create a lookup set for txIds of own comp. requests from past cycles (we ignore request form that cycle)
|
||||
Set<String> myCompensationProposalTxIs = myProposalListService.getList().stream()
|
||||
.filter(proposal -> proposal instanceof CompensationProposal)
|
||||
.map(Proposal::getTxId)
|
||||
.filter(txId -> periodService.isTxInPastCycle(txId, periodService.getChainHeight()))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
return new MeritList(bsqStateService.getIssuanceSet().stream()
|
||||
|
@ -22,6 +22,7 @@ import bisq.core.dao.state.governance.Issuance;
|
||||
|
||||
import bisq.common.proto.network.NetworkPayload;
|
||||
import bisq.common.proto.persistable.PersistablePayload;
|
||||
import bisq.common.util.Utilities;
|
||||
|
||||
import io.bisq.generated.protobuffer.PB;
|
||||
|
||||
@ -63,4 +64,12 @@ public class Merit implements PersistablePayload, NetworkPayload, ConsensusCriti
|
||||
public String getIssuanceTxId() {
|
||||
return issuance.getTxId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Merit{" +
|
||||
"\n issuance=" + issuance +
|
||||
",\n signature=" + Utilities.bytesAsHexString(signature) +
|
||||
"\n}";
|
||||
}
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ public class MeritConsensus {
|
||||
// We verify if signature of hash of blindVoteTxId is correct. EC key from first input for blind vote tx is
|
||||
// used for signature.
|
||||
if (pubKeyAsHex == null) {
|
||||
log.error("Error at getMeritStake: pubKeyAsHex is null");
|
||||
log.error("Error at isSignatureValid: pubKeyAsHex is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -110,16 +110,15 @@ public class MeritConsensus {
|
||||
|
||||
public static long getWeightedMeritAmount(long amount, int issuanceHeight, int blockHeight, int blocksPerYear) {
|
||||
if (issuanceHeight > blockHeight)
|
||||
throw new IllegalArgumentException("issuanceHeight must not be larger than blockHeight");
|
||||
throw new IllegalArgumentException("issuanceHeight must not be larger than blockHeight. issuanceHeight=" + issuanceHeight + "; blockHeight=" + blockHeight);
|
||||
if (blockHeight < 0)
|
||||
throw new IllegalArgumentException("blockHeight must not be negative");
|
||||
throw new IllegalArgumentException("blockHeight must not be negative. blockHeight=" + blockHeight);
|
||||
if (amount < 0)
|
||||
throw new IllegalArgumentException("amount must not be negative");
|
||||
throw new IllegalArgumentException("amount must not be negative. amount" + amount);
|
||||
if (blocksPerYear < 0)
|
||||
throw new IllegalArgumentException("blocksPerYear must not be negative");
|
||||
throw new IllegalArgumentException("blocksPerYear must not be negative. blocksPerYear=" + blocksPerYear);
|
||||
if (issuanceHeight < 0)
|
||||
throw new IllegalArgumentException("issuanceHeight must not be negative");
|
||||
|
||||
throw new IllegalArgumentException("issuanceHeight must not be negative. issuanceHeight=" + issuanceHeight);
|
||||
|
||||
// We use a linear function to apply a factor for the issuance amount of 1 if the issuance was recent and 0
|
||||
// if the issuance was 2 years old or older.
|
||||
|
@ -131,7 +131,6 @@ public abstract class Proposal implements PersistablePayload, NetworkPayload, Co
|
||||
|
||||
public abstract Param getThresholdParam();
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Proposal{" +
|
||||
|
@ -28,7 +28,7 @@ public class EvaluatedProposal {
|
||||
private final long requiredQuorum;
|
||||
private final long requiredThreshold;
|
||||
|
||||
public EvaluatedProposal(boolean isAccepted, ProposalVoteResult proposalVoteResult, long requiredQuorum, long requiredThreshold) {
|
||||
EvaluatedProposal(boolean isAccepted, ProposalVoteResult proposalVoteResult, long requiredQuorum, long requiredThreshold) {
|
||||
this.isAccepted = isAccepted;
|
||||
this.proposalVoteResult = proposalVoteResult;
|
||||
this.requiredQuorum = requiredQuorum;
|
||||
|
@ -19,7 +19,7 @@ package bisq.core.dao.node.full;
|
||||
|
||||
import bisq.core.dao.node.BsqNode;
|
||||
import bisq.core.dao.node.full.network.FullNodeNetworkService;
|
||||
import bisq.core.dao.node.json.JsonBlockChainExporter;
|
||||
import bisq.core.dao.node.json.ExportJsonFilesService;
|
||||
import bisq.core.dao.node.parser.BlockParser;
|
||||
import bisq.core.dao.node.parser.exceptions.BlockNotConnectingException;
|
||||
import bisq.core.dao.state.BsqStateService;
|
||||
@ -49,7 +49,7 @@ public class FullNode extends BsqNode {
|
||||
|
||||
private final RpcService rpcService;
|
||||
private final FullNodeNetworkService fullNodeNetworkService;
|
||||
private final JsonBlockChainExporter jsonBlockChainExporter;
|
||||
private final ExportJsonFilesService exportJsonFilesService;
|
||||
private boolean addBlockHandlerAdded;
|
||||
|
||||
|
||||
@ -64,12 +64,12 @@ public class FullNode extends BsqNode {
|
||||
SnapshotManager snapshotManager,
|
||||
P2PService p2PService,
|
||||
RpcService rpcService,
|
||||
JsonBlockChainExporter jsonBlockChainExporter,
|
||||
ExportJsonFilesService exportJsonFilesService,
|
||||
FullNodeNetworkService fullNodeNetworkService) {
|
||||
super(blockParser, bsqStateService, snapshotManager, p2PService);
|
||||
this.rpcService = rpcService;
|
||||
|
||||
this.jsonBlockChainExporter = jsonBlockChainExporter;
|
||||
this.exportJsonFilesService = exportJsonFilesService;
|
||||
this.fullNodeNetworkService = fullNodeNetworkService;
|
||||
}
|
||||
|
||||
@ -88,7 +88,7 @@ public class FullNode extends BsqNode {
|
||||
}
|
||||
|
||||
public void shutDown() {
|
||||
jsonBlockChainExporter.shutDown();
|
||||
exportJsonFilesService.shutDown();
|
||||
fullNodeNetworkService.shutDown();
|
||||
}
|
||||
|
||||
@ -147,7 +147,7 @@ public class FullNode extends BsqNode {
|
||||
}
|
||||
|
||||
private void onNewBlock(Block block) {
|
||||
jsonBlockChainExporter.maybeExport();
|
||||
exportJsonFilesService.exportToJson();
|
||||
|
||||
if (p2pNetworkReady && parseBlockchainComplete)
|
||||
fullNodeNetworkService.publishNewBlock(block);
|
||||
|
@ -0,0 +1,235 @@
|
||||
/*
|
||||
* 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.core.dao.node.json;
|
||||
|
||||
import bisq.core.dao.DaoOptionKeys;
|
||||
import bisq.core.dao.DaoSetupService;
|
||||
import bisq.core.dao.state.BsqState;
|
||||
import bisq.core.dao.state.BsqStateService;
|
||||
import bisq.core.dao.state.blockchain.PubKeyScript;
|
||||
import bisq.core.dao.state.blockchain.TxOutput;
|
||||
import bisq.core.dao.state.blockchain.TxType;
|
||||
|
||||
import bisq.common.storage.FileUtil;
|
||||
import bisq.common.storage.JsonFileManager;
|
||||
import bisq.common.storage.Storage;
|
||||
import bisq.common.util.Utilities;
|
||||
|
||||
import org.bitcoinj.core.Utils;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import javax.inject.Named;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Slf4j
|
||||
public class ExportJsonFilesService implements DaoSetupService {
|
||||
private final BsqStateService bsqStateService;
|
||||
private final File storageDir;
|
||||
private final boolean dumpBlockchainData;
|
||||
|
||||
private final ListeningExecutorService executor = Utilities.getListeningExecutorService("JsonExporter",
|
||||
1, 1, 1200);
|
||||
private JsonFileManager txFileManager, txOutputFileManager, bsqStateFileManager;
|
||||
|
||||
@Inject
|
||||
public ExportJsonFilesService(BsqStateService bsqStateService,
|
||||
@Named(Storage.STORAGE_DIR) File storageDir,
|
||||
@Named(DaoOptionKeys.DUMP_BLOCKCHAIN_DATA) boolean dumpBlockchainData) {
|
||||
this.bsqStateService = bsqStateService;
|
||||
this.storageDir = storageDir;
|
||||
this.dumpBlockchainData = dumpBlockchainData;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// DaoSetupService
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void addListeners() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (dumpBlockchainData) {
|
||||
File jsonDir = new File(Paths.get(storageDir.getAbsolutePath(), "json").toString());
|
||||
File txDir = new File(Paths.get(storageDir.getAbsolutePath(), "json", "tx").toString());
|
||||
File txOutputDir = new File(Paths.get(storageDir.getAbsolutePath(), "json", "txo").toString());
|
||||
File bsqStateDir = new File(Paths.get(storageDir.getAbsolutePath(), "json", "all").toString());
|
||||
try {
|
||||
if (txDir.exists())
|
||||
FileUtil.deleteDirectory(txDir);
|
||||
if (txOutputDir.exists())
|
||||
FileUtil.deleteDirectory(txOutputDir);
|
||||
if (bsqStateDir.exists())
|
||||
FileUtil.deleteDirectory(bsqStateDir);
|
||||
if (jsonDir.exists())
|
||||
FileUtil.deleteDirectory(jsonDir);
|
||||
} catch (IOException e) {
|
||||
log.error(e.toString());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (!jsonDir.mkdir())
|
||||
log.warn("make jsonDir failed.\njsonDir=" + jsonDir.getAbsolutePath());
|
||||
|
||||
if (!txDir.mkdir())
|
||||
log.warn("make txDir failed.\ntxDir=" + txDir.getAbsolutePath());
|
||||
|
||||
if (!txOutputDir.mkdir())
|
||||
log.warn("make txOutputDir failed.\ntxOutputDir=" + txOutputDir.getAbsolutePath());
|
||||
|
||||
if (!bsqStateDir.mkdir())
|
||||
log.warn("make bsqStateDir failed.\nbsqStateDir=" + bsqStateDir.getAbsolutePath());
|
||||
|
||||
txFileManager = new JsonFileManager(txDir);
|
||||
txOutputFileManager = new JsonFileManager(txOutputDir);
|
||||
bsqStateFileManager = new JsonFileManager(bsqStateDir);
|
||||
}
|
||||
}
|
||||
|
||||
public void shutDown() {
|
||||
if (dumpBlockchainData) {
|
||||
txFileManager.shutDown();
|
||||
txOutputFileManager.shutDown();
|
||||
bsqStateFileManager.shutDown();
|
||||
}
|
||||
}
|
||||
|
||||
public void exportToJson() {
|
||||
if (dumpBlockchainData) {
|
||||
// We store the data we need once we write the data to disk (in the thread) locally.
|
||||
// Access to bsqStateService is single threaded, we must not access bsqStateService from the thread.
|
||||
List<JsonTxOutput> allJsonTxOutputs = new ArrayList<>();
|
||||
List<JsonTx> jsonTxs = new ArrayList<>();
|
||||
BsqState bsqStateClone = bsqStateService.getClone();
|
||||
|
||||
bsqStateService.getTxStream().forEach(tx -> {
|
||||
List<JsonTxOutput> jsonTxOutputs = new ArrayList<>();
|
||||
String txId = tx.getId();
|
||||
long time = tx.getTime();
|
||||
int blockHeight = tx.getBlockHeight();
|
||||
long burntFee = bsqStateService.getBurntFee(tx.getId());
|
||||
TxType txType = tx.getTxType();
|
||||
JsonTxType jsonTxType = txType != null ? JsonTxType.valueOf(txType.name()) : null;
|
||||
String jsonTxTypeDisplayString = jsonTxType != null ? jsonTxType.getDisplayString() : "";
|
||||
tx.getTxOutputs().forEach(txOutput -> {
|
||||
boolean isBsqTxOutputType = bsqStateService.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 = bsqStateService.getSpentInfo(txOutput).map(JsonSpentInfo::new).orElse(null);
|
||||
JsonTxOutputType txOutputType = JsonTxOutputType.valueOf(txOutput.getTxOutputType().name());
|
||||
int lockTime = txOutput.getLockTime();
|
||||
String opReturn = txOutput.getOpReturnData() != null ? Utils.HEX.encode(txOutput.getOpReturnData()) : null;
|
||||
JsonTxOutput jsonTxOutput = new JsonTxOutput(txId,
|
||||
txOutput.getIndex(),
|
||||
bsqAmount,
|
||||
btcAmount,
|
||||
blockHeight,
|
||||
isBsqTxOutputType,
|
||||
burntFee,
|
||||
txOutput.getAddress(),
|
||||
scriptPubKey,
|
||||
spentInfo,
|
||||
time,
|
||||
jsonTxType,
|
||||
jsonTxTypeDisplayString,
|
||||
txOutputType,
|
||||
txOutputType.getDisplayString(),
|
||||
opReturn,
|
||||
lockTime
|
||||
);
|
||||
jsonTxOutputs.add(jsonTxOutput);
|
||||
allJsonTxOutputs.add(jsonTxOutput);
|
||||
});
|
||||
|
||||
List<JsonTxInput> inputs = tx.getTxInputs().stream()
|
||||
.map(txInput -> {
|
||||
Optional<TxOutput> optionalTxOutput = bsqStateService.getConnectedTxOutput(txInput);
|
||||
if (optionalTxOutput.isPresent()) {
|
||||
TxOutput connectedTxOutput = optionalTxOutput.get();
|
||||
boolean isBsqTxOutputType = bsqStateService.isBsqTxOutputType(connectedTxOutput);
|
||||
return new JsonTxInput(txInput.getConnectedTxOutputIndex(),
|
||||
txInput.getConnectedTxOutputTxId(),
|
||||
connectedTxOutput.getValue(),
|
||||
isBsqTxOutputType,
|
||||
connectedTxOutput.getAddress(),
|
||||
time);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
JsonTx jsonTx = new JsonTx(txId,
|
||||
blockHeight,
|
||||
tx.getBlockHash(),
|
||||
time,
|
||||
inputs,
|
||||
jsonTxOutputs,
|
||||
jsonTxType,
|
||||
jsonTxTypeDisplayString,
|
||||
burntFee,
|
||||
tx.getUnlockBlockHeight());
|
||||
|
||||
jsonTxs.add(jsonTx);
|
||||
});
|
||||
|
||||
ListenableFuture<Void> future = executor.submit(() -> {
|
||||
bsqStateFileManager.writeToDisc(Utilities.objectToJson(bsqStateClone), "BsqStateService");
|
||||
allJsonTxOutputs.forEach(jsonTxOutput -> txOutputFileManager.writeToDisc(Utilities.objectToJson(jsonTxOutput), jsonTxOutput.getId()));
|
||||
jsonTxs.forEach(jsonTx -> txFileManager.writeToDisc(Utilities.objectToJson(jsonTx), jsonTx.getId()));
|
||||
return null;
|
||||
});
|
||||
|
||||
Futures.addCallback(future, new FutureCallback<>() {
|
||||
public void onSuccess(Void ignore) {
|
||||
log.trace("onSuccess");
|
||||
}
|
||||
|
||||
public void onFailure(@NotNull Throwable throwable) {
|
||||
log.error(throwable.toString());
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,206 +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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.core.dao.node.json;
|
||||
|
||||
import bisq.core.dao.DaoOptionKeys;
|
||||
import bisq.core.dao.state.BsqState;
|
||||
import bisq.core.dao.state.BsqStateService;
|
||||
import bisq.core.dao.state.blockchain.PubKeyScript;
|
||||
import bisq.core.dao.state.blockchain.SpentInfo;
|
||||
import bisq.core.dao.state.blockchain.Tx;
|
||||
import bisq.core.dao.state.blockchain.TxOutput;
|
||||
import bisq.core.dao.state.blockchain.TxType;
|
||||
|
||||
import bisq.common.storage.FileUtil;
|
||||
import bisq.common.storage.JsonFileManager;
|
||||
import bisq.common.storage.Storage;
|
||||
import bisq.common.util.Utilities;
|
||||
|
||||
import org.bitcoinj.core.Utils;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import javax.inject.Named;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Slf4j
|
||||
public class JsonBlockChainExporter {
|
||||
private final BsqStateService bsqStateService;
|
||||
private final boolean dumpBlockchainData;
|
||||
|
||||
private final ListeningExecutorService executor = Utilities.getListeningExecutorService("JsonExporter", 1, 1, 1200);
|
||||
private JsonFileManager txFileManager, txOutputFileManager, jsonFileManager;
|
||||
|
||||
@Inject
|
||||
public JsonBlockChainExporter(BsqStateService bsqStateService,
|
||||
@Named(Storage.STORAGE_DIR) File storageDir,
|
||||
@Named(DaoOptionKeys.DUMP_BLOCKCHAIN_DATA) boolean dumpBlockchainData) {
|
||||
this.bsqStateService = bsqStateService;
|
||||
this.dumpBlockchainData = dumpBlockchainData;
|
||||
|
||||
init(storageDir, dumpBlockchainData);
|
||||
}
|
||||
|
||||
private void init(@Named(Storage.STORAGE_DIR) File storageDir, @Named(DaoOptionKeys.DUMP_BLOCKCHAIN_DATA) boolean dumpBlockchainData) {
|
||||
if (dumpBlockchainData) {
|
||||
File txDir = new File(Paths.get(storageDir.getAbsolutePath(), "tx").toString());
|
||||
File txOutputDir = new File(Paths.get(storageDir.getAbsolutePath(), "txo").toString());
|
||||
File blockchainDir = new File(Paths.get(storageDir.getAbsolutePath(), "all").toString());
|
||||
try {
|
||||
if (txDir.exists())
|
||||
FileUtil.deleteDirectory(txDir);
|
||||
if (txOutputDir.exists())
|
||||
FileUtil.deleteDirectory(txOutputDir);
|
||||
if (blockchainDir.exists())
|
||||
FileUtil.deleteDirectory(blockchainDir);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (!txDir.mkdir())
|
||||
log.warn("make txDir failed.\ntxDir=" + txDir.getAbsolutePath());
|
||||
|
||||
if (!txOutputDir.mkdir())
|
||||
log.warn("make txOutputDir failed.\ntxOutputDir=" + txOutputDir.getAbsolutePath());
|
||||
|
||||
if (!blockchainDir.mkdir())
|
||||
log.warn("make blockchainDir failed.\nblockchainDir=" + blockchainDir.getAbsolutePath());
|
||||
|
||||
txFileManager = new JsonFileManager(txDir);
|
||||
txOutputFileManager = new JsonFileManager(txOutputDir);
|
||||
jsonFileManager = new JsonFileManager(blockchainDir);
|
||||
}
|
||||
}
|
||||
|
||||
public void shutDown() {
|
||||
if (dumpBlockchainData) {
|
||||
txFileManager.shutDown();
|
||||
txOutputFileManager.shutDown();
|
||||
jsonFileManager.shutDown();
|
||||
}
|
||||
}
|
||||
|
||||
public void maybeExport() {
|
||||
if (dumpBlockchainData) {
|
||||
ListenableFuture<Void> future = executor.submit(() -> {
|
||||
final BsqState bsqStateClone = bsqStateService.getClone();
|
||||
Map<String, Tx> txMap = bsqStateService.getBlocksFromState(bsqStateClone).stream()
|
||||
.filter(Objects::nonNull)
|
||||
.flatMap(block -> block.getTxs().stream())
|
||||
.collect(Collectors.toMap(Tx::getId, tx -> tx));
|
||||
for (Tx tx : txMap.values()) {
|
||||
String txId = tx.getId();
|
||||
final Optional<TxType> optionalTxType = bsqStateService.getOptionalTxType(txId);
|
||||
optionalTxType.ifPresent(txType1 -> {
|
||||
JsonTxType txType = txType1 != TxType.UNDEFINED_TX_TYPE ?
|
||||
JsonTxType.valueOf(txType1.name()) : null;
|
||||
List<JsonTxOutput> outputs = new ArrayList<>();
|
||||
tx.getTxOutputs().forEach(txOutput -> {
|
||||
final Optional<SpentInfo> optionalSpentInfo = bsqStateService.getSpentInfo(txOutput);
|
||||
final boolean isBsqOutput = bsqStateService.isBsqTxOutputType(txOutput);
|
||||
final PubKeyScript pubKeyScript = txOutput.getPubKeyScript();
|
||||
final JsonTxOutput outputForJson = new JsonTxOutput(txId,
|
||||
txOutput.getIndex(),
|
||||
isBsqOutput ? txOutput.getValue() : 0,
|
||||
!isBsqOutput ? txOutput.getValue() : 0,
|
||||
txOutput.getBlockHeight(),
|
||||
isBsqOutput,
|
||||
bsqStateService.getBurntFee(tx.getId()),
|
||||
txOutput.getAddress(),
|
||||
pubKeyScript != null ? new JsonScriptPubKey(pubKeyScript) : null,
|
||||
optionalSpentInfo.map(JsonSpentInfo::new).orElse(null),
|
||||
tx.getTime(),
|
||||
txType,
|
||||
txType != null ? txType.getDisplayString() : "",
|
||||
txOutput.getOpReturnData() != null ? Utils.HEX.encode(txOutput.getOpReturnData()) : null
|
||||
);
|
||||
outputs.add(outputForJson);
|
||||
txOutputFileManager.writeToDisc(Utilities.objectToJson(outputForJson), outputForJson.getId());
|
||||
});
|
||||
|
||||
|
||||
List<JsonTxInput> inputs = tx.getTxInputs().stream()
|
||||
.map(txInput -> {
|
||||
Optional<TxOutput> optionalTxOutput = bsqStateService.getConnectedTxOutput(txInput);
|
||||
if (optionalTxOutput.isPresent()) {
|
||||
final TxOutput connectedTxOutput = optionalTxOutput.get();
|
||||
final boolean isBsqOutput = bsqStateService.isBsqTxOutputType(connectedTxOutput);
|
||||
return new JsonTxInput(txInput.getConnectedTxOutputIndex(),
|
||||
txInput.getConnectedTxOutputTxId(),
|
||||
connectedTxOutput.getValue(),
|
||||
isBsqOutput,
|
||||
connectedTxOutput.getAddress(),
|
||||
tx.getTime());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
final JsonTx jsonTx = new JsonTx(txId,
|
||||
tx.getBlockHeight(),
|
||||
tx.getBlockHash(),
|
||||
tx.getTime(),
|
||||
inputs,
|
||||
outputs,
|
||||
txType,
|
||||
txType != null ? txType.getDisplayString() : "",
|
||||
bsqStateService.getBurntFee(tx.getId()));
|
||||
|
||||
txFileManager.writeToDisc(Utilities.objectToJson(jsonTx), txId);
|
||||
});
|
||||
}
|
||||
|
||||
jsonFileManager.writeToDisc(Utilities.objectToJson(bsqStateClone), "BsqStateService");
|
||||
return null;
|
||||
});
|
||||
|
||||
Futures.addCallback(future, new FutureCallback<Void>() {
|
||||
public void onSuccess(Void ignore) {
|
||||
log.trace("onSuccess");
|
||||
}
|
||||
|
||||
public void onFailure(@NotNull Throwable throwable) {
|
||||
log.error(throwable.toString());
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -23,9 +23,8 @@ import java.util.List;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
//TODO sync up with data model
|
||||
@Value
|
||||
public class JsonTx {
|
||||
class JsonTx {
|
||||
private final String txVersion = Version.BSQ_TX_VERSION;
|
||||
private final String id;
|
||||
private final int blockHeight;
|
||||
@ -36,4 +35,6 @@ public class JsonTx {
|
||||
private final JsonTxType txType;
|
||||
private final String txTypeDisplayString;
|
||||
private final long burntFee;
|
||||
// If not set it is -1. LockTime of 0 is a valid value.
|
||||
private final int unlockBlockHeight;
|
||||
}
|
||||
|
@ -21,14 +21,13 @@ import lombok.Value;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
//TODO sync up with data model
|
||||
@Value
|
||||
@Immutable
|
||||
public class JsonTxInput {
|
||||
private final int spendingTxOutputIndex;
|
||||
private final String spendingTxId;
|
||||
class JsonTxInput {
|
||||
private final int spendingTxOutputIndex; // connectedTxOutputIndex
|
||||
private final String spendingTxId; // connectedTxOutputTxId
|
||||
private final long bsqAmount;
|
||||
private final boolean isVerified;
|
||||
private final boolean isVerified; // isBsqTxOutputType
|
||||
private final String address;
|
||||
private final long time;
|
||||
}
|
||||
|
@ -21,7 +21,8 @@ import bisq.common.app.Version;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
//TODO sync up with data model
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@Value
|
||||
public class JsonTxOutput {
|
||||
private final String txVersion = Version.BSQ_TX_VERSION;
|
||||
@ -30,15 +31,21 @@ public class JsonTxOutput {
|
||||
private final long bsqAmount;
|
||||
private final long btcAmount;
|
||||
private final int height;
|
||||
private final boolean isVerified;
|
||||
private final boolean isVerified; // isBsqTxOutputType
|
||||
private final long burntFee;
|
||||
private final String address;
|
||||
@Nullable
|
||||
private final JsonScriptPubKey scriptPubKey;
|
||||
@Nullable
|
||||
private final JsonSpentInfo spentInfo;
|
||||
private final long time;
|
||||
private final JsonTxType txType;
|
||||
private final String txTypeDisplayString;
|
||||
private final JsonTxOutputType txOutputType; // new
|
||||
private final String txOutputTypeDisplayString; // new
|
||||
@Nullable
|
||||
private final String opReturn;
|
||||
private final int lockTime; // new
|
||||
|
||||
public String getId() {
|
||||
return txId + ":" + outputIndex;
|
||||
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.core.dao.node.json;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
// Need to be in sync with TxOutputType
|
||||
public enum JsonTxOutputType {
|
||||
UNDEFINED("Undefined"),
|
||||
GENESIS_OUTPUT("Genesis"),
|
||||
BSQ_OUTPUT("BSQ"),
|
||||
BTC_OUTPUT("BTC"),
|
||||
PROPOSAL_OP_RETURN_OUTPUT("Proposal opReturn"),
|
||||
COMP_REQ_OP_RETURN_OUTPUT("Compensation 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"),
|
||||
LOCKUP("Lockup"),
|
||||
LOCKUP_OP_RETURN_OUTPUT("Lockup opReturn"),
|
||||
UNLOCK("Unlock"),
|
||||
INVALID_OUTPUT("Invalid");
|
||||
|
||||
@Getter
|
||||
private String displayString;
|
||||
|
||||
JsonTxOutputType(String displayString) {
|
||||
this.displayString = displayString;
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@ package bisq.core.dao.node.json;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
//TODO sync up with data model
|
||||
// Need to be in sync with TxOutputType
|
||||
public enum JsonTxType {
|
||||
UNDEFINED_TX_TYPE("Undefined"),
|
||||
UNVERIFIED("Unverified"),
|
||||
@ -27,13 +27,12 @@ public enum JsonTxType {
|
||||
GENESIS("Genesis"),
|
||||
TRANSFER_BSQ("Transfer BSQ"),
|
||||
PAY_TRADE_FEE("Pay trade fee"),
|
||||
PROPOSAL("Ballot"),
|
||||
PROPOSAL("Proposal"),
|
||||
COMPENSATION_REQUEST("Compensation request"),
|
||||
VOTE("Vote"),
|
||||
BLIND_VOTE("Blind vote"),
|
||||
VOTE_REVEAL("Vote reveal"),
|
||||
LOCK_UP("Lockup"),
|
||||
UN_LOCK("Unlock");
|
||||
LOCKUP("Lockup"),
|
||||
UNLOCK("Unlock");
|
||||
|
||||
@Getter
|
||||
private String displayString;
|
||||
|
@ -264,10 +264,10 @@ public class TxParser {
|
||||
/**
|
||||
* Whether the BSQ fee and phase is valid for a transaction.
|
||||
*
|
||||
* @param blockHeight The height of the block that the transaction is in.
|
||||
* @param bsqFee The fee in BSQ, in satoshi.
|
||||
* @param phase The current phase of the DAO, e.g {@code DaoPhase.Phase.PROPOSAL}.
|
||||
* @param param The parameter for the fee, e.g {@code Param.PROPOSAL_FEE}.
|
||||
* @param blockHeight The height of the block that the transaction is in.
|
||||
* @param bsqFee The fee in BSQ, in satoshi.
|
||||
* @param phase The current phase of the DAO, e.g {@code DaoPhase.Phase.PROPOSAL}.
|
||||
* @param param The parameter for the fee, e.g {@code Param.PROPOSAL_FEE}.
|
||||
* @return True if the fee and phase was valid, false otherwise.
|
||||
*/
|
||||
private boolean isFeeAndPhaseValid(int blockHeight, long bsqFee, DaoPhase.Phase phase, Param param) {
|
||||
@ -296,10 +296,10 @@ public class TxParser {
|
||||
/**
|
||||
* Retrieve the type of the transaction, assuming it is relevant to bisq.
|
||||
*
|
||||
* @param tx The temporary transaction.
|
||||
* @param hasOpReturnCandidate True if we have a candidate for an OP_RETURN.
|
||||
* @param remainingInputValue The remaining value of inputs not yet accounted for, in satoshi.
|
||||
* @param optionalOpReturnType If present, the OP_RETURN type of the transaction.
|
||||
* @param tx The temporary transaction.
|
||||
* @param hasOpReturnCandidate True if we have a candidate for an OP_RETURN.
|
||||
* @param remainingInputValue The remaining value of inputs not yet accounted for, in satoshi.
|
||||
* @param optionalOpReturnType If present, the OP_RETURN type of the transaction.
|
||||
* @return The type of the transaction, if it is relevant to bisq.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
@ -399,10 +399,10 @@ public class TxParser {
|
||||
/**
|
||||
* Parse and return the genesis transaction for bisq, if applicable.
|
||||
*
|
||||
* @param genesisTxId The transaction id of the bisq genesis transaction.
|
||||
* @param genesisBlockHeight The block height of the bisq genesis transaction.
|
||||
* @param genesisTotalSupply The total supply of the genesis issuance for bisq.
|
||||
* @param rawTx The candidate transaction.
|
||||
* @param genesisTxId The transaction id of the bisq genesis transaction.
|
||||
* @param genesisBlockHeight The block height of the bisq genesis transaction.
|
||||
* @param genesisTotalSupply The total supply of the genesis issuance for bisq.
|
||||
* @param rawTx The candidate transaction.
|
||||
* @return The genesis transaction if applicable, or Optional.empty() otherwise.
|
||||
*/
|
||||
public static Optional<TempTx> findGenesisTx(String genesisTxId, int genesisBlockHeight, Coin genesisTotalSupply,
|
||||
|
@ -132,10 +132,10 @@ public class BsqState implements PersistableEnvelope {
|
||||
|
||||
@Override
|
||||
public Message toProtoMessage() {
|
||||
return PB.PersistableEnvelope.newBuilder().setBsqState(getStateBuilder()).build();
|
||||
return PB.PersistableEnvelope.newBuilder().setBsqState(getBsqStateBuilder()).build();
|
||||
}
|
||||
|
||||
private PB.BsqState.Builder getStateBuilder() {
|
||||
private PB.BsqState.Builder getBsqStateBuilder() {
|
||||
final PB.BsqState.Builder builder = PB.BsqState.newBuilder();
|
||||
builder.setChainHeight(chainHeight)
|
||||
.addAllBlocks(blocks.stream().map(Block::toProtoMessage).collect(Collectors.toList()))
|
||||
@ -193,10 +193,10 @@ public class BsqState implements PersistableEnvelope {
|
||||
}
|
||||
|
||||
BsqState getClone() {
|
||||
return (BsqState) BsqState.fromProto(getStateBuilder().build());
|
||||
return (BsqState) BsqState.fromProto(getBsqStateBuilder().build());
|
||||
}
|
||||
|
||||
BsqState getClone(BsqState snapshotCandidate) {
|
||||
return (BsqState) BsqState.fromProto(snapshotCandidate.getStateBuilder().build());
|
||||
return (BsqState) BsqState.fromProto(snapshotCandidate.getBsqStateBuilder().build());
|
||||
}
|
||||
}
|
||||
|
@ -120,10 +120,6 @@ public class BsqStateService implements DaoSetupService {
|
||||
return bsqState.getClone();
|
||||
}
|
||||
|
||||
public LinkedList<Block> getBlocksFromState(BsqState bsqState) {
|
||||
return new LinkedList<>(bsqState.getBlocks());
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ChainHeight
|
||||
|
@ -29,6 +29,8 @@ import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
@ -105,7 +107,7 @@ public class SnapshotManager implements BsqStateListener {
|
||||
checkNotNull(storage, "storage must not be null");
|
||||
BsqState persisted = storage.initAndGetPersisted(bsqState, 100);
|
||||
if (persisted != null) {
|
||||
log.info("applySnapshot persisted.chainHeadHeight=" + bsqStateService.getBlocksFromState(persisted).getLast().getHeight());
|
||||
log.info("applySnapshot persisted.chainHeadHeight=" + new LinkedList<>(persisted.getBlocks()).getLast().getHeight());
|
||||
if (persisted.getBlocks().getLast().getHeight() >= genesisTxInfo.getGenesisBlockHeight())
|
||||
bsqStateService.applySnapshot(persisted);
|
||||
} else {
|
||||
|
@ -75,4 +75,14 @@ public class Issuance implements PersistablePayload, NetworkPayload {
|
||||
proto.getAmount(),
|
||||
proto.getPubKey().isEmpty() ? null : proto.getPubKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Issuance{" +
|
||||
"\n txId='" + txId + '\'' +
|
||||
",\n chainHeight=" + chainHeight +
|
||||
",\n amount=" + amount +
|
||||
",\n pubKey='" + pubKey + '\'' +
|
||||
"\n}";
|
||||
}
|
||||
}
|
||||
|
@ -74,9 +74,9 @@ public enum Param {
|
||||
PHASE_BREAK1(1), // 10 blocks
|
||||
PHASE_BLIND_VOTE(2), // 4 days
|
||||
PHASE_BREAK2(1), // 10 blocks
|
||||
PHASE_VOTE_REVEAL(1), // 2 days
|
||||
PHASE_VOTE_REVEAL(2), // 2 days
|
||||
PHASE_BREAK3(1), // 10 blocks
|
||||
PHASE_RESULT(1), // 1 block
|
||||
PHASE_RESULT(2), // 1 block
|
||||
PHASE_BREAK4(1); // 10 blocks
|
||||
|
||||
/*PHASE_UNDEFINED(0),
|
||||
|
@ -566,7 +566,9 @@ portfolio.pending.step3_buyer.warn.part2=The BTC seller still has not confirmed
|
||||
portfolio.pending.step3_buyer.openForDispute=The BTC seller has not confirmed your payment!\nThe max. period for the trade has elapsed.\nYou can wait longer and give the trading peer more time or contact the arbitrator for opening a dispute.
|
||||
# suppress inspection "TrailingSpacesInProperty"
|
||||
portfolio.pending.step3_seller.part=Your trading partner has confirmed that he initiated the {0} payment.\n\n
|
||||
portfolio.pending.step3_seller.altcoin={0}Please check on your favorite {1} blockchain explorer if the transaction to your receiving address\n\
|
||||
portfolio.pending.step3_seller.altcoin.explorer=on your favorite {0} blockchain explorer
|
||||
portfolio.pending.step3_seller.altcoin.wallet=at your {0} wallet
|
||||
portfolio.pending.step3_seller.altcoin={0}Please check {1} if the transaction to your receiving address\n\
|
||||
{2}\n\
|
||||
has already sufficient blockchain confirmations.\nThe payment amount has to be {3}\n\n\
|
||||
You can copy & paste your {4} address from the main screen after closing that popup.
|
||||
|
@ -309,8 +309,11 @@ public class SellerStep3View extends TradeStepView {
|
||||
String id = trade.getShortId();
|
||||
if (paymentAccountPayload instanceof CryptoCurrencyAccountPayload) {
|
||||
String address = ((CryptoCurrencyAccountPayload) paymentAccountPayload).getAddress();
|
||||
String explorerOrWalletString = trade.getOffer().getCurrencyCode().equals("XMR") ?
|
||||
Res.get("portfolio.pending.step3_seller.altcoin.wallet", currencyName) :
|
||||
Res.get("portfolio.pending.step3_seller.altcoin.explorer", currencyName);
|
||||
//noinspection UnusedAssignment
|
||||
message = Res.get("portfolio.pending.step3_seller.altcoin", part1, currencyName, address, tradeVolumeWithCode, currencyName);
|
||||
message = Res.get("portfolio.pending.step3_seller.altcoin", part1, explorerOrWalletString, address, tradeVolumeWithCode, currencyName);
|
||||
} else {
|
||||
if (paymentAccountPayload instanceof USPostalMoneyOrderAccountPayload) {
|
||||
message = Res.get("portfolio.pending.step3_seller.postal", part1, tradeVolumeWithCode, id);
|
||||
|
Loading…
Reference in New Issue
Block a user